/*
- * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2010-2017 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <net/if.h>
#include <net/if_dl.h>
+#include <net/net_api_stats.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet6/mld6_var.h>
#include <netinet6/scope6_var.h>
-#ifndef __SOCKUNION_DECLARED
-union sockunion {
- struct sockaddr_storage ss;
- struct sockaddr sa;
- struct sockaddr_dl sdl;
- struct sockaddr_in6 sin6;
-};
-typedef union sockunion sockunion_t;
-#define __SOCKUNION_DECLARED
-#endif /* __SOCKUNION_DECLARED */
-
static void im6f_commit(struct in6_mfilter *);
static int im6f_get_source(struct in6_mfilter *imf,
const struct sockaddr_in6 *psin,
static void im6f_reap(struct in6_mfilter *);
static int im6o_grow(struct ip6_moptions *, size_t);
static size_t im6o_match_group(const struct ip6_moptions *,
- const struct ifnet *, const struct sockaddr *);
+ const struct ifnet *, const struct sockaddr_in6 *);
static struct in6_msource *
- im6o_match_source(const struct ip6_moptions *, const size_t,
- const struct sockaddr *);
+ im6o_match_source(const struct ip6_moptions *,
+ const size_t, const struct sockaddr_in6 *);
static void im6s_merge(struct ip6_msource *ims,
const struct in6_msource *lims, const int rollback);
static int in6_mc_get(struct ifnet *, const struct in6_addr *,
static struct in6_msource *in6ms_alloc(int);
static void in6ms_free(struct in6_msource *);
-#define IM6O_CAST_TO_NONCONST(x) ((struct ip6_moptions *)(void *)(uintptr_t)x)
-#define IN6M_CAST_TO_NONCONST(x) ((struct in6_multi *)(void *)(uintptr_t)x)
-
/*
* IPv6 source tree comparison function.
*
*/
static size_t
im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp,
- const struct sockaddr *group)
+ const struct sockaddr_in6 *group)
{
const struct sockaddr_in6 *gsin6;
struct in6_multi *pinm;
int idx;
int nmships;
- IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo));
+ IM6O_LOCK_ASSERT_HELD(__DECONST(struct ip6_moptions *, imo));
- gsin6 = (const struct sockaddr_in6 *)group;
+ gsin6 = group;
/* The im6o_membership array may be lazy allocated. */
if (imo->im6o_membership == NULL || imo->im6o_num_memberships == 0)
*/
static struct in6_msource *
im6o_match_source(const struct ip6_moptions *imo, const size_t gidx,
- const struct sockaddr *src)
+ const struct sockaddr_in6 *src)
{
struct ip6_msource find;
struct in6_mfilter *imf;
struct ip6_msource *ims;
- const sockunion_t *psa;
+ const struct sockaddr_in6 *psa;
- IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo));
+ IM6O_LOCK_ASSERT_HELD(__DECONST(struct ip6_moptions *, imo));
- VERIFY(src->sa_family == AF_INET6);
+ VERIFY(src->sin6_family == AF_INET6);
VERIFY(gidx != (size_t)-1 && gidx < imo->im6o_num_memberships);
/* The im6o_mfilters array may be lazy allocated. */
return (NULL);
imf = &imo->im6o_mfilters[gidx];
- psa = (const sockunion_t *)src;
- find.im6s_addr = psa->sin6.sin6_addr;
+ psa = src;
+ find.im6s_addr = psa->sin6_addr;
in6_clearscope(&find.im6s_addr); /* XXX */
ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find);
*/
int
im6o_mc_filter(const struct ip6_moptions *imo, const struct ifnet *ifp,
- const struct sockaddr *group, const struct sockaddr *src)
+ const struct sockaddr_in6 *group, const struct sockaddr_in6 *src)
{
size_t gidx;
struct in6_msource *ims;
int mode;
- IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo));
+ IM6O_LOCK_ASSERT_HELD(__DECONST(struct ip6_moptions *, imo));
VERIFY(ifp != NULL);
gidx = im6o_match_group(imo, ifp, group);
lims->im6sl_st[1] = lims->im6sl_st[0];
} else {
/* revert source added t1 */
- MLD_PRINTF(("%s: free in6ms %p\n", __func__, lims));
+ MLD_PRINTF(("%s: free in6ms 0x%llx\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(lims)));
RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims);
in6ms_free(lims);
imf->im6f_nsrc--;
lims = (struct in6_msource *)ims;
if ((lims->im6sl_st[0] == MCAST_UNDEFINED) &&
(lims->im6sl_st[1] == MCAST_UNDEFINED)) {
- MLD_PRINTF(("%s: free in6ms %p\n", __func__, lims));
+ MLD_PRINTF(("%s: free in6ms 0x%llx\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(lims)));
RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims);
in6ms_free(lims);
imf->im6f_nsrc--;
RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) {
lims = (struct in6_msource *)ims;
- MLD_PRINTF(("%s: free in6ms %p\n", __func__, lims));
+ MLD_PRINTF(("%s: free in6ms 0x%llx\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(lims)));
RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims);
in6ms_free(lims);
imf->im6f_nsrc--;
RB_INSERT(ip6_msource_tree, &inm->in6m_srcs, nims);
++inm->in6m_nsrc;
ims = nims;
- MLD_PRINTF(("%s: allocated %s as %p\n", __func__,
- ip6_sprintf(addr), ims));
+ MLD_PRINTF(("%s: allocated %s as 0x%llx\n", __func__,
+ ip6_sprintf(addr), (uint64_t)VM_KERNEL_ADDRPERM(ims)));
}
*pims = ims;
im6s_get_mode(const struct in6_multi *inm, const struct ip6_msource *ims,
uint8_t t)
{
- IN6M_LOCK_ASSERT_HELD(IN6M_CAST_TO_NONCONST(inm));
+ IN6M_LOCK_ASSERT_HELD(__DECONST(struct in6_multi *, inm));
t = !!t;
if (inm->in6m_st[t].iss_ex > 0 &&
static int
in6m_merge(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
{
- struct ip6_msource *ims, *nims;
+ struct ip6_msource *ims, *nims = NULL;
struct in6_msource *lims;
int schanged, error;
int nsrc0, nsrc1;
inm->in6m_st[1].iss_asm++;
}
- MLD_PRINTF(("%s: merged imf %p to inm %p\n", __func__, imf, inm));
+ MLD_PRINTF(("%s: merged imf 0x%llx to inm 0x%llx\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(imf),
+ (uint64_t)VM_KERNEL_ADDRPERM(inm)));
in6m_print(inm);
out_reap:
IN6M_LOCK_ASSERT_HELD(inm);
- MLD_PRINTF(("%s: commit inm %p\n", __func__, inm));
+ MLD_PRINTF(("%s: commit inm 0x%llx\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(inm)));
MLD_PRINTF(("%s: pre commit:\n", __func__));
in6m_print(inm);
ims->im6s_st[1].ex > 0 || ims->im6s_st[1].in > 0 ||
ims->im6s_stp != 0)
continue;
- MLD_PRINTF(("%s: free ims %p\n", __func__, ims));
+ MLD_PRINTF(("%s: free ims 0x%llx\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(ims)));
RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims);
ip6ms_free(ims);
inm->in6m_nsrc--;
IN6M_LOCK_ASSERT_HELD(inm);
RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs, tims) {
- MLD_PRINTF(("%s: free ims %p\n", __func__, ims));
+ MLD_PRINTF(("%s: free ims 0x%llx\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(ims)));
RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims);
ip6ms_free(ims);
inm->in6m_nsrc--;
return (NULL);
}
- delay = (delay * PR_SLOWHZ) / hz;
-
error = in6_mc_join(ifp, mcaddr, NULL, &imm->i6mm_maddr, delay);
if (error) {
*errorp = error;
struct in6_mfilter timf;
struct in6_multi *inm = NULL;
int error = 0;
+ struct mld_tparams mtp;
/*
* Sanity: Check scope zone ID was set for ifp, if and
VERIFY(mcaddr->s6_addr16[1] != 0);
}
- MLD_PRINTF(("%s: join %s on %p(%s%d))\n", __func__,
- ip6_sprintf(mcaddr), ifp, ifp->if_name, ifp->if_unit));
+ MLD_PRINTF(("%s: join %s on 0x%llx(%s))\n", __func__,
+ ip6_sprintf(mcaddr), (uint64_t)VM_KERNEL_ADDRPERM(ifp),
+ if_name(ifp)));
+ bzero(&mtp, sizeof (mtp));
*pinm = NULL;
/*
}
MLD_PRINTF(("%s: doing mld downcall\n", __func__));
- error = mld_change_state(inm, delay);
+ error = mld_change_state(inm, &mtp, delay);
if (error) {
MLD_PRINTF(("%s: failed to update source\n", __func__));
+ im6f_rollback(imf);
goto out_in6m_release;
}
out_in6m_release:
if (error) {
- MLD_PRINTF(("%s: dropping ref on %p\n", __func__, inm));
+ MLD_PRINTF(("%s: dropping ref on 0x%llx\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(inm)));
IN6M_UNLOCK(inm);
IN6M_REMREF(inm);
} else {
*pinm = inm; /* keep refcount from in6_mc_get() */
}
+ /* schedule timer now that we've dropped the lock(s) */
+ mld_set_timeout(&mtp);
+
return (error);
}
{
struct in6_mfilter timf;
int error, lastref;
+ struct mld_tparams mtp;
+ bzero(&mtp, sizeof (mtp));
error = 0;
IN6M_LOCK_ASSERT_NOTHELD(inm);
in6_multihead_lock_exclusive();
IN6M_LOCK(inm);
- MLD_PRINTF(("%s: leave inm %p, %s/%s%d, imf %p\n", __func__,
- inm, ip6_sprintf(&inm->in6m_addr),
+ MLD_PRINTF(("%s: leave inm 0x%llx, %s/%s%d, imf 0x%llx\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(inm), ip6_sprintf(&inm->in6m_addr),
(in6m_is_ifp_detached(inm) ? "null" : inm->in6m_ifp->if_name),
- inm->in6m_ifp->if_unit, imf));
+ inm->in6m_ifp->if_unit, (uint64_t)VM_KERNEL_ADDRPERM(imf)));
/*
* If no imf was specified (i.e. kernel consumer),
KASSERT(error == 0, ("%s: failed to merge inm state\n", __func__));
MLD_PRINTF(("%s: doing mld downcall\n", __func__));
- error = mld_change_state(inm, 0);
+ error = mld_change_state(inm, &mtp, 0);
#if MLD_DEBUG
if (error)
MLD_PRINTF(("%s: failed mld downcall\n", __func__));
if (lastref)
IN6M_REMREF(inm); /* for in6_multihead list */
+ /* schedule timer now that we've dropped the lock(s) */
+ mld_set_timeout(&mtp);
+
return (error);
}
in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
{
struct group_source_req gsr;
- sockunion_t *gsa, *ssa;
+ struct sockaddr_in6 *gsa, *ssa;
struct ifnet *ifp;
struct in6_mfilter *imf;
struct ip6_moptions *imo;
size_t idx;
uint16_t fmode;
int error, doblock;
+ struct mld_tparams mtp;
+ bzero(&mtp, sizeof (mtp));
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_in6 *)&gsr.gsr_group;
+ ssa = (struct sockaddr_in6 *)&gsr.gsr_source;
switch (sopt->sopt_name) {
case MCAST_BLOCK_SOURCE:
if (error)
return (error);
- if (gsa->sin6.sin6_family != AF_INET6 ||
- gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+ if (gsa->sin6_family != AF_INET6 ||
+ gsa->sin6_len != sizeof(struct sockaddr_in6))
return (EINVAL);
- if (ssa->sin6.sin6_family != AF_INET6 ||
- ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+ if (ssa->sin6_family != AF_INET6 ||
+ ssa->sin6_len != sizeof(struct sockaddr_in6))
return (EINVAL);
ifnet_head_lock_shared();
MLD_PRINTF(("%s: unknown sopt_name %d\n",
__func__, sopt->sopt_name));
return (EOPNOTSUPP);
- break;
}
- if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
+ if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr))
return (EINVAL);
- (void) in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
+ (void) in6_setscope(&gsa->sin6_addr, ifp, NULL);
/*
* Check if we are actually a member of this group.
return (ENOMEM);
IM6O_LOCK(imo);
- idx = im6o_match_group(imo, ifp, &gsa->sa);
+ idx = im6o_match_group(imo, ifp, gsa);
if (idx == (size_t)-1 || imo->im6o_mfilters == NULL) {
error = EADDRNOTAVAIL;
goto out_imo_locked;
* Asked to unblock, but nothing to unblock.
* If adding a new block entry, allocate it.
*/
- ims = im6o_match_source(imo, idx, &ssa->sa);
+ ims = im6o_match_source(imo, idx, ssa);
if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
MLD_PRINTF(("%s: source %s %spresent\n", __func__,
- ip6_sprintf(&ssa->sin6.sin6_addr),
+ ip6_sprintf(&ssa->sin6_addr),
doblock ? "" : "not "));
error = EADDRNOTAVAIL;
goto out_imo_locked;
*/
if (doblock) {
MLD_PRINTF(("%s: %s source\n", __func__, "block"));
- ims = im6f_graft(imf, fmode, &ssa->sin6);
+ ims = im6f_graft(imf, fmode, ssa);
if (ims == NULL)
error = ENOMEM;
} else {
MLD_PRINTF(("%s: %s source\n", __func__, "allow"));
- error = im6f_prune(imf, &ssa->sin6);
+ error = im6f_prune(imf, ssa);
}
if (error) {
}
MLD_PRINTF(("%s: doing mld downcall\n", __func__));
- error = mld_change_state(inm, 0);
+ error = mld_change_state(inm, &mtp, 0);
IN6M_UNLOCK(inm);
#if MLD_DEBUG
if (error)
out_imo_locked:
IM6O_UNLOCK(imo);
IM6O_REMREF(imo); /* from in6p_findmoptions() */
+
+ /* schedule timer now that we've dropped the lock(s) */
+ mld_set_timeout(&mtp);
+
return (error);
}
static int
in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
{
- struct __msfilterreq64 msfr, msfr64;
+ struct __msfilterreq64 msfr = {}, msfr64;
struct __msfilterreq32 msfr32;
- sockunion_t *gsa;
+ struct sockaddr_in6 *gsa;
struct ifnet *ifp;
struct ip6_moptions *imo;
struct in6_mfilter *imf;
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),
if (error)
return (error);
/* we never use msfr.msfr_srcs; */
- memcpy(&msfr, &msfr32, sizeof(msfr));
+ memcpy(&msfr, &msfr32, sizeof(msfr32));
}
if (msfr.msfr_group.ss_family != AF_INET6 ||
msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6))
return (EINVAL);
- gsa = (sockunion_t *)&msfr.msfr_group;
- if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
+ gsa = (struct sockaddr_in6 *)&msfr.msfr_group;
+ if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr))
return (EINVAL);
ifnet_head_lock_shared();
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 > in6_mcast_maxsocksrc)
msfr.msfr_nsrcs = in6_mcast_maxsocksrc;
- (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
+ (void)in6_setscope(&gsa->sin6_addr, ifp, NULL);
IM6O_LOCK(imo);
/*
* Lookup group on the socket.
*/
- idx = im6o_match_group(imo, ifp, &gsa->sa);
+ idx = im6o_match_group(imo, ifp, gsa);
if (idx == (size_t)-1 || imo->im6o_mfilters == NULL) {
IM6O_UNLOCK(imo);
return (EADDRNOTAVAIL);
tmp_ptr = CAST_USER_ADDR_T(msfr32.msfr_srcs);
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) {
IM6O_UNLOCK(imo);
IM6O_UNLOCK(imo);
if (tss != NULL) {
- error = copyout(tss, tmp_ptr,
- sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
+ error = copyout(tss, tmp_ptr, ncsrcs * sizeof(*tss));
FREE(tss, M_TEMP);
if (error)
return (error);
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));
* 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)) {
+ if (SOCK_PROTO(inp->inp_socket) == IPPROTO_DIVERT ||
+ (SOCK_TYPE(inp->inp_socket) != SOCK_RAW &&
+ SOCK_TYPE(inp->inp_socket) != SOCK_DGRAM)) {
return (EOPNOTSUPP);
}
return NULL;
if (in6p != NULL && (in6p->inp_flags & INP_BOUND_IF))
- ifscope = in6p->inp_boundif;
+ ifscope = in6p->inp_boundifp->if_index;
ifp = NULL;
memset(&ro6, 0, sizeof(struct route_in6));
if (ro6.ro_rt != NULL) {
ifp = ro6.ro_rt->rt_ifp;
VERIFY(ifp != NULL);
- rtfree(ro6.ro_rt);
}
+ ROUTE_RELEASE(&ro6);
return (ifp);
}
ifa = ifa_ifpgetprimary(ifp, AF_INET);
if (ifa == NULL)
return (EADDRNOTAVAIL);
- sin = (struct sockaddr_in *)ifa->ifa_addr;
+ sin = (struct sockaddr_in *)(uintptr_t)(size_t)ifa->ifa_addr;
v4mreq->imr_interface.s_addr = sin->sin_addr.s_addr;
IFA_REMREF(ifa);
in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
{
struct group_source_req gsr;
- sockunion_t *gsa, *ssa;
+ struct sockaddr_in6 *gsa, *ssa;
struct ifnet *ifp;
struct in6_mfilter *imf;
struct ip6_moptions *imo;
size_t idx;
int error, is_new;
uint32_t scopeid = 0;
+ struct mld_tparams mtp;
+ bzero(&mtp, sizeof (mtp));
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_in6 *)&gsr.gsr_group;
+ ssa = (struct sockaddr_in6 *)&gsr.gsr_source;
/*
* Chew everything into struct group_source_req.
switch (sopt->sopt_name) {
case IPV6_JOIN_GROUP: {
struct ipv6_mreq mreq;
- struct sockaddr_in6 *gsin6;
error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq),
sizeof(struct ipv6_mreq));
return (inp_join_group(inp, &v4sopt));
}
- gsa->sin6.sin6_family = AF_INET6;
- gsa->sin6.sin6_len = sizeof(struct sockaddr_in6);
- gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr;
-
- gsin6 = &gsa->sin6;
+ gsa->sin6_family = AF_INET6;
+ gsa->sin6_len = sizeof(struct sockaddr_in6);
+ gsa->sin6_addr = mreq.ipv6mr_multiaddr;
/* Only allow IPv6 multicast addresses */
- if (IN6_IS_ADDR_MULTICAST(&gsin6->sin6_addr) == 0) {
+ if (IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr) == 0) {
return (EINVAL);
}
if (mreq.ipv6mr_interface == 0) {
- ifp = in6p_lookup_mcast_ifp(inp, gsin6);
+ ifp = in6p_lookup_mcast_ifp(inp, gsa);
} else {
ifnet_head_lock_shared();
if ((u_int)if_index < mreq.ipv6mr_interface) {
ifp = ifindex2ifnet[mreq.ipv6mr_interface];
ifnet_head_done();
}
- MLD_PRINTF(("%s: ipv6mr_interface = %d, ifp = %p\n",
- __func__, mreq.ipv6mr_interface, ifp));
+ MLD_PRINTF(("%s: ipv6mr_interface = %d, ifp = 0x%llx\n",
+ __func__, mreq.ipv6mr_interface,
+ (uint64_t)VM_KERNEL_ADDRPERM(ifp)));
break;
}
if (error)
return (error);
- if (gsa->sin6.sin6_family != AF_INET6 ||
- gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+ if (gsa->sin6_family != AF_INET6 ||
+ gsa->sin6_len != sizeof(struct sockaddr_in6))
return (EINVAL);
if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
- if (ssa->sin6.sin6_family != AF_INET6 ||
- ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+ if (ssa->sin6_family != AF_INET6 ||
+ ssa->sin6_len != sizeof(struct sockaddr_in6))
return (EINVAL);
- if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr))
+ if (IN6_IS_ADDR_MULTICAST(&ssa->sin6_addr))
return (EINVAL);
/*
* TODO: Validate embedded scope ID in source
* list entry against passed-in ifp, if and only
* if source list filter entry is iface or node local.
*/
- in6_clearscope(&ssa->sin6.sin6_addr);
- ssa->sin6.sin6_port = 0;
- ssa->sin6.sin6_scope_id = 0;
+ in6_clearscope(&ssa->sin6_addr);
+ ssa->sin6_port = 0;
+ ssa->sin6_scope_id = 0;
}
ifnet_head_lock_shared();
MLD_PRINTF(("%s: unknown sopt_name %d\n",
__func__, sopt->sopt_name));
return (EOPNOTSUPP);
- break;
}
- if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
+ if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr))
return (EINVAL);
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0)
return (EADDRNOTAVAIL);
- gsa->sin6.sin6_port = 0;
- gsa->sin6.sin6_scope_id = 0;
+ 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);
+ }
+
+ gsa->sin6_port = 0;
+ gsa->sin6_scope_id = 0;
/*
* Always set the scope zone ID on memberships created from userland.
* Use the passed-in ifp to do this.
*/
- (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, &scopeid);
+ (void)in6_setscope(&gsa->sin6_addr, ifp, &scopeid);
/*
* Some addresses are not valid without an embedded scopeid.
* This check must be present because otherwise we will later hit
* a VERIFY() in in6_mc_join().
*/
- if ((IN6_IS_ADDR_MC_LINKLOCAL(&gsa->sin6.sin6_addr) ||
- IN6_IS_ADDR_MC_INTFACELOCAL(&gsa->sin6.sin6_addr)) && scopeid == 0)
+ if ((IN6_IS_ADDR_MC_LINKLOCAL(&gsa->sin6_addr) ||
+ IN6_IS_ADDR_MC_INTFACELOCAL(&gsa->sin6_addr)) &&
+ (scopeid == 0 || gsa->sin6_addr.s6_addr16[1] == 0))
return (EINVAL);
imo = in6p_findmoptions(inp);
return (ENOMEM);
IM6O_LOCK(imo);
- idx = im6o_match_group(imo, ifp, &gsa->sa);
+ idx = im6o_match_group(imo, ifp, gsa);
if (idx == (size_t)-1) {
is_new = 1;
} else {
inm = imo->im6o_membership[idx];
imf = &imo->im6o_mfilters[idx];
- if (ssa->ss.ss_family != AF_UNSPEC) {
+ if (ssa->sin6_family != AF_UNSPEC) {
/*
* MCAST_JOIN_SOURCE_GROUP on an exclusive membership
* is an error. On an existing inclusive membership,
* full-state SSM API with the delta-based API,
* which is discouraged in the relevant RFCs.
*/
- lims = im6o_match_source(imo, idx, &ssa->sa);
+ lims = im6o_match_source(imo, idx, ssa);
if (lims != NULL /*&&
lims->im6sl_st[1] == MCAST_INCLUDE*/) {
error = EADDRNOTAVAIL;
* XXX: Should check for non-NULL lims (node exists but may
* not be in-mode) for interop with full-state API.
*/
- if (ssa->ss.ss_family != AF_UNSPEC) {
+ if (ssa->sin6_family != AF_UNSPEC) {
/* Membership starts in IN mode */
if (is_new) {
MLD_PRINTF(("%s: new join w/source\n", __func__);
} else {
MLD_PRINTF(("%s: %s source\n", __func__, "allow"));
}
- lims = im6f_graft(imf, MCAST_INCLUDE, &ssa->sin6);
+ lims = im6f_graft(imf, MCAST_INCLUDE, ssa);
if (lims == NULL) {
MLD_PRINTF(("%s: merge imf state failed\n",
__func__));
*/
if (is_new) {
+ /*
+ * See inp_join_group() for why we need to unlock
+ */
+ IM6O_ADDREF_LOCKED(imo);
+ IM6O_UNLOCK(imo);
+ socket_unlock(inp->inp_socket, 0);
+
VERIFY(inm == NULL);
- error = in6_mc_join(ifp, &gsa->sin6.sin6_addr, imf, &inm, 0);
+ error = in6_mc_join(ifp, &gsa->sin6_addr, imf, &inm, 0);
VERIFY(inm != NULL || error != 0);
+
+ socket_lock(inp->inp_socket, 0);
+ IM6O_REMREF(imo);
+ IM6O_LOCK(imo);
+
if (error)
goto out_im6o_free;
imo->im6o_membership[idx] = inm; /* from in6_mc_join() */
goto out_im6f_rollback;
}
MLD_PRINTF(("%s: doing mld downcall\n", __func__));
- error = mld_change_state(inm, 0);
+ error = mld_change_state(inm, &mtp, 0);
IN6M_UNLOCK(inm);
if (error) {
MLD_PRINTF(("%s: failed mld downcall\n",
out_imo_locked:
IM6O_UNLOCK(imo);
IM6O_REMREF(imo); /* from in6p_findmoptions() */
+
+ /* schedule timer now that we've dropped the lock(s) */
+ mld_set_timeout(&mtp);
+
return (error);
}
{
struct ipv6_mreq mreq;
struct group_source_req gsr;
- sockunion_t *gsa, *ssa;
+ struct sockaddr_in6 *gsa, *ssa;
struct ifnet *ifp;
struct in6_mfilter *imf;
struct ip6_moptions *imo;
uint32_t ifindex = 0;
size_t idx;
int error, is_final;
+ struct mld_tparams mtp;
+ bzero(&mtp, sizeof (mtp));
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_in6 *)&gsr.gsr_group;
+ ssa = (struct sockaddr_in6 *)&gsr.gsr_source;
/*
* Chew everything passed in up into a struct group_source_req
*/
switch (sopt->sopt_name) {
case IPV6_LEAVE_GROUP: {
- struct sockaddr_in6 *gsin6;
error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq),
sizeof(struct ipv6_mreq));
return (inp_leave_group(inp, &v4sopt));
}
- gsa->sin6.sin6_family = AF_INET6;
- gsa->sin6.sin6_len = sizeof(struct sockaddr_in6);
- gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr;
- gsa->sin6.sin6_port = 0;
- gsa->sin6.sin6_scope_id = 0;
+ gsa->sin6_family = AF_INET6;
+ gsa->sin6_len = sizeof(struct sockaddr_in6);
+ gsa->sin6_addr = mreq.ipv6mr_multiaddr;
+ gsa->sin6_port = 0;
+ gsa->sin6_scope_id = 0;
ifindex = mreq.ipv6mr_interface;
- gsin6 = &gsa->sin6;
/* Only allow IPv6 multicast addresses */
- if (IN6_IS_ADDR_MULTICAST(&gsin6->sin6_addr) == 0) {
+ if (IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr) == 0) {
return (EINVAL);
}
break;
if (error)
return (error);
- if (gsa->sin6.sin6_family != AF_INET6 ||
- gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+ if (gsa->sin6_family != AF_INET6 ||
+ gsa->sin6_len != sizeof(struct sockaddr_in6))
return (EINVAL);
if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
- if (ssa->sin6.sin6_family != AF_INET6 ||
- ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+ if (ssa->sin6_family != AF_INET6 ||
+ ssa->sin6_len != sizeof(struct sockaddr_in6))
return (EINVAL);
- if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr))
+ if (IN6_IS_ADDR_MULTICAST(&ssa->sin6_addr))
return (EINVAL);
/*
* TODO: Validate embedded scope ID in source
* list entry against passed-in ifp, if and only
* if source list filter entry is iface or node local.
*/
- in6_clearscope(&ssa->sin6.sin6_addr);
+ in6_clearscope(&ssa->sin6_addr);
}
- gsa->sin6.sin6_port = 0;
- gsa->sin6.sin6_scope_id = 0;
+ gsa->sin6_port = 0;
+ gsa->sin6_scope_id = 0;
ifindex = gsr.gsr_interface;
break;
MLD_PRINTF(("%s: unknown sopt_name %d\n",
__func__, sopt->sopt_name));
return (EOPNOTSUPP);
- break;
}
- if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
+ if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr))
return (EINVAL);
/*
ifnet_head_done();
if (ifp == NULL)
return (EADDRNOTAVAIL);
- (void) in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
+ (void) in6_setscope(&gsa->sin6_addr, ifp, NULL);
} else {
- error = sa6_embedscope(&gsa->sin6, ip6_use_defzone);
+ error = sa6_embedscope(gsa, ip6_use_defzone);
if (error)
return (EADDRNOTAVAIL);
/*
* directly until such time as this implementation is
* refactored, assuming the scope IDs are the way to go.
*/
- ifindex = ntohs(gsa->sin6.sin6_addr.s6_addr16[1]);
+ ifindex = ntohs(gsa->sin6_addr.s6_addr16[1]);
if (ifindex == 0) {
MLD_PRINTF(("%s: warning: no ifindex, looking up "
"ifp for group %s.\n", __func__,
- ip6_sprintf(&gsa->sin6.sin6_addr)));
- ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6);
+ ip6_sprintf(&gsa->sin6_addr)));
+ ifp = in6p_lookup_mcast_ifp(inp, gsa);
} else {
+ if (!IF_INDEX_IN_RANGE(ifindex))
+ return (EADDRNOTAVAIL);
ifnet_head_lock_shared();
ifp = ifindex2ifnet[ifindex];
ifnet_head_done();
}
VERIFY(ifp != NULL);
- MLD_PRINTF(("%s: ifp = %p\n", __func__, ifp));
+ MLD_PRINTF(("%s: ifp = 0x%llx\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(ifp)));
/*
* Find the membership in the membership array.
return (ENOMEM);
IM6O_LOCK(imo);
- idx = im6o_match_group(imo, ifp, &gsa->sa);
+ idx = im6o_match_group(imo, ifp, gsa);
if (idx == (size_t)-1) {
error = EADDRNOTAVAIL;
goto out_locked;
inm = imo->im6o_membership[idx];
imf = &imo->im6o_mfilters[idx];
- if (ssa->ss.ss_family != AF_UNSPEC)
+ if (ssa->sin6_family != AF_UNSPEC)
is_final = 0;
/*
error = EADDRNOTAVAIL;
goto out_locked;
}
- ims = im6o_match_source(imo, idx, &ssa->sa);
+ ims = im6o_match_source(imo, idx, ssa);
if (ims == NULL) {
- MLD_PRINTF(("%s: source %p %spresent\n", __func__,
- ip6_sprintf(&ssa->sin6.sin6_addr),
+ MLD_PRINTF(("%s: source %s %spresent\n", __func__,
+ ip6_sprintf(&ssa->sin6_addr),
"not "));
error = EADDRNOTAVAIL;
goto out_locked;
}
MLD_PRINTF(("%s: %s source\n", __func__, "block"));
- error = im6f_prune(imf, &ssa->sin6);
+ error = im6f_prune(imf, ssa);
if (error) {
MLD_PRINTF(("%s: merge imf state failed\n",
__func__));
}
MLD_PRINTF(("%s: doing mld downcall\n", __func__));
- error = mld_change_state(inm, 0);
+ error = mld_change_state(inm, &mtp, 0);
if (error) {
MLD_PRINTF(("%s: failed mld downcall\n", __func__));
}
/* Remove the gap in the membership array. */
VERIFY(inm == imo->im6o_membership[idx]);
imo->im6o_membership[idx] = NULL;
+
+ /*
+ * See inp_join_group() for why we need to unlock
+ */
+ IM6O_ADDREF_LOCKED(imo);
+ IM6O_UNLOCK(imo);
+ socket_unlock(inp->inp_socket, 0);
+
IN6M_REMREF(inm);
+
+ socket_lock(inp->inp_socket, 0);
+ IM6O_REMREF(imo);
+ IM6O_LOCK(imo);
+
for (++idx; idx < imo->im6o_num_memberships; ++idx) {
imo->im6o_membership[idx-1] = imo->im6o_membership[idx];
imo->im6o_mfilters[idx-1] = imo->im6o_mfilters[idx];
out_locked:
IM6O_UNLOCK(imo);
IM6O_REMREF(imo); /* from in6p_findmoptions() */
+
+ /* schedule timer now that we've dropped the lock(s) */
+ mld_set_timeout(&mtp);
+
return (error);
}
static int
in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
{
- struct __msfilterreq64 msfr, msfr64;
+ struct __msfilterreq64 msfr = {}, msfr64;
struct __msfilterreq32 msfr32;
- sockunion_t *gsa;
+ struct sockaddr_in6 *gsa;
struct ifnet *ifp;
struct in6_mfilter *imf;
struct ip6_moptions *imo;
struct in6_multi *inm;
size_t idx;
int error;
- user_addr_t tmp_ptr;
+ user_addr_t tmp_ptr;
+ struct mld_tparams mtp;
+
+ bzero(&mtp, sizeof (mtp));
if (IS_64BIT_PROCESS(current_proc())) {
error = sooptcopyin(sopt, &msfr64,
memcpy(&msfr, &msfr32, sizeof(msfr));
}
+ 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 > in6_mcast_maxsocksrc)
return (ENOBUFS);
msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6))
return (EINVAL);
- gsa = (sockunion_t *)&msfr.msfr_group;
- if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
+ gsa = (struct sockaddr_in6 *)&msfr.msfr_group;
+ if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr))
return (EINVAL);
- gsa->sin6.sin6_port = 0; /* ignore port */
+ gsa->sin6_port = 0; /* ignore port */
ifnet_head_lock_shared();
if (msfr.msfr_ifindex == 0 || (u_int)if_index < msfr.msfr_ifindex) {
if (ifp == NULL)
return (EADDRNOTAVAIL);
- (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
+ (void)in6_setscope(&gsa->sin6_addr, ifp, NULL);
/*
* Take the INP write lock.
return (ENOMEM);
IM6O_LOCK(imo);
- idx = im6o_match_group(imo, ifp, &gsa->sa);
+ idx = im6o_match_group(imo, ifp, gsa);
if (idx == (size_t)-1 || imo->im6o_mfilters == NULL) {
error = EADDRNOTAVAIL;
goto out_imo_locked;
MLD_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;
}
error = copyin(tmp_ptr, kss,
- sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
+ (size_t) msfr.msfr_nsrcs * sizeof(*kss));
if (error) {
FREE(kss, M_TEMP);
goto out_imo_locked;
}
MLD_PRINTF(("%s: doing mld downcall\n", __func__));
- error = mld_change_state(inm, 0);
+ error = mld_change_state(inm, &mtp, 0);
IN6M_UNLOCK(inm);
#if MLD_DEBUG
if (error)
IM6O_UNLOCK(imo);
IM6O_REMREF(imo); /* from in6p_findmoptions() */
+ /* schedule timer now that we've dropped the lock(s) */
+ mld_set_timeout(&mtp);
+
return (error);
}
* 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))
+ 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) {
break; /* abort */
}
RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) {
- MLD_PRINTF(("%s: visit node %p\n", __func__, ims));
+ MLD_PRINTF(("%s: visit node 0x%llx\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(ims)));
/*
* Only copy-out sources which are in-mode.
*/
panic("%s: Attempt to attach an already attached in6m=%p",
__func__, in6m);
/* NOTREACHED */
- } else if (in6m->in6m_debug & IFD_TRASHED) {
- panic("%s: Attempt to reattach a detached in6m=%p",
- __func__, in6m);
- /* NOTREACHED */
}
in6m->in6m_reqcnt++;
void
in6_multihead_lock_assert(int what)
{
- lck_rw_assert(&in6_multihead_lock, what);
+#if !MACH_ASSERT
+#pragma unused(what)
+#endif
+ LCK_RW_ASSERT(&in6_multihead_lock, what);
}
void
static const char *in6m_statestrs[] = {
"not-member\n",
"silent\n",
+ "reporting\n",
"idle\n",
"lazy\n",
"sleeping\n",
{
int t;
- IN6M_LOCK_ASSERT_HELD(IN6M_CAST_TO_NONCONST(inm));
+ IN6M_LOCK_ASSERT_HELD(__DECONST(struct in6_multi *, inm));
if (mld_debug == 0)
return;
- printf("%s: --- begin in6m %p ---\n", __func__, inm);
- printf("addr %s ifp %p(%s%d) ifma %p\n",
+ printf("%s: --- begin in6m 0x%llx ---\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(inm));
+ printf("addr %s ifp 0x%llx(%s) ifma 0x%llx\n",
ip6_sprintf(&inm->in6m_addr),
- inm->in6m_ifp,
- inm->in6m_ifp->if_name,
- inm->in6m_ifp->if_unit,
- inm->in6m_ifma);
+ (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifp),
+ if_name(inm->in6m_ifp),
+ (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifma));
printf("timer %u state %s refcount %u scq.len %u\n",
inm->in6m_timer,
in6m_state_str(inm->in6m_state),
inm->in6m_refcount,
inm->in6m_scq.ifq_len);
- printf("mli %p nsrc %lu sctimer %u scrv %u\n",
- inm->in6m_mli,
+ printf("mli 0x%llx nsrc %lu sctimer %u scrv %u\n",
+ (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_mli),
inm->in6m_nsrc,
inm->in6m_sctimer,
inm->in6m_scrv);
inm->in6m_st[t].iss_in,
inm->in6m_st[t].iss_rec);
}
- printf("%s: --- end in6m %p ---\n", __func__, inm);
+ printf("%s: --- end in6m 0x%llx ---\n", __func__,
+ (uint64_t)VM_KERNEL_ADDRPERM(inm));
}
#else