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