xref: /freebsd-src/sys/netinet/ip_encap.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
133841545SHajimu UMEMOTO /*	$KAME: ip_encap.c,v 1.41 2001/03/15 08:35:08 itojun Exp $	*/
2686cdd19SJun-ichiro itojun Hagino 
3c398230bSWarner Losh /*-
451369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
551369649SPedro F. Giffuni  *
6686cdd19SJun-ichiro itojun Hagino  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
76d8fdfa9SAndrey V. Elsukov  * Copyright (c) 2018 Andrey V. Elsukov <ae@FreeBSD.org>
8686cdd19SJun-ichiro itojun Hagino  * All rights reserved.
9686cdd19SJun-ichiro itojun Hagino  *
10686cdd19SJun-ichiro itojun Hagino  * Redistribution and use in source and binary forms, with or without
11686cdd19SJun-ichiro itojun Hagino  * modification, are permitted provided that the following conditions
12686cdd19SJun-ichiro itojun Hagino  * are met:
13686cdd19SJun-ichiro itojun Hagino  * 1. Redistributions of source code must retain the above copyright
14686cdd19SJun-ichiro itojun Hagino  *    notice, this list of conditions and the following disclaimer.
15686cdd19SJun-ichiro itojun Hagino  * 2. Redistributions in binary form must reproduce the above copyright
16686cdd19SJun-ichiro itojun Hagino  *    notice, this list of conditions and the following disclaimer in the
17686cdd19SJun-ichiro itojun Hagino  *    documentation and/or other materials provided with the distribution.
18686cdd19SJun-ichiro itojun Hagino  * 3. Neither the name of the project nor the names of its contributors
19686cdd19SJun-ichiro itojun Hagino  *    may be used to endorse or promote products derived from this software
20686cdd19SJun-ichiro itojun Hagino  *    without specific prior written permission.
21686cdd19SJun-ichiro itojun Hagino  *
22686cdd19SJun-ichiro itojun Hagino  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
23686cdd19SJun-ichiro itojun Hagino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24686cdd19SJun-ichiro itojun Hagino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25686cdd19SJun-ichiro itojun Hagino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
26686cdd19SJun-ichiro itojun Hagino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27686cdd19SJun-ichiro itojun Hagino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28686cdd19SJun-ichiro itojun Hagino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29686cdd19SJun-ichiro itojun Hagino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30686cdd19SJun-ichiro itojun Hagino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31686cdd19SJun-ichiro itojun Hagino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32686cdd19SJun-ichiro itojun Hagino  * SUCH DAMAGE.
33686cdd19SJun-ichiro itojun Hagino  */
34686cdd19SJun-ichiro itojun Hagino /*
35686cdd19SJun-ichiro itojun Hagino  * My grandfather said that there's a devil inside tunnelling technology...
36686cdd19SJun-ichiro itojun Hagino  *
37686cdd19SJun-ichiro itojun Hagino  * We have surprisingly many protocols that want packets with IP protocol
38686cdd19SJun-ichiro itojun Hagino  * #4 or #41.  Here's a list of protocols that want protocol #41:
39686cdd19SJun-ichiro itojun Hagino  *	RFC1933 configured tunnel
40686cdd19SJun-ichiro itojun Hagino  *	RFC1933 automatic tunnel
41686cdd19SJun-ichiro itojun Hagino  *	RFC2401 IPsec tunnel
42686cdd19SJun-ichiro itojun Hagino  *	RFC2473 IPv6 generic packet tunnelling
43686cdd19SJun-ichiro itojun Hagino  *	RFC2529 6over4 tunnel
44686cdd19SJun-ichiro itojun Hagino  *	mobile-ip6 (uses RFC2473)
4588ff5695SSUZUKI Shinsuke  *	RFC3056 6to4 tunnel
4688ff5695SSUZUKI Shinsuke  *	isatap tunnel
47686cdd19SJun-ichiro itojun Hagino  * Here's a list of protocol that want protocol #4:
48686cdd19SJun-ichiro itojun Hagino  *	RFC1853 IPv4-in-IPv4 tunnelling
49686cdd19SJun-ichiro itojun Hagino  *	RFC2003 IPv4 encapsulation within IPv4
50686cdd19SJun-ichiro itojun Hagino  *	RFC2344 reverse tunnelling for mobile-ip4
51686cdd19SJun-ichiro itojun Hagino  *	RFC2401 IPsec tunnel
52686cdd19SJun-ichiro itojun Hagino  * Well, what can I say.  They impose different en/decapsulation mechanism
53686cdd19SJun-ichiro itojun Hagino  * from each other, so they need separate protocol handler.  The only one
54686cdd19SJun-ichiro itojun Hagino  * we can easily determine by protocol # is IPsec, which always has
55686cdd19SJun-ichiro itojun Hagino  * AH/ESP/IPComp header right after outer IP header.
56686cdd19SJun-ichiro itojun Hagino  *
57686cdd19SJun-ichiro itojun Hagino  * So, clearly good old protosw does not work for protocol #4 and #41.
58686cdd19SJun-ichiro itojun Hagino  * The code will let you match protocol via src/dst address pair.
59686cdd19SJun-ichiro itojun Hagino  */
60686cdd19SJun-ichiro itojun Hagino 
614b421e2dSMike Silbersack #include <sys/cdefs.h>
62686cdd19SJun-ichiro itojun Hagino #include "opt_inet.h"
63686cdd19SJun-ichiro itojun Hagino #include "opt_inet6.h"
64686cdd19SJun-ichiro itojun Hagino 
65686cdd19SJun-ichiro itojun Hagino #include <sys/param.h>
66686cdd19SJun-ichiro itojun Hagino #include <sys/systm.h>
67f252e3f2SAndrey V. Elsukov #include <sys/eventhandler.h>
686d8fdfa9SAndrey V. Elsukov #include <sys/kernel.h>
69ea8d1492SAlexander V. Chernikov #include <sys/lock.h>
706d8fdfa9SAndrey V. Elsukov #include <sys/malloc.h>
71ea8d1492SAlexander V. Chernikov #include <sys/mutex.h>
72686cdd19SJun-ichiro itojun Hagino #include <sys/mbuf.h>
73686cdd19SJun-ichiro itojun Hagino #include <sys/errno.h>
746d8fdfa9SAndrey V. Elsukov #include <sys/socket.h>
75686cdd19SJun-ichiro itojun Hagino 
76686cdd19SJun-ichiro itojun Hagino #include <net/if.h>
776d8fdfa9SAndrey V. Elsukov #include <net/if_var.h>
78686cdd19SJun-ichiro itojun Hagino 
79686cdd19SJun-ichiro itojun Hagino #include <netinet/in.h>
80686cdd19SJun-ichiro itojun Hagino #include <netinet/ip_var.h>
81686cdd19SJun-ichiro itojun Hagino #include <netinet/ip_encap.h>
82686cdd19SJun-ichiro itojun Hagino 
83686cdd19SJun-ichiro itojun Hagino #ifdef INET6
84686cdd19SJun-ichiro itojun Hagino #include <netinet6/ip6_var.h>
85686cdd19SJun-ichiro itojun Hagino #endif
86686cdd19SJun-ichiro itojun Hagino 
876d8fdfa9SAndrey V. Elsukov static MALLOC_DEFINE(M_NETADDR, "encap_export_host",
886d8fdfa9SAndrey V. Elsukov     "Export host address structure");
89686cdd19SJun-ichiro itojun Hagino 
906d8fdfa9SAndrey V. Elsukov struct encaptab {
916d8fdfa9SAndrey V. Elsukov 	CK_LIST_ENTRY(encaptab) chain;
926d8fdfa9SAndrey V. Elsukov 	int		proto;
936d8fdfa9SAndrey V. Elsukov 	int		min_length;
946d8fdfa9SAndrey V. Elsukov 	int		exact_match;
956d8fdfa9SAndrey V. Elsukov 	void		*arg;
96686cdd19SJun-ichiro itojun Hagino 
976d8fdfa9SAndrey V. Elsukov 	encap_lookup_t	lookup;
986d8fdfa9SAndrey V. Elsukov 	encap_check_t	check;
996d8fdfa9SAndrey V. Elsukov 	encap_input_t	input;
1006d8fdfa9SAndrey V. Elsukov };
101686cdd19SJun-ichiro itojun Hagino 
1028251c68dSAndrey V. Elsukov struct srcaddrtab {
1038251c68dSAndrey V. Elsukov 	CK_LIST_ENTRY(srcaddrtab) chain;
1048251c68dSAndrey V. Elsukov 
1058251c68dSAndrey V. Elsukov 	encap_srcaddr_t	srcaddr;
1068251c68dSAndrey V. Elsukov 	void		*arg;
1078251c68dSAndrey V. Elsukov };
1088251c68dSAndrey V. Elsukov 
1096d8fdfa9SAndrey V. Elsukov CK_LIST_HEAD(encaptab_head, encaptab);
1108251c68dSAndrey V. Elsukov CK_LIST_HEAD(srcaddrtab_head, srcaddrtab);
1116d8fdfa9SAndrey V. Elsukov #ifdef INET
1126d8fdfa9SAndrey V. Elsukov static struct encaptab_head ipv4_encaptab = CK_LIST_HEAD_INITIALIZER();
1138251c68dSAndrey V. Elsukov static struct srcaddrtab_head ipv4_srcaddrtab = CK_LIST_HEAD_INITIALIZER();
1146d8fdfa9SAndrey V. Elsukov #endif
1156d8fdfa9SAndrey V. Elsukov #ifdef INET6
1166d8fdfa9SAndrey V. Elsukov static struct encaptab_head ipv6_encaptab = CK_LIST_HEAD_INITIALIZER();
1178251c68dSAndrey V. Elsukov static struct srcaddrtab_head ipv6_srcaddrtab = CK_LIST_HEAD_INITIALIZER();
1186d8fdfa9SAndrey V. Elsukov #endif
1196d8fdfa9SAndrey V. Elsukov 
1208251c68dSAndrey V. Elsukov static struct mtx encapmtx, srcaddrmtx;
121fe5a02c9SRobert Watson MTX_SYSINIT(encapmtx, &encapmtx, "encapmtx", MTX_DEF);
1228251c68dSAndrey V. Elsukov MTX_SYSINIT(srcaddrmtx, &srcaddrmtx, "srcaddrmtx", MTX_DEF);
1236d8fdfa9SAndrey V. Elsukov #define	ENCAP_WLOCK()		mtx_lock(&encapmtx)
1246d8fdfa9SAndrey V. Elsukov #define	ENCAP_WUNLOCK()		mtx_unlock(&encapmtx)
1258251c68dSAndrey V. Elsukov #define	ENCAP_RLOCK_TRACKER	struct epoch_tracker encap_et
126*b8a6e03fSGleb Smirnoff #define	ENCAP_RLOCK()		NET_EPOCH_ENTER(encap_et)
127*b8a6e03fSGleb Smirnoff #define	ENCAP_RUNLOCK()		NET_EPOCH_EXIT(encap_et)
128*b8a6e03fSGleb Smirnoff #define	ENCAP_WAIT()		NET_EPOCH_WAIT()
129686cdd19SJun-ichiro itojun Hagino 
1308251c68dSAndrey V. Elsukov #define	SRCADDR_WLOCK()		mtx_lock(&srcaddrmtx)
1318251c68dSAndrey V. Elsukov #define	SRCADDR_WUNLOCK()	mtx_unlock(&srcaddrmtx)
1328251c68dSAndrey V. Elsukov #define	SRCADDR_RLOCK_TRACKER	struct epoch_tracker srcaddr_et
1338251c68dSAndrey V. Elsukov #define	SRCADDR_RLOCK()		\
1348251c68dSAndrey V. Elsukov     epoch_enter_preempt(net_epoch_preempt, &srcaddr_et)
1358251c68dSAndrey V. Elsukov #define	SRCADDR_RUNLOCK()	\
1368251c68dSAndrey V. Elsukov     epoch_exit_preempt(net_epoch_preempt, &srcaddr_et)
1378251c68dSAndrey V. Elsukov #define	SRCADDR_WAIT()		epoch_wait_preempt(net_epoch_preempt)
1388251c68dSAndrey V. Elsukov 
1398251c68dSAndrey V. Elsukov /*
1408251c68dSAndrey V. Elsukov  * ifaddr_event_ext handler.
1418251c68dSAndrey V. Elsukov  *
1428251c68dSAndrey V. Elsukov  * Tunnelling interfaces may request the kernel to notify when
1438251c68dSAndrey V. Elsukov  * some interface addresses appears or disappears. Usually tunnelling
1448251c68dSAndrey V. Elsukov  * interface must use an address configured on the local machine as
1458251c68dSAndrey V. Elsukov  * ingress address to be able receive datagramms and do not send
1468251c68dSAndrey V. Elsukov  * spoofed packets.
1478251c68dSAndrey V. Elsukov  */
1488251c68dSAndrey V. Elsukov static void
srcaddr_change_event(void * arg __unused,struct ifnet * ifp,struct ifaddr * ifa,int event)1498251c68dSAndrey V. Elsukov srcaddr_change_event(void *arg __unused, struct ifnet *ifp,
1508251c68dSAndrey V. Elsukov     struct ifaddr *ifa, int event)
1518251c68dSAndrey V. Elsukov {
1528251c68dSAndrey V. Elsukov 	SRCADDR_RLOCK_TRACKER;
1538251c68dSAndrey V. Elsukov 	struct srcaddrtab_head *head;
1548251c68dSAndrey V. Elsukov 	struct srcaddrtab *p;
1558251c68dSAndrey V. Elsukov 
1568251c68dSAndrey V. Elsukov 	/* Support for old ifaddr_event. */
1578251c68dSAndrey V. Elsukov 	EVENTHANDLER_INVOKE(ifaddr_event, ifp);
1588251c68dSAndrey V. Elsukov 
1598251c68dSAndrey V. Elsukov 	switch (ifa->ifa_addr->sa_family) {
1608251c68dSAndrey V. Elsukov #ifdef INET
1618251c68dSAndrey V. Elsukov 	case AF_INET:
1628251c68dSAndrey V. Elsukov 		head = &ipv4_srcaddrtab;
1638251c68dSAndrey V. Elsukov 		break;
1648251c68dSAndrey V. Elsukov #endif
1658251c68dSAndrey V. Elsukov #ifdef INET6
1668251c68dSAndrey V. Elsukov 	case AF_INET6:
1678251c68dSAndrey V. Elsukov 		head = &ipv6_srcaddrtab;
1688251c68dSAndrey V. Elsukov 		break;
1698251c68dSAndrey V. Elsukov #endif
1708251c68dSAndrey V. Elsukov 	default:
1718251c68dSAndrey V. Elsukov 		/* ignore event */
1728251c68dSAndrey V. Elsukov 		return;
1738251c68dSAndrey V. Elsukov 	}
1748251c68dSAndrey V. Elsukov 
1758251c68dSAndrey V. Elsukov 	SRCADDR_RLOCK();
1768251c68dSAndrey V. Elsukov 	CK_LIST_FOREACH(p, head, chain) {
1778251c68dSAndrey V. Elsukov 		(*p->srcaddr)(p->arg, ifa->ifa_addr, event);
1788251c68dSAndrey V. Elsukov 	}
1798251c68dSAndrey V. Elsukov 	SRCADDR_RUNLOCK();
1808251c68dSAndrey V. Elsukov }
1818251c68dSAndrey V. Elsukov EVENTHANDLER_DEFINE(ifaddr_event_ext, srcaddr_change_event, NULL, 0);
1828251c68dSAndrey V. Elsukov 
1838251c68dSAndrey V. Elsukov static struct srcaddrtab *
encap_register_srcaddr(struct srcaddrtab_head * head,encap_srcaddr_t func,void * arg,int mflags)1848251c68dSAndrey V. Elsukov encap_register_srcaddr(struct srcaddrtab_head *head, encap_srcaddr_t func,
1858251c68dSAndrey V. Elsukov     void *arg, int mflags)
1868251c68dSAndrey V. Elsukov {
1878251c68dSAndrey V. Elsukov 	struct srcaddrtab *p, *tmp;
1888251c68dSAndrey V. Elsukov 
1898251c68dSAndrey V. Elsukov 	if (func == NULL)
1908251c68dSAndrey V. Elsukov 		return (NULL);
1918251c68dSAndrey V. Elsukov 	p = malloc(sizeof(*p), M_NETADDR, mflags);
1928251c68dSAndrey V. Elsukov 	if (p == NULL)
1938251c68dSAndrey V. Elsukov 		return (NULL);
1948251c68dSAndrey V. Elsukov 	p->srcaddr = func;
1958251c68dSAndrey V. Elsukov 	p->arg = arg;
1968251c68dSAndrey V. Elsukov 
1978251c68dSAndrey V. Elsukov 	SRCADDR_WLOCK();
1988251c68dSAndrey V. Elsukov 	CK_LIST_FOREACH(tmp, head, chain) {
1998251c68dSAndrey V. Elsukov 		if (func == tmp->srcaddr && arg == tmp->arg)
2008251c68dSAndrey V. Elsukov 			break;
2018251c68dSAndrey V. Elsukov 	}
2028251c68dSAndrey V. Elsukov 	if (tmp == NULL)
2038251c68dSAndrey V. Elsukov 		CK_LIST_INSERT_HEAD(head, p, chain);
2048251c68dSAndrey V. Elsukov 	SRCADDR_WUNLOCK();
2058251c68dSAndrey V. Elsukov 
2068251c68dSAndrey V. Elsukov 	if (tmp != NULL) {
2078251c68dSAndrey V. Elsukov 		free(p, M_NETADDR);
2088251c68dSAndrey V. Elsukov 		p = tmp;
2098251c68dSAndrey V. Elsukov 	}
2108251c68dSAndrey V. Elsukov 	return (p);
2118251c68dSAndrey V. Elsukov }
2128251c68dSAndrey V. Elsukov 
2138251c68dSAndrey V. Elsukov static int
encap_unregister_srcaddr(struct srcaddrtab_head * head,const struct srcaddrtab * cookie)2148251c68dSAndrey V. Elsukov encap_unregister_srcaddr(struct srcaddrtab_head *head,
2158251c68dSAndrey V. Elsukov     const struct srcaddrtab *cookie)
2168251c68dSAndrey V. Elsukov {
2178251c68dSAndrey V. Elsukov 	struct srcaddrtab *p;
2188251c68dSAndrey V. Elsukov 
2198251c68dSAndrey V. Elsukov 	SRCADDR_WLOCK();
2208251c68dSAndrey V. Elsukov 	CK_LIST_FOREACH(p, head, chain) {
2218251c68dSAndrey V. Elsukov 		if (p == cookie) {
2228251c68dSAndrey V. Elsukov 			CK_LIST_REMOVE(p, chain);
2238251c68dSAndrey V. Elsukov 			SRCADDR_WUNLOCK();
2248251c68dSAndrey V. Elsukov 			SRCADDR_WAIT();
2258251c68dSAndrey V. Elsukov 			free(p, M_NETADDR);
2268251c68dSAndrey V. Elsukov 			return (0);
2278251c68dSAndrey V. Elsukov 		}
2288251c68dSAndrey V. Elsukov 	}
2298251c68dSAndrey V. Elsukov 	SRCADDR_WUNLOCK();
2308251c68dSAndrey V. Elsukov 	return (EINVAL);
2318251c68dSAndrey V. Elsukov }
2328251c68dSAndrey V. Elsukov 
2336d8fdfa9SAndrey V. Elsukov static struct encaptab *
encap_attach(struct encaptab_head * head,const struct encap_config * cfg,void * arg,int mflags)2346d8fdfa9SAndrey V. Elsukov encap_attach(struct encaptab_head *head, const struct encap_config *cfg,
2356d8fdfa9SAndrey V. Elsukov     void *arg, int mflags)
236686cdd19SJun-ichiro itojun Hagino {
2376d8fdfa9SAndrey V. Elsukov 	struct encaptab *ep, *tmp;
238686cdd19SJun-ichiro itojun Hagino 
2396d8fdfa9SAndrey V. Elsukov 	if (cfg == NULL || cfg->input == NULL ||
2406d8fdfa9SAndrey V. Elsukov 	    (cfg->check == NULL && cfg->lookup == NULL) ||
2416d8fdfa9SAndrey V. Elsukov 	    (cfg->lookup != NULL && cfg->exact_match != ENCAP_DRV_LOOKUP) ||
2426d8fdfa9SAndrey V. Elsukov 	    (cfg->exact_match == ENCAP_DRV_LOOKUP && cfg->lookup == NULL))
243fe5a02c9SRobert Watson 		return (NULL);
244686cdd19SJun-ichiro itojun Hagino 
2456d8fdfa9SAndrey V. Elsukov 	ep = malloc(sizeof(*ep), M_NETADDR, mflags);
2464df05d61SPoul-Henning Kamp 	if (ep == NULL)
247fe5a02c9SRobert Watson 		return (NULL);
248686cdd19SJun-ichiro itojun Hagino 
2496d8fdfa9SAndrey V. Elsukov 	ep->proto = cfg->proto;
2506d8fdfa9SAndrey V. Elsukov 	ep->min_length = cfg->min_length;
2516d8fdfa9SAndrey V. Elsukov 	ep->exact_match = cfg->exact_match;
252686cdd19SJun-ichiro itojun Hagino 	ep->arg = arg;
2536d8fdfa9SAndrey V. Elsukov 	ep->lookup = cfg->exact_match == ENCAP_DRV_LOOKUP ? cfg->lookup: NULL;
2546d8fdfa9SAndrey V. Elsukov 	ep->check = cfg->exact_match != ENCAP_DRV_LOOKUP ? cfg->check: NULL;
2556d8fdfa9SAndrey V. Elsukov 	ep->input = cfg->input;
256686cdd19SJun-ichiro itojun Hagino 
2576d8fdfa9SAndrey V. Elsukov 	ENCAP_WLOCK();
2586d8fdfa9SAndrey V. Elsukov 	CK_LIST_FOREACH(tmp, head, chain) {
2596d8fdfa9SAndrey V. Elsukov 		if (tmp->exact_match <= ep->exact_match)
2606d8fdfa9SAndrey V. Elsukov 			break;
2616d8fdfa9SAndrey V. Elsukov 	}
2626d8fdfa9SAndrey V. Elsukov 	if (tmp == NULL)
2636d8fdfa9SAndrey V. Elsukov 		CK_LIST_INSERT_HEAD(head, ep, chain);
2646d8fdfa9SAndrey V. Elsukov 	else
2656d8fdfa9SAndrey V. Elsukov 		CK_LIST_INSERT_BEFORE(tmp, ep, chain);
2666d8fdfa9SAndrey V. Elsukov 	ENCAP_WUNLOCK();
267fe5a02c9SRobert Watson 	return (ep);
268686cdd19SJun-ichiro itojun Hagino }
269686cdd19SJun-ichiro itojun Hagino 
270686cdd19SJun-ichiro itojun Hagino static int
encap_detach(struct encaptab_head * head,const struct encaptab * cookie)2716d8fdfa9SAndrey V. Elsukov encap_detach(struct encaptab_head *head, const struct encaptab *cookie)
272686cdd19SJun-ichiro itojun Hagino {
2736d8fdfa9SAndrey V. Elsukov 	struct encaptab *ep;
274686cdd19SJun-ichiro itojun Hagino 
2756d8fdfa9SAndrey V. Elsukov 	ENCAP_WLOCK();
2766d8fdfa9SAndrey V. Elsukov 	CK_LIST_FOREACH(ep, head, chain) {
2776d8fdfa9SAndrey V. Elsukov 		if (ep == cookie) {
2786d8fdfa9SAndrey V. Elsukov 			CK_LIST_REMOVE(ep, chain);
2796d8fdfa9SAndrey V. Elsukov 			ENCAP_WUNLOCK();
2806d8fdfa9SAndrey V. Elsukov 			ENCAP_WAIT();
2816d8fdfa9SAndrey V. Elsukov 			free(ep, M_NETADDR);
2826d8fdfa9SAndrey V. Elsukov 			return (0);
2836d8fdfa9SAndrey V. Elsukov 		}
2846d8fdfa9SAndrey V. Elsukov 	}
2856d8fdfa9SAndrey V. Elsukov 	ENCAP_WUNLOCK();
2866d8fdfa9SAndrey V. Elsukov 	return (EINVAL);
287686cdd19SJun-ichiro itojun Hagino }
288686cdd19SJun-ichiro itojun Hagino 
2896d8fdfa9SAndrey V. Elsukov static int
encap_input(struct encaptab_head * head,struct mbuf * m,int off,int proto)2906d8fdfa9SAndrey V. Elsukov encap_input(struct encaptab_head *head, struct mbuf *m, int off, int proto)
291686cdd19SJun-ichiro itojun Hagino {
2928251c68dSAndrey V. Elsukov 	ENCAP_RLOCK_TRACKER;
2936d8fdfa9SAndrey V. Elsukov 	struct encaptab *ep, *match;
2946d8fdfa9SAndrey V. Elsukov 	void *arg;
2956d8fdfa9SAndrey V. Elsukov 	int matchprio, ret;
296686cdd19SJun-ichiro itojun Hagino 
2976d8fdfa9SAndrey V. Elsukov 	match = NULL;
2986d8fdfa9SAndrey V. Elsukov 	matchprio = 0;
2996d8fdfa9SAndrey V. Elsukov 
3006d8fdfa9SAndrey V. Elsukov 	ENCAP_RLOCK();
3016d8fdfa9SAndrey V. Elsukov 	CK_LIST_FOREACH(ep, head, chain) {
3026d8fdfa9SAndrey V. Elsukov 		if (ep->proto >= 0 && ep->proto != proto)
3036d8fdfa9SAndrey V. Elsukov 			continue;
3046d8fdfa9SAndrey V. Elsukov 		if (ep->min_length > m->m_pkthdr.len)
3056d8fdfa9SAndrey V. Elsukov 			continue;
3066d8fdfa9SAndrey V. Elsukov 		if (ep->exact_match == ENCAP_DRV_LOOKUP)
3076d8fdfa9SAndrey V. Elsukov 			ret = (*ep->lookup)(m, off, proto, &arg);
3086d8fdfa9SAndrey V. Elsukov 		else
3096d8fdfa9SAndrey V. Elsukov 			ret = (*ep->check)(m, off, proto, ep->arg);
3106d8fdfa9SAndrey V. Elsukov 		if (ret <= 0)
3116d8fdfa9SAndrey V. Elsukov 			continue;
3126d8fdfa9SAndrey V. Elsukov 		if (ret > matchprio) {
3136d8fdfa9SAndrey V. Elsukov 			match = ep;
3146d8fdfa9SAndrey V. Elsukov 			if (ep->exact_match != ENCAP_DRV_LOOKUP)
3156d8fdfa9SAndrey V. Elsukov 				arg = ep->arg;
3166d8fdfa9SAndrey V. Elsukov 			/*
3176d8fdfa9SAndrey V. Elsukov 			 * No need to continue the search, we got the
3186d8fdfa9SAndrey V. Elsukov 			 * exact match.
3196d8fdfa9SAndrey V. Elsukov 			 */
3206d8fdfa9SAndrey V. Elsukov 			if (ret >= ep->exact_match)
3216d8fdfa9SAndrey V. Elsukov 				break;
3226d8fdfa9SAndrey V. Elsukov 			matchprio = ret;
323686cdd19SJun-ichiro itojun Hagino 		}
324efb5228cSAndrey V. Elsukov 	}
325686cdd19SJun-ichiro itojun Hagino 
3266d8fdfa9SAndrey V. Elsukov 	if (match != NULL) {
3276d8fdfa9SAndrey V. Elsukov 		/* found a match, "match" has the best one */
3286d8fdfa9SAndrey V. Elsukov 		ret = (*match->input)(m, off, proto, arg);
3296d8fdfa9SAndrey V. Elsukov 		ENCAP_RUNLOCK();
3306d8fdfa9SAndrey V. Elsukov 		MPASS(ret == IPPROTO_DONE);
3316d8fdfa9SAndrey V. Elsukov 		return (IPPROTO_DONE);
3326d8fdfa9SAndrey V. Elsukov 	}
3336d8fdfa9SAndrey V. Elsukov 	ENCAP_RUNLOCK();
3346d8fdfa9SAndrey V. Elsukov 	return (0);
3356d8fdfa9SAndrey V. Elsukov }
3366d8fdfa9SAndrey V. Elsukov 
3376d8fdfa9SAndrey V. Elsukov #ifdef INET
3388251c68dSAndrey V. Elsukov const struct srcaddrtab *
ip_encap_register_srcaddr(encap_srcaddr_t func,void * arg,int mflags)3398251c68dSAndrey V. Elsukov ip_encap_register_srcaddr(encap_srcaddr_t func, void *arg, int mflags)
3408251c68dSAndrey V. Elsukov {
3418251c68dSAndrey V. Elsukov 
3428251c68dSAndrey V. Elsukov 	return (encap_register_srcaddr(&ipv4_srcaddrtab, func, arg, mflags));
3438251c68dSAndrey V. Elsukov }
3448251c68dSAndrey V. Elsukov 
3458251c68dSAndrey V. Elsukov int
ip_encap_unregister_srcaddr(const struct srcaddrtab * cookie)3468251c68dSAndrey V. Elsukov ip_encap_unregister_srcaddr(const struct srcaddrtab *cookie)
3478251c68dSAndrey V. Elsukov {
3488251c68dSAndrey V. Elsukov 
3498251c68dSAndrey V. Elsukov 	return (encap_unregister_srcaddr(&ipv4_srcaddrtab, cookie));
3508251c68dSAndrey V. Elsukov }
3518251c68dSAndrey V. Elsukov 
3526d8fdfa9SAndrey V. Elsukov const struct encaptab *
ip_encap_attach(const struct encap_config * cfg,void * arg,int mflags)3536d8fdfa9SAndrey V. Elsukov ip_encap_attach(const struct encap_config *cfg, void *arg, int mflags)
354686cdd19SJun-ichiro itojun Hagino {
355686cdd19SJun-ichiro itojun Hagino 
3566d8fdfa9SAndrey V. Elsukov 	return (encap_attach(&ipv4_encaptab, cfg, arg, mflags));
357686cdd19SJun-ichiro itojun Hagino }
3586d8fdfa9SAndrey V. Elsukov 
3596d8fdfa9SAndrey V. Elsukov int
ip_encap_detach(const struct encaptab * cookie)3606d8fdfa9SAndrey V. Elsukov ip_encap_detach(const struct encaptab *cookie)
3616d8fdfa9SAndrey V. Elsukov {
3626d8fdfa9SAndrey V. Elsukov 
3636d8fdfa9SAndrey V. Elsukov 	return (encap_detach(&ipv4_encaptab, cookie));
364686cdd19SJun-ichiro itojun Hagino }
3656d8fdfa9SAndrey V. Elsukov 
3666d8fdfa9SAndrey V. Elsukov int
encap4_input(struct mbuf ** mp,int * offp,int proto)3676d8fdfa9SAndrey V. Elsukov encap4_input(struct mbuf **mp, int *offp, int proto)
3686d8fdfa9SAndrey V. Elsukov {
3696d8fdfa9SAndrey V. Elsukov 
3706d8fdfa9SAndrey V. Elsukov 	if (encap_input(&ipv4_encaptab, *mp, *offp, proto) != IPPROTO_DONE)
3716d8fdfa9SAndrey V. Elsukov 		return (rip_input(mp, offp, proto));
3726d8fdfa9SAndrey V. Elsukov 	return (IPPROTO_DONE);
3736d8fdfa9SAndrey V. Elsukov }
3746d8fdfa9SAndrey V. Elsukov #endif /* INET */
3756d8fdfa9SAndrey V. Elsukov 
3766d8fdfa9SAndrey V. Elsukov #ifdef INET6
3778251c68dSAndrey V. Elsukov const struct srcaddrtab *
ip6_encap_register_srcaddr(encap_srcaddr_t func,void * arg,int mflags)3788251c68dSAndrey V. Elsukov ip6_encap_register_srcaddr(encap_srcaddr_t func, void *arg, int mflags)
3798251c68dSAndrey V. Elsukov {
3808251c68dSAndrey V. Elsukov 
3818251c68dSAndrey V. Elsukov 	return (encap_register_srcaddr(&ipv6_srcaddrtab, func, arg, mflags));
3828251c68dSAndrey V. Elsukov }
3838251c68dSAndrey V. Elsukov 
3848251c68dSAndrey V. Elsukov int
ip6_encap_unregister_srcaddr(const struct srcaddrtab * cookie)3858251c68dSAndrey V. Elsukov ip6_encap_unregister_srcaddr(const struct srcaddrtab *cookie)
3868251c68dSAndrey V. Elsukov {
3878251c68dSAndrey V. Elsukov 
3888251c68dSAndrey V. Elsukov 	return (encap_unregister_srcaddr(&ipv6_srcaddrtab, cookie));
3898251c68dSAndrey V. Elsukov }
3908251c68dSAndrey V. Elsukov 
3916d8fdfa9SAndrey V. Elsukov const struct encaptab *
ip6_encap_attach(const struct encap_config * cfg,void * arg,int mflags)3926d8fdfa9SAndrey V. Elsukov ip6_encap_attach(const struct encap_config *cfg, void *arg, int mflags)
3936d8fdfa9SAndrey V. Elsukov {
3946d8fdfa9SAndrey V. Elsukov 
3956d8fdfa9SAndrey V. Elsukov 	return (encap_attach(&ipv6_encaptab, cfg, arg, mflags));
3966d8fdfa9SAndrey V. Elsukov }
3976d8fdfa9SAndrey V. Elsukov 
3986d8fdfa9SAndrey V. Elsukov int
ip6_encap_detach(const struct encaptab * cookie)3996d8fdfa9SAndrey V. Elsukov ip6_encap_detach(const struct encaptab *cookie)
4006d8fdfa9SAndrey V. Elsukov {
4016d8fdfa9SAndrey V. Elsukov 
4026d8fdfa9SAndrey V. Elsukov 	return (encap_detach(&ipv6_encaptab, cookie));
4036d8fdfa9SAndrey V. Elsukov }
4046d8fdfa9SAndrey V. Elsukov 
4056d8fdfa9SAndrey V. Elsukov int
encap6_input(struct mbuf ** mp,int * offp,int proto)4066d8fdfa9SAndrey V. Elsukov encap6_input(struct mbuf **mp, int *offp, int proto)
4076d8fdfa9SAndrey V. Elsukov {
4086d8fdfa9SAndrey V. Elsukov 
4096d8fdfa9SAndrey V. Elsukov 	if (encap_input(&ipv6_encaptab, *mp, *offp, proto) != IPPROTO_DONE)
4106d8fdfa9SAndrey V. Elsukov 		return (rip6_input(mp, offp, proto));
4116d8fdfa9SAndrey V. Elsukov 	return (IPPROTO_DONE);
4126d8fdfa9SAndrey V. Elsukov }
4136d8fdfa9SAndrey V. Elsukov #endif /* INET6 */
414