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
ip6_finddst(netdissect_options * ndo,nd_ipv6 * dst,const struct ip6_hdr * ip6)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
nextproto6_cksum(netdissect_options * ndo,const struct ip6_hdr * ip6,const uint8_t * data,u_int len,u_int covlen,uint8_t next_proto)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
ip6_print(netdissect_options * ndo,const u_char * bp,u_int length)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