in6_gif.c revision 78064
1185377Ssam/*	$FreeBSD: head/sys/netinet6/in6_gif.c 78064 2001-06-11 12:39:29Z ume $	*/
2185377Ssam/*	$KAME: in6_gif.c,v 1.49 2001/05/14 14:02:17 itojun Exp $	*/
3185377Ssam
4185377Ssam/*
5185377Ssam * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6185377Ssam * All rights reserved.
7185377Ssam *
8185377Ssam * Redistribution and use in source and binary forms, with or without
9185377Ssam * modification, are permitted provided that the following conditions
10185377Ssam * are met:
11185377Ssam * 1. Redistributions of source code must retain the above copyright
12185377Ssam *    notice, this list of conditions and the following disclaimer.
13185377Ssam * 2. Redistributions in binary form must reproduce the above copyright
14185377Ssam *    notice, this list of conditions and the following disclaimer in the
15185377Ssam *    documentation and/or other materials provided with the distribution.
16185377Ssam * 3. Neither the name of the project nor the names of its contributors
17203158Srpaulo *    may be used to endorse or promote products derived from this software
18185377Ssam *    without specific prior written permission.
19185377Ssam *
20185377Ssam * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21185377Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22185377Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23185377Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24185377Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25185377Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26185377Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27185377Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28185377Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29185377Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30224512Sadrian * SUCH DAMAGE.
31224512Sadrian */
32224512Sadrian
33224512Sadrian#include "opt_inet.h"
34224512Sadrian#include "opt_inet6.h"
35224512Sadrian
36224512Sadrian#include <sys/param.h>
37224512Sadrian#include <sys/systm.h>
38224512Sadrian#include <sys/socket.h>
39224512Sadrian#include <sys/sockio.h>
40224512Sadrian#include <sys/mbuf.h>
41224512Sadrian#include <sys/errno.h>
42224512Sadrian#include <sys/queue.h>
43224512Sadrian#include <sys/syslog.h>
44224512Sadrian
45224512Sadrian#include <sys/malloc.h>
46224512Sadrian
47224512Sadrian#include <net/if.h>
48224512Sadrian#include <net/route.h>
49224512Sadrian
50224512Sadrian#include <netinet/in.h>
51224512Sadrian#include <netinet/in_systm.h>
52224512Sadrian#ifdef INET
53224512Sadrian#include <netinet/ip.h>
54224512Sadrian#endif
55224512Sadrian#include <netinet/ip_encap.h>
56224512Sadrian#ifdef INET6
57224512Sadrian#include <netinet/ip6.h>
58224512Sadrian#include <netinet6/ip6_var.h>
59224512Sadrian#include <netinet6/in6_gif.h>
60224512Sadrian#include <netinet6/in6_var.h>
61224512Sadrian#endif
62224512Sadrian#include <netinet/ip_ecn.h>
63224512Sadrian#ifdef INET6
64224512Sadrian#include <netinet6/ip6_ecn.h>
65224512Sadrian#endif
66224512Sadrian
67224512Sadrian#include <net/if_gif.h>
68224512Sadrian
69224512Sadrian#include <net/net_osdep.h>
70234747Sadrian
71234747Sadrianint
72234747Sadrianin6_gif_output(ifp, family, m, rt)
73234747Sadrian	struct ifnet *ifp;
74234747Sadrian	int family; /* family of the packet to be encapsulate. */
75234747Sadrian	struct mbuf *m;
76234747Sadrian	struct rtentry *rt;
77234747Sadrian{
78234747Sadrian	struct gif_softc *sc = (struct gif_softc*)ifp;
79234747Sadrian	struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
80234747Sadrian	struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
81234747Sadrian	struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
82234747Sadrian	struct ip6_hdr *ip6;
83234747Sadrian	int proto;
84234747Sadrian	u_int8_t itos, otos;
85234747Sadrian
86234747Sadrian	if (sin6_src == NULL || sin6_dst == NULL ||
87234747Sadrian	    sin6_src->sin6_family != AF_INET6 ||
88234747Sadrian	    sin6_dst->sin6_family != AF_INET6) {
89234747Sadrian		m_freem(m);
90234747Sadrian		return EAFNOSUPPORT;
91234747Sadrian	}
92234747Sadrian
93234747Sadrian	switch (family) {
94234747Sadrian#ifdef INET
95234747Sadrian	case AF_INET:
96234747Sadrian	    {
97234747Sadrian		struct ip *ip;
98234747Sadrian
99234747Sadrian		proto = IPPROTO_IPV4;
100234747Sadrian		if (m->m_len < sizeof(*ip)) {
101234747Sadrian			m = m_pullup(m, sizeof(*ip));
102234747Sadrian			if (!m)
103234747Sadrian				return ENOBUFS;
104185377Ssam		}
105185377Ssam		ip = mtod(m, struct ip *);
106185377Ssam		itos = ip->ip_tos;
107185377Ssam		break;
108185377Ssam	    }
109185377Ssam#endif
110185377Ssam#ifdef INET6
111185377Ssam	case AF_INET6:
112185377Ssam	    {
113185377Ssam		struct ip6_hdr *ip6;
114185380Ssam		proto = IPPROTO_IPV6;
115185380Ssam		if (m->m_len < sizeof(*ip6)) {
116185380Ssam			m = m_pullup(m, sizeof(*ip6));
117185380Ssam			if (!m)
118185380Ssam				return ENOBUFS;
119185380Ssam		}
120185377Ssam		ip6 = mtod(m, struct ip6_hdr *);
121185377Ssam		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
122185377Ssam		break;
123185377Ssam	    }
124185377Ssam#endif
125185377Ssam	default:
126185377Ssam#ifdef DEBUG
127185377Ssam		printf("in6_gif_output: warning: unknown family %d passed\n",
128185377Ssam			family);
129185377Ssam#endif
130185377Ssam		m_freem(m);
131185377Ssam		return EAFNOSUPPORT;
132185377Ssam	}
133185377Ssam
134185377Ssam	/* prepend new IP header */
135185377Ssam	M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
136185377Ssam	if (m && m->m_len < sizeof(struct ip6_hdr))
137185377Ssam		m = m_pullup(m, sizeof(struct ip6_hdr));
138185377Ssam	if (m == NULL) {
139185377Ssam		printf("ENOBUFS in in6_gif_output %d\n", __LINE__);
140185377Ssam		return ENOBUFS;
141185377Ssam	}
142185377Ssam
143185377Ssam	ip6 = mtod(m, struct ip6_hdr *);
144185377Ssam	ip6->ip6_flow	= 0;
145185377Ssam	ip6->ip6_vfc	&= ~IPV6_VERSION_MASK;
146185377Ssam	ip6->ip6_vfc	|= IPV6_VERSION;
147185377Ssam	ip6->ip6_plen	= htons((u_short)m->m_pkthdr.len);
148185377Ssam	ip6->ip6_nxt	= proto;
149185377Ssam	ip6->ip6_hlim	= ip6_gif_hlim;
150185377Ssam	ip6->ip6_src	= sin6_src->sin6_addr;
151185377Ssam	/* bidirectional configured tunnel mode */
152185377Ssam	if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
153185377Ssam		ip6->ip6_dst = sin6_dst->sin6_addr;
154220259Sadrian	else  {
155225922Sadrian		m_freem(m);
156220259Sadrian		return ENETUNREACH;
157185377Ssam	}
158185377Ssam	if (ifp->if_flags & IFF_LINK1)
159185377Ssam		ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
160185377Ssam	else
161185377Ssam		ip_ecn_ingress(ECN_NOCARE, &otos, &itos);
162185377Ssam	ip6->ip6_flow &= ~ntohl(0xff00000);
163185377Ssam	ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
164185377Ssam
165185377Ssam	if (dst->sin6_family != sin6_dst->sin6_family ||
166185377Ssam	     !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) {
167185377Ssam		/* cache route doesn't match */
168185377Ssam		bzero(dst, sizeof(*dst));
169185377Ssam		dst->sin6_family = sin6_dst->sin6_family;
170185377Ssam		dst->sin6_len = sizeof(struct sockaddr_in6);
171185377Ssam		dst->sin6_addr = sin6_dst->sin6_addr;
172185377Ssam		if (sc->gif_ro6.ro_rt) {
173185377Ssam			RTFREE(sc->gif_ro6.ro_rt);
174185377Ssam			sc->gif_ro6.ro_rt = NULL;
175185377Ssam		}
176185377Ssam#if 0
177185377Ssam		sc->gif_if.if_mtu = GIF_MTU;
178185377Ssam#endif
179185377Ssam	}
180185377Ssam
181185377Ssam	if (sc->gif_ro6.ro_rt == NULL) {
182185377Ssam		rtalloc((struct route *)&sc->gif_ro6);
183185377Ssam		if (sc->gif_ro6.ro_rt == NULL) {
184185377Ssam			m_freem(m);
185185377Ssam			return ENETUNREACH;
186185377Ssam		}
187185377Ssam
188185377Ssam		/* if it constitutes infinite encapsulation, punt. */
189185377Ssam		if (sc->gif_ro.ro_rt->rt_ifp == ifp) {
190185377Ssam			m_freem(m);
191185377Ssam			return ENETUNREACH;	/*XXX*/
192185377Ssam		}
193185377Ssam#if 0
194185377Ssam		ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu
195185377Ssam			- sizeof(struct ip6_hdr);
196185377Ssam#endif
197185377Ssam	}
198185377Ssam
199185377Ssam#ifdef IPV6_MINMTU
200185377Ssam	/*
201185377Ssam	 * force fragmentation to minimum MTU, to avoid path MTU discovery.
202185377Ssam	 * it is too painful to ask for resend of inner packet, to achieve
203185377Ssam	 * path MTU discovery for encapsulated packets.
204185377Ssam	 */
205185377Ssam	return(ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL));
206185377Ssam#else
207185377Ssam	return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL));
208185377Ssam#endif
209185377Ssam}
210185377Ssam
211185377Ssamint in6_gif_input(mp, offp, proto)
212250346Sadrian	struct mbuf **mp;
213250346Sadrian	int *offp, proto;
214250346Sadrian{
215250346Sadrian	struct mbuf *m = *mp;
216250346Sadrian	struct ifnet *gifp = NULL;
217250346Sadrian	struct ip6_hdr *ip6;
218250346Sadrian	int af = 0;
219250346Sadrian	u_int32_t otos;
220185377Ssam
221185377Ssam	ip6 = mtod(m, struct ip6_hdr *);
222185377Ssam
223185377Ssam	gifp = (struct ifnet *)encap_getarg(m);
224185377Ssam
225185377Ssam	if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
226185377Ssam		m_freem(m);
227185377Ssam		ip6stat.ip6s_nogif++;
228185377Ssam		return IPPROTO_DONE;
229185377Ssam	}
230185377Ssam
231185377Ssam	otos = ip6->ip6_flow;
232185377Ssam	m_adj(m, *offp);
233185377Ssam
234185377Ssam	switch (proto) {
235185377Ssam#ifdef INET
236185377Ssam	case IPPROTO_IPV4:
237185377Ssam	    {
238185377Ssam		struct ip *ip;
239237519Sadrian		u_int8_t otos8;
240237519Sadrian		af = AF_INET;
241237519Sadrian		otos8 = (ntohl(otos) >> 20) & 0xff;
242237521Sadrian		if (m->m_len < sizeof(*ip)) {
243237521Sadrian			m = m_pullup(m, sizeof(*ip));
244237521Sadrian			if (!m)
245237519Sadrian				return IPPROTO_DONE;
246237519Sadrian		}
247237519Sadrian		ip = mtod(m, struct ip *);
248237519Sadrian		if (gifp->if_flags & IFF_LINK1)
249185377Ssam			ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos);
250185377Ssam		else
251237626Sadrian			ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos);
252237626Sadrian		break;
253237626Sadrian	    }
254237626Sadrian#endif /* INET */
255237626Sadrian#ifdef INET6
256237626Sadrian	case IPPROTO_IPV6:
257185377Ssam	    {
258237626Sadrian		struct ip6_hdr *ip6;
259237626Sadrian		af = AF_INET6;
260237626Sadrian		if (m->m_len < sizeof(*ip6)) {
261237626Sadrian			m = m_pullup(m, sizeof(*ip6));
262237626Sadrian			if (!m)
263237626Sadrian				return IPPROTO_DONE;
264237626Sadrian		}
265237626Sadrian		ip6 = mtod(m, struct ip6_hdr *);
266237626Sadrian		if (gifp->if_flags & IFF_LINK1)
267237626Sadrian			ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow);
268237626Sadrian		else
269237521Sadrian			ip6_ecn_egress(ECN_NOCARE, &otos, &ip6->ip6_flow);
270237521Sadrian		break;
271237519Sadrian	    }
272237519Sadrian#endif
273185377Ssam	default:
274185377Ssam		ip6stat.ip6s_nogif++;
275185377Ssam		m_freem(m);
276185377Ssam		return IPPROTO_DONE;
277185377Ssam	}
278185377Ssam
279185377Ssam	gif_input(m, af, gifp);
280	return IPPROTO_DONE;
281}
282
283/*
284 * we know that we are in IFF_UP, outer address available, and outer family
285 * matched the physical addr family.  see gif_encapcheck().
286 */
287int
288gif_encapcheck6(m, off, proto, arg)
289	const struct mbuf *m;
290	int off;
291	int proto;
292	void *arg;
293{
294	struct ip6_hdr ip6;
295	struct gif_softc *sc;
296	struct sockaddr_in6 *src, *dst;
297	int addrmatch;
298
299	/* sanity check done in caller */
300	sc = (struct gif_softc *)arg;
301	src = (struct sockaddr_in6 *)sc->gif_psrc;
302	dst = (struct sockaddr_in6 *)sc->gif_pdst;
303
304	/* LINTED const cast */
305	m_copydata((struct mbuf *)m, 0, sizeof(ip6), (caddr_t)&ip6);
306
307	/* check for address match */
308	addrmatch = 0;
309	if (IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6.ip6_dst))
310		addrmatch |= 1;
311	if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6.ip6_src))
312		addrmatch |= 2;
313	if (addrmatch != 3)
314		return 0;
315
316	/* martian filters on outer source - done in ip6_input */
317
318	/* ingress filters on outer source */
319	if ((sc->gif_if.if_flags & IFF_LINK2) == 0 &&
320	    (m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) {
321		struct sockaddr_in6 sin6;
322		struct rtentry *rt;
323
324		bzero(&sin6, sizeof(sin6));
325		sin6.sin6_family = AF_INET6;
326		sin6.sin6_len = sizeof(struct sockaddr_in6);
327		sin6.sin6_addr = ip6.ip6_src;
328		/* XXX scopeid */
329		rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
330		if (!rt || rt->rt_ifp != m->m_pkthdr.rcvif) {
331#if 0
332			log(LOG_WARNING, "%s: packet from %s dropped "
333			    "due to ingress filter\n", if_name(&sc->gif_if),
334			    ip6_sprintf(&sin6.sin6_addr));
335#endif
336			if (rt)
337				rtfree(rt);
338			return 0;
339		}
340		rtfree(rt);
341	}
342
343	return 128 * 2;
344}
345