xref: /netbsd-src/external/bsd/tcpdump/dist/print-ip6.c (revision 3117ece4fc4a4ca4489ba793710b60b0d26bab6c)
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