xref: /freebsd/contrib/tcpdump/print-ip.c (revision 7bd6fde3)
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  * $FreeBSD$
22  */
23 
24 #ifndef lint
25 static const char rcsid[] _U_ =
26     "@(#) $Header: /tcpdump/master/tcpdump/print-ip.c,v 1.149.2.2 2005/09/20 06:05:38 guy Exp $ (LBL)";
27 #endif
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include <tcpdump-stdinc.h>
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 
39 #include "addrtoname.h"
40 #include "interface.h"
41 #include "extract.h"			/* must come after interface.h */
42 
43 #include "ip.h"
44 #include "ipproto.h"
45 
46 struct tok ip_option_values[] = {
47     { IPOPT_EOL, "EOL" },
48     { IPOPT_NOP, "NOP" },
49     { IPOPT_TS, "timestamp" },
50     { IPOPT_SECURITY, "security" },
51     { IPOPT_RR, "RR" },
52     { IPOPT_SSRR, "SSRR" },
53     { IPOPT_LSRR, "LSRR" },
54     { IPOPT_RA, "RA" },
55     { 0, NULL }
56 };
57 
58 /*
59  * print the recorded route in an IP RR, LSRR or SSRR option.
60  */
61 static void
62 ip_printroute(register const u_char *cp, u_int length)
63 {
64 	register u_int ptr;
65 	register u_int len;
66 
67 	if (length < 3) {
68 		printf(" [bad length %u]", length);
69 		return;
70 	}
71 	if ((length + 1) & 3)
72 		printf(" [bad length %u]", length);
73 	ptr = cp[2] - 1;
74 	if (ptr < 3 || ((ptr + 1) & 3) || ptr > length + 1)
75 		printf(" [bad ptr %u]", cp[2]);
76 
77 	for (len = 3; len < length; len += 4) {
78 		printf("%s", ipaddr_string(&cp[len]));
79                 if (ptr > len)
80                     printf (", ");
81 	}
82 }
83 
84 /*
85  * If source-routing is present and valid, return the final destination.
86  * Otherwise, return IP destination.
87  *
88  * This is used for UDP and TCP pseudo-header in the checksum
89  * calculation.
90  */
91 u_int32_t
92 ip_finddst(const struct ip *ip)
93 {
94 	int length;
95 	int len;
96 	const u_char *cp;
97 	u_int32_t retval;
98 
99 	cp = (const u_char *)(ip + 1);
100 	length = (IP_HL(ip) << 2) - sizeof(struct ip);
101 
102 	for (; length > 0; cp += len, length -= len) {
103 		int tt;
104 
105 		TCHECK(*cp);
106 		tt = *cp;
107 		if (tt == IPOPT_EOL)
108 			break;
109 		else if (tt == IPOPT_NOP)
110 			len = 1;
111 		else {
112 			TCHECK(cp[1]);
113 			len = cp[1];
114 			if (len < 2)
115 				break;
116 		}
117 		TCHECK2(*cp, len);
118 		switch (tt) {
119 
120 		case IPOPT_SSRR:
121 		case IPOPT_LSRR:
122 			if (len < 7)
123 				break;
124 			memcpy(&retval, cp + len - 4, 4);
125 			return retval;
126 		}
127 	}
128 trunc:
129 	memcpy(&retval, &ip->ip_dst.s_addr, sizeof(u_int32_t));
130 	return retval;
131 }
132 
133 static void
134 ip_printts(register const u_char *cp, u_int length)
135 {
136 	register u_int ptr;
137 	register u_int len;
138 	int hoplen;
139 	const char *type;
140 
141 	if (length < 4) {
142 		printf("[bad length %d]", length);
143 		return;
144 	}
145 	printf(" TS{");
146 	hoplen = ((cp[3]&0xF) != IPOPT_TS_TSONLY) ? 8 : 4;
147 	if ((length - 4) & (hoplen-1))
148 		printf("[bad length %d]", length);
149 	ptr = cp[2] - 1;
150 	len = 0;
151 	if (ptr < 4 || ((ptr - 4) & (hoplen-1)) || ptr > length + 1)
152 		printf("[bad ptr %d]", cp[2]);
153 	switch (cp[3]&0xF) {
154 	case IPOPT_TS_TSONLY:
155 		printf("TSONLY");
156 		break;
157 	case IPOPT_TS_TSANDADDR:
158 		printf("TS+ADDR");
159 		break;
160 	/*
161 	 * prespecified should really be 3, but some ones might send 2
162 	 * instead, and the IPOPT_TS_PRESPEC constant can apparently
163 	 * have both values, so we have to hard-code it here.
164 	 */
165 
166 	case 2:
167 		printf("PRESPEC2.0");
168 		break;
169 	case 3:			/* IPOPT_TS_PRESPEC */
170 		printf("PRESPEC");
171 		break;
172 	default:
173 		printf("[bad ts type %d]", cp[3]&0xF);
174 		goto done;
175 	}
176 
177 	type = " ";
178 	for (len = 4; len < length; len += hoplen) {
179 		if (ptr == len)
180 			type = " ^ ";
181 		printf("%s%d@%s", type, EXTRACT_32BITS(&cp[len+hoplen-4]),
182 		       hoplen!=8 ? "" : ipaddr_string(&cp[len]));
183 		type = " ";
184 	}
185 
186 done:
187 	printf("%s", ptr == len ? " ^ " : "");
188 
189 	if (cp[3]>>4)
190 		printf(" [%d hops not recorded]} ", cp[3]>>4);
191 	else
192 		printf("}");
193 }
194 
195 /*
196  * print IP options.
197  */
198 static void
199 ip_optprint(register const u_char *cp, u_int length)
200 {
201 	register u_int option_len;
202 
203 	for (; length > 0; cp += option_len, length -= option_len) {
204 		u_int option_code;
205 
206 		TCHECK(*cp);
207 		option_code = *cp;
208 
209 		if (option_code == IPOPT_NOP ||
210                     option_code == IPOPT_EOL)
211 			option_len = 1;
212 
213 		else {
214 			TCHECK(cp[1]);
215 			option_len = cp[1];
216 		}
217 
218                 printf("%s (%u) len %u",
219                        tok2str(ip_option_values,"unknown",option_code),
220                        option_code,
221                        option_len);
222 
223                 if (option_len < 2)
224                         return;
225 
226                 TCHECK2(*cp, option_len);
227 
228 		switch (option_code) {
229 		case IPOPT_EOL:
230 			return;
231 
232 		case IPOPT_TS:
233 			ip_printts(cp, option_len);
234 			break;
235 
236 		case IPOPT_RR:       /* fall through */
237 		case IPOPT_SSRR:
238 		case IPOPT_LSRR:
239 			ip_printroute( cp, option_len);
240 			break;
241 
242 		case IPOPT_RA:
243                         TCHECK(cp[3]);
244                         if (EXTRACT_16BITS(&cp[2]) != 0)
245                             printf("value %u", EXTRACT_16BITS(&cp[2]));
246 			break;
247 
248 		case IPOPT_NOP:       /* nothing to print - fall through */
249 		case IPOPT_SECURITY:
250 		default:
251 			break;
252 		}
253 	}
254 	return;
255 
256 trunc:
257 	printf("[|ip]");
258 }
259 
260 /*
261  * compute an IP header checksum.
262  * don't modifiy the packet.
263  */
264 u_short
265 in_cksum(const u_short *addr, register u_int len, int csum)
266 {
267 	int nleft = len;
268 	const u_short *w = addr;
269 	u_short answer;
270 	int sum = csum;
271 
272 	/*
273 	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
274 	 *  we add sequential 16 bit words to it, and at the end, fold
275 	 *  back all the carry bits from the top 16 bits into the lower
276 	 *  16 bits.
277 	 */
278 	while (nleft > 1)  {
279 		sum += *w++;
280 		nleft -= 2;
281 	}
282 	if (nleft == 1)
283 		sum += htons(*(u_char *)w<<8);
284 
285 	/*
286 	 * add back carry outs from top 16 bits to low 16 bits
287 	 */
288 	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
289 	sum += (sum >> 16);			/* add carry */
290 	answer = ~sum;				/* truncate to 16 bits */
291 	return (answer);
292 }
293 
294 /*
295  * Given the host-byte-order value of the checksum field in a packet
296  * header, and the network-byte-order computed checksum of the data
297  * that the checksum covers (including the checksum itself), compute
298  * what the checksum field *should* have been.
299  */
300 u_int16_t
301 in_cksum_shouldbe(u_int16_t sum, u_int16_t computed_sum)
302 {
303 	u_int32_t shouldbe;
304 
305 	/*
306 	 * The value that should have gone into the checksum field
307 	 * is the negative of the value gotten by summing up everything
308 	 * *but* the checksum field.
309 	 *
310 	 * We can compute that by subtracting the value of the checksum
311 	 * field from the sum of all the data in the packet, and then
312 	 * computing the negative of that value.
313 	 *
314 	 * "sum" is the value of the checksum field, and "computed_sum"
315 	 * is the negative of the sum of all the data in the packets,
316 	 * so that's -(-computed_sum - sum), or (sum + computed_sum).
317 	 *
318 	 * All the arithmetic in question is one's complement, so the
319 	 * addition must include an end-around carry; we do this by
320 	 * doing the arithmetic in 32 bits (with no sign-extension),
321 	 * and then adding the upper 16 bits of the sum, which contain
322 	 * the carry, to the lower 16 bits of the sum, and then do it
323 	 * again in case *that* sum produced a carry.
324 	 *
325 	 * As RFC 1071 notes, the checksum can be computed without
326 	 * byte-swapping the 16-bit words; summing 16-bit words
327 	 * on a big-endian machine gives a big-endian checksum, which
328 	 * can be directly stuffed into the big-endian checksum fields
329 	 * in protocol headers, and summing words on a little-endian
330 	 * machine gives a little-endian checksum, which must be
331 	 * byte-swapped before being stuffed into a big-endian checksum
332 	 * field.
333 	 *
334 	 * "computed_sum" is a network-byte-order value, so we must put
335 	 * it in host byte order before subtracting it from the
336 	 * host-byte-order value from the header; the adjusted checksum
337 	 * will be in host byte order, which is what we'll return.
338 	 */
339 	shouldbe = sum;
340 	shouldbe += ntohs(computed_sum);
341 	shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
342 	shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
343 	return shouldbe;
344 }
345 
346 #ifndef IP_MF
347 #define IP_MF 0x2000
348 #endif /* IP_MF */
349 #ifndef IP_DF
350 #define IP_DF 0x4000
351 #endif /* IP_DF */
352 #define IP_RES 0x8000
353 
354 static struct tok ip_frag_values[] = {
355         { IP_MF,        "+" },
356         { IP_DF,        "DF" },
357 	{ IP_RES,       "rsvd" }, /* The RFC3514 evil ;-) bit */
358         { 0,            NULL }
359 };
360 
361 struct ip_print_demux_state {
362 	const struct ip *ip;
363 	const u_char *cp;
364 	u_int   len, off;
365 	u_char  nh;
366 	int     advance;
367 };
368 
369 static void
370 ip_print_demux(netdissect_options *ndo,
371 	       struct ip_print_demux_state *ipds)
372 {
373 	struct protoent *proto;
374 
375 again:
376 	switch (ipds->nh) {
377 
378 	case IPPROTO_AH:
379 		ipds->nh = *ipds->cp;
380 		ipds->advance = ah_print(ipds->cp);
381 		if (ipds->advance <= 0)
382 			break;
383 		ipds->cp += ipds->advance;
384 		ipds->len -= ipds->advance;
385 		goto again;
386 
387 	case IPPROTO_ESP:
388 	{
389 		int enh, padlen;
390 		ipds->advance = esp_print(ndo, ipds->cp, ipds->len,
391 				    (const u_char *)ipds->ip,
392 				    &enh, &padlen);
393 		if (ipds->advance <= 0)
394 			break;
395 		ipds->cp += ipds->advance;
396 		ipds->len -= ipds->advance + padlen;
397 		ipds->nh = enh & 0xff;
398 		goto again;
399 	}
400 
401 	case IPPROTO_IPCOMP:
402 	{
403 		int enh;
404 		ipds->advance = ipcomp_print(ipds->cp, &enh);
405 		if (ipds->advance <= 0)
406 			break;
407 		ipds->cp += ipds->advance;
408 		ipds->len -= ipds->advance;
409 		ipds->nh = enh & 0xff;
410 		goto again;
411 	}
412 
413 	case IPPROTO_SCTP:
414 		sctp_print(ipds->cp, (const u_char *)ipds->ip, ipds->len);
415 		break;
416 
417 	case IPPROTO_DCCP:
418 		dccp_print(ipds->cp, (const u_char *)ipds->ip, ipds->len);
419 		break;
420 
421 	case IPPROTO_TCP:
422 		tcp_print(ipds->cp, ipds->len, (const u_char *)ipds->ip,
423 			  (ipds->off &~ 0x6000));
424 		break;
425 
426 	case IPPROTO_UDP:
427 		udp_print(ipds->cp, ipds->len, (const u_char *)ipds->ip,
428 			  (ipds->off &~ 0x6000));
429 		break;
430 
431 	case IPPROTO_ICMP:
432 		/* pass on the MF bit plus the offset to detect fragments */
433 		icmp_print(ipds->cp, ipds->len, (const u_char *)ipds->ip,
434 			   (ipds->off & 0x3fff));
435 		break;
436 
437 	case IPPROTO_PIGP:
438 		/*
439 		 * XXX - the current IANA protocol number assignments
440 		 * page lists 9 as "any private interior gateway
441 		 * (used by Cisco for their IGRP)" and 88 as
442 		 * "EIGRP" from Cisco.
443 		 *
444 		 * Recent BSD <netinet/in.h> headers define
445 		 * IP_PROTO_PIGP as 9 and IP_PROTO_IGRP as 88.
446 		 * We define IP_PROTO_PIGP as 9 and
447 		 * IP_PROTO_EIGRP as 88; those names better
448 		 * match was the current protocol number
449 		 * assignments say.
450 		 */
451 		igrp_print(ipds->cp, ipds->len, (const u_char *)ipds->ip);
452 		break;
453 
454 	case IPPROTO_EIGRP:
455 		eigrp_print(ipds->cp, ipds->len);
456 		break;
457 
458 	case IPPROTO_ND:
459 		ND_PRINT((ndo, " nd %d", ipds->len));
460 		break;
461 
462 	case IPPROTO_EGP:
463 		egp_print(ipds->cp, ipds->len);
464 		break;
465 
466 	case IPPROTO_OSPF:
467 		ospf_print(ipds->cp, ipds->len, (const u_char *)ipds->ip);
468 		break;
469 
470 	case IPPROTO_IGMP:
471 		igmp_print(ipds->cp, ipds->len);
472 		break;
473 
474 	case IPPROTO_IPV4:
475 		/* DVMRP multicast tunnel (ip-in-ip encapsulation) */
476 		ip_print(gndo, ipds->cp, ipds->len);
477 		if (! vflag) {
478 			ND_PRINT((ndo, " (ipip-proto-4)"));
479 			return;
480 		}
481 		break;
482 
483 #ifdef INET6
484 	case IPPROTO_IPV6:
485 		/* ip6-in-ip encapsulation */
486 		ip6_print(ipds->cp, ipds->len);
487 		break;
488 #endif /*INET6*/
489 
490 	case IPPROTO_RSVP:
491 		rsvp_print(ipds->cp, ipds->len);
492 		break;
493 
494 	case IPPROTO_GRE:
495 		/* do it */
496 		gre_print(ipds->cp, ipds->len);
497 		break;
498 
499 	case IPPROTO_MOBILE:
500 		mobile_print(ipds->cp, ipds->len);
501 		break;
502 
503 	case IPPROTO_PIM:
504 		pim_print(ipds->cp,  ipds->len);
505 		break;
506 
507 	case IPPROTO_VRRP:
508 		vrrp_print(ipds->cp, ipds->len, ipds->ip->ip_ttl);
509 		break;
510 
511 	case IPPROTO_PGM:
512 		pgm_print(ipds->cp, ipds->len, (const u_char *)ipds->ip);
513 		break;
514 
515 	default:
516 		if ((proto = getprotobynumber(ipds->nh)) != NULL)
517 			ND_PRINT((ndo, " %s", proto->p_name));
518 		else
519 			ND_PRINT((ndo, " ip-proto-%d", ipds->nh));
520 		ND_PRINT((ndo, " %d", ipds->len));
521 		break;
522 	}
523 }
524 
525 void
526 ip_print_inner(netdissect_options *ndo,
527 	       const u_char *bp,
528 	       u_int length, u_int nh,
529 	       const u_char *bp2)
530 {
531 	struct ip_print_demux_state  ipd;
532 
533 	ipd.ip = (const struct ip *)bp2;
534 	ipd.cp = bp;
535 	ipd.len  = length;
536 	ipd.off  = 0;
537 	ipd.nh   = nh;
538 	ipd.advance = 0;
539 
540 	ip_print_demux(ndo, &ipd);
541 }
542 
543 
544 /*
545  * print an IP datagram.
546  */
547 void
548 ip_print(netdissect_options *ndo,
549 	 const u_char *bp,
550 	 u_int length)
551 {
552 	struct ip_print_demux_state  ipd;
553 	struct ip_print_demux_state *ipds=&ipd;
554 	const u_char *ipend;
555 	u_int hlen;
556 	u_int16_t sum, ip_sum;
557 	struct protoent *proto;
558 
559 	ipds->ip = (const struct ip *)bp;
560 	if (IP_V(ipds->ip) != 4) { /* print version if != 4 */
561 	    printf("IP%u ", IP_V(ipds->ip));
562 	    if (IP_V(ipds->ip) == 6)
563 		printf(", wrong link-layer encapsulation");
564 	}
565         else if (!eflag)
566 	    printf("IP ");
567 
568 	if ((u_char *)(ipds->ip + 1) > snapend) {
569 		printf("[|ip]");
570 		return;
571 	}
572 	if (length < sizeof (struct ip)) {
573 		(void)printf("truncated-ip %u", length);
574 		return;
575 	}
576 	hlen = IP_HL(ipds->ip) * 4;
577 	if (hlen < sizeof (struct ip)) {
578 		(void)printf("bad-hlen %u", hlen);
579 		return;
580 	}
581 
582 	ipds->len = EXTRACT_16BITS(&ipds->ip->ip_len);
583 	if (length < ipds->len)
584 		(void)printf("truncated-ip - %u bytes missing! ",
585 			ipds->len - length);
586 	if (ipds->len < hlen) {
587 #ifdef GUESS_TSO
588             if (ipds->len) {
589                 (void)printf("bad-len %u", ipds->len);
590                 return;
591             }
592             else {
593                 /* we guess that it is a TSO send */
594                 ipds->len = length;
595             }
596 #else
597             (void)printf("bad-len %u", ipds->len);
598             return;
599 #endif /* GUESS_TSO */
600 	}
601 
602 	/*
603 	 * Cut off the snapshot length to the end of the IP payload.
604 	 */
605 	ipend = bp + ipds->len;
606 	if (ipend < snapend)
607 		snapend = ipend;
608 
609 	ipds->len -= hlen;
610 
611 	ipds->off = EXTRACT_16BITS(&ipds->ip->ip_off);
612 
613         if (vflag) {
614             (void)printf("(tos 0x%x", (int)ipds->ip->ip_tos);
615             /* ECN bits */
616             if (ipds->ip->ip_tos & 0x03) {
617                 switch (ipds->ip->ip_tos & 0x03) {
618                 case 1:
619                     (void)printf(",ECT(1)");
620                     break;
621                 case 2:
622                     (void)printf(",ECT(0)");
623                     break;
624                 case 3:
625                     (void)printf(",CE");
626                 }
627             }
628 
629             if (ipds->ip->ip_ttl >= 1)
630                 (void)printf(", ttl %3u", ipds->ip->ip_ttl);
631 
632 	    /*
633 	     * for the firewall guys, print id, offset.
634              * On all but the last stick a "+" in the flags portion.
635 	     * For unfragmented datagrams, note the don't fragment flag.
636 	     */
637 
638 	    (void)printf(", id %u, offset %u, flags [%s], proto: %s (%u)",
639                          EXTRACT_16BITS(&ipds->ip->ip_id),
640                          (ipds->off & 0x1fff) * 8,
641                          bittok2str(ip_frag_values, "none", ipds->off&0xe000 ),
642                          tok2str(ipproto_values,"unknown",ipds->ip->ip_p),
643                          ipds->ip->ip_p);
644 
645             (void)printf(", length: %u", EXTRACT_16BITS(&ipds->ip->ip_len));
646 
647             if ((hlen - sizeof(struct ip)) > 0) {
648                 printf(", options ( ");
649                 ip_optprint((u_char *)(ipds->ip + 1), hlen - sizeof(struct ip));
650                 printf(" )");
651             }
652 
653 	    if ((u_char *)ipds->ip + hlen <= snapend) {
654 	        sum = in_cksum((const u_short *)ipds->ip, hlen, 0);
655 		if (sum != 0) {
656 		    ip_sum = EXTRACT_16BITS(&ipds->ip->ip_sum);
657 		    (void)printf(", bad cksum %x (->%x)!", ip_sum,
658 			     in_cksum_shouldbe(ip_sum, sum));
659 		}
660 	    }
661 
662             printf(") ");
663 	}
664 
665 	/*
666 	 * If this is fragment zero, hand it to the next higher
667 	 * level protocol.
668 	 */
669 	if ((ipds->off & 0x1fff) == 0) {
670 		ipds->cp = (const u_char *)ipds->ip + hlen;
671 		ipds->nh = ipds->ip->ip_p;
672 
673 		if (ipds->nh != IPPROTO_TCP && ipds->nh != IPPROTO_UDP &&
674 		    ipds->nh != IPPROTO_SCTP && ipds->nh != IPPROTO_DCCP) {
675 			(void)printf("%s > %s: ",
676 				     ipaddr_string(&ipds->ip->ip_src),
677 				     ipaddr_string(&ipds->ip->ip_dst));
678 		}
679 		ip_print_demux(ndo, ipds);
680 	} else {
681 	    /* Ultra quiet now means that all this stuff should be suppressed */
682 	    if (qflag > 1) return;
683 
684 	    /*
685 	     * if this isn't the first frag, we're missing the
686 	     * next level protocol header.  print the ip addr
687 	     * and the protocol.
688 	     */
689 	    if (ipds->off & 0x1fff) {
690 	        (void)printf("%s > %s:", ipaddr_string(&ipds->ip->ip_src),
691 			     ipaddr_string(&ipds->ip->ip_dst));
692 		if ((proto = getprotobynumber(ipds->ip->ip_p)) != NULL)
693 		    (void)printf(" %s", proto->p_name);
694 		else
695 		    (void)printf(" ip-proto-%d", ipds->ip->ip_p);
696 	    }
697 	}
698 }
699 
700 void
701 ipN_print(register const u_char *bp, register u_int length)
702 {
703 	struct ip *ip, hdr;
704 
705 	ip = (struct ip *)bp;
706 	if (length < 4) {
707 		(void)printf("truncated-ip %d", length);
708 		return;
709 	}
710 	memcpy (&hdr, (char *)ip, 4);
711 	switch (IP_V(&hdr)) {
712 	case 4:
713 		ip_print (gndo, bp, length);
714 		return;
715 #ifdef INET6
716 	case 6:
717 		ip6_print (bp, length);
718 		return;
719 #endif
720 	default:
721 		(void)printf("unknown ip %d", IP_V(&hdr));
722 		return;
723 	}
724 }
725 
726 /*
727  * Local Variables:
728  * c-style: whitesmith
729  * c-basic-offset: 8
730  * End:
731  */
732 
733 
734