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