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