Module Name:    src
Committed By:   ozaki-r
Date:           Tue Jun 21 10:25:27 UTC 2016

Modified Files:
        src/sys/net: if.c if.h
        src/sys/netinet6: icmp6.c in6_pcb.c in6_src.c ip6_output.c ip6_var.h
            nd6_nbr.c raw_ip6.c udp6_output.c

Log Message:
Make sure returning ifp from in6_select* functions psref-ed

To this end, callers need to pass struct psref to the functions
and the fuctions acquire a reference of ifp with it. In some cases,
we can simply use if_get_byindex, however, in other cases
(say rt->rt_ifp and ia->ifa_ifp), we have no MP-safe way for now.
In order to take a reference anyway we use non MP-safe function
if_acquire_NOMPSAFE for the latter cases. They should be fixed in
the future somehow.


To generate a diff of this commit:
cvs rdiff -u -r1.343 -r1.344 src/sys/net/if.c
cvs rdiff -u -r1.213 -r1.214 src/sys/net/if.h
cvs rdiff -u -r1.188 -r1.189 src/sys/netinet6/icmp6.c
cvs rdiff -u -r1.144 -r1.145 src/sys/netinet6/in6_pcb.c
cvs rdiff -u -r1.61 -r1.62 src/sys/netinet6/in6_src.c
cvs rdiff -u -r1.169 -r1.170 src/sys/netinet6/ip6_output.c
cvs rdiff -u -r1.66 -r1.67 src/sys/netinet6/ip6_var.h
cvs rdiff -u -r1.120 -r1.121 src/sys/netinet6/nd6_nbr.c
cvs rdiff -u -r1.145 -r1.146 src/sys/netinet6/raw_ip6.c
cvs rdiff -u -r1.51 -r1.52 src/sys/netinet6/udp6_output.c

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.c
diff -u src/sys/net/if.c:1.343 src/sys/net/if.c:1.344
--- src/sys/net/if.c:1.343	Mon Jun 20 08:30:58 2016
+++ src/sys/net/if.c	Tue Jun 21 10:25:27 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.c,v 1.343 2016/06/20 08:30:58 knakahara Exp $	*/
+/*	$NetBSD: if.c,v 1.344 2016/06/21 10:25:27 ozaki-r Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001, 2008 The NetBSD Foundation, Inc.
@@ -90,7 +90,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.343 2016/06/20 08:30:58 knakahara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.344 2016/06/21 10:25:27 ozaki-r Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_inet.h"
@@ -2193,6 +2193,9 @@ void
 if_put(const struct ifnet *ifp, struct psref *psref)
 {
 
+	if (ifp == NULL)
+		return;
+
 	psref_release(psref, &ifp->if_psref, ifnet_psref_class);
 }
 
@@ -2223,14 +2226,15 @@ if_get_byindex(u_int idx, struct psref *
 }
 
 /*
- * XXX unsafe
+ * XXX it's safe only if the passed ifp is guaranteed to not be freed,
+ * for example the ifp is already held or some other object is held which
+ * guarantes the ifp to not be freed indirectly.
  */
 void
-if_acquire_unsafe(struct ifnet *ifp, struct psref *psref)
+if_acquire_NOMPSAFE(struct ifnet *ifp, struct psref *psref)
 {
 
 	KASSERT(ifp->if_index != 0);
-	KASSERT(if_byindex(ifp->if_index) != NULL);
 	psref_acquire(psref, &ifp->if_psref, ifnet_psref_class);
 }
 

Index: src/sys/net/if.h
diff -u src/sys/net/if.h:1.213 src/sys/net/if.h:1.214
--- src/sys/net/if.h:1.213	Tue Jun 21 03:28:27 2016
+++ src/sys/net/if.h	Tue Jun 21 10:25:27 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.h,v 1.213 2016/06/21 03:28:27 ozaki-r Exp $	*/
+/*	$NetBSD: if.h,v 1.214 2016/06/21 10:25:27 ozaki-r Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc.
@@ -952,7 +952,7 @@ struct	ifnet *if_get(const char *, struc
 ifnet_t *if_byindex(u_int);
 ifnet_t *if_get_byindex(u_int, struct psref *);
 void	if_put(const struct ifnet *, struct psref *);
-void	if_acquire_unsafe(struct ifnet *, struct psref *);
+void	if_acquire_NOMPSAFE(struct ifnet *, struct psref *);
 
 static inline if_index_t
 if_get_index(const struct ifnet *ifp)

Index: src/sys/netinet6/icmp6.c
diff -u src/sys/netinet6/icmp6.c:1.188 src/sys/netinet6/icmp6.c:1.189
--- src/sys/netinet6/icmp6.c:1.188	Fri Jun 10 13:31:44 2016
+++ src/sys/netinet6/icmp6.c	Tue Jun 21 10:25:27 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: icmp6.c,v 1.188 2016/06/10 13:31:44 ozaki-r Exp $	*/
+/*	$NetBSD: icmp6.c,v 1.189 2016/06/21 10:25:27 ozaki-r Exp $	*/
 /*	$KAME: icmp6.c,v 1.217 2001/06/20 15:03:29 jinmei Exp $	*/
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: icmp6.c,v 1.188 2016/06/10 13:31:44 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: icmp6.c,v 1.189 2016/06/21 10:25:27 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -2091,7 +2091,7 @@ icmp6_reflect(struct mbuf *m, size_t off
 		sockaddr_in6_init(&sin6, &ip6->ip6_dst, 0, 0, 0);
 
 		memset(&ro, 0, sizeof(ro));
-		src = in6_selectsrc(&sin6, NULL, NULL, &ro, NULL, NULL, &e);
+		src = in6_selectsrc(&sin6, NULL, NULL, &ro, NULL, NULL, NULL, &e);
 		rtcache_free(&ro);
 		if (src == NULL) {
 			nd6log(LOG_DEBUG,

Index: src/sys/netinet6/in6_pcb.c
diff -u src/sys/netinet6/in6_pcb.c:1.144 src/sys/netinet6/in6_pcb.c:1.145
--- src/sys/netinet6/in6_pcb.c:1.144	Tue Jun 21 03:28:27 2016
+++ src/sys/netinet6/in6_pcb.c	Tue Jun 21 10:25:27 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6_pcb.c,v 1.144 2016/06/21 03:28:27 ozaki-r Exp $	*/
+/*	$NetBSD: in6_pcb.c,v 1.145 2016/06/21 10:25:27 ozaki-r Exp $	*/
 /*	$KAME: in6_pcb.c,v 1.84 2001/02/08 18:02:08 itojun Exp $	*/
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6_pcb.c,v 1.144 2016/06/21 03:28:27 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_pcb.c,v 1.145 2016/06/21 10:25:27 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -438,6 +438,8 @@ in6_pcbconnect(void *v, struct sockaddr_
 #endif
 	struct sockaddr_in6 tmp;
 	struct vestigial_inpcb vestige;
+	struct psref psref;
+	int bound;
 
 	(void)&in6a;				/* XXX fool gcc */
 
@@ -478,6 +480,7 @@ in6_pcbconnect(void *v, struct sockaddr_
 	tmp = *sin6;
 	sin6 = &tmp;
 
+	bound = curlwp_bind();
 	/* Source address selection. */
 	if (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr) &&
 	    in6p->in6p_laddr.s6_addr32[3] == 0) {
@@ -512,23 +515,29 @@ in6_pcbconnect(void *v, struct sockaddr_
 		in6a = in6_selectsrc(sin6, in6p->in6p_outputopts,
 				     in6p->in6p_moptions,
 				     &in6p->in6p_route,
-				     &in6p->in6p_laddr, &ifp, &error);
+				     &in6p->in6p_laddr, &ifp, &psref, &error);
 		if (ifp && scope_ambiguous &&
 		    (error = in6_setscope(&sin6->sin6_addr, ifp, NULL)) != 0) {
+			if_put(ifp, &psref);
+			curlwp_bindx(bound);
 			return(error);
 		}
 
 		if (in6a == NULL) {
+			if_put(ifp, &psref);
+			curlwp_bindx(bound);
 			if (error == 0)
 				error = EADDRNOTAVAIL;
 			return (error);
 		}
 	}
 
-	if (ifp != NULL)
+	if (ifp != NULL) {
 		in6p->in6p_ip6.ip6_hlim = (u_int8_t)in6_selecthlim(in6p, ifp);
-	else
+		if_put(ifp, &psref);
+	} else
 		in6p->in6p_ip6.ip6_hlim = (u_int8_t)in6_selecthlim_rt(in6p);
+	curlwp_bindx(bound);
 
 	if (in6_pcblookup_connect(in6p->in6p_table, &sin6->sin6_addr,
 	    sin6->sin6_port,

Index: src/sys/netinet6/in6_src.c
diff -u src/sys/netinet6/in6_src.c:1.61 src/sys/netinet6/in6_src.c:1.62
--- src/sys/netinet6/in6_src.c:1.61	Tue Jun 21 03:28:27 2016
+++ src/sys/netinet6/in6_src.c	Tue Jun 21 10:25:27 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6_src.c,v 1.61 2016/06/21 03:28:27 ozaki-r Exp $	*/
+/*	$NetBSD: in6_src.c,v 1.62 2016/06/21 10:25:27 ozaki-r Exp $	*/
 /*	$KAME: in6_src.c,v 1.159 2005/10/19 01:40:32 t-momose Exp $	*/
 
 /*
@@ -66,7 +66,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6_src.c,v 1.61 2016/06/21 03:28:27 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_src.c,v 1.62 2016/06/21 10:25:27 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -123,10 +123,10 @@ struct in6_addrpolicy defaultaddrpolicy;
 int ip6_prefer_tempaddr = 0;
 
 static int selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
-	struct ip6_moptions *, struct route *, struct ifnet **,
+	struct ip6_moptions *, struct route *, struct ifnet **, struct psref *,
 	struct rtentry **, int, int);
 static int in6_selectif(struct sockaddr_in6 *, struct ip6_pktopts *,
-	struct ip6_moptions *, struct route *, struct ifnet **);
+	struct ip6_moptions *, struct route *, struct ifnet **, struct psref *);
 
 static struct in6_addrpolicy *lookup_addrsel_policy(struct sockaddr_in6 *);
 
@@ -174,7 +174,7 @@ static struct in6_addrpolicy *match_addr
 struct in6_addr *
 in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, 
 	struct ip6_moptions *mopts, struct route *ro, struct in6_addr *laddr, 
-	struct ifnet **ifpp, int *errorp)
+	struct ifnet **ifpp, struct psref *psref, int *errorp)
 {
 	struct in6_addr dst;
 	struct ifnet *ifp = NULL;
@@ -188,6 +188,13 @@ in6_selectsrc(struct sockaddr_in6 *dstso
 #if defined(MIP6) && NMIP > 0
 	u_int8_t ip6po_usecoa = 0;
 #endif /* MIP6 && NMIP > 0 */
+	struct psref local_psref;
+	struct in6_addr *ret_ia = NULL;
+	int bound = curlwp_bind();
+#define PSREF (psref == NULL) ? &local_psref : psref
+
+	KASSERT((ifpp != NULL && psref != NULL) ||
+	        (ifpp == NULL && psref == NULL));
 
 	dst = dstsock->sin6_addr; /* make a copy for local operation */
 	*errorp = 0;
@@ -201,8 +208,8 @@ in6_selectsrc(struct sockaddr_in6 *dstso
 	 * to this function (e.g., for identifying the appropriate scope zone
 	 * ID).
 	 */
-	error = in6_selectif(dstsock, opts, mopts, ro, &ifp);
-	if (ifpp)
+	error = in6_selectif(dstsock, opts, mopts, ro, &ifp, PSREF);
+	if (ifpp != NULL)
 		*ifpp = ifp;
 
 	/*
@@ -230,19 +237,20 @@ in6_selectsrc(struct sockaddr_in6 *dstso
 		if (ifp) {
 			*errorp = in6_setscope(&srcsock.sin6_addr, ifp, NULL);
 			if (*errorp != 0)
-				return (NULL);
+				goto exit;
 		}
 
 		ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock));
 		if (ia6 == NULL ||
 		    (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) {
 			*errorp = EADDRNOTAVAIL;
-			return (NULL);
+			goto exit;
 		}
 		pi->ipi6_addr = srcsock.sin6_addr; /* XXX: this overrides pi */
 		if (ifpp)
 			*ifpp = ifp;
-		return (&ia6->ia_addr.sin6_addr);
+		ret_ia = &ia6->ia_addr.sin6_addr;
+		goto exit;
 	}
 
 	/*
@@ -250,8 +258,10 @@ in6_selectsrc(struct sockaddr_in6 *dstso
 	 * care at the moment whether in6_selectif() succeeded above, even
 	 * though it would eventually cause an error.
 	 */
-	if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr))
-		return (laddr);
+	if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) {
+		ret_ia = laddr;
+		goto exit;
+	}
 
 	/*
 	 * The outgoing interface is crucial in the general selection procedure
@@ -259,7 +269,7 @@ in6_selectsrc(struct sockaddr_in6 *dstso
 	 */
 	if (ifp == NULL) {
 		*errorp = error;
-		return (NULL);
+		goto exit;
 	}
 
 	/*
@@ -281,7 +291,7 @@ in6_selectsrc(struct sockaddr_in6 *dstso
 
 	*errorp = in6_setscope(&dst, ifp, &odstzone);
 	if (*errorp != 0)
-		return (NULL);
+		goto exit;
 
 	for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
 		int new_scope = -1, new_matchlen = -1;
@@ -544,10 +554,16 @@ in6_selectsrc(struct sockaddr_in6 *dstso
 
 	if ((ia = ia_best) == NULL) {
 		*errorp = EADDRNOTAVAIL;
-		return (NULL);
+		goto exit;
 	}
 
-	return (&ia->ia_addr.sin6_addr);
+	ret_ia = &ia->ia_addr.sin6_addr;
+exit:
+	if (ifpp == NULL)
+		if_put(ifp, PSREF);
+	curlwp_bindx(bound);
+	return ret_ia;
+#undef PSREF
 }
 #undef REPLACE
 #undef BREAK
@@ -556,7 +572,7 @@ in6_selectsrc(struct sockaddr_in6 *dstso
 static int
 selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, 
 	struct ip6_moptions *mopts, struct route *ro, struct ifnet **retifp, 
-	struct rtentry **retrt, int clone, int norouteok)
+	struct psref *psref, struct rtentry **retrt, int clone, int norouteok)
 {
 	int error = 0;
 	struct ifnet *ifp = NULL;
@@ -564,6 +580,11 @@ selectroute(struct sockaddr_in6 *dstsock
 	struct sockaddr_in6 *sin6_next;
 	struct in6_pktinfo *pi = NULL;
 	struct in6_addr *dst;
+	struct psref local_psref;
+#define PSREF	((psref == NULL) ? &local_psref : psref)
+
+	KASSERT((retifp != NULL && psref != NULL) ||
+	        (retifp == NULL && psref == NULL));
 
 	dst = &dstsock->sin6_addr;
 
@@ -583,7 +604,7 @@ selectroute(struct sockaddr_in6 *dstsock
 	/* If the caller specify the outgoing interface explicitly, use it. */
 	if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) {
 		/* XXX boundary check is assumed to be already done. */
-		ifp = if_byindex(pi->ipi6_ifindex);
+		ifp = if_get_byindex(pi->ipi6_ifindex, PSREF);
 		if (ifp != NULL &&
 		    (norouteok || retrt == NULL ||
 		    IN6_IS_ADDR_MULTICAST(dst))) {
@@ -592,8 +613,11 @@ selectroute(struct sockaddr_in6 *dstsock
 			 * multicast.
 			 */
 			goto done;
-		} else
+		} else {
+			if_put(ifp, PSREF);
+			ifp = NULL;
 			goto getroute;
+		}
 	}
 
 	/*
@@ -601,8 +625,7 @@ selectroute(struct sockaddr_in6 *dstsock
 	 * interface for the address is specified by the caller, use it.
 	 */
 	if (IN6_IS_ADDR_MULTICAST(dst) && mopts != NULL) {
-		/* XXX not MP-safe yet */
-		ifp = if_byindex(mopts->im6o_multicast_if_index);
+		ifp = if_get_byindex(mopts->im6o_multicast_if_index, PSREF);
 		if (ifp != NULL)
 			goto done; /* we do not need a route for multicast. */
 	}
@@ -636,6 +659,8 @@ selectroute(struct sockaddr_in6 *dstsock
 			goto done;
 		}
 		ifp = rt->rt_ifp;
+		if (ifp != NULL)
+			if_acquire_NOMPSAFE(ifp, PSREF);
 
 		/*
 		 * When cloning is required, try to allocate a route to the
@@ -671,8 +696,12 @@ selectroute(struct sockaddr_in6 *dstsock
 
 		if (rt == NULL)
 			error = EHOSTUNREACH;
-		else
+		else {
+			if_put(ifp, PSREF);
 			ifp = rt->rt_ifp;
+			if (ifp != NULL)
+				if_acquire_NOMPSAFE(ifp, PSREF);
+		}
 
 		/*
 		 * Check if the outgoing interface conflicts with
@@ -705,21 +734,28 @@ selectroute(struct sockaddr_in6 *dstsock
 
 	if (retifp != NULL)
 		*retifp = ifp;
+	else
+		if_put(ifp, PSREF);
 	if (retrt != NULL)
 		*retrt = rt;	/* rt may be NULL */
 
 	return (error);
+#undef PSREF
 }
 
 static int
 in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, 
-	struct ip6_moptions *mopts, struct route *ro, struct ifnet **retifp)
+	struct ip6_moptions *mopts, struct route *ro, struct ifnet **retifp,
+	struct psref *psref)
 {
 	int error, clone;
 	struct rtentry *rt = NULL;
 
+	KASSERT(retifp != NULL);
+	*retifp = NULL;
+
 	clone = IN6_IS_ADDR_MULTICAST(&dstsock->sin6_addr) ? 0 : 1;
-	if ((error = selectroute(dstsock, opts, mopts, ro, retifp,
+	if ((error = selectroute(dstsock, opts, mopts, ro, retifp, psref,
 	    &rt, clone, 1)) != 0) {
 		return (error);
 	}
@@ -751,8 +787,12 @@ in6_selectif(struct sockaddr_in6 *dstsoc
 	 * destination address (which should probably be one of our own
 	 * addresses.)
 	 */
-	if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp)
+	if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp &&
+	    rt->rt_ifa->ifa_ifp != *retifp) {
+		if_put(*retifp, psref);
 		*retifp = rt->rt_ifa->ifa_ifp;
+		if_acquire_NOMPSAFE(*retifp, psref);
+	}
 
 	return (0);
 }
@@ -764,9 +804,9 @@ in6_selectif(struct sockaddr_in6 *dstsoc
 int
 in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, 
 	struct ip6_moptions *mopts, struct route *ro, struct ifnet **retifp, 
-	struct rtentry **retrt, int clone)
+	struct psref *psref, struct rtentry **retrt, int clone)
 {
-	return selectroute(dstsock, opts, mopts, ro, retifp,
+	return selectroute(dstsock, opts, mopts, ro, retifp, psref,
 	    retrt, clone, 0);
 }
 

Index: src/sys/netinet6/ip6_output.c
diff -u src/sys/netinet6/ip6_output.c:1.169 src/sys/netinet6/ip6_output.c:1.170
--- src/sys/netinet6/ip6_output.c:1.169	Tue Jun 21 10:21:04 2016
+++ src/sys/netinet6/ip6_output.c	Tue Jun 21 10:25:27 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip6_output.c,v 1.169 2016/06/21 10:21:04 ozaki-r Exp $	*/
+/*	$NetBSD: ip6_output.c,v 1.170 2016/06/21 10:25:27 ozaki-r Exp $	*/
 /*	$KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $	*/
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.169 2016/06/21 10:21:04 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.170 2016/06/21 10:25:27 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -163,7 +163,7 @@ ip6_output(
 )
 {
 	struct ip6_hdr *ip6, *mhip6;
-	struct ifnet *ifp, *origifp;
+	struct ifnet *ifp, *origifp = NULL;
 	struct mbuf *m = m0;
 	int hlen, tlen, len, off;
 	bool tso;
@@ -185,6 +185,9 @@ ip6_output(
 #ifdef IPSEC
 	struct secpolicy *sp = NULL;
 #endif
+	struct psref psref, psref_ia;
+	int bound = curlwp_bind();
+	bool release_psref_ia = false;
 
 	memset(&ip6route, 0, sizeof(ip6route));
 
@@ -488,8 +491,9 @@ ip6_output(
 	ip6 = mtod(m, struct ip6_hdr *);
 
 	sockaddr_in6_init(&dst_sa, &ip6->ip6_dst, 0, 0, 0);
+	ifp = NULL;
 	if ((error = in6_selectroute(&dst_sa, opt, im6o, ro,
-	    &ifp, &rt, 0)) != 0) {
+	    &ifp, &psref, &rt, 0)) != 0) {
 		if (ifp != NULL)
 			in6_ifstat_inc(ifp, ifs6_out_discard);
 		goto bad;
@@ -522,9 +526,11 @@ ip6_output(
 	 * destination addresses.  We should use ia_ifp to support the
 	 * case of sending packets to an address of our own.
 	 */
-	if (ia != NULL && ia->ia_ifp)
+	if (ia != NULL && ia->ia_ifp) {
 		origifp = ia->ia_ifp;
-	else
+		if_acquire_NOMPSAFE(origifp, &psref_ia);
+		release_psref_ia = true;
+	} else
 		origifp = ifp;
 
 	src0 = ip6->ip6_src;
@@ -976,6 +982,10 @@ done:
 		KEY_FREESP(&sp);
 #endif /* IPSEC */
 
+	if_put(ifp, &psref);
+	if (release_psref_ia)
+		if_put(origifp, &psref_ia);
+	curlwp_bindx(bound);
 
 	return (error);
 

Index: src/sys/netinet6/ip6_var.h
diff -u src/sys/netinet6/ip6_var.h:1.66 src/sys/netinet6/ip6_var.h:1.67
--- src/sys/netinet6/ip6_var.h:1.66	Tue Jun 21 03:28:27 2016
+++ src/sys/netinet6/ip6_var.h	Tue Jun 21 10:25:27 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip6_var.h,v 1.66 2016/06/21 03:28:27 ozaki-r Exp $	*/
+/*	$NetBSD: ip6_var.h,v 1.67 2016/06/21 10:25:27 ozaki-r Exp $	*/
 /*	$KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $	*/
 
 /*
@@ -397,10 +397,10 @@ struct route;
 
 struct 	in6_addr *in6_selectsrc(struct sockaddr_in6 *,
 	struct ip6_pktopts *, struct ip6_moptions *, struct route *,
-	struct in6_addr *, struct ifnet **, int *);
+	struct in6_addr *, struct ifnet **, struct psref *, int *);
 int in6_selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
 	struct ip6_moptions *, struct route *, struct ifnet **,
-	struct rtentry **, int);
+	struct psref *, struct rtentry **, int);
 int	ip6_get_membership(const struct sockopt *, struct ifnet **, void *,
 	size_t);
 

Index: src/sys/netinet6/nd6_nbr.c
diff -u src/sys/netinet6/nd6_nbr.c:1.120 src/sys/netinet6/nd6_nbr.c:1.121
--- src/sys/netinet6/nd6_nbr.c:1.120	Tue Jun 21 03:28:27 2016
+++ src/sys/netinet6/nd6_nbr.c	Tue Jun 21 10:25:27 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: nd6_nbr.c,v 1.120 2016/06/21 03:28:27 ozaki-r Exp $	*/
+/*	$NetBSD: nd6_nbr.c,v 1.121 2016/06/21 10:25:27 ozaki-r Exp $	*/
 /*	$KAME: nd6_nbr.c,v 1.61 2001/02/10 16:06:14 jinmei Exp $	*/
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.120 2016/06/21 03:28:27 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.121 2016/06/21 10:25:27 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -454,7 +454,7 @@ nd6_ns_output(struct ifnet *ifp, const s
 			sockaddr_in6_init(&dst_sa, &ip6->ip6_dst, 0, 0, 0);
 
 			src = in6_selectsrc(&dst_sa, NULL,
-			    NULL, &ro, NULL, NULL, &error);
+			    NULL, &ro, NULL, NULL, NULL, &error);
 			if (src == NULL) {
 				nd6log(LOG_DEBUG, "source can't be "
 				    "determined: dst=%s, error=%d\n",
@@ -944,7 +944,7 @@ nd6_na_output(
 	/*
 	 * Select a source whose scope is the same as that of the dest.
 	 */
-	src = in6_selectsrc(satosin6(dst), NULL, NULL, &ro, NULL, NULL, &error);
+	src = in6_selectsrc(satosin6(dst), NULL, NULL, &ro, NULL, NULL, NULL, &error);
 	if (src == NULL) {
 		nd6log(LOG_DEBUG, "source can't be "
 		    "determined: dst=%s, error=%d\n",

Index: src/sys/netinet6/raw_ip6.c
diff -u src/sys/netinet6/raw_ip6.c:1.145 src/sys/netinet6/raw_ip6.c:1.146
--- src/sys/netinet6/raw_ip6.c:1.145	Thu Jun 16 02:38:40 2016
+++ src/sys/netinet6/raw_ip6.c	Tue Jun 21 10:25:27 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: raw_ip6.c,v 1.145 2016/06/16 02:38:40 ozaki-r Exp $	*/
+/*	$NetBSD: raw_ip6.c,v 1.146 2016/06/21 10:25:27 ozaki-r Exp $	*/
 /*	$KAME: raw_ip6.c,v 1.82 2001/07/23 18:57:56 jinmei Exp $	*/
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: raw_ip6.c,v 1.145 2016/06/16 02:38:40 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: raw_ip6.c,v 1.146 2016/06/21 10:25:27 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ipsec.h"
@@ -388,6 +388,7 @@ rip6_output(struct mbuf *m, struct socke
 	int scope_ambiguous = 0;
 	struct in6_addr *in6a;
 	int bound = curlwp_bind();
+	struct psref psref;
 
 	in6p = sotoin6pcb(so);
 
@@ -448,7 +449,7 @@ rip6_output(struct mbuf *m, struct socke
 	 * Source address selection.
 	 */
 	if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions,
-	    &in6p->in6p_route, &in6p->in6p_laddr, &oifp, &error)) == 0) {
+	    &in6p->in6p_route, &in6p->in6p_laddr, &oifp, &psref, &error)) == 0) {
 		if (error == 0)
 			error = EADDRNOTAVAIL;
 		goto bad;
@@ -478,6 +479,9 @@ rip6_output(struct mbuf *m, struct socke
 	ip6->ip6_nxt   = in6p->in6p_ip6.ip6_nxt;
 	ip6->ip6_hlim = in6_selecthlim(in6p, oifp);
 
+	if_put(oifp, &psref);
+	oifp = NULL;
+
 	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
 	    in6p->in6p_cksum != -1) {
 		int off;
@@ -534,6 +538,7 @@ rip6_output(struct mbuf *m, struct socke
 		ip6_clearpktopts(&opt, -1);
 		m_freem(control);
 	}
+	if_put(oifp, &psref);
 	curlwp_bindx(bound);
 	return error;
 }
@@ -715,6 +720,8 @@ rip6_connect(struct socket *so, struct s
 	struct ifnet *ifp = NULL;
 	int scope_ambiguous = 0;
 	int error = 0;
+	struct psref psref;
+	int bound;
 
 	KASSERT(solocked(so));
 	KASSERT(in6p != NULL);
@@ -738,10 +745,11 @@ rip6_connect(struct socket *so, struct s
 	if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0)
 		return error;
 
+	bound = curlwp_bind();
 	/* Source address selection. XXX: need pcblookup? */
 	in6a = in6_selectsrc(addr, in6p->in6p_outputopts,
 	    in6p->in6p_moptions, &in6p->in6p_route,
-	    &in6p->in6p_laddr, &ifp, &error);
+	    &in6p->in6p_laddr, &ifp, &psref, &error);
 	if (in6a == NULL) {
 		if (error == 0)
 			error = EADDRNOTAVAIL;
@@ -756,6 +764,8 @@ rip6_connect(struct socket *so, struct s
 	in6p->in6p_faddr = addr->sin6_addr;
 	soisconnected(so);
 out:
+	if_put(ifp, &psref);
+	curlwp_bindx(bound);
 	return error;
 }
 

Index: src/sys/netinet6/udp6_output.c
diff -u src/sys/netinet6/udp6_output.c:1.51 src/sys/netinet6/udp6_output.c:1.52
--- src/sys/netinet6/udp6_output.c:1.51	Fri Jun 10 13:31:44 2016
+++ src/sys/netinet6/udp6_output.c	Tue Jun 21 10:25:27 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: udp6_output.c,v 1.51 2016/06/10 13:31:44 ozaki-r Exp $	*/
+/*	$NetBSD: udp6_output.c,v 1.52 2016/06/21 10:25:27 ozaki-r Exp $	*/
 /*	$KAME: udp6_output.c,v 1.43 2001/10/15 09:19:52 itojun Exp $	*/
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: udp6_output.c,v 1.51 2016/06/10 13:31:44 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: udp6_output.c,v 1.52 2016/06/21 10:25:27 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -226,14 +226,19 @@ udp6_output(struct in6pcb * const in6p, 
 		}
 
 		if (!IN6_IS_ADDR_V4MAPPED(faddr)) {
+			struct psref psref;
+
 			laddr = in6_selectsrc(sin6, optp,
 			    in6p->in6p_moptions,
 			    &in6p->in6p_route,
-			    &in6p->in6p_laddr, &oifp, &error);
+			    &in6p->in6p_laddr, &oifp, &psref, &error);
 			if (oifp && scope_ambiguous &&
 			    (error = in6_setscope(&sin6->sin6_addr,
-			    oifp, NULL)))
+			    oifp, NULL))) {
+				if_put(oifp, &psref);
 				goto release;
+			}
+			if_put(oifp, &psref);
 		} else {
 			/*
 			 * XXX: freebsd[34] does not have in_selectsrc, but

Reply via email to