xref: /netbsd-src/sys/net/if_gif.c (revision ae50415a0d4ae64507de19cdae67ca5e888136ff)
1*ae50415aSskrll /*	$NetBSD: if_gif.c,v 1.159 2024/09/15 09:46:45 skrll Exp $	*/
2e556ec90Sitojun /*	$KAME: if_gif.c,v 1.76 2001/08/20 02:01:02 kjc Exp $	*/
38789e600Sitojun 
474d3c214Sitojun /*
574d3c214Sitojun  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
674d3c214Sitojun  * All rights reserved.
774d3c214Sitojun  *
874d3c214Sitojun  * Redistribution and use in source and binary forms, with or without
974d3c214Sitojun  * modification, are permitted provided that the following conditions
1074d3c214Sitojun  * are met:
1174d3c214Sitojun  * 1. Redistributions of source code must retain the above copyright
1274d3c214Sitojun  *    notice, this list of conditions and the following disclaimer.
1374d3c214Sitojun  * 2. Redistributions in binary form must reproduce the above copyright
1474d3c214Sitojun  *    notice, this list of conditions and the following disclaimer in the
1574d3c214Sitojun  *    documentation and/or other materials provided with the distribution.
1674d3c214Sitojun  * 3. Neither the name of the project nor the names of its contributors
1774d3c214Sitojun  *    may be used to endorse or promote products derived from this software
1874d3c214Sitojun  *    without specific prior written permission.
1974d3c214Sitojun  *
2074d3c214Sitojun  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2174d3c214Sitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2274d3c214Sitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2374d3c214Sitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2474d3c214Sitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2574d3c214Sitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2674d3c214Sitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2774d3c214Sitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2874d3c214Sitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2974d3c214Sitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3074d3c214Sitojun  * SUCH DAMAGE.
3174d3c214Sitojun  */
3274d3c214Sitojun 
3334d65a34Slukem #include <sys/cdefs.h>
34*ae50415aSskrll __KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.159 2024/09/15 09:46:45 skrll Exp $");
3534d65a34Slukem 
361c4a50f1Spooka #ifdef _KERNEL_OPT
3774d3c214Sitojun #include "opt_inet.h"
386284b358Sknakahara #include "opt_net_mpsafe.h"
391c4a50f1Spooka #endif
4074d3c214Sitojun 
4174d3c214Sitojun #include <sys/param.h>
4274d3c214Sitojun #include <sys/systm.h>
43ffb7fabaSriastradh #include <sys/atomic.h>
4474d3c214Sitojun #include <sys/kernel.h>
4574d3c214Sitojun #include <sys/mbuf.h>
4674d3c214Sitojun #include <sys/socket.h>
4774d3c214Sitojun #include <sys/sockio.h>
4874d3c214Sitojun #include <sys/errno.h>
4974d3c214Sitojun #include <sys/ioctl.h>
5074d3c214Sitojun #include <sys/time.h>
51de7496deSmartin #include <sys/socketvar.h>
5274d3c214Sitojun #include <sys/syslog.h>
534a0283d9Smartin #include <sys/proc.h>
5446ed8f7dSad #include <sys/cpu.h>
5546ed8f7dSad #include <sys/intr.h>
56849e83faSknakahara #include <sys/kmem.h>
571c5d304eSknakahara #include <sys/sysctl.h>
58d81cd78eSknakahara #include <sys/xcall.h>
591d8e08d4Schristos #include <sys/device.h>
601d8e08d4Schristos #include <sys/module.h>
61493e35e3Sknakahara #include <sys/mutex.h>
62493e35e3Sknakahara #include <sys/pserialize.h>
63493e35e3Sknakahara #include <sys/psref.h>
6474d3c214Sitojun 
6574d3c214Sitojun #include <net/if.h>
6674d3c214Sitojun #include <net/if_types.h>
6774d3c214Sitojun #include <net/route.h>
6874d3c214Sitojun #include <net/bpf.h>
6974d3c214Sitojun 
7074d3c214Sitojun #include <netinet/in.h>
7174d3c214Sitojun #include <netinet/in_systm.h>
7274d3c214Sitojun #include <netinet/ip.h>
73dcfe05e7Sitojun #ifdef	INET
74dcfe05e7Sitojun #include <netinet/in_var.h>
7574d3c214Sitojun #endif	/* INET */
76fffd2322Schristos #include <netinet/in_gif.h>
7774d3c214Sitojun 
7874d3c214Sitojun #ifdef INET6
7974d3c214Sitojun #ifndef INET
8074d3c214Sitojun #include <netinet/in.h>
8174d3c214Sitojun #endif
8274d3c214Sitojun #include <netinet6/in6_var.h>
8374d3c214Sitojun #include <netinet/ip6.h>
8474d3c214Sitojun #include <netinet6/ip6_var.h>
8574d3c214Sitojun #include <netinet6/in6_gif.h>
8674d3c214Sitojun #endif /* INET6 */
8774d3c214Sitojun 
8839091335Sitojun #include <netinet/ip_encap.h>
8974d3c214Sitojun #include <net/if_gif.h>
9074d3c214Sitojun 
91e7ae23fdSchristos #include "ioconf.h"
92e7ae23fdSchristos 
936284b358Sknakahara #ifdef NET_MPSAFE
946284b358Sknakahara #define GIF_MPSAFE	1
956284b358Sknakahara #endif
966284b358Sknakahara 
9774d3c214Sitojun /*
9874d3c214Sitojun  * gif global variable definitions
9974d3c214Sitojun  */
1001cc266b8Sknakahara static struct {
101b801416bSmsaitoh 	LIST_HEAD(gif_sclist, gif_softc) list;
1021cc266b8Sknakahara 	kmutex_t lock;
1031cc266b8Sknakahara } gif_softcs __cacheline_aligned;
104b36ce94cSthorpej 
105493e35e3Sknakahara struct psref_class *gv_psref_class __read_mostly;
106493e35e3Sknakahara 
1077128a14dSknakahara static pktq_rps_hash_func_t gif_pktq_rps_hash_p;
1087128a14dSknakahara 
109916c83eeSmsaitoh static int	gifattach0(struct gif_softc *);
110d60d8acaSknakahara static int	gif_output(struct ifnet *, struct mbuf *,
111d60d8acaSknakahara 			   const struct sockaddr *, const struct rtentry *);
112ccd8e6e6Sknakahara static void	gif_start(struct ifnet *);
1137216411eSknakahara static int	gif_transmit(struct ifnet *, struct mbuf *);
114493e35e3Sknakahara static int	gif_transmit_direct(struct gif_variant *, struct mbuf *);
115d60d8acaSknakahara static int	gif_ioctl(struct ifnet *, u_long, void *);
116d60d8acaSknakahara static int	gif_set_tunnel(struct ifnet *, struct sockaddr *,
117d60d8acaSknakahara 			       struct sockaddr *);
118d60d8acaSknakahara static void	gif_delete_tunnel(struct ifnet *);
119d60d8acaSknakahara 
12063eac52bSthorpej static int	gif_clone_create(struct if_clone *, int);
12163eac52bSthorpej static int	gif_clone_destroy(struct ifnet *);
122c705cd3cSknakahara static int	gif_check_nesting(struct ifnet *, struct mbuf *);
123b36ce94cSthorpej 
124493e35e3Sknakahara static int	gif_encap_attach(struct gif_variant *);
125493e35e3Sknakahara static int	gif_encap_detach(struct gif_variant *);
126493e35e3Sknakahara 
127493e35e3Sknakahara static void	gif_update_variant(struct gif_softc *, struct gif_variant *);
128d81cd78eSknakahara 
12963eac52bSthorpej static struct if_clone gif_cloner =
130b36ce94cSthorpej     IF_CLONE_INITIALIZER("gif", gif_clone_create, gif_clone_destroy);
131b36ce94cSthorpej 
13239091335Sitojun #ifndef MAX_GIF_NEST
13339091335Sitojun /*
13439091335Sitojun  * This macro controls the upper limitation on nesting of gif tunnels.
13539091335Sitojun  * Since, setting a large value to this macro with a careless configuration
13639091335Sitojun  * may introduce system crash, we don't allow any nestings by default.
13739091335Sitojun  * If you need to configure nested gif tunnels, you can define this macro
13839091335Sitojun  * in your kernel configuration file.  However, if you do so, please be
13939091335Sitojun  * careful to configure the tunnels so that it won't make a loop.
14039091335Sitojun  */
14139091335Sitojun #define MAX_GIF_NEST 1
14239091335Sitojun #endif
14339091335Sitojun static int max_gif_nesting = MAX_GIF_NEST;
14474d3c214Sitojun 
1451d8e08d4Schristos static struct sysctllog *gif_sysctl;
1461d8e08d4Schristos 
14770414ea5Sknakahara #ifdef INET6
14870414ea5Sknakahara static int
14970414ea5Sknakahara sysctl_gif_pmtu_global(SYSCTLFN_ARGS)
15070414ea5Sknakahara {
15170414ea5Sknakahara 	int error, pmtu;
15270414ea5Sknakahara 	struct sysctlnode node = *rnode;
15370414ea5Sknakahara 
15470414ea5Sknakahara 	pmtu = ip6_gif_pmtu;
15570414ea5Sknakahara 	node.sysctl_data = &pmtu;
15670414ea5Sknakahara 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
15770414ea5Sknakahara 	if (error || newp == NULL)
15870414ea5Sknakahara 		return error;
15970414ea5Sknakahara 
16070414ea5Sknakahara 	switch (pmtu) {
16170414ea5Sknakahara 	case GIF_PMTU_MINMTU:
16270414ea5Sknakahara 	case GIF_PMTU_OUTERMTU:
16370414ea5Sknakahara 		ip6_gif_pmtu = pmtu;
16470414ea5Sknakahara 		break;
16570414ea5Sknakahara 	default:
16670414ea5Sknakahara 		return EINVAL;
16770414ea5Sknakahara 	}
16870414ea5Sknakahara 
16970414ea5Sknakahara 	return 0;
17070414ea5Sknakahara }
17170414ea5Sknakahara 
17270414ea5Sknakahara static int
17370414ea5Sknakahara sysctl_gif_pmtu_perif(SYSCTLFN_ARGS)
17470414ea5Sknakahara {
17570414ea5Sknakahara 	int error, pmtu;
17670414ea5Sknakahara 	struct sysctlnode node = *rnode;
17770414ea5Sknakahara 	struct gif_softc *sc = (struct gif_softc *)node.sysctl_data;
17870414ea5Sknakahara 
17970414ea5Sknakahara 	pmtu = sc->gif_pmtu;
18070414ea5Sknakahara 	node.sysctl_data = &pmtu;
18170414ea5Sknakahara 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
18270414ea5Sknakahara 	if (error || newp == NULL)
18370414ea5Sknakahara 		return error;
18470414ea5Sknakahara 
18570414ea5Sknakahara 	switch (pmtu) {
18670414ea5Sknakahara 	case GIF_PMTU_SYSDEFAULT:
18770414ea5Sknakahara 	case GIF_PMTU_MINMTU:
18870414ea5Sknakahara 	case GIF_PMTU_OUTERMTU:
18970414ea5Sknakahara 		sc->gif_pmtu = pmtu;
19070414ea5Sknakahara 		break;
19170414ea5Sknakahara 	default:
19270414ea5Sknakahara 		return EINVAL;
19370414ea5Sknakahara 	}
19470414ea5Sknakahara 
19570414ea5Sknakahara 	return 0;
19670414ea5Sknakahara }
19770414ea5Sknakahara #endif
19870414ea5Sknakahara 
1991c5d304eSknakahara static void
2001d8e08d4Schristos gif_sysctl_setup(void)
2011c5d304eSknakahara {
2027128a14dSknakahara 	const struct sysctlnode *node = NULL;
2037128a14dSknakahara 
2041d8e08d4Schristos 	gif_sysctl = NULL;
2051c5d304eSknakahara 
2061c5d304eSknakahara #ifdef INET
2077b53554dSknakahara 	/*
2087b53554dSknakahara 	 * Previously create "net.inet.ip" entry to avoid sysctl_createv error.
2097b53554dSknakahara 	 */
2107b53554dSknakahara 	sysctl_createv(NULL, 0, NULL, NULL,
2117b53554dSknakahara 		       CTLFLAG_PERMANENT,
2127b53554dSknakahara 		       CTLTYPE_NODE, "inet",
2137b53554dSknakahara 		       SYSCTL_DESCR("PF_INET related settings"),
2147b53554dSknakahara 		       NULL, 0, NULL, 0,
2157b53554dSknakahara 		       CTL_NET, PF_INET, CTL_EOL);
2167b53554dSknakahara 	sysctl_createv(NULL, 0, NULL, NULL,
2177b53554dSknakahara 		       CTLFLAG_PERMANENT,
2187b53554dSknakahara 		       CTLTYPE_NODE, "ip",
2197b53554dSknakahara 		       SYSCTL_DESCR("IPv4 related settings"),
2207b53554dSknakahara 		       NULL, 0, NULL, 0,
2217b53554dSknakahara 		       CTL_NET, PF_INET, IPPROTO_IP, CTL_EOL);
2227b53554dSknakahara 
2231d8e08d4Schristos 	sysctl_createv(&gif_sysctl, 0, NULL, NULL,
2241c5d304eSknakahara 		       CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
2251c5d304eSknakahara 		       CTLTYPE_INT, "gifttl",
2261c5d304eSknakahara 		       SYSCTL_DESCR("Default TTL for a gif tunnel datagram"),
2271c5d304eSknakahara 		       NULL, 0, &ip_gif_ttl, 0,
2281c5d304eSknakahara 		       CTL_NET, PF_INET, IPPROTO_IP,
2291c5d304eSknakahara 		       IPCTL_GIF_TTL, CTL_EOL);
2301c5d304eSknakahara #endif
2311c5d304eSknakahara #ifdef INET6
2327b53554dSknakahara 	/*
2337b53554dSknakahara 	 * Previously create "net.inet6.ip6" entry to avoid sysctl_createv error.
2347b53554dSknakahara 	 */
2357b53554dSknakahara 	sysctl_createv(NULL, 0, NULL, NULL,
2367b53554dSknakahara 		       CTLFLAG_PERMANENT,
2377b53554dSknakahara 		       CTLTYPE_NODE, "inet6",
2387b53554dSknakahara 		       SYSCTL_DESCR("PF_INET6 related settings"),
2397b53554dSknakahara 		       NULL, 0, NULL, 0,
2407b53554dSknakahara 		       CTL_NET, PF_INET6, CTL_EOL);
2417b53554dSknakahara 	sysctl_createv(NULL, 0, NULL, NULL,
2427b53554dSknakahara 		       CTLFLAG_PERMANENT,
2437b53554dSknakahara 		       CTLTYPE_NODE, "ip6",
2447b53554dSknakahara 		       SYSCTL_DESCR("IPv6 related settings"),
2457b53554dSknakahara 		       NULL, 0, NULL, 0,
2467b53554dSknakahara 		       CTL_NET, PF_INET6, IPPROTO_IPV6, CTL_EOL);
2477b53554dSknakahara 
2481d8e08d4Schristos 	sysctl_createv(&gif_sysctl, 0, NULL, NULL,
2491c5d304eSknakahara 		       CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
2501c5d304eSknakahara 		       CTLTYPE_INT, "gifhlim",
2511c5d304eSknakahara 		       SYSCTL_DESCR("Default hop limit for a gif tunnel datagram"),
2521c5d304eSknakahara 		       NULL, 0, &ip6_gif_hlim, 0,
2531c5d304eSknakahara 		       CTL_NET, PF_INET6, IPPROTO_IPV6,
2541c5d304eSknakahara 		       IPV6CTL_GIF_HLIM, CTL_EOL);
25570414ea5Sknakahara 
25670414ea5Sknakahara 	sysctl_createv(&gif_sysctl, 0, NULL, NULL,
25770414ea5Sknakahara 		       CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
25870414ea5Sknakahara 		       CTLTYPE_INT, "gifpmtu",
25970414ea5Sknakahara 		       SYSCTL_DESCR("Default Path MTU setting for gif tunnels"),
26070414ea5Sknakahara 		       sysctl_gif_pmtu_global, 0, NULL, 0,
26170414ea5Sknakahara 		       CTL_NET, PF_INET6, IPPROTO_IPV6,
26270414ea5Sknakahara 		       IPV6CTL_GIF_PMTU, CTL_EOL);
26370414ea5Sknakahara #endif
2647128a14dSknakahara 
2657128a14dSknakahara 	sysctl_createv(&gif_sysctl, 0, NULL, &node,
2667128a14dSknakahara 		       CTLFLAG_PERMANENT,
2677128a14dSknakahara 		       CTLTYPE_NODE, "gif",
2687128a14dSknakahara 		       SYSCTL_DESCR("gif global control"),
2697128a14dSknakahara 		       NULL, 0, NULL, 0,
2707128a14dSknakahara 		       CTL_NET, CTL_CREATE, CTL_EOL);
2717128a14dSknakahara 
2727128a14dSknakahara 	sysctl_createv(&gif_sysctl, 0, &node, NULL,
2737128a14dSknakahara 		       CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
2747128a14dSknakahara 		       CTLTYPE_STRING, "rps_hash",
2757128a14dSknakahara 		       SYSCTL_DESCR("Interface rps hash function control"),
2767128a14dSknakahara 		       sysctl_pktq_rps_hash_handler, 0, (void *)&gif_pktq_rps_hash_p,
2777128a14dSknakahara 		       PKTQ_RPS_HASH_NAME_LEN,
2787128a14dSknakahara 		       CTL_CREATE, CTL_EOL);
27970414ea5Sknakahara }
28070414ea5Sknakahara 
28170414ea5Sknakahara static void
28270414ea5Sknakahara gif_perif_sysctl_setup(struct sysctllog **clog, struct gif_softc *sc)
28370414ea5Sknakahara {
28470414ea5Sknakahara #ifdef INET6
28570414ea5Sknakahara 	const struct sysctlnode *cnode, *rnode;
28670414ea5Sknakahara 	struct ifnet *ifp = &sc->gif_if;
28770414ea5Sknakahara 	const char *ifname = ifp->if_xname;
28870414ea5Sknakahara 	int rv;
28970414ea5Sknakahara 
29070414ea5Sknakahara 	/*
29170414ea5Sknakahara 	 * Already created in sysctl_sndq_setup().
29270414ea5Sknakahara 	 */
29370414ea5Sknakahara 	sysctl_createv(clog, 0, NULL, &rnode,
29470414ea5Sknakahara 		       CTLFLAG_PERMANENT,
29570414ea5Sknakahara 		       CTLTYPE_NODE, "interfaces",
29670414ea5Sknakahara 		       SYSCTL_DESCR("Per-interface controls"),
29770414ea5Sknakahara 		       NULL, 0, NULL, 0,
29870414ea5Sknakahara 		       CTL_NET, CTL_CREATE, CTL_EOL);
29970414ea5Sknakahara 	sysctl_createv(clog, 0, &rnode, &rnode,
30070414ea5Sknakahara 		       CTLFLAG_PERMANENT,
30170414ea5Sknakahara 		       CTLTYPE_NODE, ifname,
30270414ea5Sknakahara 		       SYSCTL_DESCR("Interface controls"),
30370414ea5Sknakahara 		       NULL, 0, NULL, 0,
30470414ea5Sknakahara 		       CTL_CREATE, CTL_EOL);
30570414ea5Sknakahara 
30670414ea5Sknakahara 	rv = sysctl_createv(clog, 0, &rnode, &cnode,
30770414ea5Sknakahara 			    CTLFLAG_PERMANENT,
30870414ea5Sknakahara 			    CTLTYPE_INT, "pmtu",
30970414ea5Sknakahara 			    SYSCTL_DESCR("Path MTU setting for this gif tunnel"),
31070414ea5Sknakahara 			    sysctl_gif_pmtu_perif, 0, (void *)sc, 0,
31170414ea5Sknakahara 			    CTL_CREATE, CTL_EOL);
31270414ea5Sknakahara 	if (rv != 0)
31370414ea5Sknakahara 		log(LOG_WARNING, "%s: could not attach sysctl node pmtu\n", ifname);
31470414ea5Sknakahara 
31570414ea5Sknakahara 	sc->gif_pmtu = GIF_PMTU_SYSDEFAULT;
3161c5d304eSknakahara #endif
3171c5d304eSknakahara }
3181c5d304eSknakahara 
319b36ce94cSthorpej /* ARGSUSED */
32074d3c214Sitojun void
321168cd830Schristos gifattach(int count)
32274d3c214Sitojun {
3231d8e08d4Schristos 	/*
3241d8e08d4Schristos 	 * Nothing to do here, initialization is handled by the
3251d8e08d4Schristos 	 * module initialization code in gifinit() below).
3261d8e08d4Schristos 	 */
3271d8e08d4Schristos }
3281d8e08d4Schristos 
3291d8e08d4Schristos static void
3301d8e08d4Schristos gifinit(void)
3311d8e08d4Schristos {
33274d3c214Sitojun 
3331cc266b8Sknakahara 	mutex_init(&gif_softcs.lock, MUTEX_DEFAULT, IPL_NONE);
3341cc266b8Sknakahara 	LIST_INIT(&gif_softcs.list);
335b36ce94cSthorpej 	if_clone_attach(&gif_cloner);
3361c5d304eSknakahara 
337493e35e3Sknakahara 	gv_psref_class = psref_class_create("gifvar", IPL_SOFTNET);
338493e35e3Sknakahara 
3397128a14dSknakahara 	gif_pktq_rps_hash_p = pktq_rps_hash_default;
3401d8e08d4Schristos 	gif_sysctl_setup();
3411d8e08d4Schristos }
3421d8e08d4Schristos 
3431d8e08d4Schristos static int
3441d8e08d4Schristos gifdetach(void)
3451d8e08d4Schristos {
3461d8e08d4Schristos 
3471cc266b8Sknakahara 	mutex_enter(&gif_softcs.lock);
3481cc266b8Sknakahara 	if (!LIST_EMPTY(&gif_softcs.list)) {
3491cc266b8Sknakahara 		mutex_exit(&gif_softcs.lock);
350c3bbf81dSchristos 		return EBUSY;
3511cc266b8Sknakahara 	}
3521d8e08d4Schristos 
353493e35e3Sknakahara 	psref_class_destroy(gv_psref_class);
354493e35e3Sknakahara 
3551d8e08d4Schristos 	if_clone_detach(&gif_cloner);
3561d8e08d4Schristos 	sysctl_teardown(&gif_sysctl);
357c3bbf81dSchristos 	mutex_exit(&gif_softcs.lock);
358c3bbf81dSchristos 	mutex_destroy(&gif_softcs.lock);
359c3bbf81dSchristos 	return 0;
360b36ce94cSthorpej }
361b36ce94cSthorpej 
36263eac52bSthorpej static int
36363eac52bSthorpej gif_clone_create(struct if_clone *ifc, int unit)
364b36ce94cSthorpej {
365b36ce94cSthorpej 	struct gif_softc *sc;
366493e35e3Sknakahara 	struct gif_variant *var;
36770414ea5Sknakahara 	struct ifnet *ifp;
368916c83eeSmsaitoh 	int rv;
369b36ce94cSthorpej 
370849e83faSknakahara 	sc = kmem_zalloc(sizeof(struct gif_softc), KM_SLEEP);
371b36ce94cSthorpej 
372bc168f27Schristos 	if_initname(&sc->gif_if, ifc->ifc_name, unit);
37339091335Sitojun 
374916c83eeSmsaitoh 	rv = gifattach0(sc);
375916c83eeSmsaitoh 	if (rv != 0) {
376916c83eeSmsaitoh 		kmem_free(sc, sizeof(struct gif_softc));
377916c83eeSmsaitoh 		return rv;
378916c83eeSmsaitoh 	}
37939091335Sitojun 
38070414ea5Sknakahara 	ifp = &sc->gif_if;
38170414ea5Sknakahara 	gif_perif_sysctl_setup(&ifp->if_sysctl_log, sc);
38270414ea5Sknakahara 
383493e35e3Sknakahara 	var = kmem_zalloc(sizeof(*var), KM_SLEEP);
384493e35e3Sknakahara 	var->gv_softc = sc;
385493e35e3Sknakahara 	psref_target_init(&var->gv_psref, gv_psref_class);
386493e35e3Sknakahara 
387493e35e3Sknakahara 	sc->gif_var = var;
388493e35e3Sknakahara 	mutex_init(&sc->gif_lock, MUTEX_DEFAULT, IPL_NONE);
389ebac3c72Sknakahara 	sc->gif_psz = pserialize_create();
390ebac3c72Sknakahara 
3912da350beSknakahara 	sc->gif_ro_percpu = if_tunnel_alloc_ro_percpu();
3921cc266b8Sknakahara 	mutex_enter(&gif_softcs.lock);
3931cc266b8Sknakahara 	LIST_INSERT_HEAD(&gif_softcs.list, sc, gif_list);
3941cc266b8Sknakahara 	mutex_exit(&gif_softcs.lock);
395916c83eeSmsaitoh 	return 0;
396cad488d0Sitojun }
397cad488d0Sitojun 
398916c83eeSmsaitoh static int
39963eac52bSthorpej gifattach0(struct gif_softc *sc)
400cad488d0Sitojun {
401cad488d0Sitojun 
402cad488d0Sitojun 	sc->gif_if.if_addrlen = 0;
40374d3c214Sitojun 	sc->gif_if.if_mtu    = GIF_MTU;
40474d3c214Sitojun 	sc->gif_if.if_flags  = IFF_POINTOPOINT | IFF_MULTICAST;
4054ba8ad0bSknakahara #ifdef GIF_MPSAFE
406ab3cd725Sozaki-r 	sc->gif_if.if_extflags  |= IFEF_MPSAFE;
4074ba8ad0bSknakahara #endif
40874d3c214Sitojun 	sc->gif_if.if_ioctl  = gif_ioctl;
40974d3c214Sitojun 	sc->gif_if.if_output = gif_output;
4107216411eSknakahara 	sc->gif_if.if_start = gif_start;
4117216411eSknakahara 	sc->gif_if.if_transmit = gif_transmit;
41274d3c214Sitojun 	sc->gif_if.if_type   = IFT_GIF;
413ed7695a7Sthorpej 	sc->gif_if.if_dlt    = DLT_NULL;
414de87fe67Sdyoung 	sc->gif_if.if_softc  = sc;
415e556ec90Sitojun 	IFQ_SET_READY(&sc->gif_if.if_snd);
416076e3579Sriastradh 	if_initialize(&sc->gif_if);
417916c83eeSmsaitoh 
418680a94beSroy 	sc->gif_if.if_link_state = LINK_STATE_DOWN;
419fc5dafc7Sthorpej 	if_alloc_sadl(&sc->gif_if);
42058e86755Sjoerg 	bpf_attach(&sc->gif_if, DLT_NULL, sizeof(u_int));
42174ffb1c2Sozaki-r 	if_register(&sc->gif_if);
422916c83eeSmsaitoh 	return 0;
42339091335Sitojun }
424b36ce94cSthorpej 
42563eac52bSthorpej static int
42663eac52bSthorpej gif_clone_destroy(struct ifnet *ifp)
427b36ce94cSthorpej {
428b36ce94cSthorpej 	struct gif_softc *sc = (void *) ifp;
429493e35e3Sknakahara 	struct gif_variant *var;
430b36ce94cSthorpej 
431b36ce94cSthorpej 	LIST_REMOVE(sc, gif_list);
432b36ce94cSthorpej 
43348ec8fb3Sknakahara 	gif_delete_tunnel(&sc->gif_if);
43458e86755Sjoerg 	bpf_detach(ifp);
435b36ce94cSthorpej 	if_detach(ifp);
43656188c2aSknakahara 
4372da350beSknakahara 	if_tunnel_free_ro_percpu(sc->gif_ro_percpu);
43856188c2aSknakahara 
439ebac3c72Sknakahara 	pserialize_destroy(sc->gif_psz);
440493e35e3Sknakahara 	mutex_destroy(&sc->gif_lock);
441493e35e3Sknakahara 
442493e35e3Sknakahara 	var = sc->gif_var;
443493e35e3Sknakahara 	kmem_free(var, sizeof(*var));
444849e83faSknakahara 	kmem_free(sc, sizeof(struct gif_softc));
445b9c49ebfSpeter 
446916c83eeSmsaitoh 	return 0;
44739091335Sitojun }
44839091335Sitojun 
449c8a83266Sitojun #ifdef GIF_ENCAPCHECK
450cad488d0Sitojun int
45163eac52bSthorpej gif_encapcheck(struct mbuf *m, int off, int proto, void *arg)
45239091335Sitojun {
45339091335Sitojun 	struct ip ip;
45439091335Sitojun 	struct gif_softc *sc;
455493e35e3Sknakahara 	struct gif_variant *var;
456493e35e3Sknakahara 	struct psref psref;
457493e35e3Sknakahara 	int ret = 0;
45839091335Sitojun 
459de87fe67Sdyoung 	sc = arg;
46039091335Sitojun 	if (sc == NULL)
46139091335Sitojun 		return 0;
46239091335Sitojun 
463753ed340Sknakahara 	if ((sc->gif_if.if_flags & IFF_UP) == 0)
46439091335Sitojun 		return 0;
46539091335Sitojun 
466493e35e3Sknakahara 	var = gif_getref_variant(sc, &psref);
46739091335Sitojun 	/* no physical address */
46819e3ef90Sknakahara 	if (var->gv_psrc == NULL || var->gv_pdst == NULL)
469493e35e3Sknakahara 		goto out;
47039091335Sitojun 
47139091335Sitojun 	switch (proto) {
47239091335Sitojun #ifdef INET
47339091335Sitojun 	case IPPROTO_IPV4:
47439091335Sitojun 		break;
47539091335Sitojun #endif
47639091335Sitojun #ifdef INET6
47739091335Sitojun 	case IPPROTO_IPV6:
47839091335Sitojun 		break;
47939091335Sitojun #endif
48039091335Sitojun 	default:
481493e35e3Sknakahara 		goto out;
48239091335Sitojun 	}
48339091335Sitojun 
4845c8e1817Schristos 	/* Bail on short packets */
4855c8e1817Schristos 	KASSERT(m->m_flags & M_PKTHDR);
4865c8e1817Schristos 	if (m->m_pkthdr.len < sizeof(ip))
487493e35e3Sknakahara 		goto  out;
4885c8e1817Schristos 
489de87fe67Sdyoung 	m_copydata(m, 0, sizeof(ip), &ip);
49039091335Sitojun 
49139091335Sitojun 	switch (ip.ip_v) {
49239091335Sitojun #ifdef INET
49339091335Sitojun 	case 4:
494493e35e3Sknakahara 		if (var->gv_psrc->sa_family != AF_INET ||
495493e35e3Sknakahara 		    var->gv_pdst->sa_family != AF_INET)
496493e35e3Sknakahara 			goto out;
497493e35e3Sknakahara 		ret = gif_encapcheck4(m, off, proto, var);
498493e35e3Sknakahara 		break;
49939091335Sitojun #endif
50039091335Sitojun #ifdef INET6
50139091335Sitojun 	case 6:
50200f97b02Sitojun 		if (m->m_pkthdr.len < sizeof(struct ip6_hdr))
503493e35e3Sknakahara 			goto out;
504493e35e3Sknakahara 		if (var->gv_psrc->sa_family != AF_INET6 ||
505493e35e3Sknakahara 		    var->gv_pdst->sa_family != AF_INET6)
506493e35e3Sknakahara 			goto out;
507493e35e3Sknakahara 		ret = gif_encapcheck6(m, off, proto, var);
508493e35e3Sknakahara 		break;
50939091335Sitojun #endif
51039091335Sitojun 	default:
511493e35e3Sknakahara 		goto out;
51274d3c214Sitojun 	}
513493e35e3Sknakahara 
514493e35e3Sknakahara out:
515493e35e3Sknakahara 	gif_putref_variant(var, &psref);
516493e35e3Sknakahara 	return ret;
51774d3c214Sitojun }
518c8a83266Sitojun #endif
51974d3c214Sitojun 
520c705cd3cSknakahara /*
521c705cd3cSknakahara  * gif may cause infinite recursion calls when misconfigured.
522c705cd3cSknakahara  * We'll prevent this by introducing upper limit.
523c705cd3cSknakahara  */
524c705cd3cSknakahara static int
525c705cd3cSknakahara gif_check_nesting(struct ifnet *ifp, struct mbuf *m)
526c705cd3cSknakahara {
527c705cd3cSknakahara 
528d4228baeSknakahara 	return if_tunnel_check_nesting(ifp, m, max_gif_nesting);
529c705cd3cSknakahara }
530c705cd3cSknakahara 
531d60d8acaSknakahara static int
5325493f188Sdyoung gif_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
5332cf7873bSozaki-r     const struct rtentry *rt)
53474d3c214Sitojun {
535de87fe67Sdyoung 	struct gif_softc *sc = ifp->if_softc;
536493e35e3Sknakahara 	struct gif_variant *var = NULL;
537493e35e3Sknakahara 	struct psref psref;
53874d3c214Sitojun 	int error = 0;
53974ad87bcSitojun 
540b76ec0b0Sknakahara 	IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family);
54174d3c214Sitojun 
542c705cd3cSknakahara 	if ((error = gif_check_nesting(ifp, m)) != 0) {
543b17a3c6eSmaxv 		m_freem(m);
54474d3c214Sitojun 		goto end;
54574d3c214Sitojun 	}
54674d3c214Sitojun 
547753ed340Sknakahara 	if ((ifp->if_flags & IFF_UP) == 0) {
54874d3c214Sitojun 		m_freem(m);
54974d3c214Sitojun 		error = ENETDOWN;
55074d3c214Sitojun 		goto end;
55174d3c214Sitojun 	}
55274d3c214Sitojun 
553493e35e3Sknakahara 	var = gif_getref_variant(sc, &psref);
554493e35e3Sknakahara 	if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
555493e35e3Sknakahara 		m_freem(m);
556493e35e3Sknakahara 		error = ENETDOWN;
557493e35e3Sknakahara 		goto end;
558493e35e3Sknakahara 	}
55939091335Sitojun 	/* XXX should we check if our outer source is legal? */
56074d3c214Sitojun 
561493e35e3Sknakahara 	m->m_flags &= ~(M_BCAST | M_MCAST);
562493e35e3Sknakahara 
56374ad87bcSitojun 	/* use DLT_NULL encapsulation here to pass inner af type */
56474ad87bcSitojun 	M_PREPEND(m, sizeof(int), M_DONTWAIT);
56574ad87bcSitojun 	if (!m) {
56674ad87bcSitojun 		error = ENOBUFS;
567cad488d0Sitojun 		goto end;
56874d3c214Sitojun 	}
56974ad87bcSitojun 	*mtod(m, int *) = dst->sa_family;
57074ad87bcSitojun 
5710275d524Sdyoung 	/* Clear checksum-offload flags. */
5720275d524Sdyoung 	m->m_pkthdr.csum_flags = 0;
5730275d524Sdyoung 	m->m_pkthdr.csum_data = 0;
5740275d524Sdyoung 
57596d91f81Sknakahara 	error = if_transmit_lock(ifp, m);
57696d91f81Sknakahara 
57774d3c214Sitojun end:
578493e35e3Sknakahara 	if (var != NULL)
579493e35e3Sknakahara 		gif_putref_variant(var, &psref);
580cad488d0Sitojun 	if (error)
58170b554e6Sthorpej 		if_statinc(ifp, if_oerrors);
58274d3c214Sitojun 	return error;
58374d3c214Sitojun }
58474d3c214Sitojun 
58563eac52bSthorpej static void
586ccd8e6e6Sknakahara gif_start(struct ifnet *ifp)
58774ad87bcSitojun {
58874ad87bcSitojun 	struct gif_softc *sc;
589493e35e3Sknakahara 	struct gif_variant *var;
59074ad87bcSitojun 	struct mbuf *m;
591493e35e3Sknakahara 	struct psref psref;
59274ad87bcSitojun 	int family;
59374ad87bcSitojun 	int len;
59474ad87bcSitojun 	int error;
59574ad87bcSitojun 
596ccd8e6e6Sknakahara 	sc = ifp->if_softc;
597493e35e3Sknakahara 	var = gif_getref_variant(sc, &psref);
598493e35e3Sknakahara 
599493e35e3Sknakahara 	KASSERT(var->gv_output != NULL);
600a00e94f4Sknakahara 
60174ad87bcSitojun 	/* output processing */
60274ad87bcSitojun 	while (1) {
603e556ec90Sitojun 		IFQ_DEQUEUE(&sc->gif_if.if_snd, m);
60474ad87bcSitojun 		if (m == NULL)
60574ad87bcSitojun 			break;
60674ad87bcSitojun 
60774ad87bcSitojun 		/* grab and chop off inner af type */
60874ad87bcSitojun 		if (sizeof(int) > m->m_len) {
60974ad87bcSitojun 			m = m_pullup(m, sizeof(int));
61074ad87bcSitojun 			if (!m) {
61170b554e6Sthorpej 				if_statinc(ifp, if_oerrors);
61274ad87bcSitojun 				continue;
61374ad87bcSitojun 			}
61474ad87bcSitojun 		}
61574ad87bcSitojun 		family = *mtod(m, int *);
6163cd62456Smsaitoh 		bpf_mtap(ifp, m, BPF_D_OUT);
61774ad87bcSitojun 		m_adj(m, sizeof(int));
61874ad87bcSitojun 
61974ad87bcSitojun 		len = m->m_pkthdr.len;
62074ad87bcSitojun 
621493e35e3Sknakahara 		error = var->gv_output(var, family, m);
62274ad87bcSitojun 		if (error)
62370b554e6Sthorpej 			if_statinc(ifp, if_oerrors);
62470b554e6Sthorpej 		else
62570b554e6Sthorpej 			if_statadd2(ifp, if_opackets, 1, if_obytes, len);
62674ad87bcSitojun 	}
627493e35e3Sknakahara 
628493e35e3Sknakahara 	gif_putref_variant(var, &psref);
62974ad87bcSitojun }
63074ad87bcSitojun 
6317216411eSknakahara static int
6327216411eSknakahara gif_transmit(struct ifnet *ifp, struct mbuf *m)
6337216411eSknakahara {
6347216411eSknakahara 	struct gif_softc *sc;
635493e35e3Sknakahara 	struct gif_variant *var;
636493e35e3Sknakahara 	struct psref psref;
6377216411eSknakahara 	int error;
6387216411eSknakahara 
6397216411eSknakahara 	sc = ifp->if_softc;
6407216411eSknakahara 
6417216411eSknakahara 	/* output processing */
6427216411eSknakahara 	if (m == NULL)
6437216411eSknakahara 		return EINVAL;
6447216411eSknakahara 
645493e35e3Sknakahara 	var = gif_getref_variant(sc, &psref);
646493e35e3Sknakahara 	error = gif_transmit_direct(var, m);
647493e35e3Sknakahara 	gif_putref_variant(var, &psref);
648493e35e3Sknakahara 
649493e35e3Sknakahara 	return error;
650493e35e3Sknakahara }
651493e35e3Sknakahara 
652493e35e3Sknakahara static int
653493e35e3Sknakahara gif_transmit_direct(struct gif_variant *var, struct mbuf *m)
654493e35e3Sknakahara {
655493e35e3Sknakahara 	struct ifnet *ifp = &var->gv_softc->gif_if;
656493e35e3Sknakahara 	int error;
657493e35e3Sknakahara 	int family;
658493e35e3Sknakahara 	int len;
659493e35e3Sknakahara 
660493e35e3Sknakahara 	KASSERT(gif_heldref_variant(var));
661493e35e3Sknakahara 	KASSERT(var->gv_output != NULL);
662493e35e3Sknakahara 
6637216411eSknakahara 	/* grab and chop off inner af type */
6647216411eSknakahara 	if (sizeof(int) > m->m_len) {
6657216411eSknakahara 		m = m_pullup(m, sizeof(int));
6667216411eSknakahara 		if (!m) {
66770b554e6Sthorpej 			if_statinc(ifp, if_oerrors);
6687216411eSknakahara 			return ENOBUFS;
6697216411eSknakahara 		}
6707216411eSknakahara 	}
6717216411eSknakahara 	family = *mtod(m, int *);
6723cd62456Smsaitoh 	bpf_mtap(ifp, m, BPF_D_OUT);
6737216411eSknakahara 	m_adj(m, sizeof(int));
6747216411eSknakahara 
6757216411eSknakahara 	len = m->m_pkthdr.len;
6767216411eSknakahara 
677493e35e3Sknakahara 	error = var->gv_output(var, family, m);
6787216411eSknakahara 	if (error)
67970b554e6Sthorpej 		if_statinc(ifp, if_oerrors);
68070b554e6Sthorpej 	else
68170b554e6Sthorpej 		if_statadd2(ifp, if_opackets, 1, if_obytes, len);
6827216411eSknakahara 
6837216411eSknakahara 	return error;
6847216411eSknakahara }
6857216411eSknakahara 
68674ad87bcSitojun void
68763eac52bSthorpej gif_input(struct mbuf *m, int af, struct ifnet *ifp)
68874d3c214Sitojun {
68960d350cfSrmind 	pktqueue_t *pktq;
69060d350cfSrmind 	size_t pktlen;
69174d3c214Sitojun 
69274ad87bcSitojun 	if (ifp == NULL) {
69374d3c214Sitojun 		/* just in case */
69474d3c214Sitojun 		m_freem(m);
69574d3c214Sitojun 		return;
69674d3c214Sitojun 	}
69774d3c214Sitojun 
698d938d837Sozaki-r 	m_set_rcvif(m, ifp);
69960d350cfSrmind 	pktlen = m->m_pkthdr.len;
70074d3c214Sitojun 
7013cd62456Smsaitoh 	bpf_mtap_af(ifp, af, m, BPF_D_IN);
70274d3c214Sitojun 
70374d3c214Sitojun 	/*
70474d3c214Sitojun 	 * Put the packet to the network layer input queue according to the
70560d350cfSrmind 	 * specified address family.  Note: we avoid direct call to the
70660d350cfSrmind 	 * input function of the network layer in order to avoid recursion.
70760d350cfSrmind 	 * This may be revisited in the future.
70874d3c214Sitojun 	 */
70974d3c214Sitojun 	switch (af) {
71074d3c214Sitojun #ifdef INET
71174d3c214Sitojun 	case AF_INET:
71260d350cfSrmind 		pktq = ip_pktq;
71374d3c214Sitojun 		break;
71474d3c214Sitojun #endif
71574d3c214Sitojun #ifdef INET6
71674d3c214Sitojun 	case AF_INET6:
71760d350cfSrmind 		pktq = ip6_pktq;
71874d3c214Sitojun 		break;
71974d3c214Sitojun #endif
72074d3c214Sitojun 	default:
72174d3c214Sitojun 		m_freem(m);
72274d3c214Sitojun 		return;
72374d3c214Sitojun 	}
72474d3c214Sitojun 
7257128a14dSknakahara 	const uint32_t h = pktq_rps_hash(&gif_pktq_rps_hash_p, m);
726cdd5bc98Sknakahara 	if (__predict_true(pktq_enqueue(pktq, m, h))) {
72770b554e6Sthorpej 		if_statadd2(ifp, if_ibytes, pktlen, if_ipackets, 1);
72860d350cfSrmind 	} else {
72960d350cfSrmind 		m_freem(m);
73060d350cfSrmind 	}
73174d3c214Sitojun }
73274d3c214Sitojun 
73339091335Sitojun /* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */
734d60d8acaSknakahara static int
73553524e44Schristos gif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
73674d3c214Sitojun {
737de87fe67Sdyoung 	struct gif_softc *sc  = ifp->if_softc;
73874d3c214Sitojun 	struct ifreq     *ifr = (struct ifreq*)data;
73933e035dcSroy 	struct ifaddr    *ifa = (struct ifaddr*)data;
740493e35e3Sknakahara 	int error = 0, size, bound;
74139091335Sitojun 	struct sockaddr *dst, *src;
742493e35e3Sknakahara 	struct gif_variant *var;
743493e35e3Sknakahara 	struct psref psref;
74474d3c214Sitojun 
74574d3c214Sitojun 	switch (cmd) {
746de87fe67Sdyoung 	case SIOCINITIFADDR:
74739bc63e6Sitojun 		ifp->if_flags |= IFF_UP;
74833e035dcSroy 		ifa->ifa_rtrequest = p2p_rtrequest;
74974d3c214Sitojun 		break;
75074d3c214Sitojun 
75174d3c214Sitojun 	case SIOCADDMULTI:
75274d3c214Sitojun 	case SIOCDELMULTI:
75374d3c214Sitojun 		switch (ifr->ifr_addr.sa_family) {
75474d3c214Sitojun #ifdef INET
75574d3c214Sitojun 		case AF_INET:	/* IP supports Multicast */
75674d3c214Sitojun 			break;
75774d3c214Sitojun #endif /* INET */
75874d3c214Sitojun #ifdef INET6
75974d3c214Sitojun 		case AF_INET6:	/* IP6 supports Multicast */
76074d3c214Sitojun 			break;
76174d3c214Sitojun #endif /* INET6 */
76274d3c214Sitojun 		default:  /* Other protocols doesn't support Multicast */
76374d3c214Sitojun 			error = EAFNOSUPPORT;
76474d3c214Sitojun 			break;
76574d3c214Sitojun 		}
76674d3c214Sitojun 		break;
76774d3c214Sitojun 
76874d3c214Sitojun 	case SIOCSIFMTU:
7692ccede0aSdyoung 		if (ifr->ifr_mtu < GIF_MTU_MIN || ifr->ifr_mtu > GIF_MTU_MAX)
7702ccede0aSdyoung 			return EINVAL;
7712ccede0aSdyoung 		else if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
7722ccede0aSdyoung 			error = 0;
77374d3c214Sitojun 		break;
77474d3c214Sitojun 
775cad488d0Sitojun #ifdef INET
77674d3c214Sitojun 	case SIOCSIFPHYADDR:
777cad488d0Sitojun #endif
77874d3c214Sitojun #ifdef INET6
77974d3c214Sitojun 	case SIOCSIFPHYADDR_IN6:
78074d3c214Sitojun #endif /* INET6 */
7811e48b7fbSitojun 	case SIOCSLIFPHYADDR:
782948e11b7Sitojun 		switch (cmd) {
783dcfe05e7Sitojun #ifdef INET
784948e11b7Sitojun 		case SIOCSIFPHYADDR:
78574d3c214Sitojun 			src = (struct sockaddr *)
78674d3c214Sitojun 				&(((struct in_aliasreq *)data)->ifra_addr);
78774d3c214Sitojun 			dst = (struct sockaddr *)
78874d3c214Sitojun 				&(((struct in_aliasreq *)data)->ifra_dstaddr);
789948e11b7Sitojun 			break;
790dcfe05e7Sitojun #endif
791948e11b7Sitojun #ifdef INET6
792948e11b7Sitojun 		case SIOCSIFPHYADDR_IN6:
793948e11b7Sitojun 			src = (struct sockaddr *)
794948e11b7Sitojun 				&(((struct in6_aliasreq *)data)->ifra_addr);
795948e11b7Sitojun 			dst = (struct sockaddr *)
796948e11b7Sitojun 				&(((struct in6_aliasreq *)data)->ifra_dstaddr);
797948e11b7Sitojun 			break;
798948e11b7Sitojun #endif
7991e48b7fbSitojun 		case SIOCSLIFPHYADDR:
8001e48b7fbSitojun 			src = (struct sockaddr *)
8011e48b7fbSitojun 				&(((struct if_laddrreq *)data)->addr);
8021e48b7fbSitojun 			dst = (struct sockaddr *)
8031e48b7fbSitojun 				&(((struct if_laddrreq *)data)->dstaddr);
804cad488d0Sitojun 			break;
805cad488d0Sitojun 		default:
806cad488d0Sitojun 			return EINVAL;
8071e48b7fbSitojun 		}
8081e48b7fbSitojun 
8091e48b7fbSitojun 		/* sa_family must be equal */
8101e48b7fbSitojun 		if (src->sa_family != dst->sa_family)
8111e48b7fbSitojun 			return EINVAL;
8121e48b7fbSitojun 
8131e48b7fbSitojun 		/* validate sa_len */
8141e48b7fbSitojun 		switch (src->sa_family) {
8151e48b7fbSitojun #ifdef INET
8161e48b7fbSitojun 		case AF_INET:
8171e48b7fbSitojun 			if (src->sa_len != sizeof(struct sockaddr_in))
8181e48b7fbSitojun 				return EINVAL;
8191e48b7fbSitojun 			break;
8201e48b7fbSitojun #endif
8211e48b7fbSitojun #ifdef INET6
8221e48b7fbSitojun 		case AF_INET6:
8231e48b7fbSitojun 			if (src->sa_len != sizeof(struct sockaddr_in6))
8241e48b7fbSitojun 				return EINVAL;
8251e48b7fbSitojun 			break;
8261e48b7fbSitojun #endif
8271e48b7fbSitojun 		default:
8281e48b7fbSitojun 			return EAFNOSUPPORT;
8291e48b7fbSitojun 		}
8301e48b7fbSitojun 		switch (dst->sa_family) {
8311e48b7fbSitojun #ifdef INET
8321e48b7fbSitojun 		case AF_INET:
8331e48b7fbSitojun 			if (dst->sa_len != sizeof(struct sockaddr_in))
8341e48b7fbSitojun 				return EINVAL;
8351e48b7fbSitojun 			break;
8361e48b7fbSitojun #endif
8371e48b7fbSitojun #ifdef INET6
8381e48b7fbSitojun 		case AF_INET6:
8391e48b7fbSitojun 			if (dst->sa_len != sizeof(struct sockaddr_in6))
8401e48b7fbSitojun 				return EINVAL;
8411e48b7fbSitojun 			break;
8421e48b7fbSitojun #endif
8431e48b7fbSitojun 		default:
8441e48b7fbSitojun 			return EAFNOSUPPORT;
8451e48b7fbSitojun 		}
8461e48b7fbSitojun 
8471e48b7fbSitojun 		/* check sa_family looks sane for the cmd */
8481e48b7fbSitojun 		switch (cmd) {
8491e48b7fbSitojun 		case SIOCSIFPHYADDR:
8501e48b7fbSitojun 			if (src->sa_family == AF_INET)
8511e48b7fbSitojun 				break;
8521e48b7fbSitojun 			return EAFNOSUPPORT;
8531e48b7fbSitojun #ifdef INET6
8541e48b7fbSitojun 		case SIOCSIFPHYADDR_IN6:
8551e48b7fbSitojun 			if (src->sa_family == AF_INET6)
8561e48b7fbSitojun 				break;
8571e48b7fbSitojun 			return EAFNOSUPPORT;
8581e48b7fbSitojun #endif /* INET6 */
8591e48b7fbSitojun 		case SIOCSLIFPHYADDR:
8601e48b7fbSitojun 			/* checks done in the above */
8611e48b7fbSitojun 			break;
862948e11b7Sitojun 		}
863680a94beSroy 
864493e35e3Sknakahara 		/*
865493e35e3Sknakahara 		 * calls gif_getref_variant() for other softcs to check
8661cd43426Sandvar 		 * address pair duplication
867493e35e3Sknakahara 		 */
868493e35e3Sknakahara 		bound = curlwp_bind();
869cad488d0Sitojun 		error = gif_set_tunnel(&sc->gif_if, src, dst);
870680a94beSroy 		if (error == 0)
871680a94beSroy 			if_link_state_change(&sc->gif_if, LINK_STATE_UP);
872493e35e3Sknakahara 		curlwp_bindx(bound);
873680a94beSroy 
87474d3c214Sitojun 		break;
87574d3c214Sitojun 
87639091335Sitojun #ifdef SIOCDIFPHYADDR
87739091335Sitojun 	case SIOCDIFPHYADDR:
878493e35e3Sknakahara 		bound = curlwp_bind();
879cad488d0Sitojun 		gif_delete_tunnel(&sc->gif_if);
880680a94beSroy 		if_link_state_change(&sc->gif_if, LINK_STATE_DOWN);
881493e35e3Sknakahara 		curlwp_bindx(bound);
88239091335Sitojun 		break;
88339091335Sitojun #endif
88439091335Sitojun 
88574d3c214Sitojun 	case SIOCGIFPSRCADDR:
88674d3c214Sitojun #ifdef INET6
88774d3c214Sitojun 	case SIOCGIFPSRCADDR_IN6:
88874d3c214Sitojun #endif /* INET6 */
889493e35e3Sknakahara 		bound = curlwp_bind();
890493e35e3Sknakahara 		var = gif_getref_variant(sc, &psref);
891493e35e3Sknakahara 		if (var->gv_psrc == NULL) {
892493e35e3Sknakahara 			gif_putref_variant(var, &psref);
893493e35e3Sknakahara 			curlwp_bindx(bound);
89474d3c214Sitojun 			error = EADDRNOTAVAIL;
89574d3c214Sitojun 			goto bad;
89674d3c214Sitojun 		}
897493e35e3Sknakahara 		src = var->gv_psrc;
89808af3d2fSitojun 		switch (cmd) {
89974d3c214Sitojun #ifdef INET
90008af3d2fSitojun 		case SIOCGIFPSRCADDR:
90174d3c214Sitojun 			dst = &ifr->ifr_addr;
90208af3d2fSitojun 			size = sizeof(ifr->ifr_addr);
90374d3c214Sitojun 			break;
90474d3c214Sitojun #endif /* INET */
90574d3c214Sitojun #ifdef INET6
90608af3d2fSitojun 		case SIOCGIFPSRCADDR_IN6:
90774d3c214Sitojun 			dst = (struct sockaddr *)
90874d3c214Sitojun 				&(((struct in6_ifreq *)data)->ifr_addr);
90908af3d2fSitojun 			size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
91074d3c214Sitojun 			break;
91174d3c214Sitojun #endif /* INET6 */
91274d3c214Sitojun 		default:
913493e35e3Sknakahara 			gif_putref_variant(var, &psref);
914493e35e3Sknakahara 			curlwp_bindx(bound);
91574d3c214Sitojun 			error = EADDRNOTAVAIL;
91674d3c214Sitojun 			goto bad;
91774d3c214Sitojun 		}
918493e35e3Sknakahara 		if (src->sa_len > size) {
919493e35e3Sknakahara 			gif_putref_variant(var, &psref);
920493e35e3Sknakahara 			curlwp_bindx(bound);
92108af3d2fSitojun 			return EINVAL;
922493e35e3Sknakahara 		}
92372d41408Sdyoung 		memcpy(dst, src, src->sa_len);
924493e35e3Sknakahara 		gif_putref_variant(var, &psref);
925493e35e3Sknakahara 		curlwp_bindx(bound);
92674d3c214Sitojun 		break;
92774d3c214Sitojun 
92874d3c214Sitojun 	case SIOCGIFPDSTADDR:
92974d3c214Sitojun #ifdef INET6
93074d3c214Sitojun 	case SIOCGIFPDSTADDR_IN6:
93174d3c214Sitojun #endif /* INET6 */
932493e35e3Sknakahara 		bound = curlwp_bind();
933493e35e3Sknakahara 		var = gif_getref_variant(sc, &psref);
934493e35e3Sknakahara 		if (var->gv_pdst == NULL) {
935493e35e3Sknakahara 			gif_putref_variant(var, &psref);
936493e35e3Sknakahara 			curlwp_bindx(bound);
93774d3c214Sitojun 			error = EADDRNOTAVAIL;
93874d3c214Sitojun 			goto bad;
93974d3c214Sitojun 		}
940493e35e3Sknakahara 		src = var->gv_pdst;
94108af3d2fSitojun 		switch (cmd) {
94274d3c214Sitojun #ifdef INET
94308af3d2fSitojun 		case SIOCGIFPDSTADDR:
94474d3c214Sitojun 			dst = &ifr->ifr_addr;
94508af3d2fSitojun 			size = sizeof(ifr->ifr_addr);
94674d3c214Sitojun 			break;
94774d3c214Sitojun #endif /* INET */
94874d3c214Sitojun #ifdef INET6
94908af3d2fSitojun 		case SIOCGIFPDSTADDR_IN6:
95074d3c214Sitojun 			dst = (struct sockaddr *)
95174d3c214Sitojun 				&(((struct in6_ifreq *)data)->ifr_addr);
95208af3d2fSitojun 			size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
95374d3c214Sitojun 			break;
95474d3c214Sitojun #endif /* INET6 */
95574d3c214Sitojun 		default:
956493e35e3Sknakahara 			gif_putref_variant(var, &psref);
957493e35e3Sknakahara 			curlwp_bindx(bound);
95874d3c214Sitojun 			error = EADDRNOTAVAIL;
95974d3c214Sitojun 			goto bad;
96074d3c214Sitojun 		}
961493e35e3Sknakahara 		if (src->sa_len > size) {
962493e35e3Sknakahara 			gif_putref_variant(var, &psref);
963493e35e3Sknakahara 			curlwp_bindx(bound);
96408af3d2fSitojun 			return EINVAL;
965493e35e3Sknakahara 		}
96672d41408Sdyoung 		memcpy(dst, src, src->sa_len);
967493e35e3Sknakahara 		gif_putref_variant(var, &psref);
968493e35e3Sknakahara 		curlwp_bindx(bound);
96974d3c214Sitojun 		break;
97074d3c214Sitojun 
9711e48b7fbSitojun 	case SIOCGLIFPHYADDR:
972493e35e3Sknakahara 		bound = curlwp_bind();
973493e35e3Sknakahara 		var = gif_getref_variant(sc, &psref);
974493e35e3Sknakahara 		if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
975493e35e3Sknakahara 			gif_putref_variant(var, &psref);
976493e35e3Sknakahara 			curlwp_bindx(bound);
9771e48b7fbSitojun 			error = EADDRNOTAVAIL;
9781e48b7fbSitojun 			goto bad;
9791e48b7fbSitojun 		}
9801e48b7fbSitojun 
9811e48b7fbSitojun 		/* copy src */
982493e35e3Sknakahara 		src = var->gv_psrc;
9831e48b7fbSitojun 		dst = (struct sockaddr *)
9841e48b7fbSitojun 			&(((struct if_laddrreq *)data)->addr);
9851e48b7fbSitojun 		size = sizeof(((struct if_laddrreq *)data)->addr);
986493e35e3Sknakahara 		if (src->sa_len > size) {
987493e35e3Sknakahara 			gif_putref_variant(var, &psref);
988493e35e3Sknakahara 			curlwp_bindx(bound);
9891e48b7fbSitojun 			return EINVAL;
990493e35e3Sknakahara 		}
99172d41408Sdyoung 		memcpy(dst, src, src->sa_len);
9921e48b7fbSitojun 
9931e48b7fbSitojun 		/* copy dst */
994493e35e3Sknakahara 		src = var->gv_pdst;
9951e48b7fbSitojun 		dst = (struct sockaddr *)
9961e48b7fbSitojun 			&(((struct if_laddrreq *)data)->dstaddr);
9971e48b7fbSitojun 		size = sizeof(((struct if_laddrreq *)data)->dstaddr);
998493e35e3Sknakahara 		if (src->sa_len > size) {
999493e35e3Sknakahara 			gif_putref_variant(var, &psref);
1000493e35e3Sknakahara 			curlwp_bindx(bound);
10011e48b7fbSitojun 			return EINVAL;
1002493e35e3Sknakahara 		}
100372d41408Sdyoung 		memcpy(dst, src, src->sa_len);
1004493e35e3Sknakahara 		gif_putref_variant(var, &psref);
1005493e35e3Sknakahara 		curlwp_bindx(bound);
10061e48b7fbSitojun 		break;
10071e48b7fbSitojun 
100874d3c214Sitojun 	default:
1009de87fe67Sdyoung 		return ifioctl_common(ifp, cmd, data);
101074d3c214Sitojun 	}
101174d3c214Sitojun  bad:
101274d3c214Sitojun 	return error;
101374d3c214Sitojun }
1014b36ce94cSthorpej 
10155e4601c6Sknakahara static int
1016493e35e3Sknakahara gif_encap_attach(struct gif_variant *var)
10175e4601c6Sknakahara {
10185e4601c6Sknakahara 	int error;
10195e4601c6Sknakahara 
1020493e35e3Sknakahara 	if (var == NULL || var->gv_psrc == NULL)
10215e4601c6Sknakahara 		return EINVAL;
10225e4601c6Sknakahara 
1023493e35e3Sknakahara 	switch (var->gv_psrc->sa_family) {
10245e4601c6Sknakahara #ifdef INET
10255e4601c6Sknakahara 	case AF_INET:
1026493e35e3Sknakahara 		error = in_gif_attach(var);
10275e4601c6Sknakahara 		break;
10285e4601c6Sknakahara #endif
10295e4601c6Sknakahara #ifdef INET6
10305e4601c6Sknakahara 	case AF_INET6:
1031493e35e3Sknakahara 		error = in6_gif_attach(var);
10325e4601c6Sknakahara 		break;
10335e4601c6Sknakahara #endif
10345e4601c6Sknakahara 	default:
10355e4601c6Sknakahara 		error = EINVAL;
10365e4601c6Sknakahara 		break;
10375e4601c6Sknakahara 	}
10385e4601c6Sknakahara 
10395e4601c6Sknakahara 	return error;
10405e4601c6Sknakahara }
10415e4601c6Sknakahara 
10425e4601c6Sknakahara static int
1043493e35e3Sknakahara gif_encap_detach(struct gif_variant *var)
10445e4601c6Sknakahara {
10455e4601c6Sknakahara 	int error;
10465e4601c6Sknakahara 
1047493e35e3Sknakahara 	if (var == NULL || var->gv_psrc == NULL)
10485e4601c6Sknakahara 		return EINVAL;
10495e4601c6Sknakahara 
1050493e35e3Sknakahara 	switch (var->gv_psrc->sa_family) {
10515e4601c6Sknakahara #ifdef INET
10525e4601c6Sknakahara 	case AF_INET:
1053493e35e3Sknakahara 		error = in_gif_detach(var);
10545e4601c6Sknakahara 		break;
10555e4601c6Sknakahara #endif
10565e4601c6Sknakahara #ifdef INET6
10575e4601c6Sknakahara 	case AF_INET6:
1058493e35e3Sknakahara 		error = in6_gif_detach(var);
10595e4601c6Sknakahara 		break;
10605e4601c6Sknakahara #endif
10615e4601c6Sknakahara 	default:
10625e4601c6Sknakahara 		error = EINVAL;
10635e4601c6Sknakahara 		break;
10645e4601c6Sknakahara 	}
10655e4601c6Sknakahara 
10665e4601c6Sknakahara 	return error;
10675e4601c6Sknakahara }
10685e4601c6Sknakahara 
1069d60d8acaSknakahara static int
107063eac52bSthorpej gif_set_tunnel(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst)
1071b36ce94cSthorpej {
1072de87fe67Sdyoung 	struct gif_softc *sc = ifp->if_softc;
1073cad488d0Sitojun 	struct gif_softc *sc2;
1074493e35e3Sknakahara 	struct gif_variant *ovar, *nvar;
1075306b5425Sdyoung 	struct sockaddr *osrc, *odst;
1076118f179fSknakahara 	struct sockaddr *nsrc, *ndst;
1077cad488d0Sitojun 	int error;
10786284b358Sknakahara #ifndef GIF_MPSAFE
10796284b358Sknakahara 	int s;
1080cad488d0Sitojun 
1081cad488d0Sitojun 	s = splsoftnet();
10826284b358Sknakahara #endif
1083c544c867Sknakahara 	error = encap_lock_enter();
1084c544c867Sknakahara 	if (error) {
10856284b358Sknakahara #ifndef GIF_MPSAFE
1086c544c867Sknakahara 		splx(s);
10876284b358Sknakahara #endif
1088c544c867Sknakahara 		return error;
1089c544c867Sknakahara 	}
1090cad488d0Sitojun 
1091493e35e3Sknakahara 	nsrc = sockaddr_dup(src, M_WAITOK);
1092493e35e3Sknakahara 	ndst = sockaddr_dup(dst, M_WAITOK);
1093493e35e3Sknakahara 	nvar = kmem_alloc(sizeof(*nvar), KM_SLEEP);
1094493e35e3Sknakahara 
1095493e35e3Sknakahara 	mutex_enter(&sc->gif_lock);
1096493e35e3Sknakahara 
1097493e35e3Sknakahara 	ovar = sc->gif_var;
1098493e35e3Sknakahara 
1099493e35e3Sknakahara 	if ((ovar->gv_pdst && sockaddr_cmp(ovar->gv_pdst, dst) == 0) &&
1100493e35e3Sknakahara 	    (ovar->gv_psrc && sockaddr_cmp(ovar->gv_psrc, src) == 0)) {
1101493e35e3Sknakahara 		/* address and port pair not changed. */
1102493e35e3Sknakahara 		error = 0;
1103493e35e3Sknakahara 		goto out;
1104493e35e3Sknakahara 	}
1105493e35e3Sknakahara 
11061cc266b8Sknakahara 	mutex_enter(&gif_softcs.lock);
11071cc266b8Sknakahara 	LIST_FOREACH(sc2, &gif_softcs.list, gif_list) {
1108493e35e3Sknakahara 		struct gif_variant *var2;
1109493e35e3Sknakahara 		struct psref psref;
1110493e35e3Sknakahara 
1111cad488d0Sitojun 		if (sc2 == sc)
1112cad488d0Sitojun 			continue;
1113ce1daba5Sknakahara 		var2 = gif_getref_variant(sc2, &psref);
1114493e35e3Sknakahara 		if (!var2->gv_pdst || !var2->gv_psrc) {
1115493e35e3Sknakahara 			gif_putref_variant(var2, &psref);
1116cad488d0Sitojun 			continue;
1117493e35e3Sknakahara 		}
1118cad488d0Sitojun 		/* can't configure same pair of address onto two gifs */
1119493e35e3Sknakahara 		if (sockaddr_cmp(var2->gv_pdst, dst) == 0 &&
1120493e35e3Sknakahara 		    sockaddr_cmp(var2->gv_psrc, src) == 0) {
11217ad35d74Smsaitoh 			/* continue to use the old configuration. */
1122493e35e3Sknakahara 			gif_putref_variant(var2, &psref);
11231cc266b8Sknakahara 			mutex_exit(&gif_softcs.lock);
11246284b358Sknakahara 			error =  EADDRNOTAVAIL;
11256284b358Sknakahara 			goto out;
1126cad488d0Sitojun 		}
1127493e35e3Sknakahara 		gif_putref_variant(var2, &psref);
1128cad488d0Sitojun 		/* XXX both end must be valid? (I mean, not 0.0.0.0) */
1129cad488d0Sitojun 	}
11301cc266b8Sknakahara 	mutex_exit(&gif_softcs.lock);
1131cad488d0Sitojun 
1132493e35e3Sknakahara 	osrc = ovar->gv_psrc;
1133493e35e3Sknakahara 	odst = ovar->gv_pdst;
1134118f179fSknakahara 
1135493e35e3Sknakahara 	*nvar = *ovar;
1136493e35e3Sknakahara 	nvar->gv_psrc = nsrc;
1137493e35e3Sknakahara 	nvar->gv_pdst = ndst;
1138493e35e3Sknakahara 	nvar->gv_encap_cookie4 = NULL;
1139493e35e3Sknakahara 	nvar->gv_encap_cookie6 = NULL;
1140493e35e3Sknakahara 	error = gif_encap_attach(nvar);
1141493e35e3Sknakahara 	if (error)
1142493e35e3Sknakahara 		goto out;
1143493e35e3Sknakahara 	psref_target_init(&nvar->gv_psref, gv_psref_class);
1144493e35e3Sknakahara 	gif_update_variant(sc, nvar);
1145d81cd78eSknakahara 
1146493e35e3Sknakahara 	mutex_exit(&sc->gif_lock);
1147cad488d0Sitojun 
1148493e35e3Sknakahara 	(void)gif_encap_detach(ovar);
1149493e35e3Sknakahara 	encap_lock_exit();
1150cad488d0Sitojun 
1151cad488d0Sitojun 	if (osrc)
1152306b5425Sdyoung 		sockaddr_free(osrc);
1153cad488d0Sitojun 	if (odst)
1154306b5425Sdyoung 		sockaddr_free(odst);
1155493e35e3Sknakahara 	kmem_free(ovar, sizeof(*ovar));
1156cad488d0Sitojun 
1157493e35e3Sknakahara #ifndef GIF_MPSAFE
1158493e35e3Sknakahara 	splx(s);
1159493e35e3Sknakahara #endif
1160493e35e3Sknakahara 	return 0;
1161fd06f200Sknakahara 
11626284b358Sknakahara  out:
1163*ae50415aSskrll 	mutex_exit(&sc->gif_lock);
1164*ae50415aSskrll 	encap_lock_exit();
1165*ae50415aSskrll 
1166493e35e3Sknakahara 	sockaddr_free(nsrc);
1167493e35e3Sknakahara 	sockaddr_free(ndst);
1168493e35e3Sknakahara 	kmem_free(nvar, sizeof(*nvar));
1169493e35e3Sknakahara 
11706284b358Sknakahara #ifndef GIF_MPSAFE
1171cad488d0Sitojun 	splx(s);
11726284b358Sknakahara #endif
1173cad488d0Sitojun 	return error;
1174cad488d0Sitojun }
1175cad488d0Sitojun 
1176d60d8acaSknakahara static void
117763eac52bSthorpej gif_delete_tunnel(struct ifnet *ifp)
1178cad488d0Sitojun {
1179de87fe67Sdyoung 	struct gif_softc *sc = ifp->if_softc;
1180493e35e3Sknakahara 	struct gif_variant *ovar, *nvar;
1181493e35e3Sknakahara 	struct sockaddr *osrc, *odst;
1182c544c867Sknakahara 	int error;
11836284b358Sknakahara #ifndef GIF_MPSAFE
11846284b358Sknakahara 	int s;
1185b36ce94cSthorpej 
1186b36ce94cSthorpej 	s = splsoftnet();
11876284b358Sknakahara #endif
1188c544c867Sknakahara 	error = encap_lock_enter();
1189c544c867Sknakahara 	if (error) {
11906284b358Sknakahara #ifndef GIF_MPSAFE
1191c544c867Sknakahara 		splx(s);
11926284b358Sknakahara #endif
1193c544c867Sknakahara 		return;
1194c544c867Sknakahara 	}
1195b36ce94cSthorpej 
1196493e35e3Sknakahara 	nvar = kmem_alloc(sizeof(*nvar), KM_SLEEP);
1197b36ce94cSthorpej 
1198493e35e3Sknakahara 	mutex_enter(&sc->gif_lock);
1199d81cd78eSknakahara 
1200493e35e3Sknakahara 	ovar = sc->gif_var;
1201493e35e3Sknakahara 	osrc = ovar->gv_psrc;
1202493e35e3Sknakahara 	odst = ovar->gv_pdst;
1203493e35e3Sknakahara 	if (osrc == NULL || odst == NULL) {
1204493e35e3Sknakahara 		/* address pair not changed. */
1205493e35e3Sknakahara 		mutex_exit(&sc->gif_lock);
1206b71542e5Sknakahara 		encap_lock_exit();
1207493e35e3Sknakahara 		kmem_free(nvar, sizeof(*nvar));
12083a2cefcbSmaxv #ifndef GIF_MPSAFE
12093a2cefcbSmaxv 		splx(s);
12103a2cefcbSmaxv #endif
1211493e35e3Sknakahara 		return;
1212493e35e3Sknakahara 	}
1213493e35e3Sknakahara 
1214493e35e3Sknakahara 	*nvar = *ovar;
1215493e35e3Sknakahara 	nvar->gv_psrc = NULL;
1216493e35e3Sknakahara 	nvar->gv_pdst = NULL;
1217493e35e3Sknakahara 	nvar->gv_encap_cookie4 = NULL;
1218493e35e3Sknakahara 	nvar->gv_encap_cookie6 = NULL;
1219493e35e3Sknakahara 	nvar->gv_output = NULL;
1220493e35e3Sknakahara 	psref_target_init(&nvar->gv_psref, gv_psref_class);
1221493e35e3Sknakahara 	gif_update_variant(sc, nvar);
1222493e35e3Sknakahara 
1223493e35e3Sknakahara 	mutex_exit(&sc->gif_lock);
1224493e35e3Sknakahara 
1225493e35e3Sknakahara 	gif_encap_detach(ovar);
1226493e35e3Sknakahara 	encap_lock_exit();
1227493e35e3Sknakahara 
1228493e35e3Sknakahara 	sockaddr_free(osrc);
1229493e35e3Sknakahara 	sockaddr_free(odst);
1230493e35e3Sknakahara 	kmem_free(ovar, sizeof(*ovar));
1231493e35e3Sknakahara 
12326284b358Sknakahara #ifndef GIF_MPSAFE
1233b36ce94cSthorpej 	splx(s);
12346284b358Sknakahara #endif
1235b36ce94cSthorpej }
12361d8e08d4Schristos 
12371d8e08d4Schristos /*
1238493e35e3Sknakahara  * gif_variant update API.
1239493e35e3Sknakahara  *
1240493e35e3Sknakahara  * Assumption:
1241493e35e3Sknakahara  * reader side dereferences sc->gif_var in reader critical section only,
1242493e35e3Sknakahara  * that is, all of reader sides do not reader the sc->gif_var after
1243493e35e3Sknakahara  * pserialize_perform().
1244493e35e3Sknakahara  */
1245493e35e3Sknakahara static void
1246493e35e3Sknakahara gif_update_variant(struct gif_softc *sc, struct gif_variant *nvar)
1247493e35e3Sknakahara {
1248493e35e3Sknakahara 	struct ifnet *ifp = &sc->gif_if;
1249493e35e3Sknakahara 	struct gif_variant *ovar = sc->gif_var;
1250493e35e3Sknakahara 
1251493e35e3Sknakahara 	KASSERT(mutex_owned(&sc->gif_lock));
1252493e35e3Sknakahara 
1253ffb7fabaSriastradh 	atomic_store_release(&sc->gif_var, nvar);
1254ebac3c72Sknakahara 	pserialize_perform(sc->gif_psz);
1255493e35e3Sknakahara 	psref_target_destroy(&ovar->gv_psref, gv_psref_class);
1256493e35e3Sknakahara 
1257493e35e3Sknakahara 	if (nvar->gv_psrc != NULL && nvar->gv_pdst != NULL)
1258493e35e3Sknakahara 		ifp->if_flags |= IFF_RUNNING;
1259493e35e3Sknakahara 	else
1260493e35e3Sknakahara 		ifp->if_flags &= ~IFF_RUNNING;
1261493e35e3Sknakahara }
1262493e35e3Sknakahara 
1263493e35e3Sknakahara /*
12641d8e08d4Schristos  * Module infrastructure
12651d8e08d4Schristos  */
12661d8e08d4Schristos #include "if_module.h"
12671d8e08d4Schristos 
12682dd28fa0Spgoyette IF_MODULE(MODULE_CLASS_DRIVER, gif, "ip_ecn")
1269