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