xref: /netbsd-src/external/bsd/tcpdump/dist/print-ip6.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
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.7 2017/02/05 04:05:05 spz 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, struct in6_addr *dst,
53             const struct ip6_hdr *ip6)
54 {
55 	const u_char *cp;
56 	int advance;
57 	u_int nh;
58 	const struct in6_addr *dst_addr;
59 	const struct ip6_rthdr *dp;
60 	const struct ip6_rthdr0 *dp0;
61 	const struct in6_addr *addr;
62 	int i, len;
63 
64 	cp = (const u_char *)ip6;
65 	advance = sizeof(struct ip6_hdr);
66 	nh = ip6->ip6_nxt;
67 	dst_addr = &ip6->ip6_dst;
68 
69 	while (cp < ndo->ndo_snapend) {
70 		cp += advance;
71 
72 		switch (nh) {
73 
74 		case IPPROTO_HOPOPTS:
75 		case IPPROTO_DSTOPTS:
76 		case IPPROTO_MOBILITY_OLD:
77 		case IPPROTO_MOBILITY:
78 			/*
79 			 * These have a header length byte, following
80 			 * the next header byte, giving the length of
81 			 * the header, in units of 8 octets, excluding
82 			 * the first 8 octets.
83 			 */
84 			ND_TCHECK2(*cp, 2);
85 			advance = (int)((*(cp + 1) + 1) << 3);
86 			nh = *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 			ND_TCHECK2(*cp, 1);
96 			advance = sizeof(struct ip6_frag);
97 			nh = *cp;
98 			break;
99 
100 		case IPPROTO_ROUTING:
101 			/*
102 			 * OK, we found it.
103 			 */
104 			dp = (const struct ip6_rthdr *)cp;
105 			ND_TCHECK(*dp);
106 			len = dp->ip6r_len;
107 			switch (dp->ip6r_type) {
108 
109 			case IPV6_RTHDR_TYPE_0:
110 			case IPV6_RTHDR_TYPE_2:		/* Mobile IPv6 ID-20 */
111 				dp0 = (const struct ip6_rthdr0 *)dp;
112 				if (len % 2 == 1)
113 					goto trunc;
114 				len >>= 1;
115 				addr = &dp0->ip6r0_addr[0];
116 				for (i = 0; i < len; i++) {
117 					if ((const u_char *)(addr + 1) > ndo->ndo_snapend)
118 						goto trunc;
119 
120 					dst_addr = addr;
121 					addr++;
122 				}
123 				break;
124 
125 			default:
126 				break;
127 			}
128 
129 			/*
130 			 * Only one routing header to a customer.
131 			 */
132 			goto done;
133 
134 		case IPPROTO_AH:
135 		case IPPROTO_ESP:
136 		case IPPROTO_IPCOMP:
137 		default:
138 			/*
139 			 * AH and ESP are, in the RFCs that describe them,
140 			 * described as being "viewed as an end-to-end
141 			 * payload" "in the IPv6 context, so that they
142 			 * "should appear after hop-by-hop, routing, and
143 			 * fragmentation extension headers".  We assume
144 			 * that's the case, and stop as soon as we see
145 			 * one.  (We can't handle an ESP header in
146 			 * the general case anyway, as its length depends
147 			 * on the encryption algorithm.)
148 			 *
149 			 * IPComp is also "viewed as an end-to-end
150 			 * payload" "in the IPv6 context".
151 			 *
152 			 * All other protocols are assumed to be the final
153 			 * protocol.
154 			 */
155 			goto done;
156 		}
157 	}
158 
159 done:
160 trunc:
161 	UNALIGNED_MEMCPY(dst, dst_addr, sizeof(struct in6_addr));
162 }
163 
164 /*
165  * Compute a V6-style checksum by building a pseudoheader.
166  */
167 int
168 nextproto6_cksum(netdissect_options *ndo,
169                  const struct ip6_hdr *ip6, const uint8_t *data,
170 		 u_int len, u_int covlen, u_int next_proto)
171 {
172         struct {
173                 struct in6_addr ph_src;
174                 struct in6_addr ph_dst;
175                 uint32_t       ph_len;
176                 uint8_t        ph_zero[3];
177                 uint8_t        ph_nxt;
178         } ph;
179         struct cksum_vec vec[2];
180 
181         /* pseudo-header */
182         memset(&ph, 0, sizeof(ph));
183         UNALIGNED_MEMCPY(&ph.ph_src, &ip6->ip6_src, sizeof (struct in6_addr));
184         switch (ip6->ip6_nxt) {
185 
186         case IPPROTO_HOPOPTS:
187         case IPPROTO_DSTOPTS:
188         case IPPROTO_MOBILITY_OLD:
189         case IPPROTO_MOBILITY:
190         case IPPROTO_FRAGMENT:
191         case IPPROTO_ROUTING:
192                 /*
193                  * The next header is either a routing header or a header
194                  * after which there might be a routing header, so scan
195                  * for a routing header.
196                  */
197                 ip6_finddst(ndo, &ph.ph_dst, ip6);
198                 break;
199 
200         default:
201                 UNALIGNED_MEMCPY(&ph.ph_dst, &ip6->ip6_dst, sizeof (struct in6_addr));
202                 break;
203         }
204         ph.ph_len = htonl(len);
205         ph.ph_nxt = next_proto;
206 
207         vec[0].ptr = (const uint8_t *)(void *)&ph;
208         vec[0].len = sizeof(ph);
209         vec[1].ptr = data;
210         vec[1].len = covlen;
211 
212         return in_cksum(vec, 2);
213 }
214 
215 /*
216  * print an IP6 datagram.
217  */
218 void
219 ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
220 {
221 	register const struct ip6_hdr *ip6;
222 	register int advance;
223 	u_int len;
224 	const u_char *ipend;
225 	register const u_char *cp;
226 	register u_int payload_len;
227 	int nh;
228 	int fragmented = 0;
229 	u_int flow;
230 
231 	ip6 = (const struct ip6_hdr *)bp;
232 
233 	ND_TCHECK(*ip6);
234 	if (length < sizeof (struct ip6_hdr)) {
235 		ND_PRINT((ndo, "truncated-ip6 %u", length));
236 		return;
237 	}
238 
239         if (!ndo->ndo_eflag)
240             ND_PRINT((ndo, "IP6 "));
241 
242 	if (IP6_VERSION(ip6) != 6) {
243           ND_PRINT((ndo,"version error: %u != 6", IP6_VERSION(ip6)));
244           return;
245 	}
246 
247 	payload_len = EXTRACT_16BITS(&ip6->ip6_plen);
248 	len = payload_len + sizeof(struct ip6_hdr);
249 	if (length < len)
250 		ND_PRINT((ndo, "truncated-ip6 - %u bytes missing!",
251 			len - length));
252 
253         if (ndo->ndo_vflag) {
254             flow = EXTRACT_32BITS(&ip6->ip6_flow);
255             ND_PRINT((ndo, "("));
256 #if 0
257             /* rfc1883 */
258             if (flow & 0x0f000000)
259 		ND_PRINT((ndo, "pri 0x%02x, ", (flow & 0x0f000000) >> 24));
260             if (flow & 0x00ffffff)
261 		ND_PRINT((ndo, "flowlabel 0x%06x, ", flow & 0x00ffffff));
262 #else
263             /* RFC 2460 */
264             if (flow & 0x0ff00000)
265 		ND_PRINT((ndo, "class 0x%02x, ", (flow & 0x0ff00000) >> 20));
266             if (flow & 0x000fffff)
267 		ND_PRINT((ndo, "flowlabel 0x%05x, ", flow & 0x000fffff));
268 #endif
269 
270             ND_PRINT((ndo, "hlim %u, next-header %s (%u) payload length: %u) ",
271                          ip6->ip6_hlim,
272                          tok2str(ipproto_values,"unknown",ip6->ip6_nxt),
273                          ip6->ip6_nxt,
274                          payload_len));
275         }
276 
277 	/*
278 	 * Cut off the snapshot length to the end of the IP payload.
279 	 */
280 	ipend = bp + len;
281 	if (ipend < ndo->ndo_snapend)
282 		ndo->ndo_snapend = ipend;
283 
284 	cp = (const u_char *)ip6;
285 	advance = sizeof(struct ip6_hdr);
286 	nh = ip6->ip6_nxt;
287 	while (cp < ndo->ndo_snapend && advance > 0) {
288 		cp += advance;
289 		len -= advance;
290 
291 		if (cp == (const u_char *)(ip6 + 1) &&
292 		    nh != IPPROTO_TCP && nh != IPPROTO_UDP &&
293 		    nh != IPPROTO_DCCP && nh != IPPROTO_SCTP) {
294 			ND_PRINT((ndo, "%s > %s: ", ip6addr_string(ndo, &ip6->ip6_src),
295 				     ip6addr_string(ndo, &ip6->ip6_dst)));
296 		}
297 
298 		switch (nh) {
299 		case IPPROTO_HOPOPTS:
300 			advance = hbhopt_print(ndo, cp);
301 			if (advance < 0)
302 				return;
303 			nh = *cp;
304 			break;
305 		case IPPROTO_DSTOPTS:
306 			advance = dstopt_print(ndo, cp);
307 			if (advance < 0)
308 				return;
309 			nh = *cp;
310 			break;
311 		case IPPROTO_FRAGMENT:
312 			advance = frag6_print(ndo, cp, (const u_char *)ip6);
313 			if (advance < 0 || ndo->ndo_snapend <= cp + advance)
314 				return;
315 			nh = *cp;
316 			fragmented = 1;
317 			break;
318 
319 		case IPPROTO_MOBILITY_OLD:
320 		case IPPROTO_MOBILITY:
321 			/*
322 			 * XXX - we don't use "advance"; RFC 3775 says that
323 			 * the next header field in a mobility header
324 			 * should be IPPROTO_NONE, but speaks of
325 			 * the possiblity of a future extension in
326 			 * which payload can be piggybacked atop a
327 			 * mobility header.
328 			 */
329 			advance = mobility_print(ndo, cp, (const u_char *)ip6);
330 			nh = *cp;
331 			return;
332 		case IPPROTO_ROUTING:
333 			advance = rt6_print(ndo, cp, (const u_char *)ip6);
334 			nh = *cp;
335 			break;
336 		case IPPROTO_SCTP:
337 			sctp_print(ndo, cp, (const u_char *)ip6, len);
338 			return;
339 		case IPPROTO_DCCP:
340 			dccp_print(ndo, cp, (const u_char *)ip6, len);
341 			return;
342 		case IPPROTO_TCP:
343 			tcp_print(ndo, cp, len, (const u_char *)ip6, fragmented);
344 			return;
345 		case IPPROTO_UDP:
346 			udp_print(ndo, cp, len, (const u_char *)ip6, fragmented);
347 			return;
348 		case IPPROTO_ICMPV6:
349 			icmp6_print(ndo, cp, len, (const u_char *)ip6, fragmented);
350 			return;
351 		case IPPROTO_AH:
352 			advance = ah_print(ndo, cp);
353 			nh = *cp;
354 			break;
355 		case IPPROTO_ESP:
356 		    {
357 			int enh, padlen;
358 			advance = esp_print(ndo, cp, len, (const u_char *)ip6, &enh, &padlen);
359 			nh = enh & 0xff;
360 			len -= padlen;
361 			break;
362 		    }
363 		case IPPROTO_IPCOMP:
364 		    {
365 			ipcomp_print(ndo, cp);
366 			/*
367 			 * Either this has decompressed the payload and
368 			 * printed it, in which case there's nothing more
369 			 * to do, or it hasn't, in which case there's
370 			 * nothing more to do.
371 			 */
372 			advance = -1;
373 			break;
374 		    }
375 
376 		case IPPROTO_PIM:
377 			pim_print(ndo, cp, len, (const u_char *)ip6);
378 			return;
379 
380 		case IPPROTO_OSPF:
381 			ospf6_print(ndo, cp, len);
382 			return;
383 
384 		case IPPROTO_IPV6:
385 			ip6_print(ndo, cp, len);
386 			return;
387 
388 		case IPPROTO_IPV4:
389 		        ip_print(ndo, cp, len);
390 			return;
391 
392                 case IPPROTO_PGM:
393                         pgm_print(ndo, cp, len, (const u_char *)ip6);
394                         return;
395 
396 		case IPPROTO_GRE:
397 			gre_print(ndo, cp, len);
398 			return;
399 
400 		case IPPROTO_RSVP:
401 			rsvp_print(ndo, cp, len);
402 			return;
403 
404 		case IPPROTO_NONE:
405 			ND_PRINT((ndo, "no next header"));
406 			return;
407 
408 		default:
409 			ND_PRINT((ndo, "ip-proto-%d %d", nh, len));
410 			return;
411 		}
412 	}
413 
414 	return;
415 trunc:
416 	ND_PRINT((ndo, "[|ip6]"));
417 }
418