3 * Copyright (C) 2008 Everton da Silva Marques
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 #include "lib_errors.h"
31 #include "pim_igmpv2.h"
32 #include "pim_igmpv3.h"
33 #include "pim_igmp_mtrace.h"
34 #include "pim_iface.h"
36 #include "pim_mroute.h"
43 static void group_timer_off(struct gm_group
*group
);
44 static void pim_igmp_general_query(struct thread
*t
);
46 void igmp_anysource_forward_start(struct pim_instance
*pim
,
47 struct gm_group
*group
)
49 struct gm_source
*source
;
50 struct in_addr src_addr
= {.s_addr
= 0};
51 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
52 assert(group
->group_filtermode_isexcl
);
53 assert(listcount(group
->group_source_list
) < 1);
55 source
= igmp_get_source_by_addr(group
, src_addr
, NULL
);
57 zlog_warn("%s: Failure to create * source", __func__
);
61 igmp_source_forward_start(pim
, source
);
64 void igmp_anysource_forward_stop(struct gm_group
*group
)
66 struct gm_source
*source
;
67 struct in_addr star
= {.s_addr
= 0};
69 source
= igmp_find_source_by_addr(group
, star
);
71 igmp_source_forward_stop(source
);
74 static void igmp_source_forward_reevaluate_one(struct pim_instance
*pim
,
75 struct gm_source
*source
)
78 struct gm_group
*group
= source
->source_group
;
79 struct pim_ifchannel
*ch
;
81 if ((source
->source_addr
.s_addr
!= INADDR_ANY
) ||
82 !IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
))
85 memset(&sg
, 0, sizeof(sg
));
86 sg
.src
= source
->source_addr
;
87 sg
.grp
= group
->group_addr
;
89 ch
= pim_ifchannel_find(group
->interface
, &sg
);
90 if (pim_is_grp_ssm(pim
, group
->group_addr
)) {
91 /* If SSM group withdraw local membership */
93 (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_INCLUDE
)) {
94 if (PIM_DEBUG_PIM_EVENTS
)
96 "local membership del for %pSG as G is now SSM",
98 pim_ifchannel_local_membership_del(group
->interface
,
102 /* If ASM group add local membership */
104 (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
)) {
105 if (PIM_DEBUG_PIM_EVENTS
)
107 "local membership add for %pSG as G is now ASM",
109 pim_ifchannel_local_membership_add(
110 group
->interface
, &sg
, false /*is_vxlan*/);
115 void igmp_source_forward_reevaluate_all(struct pim_instance
*pim
)
117 struct interface
*ifp
;
119 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
120 struct pim_interface
*pim_ifp
= ifp
->info
;
121 struct listnode
*grpnode
;
122 struct gm_group
*grp
;
123 struct pim_ifchannel
*ch
, *ch_temp
;
128 /* scan igmp groups */
129 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_group_list
, grpnode
,
131 struct listnode
*srcnode
;
132 struct gm_source
*src
;
134 /* scan group sources */
135 for (ALL_LIST_ELEMENTS_RO(grp
->group_source_list
,
137 igmp_source_forward_reevaluate_one(pim
, src
);
138 } /* scan group sources */
139 } /* scan igmp groups */
141 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
,
143 if (pim_is_grp_ssm(pim
, ch
->sg
.grp
)) {
144 if (pim_addr_is_any(ch
->sg
.src
))
145 pim_ifchannel_delete(ch
);
148 } /* scan interfaces */
151 void igmp_source_forward_start(struct pim_instance
*pim
,
152 struct gm_source
*source
)
154 struct gm_group
*group
;
157 memset(&sg
, 0, sizeof(sg
));
158 sg
.src
= source
->source_addr
;
159 sg
.grp
= source
->source_group
->group_addr
;
161 if (PIM_DEBUG_IGMP_TRACE
) {
162 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__
, &sg
,
163 source
->source_group
->interface
->name
,
164 IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
));
167 /* Prevent IGMP interface from installing multicast route multiple
169 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
173 group
= source
->source_group
;
175 if (tib_sg_gm_join(pim
, sg
, group
->interface
,
176 &source
->source_channel_oil
))
177 IGMP_SOURCE_DO_FORWARDING(source
->source_flags
);
181 igmp_source_forward_stop: stop fowarding, but keep the source
182 igmp_source_delete: stop fowarding, and delete the source
184 void igmp_source_forward_stop(struct gm_source
*source
)
186 struct pim_interface
*pim_oif
;
187 struct gm_group
*group
;
190 memset(&sg
, 0, sizeof(sg
));
191 sg
.src
= source
->source_addr
;
192 sg
.grp
= source
->source_group
->group_addr
;
194 if (PIM_DEBUG_IGMP_TRACE
) {
195 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__
, &sg
,
196 source
->source_group
->interface
->name
,
197 IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
));
200 /* Prevent IGMP interface from removing multicast route multiple
202 if (!IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
206 group
= source
->source_group
;
207 pim_oif
= group
->interface
->info
;
209 tib_sg_gm_prune(pim_oif
->pim
, sg
, group
->interface
,
210 &source
->source_channel_oil
);
211 IGMP_SOURCE_DONT_FORWARDING(source
->source_flags
);
214 /* This socket is used for TXing IGMP packets only, IGMP RX happens
215 * in pim_mroute_msg()
217 static int igmp_sock_open(struct in_addr ifaddr
, struct interface
*ifp
,
218 uint32_t pim_options
)
222 struct in_addr group
;
223 struct pim_interface
*pim_ifp
= ifp
->info
;
225 fd
= pim_socket_mcast(IPPROTO_IGMP
, ifaddr
, ifp
, 1);
230 if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options
)) {
231 if (inet_aton(PIM_ALL_ROUTERS
, &group
)) {
232 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
,
237 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
238 __FILE__
, __func__
, fd
, &ifaddr
,
239 PIM_ALL_ROUTERS
, errno
, safe_strerror(errno
));
244 IGMP routers periodically send IGMP general queries to
246 IGMP routers must receive general queries for querier election.
248 if (inet_aton(PIM_ALL_SYSTEMS
, &group
)) {
249 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
, pim_ifp
))
253 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
254 __FILE__
, __func__
, fd
, &ifaddr
,
255 PIM_ALL_SYSTEMS
, errno
, safe_strerror(errno
));
258 if (inet_aton(PIM_ALL_IGMP_ROUTERS
, &group
)) {
259 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
,
265 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
266 __FILE__
, __func__
, fd
, &ifaddr
,
267 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
273 "IGMP socket fd=%d could not join any group on interface address %pI4",
282 #undef IGMP_SOCK_DUMP
284 #ifdef IGMP_SOCK_DUMP
285 static void igmp_sock_dump(array_t
*igmp_sock_array
)
287 int size
= array_size(igmp_sock_array
);
288 for (int i
= 0; i
< size
; ++i
) {
290 struct gm_sock
*igmp
= array_get(igmp_sock_array
, i
);
292 zlog_debug("%s %s: [%d/%d] igmp_addr=%pI4 fd=%d", __FILE__
,
293 __func__
, i
, size
, &igmp
->ifaddr
,
299 struct gm_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
300 struct in_addr ifaddr
)
302 struct listnode
*sock_node
;
303 struct gm_sock
*igmp
;
305 #ifdef IGMP_SOCK_DUMP
306 igmp_sock_dump(igmp_sock_list
);
309 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
310 if (ifaddr
.s_addr
== igmp
->ifaddr
.s_addr
)
316 static void pim_igmp_other_querier_expire(struct thread
*t
)
318 struct gm_sock
*igmp
;
320 igmp
= THREAD_ARG(t
);
322 assert(!igmp
->t_igmp_query_timer
);
324 if (PIM_DEBUG_IGMP_TRACE
) {
325 char ifaddr_str
[INET_ADDRSTRLEN
];
326 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
328 zlog_debug("%s: Querier %s resuming", __func__
, ifaddr_str
);
330 /* Mark the interface address as querier address */
331 igmp
->querier_addr
= igmp
->ifaddr
;
334 We are the current querier, then
335 re-start sending general queries.
336 RFC 2236 - sec 7 Other Querier
337 present timer expired (Send General
338 Query, Set Gen. Query. timer)
340 pim_igmp_general_query(t
);
343 void pim_igmp_other_querier_timer_on(struct gm_sock
*igmp
)
345 long other_querier_present_interval_msec
;
346 struct pim_interface
*pim_ifp
;
349 assert(igmp
->interface
);
350 assert(igmp
->interface
->info
);
352 pim_ifp
= igmp
->interface
->info
;
354 if (igmp
->t_other_querier_timer
) {
356 There is other querier present already,
357 then reset the other-querier-present timer.
360 if (PIM_DEBUG_IGMP_TRACE
) {
361 char ifaddr_str
[INET_ADDRSTRLEN
];
362 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
365 "Querier %s resetting TIMER event for Other-Querier-Present",
368 THREAD_OFF(igmp
->t_other_querier_timer
);
371 We are the current querier, then stop sending general queries:
372 igmp->t_igmp_query_timer = NULL;
374 pim_igmp_general_query_off(igmp
);
378 Since this socket is starting the other-querier-present timer,
379 there should not be periodic query timer for this socket.
381 assert(!igmp
->t_igmp_query_timer
);
384 RFC 3376: 8.5. Other Querier Present Interval
386 The Other Querier Present Interval is the length of time that must
387 pass before a multicast router decides that there is no longer
388 another multicast router which should be the querier. This value
389 MUST be ((the Robustness Variable) times (the Query Interval)) plus
390 (one half of one Query Response Interval).
392 other_querier_present_interval_msec = \
393 igmp->querier_robustness_variable * \
394 1000 * igmp->querier_query_interval + \
395 100 * (pim_ifp->query_max_response_time_dsec >> 1);
397 other_querier_present_interval_msec
= PIM_IGMP_OQPI_MSEC(
398 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
399 pim_ifp
->gm_query_max_response_time_dsec
);
401 if (PIM_DEBUG_IGMP_TRACE
) {
402 char ifaddr_str
[INET_ADDRSTRLEN
];
403 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
406 "Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
407 ifaddr_str
, other_querier_present_interval_msec
/ 1000,
408 other_querier_present_interval_msec
% 1000);
411 thread_add_timer_msec(router
->master
, pim_igmp_other_querier_expire
,
412 igmp
, other_querier_present_interval_msec
,
413 &igmp
->t_other_querier_timer
);
416 void pim_igmp_other_querier_timer_off(struct gm_sock
*igmp
)
420 if (PIM_DEBUG_IGMP_TRACE
) {
421 if (igmp
->t_other_querier_timer
) {
422 char ifaddr_str
[INET_ADDRSTRLEN
];
423 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
426 "IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
427 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
430 THREAD_OFF(igmp
->t_other_querier_timer
);
433 int igmp_validate_checksum(char *igmp_msg
, int igmp_msg_len
)
435 uint16_t recv_checksum
;
438 IGMP_GET_INT16((unsigned char *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
),
441 /* Clear the checksum field */
442 memset(igmp_msg
+ IGMP_CHECKSUM_OFFSET
, 0, 2);
444 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
445 if (ntohs(checksum
) != recv_checksum
) {
446 zlog_warn("Invalid checksum received %x, calculated %x",
447 recv_checksum
, ntohs(checksum
));
454 static int igmp_recv_query(struct gm_sock
*igmp
, int query_version
,
455 int max_resp_code
, struct in_addr from
,
456 const char *from_str
, char *igmp_msg
,
459 struct interface
*ifp
;
460 struct pim_interface
*pim_ifp
;
461 struct in_addr group_addr
;
463 if (igmp
->mtrace_only
)
466 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
468 ifp
= igmp
->interface
;
471 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
473 "Recv IGMP query v%d from %s on %s with invalid checksum",
474 query_version
, from_str
, ifp
->name
);
478 if (!pim_if_connected_to_source(ifp
, from
)) {
479 if (PIM_DEBUG_IGMP_PACKETS
)
480 zlog_debug("Recv IGMP query on interface: %s from a non-connected source: %s",
481 ifp
->name
, from_str
);
485 if (if_address_is_local(&from
, AF_INET
, ifp
->vrf
->vrf_id
)) {
486 if (PIM_DEBUG_IGMP_PACKETS
)
487 zlog_debug("Recv IGMP query on interface: %s from ourself %s",
488 ifp
->name
, from_str
);
492 /* Collecting IGMP Rx stats */
493 switch (query_version
) {
495 igmp
->igmp_stats
.query_v1
++;
498 igmp
->igmp_stats
.query_v2
++;
501 igmp
->igmp_stats
.query_v3
++;
504 igmp
->igmp_stats
.unsupported
++;
508 * RFC 3376 defines some guidelines on operating in backwards
509 * compatibility with older versions of IGMP but there are some gaps in
512 * - once we drop from say version 3 to version 2 we will never go back
513 * to version 3 even if the node that TXed an IGMP v2 query upgrades
516 * - The node with the lowest IP is the querier so we will only know to
517 * drop from v3 to v2 if the node that is the querier is also the one
518 * that is running igmp v2. If a non-querier only supports igmp v2
519 * we will have no way of knowing.
521 * For now we will simplify things and inform the user that they need to
522 * configure all PIM routers to use the same version of IGMP.
524 if (query_version
!= pim_ifp
->igmp_version
) {
526 "Recv IGMP query v%d from %s on %s but we are using v%d, please configure all PIM routers on this subnet to use the same IGMP version",
527 query_version
, from_str
, ifp
->name
,
528 pim_ifp
->igmp_version
);
532 if (PIM_DEBUG_IGMP_PACKETS
) {
533 char group_str
[INET_ADDRSTRLEN
];
534 pim_inet4_dump("<group?>", group_addr
, group_str
,
536 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
537 query_version
, from_str
, ifp
->name
, group_str
);
541 RFC 3376: 6.6.2. Querier Election
543 When a router receives a query with a lower IP address, it sets
544 the Other-Querier-Present timer to Other Querier Present Interval
545 and ceases to send queries on the network if it was the previously
548 if (ntohl(from
.s_addr
) < ntohl(igmp
->ifaddr
.s_addr
)) {
550 /* As per RFC 2236 section 3:
551 * When a Querier receives a Leave Group message for a group
552 * that has group members on the reception interface, it sends
553 * [Last Member Query Count] Group-Specific Queries every [Last
554 * Member Query Interval] to the group being left. These
555 * Group-Specific Queries have their Max Response time set to
556 * [Last Member Query Interval]. If no Reports are received
557 * after the response time of the last query expires, the
558 * routers assume that the group has no local members, as above.
559 * Any Querier to non-Querier transition is ignored during this
560 * time; the same router keeps sending the Group-Specific
563 struct gm_group
*group
;
565 group
= find_group_by_addr(igmp
, group_addr
);
566 if (group
&& group
->t_group_query_retransmit_timer
) {
567 if (PIM_DEBUG_IGMP_TRACE
)
569 "%s: lower address query packet from %s is ignored when last member query interval timer is running",
570 ifp
->name
, from_str
);
574 if (PIM_DEBUG_IGMP_TRACE
) {
575 char ifaddr_str
[INET_ADDRSTRLEN
];
576 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
579 "%s: local address %s (%u) lost querier election to %s (%u)",
580 ifp
->name
, ifaddr_str
,
581 ntohl(igmp
->ifaddr
.s_addr
), from_str
,
584 if (ntohl(from
.s_addr
) < ntohl(igmp
->querier_addr
.s_addr
))
585 igmp
->querier_addr
.s_addr
= from
.s_addr
;
587 pim_igmp_other_querier_timer_on(igmp
);
590 /* IGMP version 3 is the only one where we process the RXed query */
591 if (query_version
== 3) {
592 igmp_v3_recv_query(igmp
, from_str
, igmp_msg
);
598 static void on_trace(const char *label
, struct interface
*ifp
,
601 if (PIM_DEBUG_IGMP_TRACE
) {
602 char from_str
[INET_ADDRSTRLEN
];
603 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
604 zlog_debug("%s: from %s on %s", label
, from_str
, ifp
->name
);
608 static int igmp_v1_recv_report(struct gm_sock
*igmp
, struct in_addr from
,
609 const char *from_str
, char *igmp_msg
,
612 struct interface
*ifp
= igmp
->interface
;
613 struct gm_group
*group
;
614 struct in_addr group_addr
;
616 on_trace(__func__
, igmp
->interface
, from
);
618 if (igmp
->mtrace_only
)
621 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
623 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
624 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
628 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
630 "Recv IGMP report v1 from %s on %s with invalid checksum",
631 from_str
, ifp
->name
);
635 /* Collecting IGMP Rx stats */
636 igmp
->igmp_stats
.report_v1
++;
638 if (PIM_DEBUG_IGMP_TRACE
) {
639 zlog_warn("%s %s: FIXME WRITEME", __FILE__
, __func__
);
642 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
644 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
647 /* non-existant group is created as INCLUDE {empty} */
648 group
= igmp_add_group_by_addr(igmp
, group_addr
);
653 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
658 bool pim_igmp_verify_header(struct ip
*ip_hdr
, size_t len
, size_t *hlen
)
663 size_t ip_hlen
; /* ip header length in bytes */
665 if (len
< sizeof(*ip_hdr
)) {
666 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len
,
671 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
676 "IGMP packet header claims size %zu, but we only have %zu bytes",
681 igmp_msg
= (char *)ip_hdr
+ ip_hlen
;
682 igmp_msg_len
= len
- ip_hlen
;
683 msg_type
= *igmp_msg
;
685 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
686 zlog_warn("IGMP message size=%d shorter than minimum=%d",
687 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
691 if ((msg_type
!= PIM_IGMP_MTRACE_RESPONSE
)
692 && (msg_type
!= PIM_IGMP_MTRACE_QUERY_REQUEST
)) {
693 if (ip_hdr
->ip_ttl
!= 1) {
695 "Recv IGMP packet with invalid ttl=%u, discarding the packet",
704 int pim_igmp_packet(struct gm_sock
*igmp
, char *buf
, size_t len
)
706 struct ip
*ip_hdr
= (struct ip
*)buf
;
707 size_t ip_hlen
; /* ip header length in bytes */
711 char from_str
[INET_ADDRSTRLEN
];
712 char to_str
[INET_ADDRSTRLEN
];
714 if (!pim_igmp_verify_header(ip_hdr
, len
, &ip_hlen
))
717 igmp_msg
= buf
+ ip_hlen
;
718 igmp_msg_len
= len
- ip_hlen
;
719 msg_type
= *igmp_msg
;
721 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
722 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
724 if (PIM_DEBUG_IGMP_PACKETS
) {
726 "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
727 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hdr
->ip_ttl
,
728 msg_type
, igmp_msg_len
);
732 case PIM_IGMP_MEMBERSHIP_QUERY
: {
733 int max_resp_code
= igmp_msg
[1];
737 RFC 3376: 7.1. Query Version Distinctions
738 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
740 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
742 IGMPv3 Query: length >= 12 octets
745 if (igmp_msg_len
== 8) {
746 query_version
= max_resp_code
? 2 : 1;
747 } else if (igmp_msg_len
>= 12) {
750 zlog_warn("Unknown IGMP query version");
754 return igmp_recv_query(igmp
, query_version
, max_resp_code
,
755 ip_hdr
->ip_src
, from_str
, igmp_msg
,
759 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
760 return igmp_v3_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
761 igmp_msg
, igmp_msg_len
);
763 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
764 return igmp_v2_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
765 igmp_msg
, igmp_msg_len
);
767 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
768 return igmp_v1_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
769 igmp_msg
, igmp_msg_len
);
771 case PIM_IGMP_V2_LEAVE_GROUP
:
772 return igmp_v2_recv_leave(igmp
, ip_hdr
, from_str
, igmp_msg
,
775 case PIM_IGMP_MTRACE_RESPONSE
:
776 return igmp_mtrace_recv_response(igmp
, ip_hdr
, ip_hdr
->ip_src
,
779 case PIM_IGMP_MTRACE_QUERY_REQUEST
:
780 return igmp_mtrace_recv_qry_req(igmp
, ip_hdr
, ip_hdr
->ip_src
,
785 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
787 /* Collecting IGMP Rx stats */
788 igmp
->igmp_stats
.unsupported
++;
793 void pim_igmp_general_query_on(struct gm_sock
*igmp
)
795 struct pim_interface
*pim_ifp
;
800 Since this socket is starting as querier,
801 there should not exist a timer for other-querier-present.
803 assert(!igmp
->t_other_querier_timer
);
804 pim_ifp
= igmp
->interface
->info
;
808 RFC 3376: 8.6. Startup Query Interval
810 The Startup Query Interval is the interval between General Queries
811 sent by a Querier on startup. Default: 1/4 the Query Interval.
812 The first one should be sent out immediately instead of 125/4
815 startup_mode
= igmp
->startup_query_count
> 0;
818 * If this is the first time we are sending a query on a
819 * newly configured igmp interface send it out in 1 second
820 * just to give the entire world a tiny bit of time to settle
821 * else the query interval is:
822 * query_interval = pim_ifp->gm_default_query_interval >> 2;
824 if (igmp
->startup_query_count
==
825 igmp
->querier_robustness_variable
)
828 query_interval
= PIM_IGMP_SQI(
829 pim_ifp
->gm_default_query_interval
);
831 --igmp
->startup_query_count
;
833 query_interval
= igmp
->querier_query_interval
;
836 if (PIM_DEBUG_IGMP_TRACE
) {
837 char ifaddr_str
[INET_ADDRSTRLEN
];
838 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
841 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
842 ifaddr_str
, query_interval
,
843 startup_mode
? "startup" : "non-startup", igmp
->fd
);
845 thread_add_timer(router
->master
, pim_igmp_general_query
, igmp
,
846 query_interval
, &igmp
->t_igmp_query_timer
);
849 void pim_igmp_general_query_off(struct gm_sock
*igmp
)
853 if (PIM_DEBUG_IGMP_TRACE
) {
854 if (igmp
->t_igmp_query_timer
) {
855 char ifaddr_str
[INET_ADDRSTRLEN
];
856 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
859 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
860 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
863 THREAD_OFF(igmp
->t_igmp_query_timer
);
866 /* Issue IGMP general query */
867 static void pim_igmp_general_query(struct thread
*t
)
869 struct gm_sock
*igmp
;
870 struct in_addr dst_addr
;
871 struct in_addr group_addr
;
872 struct pim_interface
*pim_ifp
;
875 igmp
= THREAD_ARG(t
);
877 assert(igmp
->interface
);
878 assert(igmp
->interface
->info
);
880 pim_ifp
= igmp
->interface
->info
;
882 if (pim_ifp
->igmp_version
== 3) {
883 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
885 query_buf_size
= IGMP_V12_MSG_SIZE
;
888 char query_buf
[query_buf_size
];
891 RFC3376: 4.1.12. IP Destination Addresses for Queries
893 In IGMPv3, General Queries are sent with an IP destination address
894 of 224.0.0.1, the all-systems multicast address. Group-Specific
895 and Group-and-Source-Specific Queries are sent with an IP
896 destination address equal to the multicast address of interest.
899 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
900 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
902 if (PIM_DEBUG_IGMP_TRACE
) {
903 char querier_str
[INET_ADDRSTRLEN
];
904 char dst_str
[INET_ADDRSTRLEN
];
905 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
906 sizeof(querier_str
));
907 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
908 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
909 querier_str
, dst_str
, igmp
->interface
->name
);
912 igmp_send_query(pim_ifp
->igmp_version
, 0 /* igmp_group */, igmp
->fd
,
913 igmp
->interface
->name
, query_buf
, sizeof(query_buf
),
914 0 /* num_sources */, dst_addr
, group_addr
,
915 pim_ifp
->gm_query_max_response_time_dsec
,
916 1 /* s_flag: always set for general queries */,
917 igmp
->querier_robustness_variable
,
918 igmp
->querier_query_interval
);
920 pim_igmp_general_query_on(igmp
);
923 static void sock_close(struct gm_sock
*igmp
)
925 pim_igmp_other_querier_timer_off(igmp
);
926 pim_igmp_general_query_off(igmp
);
928 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
929 if (igmp
->t_igmp_read
) {
931 "Cancelling READ event on IGMP socket %pI4 fd=%d on interface %s",
932 &igmp
->ifaddr
, igmp
->fd
,
933 igmp
->interface
->name
);
936 THREAD_OFF(igmp
->t_igmp_read
);
938 if (close(igmp
->fd
)) {
941 "Failure closing IGMP socket %pI4 fd=%d on interface %s: errno=%d: %s",
942 &igmp
->ifaddr
, igmp
->fd
,
943 igmp
->interface
->name
, errno
, safe_strerror(errno
));
946 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
947 zlog_debug("Deleted IGMP socket %pI4 fd=%d on interface %s",
948 &igmp
->ifaddr
, igmp
->fd
,
949 igmp
->interface
->name
);
953 void igmp_startup_mode_on(struct gm_sock
*igmp
)
955 struct pim_interface
*pim_ifp
;
957 pim_ifp
= igmp
->interface
->info
;
960 RFC 3376: 8.7. Startup Query Count
962 The Startup Query Count is the number of Queries sent out on
963 startup, separated by the Startup Query Interval. Default: the
966 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
969 Since we're (re)starting, reset QQI to default Query Interval
971 igmp
->querier_query_interval
= pim_ifp
->gm_default_query_interval
;
974 static void igmp_group_free(struct gm_group
*group
)
976 list_delete(&group
->group_source_list
);
978 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
981 static void igmp_group_count_incr(struct pim_interface
*pim_ifp
)
983 ++pim_ifp
->pim
->igmp_group_count
;
984 if (pim_ifp
->pim
->igmp_group_count
985 == pim_ifp
->pim
->igmp_watermark_limit
) {
987 "IGMP group count reached watermark limit: %u(vrf: %s)",
988 pim_ifp
->pim
->igmp_group_count
,
989 VRF_LOGNAME(pim_ifp
->pim
->vrf
));
993 static void igmp_group_count_decr(struct pim_interface
*pim_ifp
)
995 if (pim_ifp
->pim
->igmp_group_count
== 0) {
996 zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
997 VRF_LOGNAME(pim_ifp
->pim
->vrf
));
1001 --pim_ifp
->pim
->igmp_group_count
;
1004 void igmp_group_delete(struct gm_group
*group
)
1006 struct listnode
*src_node
;
1007 struct listnode
*src_nextnode
;
1008 struct gm_source
*src
;
1009 struct pim_interface
*pim_ifp
= group
->interface
->info
;
1011 if (PIM_DEBUG_IGMP_TRACE
) {
1012 char group_str
[INET_ADDRSTRLEN
];
1013 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1015 zlog_debug("Deleting IGMP group %s from interface %s",
1016 group_str
, group
->interface
->name
);
1019 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
,
1021 igmp_source_delete(src
);
1024 THREAD_OFF(group
->t_group_query_retransmit_timer
);
1026 group_timer_off(group
);
1027 igmp_group_count_decr(pim_ifp
);
1028 listnode_delete(pim_ifp
->gm_group_list
, group
);
1029 hash_release(pim_ifp
->gm_group_hash
, group
);
1031 igmp_group_free(group
);
1034 void igmp_group_delete_empty_include(struct gm_group
*group
)
1036 assert(!group
->group_filtermode_isexcl
);
1037 assert(!listcount(group
->group_source_list
));
1039 igmp_group_delete(group
);
1042 void igmp_sock_free(struct gm_sock
*igmp
)
1044 assert(!igmp
->t_igmp_read
);
1045 assert(!igmp
->t_igmp_query_timer
);
1046 assert(!igmp
->t_other_querier_timer
);
1048 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
1051 void igmp_sock_delete(struct gm_sock
*igmp
)
1053 struct pim_interface
*pim_ifp
;
1057 pim_ifp
= igmp
->interface
->info
;
1059 listnode_delete(pim_ifp
->gm_socket_list
, igmp
);
1061 igmp_sock_free(igmp
);
1063 if (!listcount(pim_ifp
->gm_socket_list
))
1064 pim_igmp_if_reset(pim_ifp
);
1067 void igmp_sock_delete_all(struct interface
*ifp
)
1069 struct pim_interface
*pim_ifp
;
1070 struct listnode
*igmp_node
, *igmp_nextnode
;
1071 struct gm_sock
*igmp
;
1073 pim_ifp
= ifp
->info
;
1075 for (ALL_LIST_ELEMENTS(pim_ifp
->gm_socket_list
, igmp_node
,
1076 igmp_nextnode
, igmp
)) {
1077 igmp_sock_delete(igmp
);
1081 static unsigned int igmp_group_hash_key(const void *arg
)
1083 const struct gm_group
*group
= arg
;
1085 return jhash_1word(group
->group_addr
.s_addr
, 0);
1088 static bool igmp_group_hash_equal(const void *arg1
, const void *arg2
)
1090 const struct gm_group
*g1
= (const struct gm_group
*)arg1
;
1091 const struct gm_group
*g2
= (const struct gm_group
*)arg2
;
1093 if (g1
->group_addr
.s_addr
== g2
->group_addr
.s_addr
)
1099 void pim_igmp_if_init(struct pim_interface
*pim_ifp
, struct interface
*ifp
)
1103 pim_ifp
->gm_socket_list
= list_new();
1104 pim_ifp
->gm_socket_list
->del
= (void (*)(void *))igmp_sock_free
;
1106 pim_ifp
->gm_group_list
= list_new();
1107 pim_ifp
->gm_group_list
->del
= (void (*)(void *))igmp_group_free
;
1109 snprintf(hash_name
, sizeof(hash_name
), "IGMP %s hash", ifp
->name
);
1110 pim_ifp
->gm_group_hash
= hash_create(igmp_group_hash_key
,
1111 igmp_group_hash_equal
, hash_name
);
1114 void pim_igmp_if_reset(struct pim_interface
*pim_ifp
)
1116 struct listnode
*grp_node
, *grp_nextnode
;
1117 struct gm_group
*grp
;
1119 for (ALL_LIST_ELEMENTS(pim_ifp
->gm_group_list
, grp_node
, grp_nextnode
,
1121 igmp_group_delete(grp
);
1125 void pim_igmp_if_fini(struct pim_interface
*pim_ifp
)
1127 pim_igmp_if_reset(pim_ifp
);
1129 assert(pim_ifp
->gm_group_list
);
1130 assert(!listcount(pim_ifp
->gm_group_list
));
1132 list_delete(&pim_ifp
->gm_group_list
);
1133 hash_free(pim_ifp
->gm_group_hash
);
1135 list_delete(&pim_ifp
->gm_socket_list
);
1138 static struct gm_sock
*igmp_sock_new(int fd
, struct in_addr ifaddr
,
1139 struct interface
*ifp
, int mtrace_only
)
1141 struct pim_interface
*pim_ifp
;
1142 struct gm_sock
*igmp
;
1144 pim_ifp
= ifp
->info
;
1146 if (PIM_DEBUG_IGMP_TRACE
) {
1148 "Creating IGMP socket fd=%d for address %pI4 on interface %s",
1149 fd
, &ifaddr
, ifp
->name
);
1152 igmp
= XCALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
1155 igmp
->interface
= ifp
;
1156 igmp
->ifaddr
= ifaddr
;
1157 igmp
->querier_addr
= ifaddr
;
1158 igmp
->t_igmp_read
= NULL
;
1159 igmp
->t_igmp_query_timer
= NULL
;
1160 igmp
->t_other_querier_timer
= NULL
; /* no other querier present */
1161 igmp
->querier_robustness_variable
=
1162 pim_ifp
->gm_default_robustness_variable
;
1163 igmp
->sock_creation
= pim_time_monotonic_sec();
1165 igmp_stats_init(&igmp
->igmp_stats
);
1168 igmp
->mtrace_only
= mtrace_only
;
1172 igmp
->mtrace_only
= false;
1175 igmp_startup_mode_on() will reset QQI:
1177 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
1179 igmp_startup_mode_on(igmp
);
1180 pim_igmp_general_query_on(igmp
);
1185 static void igmp_read_on(struct gm_sock
*igmp
);
1187 static void pim_igmp_read(struct thread
*t
)
1190 struct gm_sock
*igmp
= (struct gm_sock
*)THREAD_ARG(t
);
1191 struct sockaddr_storage from
;
1192 struct sockaddr_storage to
;
1193 socklen_t fromlen
= sizeof(from
);
1194 socklen_t tolen
= sizeof(to
);
1195 ifindex_t ifindex
= -1;
1199 len
= pim_socket_recvfromto(igmp
->fd
, buf
, sizeof(buf
), &from
,
1200 &fromlen
, &to
, &tolen
, &ifindex
);
1204 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
1215 static void igmp_read_on(struct gm_sock
*igmp
)
1218 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
1219 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
1222 thread_add_read(router
->master
, pim_igmp_read
, igmp
, igmp
->fd
,
1223 &igmp
->t_igmp_read
);
1226 struct gm_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
1227 struct in_addr ifaddr
, struct interface
*ifp
,
1230 struct pim_interface
*pim_ifp
;
1231 struct gm_sock
*igmp
;
1232 struct sockaddr_in sin
;
1235 pim_ifp
= ifp
->info
;
1237 fd
= igmp_sock_open(ifaddr
, ifp
, pim_ifp
->options
);
1239 zlog_warn("Could not open IGMP socket for %pI4 on %s",
1240 &ifaddr
, ifp
->name
);
1244 sin
.sin_family
= AF_INET
;
1245 sin
.sin_addr
= ifaddr
;
1247 if (bind(fd
, (struct sockaddr
*) &sin
, sizeof(sin
)) != 0) {
1248 zlog_warn("Could not bind IGMP socket for %pI4 on %s: %s(%d)",
1249 &ifaddr
, ifp
->name
, strerror(errno
), errno
);
1255 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
, mtrace_only
);
1259 listnode_add(igmp_sock_list
, igmp
);
1261 #ifdef IGMP_SOCK_DUMP
1262 igmp_sock_dump(igmp_sock_array
);
1269 RFC 3376: 6.5. Switching Router Filter-Modes
1271 When a router's filter-mode for a group is EXCLUDE and the group
1272 timer expires, the router filter-mode for the group transitions to
1275 A router uses source records with running source timers as its state
1276 for the switch to a filter-mode of INCLUDE. If there are any source
1277 records with source timers greater than zero (i.e., requested to be
1278 forwarded), a router switches to filter-mode of INCLUDE using those
1279 source records. Source records whose timers are zero (from the
1280 previous EXCLUDE mode) are deleted.
1282 static void igmp_group_timer(struct thread
*t
)
1284 struct gm_group
*group
;
1286 group
= THREAD_ARG(t
);
1288 if (PIM_DEBUG_IGMP_TRACE
) {
1289 char group_str
[INET_ADDRSTRLEN
];
1290 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1292 zlog_debug("%s: Timer for group %s on interface %s", __func__
,
1293 group_str
, group
->interface
->name
);
1296 assert(group
->group_filtermode_isexcl
);
1298 group
->group_filtermode_isexcl
= 0;
1300 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1301 igmp_anysource_forward_stop(group
);
1303 igmp_source_delete_expired(group
->group_source_list
);
1305 assert(!group
->group_filtermode_isexcl
);
1308 RFC 3376: 6.2.2. Definition of Group Timers
1310 If there are no more source records for the group, delete group
1313 if (listcount(group
->group_source_list
) < 1) {
1314 igmp_group_delete_empty_include(group
);
1318 static void group_timer_off(struct gm_group
*group
)
1320 if (!group
->t_group_timer
)
1323 if (PIM_DEBUG_IGMP_TRACE
) {
1324 char group_str
[INET_ADDRSTRLEN
];
1325 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1327 zlog_debug("Cancelling TIMER event for group %s on %s",
1328 group_str
, group
->interface
->name
);
1330 THREAD_OFF(group
->t_group_timer
);
1333 void igmp_group_timer_on(struct gm_group
*group
, long interval_msec
,
1336 group_timer_off(group
);
1338 if (PIM_DEBUG_IGMP_EVENTS
) {
1339 char group_str
[INET_ADDRSTRLEN
];
1340 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1343 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1344 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
1349 RFC 3376: 6.2.2. Definition of Group Timers
1351 The group timer is only used when a group is in EXCLUDE mode and
1352 it represents the time for the *filter-mode* of the group to
1353 expire and switch to INCLUDE mode.
1355 assert(group
->group_filtermode_isexcl
);
1357 thread_add_timer_msec(router
->master
, igmp_group_timer
, group
,
1358 interval_msec
, &group
->t_group_timer
);
1361 struct gm_group
*find_group_by_addr(struct gm_sock
*igmp
,
1362 struct in_addr group_addr
)
1364 struct gm_group lookup
;
1365 struct pim_interface
*pim_ifp
= igmp
->interface
->info
;
1367 lookup
.group_addr
.s_addr
= group_addr
.s_addr
;
1369 return hash_lookup(pim_ifp
->gm_group_hash
, &lookup
);
1372 struct gm_group
*igmp_add_group_by_addr(struct gm_sock
*igmp
,
1373 struct in_addr group_addr
)
1375 struct gm_group
*group
;
1376 struct pim_interface
*pim_ifp
= igmp
->interface
->info
;
1378 group
= find_group_by_addr(igmp
, group_addr
);
1383 if (!pim_is_group_224_4(group_addr
)) {
1384 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1389 if (pim_is_group_224_0_0_0_24(group_addr
)) {
1390 if (PIM_DEBUG_IGMP_TRACE
)
1392 "%s: Group specified %pI4 is part of 224.0.0.0/24",
1393 __func__
, &group_addr
);
1397 Non-existant group is created as INCLUDE {empty}:
1399 RFC 3376 - 5.1. Action on Change of Interface State
1401 If no interface state existed for that multicast address before
1402 the change (i.e., the change consisted of creating a new
1403 per-interface record), or if no state exists after the change
1404 (i.e., the change consisted of deleting a per-interface record),
1405 then the "non-existent" state is considered to have a filter mode
1406 of INCLUDE and an empty source list.
1409 group
= XCALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1411 group
->group_source_list
= list_new();
1412 group
->group_source_list
->del
= (void (*)(void *))igmp_source_free
;
1414 group
->t_group_timer
= NULL
;
1415 group
->t_group_query_retransmit_timer
= NULL
;
1416 group
->group_specific_query_retransmit_count
= 0;
1417 group
->group_addr
= group_addr
;
1418 group
->interface
= igmp
->interface
;
1419 group
->last_igmp_v1_report_dsec
= -1;
1420 group
->last_igmp_v2_report_dsec
= -1;
1421 group
->group_creation
= pim_time_monotonic_sec();
1422 group
->igmp_version
= IGMP_DEFAULT_VERSION
;
1424 /* initialize new group as INCLUDE {empty} */
1425 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1427 listnode_add(pim_ifp
->gm_group_list
, group
);
1428 group
= hash_get(pim_ifp
->gm_group_hash
, group
, hash_alloc_intern
);
1430 if (PIM_DEBUG_IGMP_TRACE
) {
1431 char group_str
[INET_ADDRSTRLEN
];
1432 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1435 "Creating new IGMP group %s on socket %d interface %s",
1436 group_str
, igmp
->fd
, igmp
->interface
->name
);
1439 igmp_group_count_incr(pim_ifp
);
1442 RFC 3376: 6.2.2. Definition of Group Timers
1444 The group timer is only used when a group is in EXCLUDE mode and
1445 it represents the time for the *filter-mode* of the group to
1446 expire and switch to INCLUDE mode.
1448 assert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1449 assert(!group
->t_group_timer
); /* group timer == 0 */
1451 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1452 igmp_anysource_forward_stop(group
);
1457 void igmp_send_query(int igmp_version
, struct gm_group
*group
, int fd
,
1458 const char *ifname
, char *query_buf
, int query_buf_size
,
1459 int num_sources
, struct in_addr dst_addr
,
1460 struct in_addr group_addr
,
1461 int query_max_response_time_dsec
, uint8_t s_flag
,
1462 uint8_t querier_robustness_variable
,
1463 uint16_t querier_query_interval
)
1465 if (igmp_version
== 3) {
1466 igmp_v3_send_query(group
, fd
, ifname
, query_buf
, query_buf_size
,
1467 num_sources
, dst_addr
, group_addr
,
1468 query_max_response_time_dsec
, s_flag
,
1469 querier_robustness_variable
,
1470 querier_query_interval
);
1471 } else if (igmp_version
== 2) {
1472 igmp_v2_send_query(group
, fd
, ifname
, query_buf
, dst_addr
,
1473 group_addr
, query_max_response_time_dsec
);
1477 void igmp_send_query_on_intf(struct interface
*ifp
, int igmp_ver
)
1479 struct pim_interface
*pim_ifp
= ifp
->info
;
1480 struct listnode
*sock_node
= NULL
;
1481 struct gm_sock
*igmp
= NULL
;
1482 struct in_addr dst_addr
;
1483 struct in_addr group_addr
;
1490 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1492 query_buf_size
= IGMP_V12_MSG_SIZE
;
1494 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
1495 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
1497 if (PIM_DEBUG_IGMP_TRACE
)
1498 zlog_debug("Issuing general query on request on %s", ifp
->name
);
1500 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_socket_list
, sock_node
, igmp
)) {
1502 char query_buf
[query_buf_size
];
1504 igmp_send_query(igmp_ver
, 0 /* igmp_group */, igmp
->fd
,
1505 igmp
->interface
->name
, query_buf
,
1506 sizeof(query_buf
), 0 /* num_sources */,
1507 dst_addr
, group_addr
,
1508 pim_ifp
->gm_query_max_response_time_dsec
,
1509 1 /* s_flag: always set for general queries */,
1510 igmp
->querier_robustness_variable
,
1511 igmp
->querier_query_interval
);