xref: /freebsd/sys/netinet/in_gif.c (revision 6573d7580b851d794b6c3cbd5ee3d7bf0b4c0ccb)
1c398230bSWarner Losh /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  *
4cfa1ca9dSYoshinobu Inoue  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5b941bc1dSAndrey V. Elsukov  * Copyright (c) 2018 Andrey V. Elsukov <ae@FreeBSD.org>
6cfa1ca9dSYoshinobu Inoue  * All rights reserved.
7cfa1ca9dSYoshinobu Inoue  *
8cfa1ca9dSYoshinobu Inoue  * Redistribution and use in source and binary forms, with or without
9cfa1ca9dSYoshinobu Inoue  * modification, are permitted provided that the following conditions
10cfa1ca9dSYoshinobu Inoue  * are met:
11cfa1ca9dSYoshinobu Inoue  * 1. Redistributions of source code must retain the above copyright
12cfa1ca9dSYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer.
13cfa1ca9dSYoshinobu Inoue  * 2. Redistributions in binary form must reproduce the above copyright
14cfa1ca9dSYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer in the
15cfa1ca9dSYoshinobu Inoue  *    documentation and/or other materials provided with the distribution.
16cfa1ca9dSYoshinobu Inoue  * 3. Neither the name of the project nor the names of its contributors
17cfa1ca9dSYoshinobu Inoue  *    may be used to endorse or promote products derived from this software
18cfa1ca9dSYoshinobu Inoue  *    without specific prior written permission.
19cfa1ca9dSYoshinobu Inoue  *
20cfa1ca9dSYoshinobu Inoue  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21cfa1ca9dSYoshinobu Inoue  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22cfa1ca9dSYoshinobu Inoue  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23cfa1ca9dSYoshinobu Inoue  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24cfa1ca9dSYoshinobu Inoue  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25cfa1ca9dSYoshinobu Inoue  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26cfa1ca9dSYoshinobu Inoue  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27cfa1ca9dSYoshinobu Inoue  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28cfa1ca9dSYoshinobu Inoue  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29cfa1ca9dSYoshinobu Inoue  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30cfa1ca9dSYoshinobu Inoue  * SUCH DAMAGE.
310b9f5f8aSAndrey V. Elsukov  *
320b9f5f8aSAndrey V. Elsukov  *	$KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 itojun Exp $
33cfa1ca9dSYoshinobu Inoue  */
34cfa1ca9dSYoshinobu Inoue 
354b421e2dSMike Silbersack #include <sys/cdefs.h>
364b421e2dSMike Silbersack __FBSDID("$FreeBSD$");
374b421e2dSMike Silbersack 
38686cdd19SJun-ichiro itojun Hagino #include "opt_inet.h"
39cfa1ca9dSYoshinobu Inoue #include "opt_inet6.h"
40cfa1ca9dSYoshinobu Inoue 
41cfa1ca9dSYoshinobu Inoue #include <sys/param.h>
42cfa1ca9dSYoshinobu Inoue #include <sys/systm.h>
43b941bc1dSAndrey V. Elsukov #include <sys/jail.h>
44cfa1ca9dSYoshinobu Inoue #include <sys/socket.h>
45cfa1ca9dSYoshinobu Inoue #include <sys/sockio.h>
46cfa1ca9dSYoshinobu Inoue #include <sys/mbuf.h>
47cfa1ca9dSYoshinobu Inoue #include <sys/errno.h>
48cfa1ca9dSYoshinobu Inoue #include <sys/kernel.h>
49cfa1ca9dSYoshinobu Inoue #include <sys/sysctl.h>
50686cdd19SJun-ichiro itojun Hagino #include <sys/malloc.h>
51*6573d758SMatt Macy #include <sys/proc.h>
52cfa1ca9dSYoshinobu Inoue 
53b941bc1dSAndrey V. Elsukov #include <net/ethernet.h>
54cfa1ca9dSYoshinobu Inoue #include <net/if.h>
5576039bc8SGleb Smirnoff #include <net/if_var.h>
56cfa1ca9dSYoshinobu Inoue #include <net/route.h>
57530c0060SRobert Watson #include <net/vnet.h>
58cfa1ca9dSYoshinobu Inoue 
59cfa1ca9dSYoshinobu Inoue #include <netinet/in.h>
60cfa1ca9dSYoshinobu Inoue #include <netinet/in_systm.h>
61cfa1ca9dSYoshinobu Inoue #include <netinet/ip.h>
626a800098SYoshinobu Inoue #include <netinet/ip_var.h>
63686cdd19SJun-ichiro itojun Hagino #include <netinet/in_var.h>
64686cdd19SJun-ichiro itojun Hagino #include <netinet/ip_encap.h>
656a800098SYoshinobu Inoue #include <netinet/ip_ecn.h>
6665ff3638SAlexander V. Chernikov #include <netinet/in_fib.h>
67686cdd19SJun-ichiro itojun Hagino 
686a800098SYoshinobu Inoue #ifdef INET6
69686cdd19SJun-ichiro itojun Hagino #include <netinet/ip6.h>
706a800098SYoshinobu Inoue #endif
71cfa1ca9dSYoshinobu Inoue 
72cfa1ca9dSYoshinobu Inoue #include <net/if_gif.h>
73cfa1ca9dSYoshinobu Inoue 
74132c4490SAndrey V. Elsukov #define GIF_TTL		30
75132c4490SAndrey V. Elsukov static VNET_DEFINE(int, ip_gif_ttl) = GIF_TTL;
7682cea7e6SBjoern A. Zeeb #define	V_ip_gif_ttl		VNET(ip_gif_ttl)
776df8a710SGleb Smirnoff SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_VNET | CTLFLAG_RW,
786d8fdfa9SAndrey V. Elsukov     &VNET_NAME(ip_gif_ttl), 0, "Default TTL value for encapsulated packets");
79cfa1ca9dSYoshinobu Inoue 
80b941bc1dSAndrey V. Elsukov /*
81b941bc1dSAndrey V. Elsukov  * We keep interfaces in a hash table using src+dst as key.
82b941bc1dSAndrey V. Elsukov  * Interfaces with GIF_IGNORE_SOURCE flag are linked into plain list.
83b941bc1dSAndrey V. Elsukov  */
84b941bc1dSAndrey V. Elsukov static VNET_DEFINE(struct gif_list *, ipv4_hashtbl) = NULL;
85b941bc1dSAndrey V. Elsukov static VNET_DEFINE(struct gif_list, ipv4_list) = CK_LIST_HEAD_INITIALIZER();
86b941bc1dSAndrey V. Elsukov #define	V_ipv4_hashtbl		VNET(ipv4_hashtbl)
87b941bc1dSAndrey V. Elsukov #define	V_ipv4_list		VNET(ipv4_list)
88b941bc1dSAndrey V. Elsukov 
89b941bc1dSAndrey V. Elsukov #define	GIF_HASH(src, dst)	(V_ipv4_hashtbl[\
90b941bc1dSAndrey V. Elsukov     in_gif_hashval((src), (dst)) & (GIF_HASH_SIZE - 1)])
91b941bc1dSAndrey V. Elsukov #define	GIF_HASH_SC(sc)		GIF_HASH((sc)->gif_iphdr->ip_src.s_addr,\
92b941bc1dSAndrey V. Elsukov     (sc)->gif_iphdr->ip_dst.s_addr)
93b941bc1dSAndrey V. Elsukov static uint32_t
94b941bc1dSAndrey V. Elsukov in_gif_hashval(in_addr_t src, in_addr_t dst)
95b941bc1dSAndrey V. Elsukov {
96b941bc1dSAndrey V. Elsukov 	uint32_t ret;
97b941bc1dSAndrey V. Elsukov 
98b941bc1dSAndrey V. Elsukov 	ret = fnv_32_buf(&src, sizeof(src), FNV1_32_INIT);
99b941bc1dSAndrey V. Elsukov 	return (fnv_32_buf(&dst, sizeof(dst), ret));
100b941bc1dSAndrey V. Elsukov }
101b941bc1dSAndrey V. Elsukov 
102b941bc1dSAndrey V. Elsukov static int
103b941bc1dSAndrey V. Elsukov in_gif_checkdup(const struct gif_softc *sc, in_addr_t src, in_addr_t dst)
104b941bc1dSAndrey V. Elsukov {
105b941bc1dSAndrey V. Elsukov 	struct gif_softc *tmp;
106b941bc1dSAndrey V. Elsukov 
107b941bc1dSAndrey V. Elsukov 	if (sc->gif_family == AF_INET &&
108b941bc1dSAndrey V. Elsukov 	    sc->gif_iphdr->ip_src.s_addr == src &&
109b941bc1dSAndrey V. Elsukov 	    sc->gif_iphdr->ip_dst.s_addr == dst)
110b941bc1dSAndrey V. Elsukov 		return (EEXIST);
111b941bc1dSAndrey V. Elsukov 
112b941bc1dSAndrey V. Elsukov 	CK_LIST_FOREACH(tmp, &GIF_HASH(src, dst), chain) {
113b941bc1dSAndrey V. Elsukov 		if (tmp == sc)
114b941bc1dSAndrey V. Elsukov 			continue;
115b941bc1dSAndrey V. Elsukov 		if (tmp->gif_iphdr->ip_src.s_addr == src &&
116b941bc1dSAndrey V. Elsukov 		    tmp->gif_iphdr->ip_dst.s_addr == dst)
117b941bc1dSAndrey V. Elsukov 			return (EADDRNOTAVAIL);
118b941bc1dSAndrey V. Elsukov 	}
119b941bc1dSAndrey V. Elsukov 	return (0);
120b941bc1dSAndrey V. Elsukov }
121b941bc1dSAndrey V. Elsukov 
122b941bc1dSAndrey V. Elsukov static void
123b941bc1dSAndrey V. Elsukov in_gif_attach(struct gif_softc *sc)
124b941bc1dSAndrey V. Elsukov {
125b941bc1dSAndrey V. Elsukov 
126b941bc1dSAndrey V. Elsukov 	if (sc->gif_options & GIF_IGNORE_SOURCE)
127b941bc1dSAndrey V. Elsukov 		CK_LIST_INSERT_HEAD(&V_ipv4_list, sc, chain);
128b941bc1dSAndrey V. Elsukov 	else
129b941bc1dSAndrey V. Elsukov 		CK_LIST_INSERT_HEAD(&GIF_HASH_SC(sc), sc, chain);
130b941bc1dSAndrey V. Elsukov }
131b941bc1dSAndrey V. Elsukov 
132b941bc1dSAndrey V. Elsukov int
133b941bc1dSAndrey V. Elsukov in_gif_setopts(struct gif_softc *sc, u_int options)
134b941bc1dSAndrey V. Elsukov {
135b941bc1dSAndrey V. Elsukov 
136b941bc1dSAndrey V. Elsukov 	/* NOTE: we are protected with gif_ioctl_sx lock */
137b941bc1dSAndrey V. Elsukov 	MPASS(sc->gif_family == AF_INET);
138b941bc1dSAndrey V. Elsukov 	MPASS(sc->gif_options != options);
139b941bc1dSAndrey V. Elsukov 
140b941bc1dSAndrey V. Elsukov 	if ((options & GIF_IGNORE_SOURCE) !=
141b941bc1dSAndrey V. Elsukov 	    (sc->gif_options & GIF_IGNORE_SOURCE)) {
142b941bc1dSAndrey V. Elsukov 		CK_LIST_REMOVE(sc, chain);
143b941bc1dSAndrey V. Elsukov 		sc->gif_options = options;
144b941bc1dSAndrey V. Elsukov 		in_gif_attach(sc);
145b941bc1dSAndrey V. Elsukov 	}
146b941bc1dSAndrey V. Elsukov 	return (0);
147b941bc1dSAndrey V. Elsukov }
148b941bc1dSAndrey V. Elsukov 
149b941bc1dSAndrey V. Elsukov int
150b941bc1dSAndrey V. Elsukov in_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t data)
151b941bc1dSAndrey V. Elsukov {
152b941bc1dSAndrey V. Elsukov 	struct ifreq *ifr = (struct ifreq *)data;
153b941bc1dSAndrey V. Elsukov 	struct sockaddr_in *dst, *src;
154b941bc1dSAndrey V. Elsukov 	struct ip *ip;
155b941bc1dSAndrey V. Elsukov 	int error;
156b941bc1dSAndrey V. Elsukov 
157b941bc1dSAndrey V. Elsukov 	/* NOTE: we are protected with gif_ioctl_sx lock */
158b941bc1dSAndrey V. Elsukov 	error = EINVAL;
159b941bc1dSAndrey V. Elsukov 	switch (cmd) {
160b941bc1dSAndrey V. Elsukov 	case SIOCSIFPHYADDR:
161b941bc1dSAndrey V. Elsukov 		src = &((struct in_aliasreq *)data)->ifra_addr;
162b941bc1dSAndrey V. Elsukov 		dst = &((struct in_aliasreq *)data)->ifra_dstaddr;
163b941bc1dSAndrey V. Elsukov 
164b941bc1dSAndrey V. Elsukov 		/* sanity checks */
165b941bc1dSAndrey V. Elsukov 		if (src->sin_family != dst->sin_family ||
166b941bc1dSAndrey V. Elsukov 		    src->sin_family != AF_INET ||
167b941bc1dSAndrey V. Elsukov 		    src->sin_len != dst->sin_len ||
168b941bc1dSAndrey V. Elsukov 		    src->sin_len != sizeof(*src))
169b941bc1dSAndrey V. Elsukov 			break;
170b941bc1dSAndrey V. Elsukov 		if (src->sin_addr.s_addr == INADDR_ANY ||
171b941bc1dSAndrey V. Elsukov 		    dst->sin_addr.s_addr == INADDR_ANY) {
172b941bc1dSAndrey V. Elsukov 			error = EADDRNOTAVAIL;
173b941bc1dSAndrey V. Elsukov 			break;
174b941bc1dSAndrey V. Elsukov 		}
175b941bc1dSAndrey V. Elsukov 		if (V_ipv4_hashtbl == NULL)
176b941bc1dSAndrey V. Elsukov 			V_ipv4_hashtbl = gif_hashinit();
177b941bc1dSAndrey V. Elsukov 		error = in_gif_checkdup(sc, src->sin_addr.s_addr,
178b941bc1dSAndrey V. Elsukov 		    dst->sin_addr.s_addr);
179b941bc1dSAndrey V. Elsukov 		if (error == EADDRNOTAVAIL)
180b941bc1dSAndrey V. Elsukov 			break;
181b941bc1dSAndrey V. Elsukov 		if (error == EEXIST) {
182b941bc1dSAndrey V. Elsukov 			/* Addresses are the same. Just return. */
183b941bc1dSAndrey V. Elsukov 			error = 0;
184b941bc1dSAndrey V. Elsukov 			break;
185b941bc1dSAndrey V. Elsukov 		}
186b941bc1dSAndrey V. Elsukov 		ip = malloc(sizeof(*ip), M_GIF, M_WAITOK | M_ZERO);
187b941bc1dSAndrey V. Elsukov 		ip->ip_src.s_addr = src->sin_addr.s_addr;
188b941bc1dSAndrey V. Elsukov 		ip->ip_dst.s_addr = dst->sin_addr.s_addr;
189b941bc1dSAndrey V. Elsukov 		if (sc->gif_family != 0) {
190b941bc1dSAndrey V. Elsukov 			/* Detach existing tunnel first */
191b941bc1dSAndrey V. Elsukov 			CK_LIST_REMOVE(sc, chain);
192b941bc1dSAndrey V. Elsukov 			GIF_WAIT();
193b941bc1dSAndrey V. Elsukov 			free(sc->gif_hdr, M_GIF);
194b941bc1dSAndrey V. Elsukov 			/* XXX: should we notify about link state change? */
195b941bc1dSAndrey V. Elsukov 		}
196b941bc1dSAndrey V. Elsukov 		sc->gif_family = AF_INET;
197b941bc1dSAndrey V. Elsukov 		sc->gif_iphdr = ip;
198b941bc1dSAndrey V. Elsukov 		in_gif_attach(sc);
199b941bc1dSAndrey V. Elsukov 		break;
200b941bc1dSAndrey V. Elsukov 	case SIOCGIFPSRCADDR:
201b941bc1dSAndrey V. Elsukov 	case SIOCGIFPDSTADDR:
202b941bc1dSAndrey V. Elsukov 		if (sc->gif_family != AF_INET) {
203b941bc1dSAndrey V. Elsukov 			error = EADDRNOTAVAIL;
204b941bc1dSAndrey V. Elsukov 			break;
205b941bc1dSAndrey V. Elsukov 		}
206b941bc1dSAndrey V. Elsukov 		src = (struct sockaddr_in *)&ifr->ifr_addr;
207b941bc1dSAndrey V. Elsukov 		memset(src, 0, sizeof(*src));
208b941bc1dSAndrey V. Elsukov 		src->sin_family = AF_INET;
209b941bc1dSAndrey V. Elsukov 		src->sin_len = sizeof(*src);
210b941bc1dSAndrey V. Elsukov 		src->sin_addr = (cmd == SIOCGIFPSRCADDR) ?
211b941bc1dSAndrey V. Elsukov 		    sc->gif_iphdr->ip_src: sc->gif_iphdr->ip_dst;
212b941bc1dSAndrey V. Elsukov 		error = prison_if(curthread->td_ucred, (struct sockaddr *)src);
213b941bc1dSAndrey V. Elsukov 		if (error != 0)
214b941bc1dSAndrey V. Elsukov 			memset(src, 0, sizeof(*src));
215b941bc1dSAndrey V. Elsukov 		break;
216b941bc1dSAndrey V. Elsukov 	}
217b941bc1dSAndrey V. Elsukov 	return (error);
218b941bc1dSAndrey V. Elsukov }
219b941bc1dSAndrey V. Elsukov 
220cfa1ca9dSYoshinobu Inoue int
2210b9f5f8aSAndrey V. Elsukov in_gif_output(struct ifnet *ifp, struct mbuf *m, int proto, uint8_t ecn)
222cfa1ca9dSYoshinobu Inoue {
223fc74a9f9SBrooks Davis 	struct gif_softc *sc = ifp->if_softc;
224cfa1ca9dSYoshinobu Inoue 	struct ip *ip;
2250b9f5f8aSAndrey V. Elsukov 	int len;
226cfa1ca9dSYoshinobu Inoue 
227cfa1ca9dSYoshinobu Inoue 	/* prepend new IP header */
228*6573d758SMatt Macy 	MPASS(in_epoch(net_epoch_preempt));
229c89c8a10SMarius Strobl 	len = sizeof(struct ip);
230c89c8a10SMarius Strobl #ifndef __NO_STRICT_ALIGNMENT
2310b9f5f8aSAndrey V. Elsukov 	if (proto == IPPROTO_ETHERIP)
232c89c8a10SMarius Strobl 		len += ETHERIP_ALIGN;
233c89c8a10SMarius Strobl #endif
234eb1b1807SGleb Smirnoff 	M_PREPEND(m, len, M_NOWAIT);
2350b9f5f8aSAndrey V. Elsukov 	if (m == NULL)
2360b9f5f8aSAndrey V. Elsukov 		return (ENOBUFS);
237c89c8a10SMarius Strobl #ifndef __NO_STRICT_ALIGNMENT
2380b9f5f8aSAndrey V. Elsukov 	if (proto == IPPROTO_ETHERIP) {
239c89c8a10SMarius Strobl 		len = mtod(m, vm_offset_t) & 3;
240c89c8a10SMarius Strobl 		KASSERT(len == 0 || len == ETHERIP_ALIGN,
241c89c8a10SMarius Strobl 		    ("in_gif_output: unexpected misalignment"));
242c89c8a10SMarius Strobl 		m->m_data += len;
243c89c8a10SMarius Strobl 		m->m_len -= ETHERIP_ALIGN;
244c89c8a10SMarius Strobl 	}
245c89c8a10SMarius Strobl #endif
2460b9f5f8aSAndrey V. Elsukov 	ip = mtod(m, struct ip *);
247686cdd19SJun-ichiro itojun Hagino 
248b941bc1dSAndrey V. Elsukov 	MPASS(sc->gif_family == AF_INET);
249b941bc1dSAndrey V. Elsukov 	bcopy(sc->gif_iphdr, ip, sizeof(struct ip));
2500b9f5f8aSAndrey V. Elsukov 	ip->ip_p = proto;
2510b9f5f8aSAndrey V. Elsukov 	/* version will be set in ip_output() */
2520b9f5f8aSAndrey V. Elsukov 	ip->ip_ttl = V_ip_gif_ttl;
2530b9f5f8aSAndrey V. Elsukov 	ip->ip_len = htons(m->m_pkthdr.len);
2540b9f5f8aSAndrey V. Elsukov 	ip->ip_tos = ecn;
255cfa1ca9dSYoshinobu Inoue 
2560b9f5f8aSAndrey V. Elsukov 	return (ip_output(m, NULL, NULL, 0, NULL, NULL));
257cfa1ca9dSYoshinobu Inoue }
258cfa1ca9dSYoshinobu Inoue 
259132c4490SAndrey V. Elsukov static int
2606d8fdfa9SAndrey V. Elsukov in_gif_input(struct mbuf *m, int off, int proto, void *arg)
261cfa1ca9dSYoshinobu Inoue {
2626d8fdfa9SAndrey V. Elsukov 	struct gif_softc *sc = arg;
2630b9f5f8aSAndrey V. Elsukov 	struct ifnet *gifp;
264cfa1ca9dSYoshinobu Inoue 	struct ip *ip;
2650b9f5f8aSAndrey V. Elsukov 	uint8_t ecn;
266cfa1ca9dSYoshinobu Inoue 
267*6573d758SMatt Macy 	MPASS(in_epoch(net_epoch_preempt));
26867df9f38SBjoern A. Zeeb 	if (sc == NULL) {
26967df9f38SBjoern A. Zeeb 		m_freem(m);
270315e3e38SRobert Watson 		KMOD_IPSTAT_INC(ips_nogif);
2718f5a8818SKevin Lo 		return (IPPROTO_DONE);
27267df9f38SBjoern A. Zeeb 	}
27367df9f38SBjoern A. Zeeb 	gifp = GIF2IFP(sc);
2740b9f5f8aSAndrey V. Elsukov 	if ((gifp->if_flags & IFF_UP) != 0) {
275cfa1ca9dSYoshinobu Inoue 		ip = mtod(m, struct ip *);
2760b9f5f8aSAndrey V. Elsukov 		ecn = ip->ip_tos;
2776d8fdfa9SAndrey V. Elsukov 		m_adj(m, off);
2780b9f5f8aSAndrey V. Elsukov 		gif_input(m, gifp, proto, ecn);
2790b9f5f8aSAndrey V. Elsukov 	} else {
28059dfcba4SHajimu UMEMOTO 		m_freem(m);
281315e3e38SRobert Watson 		KMOD_IPSTAT_INC(ips_nogif);
282cfa1ca9dSYoshinobu Inoue 	}
2838f5a8818SKevin Lo 	return (IPPROTO_DONE);
284cfa1ca9dSYoshinobu Inoue }
285686cdd19SJun-ichiro itojun Hagino 
286b941bc1dSAndrey V. Elsukov static int
287b941bc1dSAndrey V. Elsukov in_gif_lookup(const struct mbuf *m, int off, int proto, void **arg)
2889426aedfSHajimu UMEMOTO {
28910a0e0bfSAndrey V. Elsukov 	const struct ip *ip;
29010a0e0bfSAndrey V. Elsukov 	struct gif_softc *sc;
291c1b4f79dSAndrey V. Elsukov 	int ret;
292686cdd19SJun-ichiro itojun Hagino 
2936e081509SAndrey V. Elsukov 	if (V_ipv4_hashtbl == NULL)
2946e081509SAndrey V. Elsukov 		return (0);
2956e081509SAndrey V. Elsukov 
296*6573d758SMatt Macy 	MPASS(in_epoch(net_epoch_preempt));
29710a0e0bfSAndrey V. Elsukov 	ip = mtod(m, const struct ip *);
298b941bc1dSAndrey V. Elsukov 	/*
299b941bc1dSAndrey V. Elsukov 	 * NOTE: it is safe to iterate without any locking here, because softc
300b941bc1dSAndrey V. Elsukov 	 * can be reclaimed only when we are not within net_epoch_preempt
301b941bc1dSAndrey V. Elsukov 	 * section, but ip_encap lookup+input are executed in epoch section.
302b941bc1dSAndrey V. Elsukov 	 */
303b941bc1dSAndrey V. Elsukov 	ret = 0;
304b941bc1dSAndrey V. Elsukov 	CK_LIST_FOREACH(sc, &GIF_HASH(ip->ip_dst.s_addr,
305b941bc1dSAndrey V. Elsukov 	    ip->ip_src.s_addr), chain) {
306b941bc1dSAndrey V. Elsukov 		/*
307b941bc1dSAndrey V. Elsukov 		 * This is an inbound packet, its ip_dst is source address
308b941bc1dSAndrey V. Elsukov 		 * in softc.
309b941bc1dSAndrey V. Elsukov 		 */
310b941bc1dSAndrey V. Elsukov 		if (sc->gif_iphdr->ip_src.s_addr == ip->ip_dst.s_addr &&
311b941bc1dSAndrey V. Elsukov 		    sc->gif_iphdr->ip_dst.s_addr == ip->ip_src.s_addr) {
312b941bc1dSAndrey V. Elsukov 			ret = ENCAP_DRV_LOOKUP;
313b941bc1dSAndrey V. Elsukov 			goto done;
314b941bc1dSAndrey V. Elsukov 		}
315b941bc1dSAndrey V. Elsukov 	}
316b941bc1dSAndrey V. Elsukov 	/*
317b941bc1dSAndrey V. Elsukov 	 * No exact match.
318b941bc1dSAndrey V. Elsukov 	 * Check the list of interfaces with GIF_IGNORE_SOURCE flag.
319b941bc1dSAndrey V. Elsukov 	 */
320b941bc1dSAndrey V. Elsukov 	CK_LIST_FOREACH(sc, &V_ipv4_list, chain) {
321b941bc1dSAndrey V. Elsukov 		if (sc->gif_iphdr->ip_src.s_addr == ip->ip_dst.s_addr) {
3226d8fdfa9SAndrey V. Elsukov 			ret = 32 + 8; /* src + proto */
323b941bc1dSAndrey V. Elsukov 			goto done;
324b941bc1dSAndrey V. Elsukov 		}
325b941bc1dSAndrey V. Elsukov 	}
326c1b4f79dSAndrey V. Elsukov 	return (0);
327b941bc1dSAndrey V. Elsukov done:
328b941bc1dSAndrey V. Elsukov 	if ((GIF2IFP(sc)->if_flags & IFF_UP) == 0)
329b941bc1dSAndrey V. Elsukov 		return (0);
330686cdd19SJun-ichiro itojun Hagino 	/* ingress filters on outer source */
33110a0e0bfSAndrey V. Elsukov 	if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0) {
33265ff3638SAlexander V. Chernikov 		struct nhop4_basic nh4;
33365ff3638SAlexander V. Chernikov 		struct in_addr dst;
334686cdd19SJun-ichiro itojun Hagino 
33565ff3638SAlexander V. Chernikov 		dst = ip->ip_src;
33665ff3638SAlexander V. Chernikov 		if (fib4_lookup_nh_basic(sc->gif_fibnum, dst, 0, 0, &nh4) != 0)
3370b9f5f8aSAndrey V. Elsukov 			return (0);
33865ff3638SAlexander V. Chernikov 		if (nh4.nh_ifp != m->m_pkthdr.rcvif)
33965ff3638SAlexander V. Chernikov 			return (0);
340686cdd19SJun-ichiro itojun Hagino 	}
341b941bc1dSAndrey V. Elsukov 	*arg = sc;
342c1b4f79dSAndrey V. Elsukov 	return (ret);
343686cdd19SJun-ichiro itojun Hagino }
3449426aedfSHajimu UMEMOTO 
345b941bc1dSAndrey V. Elsukov static struct {
346b941bc1dSAndrey V. Elsukov 	const struct encap_config encap;
347b941bc1dSAndrey V. Elsukov 	const struct encaptab *cookie;
348b941bc1dSAndrey V. Elsukov } ipv4_encap_cfg[] = {
349b941bc1dSAndrey V. Elsukov 	{
350b941bc1dSAndrey V. Elsukov 		.encap = {
351b941bc1dSAndrey V. Elsukov 			.proto = IPPROTO_IPV4,
352b941bc1dSAndrey V. Elsukov 			.min_length = 2 * sizeof(struct ip),
353b941bc1dSAndrey V. Elsukov 			.exact_match = ENCAP_DRV_LOOKUP,
354b941bc1dSAndrey V. Elsukov 			.lookup = in_gif_lookup,
3556d8fdfa9SAndrey V. Elsukov 			.input = in_gif_input
356b941bc1dSAndrey V. Elsukov 		},
357b941bc1dSAndrey V. Elsukov 	},
358b941bc1dSAndrey V. Elsukov #ifdef INET6
359b941bc1dSAndrey V. Elsukov 	{
360b941bc1dSAndrey V. Elsukov 		.encap = {
361b941bc1dSAndrey V. Elsukov 			.proto = IPPROTO_IPV6,
362b941bc1dSAndrey V. Elsukov 			.min_length = sizeof(struct ip) +
363b941bc1dSAndrey V. Elsukov 			    sizeof(struct ip6_hdr),
364b941bc1dSAndrey V. Elsukov 			.exact_match = ENCAP_DRV_LOOKUP,
365b941bc1dSAndrey V. Elsukov 			.lookup = in_gif_lookup,
366b941bc1dSAndrey V. Elsukov 			.input = in_gif_input
367b941bc1dSAndrey V. Elsukov 		},
368b941bc1dSAndrey V. Elsukov 	},
369b941bc1dSAndrey V. Elsukov #endif
370b941bc1dSAndrey V. Elsukov 	{
371b941bc1dSAndrey V. Elsukov 		.encap = {
372b941bc1dSAndrey V. Elsukov 			.proto = IPPROTO_ETHERIP,
373b941bc1dSAndrey V. Elsukov 			.min_length = sizeof(struct ip) +
374b941bc1dSAndrey V. Elsukov 			    sizeof(struct etherip_header) +
375b941bc1dSAndrey V. Elsukov 			    sizeof(struct ether_header),
376b941bc1dSAndrey V. Elsukov 			.exact_match = ENCAP_DRV_LOOKUP,
377b941bc1dSAndrey V. Elsukov 			.lookup = in_gif_lookup,
378b941bc1dSAndrey V. Elsukov 			.input = in_gif_input
379b941bc1dSAndrey V. Elsukov 		},
380b941bc1dSAndrey V. Elsukov 	}
3816d8fdfa9SAndrey V. Elsukov };
3826d8fdfa9SAndrey V. Elsukov 
383b941bc1dSAndrey V. Elsukov void
384b941bc1dSAndrey V. Elsukov in_gif_init(void)
3859426aedfSHajimu UMEMOTO {
386b941bc1dSAndrey V. Elsukov 	int i;
3870b9f5f8aSAndrey V. Elsukov 
388b941bc1dSAndrey V. Elsukov 	if (!IS_DEFAULT_VNET(curvnet))
389b941bc1dSAndrey V. Elsukov 		return;
390b941bc1dSAndrey V. Elsukov 	for (i = 0; i < nitems(ipv4_encap_cfg); i++)
391b941bc1dSAndrey V. Elsukov 		ipv4_encap_cfg[i].cookie = ip_encap_attach(
392b941bc1dSAndrey V. Elsukov 		    &ipv4_encap_cfg[i].encap, NULL, M_WAITOK);
3939426aedfSHajimu UMEMOTO }
394b941bc1dSAndrey V. Elsukov 
395b941bc1dSAndrey V. Elsukov void
396b941bc1dSAndrey V. Elsukov in_gif_uninit(void)
397b941bc1dSAndrey V. Elsukov {
398b941bc1dSAndrey V. Elsukov 	int i;
399b941bc1dSAndrey V. Elsukov 
400b941bc1dSAndrey V. Elsukov 	if (IS_DEFAULT_VNET(curvnet)) {
401b941bc1dSAndrey V. Elsukov 		for (i = 0; i < nitems(ipv4_encap_cfg); i++)
402b941bc1dSAndrey V. Elsukov 			ip_encap_detach(ipv4_encap_cfg[i].cookie);
403b941bc1dSAndrey V. Elsukov 	}
404b941bc1dSAndrey V. Elsukov 	if (V_ipv4_hashtbl != NULL)
405b941bc1dSAndrey V. Elsukov 		gif_hashdestroy(V_ipv4_hashtbl);
406b941bc1dSAndrey V. Elsukov }
407b941bc1dSAndrey V. Elsukov 
408