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.10 2024/09/02 16:15:31 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 */ 311 if (!nd_push_snaplen(ndo, bp, len)) { 312 (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, 313 "%s: can't push snaplen on buffer stack", __func__); 314 } 315 316 cp = (const u_char *)ip6; 317 advance = sizeof(struct ip6_hdr); 318 total_advance = 0; 319 /* Process extension headers */ 320 found_extension_header = 0; 321 found_jumbo = 0; 322 found_hbh = 0; 323 while (cp < ndo->ndo_snapend && advance > 0) { 324 if (len < (u_int)advance) 325 goto trunc; 326 cp += advance; 327 len -= advance; 328 total_advance += advance; 329 330 if (cp == (const u_char *)(ip6 + 1) && 331 nh != IPPROTO_TCP && nh != IPPROTO_UDP && 332 nh != IPPROTO_DCCP && nh != IPPROTO_SCTP) { 333 ND_PRINT("%s > %s: ", GET_IP6ADDR_STRING(ip6->ip6_src), 334 GET_IP6ADDR_STRING(ip6->ip6_dst)); 335 } 336 337 switch (nh) { 338 339 case IPPROTO_HOPOPTS: 340 /* 341 * The Hop-by-Hop Options header, when present, 342 * must immediately follow the IPv6 header (RFC 8200) 343 */ 344 if (found_hbh == 1) { 345 ND_PRINT("[The Hop-by-Hop Options header was already found]"); 346 nd_print_invalid(ndo); 347 return; 348 } 349 if (ph != 255) { 350 ND_PRINT("[The Hop-by-Hop Options header don't follow the IPv6 header]"); 351 nd_print_invalid(ndo); 352 return; 353 } 354 advance = hbhopt_process(ndo, cp, &found_jumbo, &payload_len); 355 if (payload_len == 0 && found_jumbo == 0) { 356 ND_PRINT("[No valid Jumbo Payload Hop-by-Hop option found]"); 357 nd_print_invalid(ndo); 358 return; 359 } 360 if (advance < 0) { 361 nd_pop_packet_info(ndo); 362 return; 363 } 364 found_extension_header = 1; 365 found_hbh = 1; 366 nh = GET_U_1(cp); 367 break; 368 369 case IPPROTO_DSTOPTS: 370 advance = dstopt_process(ndo, cp); 371 if (advance < 0) { 372 nd_pop_packet_info(ndo); 373 return; 374 } 375 found_extension_header = 1; 376 nh = GET_U_1(cp); 377 break; 378 379 case IPPROTO_FRAGMENT: 380 advance = frag6_print(ndo, cp, (const u_char *)ip6); 381 if (advance < 0 || ndo->ndo_snapend <= cp + advance) { 382 nd_pop_packet_info(ndo); 383 return; 384 } 385 found_extension_header = 1; 386 nh = GET_U_1(cp); 387 fragmented = 1; 388 break; 389 390 case IPPROTO_MOBILITY_OLD: 391 case IPPROTO_MOBILITY: 392 /* 393 * RFC 3775 says that 394 * the next header field in a mobility header 395 * should be IPPROTO_NONE, but speaks of 396 * the possibility of a future extension in 397 * which payload can be piggybacked atop a 398 * mobility header. 399 */ 400 advance = mobility_print(ndo, cp, (const u_char *)ip6); 401 if (advance < 0) { 402 nd_pop_packet_info(ndo); 403 return; 404 } 405 found_extension_header = 1; 406 nh = GET_U_1(cp); 407 nd_pop_packet_info(ndo); 408 return; 409 410 case IPPROTO_ROUTING: 411 ND_TCHECK_1(cp); 412 advance = rt6_print(ndo, cp, (const u_char *)ip6); 413 if (advance < 0) { 414 nd_pop_packet_info(ndo); 415 return; 416 } 417 found_extension_header = 1; 418 nh = GET_U_1(cp); 419 break; 420 421 default: 422 /* 423 * Not an extension header; hand off to the 424 * IP protocol demuxer. 425 */ 426 if (found_jumbo) { 427 /* 428 * We saw a Jumbo Payload option. 429 * Set the length to the payload length 430 * plus the IPv6 header length, and 431 * change the snapshot length accordingly. 432 * 433 * But make sure it's not shorter than 434 * the total number of bytes we've 435 * processed so far. 436 */ 437 len = payload_len + sizeof(struct ip6_hdr); 438 if (len < total_advance) 439 goto trunc; 440 if (len > length) { 441 ND_PRINT("[header+payload length %u > length %u]", 442 len, length); 443 nd_print_invalid(ndo); 444 ND_PRINT(" "); 445 } 446 nd_change_snaplen(ndo, bp, len); 447 448 /* 449 * Now subtract the length of the IPv6 450 * header plus extension headers to get 451 * the payload length. 452 */ 453 len -= total_advance; 454 } else { 455 /* 456 * We didn't see a Jumbo Payload option; 457 * was the payload length zero? 458 */ 459 if (payload_len == 0) { 460 /* 461 * Yes. If we found an extension 462 * header, treat that as a truncated 463 * packet header, as there was 464 * no payload to contain an 465 * extension header. 466 */ 467 if (found_extension_header) 468 goto trunc; 469 470 /* 471 * OK, we didn't see any extension 472 * header, but that means we have 473 * no payload, so set the length 474 * to the IPv6 header length, 475 * and change the snapshot length 476 * accordingly. 477 */ 478 len = sizeof(struct ip6_hdr); 479 nd_change_snaplen(ndo, bp, len); 480 481 /* 482 * Now subtract the length of 483 * the IPv6 header plus extension 484 * headers (there weren't any, so 485 * that's just the IPv6 header 486 * length) to get the payload length. 487 */ 488 len -= total_advance; 489 } 490 } 491 ip_demux_print(ndo, cp, len, 6, fragmented, 492 GET_U_1(ip6->ip6_hlim), nh, bp); 493 nd_pop_packet_info(ndo); 494 return; 495 } 496 ph = nh; 497 498 /* ndo_protocol reassignment after xxx_print() calls */ 499 ndo->ndo_protocol = "ip6"; 500 } 501 502 nd_pop_packet_info(ndo); 503 return; 504 trunc: 505 nd_print_trunc(ndo); 506 return; 507 508 invalid: 509 nd_print_invalid(ndo); 510 } 511