--- //depot/vendor/freebsd/src/sys/netinet/igmp.c 2006/12/04 00:46:36 +++ //depot/user/bms/netdev/sys/netinet/igmp.c 2007/07/24 21:36:45 @@ -36,6 +36,7 @@ /* * Internet Group Management Protocol (IGMP) routines. + * [RFC1112, RFC2236] * * Written by Steve Deering, Stanford, May 1988. * Modified by Rosen Sharma, Stanford, Aug 1994. @@ -72,16 +73,13 @@ #include -static MALLOC_DEFINE(M_IGMP, "igmp", "igmp state"); - -static struct router_info *find_rti(struct ifnet *ifp); +static void igmp_make_raopt(void); static void igmp_sendpkt(struct in_multi *, int, unsigned long); +static void rti_delete(struct ifnet *); +static int rti_fill(struct in_multi *); +static struct router_info * + rti_find(struct ifnet *); -static struct igmpstat igmpstat; - -SYSCTL_STRUCT(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_RW, &igmpstat, - igmpstat, ""); - /* * igmp_mtx protects all mutable global variables in igmp.c, as well as the * data fields in struct router_info. In general, a router_info structure @@ -90,93 +88,179 @@ * when accessed via an in_multi read-only. */ static struct mtx igmp_mtx; -static SLIST_HEAD(, router_info) router_info_head; +#define IGMP_LOCK() mtx_lock(&igmp_mtx) +#define IGMP_UNLOCK() mtx_unlock(&igmp_mtx) +#define IGMP_LOCK_ASSERT() \ + do { \ + mtx_assert(&igmp_mtx, MA_OWNED); \ + NET_ASSERT_GIANT(); \ + } while (0) +#define IGMP_LOCK_INIT() \ + mtx_init(&igmp_mtx, "igmp_mtx", NULL, MTX_DEF) +#define IGMP_LOCK_DESTROY() mtx_destroy(&igmp_mtx) + +static MALLOC_DEFINE(M_IGMP, "igmp", "igmp state"); +static LIST_HEAD(, router_info) rti_head = LIST_HEAD_INITIALIZER(&rti_head); + static int igmp_timers_are_running; +static struct in_addr igmp_all_hosts_group; +static struct in_addr igmp_all_rtrs_group; +static struct in_addr igmp_all_rpts_group; + +static struct mbuf *m_raopt; +static struct route igmprt; + +static int igmp_sendra = 1; +SYSCTL_INT(_net_inet_igmp, OID_AUTO, sendra, CTLFLAG_RW, + &igmp_sendra, 0, "Transmit IP Router Alert in IGMP messages"); +TUNABLE_INT("net.inet.igmp.sendra", &igmp_sendra); + +#ifdef notyet /* - * XXXRW: can we define these such that these can be made const? In any - * case, these shouldn't be changed after igmp_init() and therefore don't - * need locking. + * Sending IGMP reports for 224.0.0.0/8 groups is optional, however, it + * may break IGMP snooping switches. */ -static u_long igmp_all_hosts_group; -static u_long igmp_all_rtrs_group; +static int igmp_sendlocal = 1; +SYSCTL_INT(_net_inet_igmp, OID_AUTO, sendlocal, CTLFLAG_RW, + &igmp_sendlocal, 0, "Send IGMP membership reports for 224.0.0.0/8 groups"); +TUNABLE_INT("net.inet.igmp.sendlocal", &igmp_sendlocal); +#endif -static struct mbuf *router_alert; -static struct route igmprt; +static int igmp_checkra = 0; +SYSCTL_INT(_net_inet_igmp, OID_AUTO, checkra, CTLFLAG_RW, + &igmp_checkra, 0, "Require IP Router Alert in received IGMP messages"); +TUNABLE_INT("net.inet.igmp.checkra", &igmp_checkra); -#ifdef IGMP_DEBUG -#define IGMP_PRINTF(x) printf(x) -#else -#define IGMP_PRINTF(x) +#ifdef notyet +static int igmp_version = 2; +SYSCTL_INT(_net_inet_igmp, OID_AUTO, version, CTLFLAG_RW, + &igmp_version, 0, "Highest version of IGMP enabled"); +TUNABLE_INT("net.inet.igmp.version", &igmp_version); #endif +static struct igmpstat igmpstat; +SYSCTL_STRUCT(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_RW, &igmpstat, + igmpstat, ""); + + +static void +igmp_make_raopt(void) +{ + struct ipoption *p; + + /* + * Construct a Router Alert option to use in outgoing packets. + */ + MGET(m_raopt, M_DONTWAIT, MT_DATA); + p = mtod(m_raopt, struct ipoption *); + p->ipopt_dst.s_addr = 0; + p->ipopt_list[0] = IPOPT_RA; /* Router Alert Option */ + p->ipopt_list[1] = 0x04; /* 4 bytes long */ + p->ipopt_list[2] = 0x00; + p->ipopt_list[3] = 0x00; + m_raopt->m_len = sizeof(p->ipopt_dst) + p->ipopt_list[1]; +} + void igmp_init(void) { - struct ipoption *ra; + + igmp_timers_are_running = 0; /* * To avoid byte-swapping the same value over and over again. */ - igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP); - igmp_all_rtrs_group = htonl(INADDR_ALLRTRS_GROUP); + igmp_all_hosts_group.s_addr = htonl(INADDR_ALLHOSTS_GROUP); + igmp_all_rtrs_group.s_addr = htonl(INADDR_ALLRTRS_GROUP); + igmp_all_rpts_group.s_addr = htonl(INADDR_ALLRPTS_GROUP); + + igmp_make_raopt(); + + IGMP_LOCK_INIT(); + + LIST_INIT(&rti_head); +} + +static int +rti_fill(struct in_multi *inm) +{ + struct router_info *rti; - igmp_timers_are_running = 0; + IGMP_LOCK_ASSERT(); - /* - * Construct a Router Alert option to use in outgoing packets. - */ - MGET(router_alert, M_DONTWAIT, MT_DATA); - ra = mtod(router_alert, struct ipoption *); - ra->ipopt_dst.s_addr = 0; - ra->ipopt_list[0] = IPOPT_RA; /* Router Alert Option */ - ra->ipopt_list[1] = 0x04; /* 4 bytes long */ - ra->ipopt_list[2] = 0x00; - ra->ipopt_list[3] = 0x00; - router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1]; + LIST_FOREACH(rti, &rti_head, rti_link) { + if (rti->rti_ifp == inm->inm_ifp) { + inm->inm_rti = rti; + if (rti->rti_type == IGMP_V1_ROUTER) + return (IGMP_V1_MEMBERSHIP_REPORT); + else + return (IGMP_V2_MEMBERSHIP_REPORT); + } + } - mtx_init(&igmp_mtx, "igmp_mtx", NULL, MTX_DEF); - SLIST_INIT(&router_info_head); + MALLOC(rti, struct router_info *, sizeof *rti, M_IGMP, M_NOWAIT); + if (rti == NULL) { + return (0); + } + rti->rti_ifp = inm->inm_ifp; + rti->rti_type = IGMP_V2_ROUTER; + rti->rti_age = 0; + LIST_INSERT_HEAD(&rti_head, rti, rti_link); + inm->inm_rti = rti; + return (IGMP_V2_MEMBERSHIP_REPORT); } static struct router_info * -find_rti(struct ifnet *ifp) +rti_find(struct ifnet *ifp) { struct router_info *rti; - mtx_assert(&igmp_mtx, MA_OWNED); - IGMP_PRINTF("[igmp.c, _find_rti] --> entering \n"); - SLIST_FOREACH(rti, &router_info_head, rti_list) { - if (rti->rti_ifp == ifp) { - IGMP_PRINTF( - "[igmp.c, _find_rti] --> found old entry \n"); + IGMP_LOCK_ASSERT(); + + LIST_FOREACH(rti, &rti_head, rti_link) { + if (rti->rti_ifp == ifp) return (rti); - } } + MALLOC(rti, struct router_info *, sizeof *rti, M_IGMP, M_NOWAIT); if (rti == NULL) { - IGMP_PRINTF("[igmp.c, _find_rti] --> no memory for entry\n"); return (NULL); } rti->rti_ifp = ifp; rti->rti_type = IGMP_V2_ROUTER; - rti->rti_time = 0; - SLIST_INSERT_HEAD(&router_info_head, rti, rti_list); - IGMP_PRINTF("[igmp.c, _find_rti] --> created an entry \n"); + rti->rti_age = 0; + LIST_INSERT_HEAD(&rti_head, rti, rti_link); return (rti); } +static void +rti_delete(struct ifnet *ifp) +{ + struct router_info *rti; + + IGMP_LOCK_ASSERT(); + + LIST_FOREACH(rti, &rti_head, rti_link) { + if (rti->rti_ifp == ifp) { + LIST_REMOVE(rti, rti_link); + FREE(rti, M_IGMP); + return; + } + } +} + void -igmp_input(register struct mbuf *m, int off) +igmp_input(struct mbuf *m, int off) { - register int iphlen = off; - register struct igmp *igmp; - register struct ip *ip; - register int igmplen; - register struct ifnet *ifp = m->m_pkthdr.rcvif; - register int minlen; - register struct in_multi *inm; - register struct in_ifaddr *ia; + int iphlen = off; + struct igmp *igmp; + struct ip *ip; + int igmplen; + struct ifnet *ifp = m->m_pkthdr.rcvif; + int minlen; + struct in_multi *inm; + struct in_ifaddr *ia; struct in_multistep step; struct router_info *rti; int timer; /** timer value in the igmp query header **/ @@ -186,6 +270,24 @@ ip = mtod(m, struct ip *); igmplen = ip->ip_len; +#ifdef notyet + if (ip_mrouter && ip->ip_ttl != 1) { + ++igmpstat.igps_rcv_badttl; + m_freem(m); + return; + } + + /* + * Check for the IP Router Alert option, if configured to do so. + * TODO: Implementations SHOULD do this for IGMPv3. + */ + if (igmp_checkra && !ip_checkrouteralert(m)) { + ++igmpstat.igps_rcv_nora; + m_freem(m); + return; + } +#endif + /* * Validate lengths. */ @@ -201,6 +303,8 @@ return; } + ip = mtod(m, struct ip *); + /* * Validate checksum. */ @@ -215,22 +319,6 @@ m->m_data -= iphlen; m->m_len += iphlen; - ip = mtod(m, struct ip *); - timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; - if (timer == 0) - timer = 1; - - /* - * In the IGMPv2 specification, there are 3 states and a flag. - * - * In Non-Member state, we simply don't have a membership record. - * In Delaying Member state, our timer is running (inm->inm_timer). - * In Idle Member state, our timer is not running (inm->inm_timer==0). - * - * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if we - * have heard a report from another member, or IGMP_IREPORTEDLAST if - * I sent the last report. - */ switch (igmp->igmp_type) { case IGMP_MEMBERSHIP_QUERY: ++igmpstat.igps_rcv_queries; @@ -238,85 +326,164 @@ if (ifp->if_flags & IFF_LOOPBACK) break; + if (!in_nullhost(igmp->igmp_group) && + !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { + ++igmpstat.igps_rcv_badqueries; + m_freem(m); + return; + } + if (igmp->igmp_code == 0) { - /* - * Old router. Remember that the querier on this - * interface is old, and set the timer to the value - * in RFC 1112. - */ - - mtx_lock(&igmp_mtx); - rti = find_rti(ifp); + IGMP_LOCK(); + rti = rti_find(ifp); if (rti == NULL) { - mtx_unlock(&igmp_mtx); + IGMP_UNLOCK(); m_freem(m); return; } rti->rti_type = IGMP_V1_ROUTER; - rti->rti_time = 0; - mtx_unlock(&igmp_mtx); + rti->rti_age = 0; + IGMP_UNLOCK(); - timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ; - - if (ip->ip_dst.s_addr != igmp_all_hosts_group || - igmp->igmp_group.s_addr != 0) { + if (!in_hosteq(ip->ip_dst, igmp_all_hosts_group)) { ++igmpstat.igps_rcv_badqueries; m_freem(m); return; } - } else { + /* - * New router. Simply do the new validity check. + * Start the timers in all of our membership records + * for the interface on which the query arrived, + * except those that are already running and those + * that belong to the "all-hosts" group (224.0.0.1). */ - - if (igmp->igmp_group.s_addr != 0 && - !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { + IN_MULTI_LOCK(); + IN_FIRST_MULTI(step, inm); + while (inm != NULL) { + if (inm->inm_ifp == ifp && + inm->inm_timer == 0 && + !in_hosteq(inm->inm_addr, + igmp_all_hosts_group)) { + inm->inm_state = IGMP_DELAYING_MEMBER; + inm->inm_timer = IGMP_RANDOM_DELAY( + IGMP_MAX_HOST_REPORT_DELAY * + PR_FASTHZ); + igmp_timers_are_running = 1; + } + IN_NEXT_MULTI(step, inm); + } + } else { + if (!IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { ++igmpstat.igps_rcv_badqueries; m_freem(m); return; } + + timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; + if (timer == 0) + timer = 1; + + /* + * Start the timers in all of our membership records + * for the interface on which the query arrived, + * except those that are already running and those + * that belong to the "all-hosts" group (224.0.0.1). + */ + IN_MULTI_LOCK(); + IN_FIRST_MULTI(step, inm); + while (inm != NULL) { + if (inm->inm_ifp == ifp && + !in_hosteq(inm->inm_addr, + igmp_all_hosts_group) && + in_hosteq(ip->ip_dst, inm->inm_addr)) { + switch (inm->inm_state) { + case IGMP_DELAYING_MEMBER: + if (inm->inm_timer <= timer) + break; + /* FALLTHROUGH */ + case IGMP_IDLE_MEMBER: + case IGMP_LAZY_MEMBER: + case IGMP_AWAKENING_MEMBER: + inm->inm_state = + IGMP_DELAYING_MEMBER; + inm->inm_timer = + IGMP_RANDOM_DELAY(timer); + igmp_timers_are_running = 1; + break; + case IGMP_SLEEPING_MEMBER: + inm->inm_state = + IGMP_AWAKENING_MEMBER; + break; + } + } + IN_NEXT_MULTI(step, inm); + } + IN_MULTI_UNLOCK(); + } + break; + + case IGMP_V1_MEMBERSHIP_REPORT: + ++igmpstat.igps_rcv_reports; + + if (ifp->if_flags & IFF_LOOPBACK) + break; + + if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr) || + !in_hosteq(igmp->igmp_group, ip->ip_dst))) { + ++igmpstat.igps_rcv_badreports; + m_freem(m); + return; + } + + /* + * KLUDGE: If the source address of this report is unset, + * then assign the subnet address of the primary IP address + * on this interface to the report, in order to accomodate + * legacy userland code which does not understand the + * IP_RECVIF socket option. + */ + if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) { + IFP_TO_IA(ifp, ia); + if (ia != NULL) + ip->ip_src.s_addr = htonl(ia->ia_subnet); } /* - * - Start the timers in all of our membership records that - * the query applies to for the interface on which the - * query arrived excl. those that belong to the "all-hosts" - * group (224.0.0.1). - * - Restart any timer that is already running but has a - * value longer than the requested timeout. - * - Use the value specified in the query message as the - * maximum timeout. + * If we belong to the group being reported, stop + * our timer for that group. */ IN_MULTI_LOCK(); - IN_FIRST_MULTI(step, inm); - while (inm != NULL) { - if (inm->inm_ifp == ifp && - inm->inm_addr.s_addr != igmp_all_hosts_group && - (igmp->igmp_group.s_addr == 0 || - igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) { - if (inm->inm_timer == 0 || - inm->inm_timer > timer) { - inm->inm_timer = - IGMP_RANDOM_DELAY(timer); - igmp_timers_are_running = 1; - } + IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); + if (inm != NULL) { + inm->inm_timer = 0; + ++igmpstat.igps_rcv_ourreports; + + switch (inm->inm_state) { + case IGMP_IDLE_MEMBER: + case IGMP_LAZY_MEMBER: + case IGMP_AWAKENING_MEMBER: + case IGMP_SLEEPING_MEMBER: + inm->inm_state = IGMP_SLEEPING_MEMBER; + break; + case IGMP_DELAYING_MEMBER: + if (inm->inm_rti->rti_type == IGMP_V1_ROUTER) + inm->inm_state = IGMP_LAZY_MEMBER; + else + inm->inm_state = IGMP_SLEEPING_MEMBER; + break; } - IN_NEXT_MULTI(step, inm); } IN_MULTI_UNLOCK(); break; - case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: /* - * For fast leave to work, we have to know that we are the - * last person to send a report for this group. Reports can - * potentially get looped back if we are a multicast router, - * so discard reports sourced by me. + * Make sure we don't hear our own membership report. Fast + * leave requires knowing that we are the only member of a + * group. */ IFP_TO_IA(ifp, ia); - if (ia != NULL && - ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr) + if (ia != NULL && in_hosteq(ip->ip_src, IA_SIN(ia)->sin_addr)) break; ++igmpstat.igps_rcv_reports; @@ -324,20 +491,19 @@ if (ifp->if_flags & IFF_LOOPBACK) break; - if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { + if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) || + !in_hosteq(igmp->igmp_group, ip->ip_dst)) { ++igmpstat.igps_rcv_badreports; m_freem(m); return; } /* - * KLUDGE: if the IP source address of the report has an - * unspecified (i.e., zero) subnet number, as is allowed for - * a booting host, replace it with the correct subnet number - * so that a process-level multicast routing daemon can - * determine which subnet it arrived from. This is necessary - * to compensate for the lack of any way for a process to - * determine the arrival interface of an incoming packet. + * KLUDGE: If the source address of this report is unset, + * then assign the subnet address of the primary IP address + * on this interface to the report, in order to accomodate + * legacy userland code which does not understand the + * IP_RECVIF socket option. */ if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) { if (ia != NULL) @@ -345,15 +511,25 @@ } /* - * If we belong to the group being reported, stop our timer - * for that group. + * If we belong to the group being reported, stop + * our timer for that group. */ IN_MULTI_LOCK(); IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); if (inm != NULL) { inm->inm_timer = 0; ++igmpstat.igps_rcv_ourreports; - inm->inm_state = IGMP_OTHERMEMBER; + + switch (inm->inm_state) { + case IGMP_DELAYING_MEMBER: + case IGMP_IDLE_MEMBER: + case IGMP_AWAKENING_MEMBER: + inm->inm_state = IGMP_LAZY_MEMBER; + break; + case IGMP_LAZY_MEMBER: + case IGMP_SLEEPING_MEMBER: + break; + } } IN_MULTI_UNLOCK(); break; @@ -366,29 +542,34 @@ rip_input(m, off); } +/* TODO: update API to return error value. */ void igmp_joingroup(struct in_multi *inm) { + int report_type; IN_MULTI_LOCK_ASSERT(); - if (inm->inm_addr.s_addr == igmp_all_hosts_group - || inm->inm_ifp->if_flags & IFF_LOOPBACK) { + inm->inm_state = IGMP_IDLE_MEMBER; + + if (!in_hosteq(inm->inm_addr, igmp_all_hosts_group) || + (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) { + IGMP_LOCK(); + report_type = rti_fill(inm); + if (report_type == 0) { + IGMP_UNLOCK(); + return; /* ENOMEM */ + } + igmp_sendpkt(inm, report_type, 0); + inm->inm_state = IGMP_DELAYING_MEMBER; + inm->inm_timer = IGMP_RANDOM_DELAY( + IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); + igmp_timers_are_running = 1; + IGMP_UNLOCK(); + } else inm->inm_timer = 0; - inm->inm_state = IGMP_OTHERMEMBER; - } else { - mtx_lock(&igmp_mtx); - inm->inm_rti = find_rti(inm->inm_ifp); - mtx_unlock(&igmp_mtx); - if (inm->inm_rti != NULL) { - igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); - inm->inm_timer = IGMP_RANDOM_DELAY( - IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ); - inm->inm_state = IGMP_IREPORTEDLAST; - igmp_timers_are_running = 1; - } - /* XXX handling of failure case? */ - } + + return; /* 0 */ } void @@ -397,24 +578,32 @@ IN_MULTI_LOCK_ASSERT(); - if (inm->inm_state == IGMP_IREPORTEDLAST && - inm->inm_addr.s_addr != igmp_all_hosts_group && - !(inm->inm_ifp->if_flags & IFF_LOOPBACK) && - inm->inm_rti->rti_type != IGMP_V1_ROUTER) - igmp_sendpkt(inm, IGMP_V2_LEAVE_GROUP, igmp_all_rtrs_group); + switch (inm->inm_state) { + case IGMP_DELAYING_MEMBER: + case IGMP_IDLE_MEMBER: + if (!in_hosteq(inm->inm_addr, igmp_all_hosts_group) && + (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) + if (inm->inm_rti->rti_type == IGMP_V2_ROUTER) + igmp_sendpkt(inm, IGMP_V2_LEAVE_GROUP, + igmp_all_rtrs_group.s_addr); + break; + case IGMP_LAZY_MEMBER: + case IGMP_AWAKENING_MEMBER: + case IGMP_SLEEPING_MEMBER: + break; + } } void igmp_fasttimo(void) { - register struct in_multi *inm; + struct in_multi *inm; struct in_multistep step; /* * Quick check to see if any work needs to be done, in order to * minimize the overhead of fasttimo processing. */ - if (!igmp_timers_are_running) return; @@ -425,8 +614,9 @@ if (inm->inm_timer == 0) { /* do nothing */ } else if (--inm->inm_timer == 0) { - igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); - inm->inm_state = IGMP_IREPORTEDLAST; + if (inm->inm_state == IGMP_DELAYING_MEMBER) { + igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); + } } else { igmp_timers_are_running = 1; } @@ -440,17 +630,14 @@ { struct router_info *rti; - IGMP_PRINTF("[igmp.c,_slowtimo] -- > entering \n"); - mtx_lock(&igmp_mtx); - SLIST_FOREACH(rti, &router_info_head, rti_list) { - if (rti->rti_type == IGMP_V1_ROUTER) { - rti->rti_time++; - if (rti->rti_time >= IGMP_AGE_THRESHOLD) + IGMP_LOCK(); + LIST_FOREACH(rti, &rti_head, rti_link) { + if (rti->rti_type == IGMP_V1_ROUTER && + ++rti->rti_age >= IGMP_AGE_THRESHOLD) { rti->rti_type = IGMP_V2_ROUTER; } } - mtx_unlock(&igmp_mtx); - IGMP_PRINTF("[igmp.c,_slowtimo] -- > exiting \n"); + IGMP_UNLOCK(); } static void @@ -495,16 +682,24 @@ imo.imo_multicast_ifp = inm->inm_ifp; imo.imo_multicast_ttl = 1; imo.imo_multicast_vif = -1; + /* * Request loopback of the report if we are acting as a multicast * router, so that the process-level routing daemon can hear it. */ imo.imo_multicast_loop = (ip_mrouter != NULL); - /* - * XXX: Do we have to worry about reentrancy here? Don't think so. - */ - ip_output(m, router_alert, &igmprt, 0, &imo, NULL); + ip_output(m, igmp_sendra ? m_raopt : NULL, &igmprt, 0, + &imo, NULL); ++igmpstat.igps_snd_reports; } + +void +igmp_purgeif(struct ifnet *ifp) +{ + + IGMP_LOCK(); + rti_delete(ifp); + IGMP_UNLOCK(); +} --- //depot/vendor/freebsd/src/sys/netinet/igmp.h 2007/06/15 19:03:48 +++ //depot/user/bms/netdev/sys/netinet/igmp.h 2007/07/24 19:05:31 @@ -66,6 +66,12 @@ u_short igmp_numsrc; /* number of sources */ /*struct in_addr igmp_sources[1];*/ /* source addresses */ }; +#define IGMP_V3_QUERY_MINLEN 12 +#define IGMP_EXP(x) (((x) >> 4) & 0x07) +#define IGMP_MANT(x) ((x) & 0x0f) +#define IGMP_QRESV(x) (((x) >> 4) & 0x0f) +#define IGMP_SFLAG(x) (((x) >> 3) & 0x01) +#define IGMP_QRV(x) ((x) & 0x07) struct igmp_grouprec { u_char ig_type; /* record type */ @@ -74,6 +80,7 @@ struct in_addr ig_group; /* group address being reported */ /*struct in_addr ig_sources[1];*/ /* source addresses */ }; +#define IGMP_GRPREC_HDRLEN 8 struct igmp_report { u_char ir_type; /* record type */ @@ -86,46 +93,43 @@ #define IGMP_MINLEN 8 #define IGMP_HDRLEN 8 -#define IGMP_GRPREC_HDRLEN 8 #define IGMP_PREPEND 0 -#if 0 -#define IGMP_QRV(pigmp) ((pigmp)->igmp_misc & (0x07)) /* XXX */ -#define IGMP_MAXSOURCES(len) (((len) - 12) >> 2) /* XXX */ -#endif - /* * Message types, including version number. */ #define IGMP_MEMBERSHIP_QUERY 0x11 /* membership query */ #define IGMP_V1_MEMBERSHIP_REPORT 0x12 /* Ver. 1 membership report */ +#define IGMP_DVMRP 0x13 /* DVMRP routing message */ +#define IGMP_PIM 0x14 /* PIM routing message */ #define IGMP_V2_MEMBERSHIP_REPORT 0x16 /* Ver. 2 membership report */ -#define IGMP_V2_LEAVE_GROUP 0x17 /* Leave-group message */ +#define IGMP_V2_LEAVE_GROUP 0x17 /* Leave-group message */ +#define IGMP_MTRACE_RESP 0x1e /* mtrace(8) reply */ +#define IGMP_MTRACE 0x1f /* mtrace(8) probe */ +#define IGMP_V3_MEMBERSHIP_REPORT 0x22 /* Ver. 3 membership report */ -#define IGMP_DVMRP 0x13 /* DVMRP routing message */ -#define IGMP_PIM 0x14 /* PIM routing message */ +/* + * IGMPv3 report modes. + */ +#define IGMP_MODE_IS_INCLUDE 1 /* MODE_IN */ +#define IGMP_MODE_IS_EXCLUDE 2 /* MODE_EX */ +#define IGMP_CHANGE_TO_INCLUDE_MODE 3 /* TO_IN */ +#define IGMP_CHANGE_TO_EXCLUDE_MODE 4 /* TO_EX */ +#define IGMP_ALLOW_NEW_SOURCES 5 /* ALLOW_NEW */ +#define IGMP_BLOCK_OLD_SOURCES 6 /* BLOCK_OLD */ -#define IGMP_MTRACE_RESP 0x1e /* traceroute resp.(to sender)*/ -#define IGMP_MTRACE 0x1f /* mcast traceroute messages */ +/* + * IGMPv3 query types. + */ +#define IGMP_V3_GENERAL_QUERY 1 +#define IGMP_V3_GROUP_QUERY 2 +#define IGMP_V3_GROUP_SOURCE_QUERY 3 -#define IGMP_V3_MEMBERSHIP_REPORT 0x22 /* Ver. 3 membership report */ - #define IGMP_MAX_HOST_REPORT_DELAY 10 /* max delay for response to */ /* query (in seconds) according */ /* to RFC1112 */ - #define IGMP_TIMER_SCALE 10 /* denotes that the igmp code field */ /* specifies time in 10th of seconds*/ -/* - * The following four defininitions are for backwards compatibility. - * They should be removed as soon as all applications are updated to - * use the new constant names. - */ -#define IGMP_HOST_MEMBERSHIP_QUERY IGMP_MEMBERSHIP_QUERY -#define IGMP_HOST_MEMBERSHIP_REPORT IGMP_V1_MEMBERSHIP_REPORT -#define IGMP_HOST_NEW_MEMBERSHIP_REPORT IGMP_V2_MEMBERSHIP_REPORT -#define IGMP_HOST_LEAVE_MESSAGE IGMP_V2_LEAVE_GROUP - #endif /* _NETINET_IGMP_H_ */ --- //depot/vendor/freebsd/src/sys/netinet/igmp_var.h 2007/06/12 16:28:06 +++ //depot/user/bms/netdev/sys/netinet/igmp_var.h 2007/07/24 21:36:45 @@ -47,33 +47,35 @@ */ struct igmpstat { - u_int igps_rcv_total; /* total IGMP messages received */ - u_int igps_rcv_tooshort; /* received with too few bytes */ - u_int igps_rcv_badsum; /* received with bad checksum */ - u_int igps_rcv_queries; /* received membership queries */ - u_int igps_rcv_badqueries; /* received invalid queries */ - u_int igps_rcv_reports; /* received membership reports */ - u_int igps_rcv_badreports; /* received invalid reports */ - u_int igps_rcv_ourreports; /* received reports for our groups */ - u_int igps_snd_reports; /* sent membership reports */ - u_int igps_rcv_toolong; /* received with too many bytes */ + uint64_t igps_rcv_total; /* total IGMP messages received */ + uint64_t igps_rcv_tooshort; /* received with too few bytes */ + uint64_t igps_rcv_badsum; /* received with bad checksum */ + uint64_t igps_rcv_queries; /* received membership queries */ + uint64_t igps_rcv_badqueries; /* received invalid queries */ + uint64_t igps_rcv_reports; /* received membership reports */ + uint64_t igps_rcv_badreports; /* received invalid reports */ + uint64_t igps_rcv_ourreports; /* received reports for our groups */ + uint64_t igps_snd_reports; /* sent membership reports */ + uint64_t igps_rcv_toolong; /* received with too many bytes */ + uint64_t igps_rcv_nora; /* received w/o Router Alert option */ }; #ifdef _KERNEL #define IGMP_RANDOM_DELAY(X) (random() % (X) + 1) /* - * States for IGMPv2's leave processing + * States for the IGMP state table. */ -#define IGMP_OTHERMEMBER 0 -#define IGMP_IREPORTEDLAST 1 - -/* - * State masks for IGMPv3 - */ -#define IGMP_V3_NONEXISTENT 0x01 -#define IGMP_V3_OTHERMEMBER 0x02 -#define IGMP_V3_IREPORTEDLAST 0x04 +#define IGMP_DELAYING_MEMBER 1 +#define IGMP_IDLE_MEMBER 2 +#define IGMP_LAZY_MEMBER 3 +#define IGMP_SLEEPING_MEMBER 4 +#define IGMP_AWAKENING_MEMBER 5 +#ifdef notyet +#define IGMP_QUERY_PENDING_MEMBER 6 +#define IGMP_G_QUERY_PENDING_MEMBER 7 +#define IGMP_SG_QUERY_PENDING_MEMBER 8 +#endif /* * We must remember what version the subnet's querier is. @@ -102,16 +104,6 @@ #define IGMP_UNSOL_INT 1 /* Unsolicited Report interval */ /* - * IGMPv3 report types - */ -#define IGMP_REPORT_MODE_IN 1 /* mode-is-include */ -#define IGMP_REPORT_MODE_EX 2 /* mode-is-exclude */ -#define IGMP_REPORT_TO_IN 3 /* change-to-include */ -#define IGMP_REPORT_TO_EX 4 /* change-to-exclude */ -#define IGMP_REPORT_ALLOW_NEW 5 /* allow-new-sources */ -#define IGMP_REPORT_BLOCK_OLD 6 /* block-old-sources */ - -/* * Report types */ #define IGMP_MASK_CUR_STATE 0x01 /* Report current-state */ @@ -141,6 +133,7 @@ void igmp_leavegroup(struct in_multi *); void igmp_fasttimo(void); void igmp_slowtimo(void); +void igmp_purgeif(struct ifnet *); SYSCTL_DECL(_net_inet_igmp); --- //depot/vendor/freebsd/src/sys/netinet/in.c 2007/06/17 00:33:59 +++ //depot/user/bms/netdev/sys/netinet/in.c 2007/07/24 16:31:22 @@ -50,6 +50,7 @@ #include #include #include +#include static int in_mask2len(struct in_addr *); static void in_len2mask(struct in_addr *, int); @@ -990,4 +991,5 @@ in_pcbpurgeif0(&ripcbinfo, ifp); in_pcbpurgeif0(&udbinfo, ifp); in_purgemaddrs(ifp); + igmp_purgeif(ifp); } --- //depot/vendor/freebsd/src/sys/netinet/in_var.h 2007/06/12 16:28:06 +++ //depot/user/bms/netdev/sys/netinet/in_var.h 2007/07/24 16:57:08 @@ -138,15 +138,13 @@ #endif /* - * This information should be part of the ifnet structure but we don't wish - * to change that - as it might break a number of things + * Per-interface IGMP router version information. */ - struct router_info { + LIST_ENTRY(router_info) rti_link; struct ifnet *rti_ifp; - int rti_type; /* type of router which is querier on this interface */ - int rti_time; /* # of slow timeouts since last old query */ - SLIST_ENTRY(router_info) rti_list; + int rti_type; /* type of querier on this interface */ + int rti_age; /* time since last IGMPv1 query */ #ifdef notyet int rti_timev1; /* IGMPv1 querier present */ int rti_timev2; /* IGMPv2 querier present */ @@ -172,11 +170,17 @@ u_int inm_state; /* state of the membership */ struct router_info *inm_rti; /* router info*/ u_int inm_refcount; /* reference count */ -#ifdef notyet /* IGMPv3 source-specific multicast fields */ +#ifdef notyet + /* IGMPv3 source-specific multicast fields */ TAILQ_HEAD(, in_msfentry) inm_msf; /* all active source filters */ - TAILQ_HEAD(, in_msfentry) inm_msf_record; /* recorded sources */ TAILQ_HEAD(, in_msfentry) inm_msf_exclude; /* exclude sources */ TAILQ_HEAD(, in_msfentry) inm_msf_include; /* include sources */ + /* + * Stored state for pending Group-and-Source Specific Queries + * for this group. + */ + TAILQ_HEAD(, in_msfentry) inm_msf_record; /* recorded sources */ + /* XXX: should this lot go to the router_info structure? */ /* XXX: can/should these be callouts? */ /* IGMP protocol timers */