in6_gif.c revision 1.82
1/*	$NetBSD: in6_gif.c,v 1.82 2016/12/14 11:19:15 knakahara 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.82 2016/12/14 11:19:15 knakahara Exp $");
35
36#ifdef _KERNEL_OPT
37#include "opt_inet.h"
38#endif
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/socket.h>
43#include <sys/sockio.h>
44#include <sys/mbuf.h>
45#include <sys/errno.h>
46#include <sys/ioctl.h>
47#include <sys/queue.h>
48#include <sys/syslog.h>
49#include <sys/kernel.h>
50
51#include <net/if.h>
52#include <net/route.h>
53
54#include <netinet/in.h>
55#include <netinet/in_systm.h>
56#ifdef INET
57#include <netinet/ip.h>
58#endif
59#include <netinet/ip_encap.h>
60#ifdef INET6
61#include <netinet/ip6.h>
62#include <netinet6/ip6_var.h>
63#include <netinet6/ip6_private.h>
64#include <netinet6/in6_gif.h>
65#include <netinet6/in6_var.h>
66#endif
67#include <netinet6/ip6protosw.h> /* for struct ip6ctlparam */
68#include <netinet/ip_ecn.h>
69
70#include <net/if_gif.h>
71
72#include <net/net_osdep.h>
73
74static int gif_validate6(const struct ip6_hdr *, struct gif_softc *,
75	struct ifnet *);
76
77int	ip6_gif_hlim = GIF_HLIM;
78
79static const struct encapsw in6_gif_encapsw;
80
81/*
82 * family - family of the packet to be encapsulate.
83 */
84
85int
86in6_gif_output(struct ifnet *ifp, int family, struct mbuf *m)
87{
88	struct rtentry *rt;
89	struct route *ro;
90	struct gif_softc *sc = ifp->if_softc;
91	struct sockaddr_in6 *sin6_src = satosin6(sc->gif_psrc);
92	struct sockaddr_in6 *sin6_dst = satosin6(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	ro = percpu_getref(sc->gif_ro_percpu);
181	rt = rtcache_lookup(ro, &u.dst);
182	if (rt == NULL) {
183		percpu_putref(sc->gif_ro_percpu);
184		m_freem(m);
185		return ENETUNREACH;
186	}
187
188	/* If the route constitutes infinite encapsulation, punt. */
189	if (rt->rt_ifp == ifp) {
190		rtcache_unref(rt, ro);
191		rtcache_free(ro);
192		percpu_putref(sc->gif_ro_percpu);
193		m_freem(m);
194		return ENETUNREACH;	/* XXX */
195	}
196	rtcache_unref(rt, ro);
197
198#ifdef IPV6_MINMTU
199	/*
200	 * force fragmentation to minimum MTU, to avoid path MTU discovery.
201	 * it is too painful to ask for resend of inner packet, to achieve
202	 * path MTU discovery for encapsulated packets.
203	 */
204	error = ip6_output(m, 0, ro, IPV6_MINMTU, NULL, NULL, NULL);
205#else
206	error = ip6_output(m, 0, ro, 0, NULL, NULL, NULL);
207#endif
208	percpu_putref(sc->gif_ro_percpu);
209	return (error);
210}
211
212int
213in6_gif_input(struct mbuf **mp, int *offp, int 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|IFF_RUNNING))
226		!= (IFF_UP|IFF_RUNNING)) {
227		m_freem(m);
228		IP6_STATINC(IP6_STAT_NOGIF);
229		return IPPROTO_DONE;
230	}
231#ifndef GIF_ENCAPCHECK
232	struct gif_softc *sc = (struct gif_softc *)gifp->if_softc;
233	/* other CPU do delete_tunnel */
234	if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
235		m_freem(m);
236		IP6_STATINC(IP6_STAT_NOGIF);
237		return IPPROTO_DONE;
238	}
239
240	struct psref psref;
241	struct ifnet *rcvif = m_get_rcvif_psref(m, &psref);
242	if (rcvif == NULL || !gif_validate6(ip6, sc, rcvif)) {
243		m_put_rcvif_psref(rcvif, &psref);
244		m_freem(m);
245		IP6_STATINC(IP6_STAT_NOGIF);
246		return IPPROTO_DONE;
247	}
248	m_put_rcvif_psref(rcvif, &psref);
249#endif
250
251	otos = ip6->ip6_flow;
252	m_adj(m, *offp);
253
254	switch (proto) {
255#ifdef INET
256	case IPPROTO_IPV4:
257	    {
258		struct ip *ip;
259		u_int8_t otos8;
260		af = AF_INET;
261		otos8 = (ntohl(otos) >> 20) & 0xff;
262		if (m->m_len < sizeof(*ip)) {
263			m = m_pullup(m, sizeof(*ip));
264			if (!m)
265				return IPPROTO_DONE;
266		}
267		ip = mtod(m, struct ip *);
268		if (gifp->if_flags & IFF_LINK1)
269			ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos);
270		else
271			ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos);
272		break;
273	    }
274#endif /* INET */
275#ifdef INET6
276	case IPPROTO_IPV6:
277	    {
278		struct ip6_hdr *ip6x;
279		af = AF_INET6;
280		if (m->m_len < sizeof(*ip6x)) {
281			m = m_pullup(m, sizeof(*ip6x));
282			if (!m)
283				return IPPROTO_DONE;
284		}
285		ip6x = mtod(m, struct ip6_hdr *);
286		if (gifp->if_flags & IFF_LINK1)
287			ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6x->ip6_flow);
288		else
289			ip6_ecn_egress(ECN_NOCARE, &otos, &ip6x->ip6_flow);
290		break;
291	    }
292#endif
293	default:
294		IP6_STATINC(IP6_STAT_NOGIF);
295		m_freem(m);
296		return IPPROTO_DONE;
297	}
298
299	gif_input(m, af, gifp);
300	return IPPROTO_DONE;
301}
302
303/*
304 * validate outer address.
305 */
306static int
307gif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc,
308	struct ifnet *ifp)
309{
310	const struct sockaddr_in6 *src, *dst;
311
312	src = satosin6(sc->gif_psrc);
313	dst = satosin6(sc->gif_pdst);
314
315	/* check for address match */
316	if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) ||
317	    !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_src))
318		return 0;
319
320	/* martian filters on outer source - done in ip6_input */
321
322	/* ingress filters on outer source */
323	if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
324		union {
325			struct sockaddr sa;
326			struct sockaddr_in6 sin6;
327		} u;
328		struct rtentry *rt;
329
330		/* XXX scopeid */
331		sockaddr_in6_init(&u.sin6, &ip6->ip6_src, 0, 0, 0);
332		rt = rtalloc1(&u.sa, 0);
333		if (rt == NULL || rt->rt_ifp != ifp) {
334#if 0
335			log(LOG_WARNING, "%s: packet from %s dropped "
336			    "due to ingress filter\n", if_name(&sc->gif_if),
337			    ip6_sprintf(&u.sin6.sin6_addr));
338#endif
339			if (rt != NULL)
340				rt_unref(rt);
341			return 0;
342		}
343		rt_unref(rt);
344	}
345
346	return 128 * 2;
347}
348
349#ifdef GIF_ENCAPCHECK
350/*
351 * we know that we are in IFF_UP, outer address available, and outer family
352 * matched the physical addr family.  see gif_encapcheck().
353 */
354int
355gif_encapcheck6(struct mbuf *m, int off, int proto, void *arg)
356{
357	struct ip6_hdr ip6;
358	struct gif_softc *sc;
359	struct ifnet *ifp = NULL;
360	int r;
361	struct psref psref;
362
363	/* sanity check done in caller */
364	sc = arg;
365
366	m_copydata(m, 0, sizeof(ip6), (void *)&ip6);
367	if ((m->m_flags & M_PKTHDR) != 0)
368		ifp = m_get_rcvif_psref(m, &psref);
369
370	r = gif_validate6(&ip6, sc, ifp);
371
372	m_put_rcvif_psref(ifp, &psref);
373	return r;
374}
375#endif
376
377int
378in6_gif_attach(struct gif_softc *sc)
379{
380#ifndef GIF_ENCAPCHECK
381	struct sockaddr_in6 mask6;
382
383	memset(&mask6, 0, sizeof(mask6));
384	mask6.sin6_len = sizeof(struct sockaddr_in6);
385	mask6.sin6_addr.s6_addr32[0] = mask6.sin6_addr.s6_addr32[1] =
386	    mask6.sin6_addr.s6_addr32[2] = mask6.sin6_addr.s6_addr32[3] = ~0;
387
388	if (!sc->gif_psrc || !sc->gif_pdst)
389		return EINVAL;
390	sc->encap_cookie6 = encap_attach(AF_INET6, -1, sc->gif_psrc,
391	    sin6tosa(&mask6), sc->gif_pdst, sin6tosa(&mask6),
392	    (const void *)&in6_gif_encapsw, sc);
393#else
394	sc->encap_cookie6 = encap_attach_func(AF_INET6, -1, gif_encapcheck,
395	    &in6_gif_encapsw, sc);
396#endif
397	if (sc->encap_cookie6 == NULL)
398		return EEXIST;
399	return 0;
400}
401
402int
403in6_gif_detach(struct gif_softc *sc)
404{
405	int error;
406
407	error = in6_gif_pause(sc);
408
409	percpu_foreach(sc->gif_ro_percpu, gif_rtcache_free_pc, NULL);
410
411	return error;
412}
413
414int
415in6_gif_pause(struct gif_softc *sc)
416{
417	int error;
418
419	error = encap_detach(sc->encap_cookie6);
420	if (error == 0)
421		sc->encap_cookie6 = NULL;
422
423	return error;
424}
425
426void *
427in6_gif_ctlinput(int cmd, const struct sockaddr *sa, void *d, void *eparg)
428{
429	struct gif_softc *sc = eparg;
430	struct ip6ctlparam *ip6cp = NULL;
431	struct ip6_hdr *ip6;
432	const struct sockaddr_in6 *dst6;
433	struct route *ro;
434
435	if (sa->sa_family != AF_INET6 ||
436	    sa->sa_len != sizeof(struct sockaddr_in6))
437		return NULL;
438
439	if ((unsigned)cmd >= PRC_NCMDS)
440		return NULL;
441	if (cmd == PRC_HOSTDEAD)
442		d = NULL;
443	else if (inet6ctlerrmap[cmd] == 0)
444		return NULL;
445
446	/* if the parameter is from icmp6, decode it. */
447	if (d != NULL) {
448		ip6cp = (struct ip6ctlparam *)d;
449		ip6 = ip6cp->ip6c_ip6;
450	} else {
451		ip6 = NULL;
452	}
453
454	if (!ip6)
455		return NULL;
456
457	if ((sc->gif_if.if_flags & IFF_RUNNING) == 0)
458		return NULL;
459	if (sc->gif_psrc->sa_family != AF_INET6)
460		return NULL;
461
462	ro = percpu_getref(sc->gif_ro_percpu);
463	dst6 = satocsin6(rtcache_getdst(ro));
464	/* XXX scope */
465	if (dst6 == NULL)
466		;
467	else if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst6->sin6_addr))
468		rtcache_free(ro);
469
470	percpu_putref(sc->gif_ro_percpu);
471	return NULL;
472}
473
474ENCAP_PR_WRAP_CTLINPUT(in6_gif_ctlinput)
475#define	in6_gif_ctlinput	in6_gif_ctlinput_wrapper
476
477static const struct encapsw in6_gif_encapsw = {
478	.encapsw6 = {
479		.pr_input	= in6_gif_input,
480		.pr_ctlinput	= in6_gif_ctlinput,
481	}
482};
483