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