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 */
51static void
52ip6_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
169done:
170trunc:
171	GET_CPY_BYTES(dst, dst_addr, sizeof(nd_ipv6));
172}
173
174/*
175 * Compute a V6-style checksum by building a pseudoheader.
176 */
177uint16_t
178nextproto6_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 */
230void
231ip6_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;
505trunc:
506	nd_print_trunc(ndo);
507}
508