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