xref: /freebsd/sys/netinet/in_gif.c (revision b941bc1d6eddacf06eb515faa5d894f2ee2a9011)
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.
5*b941bc1dSAndrey 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>
43*b941bc1dSAndrey 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>
51cfa1ca9dSYoshinobu Inoue 
52*b941bc1dSAndrey V. Elsukov #include <net/ethernet.h>
53cfa1ca9dSYoshinobu Inoue #include <net/if.h>
5476039bc8SGleb Smirnoff #include <net/if_var.h>
55cfa1ca9dSYoshinobu Inoue #include <net/route.h>
56530c0060SRobert Watson #include <net/vnet.h>
57cfa1ca9dSYoshinobu Inoue 
58cfa1ca9dSYoshinobu Inoue #include <netinet/in.h>
59cfa1ca9dSYoshinobu Inoue #include <netinet/in_systm.h>
60cfa1ca9dSYoshinobu Inoue #include <netinet/ip.h>
616a800098SYoshinobu Inoue #include <netinet/ip_var.h>
62686cdd19SJun-ichiro itojun Hagino #include <netinet/in_var.h>
63686cdd19SJun-ichiro itojun Hagino #include <netinet/ip_encap.h>
646a800098SYoshinobu Inoue #include <netinet/ip_ecn.h>
6565ff3638SAlexander V. Chernikov #include <netinet/in_fib.h>
66686cdd19SJun-ichiro itojun Hagino 
676a800098SYoshinobu Inoue #ifdef INET6
68686cdd19SJun-ichiro itojun Hagino #include <netinet/ip6.h>
696a800098SYoshinobu Inoue #endif
70cfa1ca9dSYoshinobu Inoue 
71cfa1ca9dSYoshinobu Inoue #include <net/if_gif.h>
72cfa1ca9dSYoshinobu Inoue 
73132c4490SAndrey V. Elsukov #define GIF_TTL		30
74132c4490SAndrey V. Elsukov static VNET_DEFINE(int, ip_gif_ttl) = GIF_TTL;
7582cea7e6SBjoern A. Zeeb #define	V_ip_gif_ttl		VNET(ip_gif_ttl)
766df8a710SGleb Smirnoff SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_VNET | CTLFLAG_RW,
776d8fdfa9SAndrey V. Elsukov     &VNET_NAME(ip_gif_ttl), 0, "Default TTL value for encapsulated packets");
78cfa1ca9dSYoshinobu Inoue 
79*b941bc1dSAndrey V. Elsukov /*
80*b941bc1dSAndrey V. Elsukov  * We keep interfaces in a hash table using src+dst as key.
81*b941bc1dSAndrey V. Elsukov  * Interfaces with GIF_IGNORE_SOURCE flag are linked into plain list.
82*b941bc1dSAndrey V. Elsukov  */
83*b941bc1dSAndrey V. Elsukov static VNET_DEFINE(struct gif_list *, ipv4_hashtbl) = NULL;
84*b941bc1dSAndrey V. Elsukov static VNET_DEFINE(struct gif_list, ipv4_list) = CK_LIST_HEAD_INITIALIZER();
85*b941bc1dSAndrey V. Elsukov #define	V_ipv4_hashtbl		VNET(ipv4_hashtbl)
86*b941bc1dSAndrey V. Elsukov #define	V_ipv4_list		VNET(ipv4_list)
87*b941bc1dSAndrey V. Elsukov 
88*b941bc1dSAndrey V. Elsukov #define	GIF_HASH(src, dst)	(V_ipv4_hashtbl[\
89*b941bc1dSAndrey V. Elsukov     in_gif_hashval((src), (dst)) & (GIF_HASH_SIZE - 1)])
90*b941bc1dSAndrey V. Elsukov #define	GIF_HASH_SC(sc)		GIF_HASH((sc)->gif_iphdr->ip_src.s_addr,\
91*b941bc1dSAndrey V. Elsukov     (sc)->gif_iphdr->ip_dst.s_addr)
92*b941bc1dSAndrey V. Elsukov static uint32_t
93*b941bc1dSAndrey V. Elsukov in_gif_hashval(in_addr_t src, in_addr_t dst)
94*b941bc1dSAndrey V. Elsukov {
95*b941bc1dSAndrey V. Elsukov 	uint32_t ret;
96*b941bc1dSAndrey V. Elsukov 
97*b941bc1dSAndrey V. Elsukov 	ret = fnv_32_buf(&src, sizeof(src), FNV1_32_INIT);
98*b941bc1dSAndrey V. Elsukov 	return (fnv_32_buf(&dst, sizeof(dst), ret));
99*b941bc1dSAndrey V. Elsukov }
100*b941bc1dSAndrey V. Elsukov 
101*b941bc1dSAndrey V. Elsukov static int
102*b941bc1dSAndrey V. Elsukov in_gif_checkdup(const struct gif_softc *sc, in_addr_t src, in_addr_t dst)
103*b941bc1dSAndrey V. Elsukov {
104*b941bc1dSAndrey V. Elsukov 	struct gif_softc *tmp;
105*b941bc1dSAndrey V. Elsukov 
106*b941bc1dSAndrey V. Elsukov 	if (sc->gif_family == AF_INET &&
107*b941bc1dSAndrey V. Elsukov 	    sc->gif_iphdr->ip_src.s_addr == src &&
108*b941bc1dSAndrey V. Elsukov 	    sc->gif_iphdr->ip_dst.s_addr == dst)
109*b941bc1dSAndrey V. Elsukov 		return (EEXIST);
110*b941bc1dSAndrey V. Elsukov 
111*b941bc1dSAndrey V. Elsukov 	CK_LIST_FOREACH(tmp, &GIF_HASH(src, dst), chain) {
112*b941bc1dSAndrey V. Elsukov 		if (tmp == sc)
113*b941bc1dSAndrey V. Elsukov 			continue;
114*b941bc1dSAndrey V. Elsukov 		if (tmp->gif_iphdr->ip_src.s_addr == src &&
115*b941bc1dSAndrey V. Elsukov 		    tmp->gif_iphdr->ip_dst.s_addr == dst)
116*b941bc1dSAndrey V. Elsukov 			return (EADDRNOTAVAIL);
117*b941bc1dSAndrey V. Elsukov 	}
118*b941bc1dSAndrey V. Elsukov 	return (0);
119*b941bc1dSAndrey V. Elsukov }
120*b941bc1dSAndrey V. Elsukov 
121*b941bc1dSAndrey V. Elsukov static void
122*b941bc1dSAndrey V. Elsukov in_gif_attach(struct gif_softc *sc)
123*b941bc1dSAndrey V. Elsukov {
124*b941bc1dSAndrey V. Elsukov 
125*b941bc1dSAndrey V. Elsukov 	if (sc->gif_options & GIF_IGNORE_SOURCE)
126*b941bc1dSAndrey V. Elsukov 		CK_LIST_INSERT_HEAD(&V_ipv4_list, sc, chain);
127*b941bc1dSAndrey V. Elsukov 	else
128*b941bc1dSAndrey V. Elsukov 		CK_LIST_INSERT_HEAD(&GIF_HASH_SC(sc), sc, chain);
129*b941bc1dSAndrey V. Elsukov }
130*b941bc1dSAndrey V. Elsukov 
131*b941bc1dSAndrey V. Elsukov int
132*b941bc1dSAndrey V. Elsukov in_gif_setopts(struct gif_softc *sc, u_int options)
133*b941bc1dSAndrey V. Elsukov {
134*b941bc1dSAndrey V. Elsukov 
135*b941bc1dSAndrey V. Elsukov 	/* NOTE: we are protected with gif_ioctl_sx lock */
136*b941bc1dSAndrey V. Elsukov 	MPASS(sc->gif_family == AF_INET);
137*b941bc1dSAndrey V. Elsukov 	MPASS(sc->gif_options != options);
138*b941bc1dSAndrey V. Elsukov 
139*b941bc1dSAndrey V. Elsukov 	if ((options & GIF_IGNORE_SOURCE) !=
140*b941bc1dSAndrey V. Elsukov 	    (sc->gif_options & GIF_IGNORE_SOURCE)) {
141*b941bc1dSAndrey V. Elsukov 		CK_LIST_REMOVE(sc, chain);
142*b941bc1dSAndrey V. Elsukov 		sc->gif_options = options;
143*b941bc1dSAndrey V. Elsukov 		in_gif_attach(sc);
144*b941bc1dSAndrey V. Elsukov 	}
145*b941bc1dSAndrey V. Elsukov 	return (0);
146*b941bc1dSAndrey V. Elsukov }
147*b941bc1dSAndrey V. Elsukov 
148*b941bc1dSAndrey V. Elsukov int
149*b941bc1dSAndrey V. Elsukov in_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t data)
150*b941bc1dSAndrey V. Elsukov {
151*b941bc1dSAndrey V. Elsukov 	struct ifreq *ifr = (struct ifreq *)data;
152*b941bc1dSAndrey V. Elsukov 	struct sockaddr_in *dst, *src;
153*b941bc1dSAndrey V. Elsukov 	struct ip *ip;
154*b941bc1dSAndrey V. Elsukov 	int error;
155*b941bc1dSAndrey V. Elsukov 
156*b941bc1dSAndrey V. Elsukov 	/* NOTE: we are protected with gif_ioctl_sx lock */
157*b941bc1dSAndrey V. Elsukov 	error = EINVAL;
158*b941bc1dSAndrey V. Elsukov 	switch (cmd) {
159*b941bc1dSAndrey V. Elsukov 	case SIOCSIFPHYADDR:
160*b941bc1dSAndrey V. Elsukov 		src = &((struct in_aliasreq *)data)->ifra_addr;
161*b941bc1dSAndrey V. Elsukov 		dst = &((struct in_aliasreq *)data)->ifra_dstaddr;
162*b941bc1dSAndrey V. Elsukov 
163*b941bc1dSAndrey V. Elsukov 		/* sanity checks */
164*b941bc1dSAndrey V. Elsukov 		if (src->sin_family != dst->sin_family ||
165*b941bc1dSAndrey V. Elsukov 		    src->sin_family != AF_INET ||
166*b941bc1dSAndrey V. Elsukov 		    src->sin_len != dst->sin_len ||
167*b941bc1dSAndrey V. Elsukov 		    src->sin_len != sizeof(*src))
168*b941bc1dSAndrey V. Elsukov 			break;
169*b941bc1dSAndrey V. Elsukov 		if (src->sin_addr.s_addr == INADDR_ANY ||
170*b941bc1dSAndrey V. Elsukov 		    dst->sin_addr.s_addr == INADDR_ANY) {
171*b941bc1dSAndrey V. Elsukov 			error = EADDRNOTAVAIL;
172*b941bc1dSAndrey V. Elsukov 			break;
173*b941bc1dSAndrey V. Elsukov 		}
174*b941bc1dSAndrey V. Elsukov 		if (V_ipv4_hashtbl == NULL)
175*b941bc1dSAndrey V. Elsukov 			V_ipv4_hashtbl = gif_hashinit();
176*b941bc1dSAndrey V. Elsukov 		error = in_gif_checkdup(sc, src->sin_addr.s_addr,
177*b941bc1dSAndrey V. Elsukov 		    dst->sin_addr.s_addr);
178*b941bc1dSAndrey V. Elsukov 		if (error == EADDRNOTAVAIL)
179*b941bc1dSAndrey V. Elsukov 			break;
180*b941bc1dSAndrey V. Elsukov 		if (error == EEXIST) {
181*b941bc1dSAndrey V. Elsukov 			/* Addresses are the same. Just return. */
182*b941bc1dSAndrey V. Elsukov 			error = 0;
183*b941bc1dSAndrey V. Elsukov 			break;
184*b941bc1dSAndrey V. Elsukov 		}
185*b941bc1dSAndrey V. Elsukov 		ip = malloc(sizeof(*ip), M_GIF, M_WAITOK | M_ZERO);
186*b941bc1dSAndrey V. Elsukov 		ip->ip_src.s_addr = src->sin_addr.s_addr;
187*b941bc1dSAndrey V. Elsukov 		ip->ip_dst.s_addr = dst->sin_addr.s_addr;
188*b941bc1dSAndrey V. Elsukov 		if (sc->gif_family != 0) {
189*b941bc1dSAndrey V. Elsukov 			/* Detach existing tunnel first */
190*b941bc1dSAndrey V. Elsukov 			CK_LIST_REMOVE(sc, chain);
191*b941bc1dSAndrey V. Elsukov 			GIF_WAIT();
192*b941bc1dSAndrey V. Elsukov 			free(sc->gif_hdr, M_GIF);
193*b941bc1dSAndrey V. Elsukov 			/* XXX: should we notify about link state change? */
194*b941bc1dSAndrey V. Elsukov 		}
195*b941bc1dSAndrey V. Elsukov 		sc->gif_family = AF_INET;
196*b941bc1dSAndrey V. Elsukov 		sc->gif_iphdr = ip;
197*b941bc1dSAndrey V. Elsukov 		in_gif_attach(sc);
198*b941bc1dSAndrey V. Elsukov 		break;
199*b941bc1dSAndrey V. Elsukov 	case SIOCGIFPSRCADDR:
200*b941bc1dSAndrey V. Elsukov 	case SIOCGIFPDSTADDR:
201*b941bc1dSAndrey V. Elsukov 		if (sc->gif_family != AF_INET) {
202*b941bc1dSAndrey V. Elsukov 			error = EADDRNOTAVAIL;
203*b941bc1dSAndrey V. Elsukov 			break;
204*b941bc1dSAndrey V. Elsukov 		}
205*b941bc1dSAndrey V. Elsukov 		src = (struct sockaddr_in *)&ifr->ifr_addr;
206*b941bc1dSAndrey V. Elsukov 		memset(src, 0, sizeof(*src));
207*b941bc1dSAndrey V. Elsukov 		src->sin_family = AF_INET;
208*b941bc1dSAndrey V. Elsukov 		src->sin_len = sizeof(*src);
209*b941bc1dSAndrey V. Elsukov 		src->sin_addr = (cmd == SIOCGIFPSRCADDR) ?
210*b941bc1dSAndrey V. Elsukov 		    sc->gif_iphdr->ip_src: sc->gif_iphdr->ip_dst;
211*b941bc1dSAndrey V. Elsukov 		error = prison_if(curthread->td_ucred, (struct sockaddr *)src);
212*b941bc1dSAndrey V. Elsukov 		if (error != 0)
213*b941bc1dSAndrey V. Elsukov 			memset(src, 0, sizeof(*src));
214*b941bc1dSAndrey V. Elsukov 		break;
215*b941bc1dSAndrey V. Elsukov 	}
216*b941bc1dSAndrey V. Elsukov 	return (error);
217*b941bc1dSAndrey V. Elsukov }
218*b941bc1dSAndrey V. Elsukov 
219cfa1ca9dSYoshinobu Inoue int
2200b9f5f8aSAndrey V. Elsukov in_gif_output(struct ifnet *ifp, struct mbuf *m, int proto, uint8_t ecn)
221cfa1ca9dSYoshinobu Inoue {
222fc74a9f9SBrooks Davis 	struct gif_softc *sc = ifp->if_softc;
223cfa1ca9dSYoshinobu Inoue 	struct ip *ip;
2240b9f5f8aSAndrey V. Elsukov 	int len;
225cfa1ca9dSYoshinobu Inoue 
226cfa1ca9dSYoshinobu Inoue 	/* prepend new IP header */
227*b941bc1dSAndrey V. Elsukov 	MPASS(in_epoch());
228c89c8a10SMarius Strobl 	len = sizeof(struct ip);
229c89c8a10SMarius Strobl #ifndef __NO_STRICT_ALIGNMENT
2300b9f5f8aSAndrey V. Elsukov 	if (proto == IPPROTO_ETHERIP)
231c89c8a10SMarius Strobl 		len += ETHERIP_ALIGN;
232c89c8a10SMarius Strobl #endif
233eb1b1807SGleb Smirnoff 	M_PREPEND(m, len, M_NOWAIT);
2340b9f5f8aSAndrey V. Elsukov 	if (m == NULL)
2350b9f5f8aSAndrey V. Elsukov 		return (ENOBUFS);
236c89c8a10SMarius Strobl #ifndef __NO_STRICT_ALIGNMENT
2370b9f5f8aSAndrey V. Elsukov 	if (proto == IPPROTO_ETHERIP) {
238c89c8a10SMarius Strobl 		len = mtod(m, vm_offset_t) & 3;
239c89c8a10SMarius Strobl 		KASSERT(len == 0 || len == ETHERIP_ALIGN,
240c89c8a10SMarius Strobl 		    ("in_gif_output: unexpected misalignment"));
241c89c8a10SMarius Strobl 		m->m_data += len;
242c89c8a10SMarius Strobl 		m->m_len -= ETHERIP_ALIGN;
243c89c8a10SMarius Strobl 	}
244c89c8a10SMarius Strobl #endif
2450b9f5f8aSAndrey V. Elsukov 	ip = mtod(m, struct ip *);
246686cdd19SJun-ichiro itojun Hagino 
247*b941bc1dSAndrey V. Elsukov 	MPASS(sc->gif_family == AF_INET);
248*b941bc1dSAndrey V. Elsukov 	bcopy(sc->gif_iphdr, ip, sizeof(struct ip));
2490b9f5f8aSAndrey V. Elsukov 	ip->ip_p = proto;
2500b9f5f8aSAndrey V. Elsukov 	/* version will be set in ip_output() */
2510b9f5f8aSAndrey V. Elsukov 	ip->ip_ttl = V_ip_gif_ttl;
2520b9f5f8aSAndrey V. Elsukov 	ip->ip_len = htons(m->m_pkthdr.len);
2530b9f5f8aSAndrey V. Elsukov 	ip->ip_tos = ecn;
254cfa1ca9dSYoshinobu Inoue 
2550b9f5f8aSAndrey V. Elsukov 	return (ip_output(m, NULL, NULL, 0, NULL, NULL));
256cfa1ca9dSYoshinobu Inoue }
257cfa1ca9dSYoshinobu Inoue 
258132c4490SAndrey V. Elsukov static int
2596d8fdfa9SAndrey V. Elsukov in_gif_input(struct mbuf *m, int off, int proto, void *arg)
260cfa1ca9dSYoshinobu Inoue {
2616d8fdfa9SAndrey V. Elsukov 	struct gif_softc *sc = arg;
2620b9f5f8aSAndrey V. Elsukov 	struct ifnet *gifp;
263cfa1ca9dSYoshinobu Inoue 	struct ip *ip;
2640b9f5f8aSAndrey V. Elsukov 	uint8_t ecn;
265cfa1ca9dSYoshinobu Inoue 
266*b941bc1dSAndrey V. Elsukov 	MPASS(in_epoch());
26767df9f38SBjoern A. Zeeb 	if (sc == NULL) {
26867df9f38SBjoern A. Zeeb 		m_freem(m);
269315e3e38SRobert Watson 		KMOD_IPSTAT_INC(ips_nogif);
2708f5a8818SKevin Lo 		return (IPPROTO_DONE);
27167df9f38SBjoern A. Zeeb 	}
27267df9f38SBjoern A. Zeeb 	gifp = GIF2IFP(sc);
2730b9f5f8aSAndrey V. Elsukov 	if ((gifp->if_flags & IFF_UP) != 0) {
274cfa1ca9dSYoshinobu Inoue 		ip = mtod(m, struct ip *);
2750b9f5f8aSAndrey V. Elsukov 		ecn = ip->ip_tos;
2766d8fdfa9SAndrey V. Elsukov 		m_adj(m, off);
2770b9f5f8aSAndrey V. Elsukov 		gif_input(m, gifp, proto, ecn);
2780b9f5f8aSAndrey V. Elsukov 	} else {
27959dfcba4SHajimu UMEMOTO 		m_freem(m);
280315e3e38SRobert Watson 		KMOD_IPSTAT_INC(ips_nogif);
281cfa1ca9dSYoshinobu Inoue 	}
2828f5a8818SKevin Lo 	return (IPPROTO_DONE);
283cfa1ca9dSYoshinobu Inoue }
284686cdd19SJun-ichiro itojun Hagino 
285*b941bc1dSAndrey V. Elsukov static int
286*b941bc1dSAndrey V. Elsukov in_gif_lookup(const struct mbuf *m, int off, int proto, void **arg)
2879426aedfSHajimu UMEMOTO {
28810a0e0bfSAndrey V. Elsukov 	const struct ip *ip;
28910a0e0bfSAndrey V. Elsukov 	struct gif_softc *sc;
290c1b4f79dSAndrey V. Elsukov 	int ret;
291686cdd19SJun-ichiro itojun Hagino 
292*b941bc1dSAndrey V. Elsukov 	MPASS(in_epoch());
29310a0e0bfSAndrey V. Elsukov 	ip = mtod(m, const struct ip *);
294*b941bc1dSAndrey V. Elsukov 	/*
295*b941bc1dSAndrey V. Elsukov 	 * NOTE: it is safe to iterate without any locking here, because softc
296*b941bc1dSAndrey V. Elsukov 	 * can be reclaimed only when we are not within net_epoch_preempt
297*b941bc1dSAndrey V. Elsukov 	 * section, but ip_encap lookup+input are executed in epoch section.
298*b941bc1dSAndrey V. Elsukov 	 */
299*b941bc1dSAndrey V. Elsukov 	ret = 0;
300*b941bc1dSAndrey V. Elsukov 	CK_LIST_FOREACH(sc, &GIF_HASH(ip->ip_dst.s_addr,
301*b941bc1dSAndrey V. Elsukov 	    ip->ip_src.s_addr), chain) {
302*b941bc1dSAndrey V. Elsukov 		/*
303*b941bc1dSAndrey V. Elsukov 		 * This is an inbound packet, its ip_dst is source address
304*b941bc1dSAndrey V. Elsukov 		 * in softc.
305*b941bc1dSAndrey V. Elsukov 		 */
306*b941bc1dSAndrey V. Elsukov 		if (sc->gif_iphdr->ip_src.s_addr == ip->ip_dst.s_addr &&
307*b941bc1dSAndrey V. Elsukov 		    sc->gif_iphdr->ip_dst.s_addr == ip->ip_src.s_addr) {
308*b941bc1dSAndrey V. Elsukov 			ret = ENCAP_DRV_LOOKUP;
309*b941bc1dSAndrey V. Elsukov 			goto done;
310*b941bc1dSAndrey V. Elsukov 		}
311*b941bc1dSAndrey V. Elsukov 	}
312*b941bc1dSAndrey V. Elsukov 	/*
313*b941bc1dSAndrey V. Elsukov 	 * No exact match.
314*b941bc1dSAndrey V. Elsukov 	 * Check the list of interfaces with GIF_IGNORE_SOURCE flag.
315*b941bc1dSAndrey V. Elsukov 	 */
316*b941bc1dSAndrey V. Elsukov 	CK_LIST_FOREACH(sc, &V_ipv4_list, chain) {
317*b941bc1dSAndrey V. Elsukov 		if (sc->gif_iphdr->ip_src.s_addr == ip->ip_dst.s_addr) {
3186d8fdfa9SAndrey V. Elsukov 			ret = 32 + 8; /* src + proto */
319*b941bc1dSAndrey V. Elsukov 			goto done;
320*b941bc1dSAndrey V. Elsukov 		}
321*b941bc1dSAndrey V. Elsukov 	}
322c1b4f79dSAndrey V. Elsukov 	return (0);
323*b941bc1dSAndrey V. Elsukov done:
324*b941bc1dSAndrey V. Elsukov 	if ((GIF2IFP(sc)->if_flags & IFF_UP) == 0)
325*b941bc1dSAndrey V. Elsukov 		return (0);
326686cdd19SJun-ichiro itojun Hagino 	/* ingress filters on outer source */
32710a0e0bfSAndrey V. Elsukov 	if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0) {
32865ff3638SAlexander V. Chernikov 		struct nhop4_basic nh4;
32965ff3638SAlexander V. Chernikov 		struct in_addr dst;
330686cdd19SJun-ichiro itojun Hagino 
33165ff3638SAlexander V. Chernikov 		dst = ip->ip_src;
33265ff3638SAlexander V. Chernikov 		if (fib4_lookup_nh_basic(sc->gif_fibnum, dst, 0, 0, &nh4) != 0)
3330b9f5f8aSAndrey V. Elsukov 			return (0);
33465ff3638SAlexander V. Chernikov 		if (nh4.nh_ifp != m->m_pkthdr.rcvif)
33565ff3638SAlexander V. Chernikov 			return (0);
336686cdd19SJun-ichiro itojun Hagino 	}
337*b941bc1dSAndrey V. Elsukov 	*arg = sc;
338c1b4f79dSAndrey V. Elsukov 	return (ret);
339686cdd19SJun-ichiro itojun Hagino }
3409426aedfSHajimu UMEMOTO 
341*b941bc1dSAndrey V. Elsukov static struct {
342*b941bc1dSAndrey V. Elsukov 	const struct encap_config encap;
343*b941bc1dSAndrey V. Elsukov 	const struct encaptab *cookie;
344*b941bc1dSAndrey V. Elsukov } ipv4_encap_cfg[] = {
345*b941bc1dSAndrey V. Elsukov 	{
346*b941bc1dSAndrey V. Elsukov 		.encap = {
347*b941bc1dSAndrey V. Elsukov 			.proto = IPPROTO_IPV4,
348*b941bc1dSAndrey V. Elsukov 			.min_length = 2 * sizeof(struct ip),
349*b941bc1dSAndrey V. Elsukov 			.exact_match = ENCAP_DRV_LOOKUP,
350*b941bc1dSAndrey V. Elsukov 			.lookup = in_gif_lookup,
3516d8fdfa9SAndrey V. Elsukov 			.input = in_gif_input
352*b941bc1dSAndrey V. Elsukov 		},
353*b941bc1dSAndrey V. Elsukov 	},
354*b941bc1dSAndrey V. Elsukov #ifdef INET6
355*b941bc1dSAndrey V. Elsukov 	{
356*b941bc1dSAndrey V. Elsukov 		.encap = {
357*b941bc1dSAndrey V. Elsukov 			.proto = IPPROTO_IPV6,
358*b941bc1dSAndrey V. Elsukov 			.min_length = sizeof(struct ip) +
359*b941bc1dSAndrey V. Elsukov 			    sizeof(struct ip6_hdr),
360*b941bc1dSAndrey V. Elsukov 			.exact_match = ENCAP_DRV_LOOKUP,
361*b941bc1dSAndrey V. Elsukov 			.lookup = in_gif_lookup,
362*b941bc1dSAndrey V. Elsukov 			.input = in_gif_input
363*b941bc1dSAndrey V. Elsukov 		},
364*b941bc1dSAndrey V. Elsukov 	},
365*b941bc1dSAndrey V. Elsukov #endif
366*b941bc1dSAndrey V. Elsukov 	{
367*b941bc1dSAndrey V. Elsukov 		.encap = {
368*b941bc1dSAndrey V. Elsukov 			.proto = IPPROTO_ETHERIP,
369*b941bc1dSAndrey V. Elsukov 			.min_length = sizeof(struct ip) +
370*b941bc1dSAndrey V. Elsukov 			    sizeof(struct etherip_header) +
371*b941bc1dSAndrey V. Elsukov 			    sizeof(struct ether_header),
372*b941bc1dSAndrey V. Elsukov 			.exact_match = ENCAP_DRV_LOOKUP,
373*b941bc1dSAndrey V. Elsukov 			.lookup = in_gif_lookup,
374*b941bc1dSAndrey V. Elsukov 			.input = in_gif_input
375*b941bc1dSAndrey V. Elsukov 		},
376*b941bc1dSAndrey V. Elsukov 	}
3776d8fdfa9SAndrey V. Elsukov };
3786d8fdfa9SAndrey V. Elsukov 
379*b941bc1dSAndrey V. Elsukov void
380*b941bc1dSAndrey V. Elsukov in_gif_init(void)
3819426aedfSHajimu UMEMOTO {
382*b941bc1dSAndrey V. Elsukov 	int i;
3830b9f5f8aSAndrey V. Elsukov 
384*b941bc1dSAndrey V. Elsukov 	if (!IS_DEFAULT_VNET(curvnet))
385*b941bc1dSAndrey V. Elsukov 		return;
386*b941bc1dSAndrey V. Elsukov 	for (i = 0; i < nitems(ipv4_encap_cfg); i++)
387*b941bc1dSAndrey V. Elsukov 		ipv4_encap_cfg[i].cookie = ip_encap_attach(
388*b941bc1dSAndrey V. Elsukov 		    &ipv4_encap_cfg[i].encap, NULL, M_WAITOK);
3899426aedfSHajimu UMEMOTO }
390*b941bc1dSAndrey V. Elsukov 
391*b941bc1dSAndrey V. Elsukov void
392*b941bc1dSAndrey V. Elsukov in_gif_uninit(void)
393*b941bc1dSAndrey V. Elsukov {
394*b941bc1dSAndrey V. Elsukov 	int i;
395*b941bc1dSAndrey V. Elsukov 
396*b941bc1dSAndrey V. Elsukov 	if (IS_DEFAULT_VNET(curvnet)) {
397*b941bc1dSAndrey V. Elsukov 		for (i = 0; i < nitems(ipv4_encap_cfg); i++)
398*b941bc1dSAndrey V. Elsukov 			ip_encap_detach(ipv4_encap_cfg[i].cookie);
399*b941bc1dSAndrey V. Elsukov 	}
400*b941bc1dSAndrey V. Elsukov 	if (V_ipv4_hashtbl != NULL)
401*b941bc1dSAndrey V. Elsukov 		gif_hashdestroy(V_ipv4_hashtbl);
402*b941bc1dSAndrey V. Elsukov }
403*b941bc1dSAndrey V. Elsukov 
404