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