Module Name:    src
Committed By:   ozaki-r
Date:           Thu Mar  2 05:24:23 UTC 2017

Modified Files:
        src/sys/netinet: ip_output.c
        src/sys/netinet6: ip6_output.c ip6_var.h

Log Message:
Make usages of ifp MP-safe in some functions of IP multicast


To generate a diff of this commit:
cvs rdiff -u -r1.272 -r1.273 src/sys/netinet/ip_output.c
cvs rdiff -u -r1.188 -r1.189 src/sys/netinet6/ip6_output.c
cvs rdiff -u -r1.72 -r1.73 src/sys/netinet6/ip6_var.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/netinet/ip_output.c
diff -u src/sys/netinet/ip_output.c:1.272 src/sys/netinet/ip_output.c:1.273
--- src/sys/netinet/ip_output.c:1.272	Wed Feb 22 07:05:04 2017
+++ src/sys/netinet/ip_output.c	Thu Mar  2 05:24:23 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip_output.c,v 1.272 2017/02/22 07:05:04 ozaki-r Exp $	*/
+/*	$NetBSD: ip_output.c,v 1.273 2017/03/02 05:24:23 ozaki-r Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -91,7 +91,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.272 2017/02/22 07:05:04 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.273 2017/03/02 05:24:23 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -1444,6 +1444,7 @@ bad:
 
 /*
  * following RFC1724 section 3.3, 0.0.0.0/8 is interpreted as interface index.
+ * Must be called in a pserialize critical section.
  */
 static struct ifnet *
 ip_multicast_if(struct in_addr *a, int *ifindexp)
@@ -1462,10 +1463,12 @@ ip_multicast_if(struct in_addr *a, int *
 		if (ifindexp)
 			*ifindexp = ifindex;
 	} else {
-		LIST_FOREACH(ia, &IN_IFADDR_HASH(a->s_addr), ia_hash) {
+		IN_ADDRHASH_READER_FOREACH(ia, a->s_addr) {
 			if (in_hosteq(ia->ia_addr.sin_addr, *a) &&
 			    (ia->ia_ifp->if_flags & IFF_MULTICAST) != 0) {
 				ifp = ia->ia_ifp;
+				if (if_is_deactivated(ifp))
+					ifp = NULL;
 				break;
 			}
 		}
@@ -1509,7 +1512,7 @@ ip_getoptval(const struct sockopt *sopt,
 
 static int
 ip_get_membership(const struct sockopt *sopt, struct ifnet **ifp,
-    struct in_addr *ia, bool add)
+    struct psref *psref, struct in_addr *ia, bool add)
 {
 	int error;
 	struct ip_mreq mreq;
@@ -1546,12 +1549,28 @@ ip_get_membership(const struct sockopt *
 		if (error != 0)
 			return error;
 		*ifp = (rt = rtcache_init(&ro)) != NULL ? rt->rt_ifp : NULL;
+		if (*ifp != NULL) {
+			if (if_is_deactivated(*ifp))
+				*ifp = NULL;
+			else
+				if_acquire(*ifp, psref);
+		}
 		rtcache_unref(rt, &ro);
 		rtcache_free(&ro);
 	} else {
+		int s = pserialize_read_enter();
 		*ifp = ip_multicast_if(&mreq.imr_interface, NULL);
-		if (!add && *ifp == NULL)
+		if (!add && *ifp == NULL) {
+			pserialize_read_exit(s);
 			return EADDRNOTAVAIL;
+		}
+		if (*ifp != NULL) {
+			if (if_is_deactivated(*ifp))
+				*ifp = NULL;
+			else
+				if_acquire(*ifp, psref);
+		}
+		pserialize_read_exit(s);
 	}
 	return 0;
 }
@@ -1565,26 +1584,31 @@ ip_add_membership(struct ip_moptions *im
 {
 	struct ifnet *ifp = NULL;	// XXX: gcc [ppc]
 	struct in_addr ia;
-	int i, error;
+	int i, error, bound;
+	struct psref psref;
 
+	bound = curlwp_bind();
 	if (sopt->sopt_size == sizeof(struct ip_mreq))
-		error = ip_get_membership(sopt, &ifp, &ia, true);
+		error = ip_get_membership(sopt, &ifp, &psref, &ia, true);
 	else
 #ifdef INET6
-		error = ip6_get_membership(sopt, &ifp, &ia, sizeof(ia));
+		error = ip6_get_membership(sopt, &ifp, &psref, &ia, sizeof(ia));
 #else
-		return EINVAL;	
+		error = EINVAL;
+		goto out;
 #endif
 
 	if (error)
-		return error;
+		goto out;
 
 	/*
 	 * See if we found an interface, and confirm that it
 	 * supports multicast.
 	 */
-	if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0)
-		return EADDRNOTAVAIL;
+	if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+		error = EADDRNOTAVAIL;
+		goto out;
+	}
 
 	/*
 	 * See if the membership already exists or if all the
@@ -1595,21 +1619,31 @@ ip_add_membership(struct ip_moptions *im
 		    in_hosteq(imo->imo_membership[i]->inm_addr, ia))
 			break;
 	}
-	if (i < imo->imo_num_memberships)
-		return EADDRINUSE;
+	if (i < imo->imo_num_memberships) {
+		error = EADDRINUSE;
+		goto out;
+	}
 
-	if (i == IP_MAX_MEMBERSHIPS)
-		return ETOOMANYREFS;
+	if (i == IP_MAX_MEMBERSHIPS) {
+		error = ETOOMANYREFS;
+		goto out;
+	}
 
 	/*
 	 * Everything looks good; add a new record to the multicast
 	 * address list for the given interface.
 	 */
-	if ((imo->imo_membership[i] = in_addmulti(&ia, ifp)) == NULL)
-		return ENOBUFS;
+	if ((imo->imo_membership[i] = in_addmulti(&ia, ifp)) == NULL) {
+		error = ENOBUFS;
+		goto out;
+	}
 
 	++imo->imo_num_memberships;
-	return 0;
+	error = 0;
+out:
+	if_put(ifp, &psref);
+	curlwp_bindx(bound);
+	return error;
 }
 
 /*
@@ -1621,19 +1655,24 @@ ip_drop_membership(struct ip_moptions *i
 {
 	struct in_addr ia = { .s_addr = 0 };	// XXX: gcc [ppc]
 	struct ifnet *ifp = NULL;		// XXX: gcc [ppc]
-	int i, error;
+	int i, error, bound;
+	struct psref psref;
 
+	/* imo is protected by solock or referenced only by the caller */
+
+	bound = curlwp_bind();
 	if (sopt->sopt_size == sizeof(struct ip_mreq))
-		error = ip_get_membership(sopt, &ifp, &ia, false);
+		error = ip_get_membership(sopt, &ifp, &psref, &ia, false);
 	else
 #ifdef INET6
-		error = ip6_get_membership(sopt, &ifp, &ia, sizeof(ia));
+		error = ip6_get_membership(sopt, &ifp, &psref, &ia, sizeof(ia));
 #else
-		return EINVAL;
+		error = EINVAL;
+		goto out;
 #endif
 
 	if (error)
-		return error;
+		goto out;
 
 	/*
 	 * Find the membership in the membership array.
@@ -1644,8 +1683,10 @@ ip_drop_membership(struct ip_moptions *i
 		    in_hosteq(imo->imo_membership[i]->inm_addr, ia))
 			break;
 	}
-	if (i == imo->imo_num_memberships)
-		return EADDRNOTAVAIL;
+	if (i == imo->imo_num_memberships) {
+		error = EADDRNOTAVAIL;
+		goto out;
+	}
 
 	/*
 	 * Give up the multicast address record to which the
@@ -1659,7 +1700,11 @@ ip_drop_membership(struct ip_moptions *i
 	for (++i; i < imo->imo_num_memberships; ++i)
 		imo->imo_membership[i-1] = imo->imo_membership[i];
 	--imo->imo_num_memberships;
-	return 0;
+	error = 0;
+out:
+	curlwp_bindx(bound);
+	if_put(ifp, &psref);
+	return error;
 }
 
 /*
@@ -1691,7 +1736,8 @@ ip_setmoptions(struct ip_moptions **pimo
 	}
 
 	switch (sopt->sopt_name) {
-	case IP_MULTICAST_IF:
+	case IP_MULTICAST_IF: {
+		int s;
 		/*
 		 * Select the interface for outgoing multicast packets.
 		 */
@@ -1713,17 +1759,21 @@ ip_setmoptions(struct ip_moptions **pimo
 		 * IP address.  Find the interface and confirm that
 		 * it supports multicasting.
 		 */
+		s = pserialize_read_enter();
 		ifp = ip_multicast_if(&addr, &ifindex);
 		if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+			pserialize_read_exit(s);
 			error = EADDRNOTAVAIL;
 			break;
 		}
 		imo->imo_multicast_if_index = ifp->if_index;
+		pserialize_read_exit(s);
 		if (ifindex)
 			imo->imo_multicast_addr = addr;
 		else
 			imo->imo_multicast_addr.s_addr = INADDR_ANY;
 		break;
+	    }
 
 	case IP_MULTICAST_TTL:
 		/*

Index: src/sys/netinet6/ip6_output.c
diff -u src/sys/netinet6/ip6_output.c:1.188 src/sys/netinet6/ip6_output.c:1.189
--- src/sys/netinet6/ip6_output.c:1.188	Thu Mar  2 01:05:02 2017
+++ src/sys/netinet6/ip6_output.c	Thu Mar  2 05:24:23 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip6_output.c,v 1.188 2017/03/02 01:05:02 ozaki-r Exp $	*/
+/*	$NetBSD: ip6_output.c,v 1.189 2017/03/02 05:24:23 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.188 2017/03/02 01:05:02 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.189 2017/03/02 05:24:23 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -2380,13 +2380,14 @@ ip6_freepcbopts(struct ip6_pktopts *pkto
 }
 
 int
-ip6_get_membership(const struct sockopt *sopt, struct ifnet **ifp, void *v,
-    size_t l)
+ip6_get_membership(const struct sockopt *sopt, struct ifnet **ifp,
+    struct psref *psref, void *v, size_t l)
 {
 	struct ipv6_mreq mreq;
 	int error;
 	struct in6_addr *ia = &mreq.ipv6mr_multiaddr;
 	struct in_addr *ia4 = (void *)&ia->s6_addr32[3];
+
 	error = sockopt_get(sopt, &mreq, sizeof(mreq));
 	if (error != 0)
 		return error;
@@ -2437,15 +2438,16 @@ ip6_get_membership(const struct sockopt 
 		if (error != 0)
 			return error;
 		rt = rtcache_init(&ro);
-		*ifp = rt != NULL ? rt->rt_ifp : NULL;
-		/* FIXME *ifp is NOMPSAFE */
+		*ifp = rt != NULL ?
+		    if_get_byindex(rt->rt_ifp->if_index, psref) : NULL;
 		rtcache_unref(rt, &ro);
 		rtcache_free(&ro);
 	} else {
 		/*
 		 * If the interface is specified, validate it.
 		 */
-		if ((*ifp = if_byindex(mreq.ipv6mr_interface)) == NULL)
+		*ifp = if_get_byindex(mreq.ipv6mr_interface, psref);
+		if (*ifp == NULL)
 			return ENXIO;	/* XXX EINVAL? */
 	}
 	if (sizeof(*ia) == l)
@@ -2488,7 +2490,8 @@ ip6_setmoptions(const struct sockopt *so
 
 	switch (sopt->sopt_name) {
 
-	case IPV6_MULTICAST_IF:
+	case IPV6_MULTICAST_IF: {
+		int s;
 		/*
 		 * Select the interface for outgoing multicast packets.
 		 */
@@ -2496,19 +2499,24 @@ ip6_setmoptions(const struct sockopt *so
 		if (error != 0)
 			break;
 
+		s = pserialize_read_enter();
 		if (ifindex != 0) {
 			if ((ifp = if_byindex(ifindex)) == NULL) {
+				pserialize_read_exit(s);
 				error = ENXIO;	/* XXX EINVAL? */
 				break;
 			}
 			if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+				pserialize_read_exit(s);
 				error = EADDRNOTAVAIL;
 				break;
 			}
 		} else
 			ifp = NULL;
 		im6o->im6o_multicast_if_index = if_get_index(ifp);
+		pserialize_read_exit(s);
 		break;
+	    }
 
 	case IPV6_MULTICAST_HOPS:
 	    {
@@ -2545,17 +2553,23 @@ ip6_setmoptions(const struct sockopt *so
 		im6o->im6o_multicast_loop = loop;
 		break;
 
-	case IPV6_JOIN_GROUP:
+	case IPV6_JOIN_GROUP: {
+		int bound;
+		struct psref psref;
 		/*
 		 * Add a multicast group membership.
 		 * Group must be a valid IP6 multicast address.
 		 */
-		if ((error = ip6_get_membership(sopt, &ifp, &ia, sizeof(ia))))
+		bound = curlwp_bind();
+		error = ip6_get_membership(sopt, &ifp, &psref, &ia, sizeof(ia));
+		if (error != 0) {
+			curlwp_bindx(bound);
 			return error;
+		}
 
 		if (IN6_IS_ADDR_V4MAPPED(&ia)) {
 			error = ip_setmoptions(&in6p->in6p_v4moptions, sopt);
-			break;
+			goto put_break;
 		}
 		/*
 		 * See if we found an interface, and confirm that it
@@ -2563,12 +2577,12 @@ ip6_setmoptions(const struct sockopt *so
 		 */
 		if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
 			error = EADDRNOTAVAIL;
-			break;
+			goto put_break;
 		}
 
 		if (in6_setscope(&ia, ifp, NULL)) {
 			error = EADDRNOTAVAIL; /* XXX: should not happen */
-			break;
+			goto put_break;
 		}
 
 		/*
@@ -2578,11 +2592,11 @@ ip6_setmoptions(const struct sockopt *so
 			if (imm->i6mm_maddr->in6m_ifp == ifp &&
 			    IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
 			    &ia))
-				break;
+				goto put_break;
 		}
 		if (imm != NULL) {
 			error = EADDRINUSE;
-			break;
+			goto put_break;
 		}
 		/*
 		 * Everything looks good; add a new record to the multicast
@@ -2590,9 +2604,13 @@ ip6_setmoptions(const struct sockopt *so
 		 */
 		imm = in6_joingroup(ifp, &ia, &error, 0);
 		if (imm == NULL)
-			break;
+			goto put_break;
 		LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
+	    put_break:
+		if_put(ifp, &psref);
+		curlwp_bindx(bound);
 		break;
+	    }
 
 	case IPV6_LEAVE_GROUP:
 		/*

Index: src/sys/netinet6/ip6_var.h
diff -u src/sys/netinet6/ip6_var.h:1.72 src/sys/netinet6/ip6_var.h:1.73
--- src/sys/netinet6/ip6_var.h:1.72	Tue Feb 14 03:05:06 2017
+++ src/sys/netinet6/ip6_var.h	Thu Mar  2 05:24:23 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip6_var.h,v 1.72 2017/02/14 03:05:06 ozaki-r Exp $	*/
+/*	$NetBSD: ip6_var.h,v 1.73 2017/03/02 05:24:23 ozaki-r Exp $	*/
 /*	$KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $	*/
 
 /*
@@ -404,8 +404,8 @@ int	in6_selectsrc(struct sockaddr_in6 *,
 	   struct ifnet **, struct psref *, struct in6_addr *);
 int in6_selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
 	struct route **, struct rtentry **, bool);
-int	ip6_get_membership(const struct sockopt *, struct ifnet **, void *,
-	size_t);
+int	ip6_get_membership(const struct sockopt *, struct ifnet **,
+	    struct psref *, void *, size_t);
 
 u_int32_t ip6_randomid(void);
 u_int32_t ip6_randomflowlabel(void);

Reply via email to