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