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 */
46static void
47ip6_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
154done:
155trunc:
156	UNALIGNED_MEMCPY(dst, dst_addr, sizeof(struct in6_addr));
157}
158
159/*
160 * Compute a V6-style checksum by building a pseudoheader.
161 */
162int
163nextproto6_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 */
213void
214ip6_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;
421trunc:
422	ND_PRINT((ndo, "[|ip6]"));
423}
424