summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet6/mld6.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet6/mld6.c')
-rw-r--r--freebsd/sys/netinet6/mld6.c229
1 files changed, 120 insertions, 109 deletions
diff --git a/freebsd/sys/netinet6/mld6.c b/freebsd/sys/netinet6/mld6.c
index b00f03ef..cc946f67 100644
--- a/freebsd/sys/netinet6/mld6.c
+++ b/freebsd/sys/netinet6/mld6.c
@@ -112,7 +112,7 @@ static void mli_delete_locked(const struct ifnet *);
static void mld_dispatch_packet(struct mbuf *);
static void mld_dispatch_queue(struct mbufq *, int);
static void mld_final_leave(struct in6_multi *, struct mld_ifsoftc *);
-static void mld_fasttimo_vnet(void);
+static void mld_fasttimo_vnet(struct in6_multi_head *inmh);
static int mld_handle_state_change(struct in6_multi *,
struct mld_ifsoftc *);
static int mld_initial_join(struct in6_multi *, struct mld_ifsoftc *,
@@ -141,14 +141,15 @@ static int mld_v2_enqueue_group_record(struct mbufq *,
struct in6_multi *, const int, const int, const int,
const int);
static int mld_v2_input_query(struct ifnet *, const struct ip6_hdr *,
- struct mbuf *, const int, const int);
+ struct mbuf *, struct mldv2_query *, const int, const int);
static int mld_v2_merge_state_changes(struct in6_multi *,
struct mbufq *);
static void mld_v2_process_group_timers(struct in6_multi_head *,
struct mbufq *, struct mbufq *,
struct in6_multi *, const int);
static int mld_v2_process_group_query(struct in6_multi *,
- struct mld_ifsoftc *mli, int, struct mbuf *, const int);
+ struct mld_ifsoftc *mli, int, struct mbuf *,
+ struct mldv2_query *, const int);
static int sysctl_mld_gsr(SYSCTL_HANDLER_ARGS);
static int sysctl_mld_ifinfo(SYSCTL_HANDLER_ARGS);
@@ -245,6 +246,10 @@ static int mld_v1enable = 1;
SYSCTL_INT(_net_inet6_mld, OID_AUTO, v1enable, CTLFLAG_RWTUN,
&mld_v1enable, 0, "Enable fallback to MLDv1");
+static int mld_v2enable = 1;
+SYSCTL_INT(_net_inet6_mld, OID_AUTO, v2enable, CTLFLAG_RWTUN,
+ &mld_v2enable, 0, "Enable MLDv2");
+
static int mld_use_allow = 1;
SYSCTL_INT(_net_inet6_mld, OID_AUTO, use_allow, CTLFLAG_RWTUN,
&mld_use_allow, 0, "Use ALLOW/BLOCK for RFC 4604 SSM joins/leaves");
@@ -535,45 +540,48 @@ out:
* XXX This routine is also bitten by unlocked ifma_protospec access.
*/
void
-mld_ifdetach(struct ifnet *ifp)
+mld_ifdetach(struct ifnet *ifp, struct in6_multi_head *inmh)
{
+ struct epoch_tracker et;
struct mld_ifsoftc *mli;
- struct ifmultiaddr *ifma, *next;
+ struct ifmultiaddr *ifma;
struct in6_multi *inm;
- struct in6_multi_head inmh;
CTR3(KTR_MLD, "%s: called for ifp %p(%s)", __func__, ifp,
if_name(ifp));
- SLIST_INIT(&inmh);
IN6_MULTI_LIST_LOCK_ASSERT();
MLD_LOCK();
mli = MLD_IFINFO(ifp);
- if (mli->mli_version == MLD_VERSION_2) {
- IF_ADDR_WLOCK(ifp);
- restart:
- CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
- continue;
- inm = (struct in6_multi *)ifma->ifma_protospec;
- if (inm->in6m_state == MLD_LEAVING_MEMBER) {
- in6m_disconnect(inm);
- in6m_rele_locked(&inmh, inm);
- ifma->ifma_protospec = NULL;
- }
+ IF_ADDR_WLOCK(ifp);
+ /*
+ * Extract list of in6_multi associated with the detaching ifp
+ * which the PF_INET6 layer is about to release.
+ */
+ NET_EPOCH_ENTER(et);
+ CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
+ continue;
+ in6m_disconnect_locked(inmh, inm);
+
+ if (mli->mli_version == MLD_VERSION_2) {
in6m_clear_recorded(inm);
- if (__predict_false(ifma6_restart)) {
- ifma6_restart = false;
- goto restart;
+
+ /*
+ * We need to release the final reference held
+ * for issuing the INCLUDE {}.
+ */
+ if (inm->in6m_state == MLD_LEAVING_MEMBER) {
+ inm->in6m_state = MLD_NOT_MEMBER;
+ in6m_rele_locked(inmh, inm);
}
}
- IF_ADDR_WUNLOCK(ifp);
}
-
+ NET_EPOCH_EXIT(et);
+ IF_ADDR_WUNLOCK(ifp);
MLD_UNLOCK();
- in6m_release_list_deferred(&inmh);
}
/*
@@ -630,6 +638,7 @@ static int
mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
/*const*/ struct mld_hdr *mld)
{
+ struct epoch_tracker et;
struct ifmultiaddr *ifma;
struct mld_ifsoftc *mli;
struct in6_multi *inm;
@@ -697,7 +706,7 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
if (timer == 0)
timer = 1;
- IF_ADDR_RLOCK(ifp);
+ NET_EPOCH_ENTER(et);
if (is_general_query) {
/*
* For each reporting group joined on this
@@ -706,10 +715,9 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
CTR2(KTR_MLD, "process v1 general query on ifp %p(%s)",
ifp, if_name(ifp));
CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
continue;
- inm = (struct in6_multi *)ifma->ifma_protospec;
mld_v1_update_group(inm, timer);
}
} else {
@@ -729,7 +737,7 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
in6_clearscope(&mld->mld_addr);
}
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
MLD_UNLOCK();
IN6_MULTI_LIST_UNLOCK();
@@ -799,16 +807,16 @@ mld_v1_update_group(struct in6_multi *inm, const int timer)
* Process a received MLDv2 general, group-specific or
* group-and-source-specific query.
*
- * Assumes that the query header has been pulled up to sizeof(mldv2_query).
+ * Assumes that mld points to a struct mldv2_query which is stored in
+ * contiguous memory.
*
* Return 0 if successful, otherwise an appropriate error code is returned.
*/
static int
mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
- struct mbuf *m, const int off, const int icmp6len)
+ struct mbuf *m, struct mldv2_query *mld, const int off, const int icmp6len)
{
struct mld_ifsoftc *mli;
- struct mldv2_query *mld;
struct in6_multi *inm;
uint32_t maxdelay, nsrc, qqi;
int is_general_query;
@@ -818,7 +826,12 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
char ip6tbuf[INET6_ADDRSTRLEN];
#endif
- is_general_query = 0;
+ if (!mld_v2enable) {
+ CTR3(KTR_MLD, "ignore v2 query src %s on ifp %p(%s)",
+ ip6_sprintf(ip6tbuf, &ip6->ip6_src),
+ ifp, if_name(ifp));
+ return (0);
+ }
/*
* RFC3810 Section 6.2: MLD queries must originate from
@@ -831,9 +844,9 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
return (0);
}
- CTR2(KTR_MLD, "input v2 query on ifp %p(%s)", ifp, if_name(ifp));
+ is_general_query = 0;
- mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off);
+ CTR2(KTR_MLD, "input v2 query on ifp %p(%s)", ifp, if_name(ifp));
maxdelay = ntohs(mld->mld_maxdelay); /* in 1/10ths of a second */
if (maxdelay >= 32768) {
@@ -926,6 +939,8 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
V_interface_timers_running6 = 1;
}
} else {
+ struct epoch_tracker et;
+
/*
* MLDv2 Group-specific or Group-and-source-specific Query.
*
@@ -934,10 +949,10 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
* Queries for groups we are not a member of on this
* link are simply ignored.
*/
- IF_ADDR_RLOCK(ifp);
+ NET_EPOCH_ENTER(et);
inm = in6m_lookup_locked(ifp, &mld->mld_addr);
if (inm == NULL) {
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
goto out_locked;
}
if (nsrc > 0) {
@@ -945,7 +960,7 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
&V_mld_gsrdelay)) {
CTR1(KTR_MLD, "%s: GS query throttled.",
__func__);
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
goto out_locked;
}
}
@@ -959,11 +974,11 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
* group-specific or group-and-source query.
*/
if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer)
- mld_v2_process_group_query(inm, mli, timer, m, off);
+ mld_v2_process_group_query(inm, mli, timer, m, mld, off);
/* XXX Clear embedded scope ID as userland won't expect it. */
in6_clearscope(&mld->mld_addr);
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
}
out_locked:
@@ -980,9 +995,8 @@ out_locked:
*/
static int
mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifsoftc *mli,
- int timer, struct mbuf *m0, const int off)
+ int timer, struct mbuf *m0, struct mldv2_query *mld, const int off)
{
- struct mldv2_query *mld;
int retval;
uint16_t nsrc;
@@ -990,7 +1004,6 @@ mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifsoftc *mli,
MLD_LOCK_ASSERT();
retval = 0;
- mld = (struct mldv2_query *)(mtod(m0, uint8_t *) + off);
switch (inm->in6m_state) {
case MLD_NOT_MEMBER:
@@ -1010,6 +1023,15 @@ mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifsoftc *mli,
nsrc = ntohs(mld->mld_numsrc);
+ /* Length should be checked by calling function. */
+ KASSERT((m0->m_flags & M_PKTHDR) == 0 ||
+ m0->m_pkthdr.len >= off + sizeof(struct mldv2_query) +
+ nsrc * sizeof(struct in6_addr),
+ ("mldv2 packet is too short: (%d bytes < %zd bytes, m=%p)",
+ m0->m_pkthdr.len, off + sizeof(struct mldv2_query) +
+ nsrc * sizeof(struct in6_addr), m0));
+
+
/*
* Deal with group-specific queries upfront.
* If any group query is already pending, purge any recorded
@@ -1051,28 +1073,20 @@ mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifsoftc *mli,
* report for those sources.
*/
if (inm->in6m_nsrc > 0) {
- struct mbuf *m;
- uint8_t *sp;
+ struct in6_addr srcaddr;
int i, nrecorded;
int soff;
- m = m0;
soff = off + sizeof(struct mldv2_query);
nrecorded = 0;
for (i = 0; i < nsrc; i++) {
- sp = mtod(m, uint8_t *) + soff;
- retval = in6m_record_source(inm,
- (const struct in6_addr *)sp);
+ m_copydata(m0, soff, sizeof(struct in6_addr),
+ (caddr_t)&srcaddr);
+ retval = in6m_record_source(inm, &srcaddr);
if (retval < 0)
break;
nrecorded += retval;
soff += sizeof(struct in6_addr);
- if (soff >= m->m_len) {
- soff = soff - m->m_len;
- m = m->m_next;
- if (m == NULL)
- break;
- }
}
if (nrecorded > 0) {
CTR1(KTR_MLD,
@@ -1098,6 +1112,7 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
/*const*/ struct mld_hdr *mld)
{
struct in6_addr src, dst;
+ struct epoch_tracker et;
struct in6_ifaddr *ia;
struct in6_multi *inm;
#ifdef KTR
@@ -1173,7 +1188,7 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
IN6_MULTI_LIST_LOCK();
MLD_LOCK();
- IF_ADDR_RLOCK(ifp);
+ NET_EPOCH_ENTER(et);
/*
* MLDv1 report suppression.
@@ -1221,7 +1236,7 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
}
out_locked:
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
MLD_UNLOCK();
IN6_MULTI_LIST_UNLOCK();
@@ -1281,8 +1296,8 @@ mld_input(struct mbuf *m, int off, int icmp6len)
if (mld_v1_input_query(ifp, ip6, mld) != 0)
return (0);
} else if (icmp6len >= sizeof(struct mldv2_query)) {
- if (mld_v2_input_query(ifp, ip6, m, off,
- icmp6len) != 0)
+ if (mld_v2_input_query(ifp, ip6, m,
+ (struct mldv2_query *)mld, off, icmp6len) != 0)
return (0);
}
break;
@@ -1311,15 +1326,19 @@ mld_input(struct mbuf *m, int off, int icmp6len)
void
mld_fasttimo(void)
{
+ struct in6_multi_head inmh;
VNET_ITERATOR_DECL(vnet_iter);
+ SLIST_INIT(&inmh);
+
VNET_LIST_RLOCK_NOSLEEP();
VNET_FOREACH(vnet_iter) {
CURVNET_SET(vnet_iter);
- mld_fasttimo_vnet();
+ mld_fasttimo_vnet(&inmh);
CURVNET_RESTORE();
}
VNET_LIST_RUNLOCK_NOSLEEP();
+ in6m_release_list_deferred(&inmh);
}
/*
@@ -1328,15 +1347,15 @@ mld_fasttimo(void)
* VIMAGE: Assume caller has set up our curvnet.
*/
static void
-mld_fasttimo_vnet(void)
+mld_fasttimo_vnet(struct in6_multi_head *inmh)
{
+ struct epoch_tracker et;
struct mbufq scq; /* State-change packets */
struct mbufq qrq; /* Query response packets */
struct ifnet *ifp;
struct mld_ifsoftc *mli;
- struct ifmultiaddr *ifma, *next;
- struct in6_multi *inm, *tinm;
- struct in6_multi_head inmh;
+ struct ifmultiaddr *ifma;
+ struct in6_multi *inm;
int uri_fasthz;
uri_fasthz = 0;
@@ -1351,7 +1370,6 @@ mld_fasttimo_vnet(void)
!V_state_change_timers_running6)
return;
- SLIST_INIT(&inmh);
IN6_MULTI_LIST_LOCK();
MLD_LOCK();
@@ -1397,25 +1415,20 @@ mld_fasttimo_vnet(void)
}
IF_ADDR_WLOCK(ifp);
- restart:
- CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
+ NET_EPOCH_ENTER(et);
+ CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
continue;
- inm = (struct in6_multi *)ifma->ifma_protospec;
switch (mli->mli_version) {
case MLD_VERSION_1:
- mld_v1_process_group_timer(&inmh, inm);
+ mld_v1_process_group_timer(inmh, inm);
break;
case MLD_VERSION_2:
- mld_v2_process_group_timers(&inmh, &qrq,
+ mld_v2_process_group_timers(inmh, &qrq,
&scq, inm, uri_fasthz);
break;
}
- if (__predict_false(ifma6_restart)) {
- ifma6_restart = false;
- goto restart;
- }
}
IF_ADDR_WUNLOCK(ifp);
@@ -1429,9 +1442,8 @@ mld_fasttimo_vnet(void)
* IF_ADDR_LOCK internally as well as
* ip6_output() to transmit a packet.
*/
- SLIST_FOREACH_SAFE(inm, &inmh, in6m_nrele, tinm) {
- SLIST_REMOVE_HEAD(&inmh,
- in6m_nrele);
+ while ((inm = SLIST_FIRST(inmh)) != NULL) {
+ SLIST_REMOVE_HEAD(inmh, in6m_defer);
(void)mld_v1_transmit_report(inm,
MLD_LISTENER_REPORT);
}
@@ -1439,14 +1451,9 @@ mld_fasttimo_vnet(void)
case MLD_VERSION_2:
mld_dispatch_queue(&qrq, 0);
mld_dispatch_queue(&scq, 0);
-
- /*
- * Free the in_multi reference(s) for
- * this lifecycle.
- */
- in6m_release_list_deferred(&inmh);
break;
}
+ NET_EPOCH_EXIT(et);
}
out_locked:
@@ -1486,8 +1493,7 @@ mld_v1_process_group_timer(struct in6_multi_head *inmh, struct in6_multi *inm)
case MLD_REPORTING_MEMBER:
if (report_timer_expired) {
inm->in6m_state = MLD_IDLE_MEMBER;
- in6m_disconnect(inm);
- in6m_rele_locked(inmh, inm);
+ SLIST_INSERT_HEAD(inmh, inm, in6m_defer);
}
break;
case MLD_G_QUERY_PENDING_MEMBER:
@@ -1611,7 +1617,7 @@ mld_v2_process_group_timers(struct in6_multi_head *inmh,
if (inm->in6m_state == MLD_LEAVING_MEMBER &&
inm->in6m_scrv == 0) {
inm->in6m_state = MLD_NOT_MEMBER;
- in6m_disconnect(inm);
+ in6m_disconnect_locked(inmh, inm);
in6m_rele_locked(inmh, inm);
}
}
@@ -1656,10 +1662,11 @@ mld_set_version(struct mld_ifsoftc *mli, const int version)
static void
mld_v2_cancel_link_timers(struct mld_ifsoftc *mli)
{
- struct ifmultiaddr *ifma, *next;
+ struct epoch_tracker et;
+ struct in6_multi_head inmh;
+ struct ifmultiaddr *ifma;
struct ifnet *ifp;
struct in6_multi *inm;
- struct in6_multi_head inmh;
CTR3(KTR_MLD, "%s: cancel v2 timers on ifp %p(%s)", __func__,
mli->mli_ifp, if_name(mli->mli_ifp));
@@ -1682,12 +1689,11 @@ mld_v2_cancel_link_timers(struct mld_ifsoftc *mli)
ifp = mli->mli_ifp;
IF_ADDR_WLOCK(ifp);
- restart:
- CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
+ NET_EPOCH_ENTER(et);
+ CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
continue;
- inm = (struct in6_multi *)ifma->ifma_protospec;
switch (inm->in6m_state) {
case MLD_NOT_MEMBER:
case MLD_SILENT_MEMBER:
@@ -1702,9 +1708,9 @@ mld_v2_cancel_link_timers(struct mld_ifsoftc *mli)
* version, we need to release the final
* reference held for issuing the INCLUDE {}.
*/
- in6m_disconnect(inm);
+ if (inm->in6m_refcount == 1)
+ in6m_disconnect_locked(&inmh, inm);
in6m_rele_locked(&inmh, inm);
- ifma->ifma_protospec = NULL;
/* FALLTHROUGH */
case MLD_G_QUERY_PENDING_MEMBER:
case MLD_SG_QUERY_PENDING_MEMBER:
@@ -1720,11 +1726,8 @@ mld_v2_cancel_link_timers(struct mld_ifsoftc *mli)
mbufq_drain(&inm->in6m_scq);
break;
}
- if (__predict_false(ifma6_restart)) {
- ifma6_restart = false;
- goto restart;
- }
}
+ NET_EPOCH_EXIT(et);
IF_ADDR_WUNLOCK(ifp);
in6m_release_list_deferred(&inmh);
}
@@ -1897,6 +1900,14 @@ mld_change_state(struct in6_multi *inm, const int delay)
error = 0;
/*
+ * Check if the in6_multi has already been disconnected.
+ */
+ if (inm->in6m_ifp == NULL) {
+ CTR1(KTR_MLD, "%s: inm is disconnected", __func__);
+ return (0);
+ }
+
+ /*
* Try to detect if the upper layer just asked us to change state
* for an interface which has now gone away.
*/
@@ -2006,6 +2017,7 @@ mld_initial_join(struct in6_multi *inm, struct mld_ifsoftc *mli,
if (mli->mli_version == MLD_VERSION_2 &&
inm->in6m_state == MLD_LEAVING_MEMBER) {
inm->in6m_refcount--;
+ MPASS(inm->in6m_refcount > 0);
}
inm->in6m_state = MLD_REPORTING_MEMBER;
@@ -2985,6 +2997,7 @@ mld_v2_merge_state_changes(struct in6_multi *inm, struct mbufq *scq)
static void
mld_v2_dispatch_general_query(struct mld_ifsoftc *mli)
{
+ struct epoch_tracker et;
struct ifmultiaddr *ifma;
struct ifnet *ifp;
struct in6_multi *inm;
@@ -3007,13 +3020,11 @@ mld_v2_dispatch_general_query(struct mld_ifsoftc *mli)
ifp = mli->mli_ifp;
- IF_ADDR_RLOCK(ifp);
+ NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
continue;
-
- inm = (struct in6_multi *)ifma->ifma_protospec;
KASSERT(ifp == inm->in6m_ifp,
("%s: inconsistent ifp", __func__));
@@ -3038,7 +3049,7 @@ mld_v2_dispatch_general_query(struct mld_ifsoftc *mli)
break;
}
}
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
send:
mld_dispatch_queue(&mli->mli_gq, MLD_MAX_RESPONSE_BURST);