X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6d2010ae8f7a6078e10b361c6962983bab233e0f..a991bd8d3e7fe02dbca0644054bab73c5b75324a:/bsd/netinet/in_mcast.c diff --git a/bsd/netinet/in_mcast.c b/bsd/netinet/in_mcast.c index 1854fd26e..38a042af9 100644 --- a/bsd/netinet/in_mcast.c +++ b/bsd/netinet/in_mcast.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2010-2011 Apple Inc. All rights reserved. + * Copyright (c) 2010-2020 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,7 +22,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /*- @@ -80,6 +80,7 @@ #include #include +#include #include #include @@ -89,17 +90,6 @@ #include #include -#ifndef __SOCKUNION_DECLARED -union sockunion { - struct sockaddr_storage ss; - struct sockaddr sa; - struct sockaddr_dl sdl; - struct sockaddr_in sin; -}; -typedef union sockunion sockunion_t; -#define __SOCKUNION_DECLARED -#endif /* __SOCKUNION_DECLARED */ - /* * Functions with non-static linkage defined in this file should be * declared in in_var.h: @@ -116,43 +106,43 @@ typedef union sockunion sockunion_t; * XXX: Both carp and pf need to use the legacy (*,G) KPIs in_addmulti() * and in_delmulti(). */ -static void imf_commit(struct in_mfilter *); -static int imf_get_source(struct in_mfilter *imf, - const struct sockaddr_in *psin, - struct in_msource **); +static void imf_commit(struct in_mfilter *); +static int imf_get_source(struct in_mfilter *imf, + const struct sockaddr_in *psin, + struct in_msource **); static struct in_msource * - imf_graft(struct in_mfilter *, const uint8_t, - const struct sockaddr_in *); -static int imf_prune(struct in_mfilter *, const struct sockaddr_in *); -static void imf_rollback(struct in_mfilter *); -static void imf_reap(struct in_mfilter *); -static int imo_grow(struct ip_moptions *, size_t); -static size_t imo_match_group(const struct ip_moptions *, - const struct ifnet *, const struct sockaddr *); +imf_graft(struct in_mfilter *, const uint8_t, + const struct sockaddr_in *); +static int imf_prune(struct in_mfilter *, const struct sockaddr_in *); +static void imf_rollback(struct in_mfilter *); +static void imf_reap(struct in_mfilter *); +static int imo_grow(struct ip_moptions *, uint16_t); +static size_t imo_match_group(const struct ip_moptions *, + const struct ifnet *, const struct sockaddr_in *); static struct in_msource * - imo_match_source(const struct ip_moptions *, const size_t, - const struct sockaddr *); -static void ims_merge(struct ip_msource *ims, - const struct in_msource *lims, const int rollback); -static int in_getmulti(struct ifnet *, const struct in_addr *, - struct in_multi **); -static int in_joingroup(struct ifnet *, const struct in_addr *, - struct in_mfilter *, struct in_multi **); -static int inm_get_source(struct in_multi *inm, const in_addr_t haddr, - const int noalloc, struct ip_msource **pims); -static int inm_is_ifp_detached(const struct in_multi *); -static int inm_merge(struct in_multi *, /*const*/ struct in_mfilter *); -static void inm_reap(struct in_multi *); +imo_match_source(const struct ip_moptions *, const size_t, + const struct sockaddr_in *); +static void ims_merge(struct ip_msource *ims, + const struct in_msource *lims, const int rollback); +static int in_getmulti(struct ifnet *, const struct in_addr *, + struct in_multi **); +static int in_joingroup(struct ifnet *, const struct in_addr *, + struct in_mfilter *, struct in_multi **); +static int inm_get_source(struct in_multi *inm, const in_addr_t haddr, + const int noalloc, struct ip_msource **pims); +static int inm_is_ifp_detached(const struct in_multi *); +static int inm_merge(struct in_multi *, /*const*/ struct in_mfilter *); +static void inm_reap(struct in_multi *); static struct ip_moptions * - inp_findmoptions(struct inpcb *); -static int inp_get_source_filters(struct inpcb *, struct sockopt *); +inp_findmoptions(struct inpcb *); +static int inp_get_source_filters(struct inpcb *, struct sockopt *); static struct ifnet * - inp_lookup_mcast_ifp(const struct inpcb *, - const struct sockaddr_in *, const struct in_addr); -static int inp_block_unblock_source(struct inpcb *, struct sockopt *); -static int inp_set_multicast_if(struct inpcb *, struct sockopt *); -static int inp_set_source_filters(struct inpcb *, struct sockopt *); -static int sysctl_ip_mcast_filters SYSCTL_HANDLER_ARGS; +inp_lookup_mcast_ifp(const struct inpcb *, + const struct sockaddr_in *, const struct in_addr); +static int inp_block_unblock_source(struct inpcb *, struct sockopt *); +static int inp_set_multicast_if(struct inpcb *, struct sockopt *); +static int inp_set_source_filters(struct inpcb *, struct sockopt *); +static int sysctl_ip_mcast_filters SYSCTL_HANDLER_ARGS; static struct ifnet * ip_multicast_if(struct in_addr *, unsigned int *); static __inline__ int ip_msource_cmp(const struct ip_msource *, const struct ip_msource *); @@ -163,13 +153,13 @@ static u_long in_mcast_maxgrpsrc = IP_MAX_GROUP_SRC_FILTER; SYSCTL_LONG(_net_inet_ip_mcast, OID_AUTO, maxgrpsrc, CTLFLAG_RW | CTLFLAG_LOCKED, &in_mcast_maxgrpsrc, "Max source filters per group"); -static u_long in_mcast_maxsocksrc = IP_MAX_SOCK_SRC_FILTER; -SYSCTL_LONG(_net_inet_ip_mcast, OID_AUTO, maxsocksrc, - CTLFLAG_RW | CTLFLAG_LOCKED, &in_mcast_maxsocksrc, +static u_int in_mcast_maxsocksrc = IP_MAX_SOCK_SRC_FILTER; +SYSCTL_UINT(_net_inet_ip_mcast, OID_AUTO, maxsocksrc, + CTLFLAG_RW | CTLFLAG_LOCKED, &in_mcast_maxsocksrc, IP_MAX_SOCK_SRC_FILTER, "Max source filters per socket"); int in_mcast_loop = IP_DEFAULT_MULTICAST_LOOP; -SYSCTL_INT(_net_inet_ip_mcast, OID_AUTO, loop, CTLFLAG_RW | CTLFLAG_LOCKED, +SYSCTL_INT(_net_inet_ip_mcast, OID_AUTO, loop, CTLFLAG_RW | CTLFLAG_LOCKED, &in_mcast_loop, 0, "Loopback multicast datagrams by default"); SYSCTL_NODE(_net_inet_ip_mcast, OID_AUTO, filters, @@ -178,20 +168,20 @@ SYSCTL_NODE(_net_inet_ip_mcast, OID_AUTO, filters, RB_GENERATE_PREV(ip_msource_tree, ip_msource, ims_link, ip_msource_cmp); -#define INM_TRACE_HIST_SIZE 32 /* size of trace history */ +#define INM_TRACE_HIST_SIZE 32 /* size of trace history */ /* For gdb */ __private_extern__ unsigned int inm_trace_hist_size = INM_TRACE_HIST_SIZE; struct in_multi_dbg { - struct in_multi inm; /* in_multi */ - u_int16_t inm_refhold_cnt; /* # of ref */ - u_int16_t inm_refrele_cnt; /* # of rele */ + struct in_multi inm; /* in_multi */ + u_int16_t inm_refhold_cnt; /* # of ref */ + u_int16_t inm_refrele_cnt; /* # of rele */ /* * Circular lists of inm_addref and inm_remref callers. */ - ctrace_t inm_refhold[INM_TRACE_HIST_SIZE]; - ctrace_t inm_refrele[INM_TRACE_HIST_SIZE]; + ctrace_t inm_refhold[INM_TRACE_HIST_SIZE]; + ctrace_t inm_refrele[INM_TRACE_HIST_SIZE]; /* * Trash list linkage */ @@ -202,59 +192,48 @@ struct in_multi_dbg { static TAILQ_HEAD(, in_multi_dbg) inm_trash_head; static decl_lck_mtx_data(, inm_trash_lock); -#define INM_ZONE_MAX 64 /* maximum elements in zone */ -#define INM_ZONE_NAME "in_multi" /* zone name */ #if DEBUG -static unsigned int inm_debug = 1; /* debugging (enabled) */ +static unsigned int inm_debug = 1; /* debugging (enabled) */ #else -static unsigned int inm_debug; /* debugging (disabled) */ +static unsigned int inm_debug; /* debugging (disabled) */ #endif /* !DEBUG */ -static unsigned int inm_size; /* size of zone element */ -static struct zone *inm_zone; /* zone for in_multi */ - -#define IPMS_ZONE_MAX 64 /* maximum elements in zone */ -#define IPMS_ZONE_NAME "ip_msource" /* zone name */ - -static unsigned int ipms_size; /* size of zone element */ -static struct zone *ipms_zone; /* zone for ip_msource */ - -#define INMS_ZONE_MAX 64 /* maximum elements in zone */ -#define INMS_ZONE_NAME "in_msource" /* zone name */ +#define INM_ZONE_NAME "in_multi" /* zone name */ +static struct zone *inm_zone; /* zone for in_multi */ -static unsigned int inms_size; /* size of zone element */ -static struct zone *inms_zone; /* zone for in_msource */ +static ZONE_DECLARE(ipms_zone, "ip_msource", sizeof(struct ip_msource), + ZC_ZFREE_CLEARMEM); +static ZONE_DECLARE(inms_zone, "in_msource", sizeof(struct in_msource), + ZC_ZFREE_CLEARMEM); /* Lock group and attribute for in_multihead_lock lock */ -static lck_attr_t *in_multihead_lock_attr; -static lck_grp_t *in_multihead_lock_grp; -static lck_grp_attr_t *in_multihead_lock_grp_attr; +static lck_attr_t *in_multihead_lock_attr; +static lck_grp_t *in_multihead_lock_grp; +static lck_grp_attr_t *in_multihead_lock_grp_attr; static decl_lck_rw_data(, in_multihead_lock); struct in_multihead in_multihead; -static struct in_multi *in_multi_alloc(int); +static struct in_multi *in_multi_alloc(zalloc_flags_t); static void in_multi_free(struct in_multi *); static void in_multi_attach(struct in_multi *); static void inm_trace(struct in_multi *, int); -static struct ip_msource *ipms_alloc(int); +static struct ip_msource *ipms_alloc(zalloc_flags_t); static void ipms_free(struct ip_msource *); -static struct in_msource *inms_alloc(int); +static struct in_msource *inms_alloc(zalloc_flags_t); static void inms_free(struct in_msource *); -#define IMO_CAST_TO_NONCONST(x) ((struct ip_moptions *)(void *)(uintptr_t)x) -#define INM_CAST_TO_NONCONST(x) ((struct in_multi *)(void *)(uintptr_t)x) - static __inline int ip_msource_cmp(const struct ip_msource *a, const struct ip_msource *b) { - - if (a->ims_haddr < b->ims_haddr) - return (-1); - if (a->ims_haddr == b->ims_haddr) - return (0); - return (1); + if (a->ims_haddr < b->ims_haddr) { + return -1; + } + if (a->ims_haddr == b->ims_haddr) { + return 0; + } + return 1; } /* @@ -266,7 +245,7 @@ inm_is_ifp_detached(const struct in_multi *inm) VERIFY(inm->inm_ifma != NULL); VERIFY(inm->inm_ifp == inm->inm_ifma->ifma_ifp); - return (!ifnet_is_attached(inm->inm_ifp, 0)); + return !ifnet_is_attached(inm->inm_ifp, 0); } /* @@ -274,7 +253,7 @@ inm_is_ifp_detached(const struct in_multi *inm) * with an empty source filter list. */ static __inline__ void -imf_init(struct in_mfilter *imf, const int st0, const int st1) +imf_init(struct in_mfilter *imf, const uint8_t st0, const uint8_t st1) { memset(imf, 0, sizeof(struct in_mfilter)); RB_INIT(&imf->imf_sources); @@ -286,14 +265,14 @@ imf_init(struct in_mfilter *imf, const int st0, const int st1) * Resize the ip_moptions vector to the next power-of-two minus 1. */ static int -imo_grow(struct ip_moptions *imo, size_t newmax) +imo_grow(struct ip_moptions *imo, uint16_t newmax) { - struct in_multi **nmships; - struct in_multi **omships; - struct in_mfilter *nmfilters; - struct in_mfilter *omfilters; - size_t idx; - size_t oldmax; + struct in_multi **nmships; + struct in_multi **omships; + struct in_mfilter *nmfilters; + struct in_mfilter *omfilters; + uint16_t idx; + uint16_t oldmax; IMO_LOCK_ASSERT_HELD(imo); @@ -302,33 +281,38 @@ imo_grow(struct ip_moptions *imo, size_t newmax) omships = imo->imo_membership; omfilters = imo->imo_mfilters; oldmax = imo->imo_max_memberships; - if (newmax == 0) + if (newmax == 0) { newmax = ((oldmax + 1) * 2) - 1; + } - if (newmax > IP_MAX_MEMBERSHIPS) - return (ETOOMANYREFS); + if (newmax > IP_MAX_MEMBERSHIPS) { + return ETOOMANYREFS; + } if ((nmships = (struct in_multi **)_REALLOC(omships, - sizeof (struct in_multi *) * newmax, M_IPMOPTS, - M_WAITOK | M_ZERO)) == NULL) - return (ENOMEM); + sizeof(struct in_multi *) * newmax, M_IPMOPTS, + M_WAITOK | M_ZERO)) == NULL) { + return ENOMEM; + } imo->imo_membership = nmships; if ((nmfilters = (struct in_mfilter *)_REALLOC(omfilters, - sizeof (struct in_mfilter) * newmax, M_INMFILTER, - M_WAITOK | M_ZERO)) == NULL) - return (ENOMEM); + sizeof(struct in_mfilter) * newmax, M_INMFILTER, + M_WAITOK | M_ZERO)) == NULL) { + return ENOMEM; + } imo->imo_mfilters = nmfilters; /* Initialize newly allocated source filter heads. */ - for (idx = oldmax; idx < newmax; idx++) + for (idx = oldmax; idx < newmax; idx++) { imf_init(&nmfilters[idx], MCAST_UNDEFINED, MCAST_EXCLUDE); + } imo->imo_max_memberships = newmax; - return (0); + return 0; } /* @@ -338,38 +322,39 @@ imo_grow(struct ip_moptions *imo, size_t newmax) */ static size_t imo_match_group(const struct ip_moptions *imo, const struct ifnet *ifp, - const struct sockaddr *group) + const struct sockaddr_in *group) { - const struct sockaddr_in *gsin; - struct in_multi *pinm; - int idx; - int nmships; + struct in_multi *pinm; + int idx; + int nmships; - IMO_LOCK_ASSERT_HELD(IMO_CAST_TO_NONCONST(imo)); + IMO_LOCK_ASSERT_HELD(__DECONST(struct ip_moptions *, imo)); - gsin = (const struct sockaddr_in *)group; /* The imo_membership array may be lazy allocated. */ - if (imo->imo_membership == NULL || imo->imo_num_memberships == 0) - return (-1); + if (imo->imo_membership == NULL || imo->imo_num_memberships == 0) { + return -1; + } nmships = imo->imo_num_memberships; for (idx = 0; idx < nmships; idx++) { pinm = imo->imo_membership[idx]; - if (pinm == NULL) + if (pinm == NULL) { continue; + } INM_LOCK(pinm); if ((ifp == NULL || (pinm->inm_ifp == ifp)) && - in_hosteq(pinm->inm_addr, gsin->sin_addr)) { + in_hosteq(pinm->inm_addr, group->sin_addr)) { INM_UNLOCK(pinm); break; } INM_UNLOCK(pinm); } - if (idx >= nmships) + if (idx >= nmships) { idx = -1; + } - return (idx); + return idx; } /* @@ -381,29 +366,28 @@ imo_match_group(const struct ip_moptions *imo, const struct ifnet *ifp, */ static struct in_msource * imo_match_source(const struct ip_moptions *imo, const size_t gidx, - const struct sockaddr *src) + const struct sockaddr_in *src) { - struct ip_msource find; - struct in_mfilter *imf; - struct ip_msource *ims; - const sockunion_t *psa; + struct ip_msource find; + struct in_mfilter *imf; + struct ip_msource *ims; - IMO_LOCK_ASSERT_HELD(IMO_CAST_TO_NONCONST(imo)); + IMO_LOCK_ASSERT_HELD(__DECONST(struct ip_moptions *, imo)); - VERIFY(src->sa_family == AF_INET); + VERIFY(src->sin_family == AF_INET); VERIFY(gidx != (size_t)-1 && gidx < imo->imo_num_memberships); /* The imo_mfilters array may be lazy allocated. */ - if (imo->imo_mfilters == NULL) - return (NULL); + if (imo->imo_mfilters == NULL) { + return NULL; + } imf = &imo->imo_mfilters[gidx]; /* Source trees are keyed in host byte order. */ - psa = (const sockunion_t *)src; - find.ims_haddr = ntohl(psa->sin.sin_addr.s_addr); + find.ims_haddr = ntohl(src->sin_addr.s_addr); ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find); - return ((struct in_msource *)ims); + return (struct in_msource *)ims; } /* @@ -414,18 +398,19 @@ imo_match_source(const struct ip_moptions *imo, const size_t gidx, */ int imo_multi_filter(const struct ip_moptions *imo, const struct ifnet *ifp, - const struct sockaddr *group, const struct sockaddr *src) + const struct sockaddr_in *group, const struct sockaddr_in *src) { size_t gidx; struct in_msource *ims; int mode; - IMO_LOCK_ASSERT_HELD(IMO_CAST_TO_NONCONST(imo)); + IMO_LOCK_ASSERT_HELD(__DECONST(struct ip_moptions *, imo)); VERIFY(ifp != NULL); gidx = imo_match_group(imo, ifp, group); - if (gidx == (size_t)-1) - return (MCAST_NOTGMEMBER); + if (gidx == (size_t)-1) { + return MCAST_NOTGMEMBER; + } /* * Check if the source was included in an (S,G) join. @@ -441,24 +426,37 @@ imo_multi_filter(const struct ip_moptions *imo, const struct ifnet *ifp, if ((ims == NULL && mode == MCAST_INCLUDE) || (ims != NULL && ims->imsl_st[0] != mode)) { - return (MCAST_NOTSMEMBER); + return MCAST_NOTSMEMBER; } - return (MCAST_PASS); + return MCAST_PASS; } int -imo_clone(struct ip_moptions *from, struct ip_moptions *to) +imo_clone(struct inpcb *from_inp, struct inpcb *to_inp) { int i, err = 0; + struct ip_moptions *from; + struct ip_moptions *to; + + from = inp_findmoptions(from_inp); + if (from == NULL) { + return ENOMEM; + } + + to = inp_findmoptions(to_inp); + if (to == NULL) { + IMO_REMREF(from); + return ENOMEM; + } IMO_LOCK(from); IMO_LOCK(to); - to->imo_multicast_ifp = from->imo_multicast_ifp; - to->imo_multicast_vif = from->imo_multicast_vif; - to->imo_multicast_ttl = from->imo_multicast_ttl; - to->imo_multicast_loop = from->imo_multicast_loop; + to->imo_multicast_ifp = from->imo_multicast_ifp; + to->imo_multicast_vif = from->imo_multicast_vif; + to->imo_multicast_ttl = from->imo_multicast_ttl; + to->imo_multicast_loop = from->imo_multicast_loop; /* * We're cloning, so drop any existing memberships and source @@ -468,13 +466,15 @@ imo_clone(struct ip_moptions *from, struct ip_moptions *to) struct in_mfilter *imf; imf = to->imo_mfilters ? &to->imo_mfilters[i] : NULL; - if (imf != NULL) + if (imf != NULL) { imf_leave(imf); + } (void) in_leavegroup(to->imo_membership[i], imf); - if (imf != NULL) + if (imf != NULL) { imf_purge(imf); + } INM_REMREF(to->imo_membership[i]); to->imo_membership[i] = NULL; @@ -488,8 +488,9 @@ imo_clone(struct ip_moptions *from, struct ip_moptions *to) * and source filters arrays are at least equal in size. */ err = imo_grow(to, from->imo_max_memberships); - if (err != 0) + if (err != 0) { goto done; + } } VERIFY(to->imo_max_memberships >= from->imo_max_memberships); @@ -497,18 +498,24 @@ imo_clone(struct ip_moptions *from, struct ip_moptions *to) * Source filtering doesn't apply to OpenTransport socket, * so simply hold additional reference count per membership. */ - for (i = 0; i < from->imo_num_memberships; i++) { - to->imo_membership[i] = from->imo_membership[i]; - INM_ADDREF(from->imo_membership[i]); + for (i = 0; i < from->imo_num_memberships; i++) { + to->imo_membership[i] = + in_addmulti(&from->imo_membership[i]->inm_addr, + from->imo_membership[i]->inm_ifp); + if (to->imo_membership[i] == NULL) { + break; + } to->imo_num_memberships++; - } + } VERIFY(to->imo_num_memberships == from->imo_num_memberships); done: IMO_UNLOCK(to); + IMO_REMREF(to); IMO_UNLOCK(from); + IMO_REMREF(from); - return (err); + return err; } /* @@ -522,10 +529,10 @@ static int in_getmulti(struct ifnet *ifp, const struct in_addr *group, struct in_multi **pinm) { - struct sockaddr_in gsin; - struct ifmultiaddr *ifma; - struct in_multi *inm; - int error; + struct sockaddr_in gsin; + struct ifmultiaddr *ifma; + struct in_multi *inm; + int error; in_multihead_lock_shared(); IN_LOOKUP_MULTI(group, ifp, inm); @@ -541,7 +548,7 @@ in_getmulti(struct ifnet *ifp, const struct in_addr *group, * We already joined this group; return the inm * with a refcount held (via lookup) for caller. */ - return (0); + return 0; } in_multihead_lock_done(); @@ -555,8 +562,9 @@ in_getmulti(struct ifnet *ifp, const struct in_addr *group, * with this network-layer group on the given ifnet. */ error = if_addmulti(ifp, (struct sockaddr *)&gsin, &ifma); - if (error != 0) - return (error); + if (error != 0) { + return error; + } /* * See comments in inm_remref() for access to ifma_protospec. @@ -566,7 +574,7 @@ in_getmulti(struct ifnet *ifp, const struct in_addr *group, if ((inm = ifma->ifma_protospec) != NULL) { VERIFY(ifma->ifma_addr != NULL); VERIFY(ifma->ifma_addr->sa_family == AF_INET); - INM_ADDREF(inm); /* for caller */ + INM_ADDREF(inm); /* for caller */ IFMA_UNLOCK(ifma); INM_LOCK(inm); VERIFY(inm->inm_ifma == ifma); @@ -586,7 +594,7 @@ in_getmulti(struct ifnet *ifp, const struct in_addr *group, * been joined; return the inm with a refcount * held for caller. */ - return (0); + return 0; } /* * We lost the race with another thread doing in_delmulti(); @@ -601,7 +609,7 @@ in_getmulti(struct ifnet *ifp, const struct in_addr *group, INM_UNLOCK(inm); in_multihead_lock_done(); IFMA_REMREF(ifma); - return (0); + return 0; } IFMA_UNLOCK(ifma); @@ -612,19 +620,15 @@ in_getmulti(struct ifnet *ifp, const struct in_addr *group, * * The initial source filter state is INCLUDE, {} as per the RFC. */ - inm = in_multi_alloc(M_WAITOK); - if (inm == NULL) { - in_multihead_lock_done(); - IFMA_REMREF(ifma); - return (ENOMEM); - } + inm = in_multi_alloc(Z_WAITOK); + INM_LOCK(inm); inm->inm_addr = *group; inm->inm_ifp = ifp; inm->inm_igi = IGMP_IFINFO(ifp); VERIFY(inm->inm_igi != NULL); IGI_ADDREF(inm->inm_igi); - inm->inm_ifma = ifma; /* keep refcount from if_addmulti() */ + inm->inm_ifma = ifma; /* keep refcount from if_addmulti() */ inm->inm_state = IGMP_NOT_MEMBER; /* * Pending state-changes per group are subject to a bounds check. @@ -636,7 +640,7 @@ in_getmulti(struct ifnet *ifp, const struct in_addr *group, *pinm = inm; in_multi_attach(inm); VERIFY((inm->inm_debug & (IFD_ATTACHED | IFD_TRASHED)) == IFD_ATTACHED); - INM_ADDREF_LOCKED(inm); /* for caller */ + INM_ADDREF_LOCKED(inm); /* for caller */ INM_UNLOCK(inm); IFMA_LOCK(ifma); @@ -645,7 +649,7 @@ in_getmulti(struct ifnet *ifp, const struct in_addr *group, IFMA_UNLOCK(ifma); in_multihead_lock_done(); - return (0); + return 0; } /* @@ -656,7 +660,7 @@ in_getmulti(struct ifnet *ifp, const struct in_addr *group, void inm_clear_recorded(struct in_multi *inm) { - struct ip_msource *ims; + struct ip_msource *ims; INM_LOCK_ASSERT_HELD(inm); @@ -690,21 +694,21 @@ inm_clear_recorded(struct in_multi *inm) int inm_record_source(struct in_multi *inm, const in_addr_t naddr) { - struct ip_msource find; - struct ip_msource *ims, *nims; + struct ip_msource find; + struct ip_msource *ims, *nims; INM_LOCK_ASSERT_HELD(inm); find.ims_haddr = ntohl(naddr); ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find); - if (ims && ims->ims_stp) - return (0); + if (ims && ims->ims_stp) { + return 0; + } if (ims == NULL) { - if (inm->inm_nsrc == in_mcast_maxgrpsrc) - return (-ENOSPC); - nims = ipms_alloc(M_WAITOK); - if (nims == NULL) - return (-ENOMEM); + if (inm->inm_nsrc == in_mcast_maxgrpsrc) { + return -ENOSPC; + } + nims = ipms_alloc(Z_WAITOK); nims->ims_haddr = find.ims_haddr; RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims); ++inm->inm_nsrc; @@ -718,7 +722,7 @@ inm_record_source(struct in_multi *inm, const in_addr_t naddr) ++ims->ims_stp; ++inm->inm_st[1].iss_rec; - return (1); + return 1; } /* @@ -736,10 +740,10 @@ static int imf_get_source(struct in_mfilter *imf, const struct sockaddr_in *psin, struct in_msource **plims) { - struct ip_msource find; - struct ip_msource *ims; - struct in_msource *lims; - int error; + struct ip_msource find; + struct ip_msource *ims; + struct in_msource *lims; + int error; error = 0; ims = NULL; @@ -750,11 +754,10 @@ imf_get_source(struct in_mfilter *imf, const struct sockaddr_in *psin, ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find); lims = (struct in_msource *)ims; if (lims == NULL) { - if (imf->imf_nsrc == in_mcast_maxsocksrc) - return (ENOSPC); - lims = inms_alloc(M_WAITOK); - if (lims == NULL) - return (ENOMEM); + if (imf->imf_nsrc == in_mcast_maxsocksrc) { + return ENOSPC; + } + lims = inms_alloc(Z_WAITOK); lims->ims_haddr = find.ims_haddr; lims->imsl_st[0] = MCAST_UNDEFINED; RB_INSERT(ip_msource_tree, &imf->imf_sources, @@ -764,7 +767,7 @@ imf_get_source(struct in_mfilter *imf, const struct sockaddr_in *psin, *plims = lims; - return (error); + return error; } /* @@ -781,11 +784,9 @@ static struct in_msource * imf_graft(struct in_mfilter *imf, const uint8_t st1, const struct sockaddr_in *psin) { - struct in_msource *lims; + struct in_msource *lims; - lims = inms_alloc(M_WAITOK); - if (lims == NULL) - return (NULL); + lims = inms_alloc(Z_WAITOK); lims->ims_haddr = ntohl(psin->sin_addr.s_addr); lims->imsl_st[0] = MCAST_UNDEFINED; lims->imsl_st[1] = st1; @@ -793,7 +794,7 @@ imf_graft(struct in_mfilter *imf, const uint8_t st1, (struct ip_msource *)lims); ++imf->imf_nsrc; - return (lims); + return lims; } /* @@ -809,18 +810,19 @@ imf_graft(struct in_mfilter *imf, const uint8_t st1, static int imf_prune(struct in_mfilter *imf, const struct sockaddr_in *psin) { - struct ip_msource find; - struct ip_msource *ims; - struct in_msource *lims; + struct ip_msource find; + struct ip_msource *ims; + struct in_msource *lims; /* key is host byte order */ find.ims_haddr = ntohl(psin->sin_addr.s_addr); ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find); - if (ims == NULL) - return (ENOENT); + if (ims == NULL) { + return ENOENT; + } lims = (struct in_msource *)ims; lims->imsl_st[1] = MCAST_UNDEFINED; - return (0); + return 0; } /* @@ -831,8 +833,8 @@ imf_prune(struct in_mfilter *imf, const struct sockaddr_in *psin) static void imf_rollback(struct in_mfilter *imf) { - struct ip_msource *ims, *tims; - struct in_msource *lims; + struct ip_msource *ims, *tims; + struct in_msource *lims; RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) { lims = (struct in_msource *)ims; @@ -844,7 +846,8 @@ imf_rollback(struct in_mfilter *imf) lims->imsl_st[1] = lims->imsl_st[0]; } else { /* revert source added t1 */ - IGMP_PRINTF(("%s: free inms %p\n", __func__, lims)); + IGMP_PRINTF(("%s: free inms 0x%llx\n", __func__, + (uint64_t)VM_KERNEL_ADDRPERM(lims))); RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims); inms_free(lims); imf->imf_nsrc--; @@ -861,8 +864,8 @@ imf_rollback(struct in_mfilter *imf) void imf_leave(struct in_mfilter *imf) { - struct ip_msource *ims; - struct in_msource *lims; + struct ip_msource *ims; + struct in_msource *lims; RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { lims = (struct in_msource *)ims; @@ -879,8 +882,8 @@ imf_leave(struct in_mfilter *imf) static void imf_commit(struct in_mfilter *imf) { - struct ip_msource *ims; - struct in_msource *lims; + struct ip_msource *ims; + struct in_msource *lims; RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { lims = (struct in_msource *)ims; @@ -897,14 +900,15 @@ imf_commit(struct in_mfilter *imf) static void imf_reap(struct in_mfilter *imf) { - struct ip_msource *ims, *tims; - struct in_msource *lims; + struct ip_msource *ims, *tims; + struct in_msource *lims; RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) { lims = (struct in_msource *)ims; if ((lims->imsl_st[0] == MCAST_UNDEFINED) && (lims->imsl_st[1] == MCAST_UNDEFINED)) { - IGMP_PRINTF(("%s: free inms %p\n", __func__, lims)); + IGMP_PRINTF(("%s: free inms 0x%llx\n", __func__, + (uint64_t)VM_KERNEL_ADDRPERM(lims))); RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims); inms_free(lims); imf->imf_nsrc--; @@ -920,12 +924,13 @@ imf_reap(struct in_mfilter *imf) void imf_purge(struct in_mfilter *imf) { - struct ip_msource *ims, *tims; - struct in_msource *lims; + struct ip_msource *ims, *tims; + struct in_msource *lims; RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) { lims = (struct in_msource *)ims; - IGMP_PRINTF(("%s: free inms %p\n", __func__, lims)); + IGMP_PRINTF(("%s: free inms 0x%llx\n", __func__, + (uint64_t)VM_KERNEL_ADDRPERM(lims))); RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims); inms_free(lims); imf->imf_nsrc--; @@ -948,34 +953,35 @@ static int inm_get_source(struct in_multi *inm, const in_addr_t haddr, const int noalloc, struct ip_msource **pims) { - struct ip_msource find; - struct ip_msource *ims, *nims; + struct ip_msource find; + struct ip_msource *ims, *nims; #ifdef IGMP_DEBUG struct in_addr ia; + char buf[MAX_IPv4_STR_LEN]; #endif INM_LOCK_ASSERT_HELD(inm); find.ims_haddr = haddr; ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find); if (ims == NULL && !noalloc) { - if (inm->inm_nsrc == in_mcast_maxgrpsrc) - return (ENOSPC); - nims = ipms_alloc(M_WAITOK); - if (nims == NULL) - return (ENOMEM); + if (inm->inm_nsrc == in_mcast_maxgrpsrc) { + return ENOSPC; + } + nims = ipms_alloc(Z_WAITOK); nims->ims_haddr = haddr; RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims); ++inm->inm_nsrc; ims = nims; #ifdef IGMP_DEBUG ia.s_addr = htonl(haddr); - IGMP_PRINTF(("%s: allocated %s as %p\n", __func__, - inet_ntoa(ia), ims)); + inet_ntop(AF_INET, &ia, buf, sizeof(buf)); + IGMP_PRINTF(("%s: allocated %s as 0x%llx\n", __func__, + buf, (uint64_t)VM_KERNEL_ADDRPERM(ims))); #endif } *pims = ims; - return (0); + return 0; } /* @@ -990,15 +996,16 @@ uint8_t ims_get_mode(const struct in_multi *inm, const struct ip_msource *ims, uint8_t t) { - INM_LOCK_ASSERT_HELD(INM_CAST_TO_NONCONST(inm)); + INM_LOCK_ASSERT_HELD(__DECONST(struct in_multi *, inm)); t = !!t; if (inm->inm_st[t].iss_ex > 0 && - inm->inm_st[t].iss_ex == ims->ims_st[t].ex) - return (MCAST_EXCLUDE); - else if (ims->ims_st[t].in > 0 && ims->ims_st[t].ex == 0) - return (MCAST_INCLUDE); - return (MCAST_UNDEFINED); + inm->inm_st[t].iss_ex == ims->ims_st[t].ex) { + return MCAST_EXCLUDE; + } else if (ims->ims_st[t].in > 0 && ims->ims_st[t].ex == 0) { + return MCAST_INCLUDE; + } + return MCAST_UNDEFINED; } /* @@ -1017,22 +1024,26 @@ ims_merge(struct ip_msource *ims, const struct in_msource *lims, #endif if (lims->imsl_st[0] == MCAST_EXCLUDE) { - IGMP_PRINTF(("%s: t1 ex -= %d on %s\n", - __func__, n, inet_ntoa(ia))); + IGMP_INET_PRINTF(ia, + ("%s: t1 ex -= %d on %s\n", + __func__, n, _igmp_inet_buf)); ims->ims_st[1].ex -= n; } else if (lims->imsl_st[0] == MCAST_INCLUDE) { - IGMP_PRINTF(("%s: t1 in -= %d on %s\n", - __func__, n, inet_ntoa(ia))); + IGMP_INET_PRINTF(ia, + ("%s: t1 in -= %d on %s\n", + __func__, n, _igmp_inet_buf)); ims->ims_st[1].in -= n; } if (lims->imsl_st[1] == MCAST_EXCLUDE) { - IGMP_PRINTF(("%s: t1 ex += %d on %s\n", - __func__, n, inet_ntoa(ia))); + IGMP_INET_PRINTF(ia, + ("%s: t1 ex += %d on %s\n", + __func__, n, _igmp_inet_buf)); ims->ims_st[1].ex += n; } else if (lims->imsl_st[1] == MCAST_INCLUDE) { - IGMP_PRINTF(("%s: t1 in += %d on %s\n", - __func__, n, inet_ntoa(ia))); + IGMP_INET_PRINTF(ia, + ("%s: t1 in += %d on %s\n", + __func__, n, _igmp_inet_buf)); ims->ims_st[1].in += n; } } @@ -1054,10 +1065,10 @@ ims_merge(struct ip_msource *ims, const struct in_msource *lims, static int inm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf) { - struct ip_msource *ims, *nims; - struct in_msource *lims; - int schanged, error; - int nsrc0, nsrc1; + struct ip_msource *ims, *nims = NULL; + struct in_msource *lims; + int schanged, error; + int nsrc0, nsrc1; INM_LOCK_ASSERT_HELD(inm); @@ -1074,13 +1085,20 @@ inm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf) */ RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { lims = (struct in_msource *)ims; - if (lims->imsl_st[0] == imf->imf_st[0]) nsrc0++; - if (lims->imsl_st[1] == imf->imf_st[1]) nsrc1++; - if (lims->imsl_st[0] == lims->imsl_st[1]) continue; + if (lims->imsl_st[0] == imf->imf_st[0]) { + nsrc0++; + } + if (lims->imsl_st[1] == imf->imf_st[1]) { + nsrc1++; + } + if (lims->imsl_st[0] == lims->imsl_st[1]) { + continue; + } error = inm_get_source(inm, lims->ims_haddr, 0, &nims); ++schanged; - if (error) + if (error) { break; + } ims_merge(nims, lims, 0); } if (error) { @@ -1088,11 +1106,13 @@ inm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf) RB_FOREACH_REVERSE_FROM(ims, ip_msource_tree, nims) { lims = (struct in_msource *)ims; - if (lims->imsl_st[0] == lims->imsl_st[1]) + if (lims->imsl_st[0] == lims->imsl_st[1]) { continue; + } (void) inm_get_source(inm, lims->ims_haddr, 1, &bims); - if (bims == NULL) + if (bims == NULL) { continue; + } ims_merge(bims, lims, 1); } goto out_reap; @@ -1166,7 +1186,9 @@ inm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf) inm->inm_st[1].iss_asm++; } - IGMP_PRINTF(("%s: merged imf %p to inm %p\n", __func__, imf, inm)); + IGMP_PRINTF(("%s: merged imf 0x%llx to inm 0x%llx\n", __func__, + (uint64_t)VM_KERNEL_ADDRPERM(imf), + (uint64_t)VM_KERNEL_ADDRPERM(inm))); inm_print(inm); out_reap: @@ -1174,7 +1196,7 @@ out_reap: IGMP_PRINTF(("%s: sources changed; reaping\n", __func__)); inm_reap(inm); } - return (error); + return error; } /* @@ -1184,11 +1206,12 @@ out_reap: void inm_commit(struct in_multi *inm) { - struct ip_msource *ims; + struct ip_msource *ims; INM_LOCK_ASSERT_HELD(inm); - IGMP_PRINTF(("%s: commit inm %p\n", __func__, inm)); + IGMP_PRINTF(("%s: commit inm 0x%llx\n", __func__, + (uint64_t)VM_KERNEL_ADDRPERM(inm))); IGMP_PRINTF(("%s: pre commit:\n", __func__)); inm_print(inm); @@ -1204,16 +1227,18 @@ inm_commit(struct in_multi *inm) static void inm_reap(struct in_multi *inm) { - struct ip_msource *ims, *tims; + struct ip_msource *ims, *tims; INM_LOCK_ASSERT_HELD(inm); RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) { if (ims->ims_st[0].ex > 0 || ims->ims_st[0].in > 0 || ims->ims_st[1].ex > 0 || ims->ims_st[1].in > 0 || - ims->ims_stp != 0) + ims->ims_stp != 0) { continue; - IGMP_PRINTF(("%s: free ims %p\n", __func__, ims)); + } + IGMP_PRINTF(("%s: free ims 0x%llx\n", __func__, + (uint64_t)VM_KERNEL_ADDRPERM(ims))); RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims); ipms_free(ims); inm->inm_nsrc--; @@ -1226,12 +1251,13 @@ inm_reap(struct in_multi *inm) void inm_purge(struct in_multi *inm) { - struct ip_msource *ims, *tims; + struct ip_msource *ims, *tims; INM_LOCK_ASSERT_HELD(inm); RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) { - IGMP_PRINTF(("%s: free ims %p\n", __func__, ims)); + IGMP_PRINTF(("%s: free ims 0x%llx\n", __func__, + (uint64_t)VM_KERNEL_ADDRPERM(ims))); RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims); ipms_free(ims); inm->inm_nsrc--; @@ -1251,13 +1277,15 @@ static int in_joingroup(struct ifnet *ifp, const struct in_addr *gina, /*const*/ struct in_mfilter *imf, struct in_multi **pinm) { - struct in_mfilter timf; - struct in_multi *inm = NULL; - int error = 0; + struct in_mfilter timf; + struct in_multi *inm = NULL; + int error = 0; + struct igmp_tparams itp; - IGMP_PRINTF(("%s: join %s on %p(%s%d))\n", __func__, - inet_ntoa(*gina), ifp, ifp->if_name, ifp->if_unit)); + IGMP_INET_PRINTF(*gina, ("%s: join %s on 0x%llx(%s))\n", __func__, + _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp))); + bzero(&itp, sizeof(itp)); *pinm = NULL; /* @@ -1272,7 +1300,7 @@ in_joingroup(struct ifnet *ifp, const struct in_addr *gina, error = in_getmulti(ifp, gina, &inm); if (error) { IGMP_PRINTF(("%s: in_getmulti() failure\n", __func__)); - return (error); + return error; } IGMP_PRINTF(("%s: merge inm state\n", __func__)); @@ -1285,23 +1313,28 @@ in_joingroup(struct ifnet *ifp, const struct in_addr *gina, } IGMP_PRINTF(("%s: doing igmp downcall\n", __func__)); - error = igmp_change_state(inm); + error = igmp_change_state(inm, &itp); if (error) { IGMP_PRINTF(("%s: failed to update source\n", __func__)); + imf_rollback(imf); goto out_inm_release; } out_inm_release: if (error) { - IGMP_PRINTF(("%s: dropping ref on %p\n", __func__, inm)); + IGMP_PRINTF(("%s: dropping ref on 0x%llx\n", __func__, + (uint64_t)VM_KERNEL_ADDRPERM(inm))); INM_UNLOCK(inm); INM_REMREF(inm); } else { INM_UNLOCK(inm); - *pinm = inm; /* keep refcount from in_getmulti() */ + *pinm = inm; /* keep refcount from in_getmulti() */ } - return (error); + /* schedule timer now that we've dropped the lock(s) */ + igmp_set_timeout(&itp); + + return error; } /* @@ -1316,20 +1349,23 @@ out_inm_release: int in_leavegroup(struct in_multi *inm, /*const*/ struct in_mfilter *imf) { - struct in_mfilter timf; - int error, lastref; + struct in_mfilter timf; + int error, lastref; + struct igmp_tparams itp; + bzero(&itp, sizeof(itp)); error = 0; INM_LOCK_ASSERT_NOTHELD(inm); - in_multihead_lock_exclusive(); - INM_LOCK(inm); + in_multihead_lock_exclusive(); + INM_LOCK(inm); - IGMP_PRINTF(("%s: leave inm %p, %s/%s%d, imf %p\n", __func__, - inm, inet_ntoa(inm->inm_addr), + IGMP_INET_PRINTF(inm->inm_addr, + ("%s: leave inm 0x%llx, %s/%s%d, imf 0x%llx\n", __func__, + (uint64_t)VM_KERNEL_ADDRPERM(inm), _igmp_inet_buf, (inm_is_ifp_detached(inm) ? "null" : inm->inm_ifp->if_name), - inm->inm_ifp->if_unit, imf)); + inm->inm_ifp->if_unit, (uint64_t)VM_KERNEL_ADDRPERM(imf))); /* * If no imf was specified (i.e. kernel consumer), @@ -1353,21 +1389,25 @@ in_leavegroup(struct in_multi *inm, /*const*/ struct in_mfilter *imf) KASSERT(error == 0, ("%s: failed to merge inm state\n", __func__)); IGMP_PRINTF(("%s: doing igmp downcall\n", __func__)); - error = igmp_change_state(inm); + error = igmp_change_state(inm, &itp); #if IGMP_DEBUG - if (error) + if (error) { IGMP_PRINTF(("%s: failed igmp downcall\n", __func__)); + } #endif - lastref = in_multi_detach(inm); - VERIFY(!lastref || (!(inm->inm_debug & IFD_ATTACHED) && - inm->inm_reqcnt == 0)); + lastref = in_multi_detach(inm); + VERIFY(!lastref || (!(inm->inm_debug & IFD_ATTACHED) && + inm->inm_reqcnt == 0)); INM_UNLOCK(inm); - in_multihead_lock_done(); + in_multihead_lock_done(); - if (lastref) - INM_REMREF(inm); /* for in_multihead list */ + if (lastref) { + INM_REMREF(inm); /* for in_multihead list */ + } + /* schedule timer now that we've dropped the lock(s) */ + igmp_set_timeout(&itp); - return (error); + return error; } /* @@ -1387,7 +1427,7 @@ in_addmulti(struct in_addr *ap, struct ifnet *ifp) error = in_joingroup(ifp, ap, NULL, &pinm); VERIFY(pinm != NULL || error != 0); - return (pinm); + return pinm; } /* @@ -1397,7 +1437,6 @@ in_addmulti(struct in_addr *ap, struct ifnet *ifp) void in_delmulti(struct in_multi *inm) { - (void) in_leavegroup(inm, NULL); } @@ -1413,108 +1452,120 @@ in_delmulti(struct in_multi *inm) static int inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt) { - struct group_source_req gsr; - sockunion_t *gsa, *ssa; - struct ifnet *ifp; - struct in_mfilter *imf; - struct ip_moptions *imo; - struct in_msource *ims; - struct in_multi *inm; - size_t idx; - uint16_t fmode; - int error, doblock; - unsigned int ifindex = 0; - + struct group_source_req gsr; + struct sockaddr_in *gsa, *ssa; + struct ifnet *ifp; + struct in_mfilter *imf; + struct ip_moptions *imo; + struct in_msource *ims; + struct in_multi *inm; + size_t idx; + uint8_t fmode; + int error, doblock; + unsigned int ifindex = 0; + struct igmp_tparams itp; + + bzero(&itp, sizeof(itp)); ifp = NULL; error = 0; doblock = 0; memset(&gsr, 0, sizeof(struct group_source_req)); - gsa = (sockunion_t *)&gsr.gsr_group; - ssa = (sockunion_t *)&gsr.gsr_source; + gsa = (struct sockaddr_in *)&gsr.gsr_group; + ssa = (struct sockaddr_in *)&gsr.gsr_source; switch (sopt->sopt_name) { case IP_BLOCK_SOURCE: case IP_UNBLOCK_SOURCE: { - struct ip_mreq_source mreqs; + struct ip_mreq_source mreqs; error = sooptcopyin(sopt, &mreqs, sizeof(struct ip_mreq_source), sizeof(struct ip_mreq_source)); - if (error) - return (error); + if (error) { + return error; + } - gsa->sin.sin_family = AF_INET; - gsa->sin.sin_len = sizeof(struct sockaddr_in); - gsa->sin.sin_addr = mreqs.imr_multiaddr; + gsa->sin_family = AF_INET; + gsa->sin_len = sizeof(struct sockaddr_in); + gsa->sin_addr = mreqs.imr_multiaddr; - ssa->sin.sin_family = AF_INET; - ssa->sin.sin_len = sizeof(struct sockaddr_in); - ssa->sin.sin_addr = mreqs.imr_sourceaddr; + ssa->sin_family = AF_INET; + ssa->sin_len = sizeof(struct sockaddr_in); + ssa->sin_addr = mreqs.imr_sourceaddr; - if (!in_nullhost(mreqs.imr_interface)) + if (!in_nullhost(mreqs.imr_interface)) { ifp = ip_multicast_if(&mreqs.imr_interface, &ifindex); + } - if (sopt->sopt_name == IP_BLOCK_SOURCE) + if (sopt->sopt_name == IP_BLOCK_SOURCE) { doblock = 1; + } - IGMP_PRINTF(("%s: imr_interface = %s, ifp = %p\n", - __func__, inet_ntoa(mreqs.imr_interface), ifp)); + IGMP_INET_PRINTF(mreqs.imr_interface, + ("%s: imr_interface = %s, ifp = 0x%llx\n", __func__, + _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp))); break; - } + } case MCAST_BLOCK_SOURCE: case MCAST_UNBLOCK_SOURCE: error = sooptcopyin(sopt, &gsr, sizeof(struct group_source_req), sizeof(struct group_source_req)); - if (error) - return (error); + if (error) { + return error; + } - if (gsa->sin.sin_family != AF_INET || - gsa->sin.sin_len != sizeof(struct sockaddr_in)) - return (EINVAL); + if (gsa->sin_family != AF_INET || + gsa->sin_len != sizeof(struct sockaddr_in)) { + return EINVAL; + } - if (ssa->sin.sin_family != AF_INET || - ssa->sin.sin_len != sizeof(struct sockaddr_in)) - return (EINVAL); + if (ssa->sin_family != AF_INET || + ssa->sin_len != sizeof(struct sockaddr_in)) { + return EINVAL; + } ifnet_head_lock_shared(); if (gsr.gsr_interface == 0 || (u_int)if_index < gsr.gsr_interface) { ifnet_head_done(); - return (EADDRNOTAVAIL); + return EADDRNOTAVAIL; } ifp = ifindex2ifnet[gsr.gsr_interface]; ifnet_head_done(); - if (ifp == NULL) - return (EADDRNOTAVAIL); + if (ifp == NULL) { + return EADDRNOTAVAIL; + } - if (sopt->sopt_name == MCAST_BLOCK_SOURCE) + if (sopt->sopt_name == MCAST_BLOCK_SOURCE) { doblock = 1; + } break; default: IGMP_PRINTF(("%s: unknown sopt_name %d\n", __func__, sopt->sopt_name)); - return (EOPNOTSUPP); - break; + return EOPNOTSUPP; } - if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) - return (EINVAL); + if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) { + return EINVAL; + } /* * Check if we are actually a member of this group. */ imo = inp_findmoptions(inp); - if (imo == NULL) - return (ENOMEM); + if (imo == NULL) { + return ENOMEM; + } IMO_LOCK(imo); - idx = imo_match_group(imo, ifp, &gsa->sa); + idx = imo_match_group(imo, ifp, gsa); if (idx == (size_t)-1 || imo->imo_mfilters == NULL) { error = EADDRNOTAVAIL; goto out_imo_locked; @@ -1540,10 +1591,11 @@ inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt) * Asked to unblock, but nothing to unblock. * If adding a new block entry, allocate it. */ - ims = imo_match_source(imo, idx, &ssa->sa); + ims = imo_match_source(imo, idx, ssa); if ((ims != NULL && doblock) || (ims == NULL && !doblock)) { - IGMP_PRINTF(("%s: source %s %spresent\n", __func__, - inet_ntoa(ssa->sin.sin_addr), doblock ? "" : "not ")); + IGMP_INET_PRINTF(ssa->sin_addr, + ("%s: source %s %spresent\n", __func__, + _igmp_inet_buf, doblock ? "" : "not ")); error = EADDRNOTAVAIL; goto out_imo_locked; } @@ -1553,12 +1605,13 @@ inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt) */ if (doblock) { IGMP_PRINTF(("%s: %s source\n", __func__, "block")); - ims = imf_graft(imf, fmode, &ssa->sin); - if (ims == NULL) + ims = imf_graft(imf, fmode, ssa); + if (ims == NULL) { error = ENOMEM; + } } else { IGMP_PRINTF(("%s: %s source\n", __func__, "allow")); - error = imf_prune(imf, &ssa->sin); + error = imf_prune(imf, ssa); } if (error) { @@ -1579,25 +1632,31 @@ inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt) } IGMP_PRINTF(("%s: doing igmp downcall\n", __func__)); - error = igmp_change_state(inm); + error = igmp_change_state(inm, &itp); INM_UNLOCK(inm); #if IGMP_DEBUG - if (error) + if (error) { IGMP_PRINTF(("%s: failed igmp downcall\n", __func__)); + } #endif out_imf_rollback: - if (error) + if (error) { imf_rollback(imf); - else + } else { imf_commit(imf); + } imf_reap(imf); out_imo_locked: IMO_UNLOCK(imo); - IMO_REMREF(imo); /* from inp_findmoptions() */ - return (error); + IMO_REMREF(imo); /* from inp_findmoptions() */ + + /* schedule timer now that we've dropped the lock(s) */ + igmp_set_timeout(&itp); + + return error; } /* @@ -1609,53 +1668,55 @@ out_imo_locked: static struct ip_moptions * inp_findmoptions(struct inpcb *inp) { - struct ip_moptions *imo; - struct in_multi **immp; - struct in_mfilter *imfp; - size_t idx; + struct ip_moptions *imo; + struct in_multi **immp; + struct in_mfilter *imfp; + size_t idx; if ((imo = inp->inp_moptions) != NULL) { - IMO_ADDREF(imo); /* for caller */ - return (imo); + IMO_ADDREF(imo); /* for caller */ + return imo; } - imo = ip_allocmoptions(M_WAITOK); - if (imo == NULL) - return (NULL); + imo = ip_allocmoptions(Z_WAITOK); + if (imo == NULL) { + return NULL; + } - immp = _MALLOC(sizeof (*immp) * IP_MIN_MEMBERSHIPS, M_IPMOPTS, + immp = _MALLOC(sizeof(*immp) * IP_MIN_MEMBERSHIPS, M_IPMOPTS, M_WAITOK | M_ZERO); if (immp == NULL) { IMO_REMREF(imo); - return (NULL); + return NULL; } - imfp = _MALLOC(sizeof (struct in_mfilter) * IP_MIN_MEMBERSHIPS, + imfp = _MALLOC(sizeof(struct in_mfilter) * IP_MIN_MEMBERSHIPS, M_INMFILTER, M_WAITOK | M_ZERO); if (imfp == NULL) { _FREE(immp, M_IPMOPTS); IMO_REMREF(imo); - return (NULL); + return NULL; } imo->imo_multicast_ifp = NULL; imo->imo_multicast_addr.s_addr = INADDR_ANY; imo->imo_multicast_vif = -1; imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; - imo->imo_multicast_loop = in_mcast_loop; + imo->imo_multicast_loop = !!in_mcast_loop; imo->imo_num_memberships = 0; imo->imo_max_memberships = IP_MIN_MEMBERSHIPS; imo->imo_membership = immp; /* Initialize per-group source filters. */ - for (idx = 0; idx < IP_MIN_MEMBERSHIPS; idx++) + for (idx = 0; idx < IP_MIN_MEMBERSHIPS; idx++) { imf_init(&imfp[idx], MCAST_UNDEFINED, MCAST_EXCLUDE); + } imo->imo_mfilters = imfp; inp->inp_moptions = imo; /* keep reference from ip_allocmoptions() */ - IMO_ADDREF(imo); /* for caller */ + IMO_ADDREF(imo); /* for caller */ - return (imo); + return imo; } /* * Atomically get source filters on a socket for an IPv4 multicast group. @@ -1663,20 +1724,21 @@ inp_findmoptions(struct inpcb *inp) static int inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) { - struct __msfilterreq64 msfr, msfr64; - struct __msfilterreq32 msfr32; - sockunion_t *gsa; - struct ifnet *ifp; - struct ip_moptions *imo; - struct in_mfilter *imf; - struct ip_msource *ims; - struct in_msource *lims; - struct sockaddr_in *psin; - struct sockaddr_storage *ptss; - struct sockaddr_storage *tss; - int error; - size_t idx, nsrcs, ncsrcs; - user_addr_t tmp_ptr; + struct __msfilterreq64 msfr = {}, msfr64; + struct __msfilterreq32 msfr32; + struct sockaddr_in *gsa; + struct ifnet *ifp; + struct ip_moptions *imo; + struct in_mfilter *imf; + struct ip_msource *ims; + struct in_msource *lims; + struct sockaddr_in *psin; + struct sockaddr_storage *ptss; + struct sockaddr_storage *tss; + int error; + size_t idx; + uint32_t nsrcs, ncsrcs; + user_addr_t tmp_ptr; imo = inp->inp_moptions; VERIFY(imo != NULL); @@ -1685,44 +1747,54 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) error = sooptcopyin(sopt, &msfr64, sizeof(struct __msfilterreq64), sizeof(struct __msfilterreq64)); - if (error) - return (error); + if (error) { + return error; + } /* we never use msfr.msfr_srcs; */ - memcpy(&msfr, &msfr64, sizeof(msfr)); + memcpy(&msfr, &msfr64, sizeof(msfr64)); } else { error = sooptcopyin(sopt, &msfr32, sizeof(struct __msfilterreq32), sizeof(struct __msfilterreq32)); - if (error) - return (error); + if (error) { + return error; + } /* we never use msfr.msfr_srcs; */ - memcpy(&msfr, &msfr32, sizeof(msfr)); + memcpy(&msfr, &msfr32, sizeof(msfr32)); } ifnet_head_lock_shared(); if (msfr.msfr_ifindex == 0 || (u_int)if_index < msfr.msfr_ifindex) { ifnet_head_done(); - return (EADDRNOTAVAIL); + return EADDRNOTAVAIL; } ifp = ifindex2ifnet[msfr.msfr_ifindex]; ifnet_head_done(); - if (ifp == NULL) - return (EADDRNOTAVAIL); - - if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) + if (ifp == NULL) { + return EADDRNOTAVAIL; + } + + if ((size_t) msfr.msfr_nsrcs > + UINT32_MAX / sizeof(struct sockaddr_storage)) { + msfr.msfr_nsrcs = UINT32_MAX / sizeof(struct sockaddr_storage); + } + + if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) { msfr.msfr_nsrcs = in_mcast_maxsocksrc; + } IMO_LOCK(imo); /* * Lookup group on the socket. */ - gsa = (sockunion_t *)&msfr.msfr_group; - idx = imo_match_group(imo, ifp, &gsa->sa); + gsa = (struct sockaddr_in *)&msfr.msfr_group; + + idx = imo_match_group(imo, ifp, gsa); if (idx == (size_t)-1 || imo->imo_mfilters == NULL) { IMO_UNLOCK(imo); - return (EADDRNOTAVAIL); + return EADDRNOTAVAIL; } imf = &imo->imo_mfilters[idx]; @@ -1731,7 +1803,7 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) */ if (imf->imf_st[1] == MCAST_UNDEFINED) { IMO_UNLOCK(imo); - return (EAGAIN); + return EAGAIN; } msfr.msfr_fmode = imf->imf_st[1]; @@ -1743,18 +1815,19 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) * buffer really needs to be. */ - if (IS_64BIT_PROCESS(current_proc())) - tmp_ptr = msfr64.msfr_srcs; - else + if (IS_64BIT_PROCESS(current_proc())) { + tmp_ptr = CAST_USER_ADDR_T(msfr64.msfr_srcs); + } else { tmp_ptr = CAST_USER_ADDR_T(msfr32.msfr_srcs); + } tss = NULL; if (tmp_ptr != USER_ADDR_NULL && msfr.msfr_nsrcs > 0) { - tss = _MALLOC(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, + tss = _MALLOC((size_t) msfr.msfr_nsrcs * sizeof(*tss), M_TEMP, M_WAITOK | M_ZERO); if (tss == NULL) { IMO_UNLOCK(imo); - return (ENOBUFS); + return ENOBUFS; } } @@ -1768,8 +1841,9 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { lims = (struct in_msource *)ims; if (lims->imsl_st[0] == MCAST_UNDEFINED || - lims->imsl_st[0] != imf->imf_st[0]) + lims->imsl_st[0] != imf->imf_st[0]) { continue; + } if (tss != NULL && nsrcs > 0) { psin = (struct sockaddr_in *)ptss; psin->sin_family = AF_INET; @@ -1785,11 +1859,11 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) IMO_UNLOCK(imo); if (tss != NULL) { - error = copyout(tss, tmp_ptr, - sizeof(struct sockaddr_storage) * ncsrcs); + error = copyout(tss, CAST_USER_ADDR_T(tmp_ptr), ncsrcs * sizeof(*tss)); FREE(tss, M_TEMP); - if (error) - return (error); + if (error) { + return error; + } } msfr.msfr_nsrcs = ncsrcs; @@ -1805,13 +1879,13 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) msfr32.msfr_ifindex = msfr.msfr_ifindex; msfr32.msfr_fmode = msfr.msfr_fmode; msfr32.msfr_nsrcs = msfr.msfr_nsrcs; - memcpy(&msfr64.msfr_group, &msfr.msfr_group, + memcpy(&msfr32.msfr_group, &msfr.msfr_group, sizeof(struct sockaddr_storage)); error = sooptcopyout(sopt, &msfr32, sizeof(struct __msfilterreq32)); } - return (error); + return error; } /* @@ -1820,39 +1894,27 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) int inp_getmoptions(struct inpcb *inp, struct sockopt *sopt) { - struct ip_mreqn mreqn; - struct ip_moptions *imo; - struct ifnet *ifp; - struct in_ifaddr *ia; - int error, optval; - unsigned int ifindex; - u_char coptval; + struct ip_mreqn mreqn; + struct ip_moptions *imo; + struct ifnet *ifp; + struct in_ifaddr *ia; + int error, optval; + unsigned int ifindex; + u_char coptval; imo = inp->inp_moptions; /* * If socket is neither of type SOCK_RAW or SOCK_DGRAM, * or is a divert socket, reject it. */ - if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || - (inp->inp_socket->so_proto->pr_type != SOCK_RAW && - inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) { - return (EOPNOTSUPP); + if (SOCK_PROTO(inp->inp_socket) == IPPROTO_DIVERT || + (SOCK_TYPE(inp->inp_socket) != SOCK_RAW && + SOCK_TYPE(inp->inp_socket) != SOCK_DGRAM)) { + return EOPNOTSUPP; } error = 0; switch (sopt->sopt_name) { -#ifdef MROUTING - case IP_MULTICAST_VIF: - if (imo != NULL) { - IMO_LOCK(imo); - optval = imo->imo_multicast_vif; - IMO_UNLOCK(imo); - } else - optval = -1; - error = sooptcopyout(sopt, &optval, sizeof(int)); - break; -#endif /* MROUTING */ - case IP_MULTICAST_IF: memset(&mreqn, 0, sizeof(struct ip_mreqn)); if (imo != NULL) { @@ -1883,44 +1945,48 @@ inp_getmoptions(struct inpcb *inp, struct sockopt *sopt) break; case IP_MULTICAST_IFINDEX: - if (imo != NULL) + if (imo != NULL) { IMO_LOCK(imo); + } if (imo == NULL || imo->imo_multicast_ifp == NULL) { ifindex = 0; } else { ifindex = imo->imo_multicast_ifp->if_index; } - if (imo != NULL) + if (imo != NULL) { IMO_UNLOCK(imo); - error = sooptcopyout(sopt, &ifindex, sizeof (ifindex)); + } + error = sooptcopyout(sopt, &ifindex, sizeof(ifindex)); break; case IP_MULTICAST_TTL: - if (imo == NULL) + if (imo == NULL) { optval = coptval = IP_DEFAULT_MULTICAST_TTL; - else { + } else { IMO_LOCK(imo); optval = coptval = imo->imo_multicast_ttl; IMO_UNLOCK(imo); } - if (sopt->sopt_valsize == sizeof(u_char)) + if (sopt->sopt_valsize == sizeof(u_char)) { error = sooptcopyout(sopt, &coptval, sizeof(u_char)); - else + } else { error = sooptcopyout(sopt, &optval, sizeof(int)); + } break; case IP_MULTICAST_LOOP: - if (imo == 0) + if (imo == 0) { optval = coptval = IP_DEFAULT_MULTICAST_LOOP; - else { + } else { IMO_LOCK(imo); optval = coptval = imo->imo_multicast_loop; IMO_UNLOCK(imo); } - if (sopt->sopt_valsize == sizeof(u_char)) + if (sopt->sopt_valsize == sizeof(u_char)) { error = sooptcopyout(sopt, &coptval, sizeof(u_char)); - else + } else { error = sooptcopyout(sopt, &optval, sizeof(int)); + } break; case IP_MSFILTER: @@ -1936,7 +2002,7 @@ inp_getmoptions(struct inpcb *inp, struct sockopt *sopt) break; } - return (error); + return error; } /* @@ -1964,8 +2030,8 @@ static struct ifnet * inp_lookup_mcast_ifp(const struct inpcb *inp, const struct sockaddr_in *gsin, const struct in_addr ina) { - struct ifnet *ifp; - unsigned int ifindex = 0; + struct ifnet *ifp; + unsigned int ifindex = 0; VERIFY(gsin->sin_family == AF_INET); VERIFY(IN_MULTICAST(ntohl(gsin->sin_addr.s_addr))); @@ -1979,16 +2045,16 @@ inp_lookup_mcast_ifp(const struct inpcb *inp, struct route ro; unsigned int ifscope = IFSCOPE_NONE; - if (inp != NULL && (inp->inp_flags & INP_BOUND_IF)) - ifscope = inp->inp_boundif; + if (inp != NULL && (inp->inp_flags & INP_BOUND_IF)) { + ifscope = inp->inp_boundifp->if_index; + } - bzero(&ro, sizeof (ro)); + bzero(&ro, sizeof(ro)); memcpy(&ro.ro_dst, gsin, sizeof(struct sockaddr_in)); rtalloc_scoped_ign(&ro, 0, ifscope); if (ro.ro_rt != NULL) { ifp = ro.ro_rt->rt_ifp; VERIFY(ifp != NULL); - rtfree(ro.ro_rt); } else { struct in_ifaddr *ia; struct ifnet *mifp; @@ -2000,16 +2066,17 @@ inp_lookup_mcast_ifp(const struct inpcb *inp, mifp = ia->ia_ifp; IFA_UNLOCK(&ia->ia_ifa); if (!(mifp->if_flags & IFF_LOOPBACK) && - (mifp->if_flags & IFF_MULTICAST)) { + (mifp->if_flags & IFF_MULTICAST)) { ifp = mifp; break; } } lck_rw_done(in_ifaddr_rwlock); } + ROUTE_RELEASE(&ro); } - return (ifp); + return ifp; } /* @@ -2023,31 +2090,33 @@ inp_lookup_mcast_ifp(const struct inpcb *inp, int inp_join_group(struct inpcb *inp, struct sockopt *sopt) { - struct group_source_req gsr; - sockunion_t *gsa, *ssa; - struct ifnet *ifp; - struct in_mfilter *imf; - struct ip_moptions *imo; - struct in_multi *inm = NULL; - struct in_msource *lims; - size_t idx; - int error, is_new; - + struct group_source_req gsr; + struct sockaddr_in *gsa, *ssa; + struct ifnet *ifp; + struct in_mfilter *imf; + struct ip_moptions *imo; + struct in_multi *inm = NULL; + struct in_msource *lims; + size_t idx; + int error, is_new; + struct igmp_tparams itp; + + bzero(&itp, sizeof(itp)); ifp = NULL; imf = NULL; error = 0; is_new = 0; memset(&gsr, 0, sizeof(struct group_source_req)); - gsa = (sockunion_t *)&gsr.gsr_group; - gsa->ss.ss_family = AF_UNSPEC; - ssa = (sockunion_t *)&gsr.gsr_source; - ssa->ss.ss_family = AF_UNSPEC; + gsa = (struct sockaddr_in *)&gsr.gsr_group; + gsa->sin_family = AF_UNSPEC; + ssa = (struct sockaddr_in *)&gsr.gsr_source; + ssa->sin_family = AF_UNSPEC; switch (sopt->sopt_name) { case IP_ADD_MEMBERSHIP: case IP_ADD_SOURCE_MEMBERSHIP: { - struct ip_mreq_source mreqs; + struct ip_mreq_source mreqs; if (sopt->sopt_name == IP_ADD_MEMBERSHIP) { error = sooptcopyin(sopt, &mreqs, @@ -2068,26 +2137,27 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt) IGMP_PRINTF(("%s: error copyin IP_ADD_MEMBERSHIP/" "IP_ADD_SOURCE_MEMBERSHIP %d err=%d\n", __func__, sopt->sopt_name, error)); - return (error); + return error; } - gsa->sin.sin_family = AF_INET; - gsa->sin.sin_len = sizeof(struct sockaddr_in); - gsa->sin.sin_addr = mreqs.imr_multiaddr; + gsa->sin_family = AF_INET; + gsa->sin_len = sizeof(struct sockaddr_in); + gsa->sin_addr = mreqs.imr_multiaddr; if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) { - ssa->sin.sin_family = AF_INET; - ssa->sin.sin_len = sizeof(struct sockaddr_in); - ssa->sin.sin_addr = mreqs.imr_sourceaddr; + ssa->sin_family = AF_INET; + ssa->sin_len = sizeof(struct sockaddr_in); + ssa->sin_addr = mreqs.imr_sourceaddr; } - if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) - return (EINVAL); + if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) { + return EINVAL; + } - ifp = inp_lookup_mcast_ifp(inp, &gsa->sin, - mreqs.imr_interface); - IGMP_PRINTF(("%s: imr_interface = %s, ifp = %p\n", - __func__, inet_ntoa(mreqs.imr_interface), ifp)); + ifp = inp_lookup_mcast_ifp(inp, gsa, mreqs.imr_interface); + IGMP_INET_PRINTF(mreqs.imr_interface, + ("%s: imr_interface = %s, ifp = 0x%llx\n", __func__, + _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp))); break; } @@ -2102,33 +2172,37 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt) sizeof(struct group_source_req), sizeof(struct group_source_req)); } - if (error) - return (error); + if (error) { + return error; + } - if (gsa->sin.sin_family != AF_INET || - gsa->sin.sin_len != sizeof(struct sockaddr_in)) - return (EINVAL); + if (gsa->sin_family != AF_INET || + gsa->sin_len != sizeof(struct sockaddr_in)) { + return EINVAL; + } /* * Overwrite the port field if present, as the sockaddr * being copied in may be matched with a binary comparison. */ - gsa->sin.sin_port = 0; + gsa->sin_port = 0; if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) { - if (ssa->sin.sin_family != AF_INET || - ssa->sin.sin_len != sizeof(struct sockaddr_in)) - return (EINVAL); - ssa->sin.sin_port = 0; + if (ssa->sin_family != AF_INET || + ssa->sin_len != sizeof(struct sockaddr_in)) { + return EINVAL; + } + ssa->sin_port = 0; } - if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) - return (EINVAL); + if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) { + return EINVAL; + } ifnet_head_lock_shared(); if (gsr.gsr_interface == 0 || (u_int)if_index < gsr.gsr_interface) { ifnet_head_done(); - return (EADDRNOTAVAIL); + return EADDRNOTAVAIL; } ifp = ifindex2ifnet[gsr.gsr_interface]; ifnet_head_done(); @@ -2138,25 +2212,34 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt) default: IGMP_PRINTF(("%s: unknown sopt_name %d\n", __func__, sopt->sopt_name)); - return (EOPNOTSUPP); - break; + return EOPNOTSUPP; + } + + if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { + return EADDRNOTAVAIL; } - if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) - return (EADDRNOTAVAIL); + INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_mcast_join_total); + /* + * TBD: revisit the criteria for non-OS initiated joins + */ + if (inp->inp_lport == htons(5353)) { + INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_mcast_join_os_total); + } imo = inp_findmoptions(inp); - if (imo == NULL) - return (ENOMEM); + if (imo == NULL) { + return ENOMEM; + } IMO_LOCK(imo); - idx = imo_match_group(imo, ifp, &gsa->sa); + idx = imo_match_group(imo, ifp, gsa); if (idx == (size_t)-1) { is_new = 1; } else { inm = imo->imo_membership[idx]; imf = &imo->imo_mfilters[idx]; - if (ssa->ss.ss_family != AF_UNSPEC) { + if (ssa->sin_family != AF_UNSPEC) { /* * MCAST_JOIN_SOURCE_GROUP on an exclusive membership * is an error. On an existing inclusive membership, @@ -2182,9 +2265,9 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt) * full-state SSM API with the delta-based API, * which is discouraged in the relevant RFCs. */ - lims = imo_match_source(imo, idx, &ssa->sa); + lims = imo_match_source(imo, idx, ssa); if (lims != NULL /*&& - lims->imsl_st[1] == MCAST_INCLUDE*/) { + * lims->imsl_st[1] == MCAST_INCLUDE*/) { error = EADDRNOTAVAIL; goto out_imo_locked; } @@ -2205,8 +2288,9 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt) */ error = EINVAL; /* See comments above for EADDRINUSE */ - if (imf->imf_st[1] == MCAST_EXCLUDE) + if (imf->imf_st[1] == MCAST_EXCLUDE) { error = EADDRINUSE; + } goto out_imo_locked; } } @@ -2218,8 +2302,9 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt) if (is_new) { if (imo->imo_num_memberships == imo->imo_max_memberships) { error = imo_grow(imo, 0); - if (error) + if (error) { goto out_imo_locked; + } } /* * Allocate the new slot upfront so we can deal with @@ -2240,7 +2325,7 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt) * been allocated yet if this is a new membership, however, * the in_mfilter slot will be allocated and must be initialized. */ - if (ssa->ss.ss_family != AF_UNSPEC) { + if (ssa->sin_family != AF_UNSPEC) { /* Membership starts in IN mode */ if (is_new) { IGMP_PRINTF(("%s: new join w/source\n", __func__)); @@ -2248,7 +2333,7 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt) } else { IGMP_PRINTF(("%s: %s source\n", __func__, "allow")); } - lims = imf_graft(imf, MCAST_INCLUDE, &ssa->sin); + lims = imf_graft(imf, MCAST_INCLUDE, ssa); if (lims == NULL) { IGMP_PRINTF(("%s: merge imf state failed\n", __func__)); @@ -2266,14 +2351,29 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt) /* * Begin state merge transaction at IGMP layer. */ - if (is_new) { + /* + * Unlock socket as we may end up calling ifnet_ioctl() to join (or leave) + * the multicast group and we run the risk of a lock ordering issue + * if the ifnet thread calls into the socket layer to acquire the pcb list + * lock while the input thread delivers multicast packets + */ + IMO_ADDREF_LOCKED(imo); + IMO_UNLOCK(imo); + socket_unlock(inp->inp_socket, 0); + VERIFY(inm == NULL); - error = in_joingroup(ifp, &gsa->sin.sin_addr, imf, &inm); + error = in_joingroup(ifp, &gsa->sin_addr, imf, &inm); + + socket_lock(inp->inp_socket, 0); + IMO_REMREF(imo); + IMO_LOCK(imo); + VERIFY(inm != NULL || error != 0); - if (error) + if (error) { goto out_imo_free; - imo->imo_membership[idx] = inm; /* from in_joingroup() */ + } + imo->imo_membership[idx] = inm; /* from in_joingroup() */ } else { IGMP_PRINTF(("%s: merge inm state\n", __func__)); INM_LOCK(inm); @@ -2285,7 +2385,7 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt) goto out_imf_rollback; } IGMP_PRINTF(("%s: doing igmp downcall\n", __func__)); - error = igmp_change_state(inm); + error = igmp_change_state(inm, &itp); INM_UNLOCK(inm); if (error) { IGMP_PRINTF(("%s: failed igmp downcall\n", @@ -2297,10 +2397,11 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt) out_imf_rollback: if (error) { imf_rollback(imf); - if (is_new) + if (is_new) { imf_purge(imf); - else + } else { imf_reap(imf); + } } else { imf_commit(imf); } @@ -2314,8 +2415,12 @@ out_imo_free: out_imo_locked: IMO_UNLOCK(imo); - IMO_REMREF(imo); /* from inp_findmoptions() */ - return (error); + IMO_REMREF(imo); /* from inp_findmoptions() */ + + /* schedule timer now that we've dropped the lock(s) */ + igmp_set_timeout(&itp); + + return error; } /* @@ -2327,27 +2432,27 @@ out_imo_locked: int inp_leave_group(struct inpcb *inp, struct sockopt *sopt) { - struct group_source_req gsr; - struct ip_mreq_source mreqs; - sockunion_t *gsa, *ssa; - struct ifnet *ifp; - struct in_mfilter *imf; - struct ip_moptions *imo; - struct in_msource *ims; - struct in_multi *inm = NULL; - size_t idx; - int error, is_final; - unsigned int ifindex = 0; - + struct group_source_req gsr; + struct ip_mreq_source mreqs; + struct sockaddr_in *gsa, *ssa; + struct ifnet *ifp; + struct in_mfilter *imf; + struct ip_moptions *imo; + struct in_msource *ims; + struct in_multi *inm = NULL; + size_t idx; + int error, is_final; + unsigned int ifindex = 0; + struct igmp_tparams itp; + + bzero(&itp, sizeof(itp)); ifp = NULL; error = 0; is_final = 1; memset(&gsr, 0, sizeof(struct group_source_req)); - gsa = (sockunion_t *)&gsr.gsr_group; - gsa->ss.ss_family = AF_UNSPEC; - ssa = (sockunion_t *)&gsr.gsr_source; - ssa->ss.ss_family = AF_UNSPEC; + gsa = (struct sockaddr_in *)&gsr.gsr_group; + ssa = (struct sockaddr_in *)&gsr.gsr_source; switch (sopt->sopt_name) { case IP_DROP_MEMBERSHIP: @@ -2368,17 +2473,18 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt) sizeof(struct ip_mreq_source), sizeof(struct ip_mreq_source)); } - if (error) - return (error); + if (error) { + return error; + } - gsa->sin.sin_family = AF_INET; - gsa->sin.sin_len = sizeof(struct sockaddr_in); - gsa->sin.sin_addr = mreqs.imr_multiaddr; + gsa->sin_family = AF_INET; + gsa->sin_len = sizeof(struct sockaddr_in); + gsa->sin_addr = mreqs.imr_multiaddr; if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) { - ssa->sin.sin_family = AF_INET; - ssa->sin.sin_len = sizeof(struct sockaddr_in); - ssa->sin.sin_addr = mreqs.imr_sourceaddr; + ssa->sin_family = AF_INET; + ssa->sin_len = sizeof(struct sockaddr_in); + ssa->sin_addr = mreqs.imr_sourceaddr; } /* * Attempt to look up hinted ifp from interface address. @@ -2387,11 +2493,13 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt) * XXX NOTE WELL: The RFC 3678 API is preferred because * using an IPv4 address as a key is racy. */ - if (!in_nullhost(mreqs.imr_interface)) + if (!in_nullhost(mreqs.imr_interface)) { ifp = ip_multicast_if(&mreqs.imr_interface, &ifindex); + } - IGMP_PRINTF(("%s: imr_interface = %s, ifp = %p\n", - __func__, inet_ntoa(mreqs.imr_interface), ifp)); + IGMP_INET_PRINTF(mreqs.imr_interface, + ("%s: imr_interface = %s, ifp = 0x%llx\n", __func__, + _igmp_inet_buf, (uint64_t)VM_KERNEL_ADDRPERM(ifp))); break; @@ -2406,24 +2514,27 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt) sizeof(struct group_source_req), sizeof(struct group_source_req)); } - if (error) - return (error); + if (error) { + return error; + } - if (gsa->sin.sin_family != AF_INET || - gsa->sin.sin_len != sizeof(struct sockaddr_in)) - return (EINVAL); + if (gsa->sin_family != AF_INET || + gsa->sin_len != sizeof(struct sockaddr_in)) { + return EINVAL; + } if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) { - if (ssa->sin.sin_family != AF_INET || - ssa->sin.sin_len != sizeof(struct sockaddr_in)) - return (EINVAL); + if (ssa->sin_family != AF_INET || + ssa->sin_len != sizeof(struct sockaddr_in)) { + return EINVAL; + } } ifnet_head_lock_shared(); if (gsr.gsr_interface == 0 || (u_int)if_index < gsr.gsr_interface) { ifnet_head_done(); - return (EADDRNOTAVAIL); + return EADDRNOTAVAIL; } ifp = ifindex2ifnet[gsr.gsr_interface]; @@ -2433,22 +2544,23 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt) default: IGMP_PRINTF(("%s: unknown sopt_name %d\n", __func__, sopt->sopt_name)); - return (EOPNOTSUPP); - break; + return EOPNOTSUPP; } - if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) - return (EINVAL); + if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) { + return EINVAL; + } /* * Find the membership in the membership array. */ imo = inp_findmoptions(inp); - if (imo == NULL) - return (ENOMEM); + if (imo == NULL) { + return ENOMEM; + } IMO_LOCK(imo); - idx = imo_match_group(imo, ifp, &gsa->sa); + idx = imo_match_group(imo, ifp, gsa); if (idx == (size_t)-1) { error = EADDRNOTAVAIL; goto out_locked; @@ -2456,7 +2568,7 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt) inm = imo->imo_membership[idx]; imf = &imo->imo_mfilters[idx]; - if (ssa->ss.ss_family != AF_UNSPEC) { + if (ssa->sin_family != AF_UNSPEC) { IGMP_PRINTF(("%s: opt=%d is_final=0\n", __func__, sopt->sopt_name)); is_final = 0; @@ -2477,15 +2589,16 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt) error = EADDRNOTAVAIL; goto out_locked; } - ims = imo_match_source(imo, idx, &ssa->sa); + ims = imo_match_source(imo, idx, ssa); if (ims == NULL) { - IGMP_PRINTF(("%s: source %s %spresent\n", __func__, - inet_ntoa(ssa->sin.sin_addr), "not ")); + IGMP_INET_PRINTF(ssa->sin_addr, + ("%s: source %s %spresent\n", __func__, + _igmp_inet_buf, "not ")); error = EADDRNOTAVAIL; goto out_locked; } IGMP_PRINTF(("%s: %s source\n", __func__, "block")); - error = imf_prune(imf, &ssa->sin); + error = imf_prune(imf, ssa); if (error) { IGMP_PRINTF(("%s: merge imf state failed\n", __func__)); @@ -2497,6 +2610,7 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt) * Begin state merge transaction at IGMP layer. */ + if (is_final) { /* * Give up the multicast address record to which @@ -2516,7 +2630,7 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt) } IGMP_PRINTF(("%s: doing igmp downcall\n", __func__)); - error = igmp_change_state(inm); + error = igmp_change_state(inm, &itp); if (error) { IGMP_PRINTF(("%s: failed igmp downcall\n", __func__)); } @@ -2524,29 +2638,47 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt) } out_imf_rollback: - if (error) + if (error) { imf_rollback(imf); - else + } else { imf_commit(imf); + } imf_reap(imf); if (is_final) { - /* Remove the gap in the membership and filter array. */ + /* Remove the gap in the membership array. */ VERIFY(inm == imo->imo_membership[idx]); imo->imo_membership[idx] = NULL; + + /* + * See inp_join_group() for why we need to unlock + */ + IMO_ADDREF_LOCKED(imo); + IMO_UNLOCK(imo); + socket_unlock(inp->inp_socket, 0); + INM_REMREF(inm); + + socket_lock(inp->inp_socket, 0); + IMO_REMREF(imo); + IMO_LOCK(imo); + for (++idx; idx < imo->imo_num_memberships; ++idx) { - imo->imo_membership[idx-1] = imo->imo_membership[idx]; - imo->imo_mfilters[idx-1] = imo->imo_mfilters[idx]; + imo->imo_membership[idx - 1] = imo->imo_membership[idx]; + imo->imo_mfilters[idx - 1] = imo->imo_mfilters[idx]; } imo->imo_num_memberships--; } out_locked: IMO_UNLOCK(imo); - IMO_REMREF(imo); /* from inp_findmoptions() */ - return (error); + IMO_REMREF(imo); /* from inp_findmoptions() */ + + /* schedule timer now that we've dropped the lock(s) */ + igmp_set_timeout(&itp); + + return error; } /* @@ -2560,13 +2692,14 @@ out_locked: static int inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt) { - struct in_addr addr; - struct ip_mreqn mreqn; - struct ifnet *ifp; - struct ip_moptions *imo; - int error = 0 ; - unsigned int ifindex = 0; - + struct in_addr addr; + struct ip_mreqn mreqn; + struct ifnet *ifp; + struct ip_moptions *imo; + int error = 0; + unsigned int ifindex = 0; + + bzero(&addr, sizeof(addr)); if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) { /* * An interface index was specified using the @@ -2574,13 +2707,14 @@ inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt) */ error = sooptcopyin(sopt, &mreqn, sizeof(struct ip_mreqn), sizeof(struct ip_mreqn)); - if (error) - return (error); + if (error) { + return error; + } ifnet_head_lock_shared(); if (mreqn.imr_ifindex < 0 || if_index < mreqn.imr_ifindex) { ifnet_head_done(); - return (EINVAL); + return EINVAL; } if (mreqn.imr_ifindex == 0) { @@ -2589,7 +2723,7 @@ inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt) ifp = ifindex2ifnet[mreqn.imr_ifindex]; if (ifp == NULL) { ifnet_head_done(); - return (EADDRNOTAVAIL); + return EADDRNOTAVAIL; } } ifnet_head_done(); @@ -2600,42 +2734,43 @@ inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt) */ error = sooptcopyin(sopt, &addr, sizeof(struct in_addr), sizeof(struct in_addr)); - if (error) - return (error); + if (error) { + return error; + } if (in_nullhost(addr)) { ifp = NULL; } else { ifp = ip_multicast_if(&addr, &ifindex); if (ifp == NULL) { - IGMP_PRINTF(("%s: can't find ifp for addr=%s\n", - __func__, inet_ntoa(addr))); - return (EADDRNOTAVAIL); + IGMP_INET_PRINTF(addr, + ("%s: can't find ifp for addr=%s\n", + __func__, _igmp_inet_buf)); + return EADDRNOTAVAIL; } } -#ifdef IGMP_DEBUG0 - IGMP_PRINTF(("%s: ifp = %p, addr = %s\n", __func__, ifp, - inet_ntoa(addr))); -#endif } /* Reject interfaces which do not support multicast. */ - if (ifp != NULL && (ifp->if_flags & IFF_MULTICAST) == 0) - return (EOPNOTSUPP); + if (ifp != NULL && (ifp->if_flags & IFF_MULTICAST) == 0) { + return EOPNOTSUPP; + } imo = inp_findmoptions(inp); - if (imo == NULL) - return (ENOMEM); + if (imo == NULL) { + return ENOMEM; + } IMO_LOCK(imo); imo->imo_multicast_ifp = ifp; - if (ifindex) + if (ifindex) { imo->imo_multicast_addr = addr; - else + } else { imo->imo_multicast_addr.s_addr = INADDR_ANY; + } IMO_UNLOCK(imo); - IMO_REMREF(imo); /* from inp_findmoptions() */ + IMO_REMREF(imo); /* from inp_findmoptions() */ - return (0); + return 0; } /* @@ -2644,72 +2779,88 @@ inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt) static int inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) { - struct __msfilterreq64 msfr, msfr64; - struct __msfilterreq32 msfr32; - sockunion_t *gsa; - struct ifnet *ifp; - struct in_mfilter *imf; - struct ip_moptions *imo; - struct in_multi *inm; - size_t idx; - int error; - user_addr_t tmp_ptr; + struct __msfilterreq64 msfr = {}, msfr64; + struct __msfilterreq32 msfr32; + struct sockaddr_in *gsa; + struct ifnet *ifp; + struct in_mfilter *imf; + struct ip_moptions *imo; + struct in_multi *inm; + size_t idx; + int error; + uint64_t tmp_ptr; + struct igmp_tparams itp; + + bzero(&itp, sizeof(itp)); if (IS_64BIT_PROCESS(current_proc())) { error = sooptcopyin(sopt, &msfr64, sizeof(struct __msfilterreq64), sizeof(struct __msfilterreq64)); - if (error) - return (error); + if (error) { + return error; + } /* we never use msfr.msfr_srcs; */ - memcpy(&msfr, &msfr64, sizeof(msfr)); + memcpy(&msfr, &msfr64, sizeof(msfr64)); } else { error = sooptcopyin(sopt, &msfr32, sizeof(struct __msfilterreq32), sizeof(struct __msfilterreq32)); - if (error) - return (error); + if (error) { + return error; + } /* we never use msfr.msfr_srcs; */ - memcpy(&msfr, &msfr32, sizeof(msfr)); + memcpy(&msfr, &msfr32, sizeof(msfr32)); + } + + if ((size_t) msfr.msfr_nsrcs > + UINT32_MAX / sizeof(struct sockaddr_storage)) { + msfr.msfr_nsrcs = UINT32_MAX / sizeof(struct sockaddr_storage); } - if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) - return (ENOBUFS); + if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) { + return ENOBUFS; + } if ((msfr.msfr_fmode != MCAST_EXCLUDE && - msfr.msfr_fmode != MCAST_INCLUDE)) - return (EINVAL); + msfr.msfr_fmode != MCAST_INCLUDE)) { + return EINVAL; + } if (msfr.msfr_group.ss_family != AF_INET || - msfr.msfr_group.ss_len != sizeof(struct sockaddr_in)) - return (EINVAL); + msfr.msfr_group.ss_len != sizeof(struct sockaddr_in)) { + return EINVAL; + } - gsa = (sockunion_t *)&msfr.msfr_group; - if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) - return (EINVAL); + gsa = (struct sockaddr_in *)&msfr.msfr_group; + if (!IN_MULTICAST(ntohl(gsa->sin_addr.s_addr))) { + return EINVAL; + } - gsa->sin.sin_port = 0; /* ignore port */ + gsa->sin_port = 0; /* ignore port */ ifnet_head_lock_shared(); if (msfr.msfr_ifindex == 0 || (u_int)if_index < msfr.msfr_ifindex) { ifnet_head_done(); - return (EADDRNOTAVAIL); + return EADDRNOTAVAIL; } ifp = ifindex2ifnet[msfr.msfr_ifindex]; ifnet_head_done(); - if (ifp == NULL) - return (EADDRNOTAVAIL); + if (ifp == NULL) { + return EADDRNOTAVAIL; + } /* * Check if this socket is a member of this group. */ imo = inp_findmoptions(inp); - if (imo == NULL) - return (ENOMEM); + if (imo == NULL) { + return ENOMEM; + } IMO_LOCK(imo); - idx = imo_match_group(imo, ifp, &gsa->sa); + idx = imo_match_group(imo, ifp, gsa); if (idx == (size_t)-1 || imo->imo_mfilters == NULL) { error = EADDRNOTAVAIL; goto out_imo_locked; @@ -2721,7 +2872,7 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) * Begin state merge transaction at socket layer. */ - imf->imf_st[1] = msfr.msfr_fmode; + imf->imf_st[1] = (uint8_t)msfr.msfr_fmode; /* * Apply any new source filters, if present. @@ -2730,26 +2881,27 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) * allows us to deal with page faults up-front. */ if (msfr.msfr_nsrcs > 0) { - struct in_msource *lims; - struct sockaddr_in *psin; - struct sockaddr_storage *kss, *pkss; - int i; + struct in_msource *lims; + struct sockaddr_in *psin; + struct sockaddr_storage *kss, *pkss; + int i; - if (IS_64BIT_PROCESS(current_proc())) + if (IS_64BIT_PROCESS(current_proc())) { tmp_ptr = msfr64.msfr_srcs; - else + } else { tmp_ptr = CAST_USER_ADDR_T(msfr32.msfr_srcs); + } IGMP_PRINTF(("%s: loading %lu source list entries\n", __func__, (unsigned long)msfr.msfr_nsrcs)); - kss = _MALLOC(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, + kss = _MALLOC((size_t) msfr.msfr_nsrcs * sizeof(*kss), M_TEMP, M_WAITOK); if (kss == NULL) { error = ENOMEM; goto out_imo_locked; } - error = copyin(tmp_ptr, kss, - sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); + error = copyin(CAST_USER_ADDR_T(tmp_ptr), kss, + (size_t) msfr.msfr_nsrcs * sizeof(*kss)); if (error) { FREE(kss, M_TEMP); goto out_imo_locked; @@ -2761,7 +2913,7 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) * will set it to INCLUDE. */ imf_leave(imf); - imf->imf_st[1] = msfr.msfr_fmode; + imf->imf_st[1] = (uint8_t)msfr.msfr_fmode; /* * Update socket layer filters at t1, lazy-allocating @@ -2786,15 +2938,17 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) break; } error = imf_get_source(imf, psin, &lims); - if (error) + if (error) { break; + } lims->imsl_st[1] = imf->imf_st[1]; } FREE(kss, M_TEMP); } - if (error) + if (error) { goto out_imf_rollback; + } /* * Begin state merge transaction at IGMP layer. @@ -2809,26 +2963,31 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) } IGMP_PRINTF(("%s: doing igmp downcall\n", __func__)); - error = igmp_change_state(inm); + error = igmp_change_state(inm, &itp); INM_UNLOCK(inm); #ifdef IGMP_DEBUG - if (error) + if (error) { IGMP_PRINTF(("%s: failed igmp downcall\n", __func__)); + } #endif out_imf_rollback: - if (error) + if (error) { imf_rollback(imf); - else + } else { imf_commit(imf); + } imf_reap(imf); out_imo_locked: IMO_UNLOCK(imo); - IMO_REMREF(imo); /* from inp_findmoptions() */ + IMO_REMREF(imo); /* from inp_findmoptions() */ + + /* schedule timer now that we've dropped the lock(s) */ + igmp_set_timeout(&itp); - return (error); + return error; } /* @@ -2839,17 +2998,14 @@ out_imo_locked: * it is not possible to merge the duplicate code, because the idempotence * of the IPv4 multicast part of the BSD Sockets API must be preserved; * the effects of these options must be treated as separate and distinct. - * - * FUTURE: The IP_MULTICAST_VIF option may be eliminated if MROUTING - * is refactored to no longer use vifs. */ int inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) { - struct ip_moptions *imo; - int error; - unsigned int ifindex; - struct ifnet *ifp; + struct ip_moptions *imo; + int error; + unsigned int ifindex; + struct ifnet *ifp; error = 0; @@ -2857,42 +3013,13 @@ inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) * If socket is neither of type SOCK_RAW or SOCK_DGRAM, * or is a divert socket, reject it. */ - if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || - (inp->inp_socket->so_proto->pr_type != SOCK_RAW && - inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) - return (EOPNOTSUPP); + if (SOCK_PROTO(inp->inp_socket) == IPPROTO_DIVERT || + (SOCK_TYPE(inp->inp_socket) != SOCK_RAW && + SOCK_TYPE(inp->inp_socket) != SOCK_DGRAM)) { + return EOPNOTSUPP; + } switch (sopt->sopt_name) { -#if MROUTING - case IP_MULTICAST_VIF: { - int vifi; - /* - * Select a multicast VIF for transmission. - * Only useful if multicast forwarding is active. - */ - if (legal_vif_num == NULL) { - error = EOPNOTSUPP; - break; - } - error = sooptcopyin(sopt, &vifi, sizeof(int), sizeof(int)); - if (error) - break; - if (!legal_vif_num(vifi) && (vifi != -1)) { - error = EINVAL; - break; - } - imo = inp_findmoptions(inp); - if (imo == NULL) { - error = ENOMEM; - break; - } - IMO_LOCK(imo); - imo->imo_multicast_vif = vifi; - IMO_UNLOCK(imo); - IMO_REMREF(imo); /* from inp_findmoptions() */ - break; - } -#endif case IP_MULTICAST_IF: error = inp_set_multicast_if(inp, sopt); break; @@ -2901,10 +3028,11 @@ inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) /* * Select the interface for outgoing multicast packets. */ - error = sooptcopyin(sopt, &ifindex, sizeof (ifindex), - sizeof (ifindex)); - if (error) + error = sooptcopyin(sopt, &ifindex, sizeof(ifindex), + sizeof(ifindex)); + if (error) { break; + } imo = inp_findmoptions(inp); if (imo == NULL) { @@ -2920,7 +3048,7 @@ inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) IMO_LOCK(imo); imo->imo_multicast_ifp = NULL; IMO_UNLOCK(imo); - IMO_REMREF(imo); /* from inp_findmoptions() */ + IMO_REMREF(imo); /* from inp_findmoptions() */ break; } @@ -2928,8 +3056,8 @@ inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) /* Don't need to check is ifindex is < 0 since it's unsigned */ if ((unsigned int)if_index < ifindex) { ifnet_head_done(); - IMO_REMREF(imo); /* from inp_findmoptions() */ - error = ENXIO; /* per IPV6_MULTICAST_IF */ + IMO_REMREF(imo); /* from inp_findmoptions() */ + error = ENXIO; /* per IPV6_MULTICAST_IF */ break; } ifp = ifindex2ifnet[ifindex]; @@ -2937,7 +3065,7 @@ inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) /* If it's detached or isn't a multicast interface, bail out */ if (ifp == NULL || !(ifp->if_flags & IFF_MULTICAST)) { - IMO_REMREF(imo); /* from inp_findmoptions() */ + IMO_REMREF(imo); /* from inp_findmoptions() */ error = EADDRNOTAVAIL; break; } @@ -2952,7 +3080,7 @@ inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) */ imo->imo_multicast_addr.s_addr = INADDR_ANY; IMO_UNLOCK(imo); - IMO_REMREF(imo); /* from inp_findmoptions() */ + IMO_REMREF(imo); /* from inp_findmoptions() */ break; case IP_MULTICAST_TTL: { @@ -2967,15 +3095,17 @@ inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) if (sopt->sopt_valsize == sizeof(u_char)) { error = sooptcopyin(sopt, &ttl, sizeof(u_char), sizeof(u_char)); - if (error) + if (error) { break; + } } else { u_int ittl; error = sooptcopyin(sopt, &ittl, sizeof(u_int), sizeof(u_int)); - if (error) + if (error) { break; + } if (ittl > 255) { error = EINVAL; break; @@ -2990,7 +3120,7 @@ inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) IMO_LOCK(imo); imo->imo_multicast_ttl = ttl; IMO_UNLOCK(imo); - IMO_REMREF(imo); /* from inp_findmoptions() */ + IMO_REMREF(imo); /* from inp_findmoptions() */ break; } @@ -3006,15 +3136,17 @@ inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) if (sopt->sopt_valsize == sizeof(u_char)) { error = sooptcopyin(sopt, &loop, sizeof(u_char), sizeof(u_char)); - if (error) + if (error) { break; + } } else { u_int iloop; error = sooptcopyin(sopt, &iloop, sizeof(u_int), - sizeof(u_int)); - if (error) + sizeof(u_int)); + if (error) { break; + } loop = (u_char)iloop; } imo = inp_findmoptions(inp); @@ -3025,7 +3157,7 @@ inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) IMO_LOCK(imo); imo->imo_multicast_loop = !!loop; IMO_UNLOCK(imo); - IMO_REMREF(imo); /* from inp_findmoptions() */ + IMO_REMREF(imo); /* from inp_findmoptions() */ break; } @@ -3059,7 +3191,7 @@ inp_setmoptions(struct inpcb *inp, struct sockopt *sopt) break; } - return (error); + return error; } /* @@ -3074,24 +3206,26 @@ sysctl_ip_mcast_filters SYSCTL_HANDLER_ARGS { #pragma unused(oidp) - struct in_addr src, group; - struct ifnet *ifp; - struct in_multi *inm; - struct in_multistep step; - struct ip_msource *ims; - int *name; - int retval = 0; - u_int namelen; - uint32_t fmode, ifindex; + struct in_addr src = {}, group; + struct ifnet *ifp; + struct in_multi *inm; + struct in_multistep step; + struct ip_msource *ims; + int *name; + int retval = 0; + u_int namelen; + uint32_t fmode, ifindex; name = (int *)arg1; namelen = (u_int)arg2; - if (req->newptr != USER_ADDR_NULL) - return (EPERM); + if (req->newptr != USER_ADDR_NULL) { + return EPERM; + } - if (namelen != 2) - return (EINVAL); + if (namelen != 2) { + return EINVAL; + } ifindex = name[0]; ifnet_head_lock_shared(); @@ -3099,46 +3233,49 @@ sysctl_ip_mcast_filters SYSCTL_HANDLER_ARGS IGMP_PRINTF(("%s: ifindex %u out of range\n", __func__, ifindex)); ifnet_head_done(); - return (ENOENT); + return ENOENT; } group.s_addr = name[1]; if (!IN_MULTICAST(ntohl(group.s_addr))) { - IGMP_PRINTF(("%s: group %s is not multicast\n", - __func__, inet_ntoa(group))); + IGMP_INET_PRINTF(group, + ("%s: group %s is not multicast\n", + __func__, _igmp_inet_buf)); ifnet_head_done(); - return (EINVAL); + return EINVAL; } ifp = ifindex2ifnet[ifindex]; ifnet_head_done(); if (ifp == NULL) { IGMP_PRINTF(("%s: no ifp for ifindex %u\n", __func__, ifindex)); - return (ENOENT); + return ENOENT; } in_multihead_lock_shared(); IN_FIRST_MULTI(step, inm); while (inm != NULL) { INM_LOCK(inm); - if (inm->inm_ifp != ifp) + if (inm->inm_ifp != ifp) { goto next; + } - if (!in_hosteq(inm->inm_addr, group)) + if (!in_hosteq(inm->inm_addr, group)) { goto next; + } fmode = inm->inm_st[1].iss_fmode; retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t)); if (retval != 0) { INM_UNLOCK(inm); - break; /* abort */ + break; /* abort */ } RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) { #ifdef IGMP_DEBUG struct in_addr ina; ina.s_addr = htonl(ims->ims_haddr); - IGMP_PRINTF(("%s: visit node %s\n", __func__, - inet_ntoa(ina))); + IGMP_INET_PRINTF(ina, + ("%s: visit node %s\n", __func__, _igmp_inet_buf)); #endif /* * Only copy-out sources which are in-mode. @@ -3150,8 +3287,9 @@ sysctl_ip_mcast_filters SYSCTL_HANDLER_ARGS } src.s_addr = htonl(ims->ims_haddr); retval = SYSCTL_OUT(req, &src, sizeof(struct in_addr)); - if (retval != 0) - break; /* process next inm */ + if (retval != 0) { + break; /* process next inm */ + } } next: INM_UNLOCK(inm); @@ -3159,7 +3297,7 @@ next: } in_multihead_lock_done(); - return (retval); + return retval; } /* @@ -3178,30 +3316,32 @@ ip_multicast_if(struct in_addr *a, unsigned int *ifindexp) unsigned int ifindex; struct ifnet *ifp; - if (ifindexp != NULL) + if (ifindexp != NULL) { *ifindexp = 0; + } if (ntohl(a->s_addr) >> 24 == 0) { ifindex = ntohl(a->s_addr) & 0xffffff; ifnet_head_lock_shared(); /* Don't need to check is ifindex is < 0 since it's unsigned */ if ((unsigned int)if_index < ifindex) { ifnet_head_done(); - return (NULL); + return NULL; } ifp = ifindex2ifnet[ifindex]; ifnet_head_done(); - if (ifp != NULL && ifindexp != NULL) + if (ifp != NULL && ifindexp != NULL) { *ifindexp = ifindex; + } } else { INADDR_TO_IFP(*a, ifp); } - return (ifp); + return ifp; } void in_multi_init(void) { - PE_parse_boot_argn("ifa_debug", &inm_debug, sizeof (inm_debug)); + PE_parse_boot_argn("ifa_debug", &inm_debug, sizeof(inm_debug)); /* Setup lock group and attribute for in_multihead */ in_multihead_lock_grp_attr = lck_grp_attr_alloc_init(); @@ -3215,43 +3355,18 @@ in_multi_init(void) in_multihead_lock_attr); TAILQ_INIT(&inm_trash_head); - inm_size = (inm_debug == 0) ? sizeof (struct in_multi) : - sizeof (struct in_multi_dbg); - inm_zone = zinit(inm_size, INM_ZONE_MAX * inm_size, - 0, INM_ZONE_NAME); - if (inm_zone == NULL) { - panic("%s: failed allocating %s", __func__, INM_ZONE_NAME); - /* NOTREACHED */ - } - zone_change(inm_zone, Z_EXPAND, TRUE); - - ipms_size = sizeof (struct ip_msource); - ipms_zone = zinit(ipms_size, IPMS_ZONE_MAX * ipms_size, - 0, IPMS_ZONE_NAME); - if (ipms_zone == NULL) { - panic("%s: failed allocating %s", __func__, IPMS_ZONE_NAME); - /* NOTREACHED */ - } - zone_change(ipms_zone, Z_EXPAND, TRUE); - - inms_size = sizeof (struct in_msource); - inms_zone = zinit(inms_size, INMS_ZONE_MAX * inms_size, - 0, INMS_ZONE_NAME); - if (inms_zone == NULL) { - panic("%s: failed allocating %s", __func__, INMS_ZONE_NAME); - /* NOTREACHED */ - } - zone_change(inms_zone, Z_EXPAND, TRUE); + vm_size_t inm_size = (inm_debug == 0) ? sizeof(struct in_multi) : + sizeof(struct in_multi_dbg); + inm_zone = zone_create(INM_ZONE_NAME, inm_size, ZC_ZFREE_CLEARMEM); } static struct in_multi * -in_multi_alloc(int how) +in_multi_alloc(zalloc_flags_t how) { struct in_multi *inm; - inm = (how == M_WAITOK) ? zalloc(inm_zone) : zalloc_noblock(inm_zone); + inm = zalloc_flags(inm_zone, how | Z_ZERO); if (inm != NULL) { - bzero(inm, inm_size); lck_mtx_init(&inm->inm_lock, in_multihead_lock_grp, in_multihead_lock_attr); inm->inm_debug |= IFD_ALLOC; @@ -3260,7 +3375,7 @@ in_multi_alloc(int how) inm->inm_trace = inm_trace; } } - return (inm); + return inm; } static void @@ -3352,8 +3467,9 @@ in_multi_detach(struct in_multi *inm) } --inm->inm_reqcnt; - if (inm->inm_reqcnt > 0) - return (0); + if (inm->inm_reqcnt > 0) { + return 0; + } if (!(inm->inm_debug & IFD_ATTACHED)) { panic("%s: Attempt to detach an unattached record inm=%p", @@ -3380,16 +3496,17 @@ in_multi_detach(struct in_multi *inm) inm->inm_debug |= IFD_TRASHED; } - return (1); + return 1; } void inm_addref(struct in_multi *inm, int locked) { - if (!locked) + if (!locked) { INM_LOCK_SPIN(inm); - else + } else { INM_LOCK_ASSERT_HELD(inm); + } if (++inm->inm_refcount == 0) { panic("%s: inm=%p wraparound refcnt", __func__, inm); @@ -3397,8 +3514,9 @@ inm_addref(struct in_multi *inm, int locked) } else if (inm->inm_trace != NULL) { (*inm->inm_trace)(inm, TRUE); } - if (!locked) + if (!locked) { INM_UNLOCK(inm); + } } void @@ -3407,10 +3525,11 @@ inm_remref(struct in_multi *inm, int locked) struct ifmultiaddr *ifma; struct igmp_ifinfo *igi; - if (!locked) + if (!locked) { INM_LOCK_SPIN(inm); - else + } else { INM_LOCK_ASSERT_HELD(inm); + } if (inm->inm_refcount == 0 || (inm->inm_refcount == 1 && locked)) { panic("%s: inm=%p negative/missing refcnt", __func__, inm); @@ -3421,8 +3540,9 @@ inm_remref(struct in_multi *inm, int locked) --inm->inm_refcount; if (inm->inm_refcount > 0) { - if (!locked) + if (!locked) { INM_UNLOCK(inm); + } return; } @@ -3447,8 +3567,9 @@ inm_remref(struct in_multi *inm, int locked) INM_UNLOCK(inm); in_multihead_lock_done(); /* If it was locked, return it as such */ - if (locked) + if (locked) { INM_LOCK(inm); + } return; } inm_purge(inm); @@ -3468,8 +3589,9 @@ inm_remref(struct in_multi *inm, int locked) /* Release reference held to the underlying ifmultiaddr */ IFMA_REMREF(ifma); - if (igi != NULL) + if (igi != NULL) { IGI_REMREF(igi); + } } static void @@ -3511,7 +3633,10 @@ in_multihead_lock_shared(void) void in_multihead_lock_assert(int what) { - lck_rw_assert(&in_multihead_lock, what); +#if !MACH_ASSERT +#pragma unused(what) +#endif + LCK_RW_ASSERT(&in_multihead_lock, what); } void @@ -3521,15 +3646,9 @@ in_multihead_lock_done(void) } static struct ip_msource * -ipms_alloc(int how) +ipms_alloc(zalloc_flags_t how) { - struct ip_msource *ims; - - ims = (how == M_WAITOK) ? zalloc(ipms_zone) : zalloc_noblock(ipms_zone); - if (ims != NULL) - bzero(ims, ipms_size); - - return (ims); + return zalloc_flags(ipms_zone, how | Z_ZERO); } static void @@ -3539,16 +3658,9 @@ ipms_free(struct ip_msource *ims) } static struct in_msource * -inms_alloc(int how) +inms_alloc(zalloc_flags_t how) { - struct in_msource *inms; - - inms = (how == M_WAITOK) ? zalloc(inms_zone) : - zalloc_noblock(inms_zone); - if (inms != NULL) - bzero(inms, inms_size); - - return (inms); + return zalloc_flags(inms_zone, how | Z_ZERO); } static void @@ -3564,14 +3676,16 @@ static const char *inm_modestrs[] = { "un\n", "in", "ex" }; static const char * inm_mode_str(const int mode) { - if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) - return (inm_modestrs[mode]); - return ("??"); + if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) { + return inm_modestrs[mode]; + } + return "??"; } static const char *inm_statestrs[] = { "not-member\n", "silent\n", + "reporting\n", "idle\n", "lazy\n", "sleeping\n", @@ -3584,9 +3698,10 @@ static const char *inm_statestrs[] = { static const char * inm_state_str(const int state) { - if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER) - return (inm_statestrs[state]); - return ("??"); + if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER) { + return inm_statestrs[state]; + } + return "??"; } /* @@ -3596,26 +3711,29 @@ void inm_print(const struct in_multi *inm) { int t; + char buf[MAX_IPv4_STR_LEN]; - INM_LOCK_ASSERT_HELD(INM_CAST_TO_NONCONST(inm)); + INM_LOCK_ASSERT_HELD(__DECONST(struct in_multi *, inm)); - if (igmp_debug == 0) + if (igmp_debug == 0) { return; + } - printf("%s: --- begin inm %p ---\n", __func__, inm); - printf("addr %s ifp %p(%s%d) ifma %p\n", - inet_ntoa(inm->inm_addr), - inm->inm_ifp, - inm->inm_ifp->if_name, - inm->inm_ifp->if_unit, - inm->inm_ifma); + inet_ntop(AF_INET, &inm->inm_addr, buf, sizeof(buf)); + printf("%s: --- begin inm 0x%llx ---\n", __func__, + (uint64_t)VM_KERNEL_ADDRPERM(inm)); + printf("addr %s ifp 0x%llx(%s) ifma 0x%llx\n", + buf, + (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_ifp), + if_name(inm->inm_ifp), + (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_ifma)); printf("timer %u state %s refcount %u scq.len %u\n", inm->inm_timer, inm_state_str(inm->inm_state), inm->inm_refcount, inm->inm_scq.ifq_len); - printf("igi %p nsrc %lu sctimer %u scrv %u\n", - inm->inm_igi, + printf("igi 0x%llx nsrc %lu sctimer %u scrv %u\n", + (uint64_t)VM_KERNEL_ADDRPERM(inm->inm_igi), inm->inm_nsrc, inm->inm_sctimer, inm->inm_scrv); @@ -3627,7 +3745,8 @@ inm_print(const struct in_multi *inm) inm->inm_st[t].iss_in, inm->inm_st[t].iss_rec); } - printf("%s: --- end inm %p ---\n", __func__, inm); + printf("%s: --- end inm 0x%llx ---\n", __func__, + (uint64_t)VM_KERNEL_ADDRPERM(inm)); } #else @@ -3635,7 +3754,6 @@ inm_print(const struct in_multi *inm) void inm_print(__unused const struct in_multi *inm) { - } #endif