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