Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netinet/igmp.c
Show First 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | |||||
static void igmp_v3_cancel_link_timers(struct igmp_ifsoftc *); | static void igmp_v3_cancel_link_timers(struct igmp_ifsoftc *); | ||||
static void igmp_v3_dispatch_general_query(struct igmp_ifsoftc *); | static void igmp_v3_dispatch_general_query(struct igmp_ifsoftc *); | ||||
static struct mbuf * | static struct mbuf * | ||||
igmp_v3_encap_report(struct ifnet *, struct mbuf *); | igmp_v3_encap_report(struct ifnet *, struct mbuf *); | ||||
static int igmp_v3_enqueue_group_record(struct mbufq *, | static int igmp_v3_enqueue_group_record(struct mbufq *, | ||||
struct in_multi *, const int, const int, const int); | struct in_multi *, const int, const int, const int); | ||||
static int igmp_v3_enqueue_filter_change(struct mbufq *, | static int igmp_v3_enqueue_filter_change(struct mbufq *, | ||||
struct in_multi *); | struct in_multi *); | ||||
static void igmp_v3_process_group_timers(struct igmp_ifsoftc *, | static void igmp_v3_process_group_timers(struct in_multi_head *, | ||||
struct mbufq *, struct mbufq *, struct in_multi *, | struct mbufq *, struct mbufq *, struct in_multi *, | ||||
const int); | const int); | ||||
static int igmp_v3_merge_state_changes(struct in_multi *, | static int igmp_v3_merge_state_changes(struct in_multi *, | ||||
struct mbufq *); | struct mbufq *); | ||||
static void igmp_v3_suppress_group_record(struct in_multi *); | static void igmp_v3_suppress_group_record(struct in_multi *); | ||||
static int sysctl_igmp_default_version(SYSCTL_HANDLER_ARGS); | static int sysctl_igmp_default_version(SYSCTL_HANDLER_ARGS); | ||||
static int sysctl_igmp_gsr(SYSCTL_HANDLER_ARGS); | static int sysctl_igmp_gsr(SYSCTL_HANDLER_ARGS); | ||||
static int sysctl_igmp_ifinfo(SYSCTL_HANDLER_ARGS); | static int sysctl_igmp_ifinfo(SYSCTL_HANDLER_ARGS); | ||||
Show All 9 Lines | |||||
* System-wide globals. | * System-wide globals. | ||||
* | * | ||||
* Unlocked access to these is OK, except for the global IGMP output | * Unlocked access to these is OK, except for the global IGMP output | ||||
* queue. The IGMP subsystem lock ends up being system-wide for the moment, | * queue. The IGMP subsystem lock ends up being system-wide for the moment, | ||||
* because all VIMAGEs have to share a global output queue, as netisrs | * because all VIMAGEs have to share a global output queue, as netisrs | ||||
* themselves are not virtualized. | * themselves are not virtualized. | ||||
* | * | ||||
* Locking: | * Locking: | ||||
* * The permitted lock order is: IN_MULTI_LOCK, IGMP_LOCK, IF_ADDR_LOCK. | * * The permitted lock order is: IN_MULTI_LIST_LOCK, IGMP_LOCK, IF_ADDR_LOCK. | ||||
* Any may be taken independently; if any are held at the same | * Any may be taken independently; if any are held at the same | ||||
* time, the above lock order must be followed. | * time, the above lock order must be followed. | ||||
* * All output is delegated to the netisr. | * * All output is delegated to the netisr. | ||||
* Now that Giant has been eliminated, the netisr may be inlined. | * Now that Giant has been eliminated, the netisr may be inlined. | ||||
* * IN_MULTI_LOCK covers in_multi. | * * IN_MULTI_LIST_LOCK covers in_multi. | ||||
* * IGMP_LOCK covers igmp_ifsoftc and any global variables in this file, | * * IGMP_LOCK covers igmp_ifsoftc and any global variables in this file, | ||||
* including the output queue. | * including the output queue. | ||||
* * IF_ADDR_LOCK covers if_multiaddrs, which is used for a variety of | * * IF_ADDR_LOCK covers if_multiaddrs, which is used for a variety of | ||||
* per-link state iterators. | * per-link state iterators. | ||||
* * igmp_ifsoftc is valid as long as PF_INET is attached to the interface, | * * igmp_ifsoftc is valid as long as PF_INET is attached to the interface, | ||||
* therefore it is not refcounted. | * therefore it is not refcounted. | ||||
* We allow unlocked reads of igmp_ifsoftc when accessed via in_multi. | * We allow unlocked reads of igmp_ifsoftc when accessed via in_multi. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 257 Lines • ▼ Show 20 Lines | sysctl_igmp_ifinfo(SYSCTL_HANDLER_ARGS) | ||||
if (namelen != 1) | if (namelen != 1) | ||||
return (EINVAL); | return (EINVAL); | ||||
error = sysctl_wire_old_buffer(req, sizeof(struct igmp_ifinfo)); | error = sysctl_wire_old_buffer(req, sizeof(struct igmp_ifinfo)); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
IN_MULTI_LOCK(); | IN_MULTI_LIST_LOCK(); | ||||
IGMP_LOCK(); | IGMP_LOCK(); | ||||
if (name[0] <= 0 || name[0] > V_if_index) { | if (name[0] <= 0 || name[0] > V_if_index) { | ||||
error = ENOENT; | error = ENOENT; | ||||
goto out_locked; | goto out_locked; | ||||
} | } | ||||
error = ENOENT; | error = ENOENT; | ||||
Show All 17 Lines | if (ifp == igi->igi_ifp) { | ||||
info.igi_uri = igi->igi_uri; | info.igi_uri = igi->igi_uri; | ||||
error = SYSCTL_OUT(req, &info, sizeof(info)); | error = SYSCTL_OUT(req, &info, sizeof(info)); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
out_locked: | out_locked: | ||||
IGMP_UNLOCK(); | IGMP_UNLOCK(); | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Dispatch an entire queue of pending packet chains | * Dispatch an entire queue of pending packet chains | ||||
* using the netisr. | * using the netisr. | ||||
* VIMAGE: Assumes the vnet pointer has been set. | * VIMAGE: Assumes the vnet pointer has been set. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | igi_alloc_locked(/*const*/ struct ifnet *ifp) | ||||
igi->igi_ifp = ifp; | igi->igi_ifp = ifp; | ||||
igi->igi_version = V_igmp_default_version; | igi->igi_version = V_igmp_default_version; | ||||
igi->igi_flags = 0; | igi->igi_flags = 0; | ||||
igi->igi_rv = IGMP_RV_INIT; | igi->igi_rv = IGMP_RV_INIT; | ||||
igi->igi_qi = IGMP_QI_INIT; | igi->igi_qi = IGMP_QI_INIT; | ||||
igi->igi_qri = IGMP_QRI_INIT; | igi->igi_qri = IGMP_QRI_INIT; | ||||
igi->igi_uri = IGMP_URI_INIT; | igi->igi_uri = IGMP_URI_INIT; | ||||
SLIST_INIT(&igi->igi_relinmhead); | |||||
mbufq_init(&igi->igi_gq, IGMP_MAX_RESPONSE_PACKETS); | mbufq_init(&igi->igi_gq, IGMP_MAX_RESPONSE_PACKETS); | ||||
LIST_INSERT_HEAD(&V_igi_head, igi, igi_link); | LIST_INSERT_HEAD(&V_igi_head, igi, igi_link); | ||||
CTR2(KTR_IGMPV3, "allocate igmp_ifsoftc for ifp %p(%s)", | CTR2(KTR_IGMPV3, "allocate igmp_ifsoftc for ifp %p(%s)", | ||||
ifp, ifp->if_xname); | ifp, ifp->if_xname); | ||||
out: | out: | ||||
Show All 9 Lines | |||||
* SMPNG: igmp_ifdetach() needs to take IF_ADDR_LOCK(). | * SMPNG: igmp_ifdetach() needs to take IF_ADDR_LOCK(). | ||||
* XXX This is also bitten by unlocked ifma_protospec access. | * XXX This is also bitten by unlocked ifma_protospec access. | ||||
*/ | */ | ||||
void | void | ||||
igmp_ifdetach(struct ifnet *ifp) | igmp_ifdetach(struct ifnet *ifp) | ||||
{ | { | ||||
struct igmp_ifsoftc *igi; | struct igmp_ifsoftc *igi; | ||||
struct ifmultiaddr *ifma; | struct ifmultiaddr *ifma; | ||||
struct in_multi *inm, *tinm; | struct in_multi *inm; | ||||
struct in_multi_head inm_free_tmp; | |||||
CTR3(KTR_IGMPV3, "%s: called for ifp %p(%s)", __func__, ifp, | CTR3(KTR_IGMPV3, "%s: called for ifp %p(%s)", __func__, ifp, | ||||
ifp->if_xname); | ifp->if_xname); | ||||
SLIST_INIT(&inm_free_tmp); | |||||
IGMP_LOCK(); | IGMP_LOCK(); | ||||
igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; | igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; | ||||
if (igi->igi_version == IGMP_VERSION_3) { | if (igi->igi_version == IGMP_VERSION_3) { | ||||
IF_ADDR_RLOCK(ifp); | IF_ADDR_RLOCK(ifp); | ||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | ||||
if (ifma->ifma_addr->sa_family != AF_INET || | if (ifma->ifma_addr->sa_family != AF_INET || | ||||
ifma->ifma_protospec == NULL) | ifma->ifma_protospec == NULL) | ||||
continue; | continue; | ||||
#if 0 | #if 0 | ||||
KASSERT(ifma->ifma_protospec != NULL, | KASSERT(ifma->ifma_protospec != NULL, | ||||
("%s: ifma_protospec is NULL", __func__)); | ("%s: ifma_protospec is NULL", __func__)); | ||||
#endif | #endif | ||||
inm = (struct in_multi *)ifma->ifma_protospec; | inm = (struct in_multi *)ifma->ifma_protospec; | ||||
if (inm->inm_state == IGMP_LEAVING_MEMBER) { | if (inm->inm_state == IGMP_LEAVING_MEMBER) | ||||
SLIST_INSERT_HEAD(&igi->igi_relinmhead, | inm_rele_locked(&inm_free_tmp, inm); | ||||
inm, inm_nrele); | |||||
} | |||||
inm_clear_recorded(inm); | inm_clear_recorded(inm); | ||||
} | } | ||||
IF_ADDR_RUNLOCK(ifp); | IF_ADDR_RUNLOCK(ifp); | ||||
/* | inm_release_list_deferred(&inm_free_tmp); | ||||
* Free the in_multi reference(s) for this IGMP lifecycle. | |||||
*/ | |||||
SLIST_FOREACH_SAFE(inm, &igi->igi_relinmhead, inm_nrele, | |||||
tinm) { | |||||
SLIST_REMOVE_HEAD(&igi->igi_relinmhead, inm_nrele); | |||||
inm_release_locked(inm); | |||||
} | } | ||||
} | |||||
IGMP_UNLOCK(); | IGMP_UNLOCK(); | ||||
} | } | ||||
/* | /* | ||||
* Hook for domifdetach. | * Hook for domifdetach. | ||||
*/ | */ | ||||
void | void | ||||
igmp_domifdetach(struct ifnet *ifp) | igmp_domifdetach(struct ifnet *ifp) | ||||
{ | { | ||||
Show All 19 Lines | igi_delete_locked(const struct ifnet *ifp) | ||||
LIST_FOREACH_SAFE(igi, &V_igi_head, igi_link, tigi) { | LIST_FOREACH_SAFE(igi, &V_igi_head, igi_link, tigi) { | ||||
if (igi->igi_ifp == ifp) { | if (igi->igi_ifp == ifp) { | ||||
/* | /* | ||||
* Free deferred General Query responses. | * Free deferred General Query responses. | ||||
*/ | */ | ||||
mbufq_drain(&igi->igi_gq); | mbufq_drain(&igi->igi_gq); | ||||
LIST_REMOVE(igi, igi_link); | LIST_REMOVE(igi, igi_link); | ||||
KASSERT(SLIST_EMPTY(&igi->igi_relinmhead), | |||||
("%s: there are dangling in_multi references", | |||||
__func__)); | |||||
free(igi, M_IGMP); | free(igi, M_IGMP); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Process a received IGMPv1 query. | * Process a received IGMPv1 query. | ||||
Show All 17 Lines | igmp_input_v1_query(struct ifnet *ifp, const struct ip *ip, | ||||
* XXX SMPng: unlocked increments in igmpstat assumed atomic. | * XXX SMPng: unlocked increments in igmpstat assumed atomic. | ||||
*/ | */ | ||||
if (!in_allhosts(ip->ip_dst) || !in_nullhost(igmp->igmp_group)) { | if (!in_allhosts(ip->ip_dst) || !in_nullhost(igmp->igmp_group)) { | ||||
IGMPSTAT_INC(igps_rcv_badqueries); | IGMPSTAT_INC(igps_rcv_badqueries); | ||||
return (0); | return (0); | ||||
} | } | ||||
IGMPSTAT_INC(igps_rcv_gen_queries); | IGMPSTAT_INC(igps_rcv_gen_queries); | ||||
IN_MULTI_LOCK(); | IN_MULTI_LIST_LOCK(); | ||||
IGMP_LOCK(); | IGMP_LOCK(); | ||||
igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; | igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; | ||||
KASSERT(igi != NULL, ("%s: no igmp_ifsoftc for ifp %p", __func__, ifp)); | KASSERT(igi != NULL, ("%s: no igmp_ifsoftc for ifp %p", __func__, ifp)); | ||||
if (igi->igi_flags & IGIF_LOOPBACK) { | if (igi->igi_flags & IGIF_LOOPBACK) { | ||||
CTR2(KTR_IGMPV3, "ignore v1 query on IGIF_LOOPBACK ifp %p(%s)", | CTR2(KTR_IGMPV3, "ignore v1 query on IGIF_LOOPBACK ifp %p(%s)", | ||||
ifp, ifp->if_xname); | ifp, ifp->if_xname); | ||||
Show All 39 Lines | TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | ||||
case IGMP_LEAVING_MEMBER: | case IGMP_LEAVING_MEMBER: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
IF_ADDR_RUNLOCK(ifp); | IF_ADDR_RUNLOCK(ifp); | ||||
out_locked: | out_locked: | ||||
IGMP_UNLOCK(); | IGMP_UNLOCK(); | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Process a received IGMPv2 general or group-specific query. | * Process a received IGMPv2 general or group-specific query. | ||||
*/ | */ | ||||
static int | static int | ||||
Show All 21 Lines | if (!in_allhosts(ip->ip_dst)) | ||||
return (0); | return (0); | ||||
IGMPSTAT_INC(igps_rcv_gen_queries); | IGMPSTAT_INC(igps_rcv_gen_queries); | ||||
is_general_query = 1; | is_general_query = 1; | ||||
} else { | } else { | ||||
/* IGMPv2 Group-Specific Query. */ | /* IGMPv2 Group-Specific Query. */ | ||||
IGMPSTAT_INC(igps_rcv_group_queries); | IGMPSTAT_INC(igps_rcv_group_queries); | ||||
} | } | ||||
IN_MULTI_LOCK(); | IN_MULTI_LIST_LOCK(); | ||||
IGMP_LOCK(); | IGMP_LOCK(); | ||||
igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; | igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; | ||||
KASSERT(igi != NULL, ("%s: no igmp_ifsoftc for ifp %p", __func__, ifp)); | KASSERT(igi != NULL, ("%s: no igmp_ifsoftc for ifp %p", __func__, ifp)); | ||||
if (igi->igi_flags & IGIF_LOOPBACK) { | if (igi->igi_flags & IGIF_LOOPBACK) { | ||||
CTR2(KTR_IGMPV3, "ignore v2 query on IGIF_LOOPBACK ifp %p(%s)", | CTR2(KTR_IGMPV3, "ignore v2 query on IGIF_LOOPBACK ifp %p(%s)", | ||||
ifp, ifp->if_xname); | ifp, ifp->if_xname); | ||||
Show All 39 Lines | if (inm != NULL) { | ||||
"process v2 query 0x%08x on ifp %p(%s)", | "process v2 query 0x%08x on ifp %p(%s)", | ||||
ntohl(igmp->igmp_group.s_addr), ifp, ifp->if_xname); | ntohl(igmp->igmp_group.s_addr), ifp, ifp->if_xname); | ||||
igmp_v2_update_group(inm, timer); | igmp_v2_update_group(inm, timer); | ||||
} | } | ||||
} | } | ||||
out_locked: | out_locked: | ||||
IGMP_UNLOCK(); | IGMP_UNLOCK(); | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Update the report timer on a group in response to an IGMPv2 query. | * Update the report timer on a group in response to an IGMPv2 query. | ||||
* | * | ||||
* If we are becoming the reporting member for this group, start the timer. | * If we are becoming the reporting member for this group, start the timer. | ||||
Show All 10 Lines | |||||
*/ | */ | ||||
static void | static void | ||||
igmp_v2_update_group(struct in_multi *inm, const int timer) | igmp_v2_update_group(struct in_multi *inm, const int timer) | ||||
{ | { | ||||
CTR4(KTR_IGMPV3, "0x%08x: %s/%s timer=%d", __func__, | CTR4(KTR_IGMPV3, "0x%08x: %s/%s timer=%d", __func__, | ||||
ntohl(inm->inm_addr.s_addr), inm->inm_ifp->if_xname, timer); | ntohl(inm->inm_addr.s_addr), inm->inm_ifp->if_xname, timer); | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
switch (inm->inm_state) { | switch (inm->inm_state) { | ||||
case IGMP_NOT_MEMBER: | case IGMP_NOT_MEMBER: | ||||
case IGMP_SILENT_MEMBER: | case IGMP_SILENT_MEMBER: | ||||
break; | break; | ||||
case IGMP_REPORTING_MEMBER: | case IGMP_REPORTING_MEMBER: | ||||
if (inm->inm_timer != 0 && | if (inm->inm_timer != 0 && | ||||
inm->inm_timer <= timer) { | inm->inm_timer <= timer) { | ||||
▲ Show 20 Lines • Show All 95 Lines • ▼ Show 20 Lines | igmp_input_v3_query(struct ifnet *ifp, const struct ip *ip, | ||||
} else { | } else { | ||||
/* Group or group-source specific query. */ | /* Group or group-source specific query. */ | ||||
if (nsrc == 0) | if (nsrc == 0) | ||||
IGMPSTAT_INC(igps_rcv_group_queries); | IGMPSTAT_INC(igps_rcv_group_queries); | ||||
else | else | ||||
IGMPSTAT_INC(igps_rcv_gsr_queries); | IGMPSTAT_INC(igps_rcv_gsr_queries); | ||||
} | } | ||||
IN_MULTI_LOCK(); | IN_MULTI_LIST_LOCK(); | ||||
IGMP_LOCK(); | IGMP_LOCK(); | ||||
igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; | igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp; | ||||
KASSERT(igi != NULL, ("%s: no igmp_ifsoftc for ifp %p", __func__, ifp)); | KASSERT(igi != NULL, ("%s: no igmp_ifsoftc for ifp %p", __func__, ifp)); | ||||
if (igi->igi_flags & IGIF_LOOPBACK) { | if (igi->igi_flags & IGIF_LOOPBACK) { | ||||
CTR2(KTR_IGMPV3, "ignore v3 query on IGIF_LOOPBACK ifp %p(%s)", | CTR2(KTR_IGMPV3, "ignore v3 query on IGIF_LOOPBACK ifp %p(%s)", | ||||
ifp, ifp->if_xname); | ifp, ifp->if_xname); | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | if (is_general_query) { | ||||
* group-specific or group-and-source query. | * group-specific or group-and-source query. | ||||
*/ | */ | ||||
if (igi->igi_v3_timer == 0 || igi->igi_v3_timer >= timer) | if (igi->igi_v3_timer == 0 || igi->igi_v3_timer >= timer) | ||||
igmp_input_v3_group_query(inm, igi, timer, igmpv3); | igmp_input_v3_group_query(inm, igi, timer, igmpv3); | ||||
} | } | ||||
out_locked: | out_locked: | ||||
IGMP_UNLOCK(); | IGMP_UNLOCK(); | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Process a received IGMPv3 group-specific or group-and-source-specific | * Process a received IGMPv3 group-specific or group-and-source-specific | ||||
* query. | * query. | ||||
* Return <0 if any error occurred. Currently this is ignored. | * Return <0 if any error occurred. Currently this is ignored. | ||||
*/ | */ | ||||
static int | static int | ||||
igmp_input_v3_group_query(struct in_multi *inm, struct igmp_ifsoftc *igi, | igmp_input_v3_group_query(struct in_multi *inm, struct igmp_ifsoftc *igi, | ||||
int timer, /*const*/ struct igmpv3 *igmpv3) | int timer, /*const*/ struct igmpv3 *igmpv3) | ||||
{ | { | ||||
int retval; | int retval; | ||||
uint16_t nsrc; | uint16_t nsrc; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
IGMP_LOCK_ASSERT(); | IGMP_LOCK_ASSERT(); | ||||
retval = 0; | retval = 0; | ||||
switch (inm->inm_state) { | switch (inm->inm_state) { | ||||
case IGMP_NOT_MEMBER: | case IGMP_NOT_MEMBER: | ||||
case IGMP_SILENT_MEMBER: | case IGMP_SILENT_MEMBER: | ||||
case IGMP_SLEEPING_MEMBER: | case IGMP_SLEEPING_MEMBER: | ||||
▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | igmp_input_v1_report(struct ifnet *ifp, /*const*/ struct ip *ip, | ||||
CTR3(KTR_IGMPV3, "process v1 report 0x%08x on ifp %p(%s)", | CTR3(KTR_IGMPV3, "process v1 report 0x%08x on ifp %p(%s)", | ||||
ntohl(igmp->igmp_group.s_addr), ifp, ifp->if_xname); | ntohl(igmp->igmp_group.s_addr), ifp, ifp->if_xname); | ||||
/* | /* | ||||
* IGMPv1 report suppression. | * IGMPv1 report suppression. | ||||
* If we are a member of this group, and our membership should be | * If we are a member of this group, and our membership should be | ||||
* reported, stop our group timer and transition to the 'lazy' state. | * reported, stop our group timer and transition to the 'lazy' state. | ||||
*/ | */ | ||||
IN_MULTI_LOCK(); | IN_MULTI_LIST_LOCK(); | ||||
inm = inm_lookup(ifp, igmp->igmp_group); | inm = inm_lookup(ifp, igmp->igmp_group); | ||||
if (inm != NULL) { | if (inm != NULL) { | ||||
struct igmp_ifsoftc *igi; | struct igmp_ifsoftc *igi; | ||||
igi = inm->inm_igi; | igi = inm->inm_igi; | ||||
if (igi == NULL) { | if (igi == NULL) { | ||||
KASSERT(igi != NULL, | KASSERT(igi != NULL, | ||||
("%s: no igi for ifp %p", __func__, ifp)); | ("%s: no igi for ifp %p", __func__, ifp)); | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | if (inm != NULL) { | ||||
case IGMP_G_QUERY_PENDING_MEMBER: | case IGMP_G_QUERY_PENDING_MEMBER: | ||||
case IGMP_SG_QUERY_PENDING_MEMBER: | case IGMP_SG_QUERY_PENDING_MEMBER: | ||||
case IGMP_LEAVING_MEMBER: | case IGMP_LEAVING_MEMBER: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
out_locked: | out_locked: | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Process a received IGMPv2 host membership report. | * Process a received IGMPv2 host membership report. | ||||
* | * | ||||
* NOTE: 0.0.0.0 workaround breaks const correctness. | * NOTE: 0.0.0.0 workaround breaks const correctness. | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | CTR3(KTR_IGMPV3, "process v2 report 0x%08x on ifp %p(%s)", | ||||
ntohl(igmp->igmp_group.s_addr), ifp, ifp->if_xname); | ntohl(igmp->igmp_group.s_addr), ifp, ifp->if_xname); | ||||
/* | /* | ||||
* IGMPv2 report suppression. | * IGMPv2 report suppression. | ||||
* If we are a member of this group, and our membership should be | * If we are a member of this group, and our membership should be | ||||
* reported, and our group timer is pending or about to be reset, | * reported, and our group timer is pending or about to be reset, | ||||
* stop our group timer by transitioning to the 'lazy' state. | * stop our group timer by transitioning to the 'lazy' state. | ||||
*/ | */ | ||||
IN_MULTI_LOCK(); | IN_MULTI_LIST_LOCK(); | ||||
inm = inm_lookup(ifp, igmp->igmp_group); | inm = inm_lookup(ifp, igmp->igmp_group); | ||||
if (inm != NULL) { | if (inm != NULL) { | ||||
struct igmp_ifsoftc *igi; | struct igmp_ifsoftc *igi; | ||||
igi = inm->inm_igi; | igi = inm->inm_igi; | ||||
KASSERT(igi != NULL, ("%s: no igi for ifp %p", __func__, ifp)); | KASSERT(igi != NULL, ("%s: no igi for ifp %p", __func__, ifp)); | ||||
IGMPSTAT_INC(igps_rcv_ourreports); | IGMPSTAT_INC(igps_rcv_ourreports); | ||||
Show All 28 Lines | if (inm != NULL) { | ||||
case IGMP_G_QUERY_PENDING_MEMBER: | case IGMP_G_QUERY_PENDING_MEMBER: | ||||
case IGMP_SG_QUERY_PENDING_MEMBER: | case IGMP_SG_QUERY_PENDING_MEMBER: | ||||
case IGMP_LEAVING_MEMBER: | case IGMP_LEAVING_MEMBER: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
out_locked: | out_locked: | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
igmp_input(struct mbuf **mp, int *offp, int proto) | igmp_input(struct mbuf **mp, int *offp, int proto) | ||||
{ | { | ||||
int iphlen; | int iphlen; | ||||
▲ Show 20 Lines • Show All 212 Lines • ▼ Show 20 Lines | |||||
igmp_fasttimo_vnet(void) | igmp_fasttimo_vnet(void) | ||||
{ | { | ||||
struct mbufq scq; /* State-change packets */ | struct mbufq scq; /* State-change packets */ | ||||
struct mbufq qrq; /* Query response packets */ | struct mbufq qrq; /* Query response packets */ | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct igmp_ifsoftc *igi; | struct igmp_ifsoftc *igi; | ||||
struct ifmultiaddr *ifma; | struct ifmultiaddr *ifma; | ||||
struct in_multi *inm; | struct in_multi *inm; | ||||
struct in_multi_head inm_free_tmp; | |||||
int loop, uri_fasthz; | int loop, uri_fasthz; | ||||
loop = 0; | loop = 0; | ||||
uri_fasthz = 0; | uri_fasthz = 0; | ||||
/* | /* | ||||
* Quick check to see if any work needs to be done, in order to | * Quick check to see if any work needs to be done, in order to | ||||
* minimize the overhead of fasttimo processing. | * minimize the overhead of fasttimo processing. | ||||
* SMPng: XXX Unlocked reads. | * SMPng: XXX Unlocked reads. | ||||
*/ | */ | ||||
if (!V_current_state_timers_running && | if (!V_current_state_timers_running && | ||||
!V_interface_timers_running && | !V_interface_timers_running && | ||||
!V_state_change_timers_running) | !V_state_change_timers_running) | ||||
return; | return; | ||||
IN_MULTI_LOCK(); | SLIST_INIT(&inm_free_tmp); | ||||
IN_MULTI_LIST_LOCK(); | |||||
IGMP_LOCK(); | IGMP_LOCK(); | ||||
/* | /* | ||||
* IGMPv3 General Query response timer processing. | * IGMPv3 General Query response timer processing. | ||||
*/ | */ | ||||
if (V_interface_timers_running) { | if (V_interface_timers_running) { | ||||
CTR1(KTR_IGMPV3, "%s: interface timers running", __func__); | CTR1(KTR_IGMPV3, "%s: interface timers running", __func__); | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | ||||
inm = (struct in_multi *)ifma->ifma_protospec; | inm = (struct in_multi *)ifma->ifma_protospec; | ||||
switch (igi->igi_version) { | switch (igi->igi_version) { | ||||
case IGMP_VERSION_1: | case IGMP_VERSION_1: | ||||
case IGMP_VERSION_2: | case IGMP_VERSION_2: | ||||
igmp_v1v2_process_group_timer(inm, | igmp_v1v2_process_group_timer(inm, | ||||
igi->igi_version); | igi->igi_version); | ||||
break; | break; | ||||
case IGMP_VERSION_3: | case IGMP_VERSION_3: | ||||
igmp_v3_process_group_timers(igi, &qrq, | igmp_v3_process_group_timers(&inm_free_tmp, &qrq, | ||||
&scq, inm, uri_fasthz); | &scq, inm, uri_fasthz); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
IF_ADDR_RUNLOCK(ifp); | IF_ADDR_RUNLOCK(ifp); | ||||
if (igi->igi_version == IGMP_VERSION_3) { | if (igi->igi_version == IGMP_VERSION_3) { | ||||
struct in_multi *tinm; | |||||
igmp_dispatch_queue(&qrq, 0, loop); | igmp_dispatch_queue(&qrq, 0, loop); | ||||
igmp_dispatch_queue(&scq, 0, loop); | igmp_dispatch_queue(&scq, 0, loop); | ||||
/* | /* | ||||
* Free the in_multi reference(s) for this | * Free the in_multi reference(s) for this | ||||
* IGMP lifecycle. | * IGMP lifecycle. | ||||
*/ | */ | ||||
SLIST_FOREACH_SAFE(inm, &igi->igi_relinmhead, | inm_release_list_deferred(&inm_free_tmp); | ||||
inm_nrele, tinm) { | |||||
SLIST_REMOVE_HEAD(&igi->igi_relinmhead, | |||||
inm_nrele); | |||||
inm_release_locked(inm); | |||||
} | } | ||||
} | } | ||||
} | |||||
out_locked: | out_locked: | ||||
IGMP_UNLOCK(); | IGMP_UNLOCK(); | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
} | } | ||||
/* | /* | ||||
* Update host report group timer for IGMPv1/v2. | * Update host report group timer for IGMPv1/v2. | ||||
* Will update the global pending timer flags. | * Will update the global pending timer flags. | ||||
*/ | */ | ||||
static void | static void | ||||
igmp_v1v2_process_group_timer(struct in_multi *inm, const int version) | igmp_v1v2_process_group_timer(struct in_multi *inm, const int version) | ||||
{ | { | ||||
int report_timer_expired; | int report_timer_expired; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
IGMP_LOCK_ASSERT(); | IGMP_LOCK_ASSERT(); | ||||
if (inm->inm_timer == 0) { | if (inm->inm_timer == 0) { | ||||
report_timer_expired = 0; | report_timer_expired = 0; | ||||
} else if (--inm->inm_timer == 0) { | } else if (--inm->inm_timer == 0) { | ||||
report_timer_expired = 1; | report_timer_expired = 1; | ||||
} else { | } else { | ||||
V_current_state_timers_running = 1; | V_current_state_timers_running = 1; | ||||
Show All 25 Lines | |||||
} | } | ||||
/* | /* | ||||
* Update a group's timers for IGMPv3. | * Update a group's timers for IGMPv3. | ||||
* Will update the global pending timer flags. | * Will update the global pending timer flags. | ||||
* Note: Unlocked read from igi. | * Note: Unlocked read from igi. | ||||
*/ | */ | ||||
static void | static void | ||||
igmp_v3_process_group_timers(struct igmp_ifsoftc *igi, | igmp_v3_process_group_timers(struct in_multi_head *inmh, | ||||
struct mbufq *qrq, struct mbufq *scq, | struct mbufq *qrq, struct mbufq *scq, | ||||
struct in_multi *inm, const int uri_fasthz) | struct in_multi *inm, const int uri_fasthz) | ||||
{ | { | ||||
int query_response_timer_expired; | int query_response_timer_expired; | ||||
int state_change_retransmit_timer_expired; | int state_change_retransmit_timer_expired; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
IGMP_LOCK_ASSERT(); | IGMP_LOCK_ASSERT(); | ||||
query_response_timer_expired = 0; | query_response_timer_expired = 0; | ||||
state_change_retransmit_timer_expired = 0; | state_change_retransmit_timer_expired = 0; | ||||
/* | /* | ||||
* During a transition from v1/v2 compatibility mode back to v3, | * During a transition from v1/v2 compatibility mode back to v3, | ||||
* a group record in REPORTING state may still have its group | * a group record in REPORTING state may still have its group | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | if (state_change_retransmit_timer_expired) { | ||||
* we release IGMP's reference to it. | * we release IGMP's reference to it. | ||||
* This release must be deferred using a SLIST, | * This release must be deferred using a SLIST, | ||||
* as we are called from a loop which traverses | * as we are called from a loop which traverses | ||||
* the in_ifmultiaddr TAILQ. | * the in_ifmultiaddr TAILQ. | ||||
*/ | */ | ||||
if (inm->inm_state == IGMP_LEAVING_MEMBER && | if (inm->inm_state == IGMP_LEAVING_MEMBER && | ||||
inm->inm_scrv == 0) { | inm->inm_scrv == 0) { | ||||
inm->inm_state = IGMP_NOT_MEMBER; | inm->inm_state = IGMP_NOT_MEMBER; | ||||
SLIST_INSERT_HEAD(&igi->igi_relinmhead, | inm_rele_locked(inmh, inm); | ||||
inm, inm_nrele); | |||||
} | } | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Suppress a group's pending response to a group or source/group query. | * Suppress a group's pending response to a group or source/group query. | ||||
* | * | ||||
* Do NOT suppress state changes. This leads to IGMPv3 inconsistency. | * Do NOT suppress state changes. This leads to IGMPv3 inconsistency. | ||||
* Do NOT update ST1/ST0 as this operation merely suppresses | * Do NOT update ST1/ST0 as this operation merely suppresses | ||||
* the currently pending group record. | * the currently pending group record. | ||||
* Do NOT suppress the response to a general query. It is possible but | * Do NOT suppress the response to a general query. It is possible but | ||||
* it would require adding another state or flag. | * it would require adding another state or flag. | ||||
*/ | */ | ||||
static void | static void | ||||
igmp_v3_suppress_group_record(struct in_multi *inm) | igmp_v3_suppress_group_record(struct in_multi *inm) | ||||
{ | { | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
KASSERT(inm->inm_igi->igi_version == IGMP_VERSION_3, | KASSERT(inm->inm_igi->igi_version == IGMP_VERSION_3, | ||||
("%s: not IGMPv3 mode on link", __func__)); | ("%s: not IGMPv3 mode on link", __func__)); | ||||
if (inm->inm_state != IGMP_G_QUERY_PENDING_MEMBER || | if (inm->inm_state != IGMP_G_QUERY_PENDING_MEMBER || | ||||
inm->inm_state != IGMP_SG_QUERY_PENDING_MEMBER) | inm->inm_state != IGMP_SG_QUERY_PENDING_MEMBER) | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | |||||
* will be restarted if Compatibility Mode deems that they must be due to | * will be restarted if Compatibility Mode deems that they must be due to | ||||
* query processing. | * query processing. | ||||
*/ | */ | ||||
static void | static void | ||||
igmp_v3_cancel_link_timers(struct igmp_ifsoftc *igi) | igmp_v3_cancel_link_timers(struct igmp_ifsoftc *igi) | ||||
{ | { | ||||
struct ifmultiaddr *ifma; | struct ifmultiaddr *ifma; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct in_multi *inm, *tinm; | struct in_multi *inm; | ||||
struct in_multi_head inm_free_tmp; | |||||
CTR3(KTR_IGMPV3, "%s: cancel v3 timers on ifp %p(%s)", __func__, | CTR3(KTR_IGMPV3, "%s: cancel v3 timers on ifp %p(%s)", __func__, | ||||
igi->igi_ifp, igi->igi_ifp->if_xname); | igi->igi_ifp, igi->igi_ifp->if_xname); | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
IGMP_LOCK_ASSERT(); | IGMP_LOCK_ASSERT(); | ||||
SLIST_INIT(&inm_free_tmp); | |||||
/* | /* | ||||
* Stop the v3 General Query Response on this link stone dead. | * Stop the v3 General Query Response on this link stone dead. | ||||
* If fasttimo is woken up due to V_interface_timers_running, | * If fasttimo is woken up due to V_interface_timers_running, | ||||
* the flag will be cleared if there are no pending link timers. | * the flag will be cleared if there are no pending link timers. | ||||
*/ | */ | ||||
igi->igi_v3_timer = 0; | igi->igi_v3_timer = 0; | ||||
Show All 24 Lines | case IGMP_LEAVING_MEMBER: | ||||
/* | /* | ||||
* If we are leaving the group and switching to | * If we are leaving the group and switching to | ||||
* compatibility mode, we need to release the final | * compatibility mode, we need to release the final | ||||
* reference held for issuing the INCLUDE {}, and | * reference held for issuing the INCLUDE {}, and | ||||
* transition to REPORTING to ensure the host leave | * transition to REPORTING to ensure the host leave | ||||
* message is sent upstream to the old querier -- | * message is sent upstream to the old querier -- | ||||
* transition to NOT would lose the leave and race. | * transition to NOT would lose the leave and race. | ||||
*/ | */ | ||||
SLIST_INSERT_HEAD(&igi->igi_relinmhead, inm, inm_nrele); | inm_rele_locked(&inm_free_tmp, inm); | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case IGMP_G_QUERY_PENDING_MEMBER: | case IGMP_G_QUERY_PENDING_MEMBER: | ||||
case IGMP_SG_QUERY_PENDING_MEMBER: | case IGMP_SG_QUERY_PENDING_MEMBER: | ||||
inm_clear_recorded(inm); | inm_clear_recorded(inm); | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case IGMP_REPORTING_MEMBER: | case IGMP_REPORTING_MEMBER: | ||||
inm->inm_state = IGMP_REPORTING_MEMBER; | inm->inm_state = IGMP_REPORTING_MEMBER; | ||||
break; | break; | ||||
} | } | ||||
/* | /* | ||||
* Always clear state-change and group report timers. | * Always clear state-change and group report timers. | ||||
* Free any pending IGMPv3 state-change records. | * Free any pending IGMPv3 state-change records. | ||||
*/ | */ | ||||
inm->inm_sctimer = 0; | inm->inm_sctimer = 0; | ||||
inm->inm_timer = 0; | inm->inm_timer = 0; | ||||
mbufq_drain(&inm->inm_scq); | mbufq_drain(&inm->inm_scq); | ||||
} | } | ||||
IF_ADDR_RUNLOCK(ifp); | IF_ADDR_RUNLOCK(ifp); | ||||
SLIST_FOREACH_SAFE(inm, &igi->igi_relinmhead, inm_nrele, tinm) { | |||||
SLIST_REMOVE_HEAD(&igi->igi_relinmhead, inm_nrele); | inm_release_list_deferred(&inm_free_tmp); | ||||
inm_release_locked(inm); | |||||
} | } | ||||
} | |||||
/* | /* | ||||
* Update the Older Version Querier Present timers for a link. | * Update the Older Version Querier Present timers for a link. | ||||
* See Section 7.2.1 of RFC 3376. | * See Section 7.2.1 of RFC 3376. | ||||
*/ | */ | ||||
static void | static void | ||||
igmp_v1v2_process_querier_timers(struct igmp_ifsoftc *igi) | igmp_v1v2_process_querier_timers(struct igmp_ifsoftc *igi) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
igmp_v1v2_queue_report(struct in_multi *inm, const int type) | igmp_v1v2_queue_report(struct in_multi *inm, const int type) | ||||
{ | { | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct igmp *igmp; | struct igmp *igmp; | ||||
struct ip *ip; | struct ip *ip; | ||||
struct mbuf *m; | struct mbuf *m; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
IGMP_LOCK_ASSERT(); | IGMP_LOCK_ASSERT(); | ||||
ifp = inm->inm_ifp; | ifp = inm->inm_ifp; | ||||
m = m_gethdr(M_NOWAIT, MT_DATA); | m = m_gethdr(M_NOWAIT, MT_DATA); | ||||
if (m == NULL) | if (m == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
M_ALIGN(m, sizeof(struct ip) + sizeof(struct igmp)); | M_ALIGN(m, sizeof(struct ip) + sizeof(struct igmp)); | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
int | int | ||||
igmp_change_state(struct in_multi *inm) | igmp_change_state(struct in_multi *inm) | ||||
{ | { | ||||
struct igmp_ifsoftc *igi; | struct igmp_ifsoftc *igi; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int error; | int error; | ||||
IN_MULTI_LOCK_ASSERT(); | |||||
error = 0; | error = 0; | ||||
IN_MULTI_LOCK_ASSERT(); | |||||
/* | /* | ||||
* Try to detect if the upper layer just asked us to change state | * Try to detect if the upper layer just asked us to change state | ||||
* for an interface which has now gone away. | * for an interface which has now gone away. | ||||
*/ | */ | ||||
KASSERT(inm->inm_ifma != NULL, ("%s: no ifma", __func__)); | KASSERT(inm->inm_ifma != NULL, ("%s: no ifma", __func__)); | ||||
ifp = inm->inm_ifma->ifma_ifp; | ifp = inm->inm_ifma->ifma_ifp; | ||||
/* | /* | ||||
* Sanity check that netinet's notion of ifp is the | * Sanity check that netinet's notion of ifp is the | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | "%s: not kicking state machine for silent group", __func__); | ||||
} else { | } else { | ||||
/* | /* | ||||
* Deal with overlapping in_multi lifecycle. | * Deal with overlapping in_multi lifecycle. | ||||
* If this group was LEAVING, then make sure | * If this group was LEAVING, then make sure | ||||
* we drop the reference we picked up to keep the | * we drop the reference we picked up to keep the | ||||
* group around for the final INCLUDE {} enqueue. | * group around for the final INCLUDE {} enqueue. | ||||
*/ | */ | ||||
if (igi->igi_version == IGMP_VERSION_3 && | if (igi->igi_version == IGMP_VERSION_3 && | ||||
inm->inm_state == IGMP_LEAVING_MEMBER) | inm->inm_state == IGMP_LEAVING_MEMBER) { | ||||
inm_release_locked(inm); | MPASS(inm->inm_refcount > 1); | ||||
inm_rele_locked(NULL, inm); | |||||
} | |||||
inm->inm_state = IGMP_REPORTING_MEMBER; | inm->inm_state = IGMP_REPORTING_MEMBER; | ||||
switch (igi->igi_version) { | switch (igi->igi_version) { | ||||
case IGMP_VERSION_1: | case IGMP_VERSION_1: | ||||
case IGMP_VERSION_2: | case IGMP_VERSION_2: | ||||
inm->inm_state = IGMP_IDLE_MEMBER; | inm->inm_state = IGMP_IDLE_MEMBER; | ||||
error = igmp_v1v2_queue_report(inm, | error = igmp_v1v2_queue_report(inm, | ||||
(igi->igi_version == IGMP_VERSION_2) ? | (igi->igi_version == IGMP_VERSION_2) ? | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | igmp_handle_state_change(struct in_multi *inm, struct igmp_ifsoftc *igi) | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int retval; | int retval; | ||||
CTR4(KTR_IGMPV3, "%s: state change for 0x%08x on ifp %p(%s)", __func__, | CTR4(KTR_IGMPV3, "%s: state change for 0x%08x on ifp %p(%s)", __func__, | ||||
ntohl(inm->inm_addr.s_addr), inm->inm_ifp, inm->inm_ifp->if_xname); | ntohl(inm->inm_addr.s_addr), inm->inm_ifp, inm->inm_ifp->if_xname); | ||||
ifp = inm->inm_ifp; | ifp = inm->inm_ifp; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
IGMP_LOCK_ASSERT(); | IGMP_LOCK_ASSERT(); | ||||
KASSERT(igi && igi->igi_ifp == ifp, ("%s: inconsistent ifp", __func__)); | KASSERT(igi && igi->igi_ifp == ifp, ("%s: inconsistent ifp", __func__)); | ||||
if ((ifp->if_flags & IFF_LOOPBACK) || | if ((ifp->if_flags & IFF_LOOPBACK) || | ||||
(igi->igi_flags & IGIF_SILENT) || | (igi->igi_flags & IGIF_SILENT) || | ||||
!igmp_isgroupreported(inm->inm_addr) || | !igmp_isgroupreported(inm->inm_addr) || | ||||
(igi->igi_version != IGMP_VERSION_3)) { | (igi->igi_version != IGMP_VERSION_3)) { | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | igmp_final_leave(struct in_multi *inm, struct igmp_ifsoftc *igi) | ||||
int syncstates; | int syncstates; | ||||
syncstates = 1; | syncstates = 1; | ||||
CTR4(KTR_IGMPV3, "%s: final leave 0x%08x on ifp %p(%s)", | CTR4(KTR_IGMPV3, "%s: final leave 0x%08x on ifp %p(%s)", | ||||
__func__, ntohl(inm->inm_addr.s_addr), inm->inm_ifp, | __func__, ntohl(inm->inm_addr.s_addr), inm->inm_ifp, | ||||
inm->inm_ifp->if_xname); | inm->inm_ifp->if_xname); | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
IGMP_LOCK_ASSERT(); | IGMP_LOCK_ASSERT(); | ||||
switch (inm->inm_state) { | switch (inm->inm_state) { | ||||
case IGMP_NOT_MEMBER: | case IGMP_NOT_MEMBER: | ||||
case IGMP_SILENT_MEMBER: | case IGMP_SILENT_MEMBER: | ||||
case IGMP_LEAVING_MEMBER: | case IGMP_LEAVING_MEMBER: | ||||
/* Already leaving or left; do nothing. */ | /* Already leaving or left; do nothing. */ | ||||
CTR1(KTR_IGMPV3, | CTR1(KTR_IGMPV3, | ||||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | igmp_v3_enqueue_group_record(struct mbufq *mq, struct in_multi *inm, | ||||
int error, is_filter_list_change; | int error, is_filter_list_change; | ||||
int minrec0len, m0srcs, msrcs, nbytes, off; | int minrec0len, m0srcs, msrcs, nbytes, off; | ||||
int record_has_sources; | int record_has_sources; | ||||
int now; | int now; | ||||
int type; | int type; | ||||
in_addr_t naddr; | in_addr_t naddr; | ||||
uint8_t mode; | uint8_t mode; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
error = 0; | error = 0; | ||||
ifp = inm->inm_ifp; | ifp = inm->inm_ifp; | ||||
is_filter_list_change = 0; | is_filter_list_change = 0; | ||||
m = NULL; | m = NULL; | ||||
m0 = NULL; | m0 = NULL; | ||||
m0srcs = 0; | m0srcs = 0; | ||||
msrcs = 0; | msrcs = 0; | ||||
▲ Show 20 Lines • Show All 343 Lines • ▼ Show 20 Lines | igmp_v3_enqueue_filter_change(struct mbufq *mq, struct in_multi *inm) | ||||
struct ip_msource *ims, *nims; | struct ip_msource *ims, *nims; | ||||
struct mbuf *m, *m0, *md; | struct mbuf *m, *m0, *md; | ||||
in_addr_t naddr; | in_addr_t naddr; | ||||
int m0srcs, nbytes, npbytes, off, rsrcs, schanged; | int m0srcs, nbytes, npbytes, off, rsrcs, schanged; | ||||
int nallow, nblock; | int nallow, nblock; | ||||
uint8_t mode, now, then; | uint8_t mode, now, then; | ||||
rectype_t crt, drt, nrt; | rectype_t crt, drt, nrt; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
if (inm->inm_nsrc == 0 || | if (inm->inm_nsrc == 0 || | ||||
(inm->inm_st[0].iss_asm > 0 && inm->inm_st[1].iss_asm > 0)) | (inm->inm_st[0].iss_asm > 0 && inm->inm_st[1].iss_asm > 0)) | ||||
return (0); | return (0); | ||||
ifp = inm->inm_ifp; /* interface */ | ifp = inm->inm_ifp; /* interface */ | ||||
mode = inm->inm_st[1].iss_fmode; /* filter mode at t1 */ | mode = inm->inm_st[1].iss_fmode; /* filter mode at t1 */ | ||||
crt = REC_NONE; /* current group record type */ | crt = REC_NONE; /* current group record type */ | ||||
▲ Show 20 Lines • Show All 186 Lines • ▼ Show 20 Lines | igmp_v3_merge_state_changes(struct in_multi *inm, struct mbufq *scq) | ||||
struct mbuf *mt; /* last state-change in packet */ | struct mbuf *mt; /* last state-change in packet */ | ||||
int docopy, domerge; | int docopy, domerge; | ||||
u_int recslen; | u_int recslen; | ||||
docopy = 0; | docopy = 0; | ||||
domerge = 0; | domerge = 0; | ||||
recslen = 0; | recslen = 0; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
IGMP_LOCK_ASSERT(); | IGMP_LOCK_ASSERT(); | ||||
/* | /* | ||||
* If there are further pending retransmissions, make a writable | * If there are further pending retransmissions, make a writable | ||||
* copy of each queued state-change message before merging. | * copy of each queued state-change message before merging. | ||||
*/ | */ | ||||
if (inm->inm_scrv > 0) | if (inm->inm_scrv > 0) | ||||
docopy = 1; | docopy = 1; | ||||
▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
igmp_v3_dispatch_general_query(struct igmp_ifsoftc *igi) | igmp_v3_dispatch_general_query(struct igmp_ifsoftc *igi) | ||||
{ | { | ||||
struct ifmultiaddr *ifma; | struct ifmultiaddr *ifma; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct in_multi *inm; | struct in_multi *inm; | ||||
int retval, loop; | int retval, loop; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
IGMP_LOCK_ASSERT(); | IGMP_LOCK_ASSERT(); | ||||
KASSERT(igi->igi_version == IGMP_VERSION_3, | KASSERT(igi->igi_version == IGMP_VERSION_3, | ||||
("%s: called when version %d", __func__, igi->igi_version)); | ("%s: called when version %d", __func__, igi->igi_version)); | ||||
/* | /* | ||||
* Check that there are some packets queued. If so, send them first. | * Check that there are some packets queued. If so, send them first. | ||||
* For large number of groups the reply to general query can take | * For large number of groups the reply to general query can take | ||||
▲ Show 20 Lines • Show All 295 Lines • ▼ Show 20 Lines | LIST_FOREACH_SAFE(igi, igi_head, igi_link, tigi) { | ||||
db_printf(" v1_timer %u\n", igi->igi_v1_timer); | db_printf(" v1_timer %u\n", igi->igi_v1_timer); | ||||
db_printf(" v2_timer %u\n", igi->igi_v2_timer); | db_printf(" v2_timer %u\n", igi->igi_v2_timer); | ||||
db_printf(" v3_timer %u\n", igi->igi_v3_timer); | db_printf(" v3_timer %u\n", igi->igi_v3_timer); | ||||
db_printf(" flags %#x\n", igi->igi_flags); | db_printf(" flags %#x\n", igi->igi_flags); | ||||
db_printf(" rv %u\n", igi->igi_rv); | db_printf(" rv %u\n", igi->igi_rv); | ||||
db_printf(" qi %u\n", igi->igi_qi); | db_printf(" qi %u\n", igi->igi_qi); | ||||
db_printf(" qri %u\n", igi->igi_qri); | db_printf(" qri %u\n", igi->igi_qri); | ||||
db_printf(" uri %u\n", igi->igi_uri); | db_printf(" uri %u\n", igi->igi_uri); | ||||
/* SLIST_HEAD(,in_multi) igi_relinmhead */ | |||||
/* struct mbufq igi_gq; */ | /* struct mbufq igi_gq; */ | ||||
db_printf("\n"); | db_printf("\n"); | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
static int | static int | ||||
igmp_modevent(module_t mod, int type, void *unused __unused) | igmp_modevent(module_t mod, int type, void *unused __unused) | ||||
Show All 28 Lines |