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);