1/*
2 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22/* \summary: IP printer */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include "netdissect-stdinc.h"
29
30#include "netdissect.h"
31#include "addrtoname.h"
32#include "extract.h"
33
34#include "ip.h"
35#include "ipproto.h"
36
37
38static const struct tok ip_option_values[] = {
39    { IPOPT_EOL, "EOL" },
40    { IPOPT_NOP, "NOP" },
41    { IPOPT_TS, "timestamp" },
42    { IPOPT_SECURITY, "security" },
43    { IPOPT_RR, "RR" },
44    { IPOPT_SSRR, "SSRR" },
45    { IPOPT_LSRR, "LSRR" },
46    { IPOPT_RA, "RA" },
47    { IPOPT_RFC1393, "traceroute" },
48    { 0, NULL }
49};
50
51/*
52 * print the recorded route in an IP RR, LSRR or SSRR option.
53 */
54static int
55ip_printroute(netdissect_options *ndo,
56              const u_char *cp, u_int length)
57{
58	u_int ptr;
59	u_int len;
60
61	if (length < 3) {
62		ND_PRINT(" [bad length %u]", length);
63		return (0);
64	}
65	if ((length + 1) & 3)
66		ND_PRINT(" [bad length %u]", length);
67	ptr = GET_U_1(cp + 2) - 1;
68	if (ptr < 3 || ((ptr + 1) & 3) || ptr > length + 1)
69		ND_PRINT(" [bad ptr %u]", GET_U_1(cp + 2));
70
71	for (len = 3; len < length; len += 4) {
72		ND_TCHECK_4(cp + len);	/* Needed to print the IP addresses */
73		ND_PRINT(" %s", GET_IPADDR_STRING(cp + len));
74		if (ptr > len)
75			ND_PRINT(",");
76	}
77	return (0);
78
79trunc:
80	return (-1);
81}
82
83/*
84 * If source-routing is present and valid, return the final destination.
85 * Otherwise, return IP destination.
86 *
87 * This is used for UDP and TCP pseudo-header in the checksum
88 * calculation.
89 */
90static uint32_t
91ip_finddst(netdissect_options *ndo,
92           const struct ip *ip)
93{
94	u_int length;
95	u_int len;
96	const u_char *cp;
97
98	cp = (const u_char *)(ip + 1);
99	length = IP_HL(ip) * 4;
100	if (length < sizeof(struct ip))
101		goto trunc;
102	length -= sizeof(struct ip);
103
104	for (; length != 0; cp += len, length -= len) {
105		int tt;
106
107		tt = GET_U_1(cp);
108		if (tt == IPOPT_EOL)
109			break;
110		else if (tt == IPOPT_NOP)
111			len = 1;
112		else {
113			len = GET_U_1(cp + 1);
114			if (len < 2)
115				break;
116		}
117		if (length < len)
118			goto trunc;
119		ND_TCHECK_LEN(cp, len);
120		switch (tt) {
121
122		case IPOPT_SSRR:
123		case IPOPT_LSRR:
124			if (len < 7)
125				break;
126			return (GET_IPV4_TO_NETWORK_ORDER(cp + len - 4));
127		}
128	}
129trunc:
130	return (GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst));
131}
132
133/*
134 * Compute a V4-style checksum by building a pseudoheader.
135 */
136uint16_t
137nextproto4_cksum(netdissect_options *ndo,
138                 const struct ip *ip, const uint8_t *data,
139                 u_int len, u_int covlen, uint8_t next_proto)
140{
141	struct phdr {
142		uint32_t src;
143		uint32_t dst;
144		uint8_t mbz;
145		uint8_t proto;
146		uint16_t len;
147	} ph;
148	struct cksum_vec vec[2];
149
150	/* pseudo-header.. */
151	ph.len = htons((uint16_t)len);
152	ph.mbz = 0;
153	ph.proto = next_proto;
154	ph.src = GET_IPV4_TO_NETWORK_ORDER(ip->ip_src);
155	if (IP_HL(ip) == 5)
156		ph.dst = GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst);
157	else
158		ph.dst = ip_finddst(ndo, ip);
159
160	vec[0].ptr = (const uint8_t *)(void *)&ph;
161	vec[0].len = sizeof(ph);
162	vec[1].ptr = data;
163	vec[1].len = covlen;
164	return (in_cksum(vec, 2));
165}
166
167static int
168ip_printts(netdissect_options *ndo,
169           const u_char *cp, u_int length)
170{
171	u_int ptr;
172	u_int len;
173	u_int hoplen;
174	const char *type;
175
176	if (length < 4) {
177		ND_PRINT("[bad length %u]", length);
178		return (0);
179	}
180	ND_PRINT(" TS{");
181	hoplen = ((GET_U_1(cp + 3) & 0xF) != IPOPT_TS_TSONLY) ? 8 : 4;
182	if ((length - 4) & (hoplen-1))
183		ND_PRINT("[bad length %u]", length);
184	ptr = GET_U_1(cp + 2) - 1;
185	len = 0;
186	if (ptr < 4 || ((ptr - 4) & (hoplen-1)) || ptr > length + 1)
187		ND_PRINT("[bad ptr %u]", GET_U_1(cp + 2));
188	switch (GET_U_1(cp + 3)&0xF) {
189	case IPOPT_TS_TSONLY:
190		ND_PRINT("TSONLY");
191		break;
192	case IPOPT_TS_TSANDADDR:
193		ND_PRINT("TS+ADDR");
194		break;
195	case IPOPT_TS_PRESPEC:
196		ND_PRINT("PRESPEC");
197		break;
198	default:
199		ND_PRINT("[bad ts type %u]", GET_U_1(cp + 3)&0xF);
200		goto done;
201	}
202
203	type = " ";
204	for (len = 4; len < length; len += hoplen) {
205		if (ptr == len)
206			type = " ^ ";
207		ND_TCHECK_LEN(cp + len, hoplen);
208		ND_PRINT("%s%u@%s", type, GET_BE_U_4(cp + len + hoplen - 4),
209			  hoplen!=8 ? "" : GET_IPADDR_STRING(cp + len));
210		type = " ";
211	}
212
213done:
214	ND_PRINT("%s", ptr == len ? " ^ " : "");
215
216	if (GET_U_1(cp + 3) >> 4)
217		ND_PRINT(" [%u hops not recorded]} ", GET_U_1(cp + 3)>>4);
218	else
219		ND_PRINT("}");
220	return (0);
221
222trunc:
223	return (-1);
224}
225
226/*
227 * print IP options.
228   If truncated return -1, else 0.
229 */
230static int
231ip_optprint(netdissect_options *ndo,
232            const u_char *cp, u_int length)
233{
234	u_int option_len;
235	const char *sep = "";
236
237	for (; length > 0; cp += option_len, length -= option_len) {
238		u_int option_code;
239
240		ND_PRINT("%s", sep);
241		sep = ",";
242
243		option_code = GET_U_1(cp);
244
245		ND_PRINT("%s",
246		          tok2str(ip_option_values,"unknown %u",option_code));
247
248		if (option_code == IPOPT_NOP ||
249                    option_code == IPOPT_EOL)
250			option_len = 1;
251
252		else {
253			option_len = GET_U_1(cp + 1);
254			if (option_len < 2) {
255				ND_PRINT(" [bad length %u]", option_len);
256				return 0;
257			}
258		}
259
260		if (option_len > length) {
261			ND_PRINT(" [bad length %u]", option_len);
262			return 0;
263		}
264
265		ND_TCHECK_LEN(cp, option_len);
266
267		switch (option_code) {
268		case IPOPT_EOL:
269			return 0;
270
271		case IPOPT_TS:
272			if (ip_printts(ndo, cp, option_len) == -1)
273				goto trunc;
274			break;
275
276		case IPOPT_RR:       /* fall through */
277		case IPOPT_SSRR:
278		case IPOPT_LSRR:
279			if (ip_printroute(ndo, cp, option_len) == -1)
280				goto trunc;
281			break;
282
283		case IPOPT_RA:
284			if (option_len < 4) {
285				ND_PRINT(" [bad length %u]", option_len);
286				break;
287			}
288			ND_TCHECK_1(cp + 3);
289			if (GET_BE_U_2(cp + 2) != 0)
290				ND_PRINT(" value %u", GET_BE_U_2(cp + 2));
291			break;
292
293		case IPOPT_NOP:       /* nothing to print - fall through */
294		case IPOPT_SECURITY:
295		default:
296			break;
297		}
298	}
299	return 0;
300
301trunc:
302	return -1;
303}
304
305#define IP_RES 0x8000
306
307static const struct tok ip_frag_values[] = {
308        { IP_MF,        "+" },
309        { IP_DF,        "DF" },
310	{ IP_RES,       "rsvd" }, /* The RFC3514 evil ;-) bit */
311        { 0,            NULL }
312};
313
314
315/*
316 * print an IP datagram.
317 */
318void
319ip_print(netdissect_options *ndo,
320	 const u_char *bp,
321	 u_int length)
322{
323	const struct ip *ip;
324	u_int off;
325	u_int hlen;
326	u_int len;
327	struct cksum_vec vec[1];
328	uint8_t ip_tos, ip_ttl, ip_proto;
329	uint16_t sum, ip_sum;
330	const char *p_name;
331	int truncated = 0;
332
333	ndo->ndo_protocol = "ip";
334	ip = (const struct ip *)bp;
335	if (IP_V(ip) != 4) { /* print version and fail if != 4 */
336	    if (IP_V(ip) == 6)
337	      ND_PRINT("IP6, wrong link-layer encapsulation");
338	    else
339	      ND_PRINT("IP%u", IP_V(ip));
340	    nd_print_invalid(ndo);
341	    return;
342	}
343	if (!ndo->ndo_eflag)
344		ND_PRINT("IP ");
345
346	ND_TCHECK_SIZE(ip);
347	if (length < sizeof (struct ip)) {
348		ND_PRINT("truncated-ip %u", length);
349		return;
350	}
351	hlen = IP_HL(ip) * 4;
352	if (hlen < sizeof (struct ip)) {
353		ND_PRINT("bad-hlen %u", hlen);
354		return;
355	}
356
357	len = GET_BE_U_2(ip->ip_len);
358	if (length < len)
359		ND_PRINT("truncated-ip - %u bytes missing! ",
360			len - length);
361	if (len < hlen) {
362#ifdef GUESS_TSO
363            if (len) {
364                ND_PRINT("bad-len %u", len);
365                return;
366            }
367            else {
368                /* we guess that it is a TSO send */
369                len = length;
370            }
371#else
372            ND_PRINT("bad-len %u", len);
373            return;
374#endif /* GUESS_TSO */
375	}
376
377	/*
378	 * Cut off the snapshot length to the end of the IP payload.
379	 */
380	if (!nd_push_snaplen(ndo, bp, len)) {
381		(*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
382			"%s: can't push snaplen on buffer stack", __func__);
383	}
384
385	len -= hlen;
386
387	off = GET_BE_U_2(ip->ip_off);
388
389        ip_proto = GET_U_1(ip->ip_p);
390
391        if (ndo->ndo_vflag) {
392            ip_tos = GET_U_1(ip->ip_tos);
393            ND_PRINT("(tos 0x%x", ip_tos);
394            /* ECN bits */
395            switch (ip_tos & 0x03) {
396
397            case 0:
398                break;
399
400            case 1:
401                ND_PRINT(",ECT(1)");
402                break;
403
404            case 2:
405                ND_PRINT(",ECT(0)");
406                break;
407
408            case 3:
409                ND_PRINT(",CE");
410                break;
411            }
412
413            ip_ttl = GET_U_1(ip->ip_ttl);
414            if (ip_ttl >= 1)
415                ND_PRINT(", ttl %u", ip_ttl);
416
417	    /*
418	     * for the firewall guys, print id, offset.
419             * On all but the last stick a "+" in the flags portion.
420	     * For unfragmented datagrams, note the don't fragment flag.
421	     */
422	    ND_PRINT(", id %u, offset %u, flags [%s], proto %s (%u)",
423                         GET_BE_U_2(ip->ip_id),
424                         (off & IP_OFFMASK) * 8,
425                         bittok2str(ip_frag_values, "none", off & (IP_RES|IP_DF|IP_MF)),
426                         tok2str(ipproto_values, "unknown", ip_proto),
427                         ip_proto);
428
429            ND_PRINT(", length %u", GET_BE_U_2(ip->ip_len));
430
431            if ((hlen - sizeof(struct ip)) > 0) {
432                ND_PRINT(", options (");
433                if (ip_optprint(ndo, (const u_char *)(ip + 1),
434                    hlen - sizeof(struct ip)) == -1) {
435                        ND_PRINT(" [truncated-option]");
436			truncated = 1;
437                }
438                ND_PRINT(")");
439            }
440
441	    if (!ndo->ndo_Kflag && (const u_char *)ip + hlen <= ndo->ndo_snapend) {
442	        vec[0].ptr = (const uint8_t *)(const void *)ip;
443	        vec[0].len = hlen;
444	        sum = in_cksum(vec, 1);
445		if (sum != 0) {
446		    ip_sum = GET_BE_U_2(ip->ip_sum);
447		    ND_PRINT(", bad cksum %x (->%x)!", ip_sum,
448			     in_cksum_shouldbe(ip_sum, sum));
449		}
450	    }
451
452	    ND_PRINT(")\n    ");
453	    if (truncated) {
454		ND_PRINT("%s > %s: ",
455			 GET_IPADDR_STRING(ip->ip_src),
456			 GET_IPADDR_STRING(ip->ip_dst));
457		nd_print_trunc(ndo);
458		nd_pop_packet_info(ndo);
459		return;
460	    }
461	}
462
463	/*
464	 * If this is fragment zero, hand it to the next higher
465	 * level protocol.  Let them know whether there are more
466	 * fragments.
467	 */
468	if ((off & IP_OFFMASK) == 0) {
469		uint8_t nh = GET_U_1(ip->ip_p);
470
471		if (nh != IPPROTO_TCP && nh != IPPROTO_UDP &&
472		    nh != IPPROTO_SCTP && nh != IPPROTO_DCCP) {
473			ND_PRINT("%s > %s: ",
474				     GET_IPADDR_STRING(ip->ip_src),
475				     GET_IPADDR_STRING(ip->ip_dst));
476		}
477		/*
478		 * Do a bounds check before calling ip_demux_print().
479		 * At least the header data is required.
480		 */
481		if (!ND_TTEST_LEN((const u_char *)ip, hlen)) {
482			ND_PRINT(" [remaining caplen(%u) < header length(%u)]",
483				 ND_BYTES_AVAILABLE_AFTER((const u_char *)ip),
484				 hlen);
485			nd_trunc_longjmp(ndo);
486		}
487		ip_demux_print(ndo, (const u_char *)ip + hlen, len, 4,
488			       off & IP_MF, GET_U_1(ip->ip_ttl), nh, bp);
489	} else {
490		/*
491		 * Ultra quiet now means that all this stuff should be
492		 * suppressed.
493		 */
494		if (ndo->ndo_qflag > 1) {
495			nd_pop_packet_info(ndo);
496			return;
497		}
498
499		/*
500		 * This isn't the first frag, so we're missing the
501		 * next level protocol header.  print the ip addr
502		 * and the protocol.
503		 */
504		ND_PRINT("%s > %s:", GET_IPADDR_STRING(ip->ip_src),
505		          GET_IPADDR_STRING(ip->ip_dst));
506		if (!ndo->ndo_nflag && (p_name = netdb_protoname(ip_proto)) != NULL)
507			ND_PRINT(" %s", p_name);
508		else
509			ND_PRINT(" ip-proto-%u", ip_proto);
510	}
511	nd_pop_packet_info(ndo);
512	return;
513
514trunc:
515	nd_print_trunc(ndo);
516}
517
518void
519ipN_print(netdissect_options *ndo, const u_char *bp, u_int length)
520{
521	ndo->ndo_protocol = "ipn";
522	if (length < 1) {
523		ND_PRINT("truncated-ip %u", length);
524		return;
525	}
526
527	switch (GET_U_1(bp) & 0xF0) {
528	case 0x40:
529		ip_print(ndo, bp, length);
530		break;
531	case 0x60:
532		ip6_print(ndo, bp, length);
533		break;
534	default:
535		ND_PRINT("unknown ip %u", (GET_U_1(bp) & 0xF0) >> 4);
536		break;
537	}
538}
539