xref: /netbsd-src/sys/netinet6/in6_gif.c (revision 95d875fb90b1458e4f1de6950286ddcd6644bc61)
1 /*	$NetBSD: in6_gif.c,v 1.9 1999/12/15 06:28:44 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 #if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
37 #include "opt_inet.h"
38 #ifdef __NetBSD__	/*XXX*/
39 #include "opt_ipsec.h"
40 #endif
41 #endif
42 
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/socket.h>
46 #include <sys/sockio.h>
47 #include <sys/mbuf.h>
48 #include <sys/errno.h>
49 #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
50 #include <sys/ioctl.h>
51 #endif
52 #include <sys/protosw.h>
53 
54 #include <net/if.h>
55 #include <net/route.h>
56 
57 #include <netinet/in.h>
58 #include <netinet/in_systm.h>
59 #ifdef INET
60 #include <netinet/ip.h>
61 #endif
62 #include <netinet6/ip6.h>
63 #include <netinet6/ip6_var.h>
64 #include <netinet6/in6_gif.h>
65 #ifdef INET6
66 #include <netinet6/ip6.h>
67 #endif
68 #include <netinet/ip_ecn.h>
69 
70 #include <net/if_gif.h>
71 
72 #include <net/net_osdep.h>
73 
74 int
75 in6_gif_output(ifp, family, m, rt)
76 	struct ifnet *ifp;
77 	int family; /* family of the packet to be encapsulate. */
78 	struct mbuf *m;
79 	struct rtentry *rt;
80 {
81 	struct gif_softc *sc = (struct gif_softc*)ifp;
82 	struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
83 	struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
84 	struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
85 	struct ip6_hdr *ip6;
86 	int proto;
87 	u_int8_t itos, otos;
88 
89 	if (sin6_src == NULL || sin6_dst == NULL ||
90 	    sin6_src->sin6_family != AF_INET6 ||
91 	    sin6_dst->sin6_family != AF_INET6) {
92 		m_freem(m);
93 		return EAFNOSUPPORT;
94 	}
95 
96 	switch (family) {
97 #ifdef INET
98 	case AF_INET:
99 	    {
100 		struct ip *ip;
101 
102 		proto = IPPROTO_IPV4;
103 		if (m->m_len < sizeof(*ip)) {
104 			m = m_pullup(m, sizeof(*ip));
105 			if (!m)
106 				return ENOBUFS;
107 		}
108 		ip = mtod(m, struct ip *);
109 		itos = ip->ip_tos;
110 		break;
111 	    }
112 #endif
113 #ifdef INET6
114 	case AF_INET6:
115 	    {
116 		struct ip6_hdr *ip6;
117 		proto = IPPROTO_IPV6;
118 		if (m->m_len < sizeof(*ip6)) {
119 			m = m_pullup(m, sizeof(*ip6));
120 			if (!m)
121 				return ENOBUFS;
122 		}
123 		ip6 = mtod(m, struct ip6_hdr *);
124 		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
125 		break;
126 	    }
127 #endif
128 	default:
129 #ifdef DIAGNOSTIC
130 		printf("in6_gif_output: warning: unknown family %d passed\n",
131 			family);
132 #endif
133 		m_freem(m);
134 		return EAFNOSUPPORT;
135 	}
136 
137 	/* prepend new IP header */
138 	M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
139 	if (m && m->m_len < sizeof(struct ip6_hdr))
140 		m = m_pullup(m, sizeof(struct ip6_hdr));
141 	if (m == NULL) {
142 		printf("ENOBUFS in in6_gif_output %d\n", __LINE__);
143 		return ENOBUFS;
144 	}
145 
146 	ip6 = mtod(m, struct ip6_hdr *);
147 	ip6->ip6_flow	= 0;
148 	ip6->ip6_vfc	&= ~IPV6_VERSION_MASK;
149 	ip6->ip6_vfc	|= IPV6_VERSION;
150 	ip6->ip6_plen	= htons((u_short)m->m_pkthdr.len);
151 	ip6->ip6_nxt	= proto;
152 	ip6->ip6_hlim	= ip6_gif_hlim;
153 	ip6->ip6_src	= sin6_src->sin6_addr;
154 	if (ifp->if_flags & IFF_LINK0) {
155 		/* multi-destination mode */
156 		if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
157 			ip6->ip6_dst = sin6_dst->sin6_addr;
158 		else if (rt) {
159 			if (family != AF_INET6) {
160 				m_freem(m);
161 				return EINVAL;	/*XXX*/
162 			}
163 			ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
164 		} else {
165 			m_freem(m);
166 			return ENETUNREACH;
167 		}
168 	} else {
169 		/* bidirectional configured tunnel mode */
170 		if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
171 			ip6->ip6_dst = sin6_dst->sin6_addr;
172 		else  {
173 			m_freem(m);
174 			return ENETUNREACH;
175 		}
176 	}
177 	if (ifp->if_flags & IFF_LINK1) {
178 		otos = 0;
179 		ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
180 		ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
181 	}
182 
183 	if (dst->sin6_family != sin6_dst->sin6_family ||
184 	     !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) {
185 		/* cache route doesn't match */
186 		bzero(dst, sizeof(*dst));
187 		dst->sin6_family = sin6_dst->sin6_family;
188 		dst->sin6_len = sizeof(struct sockaddr_in6);
189 		dst->sin6_addr = sin6_dst->sin6_addr;
190 		if (sc->gif_ro6.ro_rt) {
191 			RTFREE(sc->gif_ro6.ro_rt);
192 			sc->gif_ro6.ro_rt = NULL;
193 		}
194 #if 0
195 		sc->gif_if.if_mtu = GIF_MTU;
196 #endif
197 	}
198 
199 	if (sc->gif_ro6.ro_rt == NULL) {
200 		rtalloc((struct route *)&sc->gif_ro6);
201 		if (sc->gif_ro6.ro_rt == NULL) {
202 			m_freem(m);
203 			return ENETUNREACH;
204 		}
205 #if 0
206 		ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu
207 			- sizeof(struct ip6_hdr);
208 #endif
209 	}
210 
211 #ifdef IPSEC
212 #ifndef __OpenBSD__ /*KAME IPSEC*/
213 	m->m_pkthdr.rcvif = NULL;
214 #endif
215 #endif /*IPSEC*/
216 	return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL));
217 }
218 
219 int in6_gif_input(mp, offp, proto)
220 	struct mbuf **mp;
221 	int *offp, proto;
222 {
223 	struct mbuf *m = *mp;
224 	struct gif_softc *sc;
225 	struct ifnet *gifp = NULL;
226 	struct ip6_hdr *ip6;
227 	int i;
228 	int af = 0;
229 	u_int32_t otos;
230 
231 	ip6 = mtod(m, struct ip6_hdr *);
232 
233 #define satoin6(sa)	(((struct sockaddr_in6 *)(sa))->sin6_addr)
234 	for (i = 0, sc = gif; i < ngif; i++, sc++) {
235 		if (sc->gif_psrc == NULL ||
236 		    sc->gif_pdst == NULL ||
237 		    sc->gif_psrc->sa_family != AF_INET6 ||
238 		    sc->gif_pdst->sa_family != AF_INET6) {
239 			continue;
240 		}
241 		if ((sc->gif_if.if_flags & IFF_UP) == 0)
242 			continue;
243 		if ((sc->gif_if.if_flags & IFF_LINK0) &&
244 		    IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
245 		    IN6_IS_ADDR_UNSPECIFIED(&satoin6(sc->gif_pdst))) {
246 			gifp = &sc->gif_if;
247 			continue;
248 		}
249 		if (IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
250 		    IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_pdst), &ip6->ip6_src)) {
251 			gifp = &sc->gif_if;
252 			break;
253 		}
254 	}
255 
256 	if (gifp == NULL) {
257 		m_freem(m);
258 		ip6stat.ip6s_nogif++;
259 		return IPPROTO_DONE;
260 	}
261 
262 	otos = ip6->ip6_flow;
263 	m_adj(m, *offp);
264 
265 	switch (proto) {
266 #ifdef INET
267 	case IPPROTO_IPV4:
268 	    {
269 		struct ip *ip;
270 		u_int8_t otos8;
271 		af = AF_INET;
272 		otos8 = (ntohl(otos) >> 20) & 0xff;
273 		if (m->m_len < sizeof(*ip)) {
274 			m = m_pullup(m, sizeof(*ip));
275 			if (!m)
276 				return IPPROTO_DONE;
277 		}
278 		ip = mtod(m, struct ip *);
279 		if (gifp->if_flags & IFF_LINK1)
280 			ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos);
281 		break;
282 	    }
283 #endif /* INET */
284 #ifdef INET6
285 	case IPPROTO_IPV6:
286 	    {
287 		struct ip6_hdr *ip6;
288 		af = AF_INET6;
289 		if (m->m_len < sizeof(*ip6)) {
290 			m = m_pullup(m, sizeof(*ip6));
291 			if (!m)
292 				return IPPROTO_DONE;
293 		}
294 		ip6 = mtod(m, struct ip6_hdr *);
295 		if (gifp->if_flags & IFF_LINK1)
296 			ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow);
297 		break;
298 	    }
299 #endif
300 	default:
301 		ip6stat.ip6s_nogif++;
302 		m_freem(m);
303 		return IPPROTO_DONE;
304 	}
305 
306 	gif_input(m, af, gifp);
307 	return IPPROTO_DONE;
308 }
309