xref: /netbsd-src/sys/netinet6/in6_gif.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: in6_gif.c,v 1.60 2014/05/18 14:46:16 rmind Exp $	*/
2 /*	$KAME: in6_gif.c,v 1.62 2001/07/29 04:27:25 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 <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: in6_gif.c,v 1.60 2014/05/18 14:46:16 rmind Exp $");
35 
36 #include "opt_inet.h"
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/socket.h>
41 #include <sys/sockio.h>
42 #include <sys/mbuf.h>
43 #include <sys/errno.h>
44 #include <sys/ioctl.h>
45 #include <sys/queue.h>
46 #include <sys/syslog.h>
47 #include <sys/protosw.h>
48 #include <sys/kernel.h>
49 
50 #include <net/if.h>
51 #include <net/route.h>
52 
53 #include <netinet/in.h>
54 #include <netinet/in_systm.h>
55 #ifdef INET
56 #include <netinet/ip.h>
57 #endif
58 #include <netinet/ip_encap.h>
59 #ifdef INET6
60 #include <netinet/ip6.h>
61 #include <netinet6/ip6_var.h>
62 #include <netinet6/ip6_private.h>
63 #include <netinet6/in6_gif.h>
64 #include <netinet6/in6_var.h>
65 #endif
66 #include <netinet6/ip6protosw.h>
67 #include <netinet/ip_ecn.h>
68 
69 #include <net/if_gif.h>
70 
71 #include <net/net_osdep.h>
72 
73 static int gif_validate6(const struct ip6_hdr *, struct gif_softc *,
74 	struct ifnet *);
75 
76 int	ip6_gif_hlim = GIF_HLIM;
77 
78 extern LIST_HEAD(, gif_softc) gif_softc_list;
79 
80 extern const struct ip6protosw in6_gif_protosw;
81 
82 /*
83  * family - family of the packet to be encapsulate.
84  */
85 
86 int
87 in6_gif_output(struct ifnet *ifp, int family, struct mbuf *m)
88 {
89 	struct rtentry *rt;
90 	struct gif_softc *sc = ifp->if_softc;
91 	struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
92 	struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
93 	struct ip6_hdr *ip6;
94 	int proto, error;
95 	u_int8_t itos, otos;
96 	union {
97 		struct sockaddr		dst;
98 		struct sockaddr_in6	dst6;
99 	} u;
100 
101 	if (sin6_src == NULL || sin6_dst == NULL ||
102 	    sin6_src->sin6_family != AF_INET6 ||
103 	    sin6_dst->sin6_family != AF_INET6) {
104 		m_freem(m);
105 		return EAFNOSUPPORT;
106 	}
107 
108 	switch (family) {
109 #ifdef INET
110 	case AF_INET:
111 	    {
112 		struct ip *ip;
113 
114 		proto = IPPROTO_IPV4;
115 		if (m->m_len < sizeof(*ip)) {
116 			m = m_pullup(m, sizeof(*ip));
117 			if (!m)
118 				return ENOBUFS;
119 		}
120 		ip = mtod(m, struct ip *);
121 		itos = ip->ip_tos;
122 		break;
123 	    }
124 #endif
125 #ifdef INET6
126 	case AF_INET6:
127 	    {
128 		proto = IPPROTO_IPV6;
129 		if (m->m_len < sizeof(*ip6)) {
130 			m = m_pullup(m, sizeof(*ip6));
131 			if (!m)
132 				return ENOBUFS;
133 		}
134 		ip6 = mtod(m, struct ip6_hdr *);
135 		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
136 		break;
137 	    }
138 #endif
139 	default:
140 #ifdef DEBUG
141 		printf("in6_gif_output: warning: unknown family %d passed\n",
142 			family);
143 #endif
144 		m_freem(m);
145 		return EAFNOSUPPORT;
146 	}
147 
148 	/* prepend new IP header */
149 	M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
150 	if (m && m->m_len < sizeof(struct ip6_hdr))
151 		m = m_pullup(m, sizeof(struct ip6_hdr));
152 	if (m == NULL)
153 		return ENOBUFS;
154 
155 	ip6 = mtod(m, struct ip6_hdr *);
156 	ip6->ip6_flow	= 0;
157 	ip6->ip6_vfc	&= ~IPV6_VERSION_MASK;
158 	ip6->ip6_vfc	|= IPV6_VERSION;
159 #if 0	/* ip6->ip6_plen will be filled by ip6_output */
160 	ip6->ip6_plen	= htons((u_int16_t)m->m_pkthdr.len);
161 #endif
162 	ip6->ip6_nxt	= proto;
163 	ip6->ip6_hlim	= ip6_gif_hlim;
164 	ip6->ip6_src	= sin6_src->sin6_addr;
165 	/* bidirectional configured tunnel mode */
166 	if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
167 		ip6->ip6_dst = sin6_dst->sin6_addr;
168 	else  {
169 		m_freem(m);
170 		return ENETUNREACH;
171 	}
172 	if (ifp->if_flags & IFF_LINK1)
173 		ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
174 	else
175 		ip_ecn_ingress(ECN_NOCARE, &otos, &itos);
176 	ip6->ip6_flow &= ~ntohl(0xff00000);
177 	ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
178 
179 	sockaddr_in6_init(&u.dst6, &sin6_dst->sin6_addr, 0, 0, 0);
180 	if ((rt = rtcache_lookup(&sc->gif_ro, &u.dst)) == NULL) {
181 		m_freem(m);
182 		return ENETUNREACH;
183 	}
184 
185 	/* If the route constitutes infinite encapsulation, punt. */
186 	if (rt->rt_ifp == ifp) {
187 		m_freem(m);
188 		return ENETUNREACH;	/* XXX */
189 	}
190 
191 #ifdef IPV6_MINMTU
192 	/*
193 	 * force fragmentation to minimum MTU, to avoid path MTU discovery.
194 	 * it is too painful to ask for resend of inner packet, to achieve
195 	 * path MTU discovery for encapsulated packets.
196 	 */
197 	error = ip6_output(m, 0, &sc->gif_ro, IPV6_MINMTU, NULL, NULL, NULL);
198 #else
199 	error = ip6_output(m, 0, &sc->gif_ro, 0, NULL, NULL, NULL);
200 #endif
201 
202 	return (error);
203 }
204 
205 int
206 in6_gif_input(struct mbuf **mp, int *offp, int proto)
207 {
208 	struct mbuf *m = *mp;
209 	struct ifnet *gifp = NULL;
210 	struct ip6_hdr *ip6;
211 	int af = 0;
212 	u_int32_t otos;
213 
214 	ip6 = mtod(m, struct ip6_hdr *);
215 
216 	gifp = (struct ifnet *)encap_getarg(m);
217 
218 	if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
219 		m_freem(m);
220 		IP6_STATINC(IP6_STAT_NOGIF);
221 		return IPPROTO_DONE;
222 	}
223 #ifndef GIF_ENCAPCHECK
224 	if (!gif_validate6(ip6, gifp->if_softc, m->m_pkthdr.rcvif)) {
225 		m_freem(m);
226 		IP6_STATINC(IP6_STAT_NOGIF);
227 		return IPPROTO_DONE;
228 	}
229 #endif
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 *ip6x;
259 		af = AF_INET6;
260 		if (m->m_len < sizeof(*ip6x)) {
261 			m = m_pullup(m, sizeof(*ip6x));
262 			if (!m)
263 				return IPPROTO_DONE;
264 		}
265 		ip6x = mtod(m, struct ip6_hdr *);
266 		if (gifp->if_flags & IFF_LINK1)
267 			ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6x->ip6_flow);
268 		else
269 			ip6_ecn_egress(ECN_NOCARE, &otos, &ip6x->ip6_flow);
270 		break;
271 	    }
272 #endif
273 	default:
274 		IP6_STATINC(IP6_STAT_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  * validate outer address.
285  */
286 static int
287 gif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc,
288 	struct ifnet *ifp)
289 {
290 	const struct sockaddr_in6 *src, *dst;
291 
292 	src = (struct sockaddr_in6 *)sc->gif_psrc;
293 	dst = (struct sockaddr_in6 *)sc->gif_pdst;
294 
295 	/* check for address match */
296 	if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) ||
297 	    !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_src))
298 		return 0;
299 
300 	/* martian filters on outer source - done in ip6_input */
301 
302 	/* ingress filters on outer source */
303 	if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
304 		union {
305 			struct sockaddr sa;
306 			struct sockaddr_in6 sin6;
307 		} u;
308 		struct rtentry *rt;
309 
310 		/* XXX scopeid */
311 		sockaddr_in6_init(&u.sin6, &ip6->ip6_src, 0, 0, 0);
312 		rt = rtalloc1(&u.sa, 0);
313 		if (rt == NULL || rt->rt_ifp != ifp) {
314 #if 0
315 			log(LOG_WARNING, "%s: packet from %s dropped "
316 			    "due to ingress filter\n", if_name(&sc->gif_if),
317 			    ip6_sprintf(&u.sin6.sin6_addr));
318 #endif
319 			if (rt != NULL)
320 				rtfree(rt);
321 			return 0;
322 		}
323 		rtfree(rt);
324 	}
325 
326 	return 128 * 2;
327 }
328 
329 #ifdef GIF_ENCAPCHECK
330 /*
331  * we know that we are in IFF_UP, outer address available, and outer family
332  * matched the physical addr family.  see gif_encapcheck().
333  */
334 int
335 gif_encapcheck6(struct mbuf *m, int off, int proto, void *arg)
336 {
337 	struct ip6_hdr ip6;
338 	struct gif_softc *sc;
339 	struct ifnet *ifp;
340 
341 	/* sanity check done in caller */
342 	sc = arg;
343 
344 	m_copydata(m, 0, sizeof(ip6), (void *)&ip6);
345 	ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL;
346 
347 	return gif_validate6(&ip6, sc, ifp);
348 }
349 #endif
350 
351 int
352 in6_gif_attach(struct gif_softc *sc)
353 {
354 #ifndef GIF_ENCAPCHECK
355 	struct sockaddr_in6 mask6;
356 
357 	memset(&mask6, 0, sizeof(mask6));
358 	mask6.sin6_len = sizeof(struct sockaddr_in6);
359 	mask6.sin6_addr.s6_addr32[0] = mask6.sin6_addr.s6_addr32[1] =
360 	    mask6.sin6_addr.s6_addr32[2] = mask6.sin6_addr.s6_addr32[3] = ~0;
361 
362 	if (!sc->gif_psrc || !sc->gif_pdst)
363 		return EINVAL;
364 	sc->encap_cookie6 = encap_attach(AF_INET6, -1, sc->gif_psrc,
365 	    (struct sockaddr *)&mask6, sc->gif_pdst, (struct sockaddr *)&mask6,
366 	    (const void *)&in6_gif_protosw, sc);
367 #else
368 	sc->encap_cookie6 = encap_attach_func(AF_INET6, -1, gif_encapcheck,
369 	    (struct protosw *)&in6_gif_protosw, sc);
370 #endif
371 	if (sc->encap_cookie6 == NULL)
372 		return EEXIST;
373 	return 0;
374 }
375 
376 int
377 in6_gif_detach(struct gif_softc *sc)
378 {
379 	int error;
380 
381 	error = encap_detach(sc->encap_cookie6);
382 	if (error == 0)
383 		sc->encap_cookie6 = NULL;
384 
385 	rtcache_free(&sc->gif_ro);
386 
387 	return error;
388 }
389 
390 void *
391 in6_gif_ctlinput(int cmd, const struct sockaddr *sa, void *d)
392 {
393 	struct gif_softc *sc;
394 	struct ip6ctlparam *ip6cp = NULL;
395 	struct ip6_hdr *ip6;
396 	const struct sockaddr_in6 *dst6;
397 
398 	if (sa->sa_family != AF_INET6 ||
399 	    sa->sa_len != sizeof(struct sockaddr_in6))
400 		return NULL;
401 
402 	if ((unsigned)cmd >= PRC_NCMDS)
403 		return NULL;
404 	if (cmd == PRC_HOSTDEAD)
405 		d = NULL;
406 	else if (inet6ctlerrmap[cmd] == 0)
407 		return NULL;
408 
409 	/* if the parameter is from icmp6, decode it. */
410 	if (d != NULL) {
411 		ip6cp = (struct ip6ctlparam *)d;
412 		ip6 = ip6cp->ip6c_ip6;
413 	} else {
414 		ip6 = NULL;
415 	}
416 
417 	if (!ip6)
418 		return NULL;
419 
420 	/*
421 	 * for now we don't care which type it was, just flush the route cache.
422 	 * XXX slow.  sc (or sc->encap_cookie6) should be passed from
423 	 * ip_encap.c.
424 	 */
425 	LIST_FOREACH(sc, &gif_softc_list, gif_list) {
426 		if ((sc->gif_if.if_flags & IFF_RUNNING) == 0)
427 			continue;
428 		if (sc->gif_psrc->sa_family != AF_INET6)
429 			continue;
430 
431 		dst6 = satocsin6(rtcache_getdst(&sc->gif_ro));
432 		/* XXX scope */
433 		if (dst6 == NULL)
434 			;
435 		else if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst6->sin6_addr))
436 			rtcache_free(&sc->gif_ro);
437 	}
438 
439 	return NULL;
440 }
441 
442 PR_WRAP_CTLINPUT(in6_gif_ctlinput)
443 PR_WRAP_CTLOUTPUT(rip6_ctloutput)
444 
445 #define	in6_gif_ctlinput	in6_gif_ctlinput_wrapper
446 #define	rip6_ctloutput		rip6_ctloutput_wrapper
447 
448 extern struct domain inet6domain;
449 
450 const struct ip6protosw in6_gif_protosw = {
451 	.pr_type	= SOCK_RAW,
452 	.pr_domain	= &inet6domain,
453 	.pr_protocol	= 0 /* IPPROTO_IPV[46] */,
454 	.pr_flags	= PR_ATOMIC | PR_ADDR,
455 	.pr_input	= in6_gif_input,
456 	.pr_output	= rip6_output,
457 	.pr_ctlinput	= in6_gif_ctlinput,
458 	.pr_ctloutput	= rip6_ctloutput,
459 	.pr_usrreqs	= &rip6_usrreqs,
460 };
461