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