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.14 2024/09/02 16:15:31 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 } else 362 ND_ICHECKMSG_U("total length", len, <, hlen); 363 364 ND_TCHECK_SIZE(ip); 365 /* 366 * Cut off the snapshot length to the end of the IP payload. 367 */ 368 if (!nd_push_snaplen(ndo, bp, len)) { 369 (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, 370 "%s: can't push snaplen on buffer stack", __func__); 371 } 372 373 len -= hlen; 374 375 off = GET_BE_U_2(ip->ip_off); 376 377 ip_proto = GET_U_1(ip->ip_p); 378 379 if (ndo->ndo_vflag) { 380 ip_tos = GET_U_1(ip->ip_tos); 381 ND_PRINT("(tos 0x%x", ip_tos); 382 /* ECN bits */ 383 switch (ip_tos & 0x03) { 384 385 case 0: 386 break; 387 388 case 1: 389 ND_PRINT(",ECT(1)"); 390 break; 391 392 case 2: 393 ND_PRINT(",ECT(0)"); 394 break; 395 396 case 3: 397 ND_PRINT(",CE"); 398 break; 399 } 400 401 ip_ttl = GET_U_1(ip->ip_ttl); 402 if (ip_ttl >= 1) 403 ND_PRINT(", ttl %u", ip_ttl); 404 405 /* 406 * for the firewall guys, print id, offset. 407 * On all but the last stick a "+" in the flags portion. 408 * For unfragmented datagrams, note the don't fragment flag. 409 */ 410 ND_PRINT(", id %u, offset %u, flags [%s], proto %s (%u)", 411 GET_BE_U_2(ip->ip_id), 412 (off & IP_OFFMASK) * 8, 413 bittok2str(ip_frag_values, "none", off & (IP_RES|IP_DF|IP_MF)), 414 tok2str(ipproto_values, "unknown", ip_proto), 415 ip_proto); 416 417 if (presumed_tso) 418 ND_PRINT(", length %u [was 0, presumed TSO]", length); 419 else 420 ND_PRINT(", length %u", GET_BE_U_2(ip->ip_len)); 421 422 if ((hlen - sizeof(struct ip)) > 0) { 423 ND_PRINT(", options ("); 424 if (ip_optprint(ndo, (const u_char *)(ip + 1), 425 hlen - sizeof(struct ip)) == -1) { 426 ND_PRINT(" [truncated-option]"); 427 truncated = 1; 428 } 429 ND_PRINT(")"); 430 } 431 432 if (!ndo->ndo_Kflag && (const u_char *)ip + hlen <= ndo->ndo_snapend) { 433 vec[0].ptr = (const uint8_t *)(const void *)ip; 434 vec[0].len = hlen; 435 sum = in_cksum(vec, 1); 436 if (sum != 0) { 437 ip_sum = GET_BE_U_2(ip->ip_sum); 438 ND_PRINT(", bad cksum %x (->%x)!", ip_sum, 439 in_cksum_shouldbe(ip_sum, sum)); 440 } 441 } 442 443 ND_PRINT(")\n "); 444 if (truncated) { 445 ND_PRINT("%s > %s: ", 446 GET_IPADDR_STRING(ip->ip_src), 447 GET_IPADDR_STRING(ip->ip_dst)); 448 nd_print_trunc(ndo); 449 nd_pop_packet_info(ndo); 450 return; 451 } 452 } 453 454 /* 455 * If this is fragment zero, hand it to the next higher 456 * level protocol. Let them know whether there are more 457 * fragments. 458 */ 459 if ((off & IP_OFFMASK) == 0) { 460 uint8_t nh = GET_U_1(ip->ip_p); 461 462 if (nh != IPPROTO_TCP && nh != IPPROTO_UDP && 463 nh != IPPROTO_SCTP && nh != IPPROTO_DCCP) { 464 ND_PRINT("%s > %s: ", 465 GET_IPADDR_STRING(ip->ip_src), 466 GET_IPADDR_STRING(ip->ip_dst)); 467 } 468 /* 469 * Do a bounds check before calling ip_demux_print(). 470 * At least the header data is required. 471 */ 472 if (!ND_TTEST_LEN((const u_char *)ip, hlen)) { 473 ND_PRINT(" [remaining caplen(%u) < header length(%u)]", 474 ND_BYTES_AVAILABLE_AFTER((const u_char *)ip), 475 hlen); 476 nd_trunc_longjmp(ndo); 477 } 478 ip_demux_print(ndo, (const u_char *)ip + hlen, len, 4, 479 off & IP_MF, GET_U_1(ip->ip_ttl), nh, bp); 480 } else { 481 /* 482 * Ultra quiet now means that all this stuff should be 483 * suppressed. 484 */ 485 if (ndo->ndo_qflag > 1) { 486 nd_pop_packet_info(ndo); 487 return; 488 } 489 490 /* 491 * This isn't the first frag, so we're missing the 492 * next level protocol header. print the ip addr 493 * and the protocol. 494 */ 495 ND_PRINT("%s > %s:", GET_IPADDR_STRING(ip->ip_src), 496 GET_IPADDR_STRING(ip->ip_dst)); 497 if (!ndo->ndo_nflag && (p_name = netdb_protoname(ip_proto)) != NULL) 498 ND_PRINT(" %s", p_name); 499 else 500 ND_PRINT(" ip-proto-%u", ip_proto); 501 } 502 nd_pop_packet_info(ndo); 503 return; 504 505 trunc: 506 nd_print_trunc(ndo); 507 return; 508 509 invalid: 510 nd_print_invalid(ndo); 511 } 512 513 void 514 ipN_print(netdissect_options *ndo, const u_char *bp, u_int length) 515 { 516 ndo->ndo_protocol = "ipn"; 517 if (length < 1) { 518 ND_PRINT("truncated-ip %u", length); 519 return; 520 } 521 522 switch (GET_U_1(bp) & 0xF0) { 523 case 0x40: 524 ip_print(ndo, bp, length); 525 break; 526 case 0x60: 527 ip6_print(ndo, bp, length); 528 break; 529 default: 530 ND_PRINT("unknown ip %u", (GET_U_1(bp) & 0xF0) >> 4); 531 break; 532 } 533 } 534