Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/igmp.c
Show First 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||||||||||
#include "opt_ddb.h" | #include "opt_ddb.h" | ||||||||||||
#include <sys/param.h> | #include <sys/param.h> | ||||||||||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||||||||||
#include <sys/module.h> | #include <sys/module.h> | ||||||||||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||||||||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||||||||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||||||||||
#include <sys/protosw.h> | |||||||||||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||||||||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||||||||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||||||||||
#include <sys/ktr.h> | #include <sys/ktr.h> | ||||||||||||
#include <sys/condvar.h> | #include <sys/condvar.h> | ||||||||||||
#ifdef DDB | #ifdef DDB | ||||||||||||
#include <ddb/ddb.h> | #include <ddb/ddb.h> | ||||||||||||
Show All 16 Lines | |||||||||||||
#include <machine/in_cksum.h> | #include <machine/in_cksum.h> | ||||||||||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||||||||||
#ifndef KTR_IGMPV3 | #ifndef KTR_IGMPV3 | ||||||||||||
#define KTR_IGMPV3 KTR_INET | #define KTR_IGMPV3 KTR_INET | ||||||||||||
#endif | #endif | ||||||||||||
#define IGMP_SLOWHZ 2 /* 2 slow timeouts per second */ | |||||||||||||
#define IGMP_FASTHZ 5 /* 5 fast timeouts per second */ | |||||||||||||
melifaroUnsubmitted Done Inline Actions
melifaro: | |||||||||||||
#define IGMP_RESPONSE_BURST_INTERVAL (IGMP_FASTHZ / 2) | |||||||||||||
static struct igmp_ifsoftc * | static struct igmp_ifsoftc * | ||||||||||||
igi_alloc_locked(struct ifnet *); | igi_alloc_locked(struct ifnet *); | ||||||||||||
static void igi_delete_locked(const struct ifnet *); | static void igi_delete_locked(const struct ifnet *); | ||||||||||||
static void igmp_dispatch_queue(struct mbufq *, int, const int); | static void igmp_dispatch_queue(struct mbufq *, int, const int); | ||||||||||||
static void igmp_fasttimo_vnet(void); | static void igmp_fasttimo_vnet(void); | ||||||||||||
static void igmp_final_leave(struct in_multi *, struct igmp_ifsoftc *); | static void igmp_final_leave(struct in_multi *, struct igmp_ifsoftc *); | ||||||||||||
static int igmp_handle_state_change(struct in_multi *, | static int igmp_handle_state_change(struct in_multi *, | ||||||||||||
struct igmp_ifsoftc *); | struct igmp_ifsoftc *); | ||||||||||||
▲ Show 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | |||||||||||||
struct mtx igmp_mtx; | struct mtx igmp_mtx; | ||||||||||||
struct mbuf *m_raopt; /* Router Alert option */ | struct mbuf *m_raopt; /* Router Alert option */ | ||||||||||||
static MALLOC_DEFINE(M_IGMP, "igmp", "igmp state"); | static MALLOC_DEFINE(M_IGMP, "igmp", "igmp state"); | ||||||||||||
/* | /* | ||||||||||||
* VIMAGE-wide globals. | * VIMAGE-wide globals. | ||||||||||||
* | * | ||||||||||||
* The IGMPv3 timers themselves need to run per-image, however, | * The IGMPv3 timers themselves need to run per-image, however, for | ||||||||||||
* protosw timers run globally (see tcp). | * historical reasons, timers run globally. This needs to be improved. | ||||||||||||
Done Inline ActionsJust update the comment to be true? emaste: Just update the comment to be true? | |||||||||||||
* An ifnet can only be in one vimage at a time, and the loopback | * An ifnet can only be in one vimage at a time, and the loopback | ||||||||||||
* ifnet, loif, is itself virtualized. | * ifnet, loif, is itself virtualized. | ||||||||||||
* It would otherwise be possible to seriously hose IGMP state, | * It would otherwise be possible to seriously hose IGMP state, | ||||||||||||
* and create inconsistencies in upstream multicast routing, if you have | * and create inconsistencies in upstream multicast routing, if you have | ||||||||||||
* multiple VIMAGEs running on the same link joining different multicast | * multiple VIMAGEs running on the same link joining different multicast | ||||||||||||
* groups, UNLESS the "primary IP address" is different. This is because | * groups, UNLESS the "primary IP address" is different. This is because | ||||||||||||
* IGMP for IPv4 does not force link-local addresses to be used for each | * IGMP for IPv4 does not force link-local addresses to be used for each | ||||||||||||
* node, unlike MLD for IPv6. | * node, unlike MLD for IPv6. | ||||||||||||
▲ Show 20 Lines • Show All 597 Lines • ▼ Show 20 Lines | CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | ||||||||||||
case IGMP_SG_QUERY_PENDING_MEMBER: | case IGMP_SG_QUERY_PENDING_MEMBER: | ||||||||||||
case IGMP_REPORTING_MEMBER: | case IGMP_REPORTING_MEMBER: | ||||||||||||
case IGMP_IDLE_MEMBER: | case IGMP_IDLE_MEMBER: | ||||||||||||
case IGMP_LAZY_MEMBER: | case IGMP_LAZY_MEMBER: | ||||||||||||
case IGMP_SLEEPING_MEMBER: | case IGMP_SLEEPING_MEMBER: | ||||||||||||
case IGMP_AWAKENING_MEMBER: | case IGMP_AWAKENING_MEMBER: | ||||||||||||
inm->inm_state = IGMP_REPORTING_MEMBER; | inm->inm_state = IGMP_REPORTING_MEMBER; | ||||||||||||
inm->inm_timer = IGMP_RANDOM_DELAY( | inm->inm_timer = IGMP_RANDOM_DELAY( | ||||||||||||
IGMP_V1V2_MAX_RI * PR_FASTHZ); | IGMP_V1V2_MAX_RI * IGMP_FASTHZ); | ||||||||||||
V_current_state_timers_running = 1; | V_current_state_timers_running = 1; | ||||||||||||
break; | break; | ||||||||||||
case IGMP_LEAVING_MEMBER: | case IGMP_LEAVING_MEMBER: | ||||||||||||
break; | break; | ||||||||||||
} | } | ||||||||||||
} | } | ||||||||||||
out_locked: | out_locked: | ||||||||||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | igmp_input_v2_query(struct ifnet *ifp, const struct ip *ip, | ||||||||||||
/* | /* | ||||||||||||
* Ignore v2 query if in v1 Compatibility Mode. | * Ignore v2 query if in v1 Compatibility Mode. | ||||||||||||
*/ | */ | ||||||||||||
if (igi->igi_version == IGMP_VERSION_1) | if (igi->igi_version == IGMP_VERSION_1) | ||||||||||||
goto out_locked; | goto out_locked; | ||||||||||||
igmp_set_version(igi, IGMP_VERSION_2); | igmp_set_version(igi, IGMP_VERSION_2); | ||||||||||||
timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; | timer = igmp->igmp_code * IGMP_FASTHZ / IGMP_TIMER_SCALE; | ||||||||||||
if (timer == 0) | if (timer == 0) | ||||||||||||
timer = 1; | timer = 1; | ||||||||||||
if (is_general_query) { | if (is_general_query) { | ||||||||||||
/* | /* | ||||||||||||
* For each reporting group joined on this | * For each reporting group joined on this | ||||||||||||
* interface, kick the report timer. | * interface, kick the report timer. | ||||||||||||
*/ | */ | ||||||||||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | igmp_input_v3_query(struct ifnet *ifp, const struct ip *ip, | ||||||||||||
} | } | ||||||||||||
qqi = igmpv3->igmp_qqi; | qqi = igmpv3->igmp_qqi; | ||||||||||||
if (qqi >= 128) { | if (qqi >= 128) { | ||||||||||||
qqi = IGMP_MANT(igmpv3->igmp_qqi) << | qqi = IGMP_MANT(igmpv3->igmp_qqi) << | ||||||||||||
(IGMP_EXP(igmpv3->igmp_qqi) + 3); | (IGMP_EXP(igmpv3->igmp_qqi) + 3); | ||||||||||||
} | } | ||||||||||||
timer = maxresp * PR_FASTHZ / IGMP_TIMER_SCALE; | timer = maxresp * IGMP_FASTHZ / IGMP_TIMER_SCALE; | ||||||||||||
if (timer == 0) | if (timer == 0) | ||||||||||||
timer = 1; | timer = 1; | ||||||||||||
nsrc = ntohs(igmpv3->igmp_numsrc); | nsrc = ntohs(igmpv3->igmp_numsrc); | ||||||||||||
/* | /* | ||||||||||||
* Validate address fields and versions upfront before | * Validate address fields and versions upfront before | ||||||||||||
* accepting v3 query. | * accepting v3 query. | ||||||||||||
▲ Show 20 Lines • Show All 612 Lines • ▼ Show 20 Lines | igmp_input(struct mbuf **mp, int *offp, int proto) | ||||||||||||
*mp = m; | *mp = m; | ||||||||||||
return (rip_input(mp, offp, proto)); | return (rip_input(mp, offp, proto)); | ||||||||||||
} | } | ||||||||||||
/* | /* | ||||||||||||
* Fast timeout handler (global). | * Fast timeout handler (global). | ||||||||||||
* VIMAGE: Timeout handlers are expected to service all vimages. | * VIMAGE: Timeout handlers are expected to service all vimages. | ||||||||||||
*/ | */ | ||||||||||||
void | static struct callout igmpfast_callout; | ||||||||||||
igmp_fasttimo(void) | static void | ||||||||||||
igmp_fasttimo(void *arg __unused) | |||||||||||||
{ | { | ||||||||||||
struct epoch_tracker et; | |||||||||||||
VNET_ITERATOR_DECL(vnet_iter); | VNET_ITERATOR_DECL(vnet_iter); | ||||||||||||
NET_EPOCH_ENTER(et); | |||||||||||||
VNET_LIST_RLOCK_NOSLEEP(); | VNET_LIST_RLOCK_NOSLEEP(); | ||||||||||||
VNET_FOREACH(vnet_iter) { | VNET_FOREACH(vnet_iter) { | ||||||||||||
CURVNET_SET(vnet_iter); | CURVNET_SET(vnet_iter); | ||||||||||||
igmp_fasttimo_vnet(); | igmp_fasttimo_vnet(); | ||||||||||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||||||||||
} | } | ||||||||||||
VNET_LIST_RUNLOCK_NOSLEEP(); | VNET_LIST_RUNLOCK_NOSLEEP(); | ||||||||||||
NET_EPOCH_EXIT(et); | |||||||||||||
callout_reset(&igmpfast_callout, hz / IGMP_FASTHZ, igmp_fasttimo, NULL); | |||||||||||||
Done Inline Actions
melifaro: | |||||||||||||
} | } | ||||||||||||
/* | /* | ||||||||||||
* Fast timeout handler (per-vnet). | * Fast timeout handler (per-vnet). | ||||||||||||
* Sends are shuffled off to a netisr to deal with Giant. | * Sends are shuffled off to a netisr to deal with Giant. | ||||||||||||
* | * | ||||||||||||
* VIMAGE: Assume caller has set up our curvnet. | * VIMAGE: Assume caller has set up our curvnet. | ||||||||||||
*/ | */ | ||||||||||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | igmp_fasttimo_vnet(void) | ||||||||||||
* Note: Processing a v3 group timer may remove a node. | * Note: Processing a v3 group timer may remove a node. | ||||||||||||
*/ | */ | ||||||||||||
LIST_FOREACH(igi, &V_igi_head, igi_link) { | LIST_FOREACH(igi, &V_igi_head, igi_link) { | ||||||||||||
ifp = igi->igi_ifp; | ifp = igi->igi_ifp; | ||||||||||||
if (igi->igi_version == IGMP_VERSION_3) { | if (igi->igi_version == IGMP_VERSION_3) { | ||||||||||||
loop = (igi->igi_flags & IGIF_LOOPBACK) ? 1 : 0; | loop = (igi->igi_flags & IGIF_LOOPBACK) ? 1 : 0; | ||||||||||||
uri_fasthz = IGMP_RANDOM_DELAY(igi->igi_uri * | uri_fasthz = IGMP_RANDOM_DELAY(igi->igi_uri * | ||||||||||||
PR_FASTHZ); | IGMP_FASTHZ); | ||||||||||||
mbufq_init(&qrq, IGMP_MAX_G_GS_PACKETS); | mbufq_init(&qrq, IGMP_MAX_G_GS_PACKETS); | ||||||||||||
mbufq_init(&scq, IGMP_MAX_STATE_CHANGE_PACKETS); | mbufq_init(&scq, IGMP_MAX_STATE_CHANGE_PACKETS); | ||||||||||||
} | } | ||||||||||||
IF_ADDR_WLOCK(ifp); | IF_ADDR_WLOCK(ifp); | ||||||||||||
restart: | restart: | ||||||||||||
CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) { | CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) { | ||||||||||||
if (ifma->ifma_addr->sa_family != AF_INET || | if (ifma->ifma_addr->sa_family != AF_INET || | ||||||||||||
▲ Show 20 Lines • Show All 242 Lines • ▼ Show 20 Lines | CTR4(KTR_IGMPV3, "%s: switching to v%d on ifp %p(%s)", __func__, | ||||||||||||
version, igi->igi_ifp, igi->igi_ifp->if_xname); | version, igi->igi_ifp, igi->igi_ifp->if_xname); | ||||||||||||
if (version == IGMP_VERSION_1 || version == IGMP_VERSION_2) { | if (version == IGMP_VERSION_1 || version == IGMP_VERSION_2) { | ||||||||||||
/* | /* | ||||||||||||
* Compute the "Older Version Querier Present" timer as per | * Compute the "Older Version Querier Present" timer as per | ||||||||||||
* Section 8.12. | * Section 8.12. | ||||||||||||
*/ | */ | ||||||||||||
old_version_timer = igi->igi_rv * igi->igi_qi + igi->igi_qri; | old_version_timer = igi->igi_rv * igi->igi_qi + igi->igi_qri; | ||||||||||||
old_version_timer *= PR_SLOWHZ; | old_version_timer *= IGMP_SLOWHZ; | ||||||||||||
if (version == IGMP_VERSION_1) { | if (version == IGMP_VERSION_1) { | ||||||||||||
igi->igi_v1_timer = old_version_timer; | igi->igi_v1_timer = old_version_timer; | ||||||||||||
igi->igi_v2_timer = 0; | igi->igi_v2_timer = 0; | ||||||||||||
} else if (version == IGMP_VERSION_2) { | } else if (version == IGMP_VERSION_2) { | ||||||||||||
igi->igi_v1_timer = 0; | igi->igi_v1_timer = 0; | ||||||||||||
igi->igi_v2_timer = old_version_timer; | igi->igi_v2_timer = old_version_timer; | ||||||||||||
} | } | ||||||||||||
▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | if (igi->igi_v1_timer == 0 && igi->igi_v2_timer == 0) { | ||||||||||||
} | } | ||||||||||||
} | } | ||||||||||||
} | } | ||||||||||||
/* | /* | ||||||||||||
* Global slowtimo handler. | * Global slowtimo handler. | ||||||||||||
* VIMAGE: Timeout handlers are expected to service all vimages. | * VIMAGE: Timeout handlers are expected to service all vimages. | ||||||||||||
*/ | */ | ||||||||||||
void | static struct callout igmpslow_callout; | ||||||||||||
igmp_slowtimo(void) | static void | ||||||||||||
igmp_slowtimo(void *arg __unused) | |||||||||||||
{ | { | ||||||||||||
struct epoch_tracker et; | |||||||||||||
VNET_ITERATOR_DECL(vnet_iter); | VNET_ITERATOR_DECL(vnet_iter); | ||||||||||||
NET_EPOCH_ENTER(et); | |||||||||||||
VNET_LIST_RLOCK_NOSLEEP(); | VNET_LIST_RLOCK_NOSLEEP(); | ||||||||||||
VNET_FOREACH(vnet_iter) { | VNET_FOREACH(vnet_iter) { | ||||||||||||
CURVNET_SET(vnet_iter); | CURVNET_SET(vnet_iter); | ||||||||||||
igmp_slowtimo_vnet(); | igmp_slowtimo_vnet(); | ||||||||||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||||||||||
} | } | ||||||||||||
VNET_LIST_RUNLOCK_NOSLEEP(); | VNET_LIST_RUNLOCK_NOSLEEP(); | ||||||||||||
NET_EPOCH_EXIT(et); | |||||||||||||
callout_reset(&igmpslow_callout, hz / IGMP_SLOWHZ, igmp_slowtimo, NULL); | |||||||||||||
Done Inline Actions
melifaro: | |||||||||||||
} | } | ||||||||||||
/* | /* | ||||||||||||
* Per-vnet slowtimo handler. | * Per-vnet slowtimo handler. | ||||||||||||
*/ | */ | ||||||||||||
static void | static void | ||||||||||||
igmp_slowtimo_vnet(void) | igmp_slowtimo_vnet(void) | ||||||||||||
{ | { | ||||||||||||
▲ Show 20 Lines • Show All 212 Lines • ▼ Show 20 Lines | "%s: not kicking state machine for silent group", __func__); | ||||||||||||
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) ? | ||||||||||||
IGMP_v2_HOST_MEMBERSHIP_REPORT : | IGMP_v2_HOST_MEMBERSHIP_REPORT : | ||||||||||||
IGMP_v1_HOST_MEMBERSHIP_REPORT); | IGMP_v1_HOST_MEMBERSHIP_REPORT); | ||||||||||||
if (error == 0) { | if (error == 0) { | ||||||||||||
inm->inm_timer = IGMP_RANDOM_DELAY( | inm->inm_timer = IGMP_RANDOM_DELAY( | ||||||||||||
IGMP_V1V2_MAX_RI * PR_FASTHZ); | IGMP_V1V2_MAX_RI * IGMP_FASTHZ); | ||||||||||||
V_current_state_timers_running = 1; | V_current_state_timers_running = 1; | ||||||||||||
} | } | ||||||||||||
break; | break; | ||||||||||||
case IGMP_VERSION_3: | case IGMP_VERSION_3: | ||||||||||||
/* | /* | ||||||||||||
* Defer update of T0 to T1, until the first copy | * Defer update of T0 to T1, until the first copy | ||||||||||||
* of the state change has been transmitted. | * of the state change has been transmitted. | ||||||||||||
▲ Show 20 Lines • Show All 1,238 Lines • ▼ Show 20 Lines | |||||||||||||
{ | { | ||||||||||||
switch (type) { | switch (type) { | ||||||||||||
case MOD_LOAD: | case MOD_LOAD: | ||||||||||||
CTR1(KTR_IGMPV3, "%s: initializing", __func__); | CTR1(KTR_IGMPV3, "%s: initializing", __func__); | ||||||||||||
IGMP_LOCK_INIT(); | IGMP_LOCK_INIT(); | ||||||||||||
m_raopt = igmp_ra_alloc(); | m_raopt = igmp_ra_alloc(); | ||||||||||||
netisr_register(&igmp_nh); | netisr_register(&igmp_nh); | ||||||||||||
callout_init(&igmpslow_callout, 1); | |||||||||||||
callout_reset(&igmpslow_callout, hz / IGMP_SLOWHZ, | |||||||||||||
Done Inline Actions
melifaro: | |||||||||||||
igmp_slowtimo, NULL); | |||||||||||||
callout_init(&igmpfast_callout, 1); | |||||||||||||
callout_reset(&igmpfast_callout, hz / IGMP_FASTHZ, | |||||||||||||
Done Inline Actions
melifaro: | |||||||||||||
igmp_fasttimo, NULL); | |||||||||||||
break; | break; | ||||||||||||
case MOD_UNLOAD: | case MOD_UNLOAD: | ||||||||||||
CTR1(KTR_IGMPV3, "%s: tearing down", __func__); | CTR1(KTR_IGMPV3, "%s: tearing down", __func__); | ||||||||||||
netisr_unregister(&igmp_nh); | netisr_unregister(&igmp_nh); | ||||||||||||
m_free(m_raopt); | m_free(m_raopt); | ||||||||||||
m_raopt = NULL; | m_raopt = NULL; | ||||||||||||
IGMP_LOCK_DESTROY(); | IGMP_LOCK_DESTROY(); | ||||||||||||
break; | break; | ||||||||||||
Show All 12 Lines |