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