1 /* 2 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994 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-ip6.c,v 1.11 2026/03/19 00:05:13 christos Exp $"); 25 #endif 26 27 /* \summary: IPv6 printer */ 28 29 #include <config.h> 30 31 #include "netdissect-stdinc.h" 32 33 #include <string.h> 34 35 #include "netdissect.h" 36 #include "addrtoname.h" 37 #include "extract.h" 38 39 #include "ip6.h" 40 #include "ipproto.h" 41 42 /* 43 * If routing headers are presend and valid, set dst to the final destination. 44 * Otherwise, set it to the IPv6 destination. 45 * 46 * This is used for UDP and TCP pseudo-header in the checksum 47 * calculation. 48 */ 49 static void 50 ip6_finddst(netdissect_options *ndo, nd_ipv6 *dst, 51 const struct ip6_hdr *ip6) 52 { 53 const u_char *cp; 54 u_int advance; 55 u_int nh; 56 const void *dst_addr; 57 const struct ip6_rthdr *dp; 58 const struct ip6_rthdr0 *dp0; 59 const struct ip6_srh *srh; 60 const u_char *p; 61 int i, len; 62 63 cp = (const u_char *)ip6; 64 advance = sizeof(struct ip6_hdr); 65 nh = GET_U_1(ip6->ip6_nxt); 66 dst_addr = (const void *)ip6->ip6_dst; 67 68 while (cp < ndo->ndo_snapend) { 69 cp += advance; 70 71 switch (nh) { 72 73 case IPPROTO_HOPOPTS: 74 case IPPROTO_DSTOPTS: 75 case IPPROTO_MOBILITY_OLD: 76 case IPPROTO_MOBILITY: 77 /* 78 * These have a header length byte, following 79 * the next header byte, giving the length of 80 * the header, in units of 8 octets, excluding 81 * the first 8 octets. 82 */ 83 advance = (GET_U_1(cp + 1) + 1) << 3; 84 nh = GET_U_1(cp); 85 break; 86 87 case IPPROTO_FRAGMENT: 88 /* 89 * The byte following the next header byte is 90 * marked as reserved, and the header is always 91 * the same size. 92 */ 93 advance = sizeof(struct ip6_frag); 94 nh = GET_U_1(cp); 95 break; 96 97 case IPPROTO_ROUTING: 98 /* 99 * OK, we found it. 100 */ 101 dp = (const struct ip6_rthdr *)cp; 102 ND_TCHECK_SIZE(dp); 103 len = GET_U_1(dp->ip6r_len); 104 switch (GET_U_1(dp->ip6r_type)) { 105 106 case IPV6_RTHDR_TYPE_0: 107 case IPV6_RTHDR_TYPE_2: /* Mobile IPv6 ID-20 */ 108 dp0 = (const struct ip6_rthdr0 *)dp; 109 if (len % 2 == 1) 110 goto trunc; 111 len >>= 1; 112 p = (const u_char *) dp0->ip6r0_addr; 113 for (i = 0; i < len; i++) { 114 ND_TCHECK_16(p); 115 dst_addr = (const void *)p; 116 p += 16; 117 } 118 break; 119 case IPV6_RTHDR_TYPE_4: 120 /* IPv6 Segment Routing Header (SRH) */ 121 srh = (const struct ip6_srh *)dp; 122 if (len % 2 == 1) 123 goto trunc; 124 p = (const u_char *) srh->srh_segments; 125 /* 126 * The list of segments are encoded in the reverse order. 127 * Accordingly, the final DA is encoded in srh_segments[0] 128 */ 129 ND_TCHECK_16(p); 130 dst_addr = (const void *)p; 131 break; 132 133 default: 134 break; 135 } 136 137 /* 138 * Only one routing header to a customer. 139 */ 140 goto done; 141 142 case IPPROTO_AH: 143 case IPPROTO_ESP: 144 case IPPROTO_IPCOMP: 145 default: 146 /* 147 * AH and ESP are, in the RFCs that describe them, 148 * described as being "viewed as an end-to-end 149 * payload" "in the IPv6 context, so that they 150 * "should appear after hop-by-hop, routing, and 151 * fragmentation extension headers". We assume 152 * that's the case, and stop as soon as we see 153 * one. (We can't handle an ESP header in 154 * the general case anyway, as its length depends 155 * on the encryption algorithm.) 156 * 157 * IPComp is also "viewed as an end-to-end 158 * payload" "in the IPv6 context". 159 * 160 * All other protocols are assumed to be the final 161 * protocol. 162 */ 163 goto done; 164 } 165 } 166 167 done: 168 trunc: 169 GET_CPY_BYTES(dst, dst_addr, sizeof(nd_ipv6)); 170 } 171 172 /* 173 * Compute a V6-style checksum by building a pseudoheader. 174 */ 175 uint16_t 176 nextproto6_cksum(netdissect_options *ndo, 177 const struct ip6_hdr *ip6, const uint8_t *data, 178 u_int len, u_int covlen, uint8_t next_proto) 179 { 180 struct { 181 nd_ipv6 ph_src; 182 nd_ipv6 ph_dst; 183 uint32_t ph_len; 184 uint8_t ph_zero[3]; 185 uint8_t ph_nxt; 186 } ph; 187 struct cksum_vec vec[2]; 188 u_int nh; 189 190 /* pseudo-header */ 191 memset(&ph, 0, sizeof(ph)); 192 GET_CPY_BYTES(&ph.ph_src, ip6->ip6_src, sizeof(nd_ipv6)); 193 nh = GET_U_1(ip6->ip6_nxt); 194 switch (nh) { 195 196 case IPPROTO_HOPOPTS: 197 case IPPROTO_DSTOPTS: 198 case IPPROTO_MOBILITY_OLD: 199 case IPPROTO_MOBILITY: 200 case IPPROTO_FRAGMENT: 201 case IPPROTO_ROUTING: 202 /* 203 * The next header is either a routing header or a header 204 * after which there might be a routing header, so scan 205 * for a routing header. 206 */ 207 ip6_finddst(ndo, &ph.ph_dst, ip6); 208 break; 209 210 default: 211 GET_CPY_BYTES(&ph.ph_dst, ip6->ip6_dst, sizeof(nd_ipv6)); 212 break; 213 } 214 ph.ph_len = htonl(len); 215 ph.ph_nxt = next_proto; 216 217 vec[0].ptr = (const uint8_t *)(void *)&ph; 218 vec[0].len = sizeof(ph); 219 vec[1].ptr = data; 220 vec[1].len = covlen; 221 222 return in_cksum(vec, 2); 223 } 224 225 /* 226 * print an IP6 datagram. 227 */ 228 void 229 ip6_print(netdissect_options *ndo, const u_char *bp, u_int length) 230 { 231 const struct ip6_hdr *ip6; 232 int advance; 233 u_int len; 234 u_int total_advance; 235 const u_char *cp; 236 uint32_t payload_len; 237 uint8_t ph, nh; 238 int fragmented = 0; 239 u_int flow; 240 int found_extension_header; 241 int found_jumbo; 242 int found_hbh; 243 244 ndo->ndo_protocol = "ip6"; 245 ip6 = (const struct ip6_hdr *)bp; 246 247 if (!ndo->ndo_eflag) { 248 nd_print_protocol_caps(ndo); 249 ND_PRINT(" "); 250 } 251 252 ND_ICHECK_ZU(length, <, sizeof (struct ip6_hdr)); 253 ND_ICHECKMSG_U("version", IP6_VERSION(ip6), !=, 6); 254 255 payload_len = GET_BE_U_2(ip6->ip6_plen); 256 /* 257 * RFC 1883 says: 258 * 259 * The Payload Length field in the IPv6 header must be set to zero 260 * in every packet that carries the Jumbo Payload option. If a 261 * packet is received with a valid Jumbo Payload option present and 262 * a non-zero IPv6 Payload Length field, an ICMP Parameter Problem 263 * message, Code 0, should be sent to the packet's source, pointing 264 * to the Option Type field of the Jumbo Payload option. 265 * 266 * Later versions of the IPv6 spec don't discuss the Jumbo Payload 267 * option. 268 * 269 * If the payload length is 0, we temporarily just set the total 270 * length to the remaining data in the packet (which, for Ethernet, 271 * could include frame padding, but if it's a Jumbo Payload frame, 272 * it shouldn't even be sendable over Ethernet, so we don't worry 273 * about that), so we can process the extension headers in order 274 * to *find* a Jumbo Payload hop-by-hop option and, when we've 275 * processed all the extension headers, check whether we found 276 * a Jumbo Payload option, and fail if we haven't. 277 */ 278 if (payload_len != 0) { 279 len = payload_len + sizeof(struct ip6_hdr); 280 if (len > length) { 281 ND_PRINT("[header+payload length %u > length %u]", 282 len, length); 283 nd_print_invalid(ndo); 284 ND_PRINT(" "); 285 } 286 } else 287 len = length + sizeof(struct ip6_hdr); 288 289 ph = 255; 290 nh = GET_U_1(ip6->ip6_nxt); 291 if (ndo->ndo_vflag) { 292 flow = GET_BE_U_4(ip6->ip6_flow); 293 ND_PRINT("("); 294 /* RFC 2460 */ 295 if (flow & 0x0ff00000) 296 ND_PRINT("class 0x%02x, ", (flow & 0x0ff00000) >> 20); 297 if (flow & 0x000fffff) 298 ND_PRINT("flowlabel 0x%05x, ", flow & 0x000fffff); 299 300 ND_PRINT("hlim %u, next-header %s (%u), payload length %u) ", 301 GET_U_1(ip6->ip6_hlim), 302 tok2str(ipproto_values,"unknown",nh), 303 nh, 304 payload_len); 305 } 306 ND_TCHECK_SIZE(ip6); 307 308 /* 309 * Cut off the snapshot length to the end of the IP payload 310 * or the end of the data in which it's contained, whichever 311 * comes first. 312 */ 313 if (!nd_push_snaplen(ndo, bp, ND_MIN(length, len))) { 314 (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, 315 "%s: can't push snaplen on buffer stack", __func__); 316 } 317 318 cp = (const u_char *)ip6; 319 advance = sizeof(struct ip6_hdr); 320 total_advance = 0; 321 /* Process extension headers */ 322 found_extension_header = 0; 323 found_jumbo = 0; 324 found_hbh = 0; 325 while (cp < ndo->ndo_snapend && advance > 0) { 326 if (len < (u_int)advance) 327 goto trunc; 328 cp += advance; 329 len -= advance; 330 total_advance += advance; 331 332 if (cp == (const u_char *)(ip6 + 1) && 333 nh != IPPROTO_TCP && nh != IPPROTO_UDP && 334 nh != IPPROTO_DCCP && nh != IPPROTO_SCTP) { 335 ND_PRINT("%s > %s: ", GET_IP6ADDR_STRING(ip6->ip6_src), 336 GET_IP6ADDR_STRING(ip6->ip6_dst)); 337 } 338 339 switch (nh) { 340 341 case IPPROTO_HOPOPTS: 342 /* 343 * The Hop-by-Hop Options header, when present, 344 * must immediately follow the IPv6 header (RFC 8200) 345 */ 346 if (found_hbh == 1) { 347 ND_PRINT("[The Hop-by-Hop Options header was already found]"); 348 nd_print_invalid(ndo); 349 return; 350 } 351 if (ph != 255) { 352 ND_PRINT("[The Hop-by-Hop Options header don't follow the IPv6 header]"); 353 nd_print_invalid(ndo); 354 return; 355 } 356 advance = hbhopt_process(ndo, cp, &found_jumbo, &payload_len); 357 if (payload_len == 0 && found_jumbo == 0) { 358 ND_PRINT("[No valid Jumbo Payload Hop-by-Hop option found]"); 359 nd_print_invalid(ndo); 360 return; 361 } 362 if (advance < 0) { 363 nd_pop_packet_info(ndo); 364 return; 365 } 366 found_extension_header = 1; 367 found_hbh = 1; 368 nh = GET_U_1(cp); 369 break; 370 371 case IPPROTO_DSTOPTS: 372 advance = dstopt_process(ndo, cp); 373 if (advance < 0) { 374 nd_pop_packet_info(ndo); 375 return; 376 } 377 found_extension_header = 1; 378 nh = GET_U_1(cp); 379 break; 380 381 case IPPROTO_FRAGMENT: 382 advance = frag6_print(ndo, cp, (const u_char *)ip6); 383 if (advance < 0 || ndo->ndo_snapend <= cp + advance) { 384 nd_pop_packet_info(ndo); 385 return; 386 } 387 found_extension_header = 1; 388 nh = GET_U_1(cp); 389 fragmented = 1; 390 break; 391 392 case IPPROTO_MOBILITY_OLD: 393 case IPPROTO_MOBILITY: 394 /* 395 * RFC 3775 says that 396 * the next header field in a mobility header 397 * should be IPPROTO_NONE, but speaks of 398 * the possibility of a future extension in 399 * which payload can be piggybacked atop a 400 * mobility header. 401 */ 402 advance = mobility_print(ndo, cp, (const u_char *)ip6); 403 if (advance < 0) { 404 nd_pop_packet_info(ndo); 405 return; 406 } 407 found_extension_header = 1; 408 nh = GET_U_1(cp); 409 nd_pop_packet_info(ndo); 410 return; 411 412 case IPPROTO_ROUTING: 413 ND_TCHECK_1(cp); 414 advance = rt6_print(ndo, cp, (const u_char *)ip6); 415 if (advance < 0) { 416 nd_pop_packet_info(ndo); 417 return; 418 } 419 found_extension_header = 1; 420 nh = GET_U_1(cp); 421 break; 422 423 default: 424 /* 425 * Not an extension header; hand off to the 426 * IP protocol demuxer. 427 */ 428 if (found_jumbo) { 429 /* 430 * We saw a Jumbo Payload option. 431 * Set the length to the payload length 432 * plus the IPv6 header length, and 433 * change the snapshot length accordingly. 434 * 435 * But make sure it's not shorter than 436 * the total number of bytes we've 437 * processed so far. 438 */ 439 len = payload_len + sizeof(struct ip6_hdr); 440 if (len < total_advance) 441 goto trunc; 442 if (len > length) { 443 ND_PRINT("[header+payload length %u > length %u]", 444 len, length); 445 nd_print_invalid(ndo); 446 ND_PRINT(" "); 447 } 448 nd_change_snaplen(ndo, bp, len); 449 450 /* 451 * Now subtract the length of the IPv6 452 * header plus extension headers to get 453 * the payload length. 454 */ 455 len -= total_advance; 456 } else { 457 /* 458 * We didn't see a Jumbo Payload option; 459 * was the payload length zero? 460 */ 461 if (payload_len == 0) { 462 /* 463 * Yes. If we found an extension 464 * header, treat that as a truncated 465 * packet header, as there was 466 * no payload to contain an 467 * extension header. 468 */ 469 if (found_extension_header) 470 goto trunc; 471 472 /* 473 * OK, we didn't see any extension 474 * header, but that means we have 475 * no payload, so set the length 476 * to the IPv6 header length, 477 * and change the snapshot length 478 * accordingly. 479 */ 480 len = sizeof(struct ip6_hdr); 481 nd_change_snaplen(ndo, bp, len); 482 483 /* 484 * Now subtract the length of 485 * the IPv6 header plus extension 486 * headers (there weren't any, so 487 * that's just the IPv6 header 488 * length) to get the payload length. 489 */ 490 len -= total_advance; 491 } 492 } 493 ip_demux_print(ndo, cp, len, 6, fragmented, 494 GET_U_1(ip6->ip6_hlim), nh, bp); 495 nd_pop_packet_info(ndo); 496 return; 497 } 498 ph = nh; 499 500 /* ndo_protocol reassignment after xxx_print() calls */ 501 ndo->ndo_protocol = "ip6"; 502 } 503 504 nd_pop_packet_info(ndo); 505 return; 506 trunc: 507 nd_print_trunc(ndo); 508 return; 509 510 invalid: 511 nd_print_invalid(ndo); 512 } 513