59#include <sys/module.h>
60#include <sys/malloc.h>
62#include <sys/socket.h>
63#include <sys/protosw.h>
64#include <sys/kernel.h>
66#include <sys/sysctl.h>
68#include <sys/condvar.h>
75#include <net/if_var.h>
76#include <net/netisr.h>
88#include <machine/in_cksum.h>
90#include <security/mac/mac_framework.h>
93#define KTR_IGMPV3 KTR_INET
106 const struct igmp *);
108 const struct igmp *);
122static char * igmp_rec_type_to_str(
const int);
135 struct in_multi *,
const int,
const int,
const int);
139 struct mbufq *,
struct mbufq *,
struct in_multi *,
152 .nh_proto = NETISR_IGMP,
153 .nh_policy = NETISR_POLICY_SOURCE,
228#define V_interface_timers_running VNET(interface_timers_running)
229#define V_state_change_timers_running VNET(state_change_timers_running)
230#define V_current_state_timers_running VNET(current_state_timers_running)
237 LIST_HEAD_INITIALIZER(igi_head);
240#define V_igi_head VNET(igi_head)
241#define V_igmp_gsrdelay VNET(igmp_gsrdelay)
251#define V_igmp_recvifkludge VNET(igmp_recvifkludge)
252#define V_igmp_sendra VNET(igmp_sendra)
253#define V_igmp_sendlocal VNET(igmp_sendlocal)
254#define V_igmp_v1enable VNET(igmp_v1enable)
255#define V_igmp_v2enable VNET(igmp_v2enable)
256#define V_igmp_legacysupp VNET(igmp_legacysupp)
257#define V_igmp_default_version VNET(igmp_default_version)
263 CTLFLAG_VNET | CTLTYPE_STRUCT | CTLFLAG_RW | CTLFLAG_MPSAFE,
265 "IGMP statistics (struct igmpstat, netinet/igmp_var.h)");
266SYSCTL_INT(_net_inet_igmp, OID_AUTO, recvifkludge, CTLFLAG_VNET | CTLFLAG_RW,
267 &VNET_NAME(igmp_recvifkludge), 0,
268 "Rewrite IGMPv1/v2 reports from 0.0.0.0 to contain subnet address");
269SYSCTL_INT(_net_inet_igmp, OID_AUTO, sendra, CTLFLAG_VNET | CTLFLAG_RW,
270 &VNET_NAME(igmp_sendra), 0,
271 "Send IP Router Alert option in IGMPv2/v3 messages");
272SYSCTL_INT(_net_inet_igmp, OID_AUTO, sendlocal, CTLFLAG_VNET | CTLFLAG_RW,
273 &VNET_NAME(igmp_sendlocal), 0,
274 "Send IGMP membership reports for 224.0.0.0/24 groups");
275SYSCTL_INT(_net_inet_igmp, OID_AUTO, v1enable, CTLFLAG_VNET | CTLFLAG_RW,
276 &VNET_NAME(igmp_v1enable), 0,
277 "Enable backwards compatibility with IGMPv1");
278SYSCTL_INT(_net_inet_igmp, OID_AUTO, v2enable, CTLFLAG_VNET | CTLFLAG_RW,
279 &VNET_NAME(igmp_v2enable), 0,
280 "Enable backwards compatibility with IGMPv2");
281SYSCTL_INT(_net_inet_igmp, OID_AUTO, legacysupp, CTLFLAG_VNET | CTLFLAG_RW,
282 &VNET_NAME(igmp_legacysupp), 0,
283 "Allow v1/v2 reports to suppress v3 group responses");
285 CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
287 "Default version of IGMP to run on each interface");
289 CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
291 "Rate limit for IGMPv3 Group-and-Source queries in seconds");
298 "Per-interface IGMPv3 state");
305 m->m_pkthdr.PH_loc.ptr = ifp->if_vnet;
307 m->m_pkthdr.rcvif = ifp;
308 m->m_pkthdr.flowid = ifp->if_index;
315 m->m_pkthdr.PH_loc.ptr = NULL;
316 m->m_pkthdr.flowid = 0;
331#if defined(VIMAGE) && defined(INVARIANTS)
332 KASSERT(curvnet == (m->m_pkthdr.PH_loc.ptr),
333 (
"%s: called when curvnet was not restored", __func__));
336 return (m->m_pkthdr.flowid);
349 error = sysctl_wire_old_buffer(req,
sizeof(
struct igmpstat));
353 if (req->oldptr != NULL) {
354 if (req->oldlen <
sizeof(
struct igmpstat))
361 COUNTER_ARRAY_COPY(VNET(
igmpstat), &igps0,
362 sizeof(
struct igmpstat) /
sizeof(uint64_t));
365 error = SYSCTL_OUT(req, &igps0,
369 req->validlen =
sizeof(
struct igmpstat);
372 if (req->newptr != NULL) {
373 if (req->newlen <
sizeof(
struct igmpstat))
376 error = SYSCTL_IN(req, &igps0,
384 while (p < (
char *)&igps0 +
sizeof(igps0) && *p ==
'\0')
386 if (p != (
char *)&igps0 +
sizeof(igps0)) {
391 sizeof(
struct igmpstat) /
sizeof(uint64_t));
409 error = sysctl_wire_old_buffer(req,
sizeof(
int));
417 error = sysctl_handle_int(oidp, &
new, 0, req);
418 if (error || !req->newptr)
426 CTR2(
KTR_IGMPV3,
"change igmp_default_version from %d to %d",
448 error = sysctl_wire_old_buffer(req,
sizeof(
int));
456 error = sysctl_handle_int(oidp, &i, 0, req);
457 if (error || !req->newptr)
460 if (i < -1 || i >= 60) {
465 CTR2(
KTR_IGMPV3,
"change igmp_gsrdelay from %d to %d",
485 struct epoch_tracker et;
495 if (req->newptr != NULL)
501 error = sysctl_wire_old_buffer(req,
sizeof(
struct igmp_ifinfo));
511 ifp = ifnet_byindex(name[0]);
529 error = SYSCTL_OUT(req, &info,
sizeof(info));
548 struct epoch_tracker et;
552 while ((m = mbufq_dequeue(mq)) != NULL) {
553 CTR3(
KTR_IGMPV3,
"%s: dispatch %p from %p", __func__, mq, m);
556 netisr_dispatch(NETISR_IGMP, m);
595 m = m_get(M_WAITOK, MT_DATA);
616 __func__, ifp, ifp->if_xname);
621 if (!(ifp->if_flags & IFF_MULTICAST))
639 igi = malloc(
sizeof(
struct igmp_ifsoftc), M_IGMP, M_NOWAIT|M_ZERO);
654 CTR2(
KTR_IGMPV3,
"allocate igmp_ifsoftc for ifp %p(%s)",
674 struct ifmultiaddr *ifma, *
next;
676 struct in_multi_head inm_free_tmp;
677 CTR3(
KTR_IGMPV3,
"%s: called for ifp %p(%s)", __func__, ifp,
680 SLIST_INIT(&inm_free_tmp);
683 igi = ((
struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp;
687 CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link,
next) {
688 if (ifma->ifma_addr->sa_family != AF_INET ||
689 ifma->ifma_protospec == NULL)
691 inm = (
struct in_multi *)ifma->ifma_protospec;
700 IF_ADDR_WUNLOCK(ifp);
715 __func__, ifp, ifp->if_xname);
727 CTR3(
KTR_IGMPV3,
"%s: freeing igmp_ifsoftc for ifp %p(%s)",
728 __func__, ifp, ifp->if_xname);
732 LIST_FOREACH_SAFE(igi, &
V_igi_head, igi_link, tigi) {
737 mbufq_drain(&igi->
igi_gq);
739 LIST_REMOVE(igi, igi_link);
756 struct ifmultiaddr *ifma;
778 igi = ((
struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp;
779 KASSERT(igi != NULL, (
"%s: no igmp_ifsoftc for ifp %p", __func__, ifp));
782 CTR2(
KTR_IGMPV3,
"ignore v1 query on IGIF_LOOPBACK ifp %p(%s)",
792 CTR2(
KTR_IGMPV3,
"process v1 query on ifp %p(%s)", ifp, ifp->if_xname);
799 CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
800 if (ifma->ifma_addr->sa_family != AF_INET ||
801 ifma->ifma_protospec == NULL)
803 inm = (
struct in_multi *)ifma->ifma_protospec;
841 struct ifmultiaddr *ifma;
844 int is_general_query;
849 is_general_query = 0;
863 is_general_query = 1;
872 igi = ((
struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp;
873 KASSERT(igi != NULL, (
"%s: no igmp_ifsoftc for ifp %p", __func__, ifp));
876 CTR2(
KTR_IGMPV3,
"ignore v2 query on IGIF_LOOPBACK ifp %p(%s)",
893 if (is_general_query) {
898 CTR2(
KTR_IGMPV3,
"process v2 general query on ifp %p(%s)",
900 CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
901 if (ifma->ifma_addr->sa_family != AF_INET ||
902 ifma->ifma_protospec == NULL)
904 inm = (
struct in_multi *)ifma->ifma_protospec;
915 "process v2 query 0x%08x on ifp %p(%s)",
947 CTR4(
KTR_IGMPV3,
"0x%08x: %s/%s timer=%d", __func__,
959 CTR1(
KTR_IGMPV3,
"%s: REPORTING and timer running, "
960 "skipping.", __func__);
969 CTR1(
KTR_IGMPV3,
"%s: ->REPORTING", __func__);
975 CTR1(
KTR_IGMPV3,
"%s: ->AWAKENING", __func__);
995 int is_general_query;
1000 is_general_query = 0;
1002 CTR2(
KTR_IGMPV3,
"process v3 query on ifp %p(%s)", ifp, ifp->if_xname);
1005 if (maxresp >= 128) {
1018 CTR3(
KTR_IGMPV3,
"%s: clamping qrv %d to %d", __func__,
1053 is_general_query = 1;
1065 igi = ((
struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp;
1066 KASSERT(igi != NULL, (
"%s: no igmp_ifsoftc for ifp %p", __func__, ifp));
1069 CTR2(
KTR_IGMPV3,
"ignore v3 query on IGIF_LOOPBACK ifp %p(%s)",
1070 ifp, ifp->if_xname);
1081 CTR3(
KTR_IGMPV3,
"ignore v3 query in v%d mode on ifp %p(%s)",
1091 CTR4(
KTR_IGMPV3,
"%s: qrv %d qi %d qri %d", __func__, qrv, qqi,
1094 if (is_general_query) {
1103 CTR2(
KTR_IGMPV3,
"process v3 general query on ifp %p(%s)",
1104 ifp, ifp->if_xname);
1128 CTR3(
KTR_IGMPV3,
"process v3 0x%08x query on ifp %p(%s)",
1232 for (i = 0; i < nsrc; i++, ap++) {
1236 nrecorded += retval;
1238 if (nrecorded > 0) {
1240 "%s: schedule response to SG query", __func__);
1264 if (ifp->if_flags & IFF_LOOPBACK)
1286 CTR3(
KTR_IGMPV3,
"process v1 report 0x%08x on ifp %p(%s)",
1301 KASSERT(igi != NULL,
1302 (
"%s: no igi for ifp %p", __func__, ifp));
1329 "report suppressed for 0x%08x on ifp %p(%s)",
1337 "report suppressed for 0x%08x on ifp %p(%s)",
1382 if (ifp->if_flags & IFF_LOOPBACK) {
1404 CTR3(
KTR_IGMPV3,
"process v2 report 0x%08x on ifp %p(%s)",
1419 KASSERT(igi != NULL, (
"%s: no igi for ifp %p", __func__, ifp));
1445 "report suppressed for 0x%08x on ifp %p(%s)",
1475 CTR3(
KTR_IGMPV3,
"%s: called w/mbuf (%p,%d)", __func__, *mp, *offp);
1478 ifp = m->m_pkthdr.rcvif;
1483 ip = mtod(m,
struct ip *);
1485 igmplen = ntohs(
ip->
ip_len) - iphlen;
1493 return (IPPROTO_DONE);
1505 if ((!M_WRITABLE(m) || m->m_len < minlen) &&
1506 (m = m_pullup(m, minlen)) == NULL) {
1508 return (IPPROTO_DONE);
1510 ip = mtod(m,
struct ip *);
1515 m->m_data += iphlen;
1518 if (in_cksum(m, igmplen)) {
1521 return (IPPROTO_DONE);
1523 m->m_data -= iphlen;
1534 return (IPPROTO_DONE);
1549 return (IPPROTO_DONE);
1559 return (IPPROTO_DONE);
1569 return (IPPROTO_DONE);
1588 return (IPPROTO_DONE);
1595 sizeof(
struct in_addr) * nsrc;
1596 if ((!M_WRITABLE(m) ||
1597 m->m_len < igmpv3len) &&
1598 (m = m_pullup(m, igmpv3len)) == NULL) {
1600 return (IPPROTO_DONE);
1606 return (IPPROTO_DONE);
1618 return (IPPROTO_DONE);
1629 return (IPPROTO_DONE);
1661 VNET_ITERATOR_DECL(vnet_iter);
1663 VNET_LIST_RLOCK_NOSLEEP();
1664 VNET_FOREACH(vnet_iter) {
1665 CURVNET_SET(vnet_iter);
1669 VNET_LIST_RUNLOCK_NOSLEEP();
1685 struct ifmultiaddr *ifma, *
next;
1687 struct in_multi_head inm_free_tmp;
1688 int loop, uri_fasthz;
1703 SLIST_INIT(&inm_free_tmp);
1711 CTR1(
KTR_IGMPV3,
"%s: interface timers running", __func__);
1732 CTR1(
KTR_IGMPV3,
"%s: state change timers running", __func__);
1751 CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link,
next) {
1752 if (ifma->ifma_addr->sa_family != AF_INET ||
1753 ifma->ifma_protospec == NULL)
1755 inm = (
struct in_multi *)ifma->ifma_protospec;
1764 &scq, inm, uri_fasthz);
1772 IF_ADDR_WUNLOCK(ifp);
1798 int report_timer_expired;
1804 report_timer_expired = 0;
1806 report_timer_expired = 1;
1821 if (report_timer_expired) {
1843 struct mbufq *qrq,
struct mbufq *scq,
1844 struct in_multi *inm,
const int uri_fasthz)
1846 int query_response_timer_expired;
1847 int state_change_retransmit_timer_expired;
1852 query_response_timer_expired = 0;
1853 state_change_retransmit_timer_expired = 0;
1862 query_response_timer_expired = 0;
1864 query_response_timer_expired = 1;
1870 state_change_retransmit_timer_expired = 0;
1872 state_change_retransmit_timer_expired = 1;
1878 if (!state_change_retransmit_timer_expired &&
1879 !query_response_timer_expired)
1898 if (query_response_timer_expired) {
1899 int retval __unused;
1912 if (state_change_retransmit_timer_expired) {
1933 CTR3(
KTR_IGMPV3,
"%s: T1 -> T0 for 0x%08x/%s", __func__,
1970 (
"%s: not IGMPv3 mode on link", __func__));
1990 int old_version_timer;
1994 CTR4(
KTR_IGMPV3,
"%s: switching to v%d on ifp %p(%s)", __func__,
2003 old_version_timer *= PR_SLOWHZ;
2039 struct ifmultiaddr *ifma, *ifmatmp;
2042 struct in_multi_head inm_free_tmp;
2044 CTR3(
KTR_IGMPV3,
"%s: cancel v3 timers on ifp %p(%s)", __func__,
2051 SLIST_INIT(&inm_free_tmp);
2066 CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, ifmatmp) {
2067 if (ifma->ifma_addr->sa_family != AF_INET ||
2068 ifma->ifma_protospec == NULL)
2070 inm = (
struct in_multi *)ifma->ifma_protospec;
2110 IF_ADDR_WUNLOCK(ifp);
2133 "%s: transition from v%d -> v%d on %p(%s)",
2148 "%s: transition from v%d -> v%d on %p(%s)",
2157 "%s: transition from v%d -> v%d on %p(%s)",
2175 "%s: transition from v%d -> v%d on %p(%s)",
2185 "%s: cancel v2 timer on %p(%s)",
2199 VNET_ITERATOR_DECL(vnet_iter);
2201 VNET_LIST_RLOCK_NOSLEEP();
2202 VNET_FOREACH(vnet_iter) {
2203 CURVNET_SET(vnet_iter);
2207 VNET_LIST_RUNLOCK_NOSLEEP();
2234 struct epoch_tracker et;
2245 m = m_gethdr(M_NOWAIT, MT_DATA);
2248 M_ALIGN(m,
sizeof(
struct ip) +
sizeof(
struct igmp));
2250 m->m_pkthdr.len =
sizeof(
struct ip) + sizeof(struct
igmp);
2252 m->m_data +=
sizeof(
struct ip);
2253 m->m_len =
sizeof(
struct igmp);
2262 m->m_data -=
sizeof(
struct ip);
2263 m->m_len +=
sizeof(
struct ip);
2265 ip = mtod(m,
struct ip *);
2283 CTR2(
KTR_IGMPV3,
"%s: netisr_dispatch(NETISR_IGMP, %p)", __func__, m);
2284 NET_EPOCH_ENTER(et);
2285 netisr_dispatch(NETISR_IGMP, m);
2325 KASSERT(inm->
inm_ifma != NULL, (
"%s: no ifma", __func__));
2331 KASSERT(inm->
inm_ifp == ifp, (
"%s: bad ifp", __func__));
2335 igi = ((
struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp;
2336 KASSERT(igi != NULL, (
"%s: no igmp_ifsoftc for ifp %p", __func__, ifp));
2344 CTR3(
KTR_IGMPV3,
"%s: inm transition %d -> %d", __func__,
2347 CTR1(
KTR_IGMPV3,
"%s: initial join", __func__);
2351 CTR1(
KTR_IGMPV3,
"%s: final leave", __func__);
2356 CTR1(
KTR_IGMPV3,
"%s: filter set change", __func__);
2381 int error, retval, syncstates;
2383 CTR4(
KTR_IGMPV3,
"%s: initial join 0x%08x on ifp %p(%s)", __func__,
2394 KASSERT(igi && igi->
igi_ifp == ifp, (
"%s: inconsistent ifp", __func__));
2405 if ((ifp->if_flags & IFF_LOOPBACK) ||
2409"%s: not kicking state machine for silent group", __func__);
2461 error = retval * -1;
2475 (
"%s: invalid robustness %d", __func__,
2494 CTR3(
KTR_IGMPV3,
"%s: T1 -> T0 for 0x%08x/%s", __func__,
2510 CTR4(
KTR_IGMPV3,
"%s: state change for 0x%08x on ifp %p(%s)", __func__,
2518 KASSERT(igi && igi->
igi_ifp == ifp, (
"%s: inconsistent ifp", __func__));
2520 if ((ifp->if_flags & IFF_LOOPBACK) ||
2526"%s: not kicking state machine for silent group", __func__);
2528 CTR1(
KTR_IGMPV3,
"%s: nothing to do", __func__);
2530 CTR3(
KTR_IGMPV3,
"%s: T1 -> T0 for 0x%08x/%s", __func__,
2538 CTR2(
KTR_IGMPV3,
"%s: enqueue record = %d", __func__, retval);
2569 CTR4(
KTR_IGMPV3,
"%s: final leave 0x%08x on ifp %p(%s)",
2582"%s: not kicking state machine for silent group", __func__);
2592 panic(
"%s: IGMPv3 state reached, not IGMPv3 mode",
2611 CTR4(
KTR_IGMPV3,
"%s: Leaving 0x%08x/%s with %d "
2612 "pending retransmissions.", __func__,
2619 int retval __unused;
2625 KASSERT(retval != 0,
2626 (
"%s: enqueue record = %d", __func__,
2646 CTR3(
KTR_IGMPV3,
"%s: T1 -> T0 for 0x%08x/%s", __func__,
2649 CTR3(
KTR_IGMPV3,
"%s: T1 now MCAST_UNDEFINED for 0x%08x/%s",
2684 const int is_state_change,
const int is_group_query,
2685 const int is_source_query)
2691 struct mbuf *m0, *m, *md;
2692 int is_filter_list_change;
2693 int minrec0len, m0srcs, msrcs, nbytes, off;
2694 int record_has_sources;
2703 is_filter_list_change = 0;
2710 record_has_sources = 1;
2722 record_has_sources = 0;
2724 if (is_state_change) {
2736 if (mode == MCAST_EXCLUDE) {
2744 if (mode == MCAST_UNDEFINED)
2745 record_has_sources = 0;
2748 if (record_has_sources) {
2749 is_filter_list_change = 1;
2758 if (mode == MCAST_EXCLUDE) {
2760 }
else if (mode == MCAST_INCLUDE) {
2763 (
"%s: inm %p is INCLUDE but ASM count is %d",
2771 if (is_filter_list_change)
2775 CTR3(
KTR_IGMPV3,
"%s: nothing to do for 0x%08x/%s", __func__,
2786 if (record_has_sources)
2789 CTR4(
KTR_IGMPV3,
"%s: queueing %s for 0x%08x/%s", __func__,
2801 m0 = mbufq_last(mq);
2802 if (!is_group_query &&
2805 (m0->m_pkthdr.len + minrec0len) <
2807 m0srcs = (ifp->if_mtu - m0->m_pkthdr.len -
2810 CTR1(
KTR_IGMPV3,
"%s: use existing packet", __func__);
2812 if (mbufq_full(mq)) {
2813 CTR1(
KTR_IGMPV3,
"%s: outbound queue full", __func__);
2819 if (!is_state_change && !is_group_query) {
2820 m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
2825 m = m_gethdr(M_NOWAIT, MT_DATA);
2834 CTR1(
KTR_IGMPV3,
"%s: allocated first packet", __func__);
2845 if (!m_append(m,
sizeof(
struct igmp_grouprec), (
void *)&ig)) {
2848 CTR1(
KTR_IGMPV3,
"%s: m_append() failed.", __func__);
2866 if (record_has_sources) {
2870 md->m_len - nbytes);
2872 md = m_getptr(m, 0, &off);
2877 RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->
inm_srcs, nims) {
2878 CTR2(
KTR_IGMPV3,
"%s: visit node 0x%08x", __func__,
2881 CTR2(
KTR_IGMPV3,
"%s: node is %d", __func__, now);
2882 if ((now != mode) ||
2883 (now == mode && mode == MCAST_UNDEFINED)) {
2887 if (is_source_query && ims->
ims_stp == 0) {
2892 CTR1(
KTR_IGMPV3,
"%s: append node", __func__);
2894 if (!m_append(m,
sizeof(
in_addr_t), (
void *)&naddr)) {
2903 if (msrcs == m0srcs)
2906 CTR2(
KTR_IGMPV3,
"%s: msrcs is %d this packet", __func__,
2912 if (is_source_query && msrcs == 0) {
2913 CTR1(
KTR_IGMPV3,
"%s: no recorded sources to report", __func__);
2923 CTR1(
KTR_IGMPV3,
"%s: enqueueing first packet", __func__);
2924 m->m_pkthdr.PH_vt.vt_nrecs = 1;
2925 mbufq_enqueue(mq, m);
2927 m->m_pkthdr.PH_vt.vt_nrecs++;
2932 if (!record_has_sources)
2940 while (nims != NULL) {
2941 if (mbufq_full(mq)) {
2942 CTR1(
KTR_IGMPV3,
"%s: outbound queue full", __func__);
2945 m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
2949 m = m_gethdr(M_NOWAIT, MT_DATA);
2956 md = m_getptr(m, 0, &off);
2958 CTR1(
KTR_IGMPV3,
"%s: allocated next packet", __func__);
2960 if (!m_append(m,
sizeof(
struct igmp_grouprec), (
void *)&ig)) {
2963 CTR1(
KTR_IGMPV3,
"%s: m_append() failed.", __func__);
2966 m->m_pkthdr.PH_vt.vt_nrecs = 1;
2973 RB_FOREACH_FROM(ims, ip_msource_tree, nims) {
2974 CTR2(
KTR_IGMPV3,
"%s: visit node 0x%08x", __func__,
2977 if ((now != mode) ||
2978 (now == mode && mode == MCAST_UNDEFINED)) {
2982 if (is_source_query && ims->
ims_stp == 0) {
2987 CTR1(
KTR_IGMPV3,
"%s: append node", __func__);
2989 if (!m_append(m,
sizeof(
in_addr_t), (
void *)&naddr)) {
2997 if (msrcs == m0srcs)
3003 CTR1(
KTR_IGMPV3,
"%s: enqueueing next packet", __func__);
3004 mbufq_enqueue(mq, m);
3046 static const int MINRECLEN =
3052 struct mbuf *m, *m0, *md;
3054 int m0srcs, nbytes, npbytes, off, rsrcs, schanged;
3088 m0 = mbufq_last(mq);
3090 (m0->m_pkthdr.PH_vt.vt_nrecs + 1 <=
3092 (m0->m_pkthdr.len + MINRECLEN) <
3095 m0srcs = (ifp->if_mtu - m0->m_pkthdr.len -
3099 "%s: use previous packet", __func__);
3101 m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
3105 m = m_gethdr(M_NOWAIT, MT_DATA);
3111 "%s: m_get*() failed", __func__);
3114 m->m_pkthdr.PH_vt.vt_nrecs = 0;
3121 "%s: allocated new packet", __func__);
3130 memset(&ig, 0,
sizeof(ig));
3132 if (!m_append(m,
sizeof(ig), (
void *)&ig)) {
3136 "%s: m_append() failed", __func__);
3142 md = m_getptr(m, npbytes -
3164 nims = RB_MIN(ip_msource_tree, &inm->
inm_srcs);
3165 RB_FOREACH_FROM(ims, ip_msource_tree, nims) {
3171 __func__, then, now);
3174 "%s: skip unchanged", __func__);
3177 if (mode == MCAST_EXCLUDE &&
3178 now == MCAST_INCLUDE) {
3180 "%s: skip IN src on EX group",
3187 if (schanged++ == 0) {
3189 }
else if (crt != nrt)
3197 "%s: m_append() failed", __func__);
3202 if (++rsrcs == m0srcs)
3213 "%s: m_free(m)", __func__);
3217 "%s: m_adj(m, -ig)", __func__);
3218 m_adj(m, -((
int)
sizeof(
3233 m->m_pkthdr.PH_vt.vt_nrecs++;
3235 mbufq_enqueue(mq, m);
3237 }
while (nims != NULL);
3242 CTR3(
KTR_IGMPV3,
"%s: queued %d ALLOW_NEW, %d BLOCK_OLD", __func__,
3255 int docopy, domerge;
3274 if (mbufq_first(gq) == NULL) {
3275 CTR2(
KTR_IGMPV3,
"%s: WARNING: queue for inm %p is empty",
3280 m = mbufq_first(gq);
3291 mt = mbufq_last(scq);
3293 recslen = m_length(m, NULL);
3295 if ((mt->m_pkthdr.PH_vt.vt_nrecs +
3296 m->m_pkthdr.PH_vt.vt_nrecs <=
3298 (mt->m_pkthdr.len + recslen <=
3303 if (!domerge && mbufq_full(gq)) {
3305 "%s: outbound queue full, skipping whole packet %p",
3315 CTR2(
KTR_IGMPV3,
"%s: dequeueing %p", __func__, m);
3316 m0 = mbufq_dequeue(gq);
3319 CTR2(
KTR_IGMPV3,
"%s: copying %p", __func__, m);
3320 m0 = m_dup(m, M_NOWAIT);
3323 m0->m_nextpkt = NULL;
3328 CTR3(
KTR_IGMPV3,
"%s: queueing %p to scq %p)",
3330 mbufq_enqueue(scq, m0);
3334 CTR3(
KTR_IGMPV3,
"%s: merging %p with scq tail %p)",
3338 m0->m_flags &= ~M_PKTHDR;
3339 mt->m_pkthdr.len += recslen;
3340 mt->m_pkthdr.PH_vt.vt_nrecs +=
3341 m0->m_pkthdr.PH_vt.vt_nrecs;
3356 struct ifmultiaddr *ifma;
3359 int retval __unused, loop;
3366 (
"%s: called when version %d", __func__, igi->
igi_version));
3374 if (mbufq_len(&igi->
igi_gq) != 0)
3379 CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
3380 if (ifma->ifma_addr->sa_family != AF_INET ||
3381 ifma->ifma_protospec == NULL)
3384 inm = (
struct in_multi *)ifma->ifma_protospec;
3386 (
"%s: inconsistent ifp", __func__));
3417 if (mbufq_first(&igi->
igi_gq) != NULL) {
3437 struct ip_moptions imo;
3439 struct mbuf *ipopts, *m0;
3443 CTR2(
KTR_IGMPV3,
"%s: transmit %p", __func__, m);
3451 CURVNET_SET((
struct vnet *)(m->m_pkthdr.PH_loc.ptr));
3459 ifp = ifnet_byindex(ifindex);
3461 CTR3(
KTR_IGMPV3,
"%s: dropped %p as ifindex %u went away.",
3462 __func__, m, ifindex);
3470 imo.imo_multicast_ttl = 1;
3471 imo.imo_multicast_vif = -1;
3481 imo.imo_multicast_ifp = V_loif;
3483 imo.imo_multicast_ifp = ifp;
3490 CTR2(
KTR_IGMPV3,
"%s: dropped %p", __func__, m);
3499 m0->m_pkthdr.rcvif = V_loif;
3501 mac_netinet_igmp_send(ifp, m0);
3503 error =
ip_output(m0, ipopts, NULL, 0, &imo, NULL);
3505 CTR3(
KTR_IGMPV3,
"%s: ip_output(%p) = %d", __func__, m0, error);
3535 int hdrlen, igmpreclen;
3537 KASSERT((m->m_flags & M_PKTHDR),
3538 (
"%s: mbuf chain %p is !M_PKTHDR", __func__, m));
3540 igmpreclen = m_length(m, NULL);
3544 igmpreclen -= hdrlen;
3546 M_PREPEND(m, hdrlen, M_NOWAIT);
3552 CTR2(
KTR_IGMPV3,
"%s: igmpreclen is %d", __func__, igmpreclen);
3554 m->m_data +=
sizeof(
struct ip);
3555 m->m_len -=
sizeof(
struct ip);
3561 igmp->ir_numgrps = htons(m->m_pkthdr.PH_vt.vt_nrecs);
3563 igmp->ir_cksum = in_cksum(m,
sizeof(
struct igmp_report) + igmpreclen);
3564 m->m_pkthdr.PH_vt.vt_nrecs = 0;
3566 m->m_data -=
sizeof(
struct ip);
3567 m->m_len +=
sizeof(
struct ip);
3569 ip = mtod(m,
struct ip *);
3571 ip->
ip_len = htons(hdrlen + igmpreclen);
3593igmp_rec_type_to_str(
const int type)
3624vnet_igmp_init(
const void *unused __unused)
3627 netisr_register_vnet(&
igmp_nh);
3629VNET_SYSINIT(vnet_igmp_init, SI_SUB_PROTO_MC, SI_ORDER_ANY,
3630 vnet_igmp_init, NULL);
3633vnet_igmp_uninit(
const void *unused __unused)
3637 CTR1(
KTR_IGMPV3,
"%s: tearing down", __func__);
3639 netisr_unregister_vnet(&
igmp_nh);
3642 vnet_igmp_uninit, NULL);
3646DB_SHOW_COMMAND(igi_list, db_show_igi_list)
3652 db_printf(
"usage: show igi_list <addr>\n");
3655 igi_head = (
struct _igi_list *)addr;
3657 LIST_FOREACH_SAFE(igi, igi_head, igi_link, tigi) {
3658 db_printf(
"igmp_ifsoftc %p:\n", igi);
3659 db_printf(
" ifp %p\n", igi->
igi_ifp);
3664 db_printf(
" flags %#x\n", igi->
igi_flags);
3665 db_printf(
" rv %u\n", igi->
igi_rv);
3666 db_printf(
" qi %u\n", igi->
igi_qi);
3667 db_printf(
" qri %u\n", igi->
igi_qri);
3668 db_printf(
" uri %u\n", igi->
igi_uri);
3681 CTR1(
KTR_IGMPV3,
"%s: initializing", __func__);
3687 CTR1(
KTR_IGMPV3,
"%s: tearing down", __func__);
3694 return (EOPNOTSUPP);
VNET_SYSINIT(vnet_cc_sysinit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_cc_sysinit, NULL)
static int sysctl_igmp_default_version(SYSCTL_HANDLER_ARGS)
static int igmp_initial_join(struct in_multi *, struct igmp_ifsoftc *)
#define V_igmp_recvifkludge
static struct igmp_ifsoftc * igi_alloc_locked(struct ifnet *)
static int igmp_isgroupreported(const struct in_addr)
static int sysctl_igmp_gsr(SYSCTL_HANDLER_ARGS)
static int sysctl_igmp_ifinfo(SYSCTL_HANDLER_ARGS)
#define V_igmp_default_version
static void igi_delete_locked(const struct ifnet *)
static int igmp_v3_enqueue_filter_change(struct mbufq *, struct in_multi *)
static int igmp_input_v1_query(struct ifnet *, const struct ip *, const struct igmp *)
#define V_interface_timers_running
static void igmp_v1v2_process_group_timer(struct in_multi *, const int)
static int igmp_input_v3_group_query(struct in_multi *, struct igmp_ifsoftc *, int, struct igmpv3 *)
VNET_PCPUSTAT_SYSUNINIT(igmpstat)
static moduledata_t igmp_mod
static void igmp_intr(struct mbuf *)
static int sysctl_igmp_stat(SYSCTL_HANDLER_ARGS)
static void igmp_v3_dispatch_general_query(struct igmp_ifsoftc *)
static MALLOC_DEFINE(M_IGMP, "igmp", "igmp state")
VNET_DEFINE_STATIC(int, interface_timers_running)
void igmp_domifdetach(struct ifnet *ifp)
#define V_current_state_timers_running
static __inline void igmp_scrub_context(struct mbuf *m)
static int igmp_input_v2_report(struct ifnet *, struct ip *, struct igmp *)
static int igmp_v3_enqueue_group_record(struct mbufq *, struct in_multi *, const int, const int, const int)
#define V_igmp_legacysupp
#define V_state_change_timers_running
static void igmp_set_version(struct igmp_ifsoftc *, const int)
static void igmp_v3_suppress_group_record(struct in_multi *)
int igmp_input(struct mbuf **mp, int *offp, int proto)
static void igmp_final_leave(struct in_multi *, struct igmp_ifsoftc *)
static void igmp_v3_cancel_link_timers(struct igmp_ifsoftc *)
DECLARE_MODULE(igmp, igmp_mod, SI_SUB_PROTO_MC, SI_ORDER_MIDDLE)
static struct mbuf * igmp_v3_encap_report(struct ifnet *, struct mbuf *)
static void igmp_v2_update_group(struct in_multi *, const int)
static void igmp_slowtimo_vnet(void)
struct igmp_ifsoftc * igmp_domifattach(struct ifnet *ifp)
static void igmp_fasttimo_vnet(void)
static void igmp_v3_process_group_timers(struct in_multi_head *, struct mbufq *, struct mbufq *, struct in_multi *, const int)
static int igmp_input_v3_query(struct ifnet *, const struct ip *, struct igmpv3 *)
static __inline void igmp_save_context(struct mbuf *m, struct ifnet *ifp)
static void igmp_v1v2_process_querier_timers(struct igmp_ifsoftc *)
static struct mbuf * igmp_ra_alloc(void)
VNET_PCPUSTAT_SYSINIT(igmpstat)
static int igmp_input_v2_query(struct ifnet *, const struct ip *, const struct igmp *)
VNET_PCPUSTAT_DEFINE(struct igmpstat, igmpstat)
void igmp_ifdetach(struct ifnet *ifp)
static int igmp_handle_state_change(struct in_multi *, struct igmp_ifsoftc *)
static int igmp_v3_merge_state_changes(struct in_multi *, struct mbufq *)
static __inline uint32_t igmp_restore_context(struct mbuf *m)
static SYSCTL_NODE(_net_inet_igmp, OID_AUTO, ifinfo, CTLFLAG_RD|CTLFLAG_MPSAFE, sysctl_igmp_ifinfo, "Per-interface IGMPv3 state")
static const struct netisr_handler igmp_nh
static int igmp_v1v2_queue_report(struct in_multi *, const int)
SYSCTL_PROC(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_VNET|CTLTYPE_STRUCT|CTLFLAG_RW|CTLFLAG_MPSAFE, &VNET_NAME(igmpstat), 0, sysctl_igmp_stat, "S,igmpstat", "IGMP statistics (struct igmpstat, netinet/igmp_var.h)")
int igmp_change_state(struct in_multi *inm)
SYSCTL_INT(_net_inet_igmp, OID_AUTO, recvifkludge, CTLFLAG_VNET|CTLFLAG_RW, &VNET_NAME(igmp_recvifkludge), 0, "Rewrite IGMPv1/v2 reports from 0.0.0.0 to contain subnet address")
static int igmp_input_v1_report(struct ifnet *, struct ip *, struct igmp *)
static void igmp_dispatch_queue(struct mbufq *, int, const int)
static int igmp_modevent(module_t mod, int type, void *unused __unused)
#define IGMP_MODE_IS_EXCLUDE
#define IGMP_BLOCK_OLD_SOURCES
#define IGMP_v3_HOST_MEMBERSHIP_REPORT
#define IGMP_V3_QUERY_MINLEN
#define IGMP_ALLOW_NEW_SOURCES
#define IGMP_MODE_IS_INCLUDE
#define IGMP_v1_HOST_MEMBERSHIP_REPORT
#define IGMP_V3_REPORT_MAXRECS
#define IGMP_HOST_LEAVE_MESSAGE
#define IGMP_HOST_MEMBERSHIP_QUERY
#define IGMP_CHANGE_TO_EXCLUDE_MODE
#define IGMP_v2_HOST_MEMBERSHIP_REPORT
#define IGMP_CHANGE_TO_INCLUDE_MODE
#define IGMP_LOCK_ASSERT()
#define IGMP_AWAKENING_MEMBER
#define IGMP_LOCK_DESTROY()
#define IGMP_G_QUERY_PENDING_MEMBER
#define IGMP_LEADINGSPACE
#define IGMP_REPORTING_MEMBER
#define IGMP_SLEEPING_MEMBER
#define IGMP_RESPONSE_BURST_INTERVAL
#define IGMP_MAX_RESPONSE_BURST
#define IGMP_RANDOM_DELAY(X)
#define IGMP_MAX_G_GS_PACKETS
#define IGMP_MAX_STATE_CHANGE_PACKETS
#define IGMP_SG_QUERY_PENDING_MEMBER
#define IGMP_MAX_RESPONSE_PACKETS
#define IGMP_LEAVING_MEMBER
#define IGPS_VERSION3_LEN
#define IGMP_SILENT_MEMBER
#define IGMPSTAT_INC(name)
void inm_release_list_deferred(struct in_multi_head *inmh)
int inm_record_source(struct in_multi *inm, const in_addr_t naddr)
void inm_commit(struct in_multi *inm)
void inm_clear_recorded(struct in_multi *inm)
struct in_multi * inm_lookup(struct ifnet *ifp, const struct in_addr ina)
static __inline uint8_t ims_get_mode(const struct in_multi *inm, const struct ip_msource *ims, uint8_t t)
#define IN_MULTI_LOCK_ASSERT()
#define IN_MULTI_LIST_UNLOCK()
static __inline void inm_acquire_locked(struct in_multi *inm)
#define IFP_TO_IA(ifp, ia)
static __inline void inm_rele_locked(struct in_multi_head *inmh, struct in_multi *inm)
#define IN_MULTI_LIST_LOCK()
#define IN_MULTI_LIST_LOCK_ASSERT()
#define IPTOS_PREC_INTERNETCONTROL
static LIST_HEAD(carp_softc)
VNET_SYSUNINIT(divert, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, div_destroy, NULL)
int ip_checkrouteralert(struct mbuf *m)
int ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags, struct ip_moptions *imo, struct inpcb *inp)
int rip_input(struct mbuf **, int *, int)
struct in_addr igmp_group
struct in_addr igmp_group
struct sockaddr_in ia_addr
struct igmp_ifsoftc * inm_igi
struct ifmultiaddr * inm_ifma
struct in_multi::inm_st inm_st[2]
struct ip_msource_tree inm_srcs
struct timeval inm_lastgsrtv
struct in_addr ip_src ip_dst
char ipopt_list[MAX_IPOPTLEN]