in6_gif.c revision d1ee857bcfed93b546537a014857026873662367
1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * Copyright (c) 2018 Andrey V. Elsukov <ae@FreeBSD.org>
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 *	$KAME: in6_gif.c,v 1.49 2001/05/14 14:02:17 itojun Exp $
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38#include "opt_inet.h"
39#include "opt_inet6.h"
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/jail.h>
44#include <sys/socket.h>
45#include <sys/sockio.h>
46#include <sys/mbuf.h>
47#include <sys/errno.h>
48#include <sys/kernel.h>
49#include <sys/syslog.h>
50#include <sys/sysctl.h>
51#include <sys/malloc.h>
52
53#include <net/ethernet.h>
54#include <net/if.h>
55#include <net/if_var.h>
56#include <net/route.h>
57#include <net/vnet.h>
58
59#include <netinet/in.h>
60#include <netinet/in_systm.h>
61#ifdef INET
62#include <netinet/ip.h>
63#include <netinet/ip_ecn.h>
64#endif
65#include <netinet/ip_encap.h>
66#include <netinet/ip6.h>
67#include <netinet6/ip6_var.h>
68#include <netinet6/in6_var.h>
69#include <netinet6/scope6_var.h>
70#include <netinet6/ip6_ecn.h>
71#include <netinet6/in6_fib.h>
72
73#include <net/if_gif.h>
74
75#define GIF_HLIM	30
76static VNET_DEFINE(int, ip6_gif_hlim) = GIF_HLIM;
77#define	V_ip6_gif_hlim			VNET(ip6_gif_hlim)
78
79SYSCTL_DECL(_net_inet6_ip6);
80SYSCTL_INT(_net_inet6_ip6, IPV6CTL_GIF_HLIM, gifhlim,
81    CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_gif_hlim), 0,
82    "Default hop limit for encapsulated packets");
83
84/*
85 * We keep interfaces in a hash table using src+dst as key.
86 * Interfaces with GIF_IGNORE_SOURCE flag are linked into plain list.
87 */
88static VNET_DEFINE(struct gif_list *, ipv6_hashtbl) = NULL;
89static VNET_DEFINE(struct gif_list, ipv6_list) = CK_LIST_HEAD_INITIALIZER();
90#define	V_ipv6_hashtbl		VNET(ipv6_hashtbl)
91#define	V_ipv6_list		VNET(ipv6_list)
92
93#define	GIF_HASH(src, dst)	(V_ipv6_hashtbl[\
94    in6_gif_hashval((src), (dst)) & (GIF_HASH_SIZE - 1)])
95#define	GIF_HASH_SC(sc)		GIF_HASH(&(sc)->gif_ip6hdr->ip6_src,\
96    &(sc)->gif_ip6hdr->ip6_dst)
97static uint32_t
98in6_gif_hashval(const struct in6_addr *src, const struct in6_addr *dst)
99{
100	uint32_t ret;
101
102	ret = fnv_32_buf(src, sizeof(*src), FNV1_32_INIT);
103	return (fnv_32_buf(dst, sizeof(*dst), ret));
104}
105
106static int
107in6_gif_checkdup(const struct gif_softc *sc, const struct in6_addr *src,
108    const struct in6_addr *dst)
109{
110	struct gif_softc *tmp;
111
112	if (sc->gif_family == AF_INET6 &&
113	    IN6_ARE_ADDR_EQUAL(&sc->gif_ip6hdr->ip6_src, src) &&
114	    IN6_ARE_ADDR_EQUAL(&sc->gif_ip6hdr->ip6_dst, dst))
115		return (EEXIST);
116
117	CK_LIST_FOREACH(tmp, &GIF_HASH(src, dst), chain) {
118		if (tmp == sc)
119			continue;
120		if (IN6_ARE_ADDR_EQUAL(&tmp->gif_ip6hdr->ip6_src, src) &&
121		    IN6_ARE_ADDR_EQUAL(&tmp->gif_ip6hdr->ip6_dst, dst))
122			return (EADDRNOTAVAIL);
123	}
124	return (0);
125}
126
127static void
128in6_gif_attach(struct gif_softc *sc)
129{
130
131	if (sc->gif_options & GIF_IGNORE_SOURCE)
132		CK_LIST_INSERT_HEAD(&V_ipv6_list, sc, chain);
133	else
134		CK_LIST_INSERT_HEAD(&GIF_HASH_SC(sc), sc, chain);
135}
136
137int
138in6_gif_setopts(struct gif_softc *sc, u_int options)
139{
140
141	/* NOTE: we are protected with gif_ioctl_sx lock */
142	MPASS(sc->gif_family == AF_INET6);
143	MPASS(sc->gif_options != options);
144
145	if ((options & GIF_IGNORE_SOURCE) !=
146	    (sc->gif_options & GIF_IGNORE_SOURCE)) {
147		CK_LIST_REMOVE(sc, chain);
148		sc->gif_options = options;
149		in6_gif_attach(sc);
150	}
151	return (0);
152}
153
154int
155in6_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t data)
156{
157	struct in6_ifreq *ifr = (struct in6_ifreq *)data;
158	struct sockaddr_in6 *dst, *src;
159	struct ip6_hdr *ip6;
160	int error;
161
162	/* NOTE: we are protected with gif_ioctl_sx lock */
163	error = EINVAL;
164	switch (cmd) {
165	case SIOCSIFPHYADDR_IN6:
166		src = &((struct in6_aliasreq *)data)->ifra_addr;
167		dst = &((struct in6_aliasreq *)data)->ifra_dstaddr;
168
169		/* sanity checks */
170		if (src->sin6_family != dst->sin6_family ||
171		    src->sin6_family != AF_INET6 ||
172		    src->sin6_len != dst->sin6_len ||
173		    src->sin6_len != sizeof(*src))
174			break;
175		if (IN6_IS_ADDR_UNSPECIFIED(&src->sin6_addr) ||
176		    IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) {
177			error = EADDRNOTAVAIL;
178			break;
179		}
180		/*
181		 * Check validity of the scope zone ID of the
182		 * addresses, and convert it into the kernel
183		 * internal form if necessary.
184		 */
185		if ((error = sa6_embedscope(src, 0)) != 0 ||
186		    (error = sa6_embedscope(dst, 0)) != 0)
187			break;
188
189		if (V_ipv6_hashtbl == NULL)
190			V_ipv6_hashtbl = gif_hashinit();
191		error = in6_gif_checkdup(sc, &src->sin6_addr,
192		    &dst->sin6_addr);
193		if (error == EADDRNOTAVAIL)
194			break;
195		if (error == EEXIST) {
196			/* Addresses are the same. Just return. */
197			error = 0;
198			break;
199		}
200		ip6 = malloc(sizeof(*ip6), M_GIF, M_WAITOK | M_ZERO);
201		ip6->ip6_src = src->sin6_addr;
202		ip6->ip6_dst = dst->sin6_addr;
203		if (sc->gif_family != 0) {
204			/* Detach existing tunnel first */
205			CK_LIST_REMOVE(sc, chain);
206			GIF_WAIT();
207			free(sc->gif_hdr, M_GIF);
208			/* XXX: should we notify about link state change? */
209		}
210		sc->gif_family = AF_INET6;
211		sc->gif_ip6hdr = ip6;
212		in6_gif_attach(sc);
213		break;
214	case SIOCGIFPSRCADDR_IN6:
215	case SIOCGIFPDSTADDR_IN6:
216		if (sc->gif_family != AF_INET6) {
217			error = EADDRNOTAVAIL;
218			break;
219		}
220		src = (struct sockaddr_in6 *)&ifr->ifr_addr;
221		memset(src, 0, sizeof(*src));
222		src->sin6_family = AF_INET6;
223		src->sin6_len = sizeof(*src);
224		src->sin6_addr = (cmd == SIOCGIFPSRCADDR_IN6) ?
225		    sc->gif_ip6hdr->ip6_src: sc->gif_ip6hdr->ip6_dst;
226		error = prison_if(curthread->td_ucred, (struct sockaddr *)src);
227		if (error == 0)
228			error = sa6_recoverscope(src);
229		if (error != 0)
230			memset(src, 0, sizeof(*src));
231		break;
232	}
233	return (error);
234}
235
236int
237in6_gif_output(struct ifnet *ifp, struct mbuf *m, int proto, uint8_t ecn)
238{
239	struct gif_softc *sc = ifp->if_softc;
240	struct ip6_hdr *ip6;
241	int len;
242
243	/* prepend new IP header */
244	MPASS(in_epoch());
245	len = sizeof(struct ip6_hdr);
246#ifndef __NO_STRICT_ALIGNMENT
247	if (proto == IPPROTO_ETHERIP)
248		len += ETHERIP_ALIGN;
249#endif
250	M_PREPEND(m, len, M_NOWAIT);
251	if (m == NULL)
252		return (ENOBUFS);
253#ifndef __NO_STRICT_ALIGNMENT
254	if (proto == IPPROTO_ETHERIP) {
255		len = mtod(m, vm_offset_t) & 3;
256		KASSERT(len == 0 || len == ETHERIP_ALIGN,
257		    ("in6_gif_output: unexpected misalignment"));
258		m->m_data += len;
259		m->m_len -= ETHERIP_ALIGN;
260	}
261#endif
262
263	ip6 = mtod(m, struct ip6_hdr *);
264	MPASS(sc->gif_family == AF_INET6);
265	bcopy(sc->gif_ip6hdr, ip6, sizeof(struct ip6_hdr));
266
267	ip6->ip6_flow  |= htonl((uint32_t)ecn << 20);
268	ip6->ip6_nxt	= proto;
269	ip6->ip6_hlim	= V_ip6_gif_hlim;
270	/*
271	 * force fragmentation to minimum MTU, to avoid path MTU discovery.
272	 * it is too painful to ask for resend of inner packet, to achieve
273	 * path MTU discovery for encapsulated packets.
274	 */
275	return (ip6_output(m, 0, NULL, IPV6_MINMTU, 0, NULL, NULL));
276}
277
278static int
279in6_gif_input(struct mbuf *m, int off, int proto, void *arg)
280{
281	struct gif_softc *sc = arg;
282	struct ifnet *gifp;
283	struct ip6_hdr *ip6;
284	uint8_t ecn;
285
286	MPASS(in_epoch());
287	if (sc == NULL) {
288		m_freem(m);
289		IP6STAT_INC(ip6s_nogif);
290		return (IPPROTO_DONE);
291	}
292	gifp = GIF2IFP(sc);
293	if ((gifp->if_flags & IFF_UP) != 0) {
294		ip6 = mtod(m, struct ip6_hdr *);
295		ecn = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
296		m_adj(m, off);
297		gif_input(m, gifp, proto, ecn);
298	} else {
299		m_freem(m);
300		IP6STAT_INC(ip6s_nogif);
301	}
302	return (IPPROTO_DONE);
303}
304
305static int
306in6_gif_lookup(const struct mbuf *m, int off, int proto, void **arg)
307{
308	const struct ip6_hdr *ip6;
309	struct gif_softc *sc;
310	int ret;
311
312	MPASS(in_epoch());
313	/*
314	 * NOTE: it is safe to iterate without any locking here, because softc
315	 * can be reclaimed only when we are not within net_epoch_preempt
316	 * section, but ip_encap lookup+input are executed in epoch section.
317	 */
318	ip6 = mtod(m, const struct ip6_hdr *);
319	ret = 0;
320	CK_LIST_FOREACH(sc, &GIF_HASH(&ip6->ip6_dst, &ip6->ip6_src), chain) {
321		/*
322		 * This is an inbound packet, its ip6_dst is source address
323		 * in softc.
324		 */
325		if (IN6_ARE_ADDR_EQUAL(&sc->gif_ip6hdr->ip6_src,
326		    &ip6->ip6_dst) &&
327		    IN6_ARE_ADDR_EQUAL(&sc->gif_ip6hdr->ip6_dst,
328		    &ip6->ip6_src)) {
329			ret = ENCAP_DRV_LOOKUP;
330			goto done;
331		}
332	}
333	/*
334	 * No exact match.
335	 * Check the list of interfaces with GIF_IGNORE_SOURCE flag.
336	 */
337	CK_LIST_FOREACH(sc, &V_ipv6_list, chain) {
338		if (IN6_ARE_ADDR_EQUAL(&sc->gif_ip6hdr->ip6_src,
339		    &ip6->ip6_dst)) {
340			ret = 128 + 8; /* src + proto */
341			goto done;
342		}
343	}
344	return (0);
345done:
346	if ((GIF2IFP(sc)->if_flags & IFF_UP) == 0)
347		return (0);
348	/* ingress filters on outer source */
349	if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0) {
350		struct nhop6_basic nh6;
351
352		if (fib6_lookup_nh_basic(sc->gif_fibnum, &ip6->ip6_src,
353		    ntohs(in6_getscope(&ip6->ip6_src)), 0, 0, &nh6) != 0)
354			return (0);
355
356		if (nh6.nh_ifp != m->m_pkthdr.rcvif)
357			return (0);
358	}
359	*arg = sc;
360	return (ret);
361}
362
363static struct {
364	const struct encap_config encap;
365	const struct encaptab *cookie;
366} ipv6_encap_cfg[] = {
367#ifdef INET
368	{
369		.encap = {
370			.proto = IPPROTO_IPV4,
371			.min_length = sizeof(struct ip6_hdr) +
372			    sizeof(struct ip),
373			.exact_match = ENCAP_DRV_LOOKUP,
374			.lookup = in6_gif_lookup,
375			.input = in6_gif_input
376		},
377	},
378#endif
379	{
380		.encap = {
381			.proto = IPPROTO_IPV6,
382			.min_length = 2 * sizeof(struct ip6_hdr),
383			.exact_match = ENCAP_DRV_LOOKUP,
384			.lookup = in6_gif_lookup,
385			.input = in6_gif_input
386		},
387	},
388	{
389		.encap = {
390			.proto = IPPROTO_ETHERIP,
391			.min_length = sizeof(struct ip6_hdr) +
392			    sizeof(struct etherip_header) +
393			    sizeof(struct ether_header),
394			.exact_match = ENCAP_DRV_LOOKUP,
395			.lookup = in6_gif_lookup,
396			.input = in6_gif_input
397		},
398	}
399};
400
401void
402in6_gif_init(void)
403{
404	int i;
405
406	if (!IS_DEFAULT_VNET(curvnet))
407		return;
408	for (i = 0; i < nitems(ipv6_encap_cfg); i++)
409		ipv6_encap_cfg[i].cookie = ip6_encap_attach(
410		    &ipv6_encap_cfg[i].encap, NULL, M_WAITOK);
411}
412
413void
414in6_gif_uninit(void)
415{
416	int i;
417
418	if (IS_DEFAULT_VNET(curvnet)) {
419		for (i = 0; i < nitems(ipv6_encap_cfg); i++)
420			ip6_encap_detach(ipv6_encap_cfg[i].cookie);
421	}
422	if (V_ipv6_hashtbl != NULL)
423		gif_hashdestroy(V_ipv6_hashtbl);
424}
425