156893Sfenner/*
256893Sfenner * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994
356893Sfenner *	The Regents of the University of California.  All rights reserved.
456893Sfenner *
556893Sfenner * Redistribution and use in source and binary forms, with or without
656893Sfenner * modification, are permitted provided that: (1) source code distributions
756893Sfenner * retain the above copyright notice and this paragraph in its entirety, (2)
856893Sfenner * distributions including binary code include the above copyright notice and
956893Sfenner * this paragraph in its entirety in the documentation or other materials
1056893Sfenner * provided with the distribution, and (3) all advertising materials mentioning
1156893Sfenner * features or use of this software display the following acknowledgement:
1256893Sfenner * ``This product includes software developed by the University of California,
1356893Sfenner * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
1456893Sfenner * the University nor the names of its contributors may be used to endorse
1556893Sfenner * or promote products derived from this software without specific prior
1656893Sfenner * written permission.
1756893Sfenner * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
1856893Sfenner * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
1956893Sfenner * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2056893Sfenner */
2156893Sfenner
22313537Sglebius/* \summary: IPv6 printer */
23313537Sglebius
2456893Sfenner#ifdef HAVE_CONFIG_H
2556893Sfenner#include "config.h"
2656893Sfenner#endif
2756893Sfenner
28313537Sglebius#include <netdissect-stdinc.h>
2956893Sfenner
3075118Sfenner#include <string.h>
3156893Sfenner
32313537Sglebius#include "netdissect.h"
3356893Sfenner#include "addrtoname.h"
34127675Sbms#include "extract.h"
3556893Sfenner
3675118Sfenner#include "ip6.h"
37127675Sbms#include "ipproto.h"
3856893Sfenner
3956893Sfenner/*
40313537Sglebius * If routing headers are presend and valid, set dst to the final destination.
41313537Sglebius * Otherwise, set it to the IPv6 destination.
42313537Sglebius *
43313537Sglebius * This is used for UDP and TCP pseudo-header in the checksum
44313537Sglebius * calculation.
45313537Sglebius */
46313537Sglebiusstatic void
47313537Sglebiusip6_finddst(netdissect_options *ndo, struct in6_addr *dst,
48313537Sglebius            const struct ip6_hdr *ip6)
49313537Sglebius{
50313537Sglebius	const u_char *cp;
51313537Sglebius	int advance;
52313537Sglebius	u_int nh;
53313537Sglebius	const struct in6_addr *dst_addr;
54313537Sglebius	const struct ip6_rthdr *dp;
55313537Sglebius	const struct ip6_rthdr0 *dp0;
56313537Sglebius	const struct in6_addr *addr;
57313537Sglebius	int i, len;
58313537Sglebius
59313537Sglebius	cp = (const u_char *)ip6;
60313537Sglebius	advance = sizeof(struct ip6_hdr);
61313537Sglebius	nh = ip6->ip6_nxt;
62313537Sglebius	dst_addr = &ip6->ip6_dst;
63313537Sglebius
64313537Sglebius	while (cp < ndo->ndo_snapend) {
65313537Sglebius		cp += advance;
66313537Sglebius
67313537Sglebius		switch (nh) {
68313537Sglebius
69313537Sglebius		case IPPROTO_HOPOPTS:
70313537Sglebius		case IPPROTO_DSTOPTS:
71313537Sglebius		case IPPROTO_MOBILITY_OLD:
72313537Sglebius		case IPPROTO_MOBILITY:
73313537Sglebius			/*
74313537Sglebius			 * These have a header length byte, following
75313537Sglebius			 * the next header byte, giving the length of
76313537Sglebius			 * the header, in units of 8 octets, excluding
77313537Sglebius			 * the first 8 octets.
78313537Sglebius			 */
79313537Sglebius			ND_TCHECK2(*cp, 2);
80313537Sglebius			advance = (int)((*(cp + 1) + 1) << 3);
81313537Sglebius			nh = *cp;
82313537Sglebius			break;
83313537Sglebius
84313537Sglebius		case IPPROTO_FRAGMENT:
85313537Sglebius			/*
86313537Sglebius			 * The byte following the next header byte is
87313537Sglebius			 * marked as reserved, and the header is always
88313537Sglebius			 * the same size.
89313537Sglebius			 */
90313537Sglebius			ND_TCHECK2(*cp, 1);
91313537Sglebius			advance = sizeof(struct ip6_frag);
92313537Sglebius			nh = *cp;
93313537Sglebius			break;
94313537Sglebius
95313537Sglebius		case IPPROTO_ROUTING:
96313537Sglebius			/*
97313537Sglebius			 * OK, we found it.
98313537Sglebius			 */
99313537Sglebius			dp = (const struct ip6_rthdr *)cp;
100313537Sglebius			ND_TCHECK(*dp);
101313537Sglebius			len = dp->ip6r_len;
102313537Sglebius			switch (dp->ip6r_type) {
103313537Sglebius
104313537Sglebius			case IPV6_RTHDR_TYPE_0:
105313537Sglebius			case IPV6_RTHDR_TYPE_2:		/* Mobile IPv6 ID-20 */
106313537Sglebius				dp0 = (const struct ip6_rthdr0 *)dp;
107313537Sglebius				if (len % 2 == 1)
108313537Sglebius					goto trunc;
109313537Sglebius				len >>= 1;
110313537Sglebius				addr = &dp0->ip6r0_addr[0];
111313537Sglebius				for (i = 0; i < len; i++) {
112313537Sglebius					if ((const u_char *)(addr + 1) > ndo->ndo_snapend)
113313537Sglebius						goto trunc;
114313537Sglebius
115313537Sglebius					dst_addr = addr;
116313537Sglebius					addr++;
117313537Sglebius				}
118313537Sglebius				break;
119313537Sglebius
120313537Sglebius			default:
121313537Sglebius				break;
122313537Sglebius			}
123313537Sglebius
124313537Sglebius			/*
125313537Sglebius			 * Only one routing header to a customer.
126313537Sglebius			 */
127313537Sglebius			goto done;
128313537Sglebius
129313537Sglebius		case IPPROTO_AH:
130313537Sglebius		case IPPROTO_ESP:
131313537Sglebius		case IPPROTO_IPCOMP:
132313537Sglebius		default:
133313537Sglebius			/*
134313537Sglebius			 * AH and ESP are, in the RFCs that describe them,
135313537Sglebius			 * described as being "viewed as an end-to-end
136313537Sglebius			 * payload" "in the IPv6 context, so that they
137313537Sglebius			 * "should appear after hop-by-hop, routing, and
138313537Sglebius			 * fragmentation extension headers".  We assume
139313537Sglebius			 * that's the case, and stop as soon as we see
140313537Sglebius			 * one.  (We can't handle an ESP header in
141313537Sglebius			 * the general case anyway, as its length depends
142313537Sglebius			 * on the encryption algorithm.)
143313537Sglebius			 *
144313537Sglebius			 * IPComp is also "viewed as an end-to-end
145313537Sglebius			 * payload" "in the IPv6 context".
146313537Sglebius			 *
147313537Sglebius			 * All other protocols are assumed to be the final
148313537Sglebius			 * protocol.
149313537Sglebius			 */
150313537Sglebius			goto done;
151313537Sglebius		}
152313537Sglebius	}
153313537Sglebius
154313537Sglebiusdone:
155313537Sglebiustrunc:
156313537Sglebius	UNALIGNED_MEMCPY(dst, dst_addr, sizeof(struct in6_addr));
157313537Sglebius}
158313537Sglebius
159313537Sglebius/*
160214478Srpaulo * Compute a V6-style checksum by building a pseudoheader.
161214478Srpaulo */
162214478Srpauloint
163313537Sglebiusnextproto6_cksum(netdissect_options *ndo,
164313537Sglebius                 const struct ip6_hdr *ip6, const uint8_t *data,
165276788Sdelphij		 u_int len, u_int covlen, u_int next_proto)
166214478Srpaulo{
167235530Sdelphij        struct {
168235530Sdelphij                struct in6_addr ph_src;
169235530Sdelphij                struct in6_addr ph_dst;
170276788Sdelphij                uint32_t       ph_len;
171276788Sdelphij                uint8_t        ph_zero[3];
172276788Sdelphij                uint8_t        ph_nxt;
173235530Sdelphij        } ph;
174235530Sdelphij        struct cksum_vec vec[2];
175214478Srpaulo
176214478Srpaulo        /* pseudo-header */
177235530Sdelphij        memset(&ph, 0, sizeof(ph));
178276788Sdelphij        UNALIGNED_MEMCPY(&ph.ph_src, &ip6->ip6_src, sizeof (struct in6_addr));
179313537Sglebius        switch (ip6->ip6_nxt) {
180313537Sglebius
181313537Sglebius        case IPPROTO_HOPOPTS:
182313537Sglebius        case IPPROTO_DSTOPTS:
183313537Sglebius        case IPPROTO_MOBILITY_OLD:
184313537Sglebius        case IPPROTO_MOBILITY:
185313537Sglebius        case IPPROTO_FRAGMENT:
186313537Sglebius        case IPPROTO_ROUTING:
187313537Sglebius                /*
188313537Sglebius                 * The next header is either a routing header or a header
189313537Sglebius                 * after which there might be a routing header, so scan
190313537Sglebius                 * for a routing header.
191313537Sglebius                 */
192313537Sglebius                ip6_finddst(ndo, &ph.ph_dst, ip6);
193313537Sglebius                break;
194313537Sglebius
195313537Sglebius        default:
196313537Sglebius                UNALIGNED_MEMCPY(&ph.ph_dst, &ip6->ip6_dst, sizeof (struct in6_addr));
197313537Sglebius                break;
198313537Sglebius        }
199235530Sdelphij        ph.ph_len = htonl(len);
200235530Sdelphij        ph.ph_nxt = next_proto;
201214478Srpaulo
202276788Sdelphij        vec[0].ptr = (const uint8_t *)(void *)&ph;
203235530Sdelphij        vec[0].len = sizeof(ph);
204235530Sdelphij        vec[1].ptr = data;
205276788Sdelphij        vec[1].len = covlen;
206214478Srpaulo
207235530Sdelphij        return in_cksum(vec, 2);
208214478Srpaulo}
209214478Srpaulo
210214478Srpaulo/*
21156893Sfenner * print an IP6 datagram.
21256893Sfenner */
21356893Sfennervoid
214235530Sdelphijip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
21556893Sfenner{
21656893Sfenner	register const struct ip6_hdr *ip6;
21775118Sfenner	register int advance;
218127675Sbms	u_int len;
219127675Sbms	const u_char *ipend;
22056893Sfenner	register const u_char *cp;
221127675Sbms	register u_int payload_len;
22256893Sfenner	int nh;
22375118Sfenner	int fragmented = 0;
22456893Sfenner	u_int flow;
225127675Sbms
22656893Sfenner	ip6 = (const struct ip6_hdr *)bp;
22756893Sfenner
228276788Sdelphij	ND_TCHECK(*ip6);
22956893Sfenner	if (length < sizeof (struct ip6_hdr)) {
230276788Sdelphij		ND_PRINT((ndo, "truncated-ip6 %u", length));
23156893Sfenner		return;
23256893Sfenner	}
23356893Sfenner
234235530Sdelphij        if (!ndo->ndo_eflag)
235235530Sdelphij            ND_PRINT((ndo, "IP6 "));
236146778Ssam
237276788Sdelphij	if (IP6_VERSION(ip6) != 6) {
238276788Sdelphij          ND_PRINT((ndo,"version error: %u != 6", IP6_VERSION(ip6)));
239276788Sdelphij          return;
240276788Sdelphij	}
241276788Sdelphij
242127675Sbms	payload_len = EXTRACT_16BITS(&ip6->ip6_plen);
243127675Sbms	len = payload_len + sizeof(struct ip6_hdr);
244127675Sbms	if (length < len)
245276788Sdelphij		ND_PRINT((ndo, "truncated-ip6 - %u bytes missing!",
246235530Sdelphij			len - length));
24756893Sfenner
248235530Sdelphij        if (ndo->ndo_vflag) {
249146778Ssam            flow = EXTRACT_32BITS(&ip6->ip6_flow);
250235530Sdelphij            ND_PRINT((ndo, "("));
251146778Ssam#if 0
252146778Ssam            /* rfc1883 */
253146778Ssam            if (flow & 0x0f000000)
254276788Sdelphij		ND_PRINT((ndo, "pri 0x%02x, ", (flow & 0x0f000000) >> 24));
255146778Ssam            if (flow & 0x00ffffff)
256276788Sdelphij		ND_PRINT((ndo, "flowlabel 0x%06x, ", flow & 0x00ffffff));
257146778Ssam#else
258146778Ssam            /* RFC 2460 */
259146778Ssam            if (flow & 0x0ff00000)
260276788Sdelphij		ND_PRINT((ndo, "class 0x%02x, ", (flow & 0x0ff00000) >> 20));
261146778Ssam            if (flow & 0x000fffff)
262276788Sdelphij		ND_PRINT((ndo, "flowlabel 0x%05x, ", flow & 0x000fffff));
263146778Ssam#endif
264146778Ssam
265276788Sdelphij            ND_PRINT((ndo, "hlim %u, next-header %s (%u) payload length: %u) ",
266146778Ssam                         ip6->ip6_hlim,
267146778Ssam                         tok2str(ipproto_values,"unknown",ip6->ip6_nxt),
268146778Ssam                         ip6->ip6_nxt,
269235530Sdelphij                         payload_len));
270146778Ssam        }
271146778Ssam
272127675Sbms	/*
273127675Sbms	 * Cut off the snapshot length to the end of the IP payload.
274127675Sbms	 */
275127675Sbms	ipend = bp + len;
276235530Sdelphij	if (ipend < ndo->ndo_snapend)
277235530Sdelphij		ndo->ndo_snapend = ipend;
278127675Sbms
27956893Sfenner	cp = (const u_char *)ip6;
280127675Sbms	advance = sizeof(struct ip6_hdr);
28156893Sfenner	nh = ip6->ip6_nxt;
282235530Sdelphij	while (cp < ndo->ndo_snapend && advance > 0) {
283327234Semaste		if (len < (u_int)advance)
284327234Semaste			goto trunc;
28575118Sfenner		cp += advance;
286127675Sbms		len -= advance;
28756893Sfenner
288127675Sbms		if (cp == (const u_char *)(ip6 + 1) &&
289127675Sbms		    nh != IPPROTO_TCP && nh != IPPROTO_UDP &&
290162021Ssam		    nh != IPPROTO_DCCP && nh != IPPROTO_SCTP) {
291276788Sdelphij			ND_PRINT((ndo, "%s > %s: ", ip6addr_string(ndo, &ip6->ip6_src),
292276788Sdelphij				     ip6addr_string(ndo, &ip6->ip6_dst)));
29356893Sfenner		}
29456893Sfenner
29556893Sfenner		switch (nh) {
29656893Sfenner		case IPPROTO_HOPOPTS:
297276788Sdelphij			advance = hbhopt_print(ndo, cp);
298313537Sglebius			if (advance < 0)
299313537Sglebius				return;
30056893Sfenner			nh = *cp;
30156893Sfenner			break;
30256893Sfenner		case IPPROTO_DSTOPTS:
303276788Sdelphij			advance = dstopt_print(ndo, cp);
304313537Sglebius			if (advance < 0)
305313537Sglebius				return;
30656893Sfenner			nh = *cp;
30756893Sfenner			break;
30856893Sfenner		case IPPROTO_FRAGMENT:
309276788Sdelphij			advance = frag6_print(ndo, cp, (const u_char *)ip6);
310313537Sglebius			if (advance < 0 || ndo->ndo_snapend <= cp + advance)
311146778Ssam				return;
31256893Sfenner			nh = *cp;
31375118Sfenner			fragmented = 1;
31456893Sfenner			break;
315127675Sbms
316127675Sbms		case IPPROTO_MOBILITY_OLD:
317127675Sbms		case IPPROTO_MOBILITY:
318127675Sbms			/*
319313537Sglebius			 * XXX - we don't use "advance"; RFC 3775 says that
320127675Sbms			 * the next header field in a mobility header
321127675Sbms			 * should be IPPROTO_NONE, but speaks of
322127675Sbms			 * the possiblity of a future extension in
323127675Sbms			 * which payload can be piggybacked atop a
324127675Sbms			 * mobility header.
325127675Sbms			 */
326276788Sdelphij			advance = mobility_print(ndo, cp, (const u_char *)ip6);
327327234Semaste			if (advance < 0)
328327234Semaste				return;
329127675Sbms			nh = *cp;
330146778Ssam			return;
33156893Sfenner		case IPPROTO_ROUTING:
332327234Semaste			ND_TCHECK(*cp);
333276788Sdelphij			advance = rt6_print(ndo, cp, (const u_char *)ip6);
334327234Semaste			if (advance < 0)
335327234Semaste				return;
33656893Sfenner			nh = *cp;
33756893Sfenner			break;
338127675Sbms		case IPPROTO_SCTP:
339276788Sdelphij			sctp_print(ndo, cp, (const u_char *)ip6, len);
340146778Ssam			return;
341162021Ssam		case IPPROTO_DCCP:
342276788Sdelphij			dccp_print(ndo, cp, (const u_char *)ip6, len);
343162021Ssam			return;
34456893Sfenner		case IPPROTO_TCP:
345276788Sdelphij			tcp_print(ndo, cp, len, (const u_char *)ip6, fragmented);
346146778Ssam			return;
34756893Sfenner		case IPPROTO_UDP:
348276788Sdelphij			udp_print(ndo, cp, len, (const u_char *)ip6, fragmented);
349146778Ssam			return;
35056893Sfenner		case IPPROTO_ICMPV6:
351235530Sdelphij			icmp6_print(ndo, cp, len, (const u_char *)ip6, fragmented);
352146778Ssam			return;
35356893Sfenner		case IPPROTO_AH:
354276788Sdelphij			advance = ah_print(ndo, cp);
355327234Semaste			if (advance < 0)
356327234Semaste				return;
35756893Sfenner			nh = *cp;
35856893Sfenner			break;
35956893Sfenner		case IPPROTO_ESP:
36056893Sfenner		    {
36198527Sfenner			int enh, padlen;
362235530Sdelphij			advance = esp_print(ndo, cp, len, (const u_char *)ip6, &enh, &padlen);
363327234Semaste			if (advance < 0)
364327234Semaste				return;
36556893Sfenner			nh = enh & 0xff;
36698527Sfenner			len -= padlen;
36756893Sfenner			break;
36856893Sfenner		    }
36956893Sfenner		case IPPROTO_IPCOMP:
37056893Sfenner		    {
371313537Sglebius			ipcomp_print(ndo, cp);
372313537Sglebius			/*
373313537Sglebius			 * Either this has decompressed the payload and
374313537Sglebius			 * printed it, in which case there's nothing more
375313537Sglebius			 * to do, or it hasn't, in which case there's
376313537Sglebius			 * nothing more to do.
377313537Sglebius			 */
378313537Sglebius			advance = -1;
37956893Sfenner			break;
38056893Sfenner		    }
38175118Sfenner
38256893Sfenner		case IPPROTO_PIM:
383313537Sglebius			pim_print(ndo, cp, len, (const u_char *)ip6);
384146778Ssam			return;
385147904Ssam
38656893Sfenner		case IPPROTO_OSPF:
387276788Sdelphij			ospf6_print(ndo, cp, len);
388146778Ssam			return;
389127675Sbms
39056893Sfenner		case IPPROTO_IPV6:
391235530Sdelphij			ip6_print(ndo, cp, len);
392146778Ssam			return;
393127675Sbms
39456893Sfenner		case IPPROTO_IPV4:
395235530Sdelphij		        ip_print(ndo, cp, len);
396146778Ssam			return;
397127675Sbms
398147904Ssam                case IPPROTO_PGM:
399276788Sdelphij                        pgm_print(ndo, cp, len, (const u_char *)ip6);
400147904Ssam                        return;
401147904Ssam
402147904Ssam		case IPPROTO_GRE:
403276788Sdelphij			gre_print(ndo, cp, len);
404147904Ssam			return;
405147904Ssam
406147904Ssam		case IPPROTO_RSVP:
407276788Sdelphij			rsvp_print(ndo, cp, len);
408147904Ssam			return;
409147904Ssam
41056893Sfenner		case IPPROTO_NONE:
411276788Sdelphij			ND_PRINT((ndo, "no next header"));
412146778Ssam			return;
41356893Sfenner
41456893Sfenner		default:
415276788Sdelphij			ND_PRINT((ndo, "ip-proto-%d %d", nh, len));
416146778Ssam			return;
41756893Sfenner		}
41856893Sfenner	}
41956893Sfenner
42098527Sfenner	return;
42198527Sfennertrunc:
422276788Sdelphij	ND_PRINT((ndo, "[|ip6]"));
42356893Sfenner}
424