in6_gif.c revision 1.12
1/*	$NetBSD: in6_gif.c,v 1.12 2000/02/07 06:15:17 itojun Exp $	*/
2
3/*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/*
33 * in6_gif.c
34 */
35
36#include "opt_inet.h"
37#include "opt_ipsec.h"
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/socket.h>
42#include <sys/sockio.h>
43#include <sys/mbuf.h>
44#include <sys/errno.h>
45#include <sys/ioctl.h>
46#include <sys/protosw.h>
47
48#include <net/if.h>
49#include <net/route.h>
50
51#include <netinet/in.h>
52#include <netinet/in_systm.h>
53#ifdef INET
54#include <netinet/ip.h>
55#endif
56#ifdef INET6
57#include <netinet/ip6.h>
58#include <netinet6/ip6_var.h>
59#include <netinet6/in6_gif.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	default:
122#ifdef DEBUG
123		printf("in6_gif_output: warning: unknown family %d passed\n",
124			family);
125#endif
126		m_freem(m);
127		return EAFNOSUPPORT;
128	}
129
130	/* prepend new IP header */
131	M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
132	if (m && m->m_len < sizeof(struct ip6_hdr))
133		m = m_pullup(m, sizeof(struct ip6_hdr));
134	if (m == NULL) {
135		printf("ENOBUFS in in6_gif_output %d\n", __LINE__);
136		return ENOBUFS;
137	}
138
139	ip6 = mtod(m, struct ip6_hdr *);
140	ip6->ip6_flow	= 0;
141	ip6->ip6_vfc	&= ~IPV6_VERSION_MASK;
142	ip6->ip6_vfc	|= IPV6_VERSION;
143	ip6->ip6_plen	= htons((u_short)m->m_pkthdr.len);
144	ip6->ip6_nxt	= proto;
145	ip6->ip6_hlim	= ip6_gif_hlim;
146	ip6->ip6_src	= sin6_src->sin6_addr;
147	if (ifp->if_flags & IFF_LINK0) {
148		/* multi-destination mode */
149		if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
150			ip6->ip6_dst = sin6_dst->sin6_addr;
151		else if (rt) {
152			if (family != AF_INET6) {
153				m_freem(m);
154				return EINVAL;	/*XXX*/
155			}
156			ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
157		} else {
158			m_freem(m);
159			return ENETUNREACH;
160		}
161	} else {
162		/* bidirectional configured tunnel mode */
163		if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
164			ip6->ip6_dst = sin6_dst->sin6_addr;
165		else  {
166			m_freem(m);
167			return ENETUNREACH;
168		}
169	}
170	if (ifp->if_flags & IFF_LINK1) {
171		otos = 0;
172		ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
173		ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
174	}
175
176	if (dst->sin6_family != sin6_dst->sin6_family ||
177	     !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) {
178		/* cache route doesn't match */
179		bzero(dst, sizeof(*dst));
180		dst->sin6_family = sin6_dst->sin6_family;
181		dst->sin6_len = sizeof(struct sockaddr_in6);
182		dst->sin6_addr = sin6_dst->sin6_addr;
183		if (sc->gif_ro6.ro_rt) {
184			RTFREE(sc->gif_ro6.ro_rt);
185			sc->gif_ro6.ro_rt = NULL;
186		}
187#if 0
188		sc->gif_if.if_mtu = GIF_MTU;
189#endif
190	}
191
192	if (sc->gif_ro6.ro_rt == NULL) {
193		rtalloc((struct route *)&sc->gif_ro6);
194		if (sc->gif_ro6.ro_rt == NULL) {
195			m_freem(m);
196			return ENETUNREACH;
197		}
198#if 0
199		ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu
200			- sizeof(struct ip6_hdr);
201#endif
202	}
203
204#ifdef IPSEC
205	m->m_pkthdr.rcvif = NULL;
206#endif /*IPSEC*/
207	return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL));
208}
209
210int in6_gif_input(mp, offp, proto)
211	struct mbuf **mp;
212	int *offp, proto;
213{
214	struct mbuf *m = *mp;
215	struct gif_softc *sc;
216	struct ifnet *gifp = NULL;
217	struct ip6_hdr *ip6;
218	int i;
219	int af = 0;
220	u_int32_t otos;
221
222	ip6 = mtod(m, struct ip6_hdr *);
223
224#define satoin6(sa)	(((struct sockaddr_in6 *)(sa))->sin6_addr)
225	for (i = 0, sc = gif; i < ngif; i++, sc++) {
226		if (sc->gif_psrc == NULL ||
227		    sc->gif_pdst == NULL ||
228		    sc->gif_psrc->sa_family != AF_INET6 ||
229		    sc->gif_pdst->sa_family != AF_INET6) {
230			continue;
231		}
232		if ((sc->gif_if.if_flags & IFF_UP) == 0)
233			continue;
234		if ((sc->gif_if.if_flags & IFF_LINK0) &&
235		    IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
236		    IN6_IS_ADDR_UNSPECIFIED(&satoin6(sc->gif_pdst))) {
237			gifp = &sc->gif_if;
238			continue;
239		}
240		if (IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
241		    IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_pdst), &ip6->ip6_src)) {
242			gifp = &sc->gif_if;
243			break;
244		}
245	}
246
247	if (gifp == NULL) {
248		m_freem(m);
249		ip6stat.ip6s_nogif++;
250		return IPPROTO_DONE;
251	}
252
253	otos = ip6->ip6_flow;
254	m_adj(m, *offp);
255
256	switch (proto) {
257#ifdef INET
258	case IPPROTO_IPV4:
259	    {
260		struct ip *ip;
261		u_int8_t otos8;
262		af = AF_INET;
263		otos8 = (ntohl(otos) >> 20) & 0xff;
264		if (m->m_len < sizeof(*ip)) {
265			m = m_pullup(m, sizeof(*ip));
266			if (!m)
267				return IPPROTO_DONE;
268		}
269		ip = mtod(m, struct ip *);
270		if (gifp->if_flags & IFF_LINK1)
271			ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos);
272		break;
273	    }
274#endif /* INET */
275#ifdef INET6
276	case IPPROTO_IPV6:
277	    {
278		struct ip6_hdr *ip6;
279		af = AF_INET6;
280		if (m->m_len < sizeof(*ip6)) {
281			m = m_pullup(m, sizeof(*ip6));
282			if (!m)
283				return IPPROTO_DONE;
284		}
285		ip6 = mtod(m, struct ip6_hdr *);
286		if (gifp->if_flags & IFF_LINK1)
287			ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow);
288		break;
289	    }
290#endif
291	default:
292		ip6stat.ip6s_nogif++;
293		m_freem(m);
294		return IPPROTO_DONE;
295	}
296
297	gif_input(m, af, gifp);
298	return IPPROTO_DONE;
299}
300