in6_gif.c revision 2a86be217a6aed33eda6628df2b175e49172cd9f
1/*	$FreeBSD$	*/
2/*	$KAME: in6_gif.c,v 1.49 2001/05/14 14:02:17 itojun Exp $	*/
3
4/*
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include "opt_inet.h"
34#include "opt_inet6.h"
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/socket.h>
39#include <sys/sockio.h>
40#include <sys/mbuf.h>
41#include <sys/errno.h>
42#include <sys/queue.h>
43#include <sys/syslog.h>
44
45#include <sys/malloc.h>
46
47#include <net/if.h>
48#include <net/route.h>
49
50#include <netinet/in.h>
51#include <netinet/in_systm.h>
52#ifdef INET
53#include <netinet/ip.h>
54#endif
55#include <netinet/ip_encap.h>
56#ifdef INET6
57#include <netinet/ip6.h>
58#include <netinet6/ip6_var.h>
59#include <netinet6/in6_gif.h>
60#include <netinet6/in6_var.h>
61#endif
62#include <netinet/ip_ecn.h>
63#ifdef INET6
64#include <netinet6/ip6_ecn.h>
65#endif
66
67#include <net/if_gif.h>
68
69#include <net/net_osdep.h>
70
71int
72in6_gif_output(ifp, family, m, rt)
73	struct ifnet *ifp;
74	int family; /* family of the packet to be encapsulate. */
75	struct mbuf *m;
76	struct rtentry *rt;
77{
78	struct gif_softc *sc = (struct gif_softc*)ifp;
79	struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
80	struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
81	struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
82	struct ip6_hdr *ip6;
83	int proto;
84	u_int8_t itos, otos;
85
86	if (sin6_src == NULL || sin6_dst == NULL ||
87	    sin6_src->sin6_family != AF_INET6 ||
88	    sin6_dst->sin6_family != AF_INET6) {
89		m_freem(m);
90		return EAFNOSUPPORT;
91	}
92
93	switch (family) {
94#ifdef INET
95	case AF_INET:
96	    {
97		struct ip *ip;
98
99		proto = IPPROTO_IPV4;
100		if (m->m_len < sizeof(*ip)) {
101			m = m_pullup(m, sizeof(*ip));
102			if (!m)
103				return ENOBUFS;
104		}
105		ip = mtod(m, struct ip *);
106		itos = ip->ip_tos;
107		break;
108	    }
109#endif
110#ifdef INET6
111	case AF_INET6:
112	    {
113		struct ip6_hdr *ip6;
114		proto = IPPROTO_IPV6;
115		if (m->m_len < sizeof(*ip6)) {
116			m = m_pullup(m, sizeof(*ip6));
117			if (!m)
118				return ENOBUFS;
119		}
120		ip6 = mtod(m, struct ip6_hdr *);
121		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
122		break;
123	    }
124#endif
125	default:
126#ifdef DEBUG
127		printf("in6_gif_output: warning: unknown family %d passed\n",
128			family);
129#endif
130		m_freem(m);
131		return EAFNOSUPPORT;
132	}
133
134	/* prepend new IP header */
135	M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
136	if (m && m->m_len < sizeof(struct ip6_hdr))
137		m = m_pullup(m, sizeof(struct ip6_hdr));
138	if (m == NULL) {
139		printf("ENOBUFS in in6_gif_output %d\n", __LINE__);
140		return ENOBUFS;
141	}
142
143	ip6 = mtod(m, struct ip6_hdr *);
144	ip6->ip6_flow	= 0;
145	ip6->ip6_vfc	&= ~IPV6_VERSION_MASK;
146	ip6->ip6_vfc	|= IPV6_VERSION;
147	ip6->ip6_plen	= htons((u_short)m->m_pkthdr.len);
148	ip6->ip6_nxt	= proto;
149	ip6->ip6_hlim	= ip6_gif_hlim;
150	ip6->ip6_src	= sin6_src->sin6_addr;
151	/* bidirectional configured tunnel mode */
152	if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
153		ip6->ip6_dst = sin6_dst->sin6_addr;
154	else  {
155		m_freem(m);
156		return ENETUNREACH;
157	}
158	if (ifp->if_flags & IFF_LINK1)
159		ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
160	else
161		ip_ecn_ingress(ECN_NOCARE, &otos, &itos);
162	ip6->ip6_flow &= ~ntohl(0xff00000);
163	ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
164
165	if (dst->sin6_family != sin6_dst->sin6_family ||
166	     !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) {
167		/* cache route doesn't match */
168		bzero(dst, sizeof(*dst));
169		dst->sin6_family = sin6_dst->sin6_family;
170		dst->sin6_len = sizeof(struct sockaddr_in6);
171		dst->sin6_addr = sin6_dst->sin6_addr;
172		if (sc->gif_ro6.ro_rt) {
173			RTFREE(sc->gif_ro6.ro_rt);
174			sc->gif_ro6.ro_rt = NULL;
175		}
176#if 0
177		sc->gif_if.if_mtu = GIF_MTU;
178#endif
179	}
180
181	if (sc->gif_ro6.ro_rt == NULL) {
182		rtalloc((struct route *)&sc->gif_ro6);
183		if (sc->gif_ro6.ro_rt == NULL) {
184			m_freem(m);
185			return ENETUNREACH;
186		}
187
188		/* if it constitutes infinite encapsulation, punt. */
189		if (sc->gif_ro.ro_rt->rt_ifp == ifp) {
190			m_freem(m);
191			return ENETUNREACH;	/*XXX*/
192		}
193#if 0
194		ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu
195			- sizeof(struct ip6_hdr);
196#endif
197	}
198
199#ifdef IPV6_MINMTU
200	/*
201	 * force fragmentation to minimum MTU, to avoid path MTU discovery.
202	 * it is too painful to ask for resend of inner packet, to achieve
203	 * path MTU discovery for encapsulated packets.
204	 */
205	return(ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL, NULL));
206#else
207	return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL, NULL));
208#endif
209}
210
211int in6_gif_input(mp, offp, proto)
212	struct mbuf **mp;
213	int *offp, proto;
214{
215	struct mbuf *m = *mp;
216	struct ifnet *gifp = NULL;
217	struct ip6_hdr *ip6;
218	int af = 0;
219	u_int32_t otos;
220
221	ip6 = mtod(m, struct ip6_hdr *);
222
223	gifp = (struct ifnet *)encap_getarg(m);
224
225	if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
226		m_freem(m);
227		ip6stat.ip6s_nogif++;
228		return IPPROTO_DONE;
229	}
230
231	otos = ip6->ip6_flow;
232	m_adj(m, *offp);
233
234	switch (proto) {
235#ifdef INET
236	case IPPROTO_IPV4:
237	    {
238		struct ip *ip;
239		u_int8_t otos8;
240		af = AF_INET;
241		otos8 = (ntohl(otos) >> 20) & 0xff;
242		if (m->m_len < sizeof(*ip)) {
243			m = m_pullup(m, sizeof(*ip));
244			if (!m)
245				return IPPROTO_DONE;
246		}
247		ip = mtod(m, struct ip *);
248		if (gifp->if_flags & IFF_LINK1)
249			ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos);
250		else
251			ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos);
252		break;
253	    }
254#endif /* INET */
255#ifdef INET6
256	case IPPROTO_IPV6:
257	    {
258		struct ip6_hdr *ip6;
259		af = AF_INET6;
260		if (m->m_len < sizeof(*ip6)) {
261			m = m_pullup(m, sizeof(*ip6));
262			if (!m)
263				return IPPROTO_DONE;
264		}
265		ip6 = mtod(m, struct ip6_hdr *);
266		if (gifp->if_flags & IFF_LINK1)
267			ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow);
268		else
269			ip6_ecn_egress(ECN_NOCARE, &otos, &ip6->ip6_flow);
270		break;
271	    }
272#endif
273	default:
274		ip6stat.ip6s_nogif++;
275		m_freem(m);
276		return IPPROTO_DONE;
277	}
278
279	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	m_copydata(m, 0, sizeof(ip6), (caddr_t)&ip6);
305
306	/* check for address match */
307	addrmatch = 0;
308	if (IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6.ip6_dst))
309		addrmatch |= 1;
310	if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6.ip6_src))
311		addrmatch |= 2;
312	if (addrmatch != 3)
313		return 0;
314
315	/* martian filters on outer source - done in ip6_input */
316
317	/* ingress filters on outer source */
318	if ((sc->gif_if.if_flags & IFF_LINK2) == 0 &&
319	    (m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) {
320		struct sockaddr_in6 sin6;
321		struct rtentry *rt;
322
323		bzero(&sin6, sizeof(sin6));
324		sin6.sin6_family = AF_INET6;
325		sin6.sin6_len = sizeof(struct sockaddr_in6);
326		sin6.sin6_addr = ip6.ip6_src;
327		/* XXX scopeid */
328		rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
329		if (!rt || rt->rt_ifp != m->m_pkthdr.rcvif) {
330#if 0
331			log(LOG_WARNING, "%s: packet from %s dropped "
332			    "due to ingress filter\n", if_name(&sc->gif_if),
333			    ip6_sprintf(&sin6.sin6_addr));
334#endif
335			if (rt)
336				rtfree(rt);
337			return 0;
338		}
339		rtfree(rt);
340	}
341
342	return 128 * 2;
343}
344