Module Name:    src
Committed By:   knakahara
Date:           Mon Nov 27 05:02:22 UTC 2017

Modified Files:
        src/sys/net: if_gif.c if_gif.h
        src/sys/netinet: in_gif.c in_gif.h
        src/sys/netinet6: in6_gif.c in6_gif.h

Log Message:
preserve gif(4) configs by psref(9) like vlan(4) and l2tp(4).

After Tx side does not use softint, gif(4) can use psref(9) for config
preservation like vlan(4) and l2tp(4).

update locking notes later.


To generate a diff of this commit:
cvs rdiff -u -r1.132 -r1.133 src/sys/net/if_gif.c
cvs rdiff -u -r1.27 -r1.28 src/sys/net/if_gif.h
cvs rdiff -u -r1.89 -r1.90 src/sys/netinet/in_gif.c
cvs rdiff -u -r1.17 -r1.18 src/sys/netinet/in_gif.h
cvs rdiff -u -r1.87 -r1.88 src/sys/netinet6/in6_gif.c
cvs rdiff -u -r1.16 -r1.17 src/sys/netinet6/in6_gif.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if_gif.c
diff -u src/sys/net/if_gif.c:1.132 src/sys/net/if_gif.c:1.133
--- src/sys/net/if_gif.c:1.132	Thu Nov 16 03:07:18 2017
+++ src/sys/net/if_gif.c	Mon Nov 27 05:02:22 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_gif.c,v 1.132 2017/11/16 03:07:18 ozaki-r Exp $	*/
+/*	$NetBSD: if_gif.c,v 1.133 2017/11/27 05:02:22 knakahara Exp $	*/
 /*	$KAME: if_gif.c,v 1.76 2001/08/20 02:01:02 kjc Exp $	*/
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.132 2017/11/16 03:07:18 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.133 2017/11/27 05:02:22 knakahara Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -57,6 +57,9 @@ __KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1
 #include <sys/xcall.h>
 #include <sys/device.h>
 #include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/pserialize.h>
+#include <sys/psref.h>
 
 #include <net/if.h>
 #include <net/if_types.h>
@@ -102,6 +105,9 @@ static struct {
 	kmutex_t lock;
 } gif_softcs __cacheline_aligned;
 
+pserialize_t gif_psz __read_mostly;
+struct psref_class *gv_psref_class __read_mostly;
+
 static void	gif_ro_init_pc(void *, void *, struct cpu_info *);
 static void	gif_ro_fini_pc(void *, void *, struct cpu_info *);
 
@@ -110,6 +116,7 @@ static int	gif_output(struct ifnet *, st
 			   const struct sockaddr *, const struct rtentry *);
 static void	gif_start(struct ifnet *);
 static int	gif_transmit(struct ifnet *, struct mbuf *);
+static int	gif_transmit_direct(struct gif_variant *, struct mbuf *);
 static int	gif_ioctl(struct ifnet *, u_long, void *);
 static int	gif_set_tunnel(struct ifnet *, struct sockaddr *,
 			       struct sockaddr *);
@@ -119,9 +126,10 @@ static int	gif_clone_create(struct if_cl
 static int	gif_clone_destroy(struct ifnet *);
 static int	gif_check_nesting(struct ifnet *, struct mbuf *);
 
-static int	gif_encap_attach(struct gif_softc *);
-static int	gif_encap_detach(struct gif_softc *);
-static void	gif_encap_pause(struct gif_softc *);
+static int	gif_encap_attach(struct gif_variant *);
+static int	gif_encap_detach(struct gif_variant *);
+
+static void	gif_update_variant(struct gif_softc *, struct gif_variant *);
 
 static struct if_clone gif_cloner =
     IF_CLONE_INITIALIZER("gif", gif_clone_create, gif_clone_destroy);
@@ -216,6 +224,9 @@ gifinit(void)
 	LIST_INIT(&gif_softcs.list);
 	if_clone_attach(&gif_cloner);
 
+	gif_psz = pserialize_create();
+	gv_psref_class = psref_class_create("gifvar", IPL_SOFTNET);
+
 	gif_sysctl_setup();
 }
 
@@ -231,6 +242,9 @@ gifdetach(void)
 	}
 
 	if (error == 0) {
+		psref_class_destroy(gv_psref_class);
+		pserialize_destroy(gif_psz);
+
 		if_clone_detach(&gif_cloner);
 		sysctl_teardown(&gif_sysctl);
 	}
@@ -242,6 +256,7 @@ static int
 gif_clone_create(struct if_clone *ifc, int unit)
 {
 	struct gif_softc *sc;
+	struct gif_variant *var;
 	int rv;
 
 	sc = kmem_zalloc(sizeof(struct gif_softc), KM_SLEEP);
@@ -254,6 +269,12 @@ gif_clone_create(struct if_clone *ifc, i
 		return rv;
 	}
 
+	var = kmem_zalloc(sizeof(*var), KM_SLEEP);
+	var->gv_softc = sc;
+	psref_target_init(&var->gv_psref, gv_psref_class);
+
+	sc->gif_var = var;
+	mutex_init(&sc->gif_lock, MUTEX_DEFAULT, IPL_NONE);
 	sc->gif_ro_percpu = percpu_alloc(sizeof(struct gif_ro));
 	percpu_foreach(sc->gif_ro_percpu, gif_ro_init_pc, NULL);
 
@@ -268,8 +289,6 @@ gifattach0(struct gif_softc *sc)
 {
 	int rv;
 
-	sc->encap_cookie4 = sc->encap_cookie6 = NULL;
-
 	sc->gif_if.if_addrlen = 0;
 	sc->gif_if.if_mtu    = GIF_MTU;
 	sc->gif_if.if_flags  = IFF_POINTOPOINT | IFF_MULTICAST;
@@ -325,6 +344,7 @@ static int
 gif_clone_destroy(struct ifnet *ifp)
 {
 	struct gif_softc *sc = (void *) ifp;
+	struct gif_variant *var;
 
 	LIST_REMOVE(sc, gif_list);
 
@@ -335,6 +355,10 @@ gif_clone_destroy(struct ifnet *ifp)
 	percpu_foreach(sc->gif_ro_percpu, gif_ro_fini_pc, NULL);
 	percpu_free(sc->gif_ro_percpu, sizeof(struct gif_ro));
 
+	mutex_destroy(&sc->gif_lock);
+
+	var = sc->gif_var;
+	kmem_free(var, sizeof(*var));
 	kmem_free(sc, sizeof(struct gif_softc));
 
 	return 0;
@@ -346,6 +370,9 @@ gif_encapcheck(struct mbuf *m, int off, 
 {
 	struct ip ip;
 	struct gif_softc *sc;
+	struct gif_variant *var;
+	struct psref psref;
+	int ret = 0;
 
 	sc = arg;
 	if (sc == NULL)
@@ -355,9 +382,13 @@ gif_encapcheck(struct mbuf *m, int off, 
 	    != (IFF_UP|IFF_RUNNING))
 		return 0;
 
+	var = gif_getref_variant(sc, &psref);
+	if (var->gv_psrc == NULL || var->gv_pdst == NULL)
+		goto out;
+
 	/* no physical address */
-	if (!sc->gif_psrc || !sc->gif_pdst)
-		return 0;
+	if (!var->gv_psrc || !var->gv_pdst)
+		goto out;
 
 	switch (proto) {
 #ifdef INET
@@ -369,36 +400,42 @@ gif_encapcheck(struct mbuf *m, int off, 
 		break;
 #endif
 	default:
-		return 0;
+		goto out;
 	}
 
 	/* Bail on short packets */
 	KASSERT(m->m_flags & M_PKTHDR);
 	if (m->m_pkthdr.len < sizeof(ip))
-		return 0;
+		goto  out;
 
 	m_copydata(m, 0, sizeof(ip), &ip);
 
 	switch (ip.ip_v) {
 #ifdef INET
 	case 4:
-		if (sc->gif_psrc->sa_family != AF_INET ||
-		    sc->gif_pdst->sa_family != AF_INET)
-			return 0;
-		return gif_encapcheck4(m, off, proto, arg);
+		if (var->gv_psrc->sa_family != AF_INET ||
+		    var->gv_pdst->sa_family != AF_INET)
+			goto out;
+		ret = gif_encapcheck4(m, off, proto, var);
+		break;
 #endif
 #ifdef INET6
 	case 6:
 		if (m->m_pkthdr.len < sizeof(struct ip6_hdr))
-			return 0;
-		if (sc->gif_psrc->sa_family != AF_INET6 ||
-		    sc->gif_pdst->sa_family != AF_INET6)
-			return 0;
-		return gif_encapcheck6(m, off, proto, arg);
+			goto out;
+		if (var->gv_psrc->sa_family != AF_INET6 ||
+		    var->gv_pdst->sa_family != AF_INET6)
+			goto out;
+		ret = gif_encapcheck6(m, off, proto, var);
+		break;
 #endif
 	default:
-		return 0;
+		goto out;
 	}
+
+out:
+	gif_putref_variant(var, &psref);
+	return ret;
 }
 #endif
 
@@ -444,6 +481,8 @@ gif_output(struct ifnet *ifp, struct mbu
     const struct rtentry *rt)
 {
 	struct gif_softc *sc = ifp->if_softc;
+	struct gif_variant *var = NULL;
+	struct psref psref;
 	int error = 0;
 
 	IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family);
@@ -453,16 +492,22 @@ gif_output(struct ifnet *ifp, struct mbu
 		goto end;
 	}
 
-	m->m_flags &= ~(M_BCAST|M_MCAST);
-	if (((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) ||
-	    sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
+	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
 		m_freem(m);
 		error = ENETDOWN;
 		goto end;
 	}
 
+	var = gif_getref_variant(sc, &psref);
+	if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
+		m_freem(m);
+		error = ENETDOWN;
+		goto end;
+	}
 	/* XXX should we check if our outer source is legal? */
 
+	m->m_flags &= ~(M_BCAST|M_MCAST);
+
 	/* use DLT_NULL encapsulation here to pass inner af type */
 	M_PREPEND(m, sizeof(int), M_DONTWAIT);
 	if (!m) {
@@ -475,8 +520,10 @@ gif_output(struct ifnet *ifp, struct mbu
 	m->m_pkthdr.csum_flags = 0;
 	m->m_pkthdr.csum_data = 0;
 
-	error = if_transmit_lock(ifp, m);
-  end:
+	error = gif_transmit_direct(var, m);
+end:
+	if (var != NULL)
+		gif_putref_variant(var, &psref);
 	if (error)
 		ifp->if_oerrors++;
 	return error;
@@ -486,12 +533,17 @@ static void
 gif_start(struct ifnet *ifp)
 {
 	struct gif_softc *sc;
+	struct gif_variant *var;
 	struct mbuf *m;
+	struct psref psref;
 	int family;
 	int len;
 	int error;
 
 	sc = ifp->if_softc;
+	var = gif_getref_variant(sc, &psref);
+
+	KASSERT(var->gv_output != NULL);
 
 	/* output processing */
 	while (1) {
@@ -513,33 +565,7 @@ gif_start(struct ifnet *ifp)
 
 		len = m->m_pkthdr.len;
 
-		/* dispatch to output logic based on outer AF */
-		switch (sc->gif_psrc->sa_family) {
-#ifdef INET
-		case AF_INET:
-			/* XXX
-			 * To add mutex_enter(softnet_lock) or
-			 * KASSERT(mutex_owned(softnet_lock)) here, we shold
-			 * coordinate softnet_lock between in6_if_up() and
-			 * in6_purgeif().
-			 */
-			error = in_gif_output(ifp, family, m);
-			break;
-#endif
-#ifdef INET6
-		case AF_INET6:
-			/* XXX
-			 * the same as in_gif_output()
-			 */
-			error = in6_gif_output(ifp, family, m);
-			break;
-#endif
-		default:
-			m_freem(m);
-			error = ENETDOWN;
-			break;
-		}
-
+		error = var->gv_output(var, family, m);
 		if (error)
 			ifp->if_oerrors++;
 		else {
@@ -547,14 +573,16 @@ gif_start(struct ifnet *ifp)
 			ifp->if_obytes += len;
 		}
 	}
+
+	gif_putref_variant(var, &psref);
 }
 
 static int
 gif_transmit(struct ifnet *ifp, struct mbuf *m)
 {
 	struct gif_softc *sc;
-	int family;
-	int len;
+	struct gif_variant *var;
+	struct psref psref;
 	int error;
 
 	sc = ifp->if_softc;
@@ -563,6 +591,24 @@ gif_transmit(struct ifnet *ifp, struct m
 	if (m == NULL)
 		return EINVAL;
 
+	var = gif_getref_variant(sc, &psref);
+	error = gif_transmit_direct(var, m);
+	gif_putref_variant(var, &psref);
+
+	return error;
+}
+
+static int
+gif_transmit_direct(struct gif_variant *var, struct mbuf *m)
+{
+	struct ifnet *ifp = &var->gv_softc->gif_if;
+	int error;
+	int family;
+	int len;
+
+	KASSERT(gif_heldref_variant(var));
+	KASSERT(var->gv_output != NULL);
+
 	/* grab and chop off inner af type */
 	if (sizeof(int) > m->m_len) {
 		m = m_pullup(m, sizeof(int));
@@ -577,33 +623,7 @@ gif_transmit(struct ifnet *ifp, struct m
 
 	len = m->m_pkthdr.len;
 
-	/* dispatch to output logic based on outer AF */
-	switch (sc->gif_psrc->sa_family) {
-#ifdef INET
-	case AF_INET:
-		/* XXX
-		 * To add mutex_enter(softnet_lock) or
-		 * KASSERT(mutex_owned(softnet_lock)) here, we shold
-		 * coordinate softnet_lock between in6_if_up() and
-		 * in6_purgeif().
-		 */
-		error = in_gif_output(ifp, family, m);
-		break;
-#endif
-#ifdef INET6
-	case AF_INET6:
-		/* XXX
-		 * the same as in_gif_output()
-		 */
-		error = in6_gif_output(ifp, family, m);
-		break;
-#endif
-	default:
-		m_freem(m);
-		error = ENETDOWN;
-		break;
-	}
-
+	error = var->gv_output(var, family, m);
 	if (error)
 		ifp->if_oerrors++;
 	else {
@@ -673,8 +693,10 @@ gif_ioctl(struct ifnet *ifp, u_long cmd,
 	struct gif_softc *sc  = ifp->if_softc;
 	struct ifreq     *ifr = (struct ifreq*)data;
 	struct ifaddr    *ifa = (struct ifaddr*)data;
-	int error = 0, size;
+	int error = 0, size, bound;
 	struct sockaddr *dst, *src;
+	struct gif_variant *var;
+	struct psref psref;
 
 	switch (cmd) {
 	case SIOCINITIFADDR:
@@ -794,13 +816,20 @@ gif_ioctl(struct ifnet *ifp, u_long cmd,
 			/* checks done in the above */
 			break;
 		}
-
+		/*
+		 * calls gif_getref_variant() for other softcs to check
+		 * address pair duplicattion
+		 */
+		bound = curlwp_bind();
 		error = gif_set_tunnel(&sc->gif_if, src, dst);
+		curlwp_bindx(bound);
 		break;
 
 #ifdef SIOCDIFPHYADDR
 	case SIOCDIFPHYADDR:
+		bound = curlwp_bind();
 		gif_delete_tunnel(&sc->gif_if);
+		curlwp_bindx(bound);
 		break;
 #endif
 
@@ -808,11 +837,15 @@ gif_ioctl(struct ifnet *ifp, u_long cmd,
 #ifdef INET6
 	case SIOCGIFPSRCADDR_IN6:
 #endif /* INET6 */
-		if (sc->gif_psrc == NULL) {
+		bound = curlwp_bind();
+		var = gif_getref_variant(sc, &psref);
+		if (var->gv_psrc == NULL) {
+			gif_putref_variant(var, &psref);
+			curlwp_bindx(bound);
 			error = EADDRNOTAVAIL;
 			goto bad;
 		}
-		src = sc->gif_psrc;
+		src = var->gv_psrc;
 		switch (cmd) {
 #ifdef INET
 		case SIOCGIFPSRCADDR:
@@ -828,23 +861,34 @@ gif_ioctl(struct ifnet *ifp, u_long cmd,
 			break;
 #endif /* INET6 */
 		default:
+			gif_putref_variant(var, &psref);
+			curlwp_bindx(bound);
 			error = EADDRNOTAVAIL;
 			goto bad;
 		}
-		if (src->sa_len > size)
+		if (src->sa_len > size) {
+			gif_putref_variant(var, &psref);
+			curlwp_bindx(bound);
 			return EINVAL;
+		}
 		memcpy(dst, src, src->sa_len);
+		gif_putref_variant(var, &psref);
+		curlwp_bindx(bound);
 		break;
 
 	case SIOCGIFPDSTADDR:
 #ifdef INET6
 	case SIOCGIFPDSTADDR_IN6:
 #endif /* INET6 */
-		if (sc->gif_pdst == NULL) {
+		bound = curlwp_bind();
+		var = gif_getref_variant(sc, &psref);
+		if (var->gv_pdst == NULL) {
+			gif_putref_variant(var, &psref);
+			curlwp_bindx(bound);
 			error = EADDRNOTAVAIL;
 			goto bad;
 		}
-		src = sc->gif_pdst;
+		src = var->gv_pdst;
 		switch (cmd) {
 #ifdef INET
 		case SIOCGIFPDSTADDR:
@@ -860,37 +904,56 @@ gif_ioctl(struct ifnet *ifp, u_long cmd,
 			break;
 #endif /* INET6 */
 		default:
+			gif_putref_variant(var, &psref);
+			curlwp_bindx(bound);
 			error = EADDRNOTAVAIL;
 			goto bad;
 		}
-		if (src->sa_len > size)
+		if (src->sa_len > size) {
+			gif_putref_variant(var, &psref);
+			curlwp_bindx(bound);
 			return EINVAL;
+		}
 		memcpy(dst, src, src->sa_len);
+		gif_putref_variant(var, &psref);
+		curlwp_bindx(bound);
 		break;
 
 	case SIOCGLIFPHYADDR:
-		if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
+		bound = curlwp_bind();
+		var = gif_getref_variant(sc, &psref);
+		if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
+			gif_putref_variant(var, &psref);
+			curlwp_bindx(bound);
 			error = EADDRNOTAVAIL;
 			goto bad;
 		}
 
 		/* copy src */
-		src = sc->gif_psrc;
+		src = var->gv_psrc;
 		dst = (struct sockaddr *)
 			&(((struct if_laddrreq *)data)->addr);
 		size = sizeof(((struct if_laddrreq *)data)->addr);
-		if (src->sa_len > size)
+		if (src->sa_len > size) {
+			gif_putref_variant(var, &psref);
+			curlwp_bindx(bound);
 			return EINVAL;
+		}
 		memcpy(dst, src, src->sa_len);
 
 		/* copy dst */
-		src = sc->gif_pdst;
+		src = var->gv_pdst;
 		dst = (struct sockaddr *)
 			&(((struct if_laddrreq *)data)->dstaddr);
 		size = sizeof(((struct if_laddrreq *)data)->dstaddr);
-		if (src->sa_len > size)
+		if (src->sa_len > size) {
+			gif_putref_variant(var, &psref);
+			curlwp_bindx(bound);
 			return EINVAL;
+		}
 		memcpy(dst, src, src->sa_len);
+		gif_putref_variant(var, &psref);
+		curlwp_bindx(bound);
 		break;
 
 	default:
@@ -901,22 +964,22 @@ gif_ioctl(struct ifnet *ifp, u_long cmd,
 }
 
 static int
-gif_encap_attach(struct gif_softc *sc)
+gif_encap_attach(struct gif_variant *var)
 {
 	int error;
 
-	if (sc == NULL || sc->gif_psrc == NULL)
+	if (var == NULL || var->gv_psrc == NULL)
 		return EINVAL;
 
-	switch (sc->gif_psrc->sa_family) {
+	switch (var->gv_psrc->sa_family) {
 #ifdef INET
 	case AF_INET:
-		error = in_gif_attach(sc);
+		error = in_gif_attach(var);
 		break;
 #endif
 #ifdef INET6
 	case AF_INET6:
-		error = in6_gif_attach(sc);
+		error = in6_gif_attach(var);
 		break;
 #endif
 	default:
@@ -928,22 +991,22 @@ gif_encap_attach(struct gif_softc *sc)
 }
 
 static int
-gif_encap_detach(struct gif_softc *sc)
+gif_encap_detach(struct gif_variant *var)
 {
 	int error;
 
-	if (sc == NULL || sc->gif_psrc == NULL)
+	if (var == NULL || var->gv_psrc == NULL)
 		return EINVAL;
 
-	switch (sc->gif_psrc->sa_family) {
+	switch (var->gv_psrc->sa_family) {
 #ifdef INET
 	case AF_INET:
-		error = in_gif_detach(sc);
+		error = in_gif_detach(var);
 		break;
 #endif
 #ifdef INET6
 	case AF_INET6:
-		error = in6_gif_detach(sc);
+		error = in6_gif_detach(var);
 		break;
 #endif
 	default:
@@ -954,50 +1017,12 @@ gif_encap_detach(struct gif_softc *sc)
 	return error;
 }
 
-static void
-gif_encap_pause(struct gif_softc *sc)
-{
-	struct ifnet *ifp;
-	uint64_t where;
-
-	if (sc == NULL || sc->gif_psrc == NULL)
-		return;
-
-	ifp = &sc->gif_if;
-	if ((ifp->if_flags & IFF_RUNNING) == 0)
-		return;
-
-	switch (sc->gif_psrc->sa_family) {
-#ifdef INET
-	case AF_INET:
-		(void)in_gif_pause(sc);
-		break;
-#endif
-#ifdef INET6
-	case AF_INET6:
-		(void)in6_gif_pause(sc);
-		break;
-#endif
-	}
-
-	ifp->if_flags &= ~IFF_RUNNING;
-	/* membar_sync() is done in xc_broadcast(). */
-
-	/*
-	 * Wait for softint_execute()(ipintr() or ip6intr())
-	 * completion done by other CPUs which already run over if_flags
-	 * check in in_gif_input() or in6_gif_input().
-	 * Furthermore, wait for gif_output() completion too.
-	 */
-	where = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL);
-	xc_wait(where);
-}
-
 static int
 gif_set_tunnel(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst)
 {
 	struct gif_softc *sc = ifp->if_softc;
 	struct gif_softc *sc2;
+	struct gif_variant *ovar, *nvar;
 	struct sockaddr *osrc, *odst;
 	struct sockaddr *nsrc, *ndst;
 	int error;
@@ -1014,78 +1039,84 @@ gif_set_tunnel(struct ifnet *ifp, struct
 		return error;
 	}
 
+	nsrc = sockaddr_dup(src, M_WAITOK);
+	ndst = sockaddr_dup(dst, M_WAITOK);
+	nvar = kmem_alloc(sizeof(*nvar), KM_SLEEP);
+
+	mutex_enter(&sc->gif_lock);
+
+	ovar = sc->gif_var;
+
+	if ((ovar->gv_pdst && sockaddr_cmp(ovar->gv_pdst, dst) == 0) &&
+	    (ovar->gv_psrc && sockaddr_cmp(ovar->gv_psrc, src) == 0)) {
+		/* address and port pair not changed. */
+		error = 0;
+		goto out;
+	}
+
 	mutex_enter(&gif_softcs.lock);
 	LIST_FOREACH(sc2, &gif_softcs.list, gif_list) {
+		struct gif_variant *var2;
+		struct psref psref;
+
 		if (sc2 == sc)
 			continue;
-		if (!sc2->gif_pdst || !sc2->gif_psrc)
+		var2 = gif_getref_variant(sc, &psref);
+		if (!var2->gv_pdst || !var2->gv_psrc) {
+			gif_putref_variant(var2, &psref);
 			continue;
+		}
 		/* can't configure same pair of address onto two gifs */
-		if (sockaddr_cmp(sc2->gif_pdst, dst) == 0 &&
-		    sockaddr_cmp(sc2->gif_psrc, src) == 0) {
+		if (sockaddr_cmp(var2->gv_pdst, dst) == 0 &&
+		    sockaddr_cmp(var2->gv_psrc, src) == 0) {
 			/* continue to use the old configureation. */
+			gif_putref_variant(var2, &psref);
 			mutex_exit(&gif_softcs.lock);
 			error =  EADDRNOTAVAIL;
 			goto out;
 		}
-
+		gif_putref_variant(var2, &psref);
 		/* XXX both end must be valid? (I mean, not 0.0.0.0) */
 	}
 	mutex_exit(&gif_softcs.lock);
 
-	nsrc = sockaddr_dup(src, M_WAITOK);
-	ndst = sockaddr_dup(dst, M_WAITOK);
+	osrc = ovar->gv_psrc;
+	odst = ovar->gv_pdst;
 
-	gif_encap_pause(sc);
+	*nvar = *ovar;
+	nvar->gv_psrc = nsrc;
+	nvar->gv_pdst = ndst;
+	nvar->gv_encap_cookie4 = NULL;
+	nvar->gv_encap_cookie6 = NULL;
+	error = gif_encap_attach(nvar);
+	if (error)
+		goto out;
+	psref_target_init(&nvar->gv_psref, gv_psref_class);
+	membar_producer();
+	gif_update_variant(sc, nvar);
 
-	/* Firstly, clear old configurations. */
-	/* XXX we can detach from both, but be polite just in case */
-	if (sc->gif_psrc)
-		(void)gif_encap_detach(sc);
+	mutex_exit(&sc->gif_lock);
 
-	/*
-	 * Secondly, try to set new configurations.
-	 */
-	osrc = sc->gif_psrc;
-	odst = sc->gif_pdst;
-	sc->gif_psrc = nsrc;
-	sc->gif_pdst = ndst;
-	error = gif_encap_attach(sc);
-	if (error && osrc != NULL && odst != NULL) {
-		/*
-		 * Thirdly, when error occured, rollback to old configurations,
-		 * if last setting is valid.
-		 */
-		sc->gif_psrc = osrc;
-		sc->gif_pdst = odst;
-		osrc = nsrc; /* to free */
-		odst = ndst; /* to free */
-		error = gif_encap_attach(sc);
-	}
-	if (error) {
-		/*
-		 * Fourthly, even rollback failed or last setting is not valid,
-		 * clear configurations.
-		 */
-		osrc = sc->gif_psrc; /* to free */
-		odst = sc->gif_pdst; /* to free */
-		sc->gif_psrc = NULL;
-		sc->gif_pdst = NULL;
-		sockaddr_free(nsrc);
-		sockaddr_free(ndst);
-	}
+	(void)gif_encap_detach(ovar);
+	encap_lock_exit();
 
 	if (osrc)
 		sockaddr_free(osrc);
 	if (odst)
 		sockaddr_free(odst);
+	kmem_free(ovar, sizeof(*ovar));
 
-	if (sc->gif_psrc && sc->gif_pdst)
-		ifp->if_flags |= IFF_RUNNING;
-	else
-		ifp->if_flags &= ~IFF_RUNNING;
+#ifndef GIF_MPSAFE
+	splx(s);
+#endif
+	return 0;
 
  out:
+	sockaddr_free(nsrc);
+	sockaddr_free(ndst);
+	kmem_free(nvar, sizeof(*nvar));
+
+	mutex_exit(&sc->gif_lock);
 	encap_lock_exit();
 #ifndef GIF_MPSAFE
 	splx(s);
@@ -1097,6 +1128,8 @@ static void
 gif_delete_tunnel(struct ifnet *ifp)
 {
 	struct gif_softc *sc = ifp->if_softc;
+	struct gif_variant *ovar, *nvar;
+	struct sockaddr *osrc, *odst;
 	int error;
 #ifndef GIF_MPSAFE
 	int s;
@@ -1111,35 +1144,72 @@ gif_delete_tunnel(struct ifnet *ifp)
 		return;
 	}
 
-	gif_encap_pause(sc);
-	if (sc->gif_psrc) {
-		sockaddr_free(sc->gif_psrc);
-		sc->gif_psrc = NULL;
-	}
-	if (sc->gif_pdst) {
-		sockaddr_free(sc->gif_pdst);
-		sc->gif_pdst = NULL;
+	nvar = kmem_alloc(sizeof(*nvar), KM_SLEEP);
+
+	mutex_enter(&sc->gif_lock);
+
+	ovar = sc->gif_var;
+	osrc = ovar->gv_psrc;
+	odst = ovar->gv_pdst;
+	if (osrc == NULL || odst == NULL) {
+		/* address pair not changed. */
+		mutex_exit(&sc->gif_lock);
+		encap_lock_exit();
+		kmem_free(nvar, sizeof(*nvar));
+		return;
 	}
-	/* it is safe to detach from both */
-#ifdef INET
-	(void)in_gif_detach(sc);
-#endif
-#ifdef INET6
-	(void)in6_gif_detach(sc);
-#endif
 
-	if (sc->gif_psrc && sc->gif_pdst)
-		ifp->if_flags |= IFF_RUNNING;
-	else
-		ifp->if_flags &= ~IFF_RUNNING;
+	*nvar = *ovar;
+	nvar->gv_psrc = NULL;
+	nvar->gv_pdst = NULL;
+	nvar->gv_encap_cookie4 = NULL;
+	nvar->gv_encap_cookie6 = NULL;
+	nvar->gv_output = NULL;
+	psref_target_init(&nvar->gv_psref, gv_psref_class);
+	membar_producer();
+	gif_update_variant(sc, nvar);
 
+	mutex_exit(&sc->gif_lock);
+
+	gif_encap_detach(ovar);
 	encap_lock_exit();
+
+	sockaddr_free(osrc);
+	sockaddr_free(odst);
+	kmem_free(ovar, sizeof(*ovar));
+
 #ifndef GIF_MPSAFE
 	splx(s);
 #endif
 }
 
 /*
+ * gif_variant update API.
+ *
+ * Assumption:
+ * reader side dereferences sc->gif_var in reader critical section only,
+ * that is, all of reader sides do not reader the sc->gif_var after
+ * pserialize_perform().
+ */
+static void
+gif_update_variant(struct gif_softc *sc, struct gif_variant *nvar)
+{
+	struct ifnet *ifp = &sc->gif_if;
+	struct gif_variant *ovar = sc->gif_var;
+
+	KASSERT(mutex_owned(&sc->gif_lock));
+
+	sc->gif_var = nvar;
+	pserialize_perform(gif_psz);
+	psref_target_destroy(&ovar->gv_psref, gv_psref_class);
+
+	if (nvar->gv_psrc != NULL && nvar->gv_pdst != NULL)
+		ifp->if_flags |= IFF_RUNNING;
+	else
+		ifp->if_flags &= ~IFF_RUNNING;
+}
+
+/*
  * Module infrastructure
  */
 #include "if_module.h"

Index: src/sys/net/if_gif.h
diff -u src/sys/net/if_gif.h:1.27 src/sys/net/if_gif.h:1.28
--- src/sys/net/if_gif.h:1.27	Thu Sep 21 09:48:15 2017
+++ src/sys/net/if_gif.h	Mon Nov 27 05:02:22 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_gif.h,v 1.27 2017/09/21 09:48:15 knakahara Exp $	*/
+/*	$NetBSD: if_gif.h,v 1.28 2017/11/27 05:02:22 knakahara Exp $	*/
 /*	$KAME: if_gif.h,v 1.23 2001/07/27 09:21:42 itojun Exp $	*/
 
 /*
@@ -39,6 +39,9 @@
 
 #include <sys/queue.h>
 #include <sys/percpu.h>
+#ifdef _KERNEL
+#include <sys/psref.h>
+#endif
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -47,6 +50,8 @@
 #include <netinet/in.h>
 /* xxx sigh, why route have struct route instead of pointer? */
 
+extern struct psref_class *gv_psref_class;
+
 struct encaptab;
 
 struct gif_ro {
@@ -54,14 +59,26 @@ struct gif_ro {
 	kmutex_t gr_lock;
 };
 
+struct gif_variant {
+	struct gif_softc *gv_softc;
+	struct sockaddr	*gv_psrc; /* Physical src addr */
+	struct sockaddr	*gv_pdst; /* Physical dst addr */
+	const struct encaptab *gv_encap_cookie4;
+	const struct encaptab *gv_encap_cookie6;
+	int (*gv_output)(struct gif_variant *, int, struct mbuf *);
+
+	struct psref_target gv_psref;
+};
+
 struct gif_softc {
-	struct ifnet	gif_if;	   /* common area - must be at the top */
-	struct sockaddr	*gif_psrc; /* Physical src addr */
-	struct sockaddr	*gif_pdst; /* Physical dst addr */
-	percpu_t *gif_ro_percpu;   /* struct gif_ro */
-	int		gif_flags;
-	const struct encaptab *encap_cookie4;
-	const struct encaptab *encap_cookie6;
+	struct ifnet	gif_if;		/* common area - must be at the top */
+	percpu_t *gif_ro_percpu;	/* struct gif_ro */
+	struct gif_variant *gif_var;	/*
+					 * reader must use gif_getref_variant()
+					 * instead of direct dereference.
+					 */
+	kmutex_t gif_lock;		/* writer lock for gif_var */
+
 	LIST_ENTRY(gif_softc) gif_list;	/* list of all gifs */
 };
 #define GIF_ROUTE_TTL	10
@@ -70,6 +87,45 @@ struct gif_softc {
 #define	GIF_MTU_MIN	(1280)	/* Minimum MTU */
 #define	GIF_MTU_MAX	(8192)	/* Maximum MTU */
 
+/*
+ * Get gif_variant from gif_softc.
+ *
+ * Never return NULL by contract.
+ * gif_variant itself is protected not to be freed by gv_psref.
+ * Once a reader dereference sc->sc_var by this API, the reader must not
+ * re-dereference form sc->sc_var.
+ */
+static inline struct gif_variant *
+gif_getref_variant(struct gif_softc *sc, struct psref *psref)
+{
+	struct gif_variant *var;
+	int s;
+
+	s = pserialize_read_enter();
+	var = sc->gif_var;
+	KASSERT(var != NULL);
+	membar_datadep_consumer();
+	psref_acquire(psref, &var->gv_psref, gv_psref_class);
+	pserialize_read_exit(s);
+
+	return var;
+}
+
+static inline void
+gif_putref_variant(struct gif_variant *var, struct psref *psref)
+{
+
+	KASSERT(var != NULL);
+	psref_release(psref, &var->gv_psref, gv_psref_class);
+}
+
+static inline bool
+gif_heldref_variant(struct gif_variant *var)
+{
+
+	return psref_held(&var->gv_psref, gv_psref_class);
+}
+
 /* Prototypes */
 void	gif_input(struct mbuf *, int, struct ifnet *);
 

Index: src/sys/netinet/in_gif.c
diff -u src/sys/netinet/in_gif.c:1.89 src/sys/netinet/in_gif.c:1.90
--- src/sys/netinet/in_gif.c:1.89	Wed Nov 15 10:42:41 2017
+++ src/sys/netinet/in_gif.c	Mon Nov 27 05:02:22 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in_gif.c,v 1.89 2017/11/15 10:42:41 knakahara Exp $	*/
+/*	$NetBSD: in_gif.c,v 1.90 2017/11/27 05:02:22 knakahara Exp $	*/
 /*	$KAME: in_gif.c,v 1.66 2001/07/29 04:46:09 itojun Exp $	*/
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1.89 2017/11/15 10:42:41 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1.90 2017/11/27 05:02:22 knakahara Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -67,7 +67,7 @@ __KERNEL_RCSID(0, "$NetBSD: in_gif.c,v 1
 
 #include <net/net_osdep.h>
 
-static int gif_validate4(const struct ip *, struct gif_softc *,
+static int gif_validate4(const struct ip *, struct gif_variant *,
 	struct ifnet *);
 
 int ip_gif_ttl = GIF_TTL;
@@ -79,19 +79,26 @@ static const struct encapsw in_gif_encap
 	}
 };
 
-int
-in_gif_output(struct ifnet *ifp, int family, struct mbuf *m)
+static int
+in_gif_output(struct gif_variant *var, int family, struct mbuf *m)
 {
 	struct rtentry *rt;
 	struct route *ro;
 	struct gif_ro *gro;
-	struct gif_softc *sc = ifp->if_softc;
-	struct sockaddr_in *sin_src = satosin(sc->gif_psrc);
-	struct sockaddr_in *sin_dst = satosin(sc->gif_pdst);
+	struct gif_softc *sc;
+	struct sockaddr_in *sin_src;
+	struct sockaddr_in *sin_dst;
+	struct ifnet *ifp;
 	struct ip iphdr;	/* capsule IP header, host byte ordered */
 	int proto, error;
 	u_int8_t tos;
 
+	KASSERT(gif_heldref_variant(var));
+
+	sin_src = satosin(var->gv_psrc);
+	sin_dst = satosin(var->gv_pdst);
+	ifp = &var->gv_softc->gif_if;
+
 	if (sin_src == NULL || sin_dst == NULL ||
 	    sin_src->sin_family != AF_INET ||
 	    sin_dst->sin_family != AF_INET) {
@@ -167,10 +174,11 @@ in_gif_output(struct ifnet *ifp, int fam
 		return ENOBUFS;
 	bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip));
 
+	sc = var->gv_softc;
 	gro = percpu_getref(sc->gif_ro_percpu);
 	mutex_enter(&gro->gr_lock);
 	ro = &gro->gr_ro;
-	if ((rt = rtcache_lookup(ro, sc->gif_pdst)) == NULL) {
+	if ((rt = rtcache_lookup(ro, var->gv_pdst)) == NULL) {
 		mutex_exit(&gro->gr_lock);
 		percpu_putref(sc->gif_ro_percpu);
 		m_freem(m);
@@ -197,39 +205,45 @@ in_gif_output(struct ifnet *ifp, int fam
 void
 in_gif_input(struct mbuf *m, int off, int proto, void *eparg)
 {
-	struct ifnet *gifp = eparg;
+	struct gif_softc *sc = eparg;
+	struct ifnet *gifp = &sc->gif_if;
 	const struct ip *ip;
 	int af;
 	u_int8_t otos;
 
-	KASSERT(gifp != NULL);
+	KASSERT(sc != NULL);
 
 	ip = mtod(m, const struct ip *);
 
+	gifp = &sc->gif_if;
 	if ((gifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
 		m_freem(m);
 		ip_statinc(IP_STAT_NOGIF);
 		return;
 	}
 #ifndef GIF_ENCAPCHECK
-	struct gif_softc *sc = (struct gif_softc *)gifp->if_softc;
+	struct psref psref_var;
+	struct gif_variant *var = gif_getref_variant(sc, &psref_var);
 	/* other CPU do delete_tunnel */
-	if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
+	if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
+		gif_putref_variant(var, &psref_var);
 		m_freem(m);
 		ip_statinc(IP_STAT_NOGIF);
 		return;
 	}
 
 	struct ifnet *rcvif;
-	struct psref psref;
-	rcvif = m_get_rcvif_psref(m, &psref);
-	if (!gif_validate4(ip, sc, rcvif)) {
-		m_put_rcvif_psref(rcvif, &psref);
+	struct psref psref_rcvif;
+	rcvif = m_get_rcvif_psref(m, &psref_rcvif);
+	if (!gif_validate4(ip, var, rcvif)) {
+		m_put_rcvif_psref(rcvif, &psref_rcvif);
+		gif_putref_variant(var, &psref_var);
 		m_freem(m);
 		ip_statinc(IP_STAT_NOGIF);
 		return;
 	}
-	m_put_rcvif_psref(rcvif, &psref);
+	m_put_rcvif_psref(rcvif, &psref_rcvif);
+	gif_putref_variant(var, &psref_var);
 #endif
 	otos = ip->ip_tos;
 	m_adj(m, off);
@@ -286,14 +300,14 @@ in_gif_input(struct mbuf *m, int off, in
  * validate outer address.
  */
 static int
-gif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp)
+gif_validate4(const struct ip *ip, struct gif_variant *var, struct ifnet *ifp)
 {
 	struct sockaddr_in *src, *dst;
 	struct in_ifaddr *ia4;
 	int s;
 
-	src = satosin(sc->gif_psrc);
-	dst = satosin(sc->gif_pdst);
+	src = satosin(var->gv_psrc);
+	dst = satosin(var->gv_pdst);
 
 	/* check for address match */
 	if (src->sin_addr.s_addr != ip->ip_dst.s_addr ||
@@ -320,7 +334,7 @@ gif_validate4(const struct ip *ip, struc
 	pserialize_read_exit(s);
 
 	/* ingress filters on outer source */
-	if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
+	if ((var->gv_softc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
 		union {
 			struct sockaddr sa;
 			struct sockaddr_in sin;
@@ -332,7 +346,8 @@ gif_validate4(const struct ip *ip, struc
 		if (rt == NULL || rt->rt_ifp != ifp) {
 #if 0
 			log(LOG_WARNING, "%s: packet from 0x%x dropped "
-			    "due to ingress filter\n", if_name(&sc->gif_if),
+			    "due to ingress filter\n",
+			    if_name(&var->gv_softc->gif_if),
 			    (u_int32_t)ntohl(u.sin.sin_addr.s_addr));
 #endif
 			if (rt != NULL)
@@ -351,22 +366,19 @@ gif_validate4(const struct ip *ip, struc
  * matched the physical addr family.  see gif_encapcheck().
  */
 int
-gif_encapcheck4(struct mbuf *m, int off, int proto, void *arg)
+gif_encapcheck4(struct mbuf *m, int off, int proto, struct gif_variant *var)
 {
 	struct ip ip;
-	struct gif_softc *sc;
+
 	struct ifnet *ifp = NULL;
 	int r;
 	struct psref psref;
 
-	/* sanity check done in caller */
-	sc = arg;
-
 	m_copydata(m, 0, sizeof(ip), &ip);
 	if ((m->m_flags & M_PKTHDR) != 0)
 		ifp = m_get_rcvif_psref(m, &psref);
 
-	r = gif_validate4(&ip, sc, ifp);
+	r = gif_validate4(&ip, var, ifp);
 
 	m_put_rcvif_psref(ifp, &psref);
 	return r;
@@ -374,7 +386,7 @@ gif_encapcheck4(struct mbuf *m, int off,
 #endif
 
 int
-in_gif_attach(struct gif_softc *sc)
+in_gif_attach(struct gif_variant *var)
 {
 #ifndef GIF_ENCAPCHECK
 	struct sockaddr_in mask4;
@@ -383,40 +395,33 @@ in_gif_attach(struct gif_softc *sc)
 	mask4.sin_len = sizeof(struct sockaddr_in);
 	mask4.sin_addr.s_addr = ~0;
 
-	if (!sc->gif_psrc || !sc->gif_pdst)
+	if (!var->gv_psrc || !var->gv_pdst)
 		return EINVAL;
-	sc->encap_cookie4 = encap_attach(AF_INET, -1, sc->gif_psrc,
-	    (struct sockaddr *)&mask4, sc->gif_pdst, (struct sockaddr *)&mask4,
-	    &in_gif_encapsw, sc);
+	var->gv_encap_cookie4 = encap_attach(AF_INET, -1, var->gv_psrc,
+	    (struct sockaddr *)&mask4, var->gv_pdst, (struct sockaddr *)&mask4,
+	    &in_gif_encapsw, var->gv_softc);
 #else
-	sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck,
-	    &in_gif_encapsw, sc);
+	var->gv_encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck,
+	    &in_gif_encapsw, var->gv_softc);
 #endif
-	if (sc->encap_cookie4 == NULL)
+	if (var->gv_encap_cookie4 == NULL)
 		return EEXIST;
+
+	var->gv_output = in_gif_output;
 	return 0;
 }
 
 int
-in_gif_detach(struct gif_softc *sc)
+in_gif_detach(struct gif_variant *var)
 {
 	int error;
+	struct gif_softc *sc = var->gv_softc;
 
-	error = in_gif_pause(sc);
+	error = encap_detach(var->gv_encap_cookie4);
+	if (error == 0)
+		var->gv_encap_cookie4 = NULL;
 
 	percpu_foreach(sc->gif_ro_percpu, gif_rtcache_free_pc, NULL);
 
 	return error;
 }
-
-int
-in_gif_pause(struct gif_softc *sc)
-{
-	int error;
-
-	error = encap_detach(sc->encap_cookie4);
-	if (error == 0)
-		sc->encap_cookie4 = NULL;
-
-	return error;
-}

Index: src/sys/netinet/in_gif.h
diff -u src/sys/netinet/in_gif.h:1.17 src/sys/netinet/in_gif.h:1.18
--- src/sys/netinet/in_gif.h:1.17	Wed Nov 15 10:42:41 2017
+++ src/sys/netinet/in_gif.h	Mon Nov 27 05:02:22 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in_gif.h,v 1.17 2017/11/15 10:42:41 knakahara Exp $	*/
+/*	$NetBSD: in_gif.h,v 1.18 2017/11/27 05:02:22 knakahara Exp $	*/
 /*	$KAME: in_gif.h,v 1.6 2001/07/25 00:55:48 itojun Exp $	*/
 
 /*
@@ -37,14 +37,12 @@
 
 extern int ip_gif_ttl;
 
-struct gif_softc;
+struct gif_variant;
 void in_gif_input(struct mbuf *, int, int, void *);
-int in_gif_output(struct ifnet *, int, struct mbuf *);
 #ifdef GIF_ENCAPCHECK
-int gif_encapcheck4(struct mbuf *, int, int, void *);
+int gif_encapcheck4(struct mbuf *, int, int, struct gif_variant *);
 #endif
-int in_gif_attach(struct gif_softc *);
-int in_gif_detach(struct gif_softc *);
-int in_gif_pause(struct gif_softc *);
+int in_gif_attach(struct gif_variant *);
+int in_gif_detach(struct gif_variant *);
 
 #endif /* !_NETINET_IN_GIF_H_ */

Index: src/sys/netinet6/in6_gif.c
diff -u src/sys/netinet6/in6_gif.c:1.87 src/sys/netinet6/in6_gif.c:1.88
--- src/sys/netinet6/in6_gif.c:1.87	Wed Nov 15 10:42:41 2017
+++ src/sys/netinet6/in6_gif.c	Mon Nov 27 05:02:22 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6_gif.c,v 1.87 2017/11/15 10:42:41 knakahara Exp $	*/
+/*	$NetBSD: in6_gif.c,v 1.88 2017/11/27 05:02:22 knakahara Exp $	*/
 /*	$KAME: in6_gif.c,v 1.62 2001/07/29 04:27:25 itojun Exp $	*/
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6_gif.c,v 1.87 2017/11/15 10:42:41 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_gif.c,v 1.88 2017/11/27 05:02:22 knakahara Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -71,7 +71,7 @@ __KERNEL_RCSID(0, "$NetBSD: in6_gif.c,v 
 
 #include <net/net_osdep.h>
 
-static int gif_validate6(const struct ip6_hdr *, struct gif_softc *,
+static int gif_validate6(const struct ip6_hdr *, struct gif_variant *,
 	struct ifnet *);
 
 int	ip6_gif_hlim = GIF_HLIM;
@@ -82,19 +82,26 @@ static const struct encapsw in6_gif_enca
  * family - family of the packet to be encapsulate. 
  */
 
-int
-in6_gif_output(struct ifnet *ifp, int family, struct mbuf *m)
+static int
+in6_gif_output(struct gif_variant *var, int family, struct mbuf *m)
 {
 	struct rtentry *rt;
 	struct route *ro;
 	struct gif_ro *gro;
-	struct gif_softc *sc = ifp->if_softc;
-	struct sockaddr_in6 *sin6_src = satosin6(sc->gif_psrc);
-	struct sockaddr_in6 *sin6_dst = satosin6(sc->gif_pdst);
+	struct gif_softc *sc;
+	struct sockaddr_in6 *sin6_src;
+	struct sockaddr_in6 *sin6_dst;
+	struct ifnet *ifp;
 	struct ip6_hdr *ip6;
 	int proto, error;
 	u_int8_t itos, otos;
 
+	KASSERT(gif_heldref_variant(var));
+
+	sin6_src = satosin6(var->gv_psrc);
+	sin6_dst = satosin6(var->gv_pdst);
+	ifp = &var->gv_softc->gif_if;
+
 	if (sin6_src == NULL || sin6_dst == NULL ||
 	    sin6_src->sin6_family != AF_INET6 ||
 	    sin6_dst->sin6_family != AF_INET6) {
@@ -173,10 +180,11 @@ in6_gif_output(struct ifnet *ifp, int fa
 	ip6->ip6_flow &= ~ntohl(0xff00000);
 	ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
 
+	sc = ifp->if_softc;
 	gro = percpu_getref(sc->gif_ro_percpu);
 	mutex_enter(&gro->gr_lock);
 	ro = &gro->gr_ro;
-	rt = rtcache_lookup(ro, sc->gif_pdst);
+	rt = rtcache_lookup(ro, var->gv_pdst);
 	if (rt == NULL) {
 		mutex_exit(&gro->gr_lock);
 		percpu_putref(sc->gif_ro_percpu);
@@ -214,24 +222,28 @@ int
 in6_gif_input(struct mbuf **mp, int *offp, int proto, void *eparg)
 {
 	struct mbuf *m = *mp;
-	struct ifnet *gifp = eparg;
+	struct gif_softc *sc = eparg;
+	struct ifnet *gifp;
 	struct ip6_hdr *ip6;
 	int af = 0;
 	u_int32_t otos;
 
-	KASSERT(gifp != NULL);
+	KASSERT(sc != NULL);
 
 	ip6 = mtod(m, struct ip6_hdr *);
 
+	gifp = &sc->gif_if;
 	if ((gifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
 		m_freem(m);
 		IP6_STATINC(IP6_STAT_NOGIF);
 		return IPPROTO_DONE;
 	}
 #ifndef GIF_ENCAPCHECK
-	struct gif_softc *sc = (struct gif_softc *)gifp->if_softc;
+	struct psref psref_var;
+	struct gif_variant *var = gif_getref_variant(sc, &psref_var);
 	/* other CPU do delete_tunnel */
-	if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
+	if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
+		gif_putref_variant(var, &psref_var);
 		m_freem(m);
 		IP6_STATINC(IP6_STAT_NOGIF);
 		return IPPROTO_DONE;
@@ -239,13 +251,15 @@ in6_gif_input(struct mbuf **mp, int *off
 
 	struct psref psref;
 	struct ifnet *rcvif = m_get_rcvif_psref(m, &psref);
-	if (rcvif == NULL || !gif_validate6(ip6, sc, rcvif)) {
+	if (rcvif == NULL || !gif_validate6(ip6, var, rcvif)) {
 		m_put_rcvif_psref(rcvif, &psref);
+		gif_putref_variant(var, &psref_var);
 		m_freem(m);
 		IP6_STATINC(IP6_STAT_NOGIF);
 		return IPPROTO_DONE;
 	}
 	m_put_rcvif_psref(rcvif, &psref);
+	gif_putref_variant(var, &psref_var);
 #endif
 
 	otos = ip6->ip6_flow;
@@ -304,13 +318,13 @@ in6_gif_input(struct mbuf **mp, int *off
  * validate outer address.
  */
 static int
-gif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc, 
+gif_validate6(const struct ip6_hdr *ip6, struct gif_variant *var, 
 	struct ifnet *ifp)
 {
 	const struct sockaddr_in6 *src, *dst;
 
-	src = satosin6(sc->gif_psrc);
-	dst = satosin6(sc->gif_pdst);
+	src = satosin6(var->gv_psrc);
+	dst = satosin6(var->gv_pdst);
 
 	/* check for address match */
 	if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) ||
@@ -320,7 +334,7 @@ gif_validate6(const struct ip6_hdr *ip6,
 	/* martian filters on outer source - done in ip6_input */
 
 	/* ingress filters on outer source */
-	if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
+	if ((var->gv_softc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
 		union {
 			struct sockaddr sa;
 			struct sockaddr_in6 sin6;
@@ -334,7 +348,8 @@ gif_validate6(const struct ip6_hdr *ip6,
 #if 0
 			char ip6buf[INET6_ADDRSTRLEN];
 			log(LOG_WARNING, "%s: packet from %s dropped "
-			    "due to ingress filter\n", if_name(&sc->gif_if),
+			    "due to ingress filter\n",
+			    if_name(&var->gv_softc->gif_if),
 			    IN6_PRINT(ip6buf, &u.sin6.sin6_addr));
 #endif
 			if (rt != NULL)
@@ -353,22 +368,18 @@ gif_validate6(const struct ip6_hdr *ip6,
  * matched the physical addr family.  see gif_encapcheck().
  */
 int
-gif_encapcheck6(struct mbuf *m, int off, int proto, void *arg)
+gif_encapcheck6(struct mbuf *m, int off, int proto, struct gif_variant *var)
 {
 	struct ip6_hdr ip6;
-	struct gif_softc *sc;
 	struct ifnet *ifp = NULL;
 	int r;
 	struct psref psref;
 
-	/* sanity check done in caller */
-	sc = arg;
-
 	m_copydata(m, 0, sizeof(ip6), (void *)&ip6);
 	if ((m->m_flags & M_PKTHDR) != 0)
 		ifp = m_get_rcvif_psref(m, &psref);
 
-	r = gif_validate6(&ip6, sc, ifp);
+	r = gif_validate6(&ip6, var, ifp);
 
 	m_put_rcvif_psref(ifp, &psref);
 	return r;
@@ -376,7 +387,7 @@ gif_encapcheck6(struct mbuf *m, int off,
 #endif
 
 int
-in6_gif_attach(struct gif_softc *sc)
+in6_gif_attach(struct gif_variant *var)
 {
 #ifndef GIF_ENCAPCHECK
 	struct sockaddr_in6 mask6;
@@ -386,52 +397,47 @@ in6_gif_attach(struct gif_softc *sc)
 	mask6.sin6_addr.s6_addr32[0] = mask6.sin6_addr.s6_addr32[1] =
 	    mask6.sin6_addr.s6_addr32[2] = mask6.sin6_addr.s6_addr32[3] = ~0;
 
-	if (!sc->gif_psrc || !sc->gif_pdst)
+	if (!var->gv_psrc || !var->gv_pdst)
 		return EINVAL;
-	sc->encap_cookie6 = encap_attach(AF_INET6, -1, sc->gif_psrc,
-	    sin6tosa(&mask6), sc->gif_pdst, sin6tosa(&mask6),
-	    (const void *)&in6_gif_encapsw, sc);
+	var->gv_encap_cookie6 = encap_attach(AF_INET6, -1, var->gv_psrc,
+	    sin6tosa(&mask6), var->gv_pdst, sin6tosa(&mask6),
+	    (const void *)&in6_gif_encapsw, var->gv_softc);
 #else
-	sc->encap_cookie6 = encap_attach_func(AF_INET6, -1, gif_encapcheck,
-	    &in6_gif_encapsw, sc);
+	var->gv_encap_cookie6 = encap_attach_func(AF_INET6, -1, gif_encapcheck,
+	    &in6_gif_encapsw, var->gv_softc);
 #endif
-	if (sc->encap_cookie6 == NULL)
+	if (var->gv_encap_cookie6 == NULL)
 		return EEXIST;
+
+	var->gv_output = in6_gif_output;
 	return 0;
 }
 
 int
-in6_gif_detach(struct gif_softc *sc)
+in6_gif_detach(struct gif_variant *var)
 {
 	int error;
+	struct gif_softc *sc = var->gv_softc;
 
-	error = in6_gif_pause(sc);
+	error = encap_detach(var->gv_encap_cookie6);
+	if (error == 0)
+		var->gv_encap_cookie6 = NULL;
 
 	percpu_foreach(sc->gif_ro_percpu, gif_rtcache_free_pc, NULL);
 
 	return error;
 }
 
-int
-in6_gif_pause(struct gif_softc *sc)
-{
-	int error;
-
-	error = encap_detach(sc->encap_cookie6);
-	if (error == 0)
-		sc->encap_cookie6 = NULL;
-
-	return error;
-}
-
 void *
 in6_gif_ctlinput(int cmd, const struct sockaddr *sa, void *d, void *eparg)
 {
 	struct gif_softc *sc = eparg;
+	struct gif_variant *var;
 	struct ip6ctlparam *ip6cp = NULL;
 	struct ip6_hdr *ip6;
 	const struct sockaddr_in6 *dst6;
 	struct route *ro;
+	struct psref psref;
 
 	if (sa->sa_family != AF_INET6 ||
 	    sa->sa_len != sizeof(struct sockaddr_in6))
@@ -457,8 +463,12 @@ in6_gif_ctlinput(int cmd, const struct s
 
 	if ((sc->gif_if.if_flags & IFF_RUNNING) == 0)
 		return NULL;
-	if (sc->gif_psrc->sa_family != AF_INET6)
+	var = gif_getref_variant(sc, &psref);
+	if (var->gv_psrc->sa_family != AF_INET6) {
+		gif_putref_variant(var, &psref);
 		return NULL;
+	}
+	gif_putref_variant(var, &psref);
 
 	ro = percpu_getref(sc->gif_ro_percpu);
 	dst6 = satocsin6(rtcache_getdst(ro));

Index: src/sys/netinet6/in6_gif.h
diff -u src/sys/netinet6/in6_gif.h:1.16 src/sys/netinet6/in6_gif.h:1.17
--- src/sys/netinet6/in6_gif.h:1.16	Wed Nov 15 10:42:41 2017
+++ src/sys/netinet6/in6_gif.h	Mon Nov 27 05:02:22 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6_gif.h,v 1.16 2017/11/15 10:42:41 knakahara Exp $	*/
+/*	$NetBSD: in6_gif.h,v 1.17 2017/11/27 05:02:22 knakahara Exp $	*/
 /*	$KAME: in6_gif.h,v 1.7 2001/07/26 06:53:16 jinmei Exp $	*/
 
 /*
@@ -36,16 +36,14 @@
 #define GIF_HLIM	30
 extern int	ip6_gif_hlim;		/* Hop limit for gif encap packet */
 
-struct gif_softc;
+struct gif_variant;
 struct sockaddr;
 int in6_gif_input(struct mbuf **, int *, int, void *);
-int in6_gif_output(struct ifnet *, int, struct mbuf *);
 #ifdef GIF_ENCAPCHECK
-int gif_encapcheck6(struct mbuf *, int, int, void *);
+int gif_encapcheck6(struct mbuf *, int, int, struct gif_variant *);
 #endif
-int in6_gif_attach(struct gif_softc *);
-int in6_gif_detach(struct gif_softc *);
-int in6_gif_pause(struct gif_softc *);
+int in6_gif_attach(struct gif_variant *);
+int in6_gif_detach(struct gif_variant *);
 void *in6_gif_ctlinput(int, const struct sockaddr *, void *, void *);
 
 #endif /* !_NETINET6_IN6_GIF_H_ */

Reply via email to