Home | History | Annotate | Line # | Download | only in dist
      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