in6_gif.c revision 1.20
1/*	$NetBSD: in6_gif.c,v 1.20 2001/05/14 13:35:21 itojun Exp $	*/
2/*	$KAME: in6_gif.c,v 1.48 2001/05/03 14:51:48 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_iso.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/ioctl.h>
43#include <sys/queue.h>
44#include <sys/syslog.h>
45
46#include <net/if.h>
47#include <net/route.h>
48
49#include <netinet/in.h>
50#include <netinet/in_systm.h>
51#ifdef INET
52#include <netinet/ip.h>
53#endif
54#include <netinet/ip_encap.h>
55#ifdef INET6
56#include <netinet/ip6.h>
57#include <netinet6/ip6_var.h>
58#include <netinet6/in6_gif.h>
59#include <netinet6/in6_var.h>
60#endif
61#include <netinet/ip_ecn.h>
62
63#include <net/if_gif.h>
64
65#include <net/net_osdep.h>
66
67int
68in6_gif_output(ifp, family, m, rt)
69	struct ifnet *ifp;
70	int family; /* family of the packet to be encapsulate. */
71	struct mbuf *m;
72	struct rtentry *rt;
73{
74	struct gif_softc *sc = (struct gif_softc*)ifp;
75	struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
76	struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
77	struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
78	struct ip6_hdr *ip6;
79	int proto;
80	u_int8_t itos, otos;
81
82	if (sin6_src == NULL || sin6_dst == NULL ||
83	    sin6_src->sin6_family != AF_INET6 ||
84	    sin6_dst->sin6_family != AF_INET6) {
85		m_freem(m);
86		return EAFNOSUPPORT;
87	}
88
89	switch (family) {
90#ifdef INET
91	case AF_INET:
92	    {
93		struct ip *ip;
94
95		proto = IPPROTO_IPV4;
96		if (m->m_len < sizeof(*ip)) {
97			m = m_pullup(m, sizeof(*ip));
98			if (!m)
99				return ENOBUFS;
100		}
101		ip = mtod(m, struct ip *);
102		itos = ip->ip_tos;
103		break;
104	    }
105#endif
106#ifdef INET6
107	case AF_INET6:
108	    {
109		struct ip6_hdr *ip6;
110		proto = IPPROTO_IPV6;
111		if (m->m_len < sizeof(*ip6)) {
112			m = m_pullup(m, sizeof(*ip6));
113			if (!m)
114				return ENOBUFS;
115		}
116		ip6 = mtod(m, struct ip6_hdr *);
117		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
118		break;
119	    }
120#endif
121#ifdef ISO
122	case AF_ISO:
123		proto = IPPROTO_EON;
124		itos = 0;
125		break;
126#endif
127	default:
128#ifdef DEBUG
129		printf("in6_gif_output: warning: unknown family %d passed\n",
130			family);
131#endif
132		m_freem(m);
133		return EAFNOSUPPORT;
134	}
135
136	/* prepend new IP header */
137	M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
138	if (m && m->m_len < sizeof(struct ip6_hdr))
139		m = m_pullup(m, sizeof(struct ip6_hdr));
140	if (m == NULL) {
141		printf("ENOBUFS in in6_gif_output %d\n", __LINE__);
142		return ENOBUFS;
143	}
144
145	ip6 = mtod(m, struct ip6_hdr *);
146	ip6->ip6_flow	= 0;
147	ip6->ip6_vfc	&= ~IPV6_VERSION_MASK;
148	ip6->ip6_vfc	|= IPV6_VERSION;
149	ip6->ip6_plen	= htons((u_short)m->m_pkthdr.len);
150	ip6->ip6_nxt	= proto;
151	ip6->ip6_hlim	= ip6_gif_hlim;
152	ip6->ip6_src	= sin6_src->sin6_addr;
153	/* bidirectional configured tunnel mode */
154	if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
155		ip6->ip6_dst = sin6_dst->sin6_addr;
156	else  {
157		m_freem(m);
158		return ENETUNREACH;
159	}
160	if (ifp->if_flags & IFF_LINK1)
161		ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
162	else
163		ip_ecn_ingress(ECN_NOCARE, &otos, &itos);
164	ip6->ip6_flow &= ~ntohl(0xff00000);
165	ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
166
167	if (dst->sin6_family != sin6_dst->sin6_family ||
168	     !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) {
169		/* cache route doesn't match */
170		bzero(dst, sizeof(*dst));
171		dst->sin6_family = sin6_dst->sin6_family;
172		dst->sin6_len = sizeof(struct sockaddr_in6);
173		dst->sin6_addr = sin6_dst->sin6_addr;
174		if (sc->gif_ro6.ro_rt) {
175			RTFREE(sc->gif_ro6.ro_rt);
176			sc->gif_ro6.ro_rt = NULL;
177		}
178#if 0
179		sc->gif_if.if_mtu = GIF_MTU;
180#endif
181	}
182
183	if (sc->gif_ro6.ro_rt == NULL) {
184		rtalloc((struct route *)&sc->gif_ro6);
185		if (sc->gif_ro6.ro_rt == NULL) {
186			m_freem(m);
187			return ENETUNREACH;
188		}
189
190		/* if it constitutes infinite encapsulation, punt. */
191		if (sc->gif_ro.ro_rt->rt_ifp == ifp) {
192			m_freem(m);
193			return ENETUNREACH;	/*XXX*/
194		}
195#if 0
196		ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu
197			- sizeof(struct ip6_hdr);
198#endif
199	}
200
201#ifdef IPV6_MINMTU
202	/*
203	 * force fragmentation to minimum MTU, to avoid path MTU discovery.
204	 * it is too painful to ask for resend of inner packet, to achieve
205	 * path MTU discovery for encapsulated packets.
206	 */
207	return(ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL));
208#else
209	return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL));
210#endif
211}
212
213int in6_gif_input(mp, offp, proto)
214	struct mbuf **mp;
215	int *offp, proto;
216{
217	struct mbuf *m = *mp;
218	struct ifnet *gifp = NULL;
219	struct ip6_hdr *ip6;
220	int af = 0;
221	u_int32_t otos;
222
223	ip6 = mtod(m, struct ip6_hdr *);
224
225	gifp = (struct ifnet *)encap_getarg(m);
226
227	if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
228		m_freem(m);
229		ip6stat.ip6s_nogif++;
230		return IPPROTO_DONE;
231	}
232
233	otos = ip6->ip6_flow;
234	m_adj(m, *offp);
235
236	switch (proto) {
237#ifdef INET
238	case IPPROTO_IPV4:
239	    {
240		struct ip *ip;
241		u_int8_t otos8;
242		af = AF_INET;
243		otos8 = (ntohl(otos) >> 20) & 0xff;
244		if (m->m_len < sizeof(*ip)) {
245			m = m_pullup(m, sizeof(*ip));
246			if (!m)
247				return IPPROTO_DONE;
248		}
249		ip = mtod(m, struct ip *);
250		if (gifp->if_flags & IFF_LINK1)
251			ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos);
252		else
253			ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos);
254		break;
255	    }
256#endif /* INET */
257#ifdef INET6
258	case IPPROTO_IPV6:
259	    {
260		struct ip6_hdr *ip6;
261		af = AF_INET6;
262		if (m->m_len < sizeof(*ip6)) {
263			m = m_pullup(m, sizeof(*ip6));
264			if (!m)
265				return IPPROTO_DONE;
266		}
267		ip6 = mtod(m, struct ip6_hdr *);
268		if (gifp->if_flags & IFF_LINK1)
269			ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow);
270		else
271			ip6_ecn_egress(ECN_NOCARE, &otos, &ip6->ip6_flow);
272		break;
273	    }
274#endif
275#ifdef ISO
276	case IPPROTO_EON:
277		af = AF_ISO;
278		break;
279#endif
280	default:
281		ip6stat.ip6s_nogif++;
282		m_freem(m);
283		return IPPROTO_DONE;
284	}
285
286	gif_input(m, af, gifp);
287	return IPPROTO_DONE;
288}
289
290/*
291 * we know that we are in IFF_UP, outer address available, and outer family
292 * matched the physical addr family.  see gif_encapcheck().
293 */
294int
295gif_encapcheck6(m, off, proto, arg)
296	const struct mbuf *m;
297	int off;
298	int proto;
299	void *arg;
300{
301	struct ip6_hdr ip6;
302	struct gif_softc *sc;
303	struct sockaddr_in6 *src, *dst;
304	int addrmatch;
305
306	/* sanity check done in caller */
307	sc = (struct gif_softc *)arg;
308	src = (struct sockaddr_in6 *)sc->gif_psrc;
309	dst = (struct sockaddr_in6 *)sc->gif_pdst;
310
311	/* LINTED const cast */
312	m_copydata((struct mbuf *)m, 0, sizeof(ip6), (caddr_t)&ip6);
313
314	/* check for address match */
315	addrmatch = 0;
316	if (IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6.ip6_dst))
317		addrmatch |= 1;
318	if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6.ip6_src))
319		addrmatch |= 2;
320	if (addrmatch != 3)
321		return 0;
322
323	/* martian filters on outer source - done in ip6_input */
324
325	/* ingress filters on outer source */
326	if ((sc->gif_if.if_flags & IFF_LINK2) == 0 &&
327	    (m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) {
328		struct sockaddr_in6 sin6;
329		struct rtentry *rt;
330
331		bzero(&sin6, sizeof(sin6));
332		sin6.sin6_family = AF_INET6;
333		sin6.sin6_len = sizeof(struct sockaddr_in6);
334		sin6.sin6_addr = ip6.ip6_src;
335		/* XXX scopeid */
336		rt = rtalloc1((struct sockaddr *)&sin6, 0);
337		if (!rt || rt->rt_ifp != m->m_pkthdr.rcvif) {
338#if 0
339			log(LOG_WARNING, "%s: packet from %s dropped "
340			    "due to ingress filter\n", if_name(&sc->gif_if),
341			    ip6_sprintf(&sin6.sin6_addr));
342#endif
343			if (rt)
344				rtfree(rt);
345			return 0;
346		}
347		rtfree(rt);
348	}
349
350	return 128 * 2;
351}
352