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