1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2008 Everton da Silva Marques
14 #include "lib_errors.h"
17 #include "pim_instance.h"
19 #include "pim_igmpv2.h"
20 #include "pim_igmpv3.h"
21 #include "pim_igmp_mtrace.h"
22 #include "pim_iface.h"
24 #include "pim_mroute.h"
31 static void group_timer_off(struct gm_group
*group
);
32 static void pim_igmp_general_query(struct thread
*t
);
34 void igmp_anysource_forward_start(struct pim_instance
*pim
,
35 struct gm_group
*group
)
37 struct gm_source
*source
;
38 struct in_addr src_addr
= {.s_addr
= 0};
39 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
40 assert(group
->group_filtermode_isexcl
);
41 assert(listcount(group
->group_source_list
) < 1);
43 source
= igmp_get_source_by_addr(group
, src_addr
, NULL
);
45 zlog_warn("%s: Failure to create * source", __func__
);
49 igmp_source_forward_start(pim
, source
);
52 void igmp_anysource_forward_stop(struct gm_group
*group
)
54 struct gm_source
*source
;
55 struct in_addr star
= {.s_addr
= 0};
57 source
= igmp_find_source_by_addr(group
, star
);
59 igmp_source_forward_stop(source
);
62 static void igmp_source_forward_reevaluate_one(struct pim_instance
*pim
,
63 struct gm_source
*source
,
67 struct gm_group
*group
= source
->source_group
;
69 memset(&sg
, 0, sizeof(sg
));
70 sg
.src
= source
->source_addr
;
71 sg
.grp
= group
->group_addr
;
73 /** if there is no PIM state **/
74 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
75 if (pim_addr_is_any(source
->source_addr
)) {
77 if (PIM_DEBUG_PIM_EVENTS
)
79 "local membership del for %pSG as G is now SSM",
81 igmp_source_forward_stop(source
);
85 if (PIM_DEBUG_PIM_EVENTS
)
87 "local membership del for %pSG as G is now ASM",
89 igmp_source_forward_stop(source
);
93 if (!pim_addr_is_any(source
->source_addr
) && (is_grp_ssm
)) {
94 if (PIM_DEBUG_PIM_EVENTS
)
96 "local membership add for %pSG as G is now SSM",
98 igmp_source_forward_start(pim
, source
);
103 void igmp_source_forward_reevaluate_all(struct pim_instance
*pim
)
105 struct interface
*ifp
;
107 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
108 struct pim_interface
*pim_ifp
= ifp
->info
;
109 struct listnode
*grpnode
, *grp_nextnode
;
110 struct gm_group
*grp
;
111 struct pim_ifchannel
*ch
, *ch_temp
;
116 /* scan igmp groups */
117 for (ALL_LIST_ELEMENTS(pim_ifp
->gm_group_list
, grpnode
,
118 grp_nextnode
, grp
)) {
119 struct listnode
*srcnode
;
120 struct gm_source
*src
;
126 * EXCLUDE mode does not apply to SSM addresses,
127 * and an SSM-aware router will ignore
128 * MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE
129 * requests in the SSM range.
131 is_grp_ssm
= pim_is_grp_ssm(pim
, grp
->group_addr
);
132 if (is_grp_ssm
&& grp
->group_filtermode_isexcl
) {
133 igmp_group_delete(grp
);
135 /* scan group sources */
136 for (ALL_LIST_ELEMENTS_RO(
137 grp
->group_source_list
, srcnode
,
139 igmp_source_forward_reevaluate_one(
140 pim
, src
, is_grp_ssm
);
141 } /* scan group sources */
143 } /* scan igmp groups */
145 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
,
147 if (pim_is_grp_ssm(pim
, ch
->sg
.grp
)) {
148 if (pim_addr_is_any(ch
->sg
.src
))
149 pim_ifchannel_delete(ch
);
152 } /* scan interfaces */
155 void igmp_source_forward_start(struct pim_instance
*pim
,
156 struct gm_source
*source
)
158 struct gm_group
*group
;
161 memset(&sg
, 0, sizeof(sg
));
162 sg
.src
= source
->source_addr
;
163 sg
.grp
= source
->source_group
->group_addr
;
165 if (PIM_DEBUG_GM_TRACE
) {
166 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__
, &sg
,
167 source
->source_group
->interface
->name
,
168 IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
));
172 * PIM state should not be allowed for ASM group with valid source
175 if ((!pim_is_grp_ssm(pim
, source
->source_group
->group_addr
)) &&
176 !pim_addr_is_any(source
->source_addr
)) {
178 "%s: (S,G)=%pSG ASM range having source address, not allowed to create PIM state",
183 /* Prevent IGMP interface from installing multicast route multiple
185 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
189 group
= source
->source_group
;
191 if (tib_sg_gm_join(pim
, sg
, group
->interface
,
192 &source
->source_channel_oil
))
193 IGMP_SOURCE_DO_FORWARDING(source
->source_flags
);
197 igmp_source_forward_stop: stop forwarding, but keep the source
198 igmp_source_delete: stop forwarding, and delete the source
200 void igmp_source_forward_stop(struct gm_source
*source
)
202 struct pim_interface
*pim_oif
;
203 struct gm_group
*group
;
206 memset(&sg
, 0, sizeof(sg
));
207 sg
.src
= source
->source_addr
;
208 sg
.grp
= source
->source_group
->group_addr
;
210 if (PIM_DEBUG_GM_TRACE
) {
211 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__
, &sg
,
212 source
->source_group
->interface
->name
,
213 IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
));
216 /* Prevent IGMP interface from removing multicast route multiple
218 if (!IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
222 group
= source
->source_group
;
223 pim_oif
= group
->interface
->info
;
225 tib_sg_gm_prune(pim_oif
->pim
, sg
, group
->interface
,
226 &source
->source_channel_oil
);
227 IGMP_SOURCE_DONT_FORWARDING(source
->source_flags
);
230 /* This socket is used for TXing IGMP packets only, IGMP RX happens
231 * in pim_mroute_msg()
233 static int igmp_sock_open(struct in_addr ifaddr
, struct interface
*ifp
)
237 struct in_addr group
;
238 struct pim_interface
*pim_ifp
= ifp
->info
;
240 fd
= pim_socket_mcast(IPPROTO_IGMP
, ifaddr
, ifp
, 1);
245 if (inet_aton(PIM_ALL_ROUTERS
, &group
)) {
246 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
, pim_ifp
))
250 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
251 __FILE__
, __func__
, fd
, &ifaddr
, PIM_ALL_ROUTERS
, errno
,
252 safe_strerror(errno
));
256 IGMP routers periodically send IGMP general queries to
258 IGMP routers must receive general queries for querier election.
260 if (inet_aton(PIM_ALL_SYSTEMS
, &group
)) {
261 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
, pim_ifp
))
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_SYSTEMS
, errno
, safe_strerror(errno
));
270 if (inet_aton(PIM_ALL_IGMP_ROUTERS
, &group
)) {
271 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
,
277 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
278 __FILE__
, __func__
, fd
, &ifaddr
,
279 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
285 "IGMP socket fd=%d could not join any group on interface address %pI4",
294 #undef IGMP_SOCK_DUMP
296 #ifdef IGMP_SOCK_DUMP
297 static void igmp_sock_dump(array_t
*igmp_sock_array
)
299 int size
= array_size(igmp_sock_array
);
300 for (int i
= 0; i
< size
; ++i
) {
302 struct gm_sock
*igmp
= array_get(igmp_sock_array
, i
);
304 zlog_debug("%s %s: [%d/%d] igmp_addr=%pI4 fd=%d", __FILE__
,
305 __func__
, i
, size
, &igmp
->ifaddr
,
311 struct gm_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
312 struct in_addr ifaddr
)
314 struct listnode
*sock_node
;
315 struct gm_sock
*igmp
;
317 #ifdef IGMP_SOCK_DUMP
318 igmp_sock_dump(igmp_sock_list
);
321 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
322 if (ifaddr
.s_addr
== igmp
->ifaddr
.s_addr
)
328 static void pim_igmp_other_querier_expire(struct thread
*t
)
330 struct gm_sock
*igmp
;
332 igmp
= THREAD_ARG(t
);
334 assert(!igmp
->t_igmp_query_timer
);
336 if (PIM_DEBUG_GM_TRACE
) {
337 char ifaddr_str
[INET_ADDRSTRLEN
];
338 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
340 zlog_debug("%s: Querier %s resuming", __func__
, ifaddr_str
);
342 /* Mark the interface address as querier address */
343 igmp
->querier_addr
= igmp
->ifaddr
;
346 We are the current querier, then
347 re-start sending general queries.
348 RFC 2236 - sec 7 Other Querier
349 present timer expired (Send General
350 Query, Set Gen. Query. timer)
352 pim_igmp_general_query(t
);
355 void pim_igmp_other_querier_timer_on(struct gm_sock
*igmp
)
357 long other_querier_present_interval_msec
;
358 struct pim_interface
*pim_ifp
;
361 assert(igmp
->interface
);
362 assert(igmp
->interface
->info
);
364 pim_ifp
= igmp
->interface
->info
;
366 if (igmp
->t_other_querier_timer
) {
368 There is other querier present already,
369 then reset the other-querier-present timer.
372 if (PIM_DEBUG_GM_TRACE
) {
373 char ifaddr_str
[INET_ADDRSTRLEN
];
374 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
377 "Querier %s resetting TIMER event for Other-Querier-Present",
380 THREAD_OFF(igmp
->t_other_querier_timer
);
383 We are the current querier, then stop sending general queries:
384 igmp->t_igmp_query_timer = NULL;
386 pim_igmp_general_query_off(igmp
);
390 Since this socket is starting the other-querier-present timer,
391 there should not be periodic query timer for this socket.
393 assert(!igmp
->t_igmp_query_timer
);
396 RFC 3376: 8.5. Other Querier Present Interval
398 The Other Querier Present Interval is the length of time that must
399 pass before a multicast router decides that there is no longer
400 another multicast router which should be the querier. This value
401 MUST be ((the Robustness Variable) times (the Query Interval)) plus
402 (one half of one Query Response Interval).
404 other_querier_present_interval_msec = \
405 igmp->querier_robustness_variable * \
406 1000 * igmp->querier_query_interval + \
407 100 * (pim_ifp->query_max_response_time_dsec >> 1);
409 other_querier_present_interval_msec
= PIM_IGMP_OQPI_MSEC(
410 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
411 pim_ifp
->gm_query_max_response_time_dsec
);
413 if (PIM_DEBUG_GM_TRACE
) {
414 char ifaddr_str
[INET_ADDRSTRLEN
];
415 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
418 "Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
419 ifaddr_str
, other_querier_present_interval_msec
/ 1000,
420 other_querier_present_interval_msec
% 1000);
423 thread_add_timer_msec(router
->master
, pim_igmp_other_querier_expire
,
424 igmp
, other_querier_present_interval_msec
,
425 &igmp
->t_other_querier_timer
);
428 void pim_igmp_other_querier_timer_off(struct gm_sock
*igmp
)
432 if (PIM_DEBUG_GM_TRACE
) {
433 if (igmp
->t_other_querier_timer
) {
434 char ifaddr_str
[INET_ADDRSTRLEN
];
435 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
438 "IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
439 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
442 THREAD_OFF(igmp
->t_other_querier_timer
);
445 int igmp_validate_checksum(char *igmp_msg
, int igmp_msg_len
)
447 uint16_t recv_checksum
;
450 IGMP_GET_INT16((unsigned char *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
),
453 /* Clear the checksum field */
454 memset(igmp_msg
+ IGMP_CHECKSUM_OFFSET
, 0, 2);
456 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
457 if (ntohs(checksum
) != recv_checksum
) {
458 zlog_warn("Invalid checksum received %x, calculated %x",
459 recv_checksum
, ntohs(checksum
));
466 static int igmp_recv_query(struct gm_sock
*igmp
, int query_version
,
467 int max_resp_code
, struct in_addr from
,
468 const char *from_str
, char *igmp_msg
,
471 struct interface
*ifp
;
472 struct pim_interface
*pim_ifp
;
473 struct in_addr group_addr
;
475 if (igmp
->mtrace_only
)
478 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
480 ifp
= igmp
->interface
;
483 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
485 "Recv IGMP query v%d from %s on %s with invalid checksum",
486 query_version
, from_str
, ifp
->name
);
490 if (!pim_if_connected_to_source(ifp
, from
)) {
491 if (PIM_DEBUG_GM_PACKETS
)
492 zlog_debug("Recv IGMP query on interface: %s from a non-connected source: %s",
493 ifp
->name
, from_str
);
497 if (if_address_is_local(&from
, AF_INET
, ifp
->vrf
->vrf_id
)) {
498 if (PIM_DEBUG_GM_PACKETS
)
499 zlog_debug("Recv IGMP query on interface: %s from ourself %s",
500 ifp
->name
, from_str
);
504 /* Collecting IGMP Rx stats */
505 switch (query_version
) {
507 igmp
->igmp_stats
.query_v1
++;
510 igmp
->igmp_stats
.query_v2
++;
513 igmp
->igmp_stats
.query_v3
++;
516 igmp
->igmp_stats
.unsupported
++;
520 * RFC 3376 defines some guidelines on operating in backwards
521 * compatibility with older versions of IGMP but there are some gaps in
524 * - once we drop from say version 3 to version 2 we will never go back
525 * to version 3 even if the node that TXed an IGMP v2 query upgrades
528 * - The node with the lowest IP is the querier so we will only know to
529 * drop from v3 to v2 if the node that is the querier is also the one
530 * that is running igmp v2. If a non-querier only supports igmp v2
531 * we will have no way of knowing.
533 * For now we will simplify things and inform the user that they need to
534 * configure all PIM routers to use the same version of IGMP.
536 if (query_version
!= pim_ifp
->igmp_version
) {
538 "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",
539 query_version
, from_str
, ifp
->name
,
540 pim_ifp
->igmp_version
);
544 if (PIM_DEBUG_GM_PACKETS
) {
545 char group_str
[INET_ADDRSTRLEN
];
546 pim_inet4_dump("<group?>", group_addr
, group_str
,
548 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
549 query_version
, from_str
, ifp
->name
, group_str
);
553 RFC 3376: 6.6.2. Querier Election
555 When a router receives a query with a lower IP address, it sets
556 the Other-Querier-Present timer to Other Querier Present Interval
557 and ceases to send queries on the network if it was the previously
560 if (ntohl(from
.s_addr
) < ntohl(igmp
->ifaddr
.s_addr
)) {
562 /* As per RFC 2236 section 3:
563 * When a Querier receives a Leave Group message for a group
564 * that has group members on the reception interface, it sends
565 * [Last Member Query Count] Group-Specific Queries every [Last
566 * Member Query Interval] to the group being left. These
567 * Group-Specific Queries have their Max Response time set to
568 * [Last Member Query Interval]. If no Reports are received
569 * after the response time of the last query expires, the
570 * routers assume that the group has no local members, as above.
571 * Any Querier to non-Querier transition is ignored during this
572 * time; the same router keeps sending the Group-Specific
575 const struct gm_group
*group
;
576 const struct listnode
*grpnode
;
578 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_group_list
, grpnode
,
580 if (!group
->t_group_query_retransmit_timer
)
583 if (PIM_DEBUG_GM_TRACE
)
585 "%s: lower address query packet from %s is ignored when last member query interval timer is running",
586 ifp
->name
, from_str
);
590 if (PIM_DEBUG_GM_TRACE
) {
591 char ifaddr_str
[INET_ADDRSTRLEN
];
592 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
595 "%s: local address %s (%u) lost querier election to %s (%u)",
596 ifp
->name
, ifaddr_str
,
597 ntohl(igmp
->ifaddr
.s_addr
), from_str
,
600 /* Reset the other querier timer only if query is received from
601 * the previously elected querier or a better new querier
602 * This will make sure that non-querier elects the new querier
603 * whose ip address is higher than the old querier
604 * in case the old querier goes down via other querier present
607 if (ntohl(from
.s_addr
) <= ntohl(igmp
->querier_addr
.s_addr
)) {
608 igmp
->querier_addr
.s_addr
= from
.s_addr
;
609 pim_igmp_other_querier_timer_on(igmp
);
613 /* IGMP version 3 is the only one where we process the RXed query */
614 if (query_version
== 3) {
615 igmp_v3_recv_query(igmp
, from_str
, igmp_msg
);
621 static void on_trace(const char *label
, struct interface
*ifp
,
624 if (PIM_DEBUG_GM_TRACE
) {
625 char from_str
[INET_ADDRSTRLEN
];
626 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
627 zlog_debug("%s: from %s on %s", label
, from_str
, ifp
->name
);
631 static int igmp_v1_recv_report(struct gm_sock
*igmp
, struct in_addr from
,
632 const char *from_str
, char *igmp_msg
,
635 struct interface
*ifp
= igmp
->interface
;
636 struct gm_group
*group
;
637 struct in_addr group_addr
;
639 on_trace(__func__
, igmp
->interface
, from
);
641 if (igmp
->mtrace_only
)
644 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
646 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
647 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
651 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
653 "Recv IGMP report v1 from %s on %s with invalid checksum",
654 from_str
, ifp
->name
);
658 /* Collecting IGMP Rx stats */
659 igmp
->igmp_stats
.report_v1
++;
661 if (PIM_DEBUG_GM_TRACE
) {
662 zlog_warn("%s %s: FIXME WRITEME", __FILE__
, __func__
);
665 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
667 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
670 /* non-existent group is created as INCLUDE {empty} */
671 group
= igmp_add_group_by_addr(igmp
, group_addr
);
676 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
681 bool pim_igmp_verify_header(struct ip
*ip_hdr
, size_t len
, size_t *hlen
)
686 size_t ip_hlen
; /* ip header length in bytes */
688 if (len
< sizeof(*ip_hdr
)) {
689 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len
,
694 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
699 "IGMP packet header claims size %zu, but we only have %zu bytes",
704 igmp_msg
= (char *)ip_hdr
+ ip_hlen
;
705 igmp_msg_len
= len
- ip_hlen
;
706 msg_type
= *igmp_msg
;
708 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
709 zlog_warn("IGMP message size=%d shorter than minimum=%d",
710 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
714 if ((msg_type
!= PIM_IGMP_MTRACE_RESPONSE
)
715 && (msg_type
!= PIM_IGMP_MTRACE_QUERY_REQUEST
)) {
716 if (ip_hdr
->ip_ttl
!= 1) {
718 "Recv IGMP packet with invalid ttl=%u, discarding the packet",
727 int pim_igmp_packet(struct gm_sock
*igmp
, char *buf
, size_t len
)
729 struct ip
*ip_hdr
= (struct ip
*)buf
;
730 size_t ip_hlen
; /* ip header length in bytes */
734 char from_str
[INET_ADDRSTRLEN
];
735 char to_str
[INET_ADDRSTRLEN
];
737 if (!pim_igmp_verify_header(ip_hdr
, len
, &ip_hlen
))
740 igmp_msg
= buf
+ ip_hlen
;
741 igmp_msg_len
= len
- ip_hlen
;
742 msg_type
= *igmp_msg
;
744 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
745 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
747 if (PIM_DEBUG_GM_PACKETS
) {
749 "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
750 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hdr
->ip_ttl
,
751 msg_type
, igmp_msg_len
);
755 case PIM_IGMP_MEMBERSHIP_QUERY
: {
756 int max_resp_code
= igmp_msg
[1];
760 RFC 3376: 7.1. Query Version Distinctions
761 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
763 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
765 IGMPv3 Query: length >= 12 octets
768 if (igmp_msg_len
== 8) {
769 query_version
= max_resp_code
? 2 : 1;
770 } else if (igmp_msg_len
>= 12) {
773 zlog_warn("Unknown IGMP query version");
777 return igmp_recv_query(igmp
, query_version
, max_resp_code
,
778 ip_hdr
->ip_src
, from_str
, igmp_msg
,
782 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
783 return igmp_v3_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
784 igmp_msg
, igmp_msg_len
);
786 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
787 return igmp_v2_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
788 igmp_msg
, igmp_msg_len
);
790 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
791 return igmp_v1_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
792 igmp_msg
, igmp_msg_len
);
794 case PIM_IGMP_V2_LEAVE_GROUP
:
795 return igmp_v2_recv_leave(igmp
, ip_hdr
, from_str
, igmp_msg
,
798 case PIM_IGMP_MTRACE_RESPONSE
:
799 return igmp_mtrace_recv_response(igmp
, ip_hdr
, ip_hdr
->ip_src
,
802 case PIM_IGMP_MTRACE_QUERY_REQUEST
:
803 return igmp_mtrace_recv_qry_req(igmp
, ip_hdr
, ip_hdr
->ip_src
,
808 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
810 /* Collecting IGMP Rx stats */
811 igmp
->igmp_stats
.unsupported
++;
816 void pim_igmp_general_query_on(struct gm_sock
*igmp
)
818 struct pim_interface
*pim_ifp
;
823 Since this socket is starting as querier,
824 there should not exist a timer for other-querier-present.
826 assert(!igmp
->t_other_querier_timer
);
827 pim_ifp
= igmp
->interface
->info
;
831 RFC 3376: 8.6. Startup Query Interval
833 The Startup Query Interval is the interval between General Queries
834 sent by a Querier on startup. Default: 1/4 the Query Interval.
835 The first one should be sent out immediately instead of 125/4
838 startup_mode
= igmp
->startup_query_count
> 0;
841 * If this is the first time we are sending a query on a
842 * newly configured igmp interface send it out in 1 second
843 * just to give the entire world a tiny bit of time to settle
844 * else the query interval is:
845 * query_interval = pim_ifp->gm_default_query_interval >> 2;
847 if (igmp
->startup_query_count
==
848 igmp
->querier_robustness_variable
)
851 query_interval
= PIM_IGMP_SQI(
852 pim_ifp
->gm_default_query_interval
);
854 --igmp
->startup_query_count
;
856 query_interval
= igmp
->querier_query_interval
;
859 if (PIM_DEBUG_GM_TRACE
) {
860 char ifaddr_str
[INET_ADDRSTRLEN
];
861 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
864 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
865 ifaddr_str
, query_interval
,
866 startup_mode
? "startup" : "non-startup", igmp
->fd
);
868 thread_add_timer(router
->master
, pim_igmp_general_query
, igmp
,
869 query_interval
, &igmp
->t_igmp_query_timer
);
872 void pim_igmp_general_query_off(struct gm_sock
*igmp
)
876 if (PIM_DEBUG_GM_TRACE
) {
877 if (igmp
->t_igmp_query_timer
) {
878 char ifaddr_str
[INET_ADDRSTRLEN
];
879 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
882 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
883 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
886 THREAD_OFF(igmp
->t_igmp_query_timer
);
889 /* Issue IGMP general query */
890 static void pim_igmp_general_query(struct thread
*t
)
892 struct gm_sock
*igmp
;
893 struct in_addr dst_addr
;
894 struct in_addr group_addr
;
895 struct pim_interface
*pim_ifp
;
898 igmp
= THREAD_ARG(t
);
900 assert(igmp
->interface
);
901 assert(igmp
->interface
->info
);
903 pim_ifp
= igmp
->interface
->info
;
905 if (pim_ifp
->igmp_version
== 3) {
906 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
908 query_buf_size
= IGMP_V12_MSG_SIZE
;
911 char query_buf
[query_buf_size
];
914 RFC3376: 4.1.12. IP Destination Addresses for Queries
916 In IGMPv3, General Queries are sent with an IP destination address
917 of 224.0.0.1, the all-systems multicast address. Group-Specific
918 and Group-and-Source-Specific Queries are sent with an IP
919 destination address equal to the multicast address of interest.
922 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
923 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
925 if (PIM_DEBUG_GM_TRACE
) {
926 char querier_str
[INET_ADDRSTRLEN
];
927 char dst_str
[INET_ADDRSTRLEN
];
928 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
929 sizeof(querier_str
));
930 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
931 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
932 querier_str
, dst_str
, igmp
->interface
->name
);
935 igmp_send_query(pim_ifp
->igmp_version
, 0 /* igmp_group */, query_buf
,
936 sizeof(query_buf
), 0 /* num_sources */, dst_addr
,
937 group_addr
, pim_ifp
->gm_query_max_response_time_dsec
,
938 1 /* s_flag: always set for general queries */, igmp
);
940 pim_igmp_general_query_on(igmp
);
943 static void sock_close(struct gm_sock
*igmp
)
945 pim_igmp_other_querier_timer_off(igmp
);
946 pim_igmp_general_query_off(igmp
);
948 if (PIM_DEBUG_GM_TRACE_DETAIL
) {
949 if (igmp
->t_igmp_read
) {
951 "Cancelling READ event on IGMP socket %pI4 fd=%d on interface %s",
952 &igmp
->ifaddr
, igmp
->fd
,
953 igmp
->interface
->name
);
956 THREAD_OFF(igmp
->t_igmp_read
);
958 if (close(igmp
->fd
)) {
961 "Failure closing IGMP socket %pI4 fd=%d on interface %s: errno=%d: %s",
962 &igmp
->ifaddr
, igmp
->fd
,
963 igmp
->interface
->name
, errno
, safe_strerror(errno
));
966 if (PIM_DEBUG_GM_TRACE_DETAIL
) {
967 zlog_debug("Deleted IGMP socket %pI4 fd=%d on interface %s",
968 &igmp
->ifaddr
, igmp
->fd
,
969 igmp
->interface
->name
);
973 void igmp_startup_mode_on(struct gm_sock
*igmp
)
975 struct pim_interface
*pim_ifp
;
977 pim_ifp
= igmp
->interface
->info
;
980 RFC 3376: 8.7. Startup Query Count
982 The Startup Query Count is the number of Queries sent out on
983 startup, separated by the Startup Query Interval. Default: the
986 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
989 Since we're (re)starting, reset QQI to default Query Interval
991 igmp
->querier_query_interval
= pim_ifp
->gm_default_query_interval
;
994 static void igmp_group_free(struct gm_group
*group
)
996 list_delete(&group
->group_source_list
);
998 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
1001 static void igmp_group_count_incr(struct pim_interface
*pim_ifp
)
1003 uint32_t group_count
= listcount(pim_ifp
->gm_group_list
);
1005 ++pim_ifp
->pim
->gm_group_count
;
1006 if (pim_ifp
->pim
->gm_group_count
== pim_ifp
->pim
->gm_watermark_limit
) {
1008 "IGMP group count reached watermark limit: %u(vrf: %s)",
1009 pim_ifp
->pim
->gm_group_count
,
1010 VRF_LOGNAME(pim_ifp
->pim
->vrf
));
1013 if (pim_ifp
->igmp_peak_group_count
< group_count
)
1014 pim_ifp
->igmp_peak_group_count
= group_count
;
1017 static void igmp_group_count_decr(struct pim_interface
*pim_ifp
)
1019 if (pim_ifp
->pim
->gm_group_count
== 0) {
1020 zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
1021 VRF_LOGNAME(pim_ifp
->pim
->vrf
));
1025 --pim_ifp
->pim
->gm_group_count
;
1028 void igmp_group_delete(struct gm_group
*group
)
1030 struct listnode
*src_node
;
1031 struct listnode
*src_nextnode
;
1032 struct gm_source
*src
;
1033 struct pim_interface
*pim_ifp
= group
->interface
->info
;
1035 if (PIM_DEBUG_GM_TRACE
) {
1036 char group_str
[INET_ADDRSTRLEN
];
1037 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1039 zlog_debug("Deleting IGMP group %s from interface %s",
1040 group_str
, group
->interface
->name
);
1043 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
,
1045 igmp_source_delete(src
);
1048 THREAD_OFF(group
->t_group_query_retransmit_timer
);
1050 group_timer_off(group
);
1051 igmp_group_count_decr(pim_ifp
);
1052 listnode_delete(pim_ifp
->gm_group_list
, group
);
1053 hash_release(pim_ifp
->gm_group_hash
, group
);
1055 igmp_group_free(group
);
1058 void igmp_group_delete_empty_include(struct gm_group
*group
)
1060 assert(!group
->group_filtermode_isexcl
);
1061 assert(!listcount(group
->group_source_list
));
1063 igmp_group_delete(group
);
1066 void igmp_sock_free(struct gm_sock
*igmp
)
1068 assert(!igmp
->t_igmp_read
);
1069 assert(!igmp
->t_igmp_query_timer
);
1070 assert(!igmp
->t_other_querier_timer
);
1072 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
1075 void igmp_sock_delete(struct gm_sock
*igmp
)
1077 struct pim_interface
*pim_ifp
;
1081 pim_ifp
= igmp
->interface
->info
;
1083 listnode_delete(pim_ifp
->gm_socket_list
, igmp
);
1085 igmp_sock_free(igmp
);
1087 if (!listcount(pim_ifp
->gm_socket_list
))
1088 pim_igmp_if_reset(pim_ifp
);
1091 void igmp_sock_delete_all(struct interface
*ifp
)
1093 struct pim_interface
*pim_ifp
;
1094 struct listnode
*igmp_node
, *igmp_nextnode
;
1095 struct gm_sock
*igmp
;
1097 pim_ifp
= ifp
->info
;
1099 for (ALL_LIST_ELEMENTS(pim_ifp
->gm_socket_list
, igmp_node
,
1100 igmp_nextnode
, igmp
)) {
1101 igmp_sock_delete(igmp
);
1105 static unsigned int igmp_group_hash_key(const void *arg
)
1107 const struct gm_group
*group
= arg
;
1109 return jhash_1word(group
->group_addr
.s_addr
, 0);
1112 static bool igmp_group_hash_equal(const void *arg1
, const void *arg2
)
1114 const struct gm_group
*g1
= (const struct gm_group
*)arg1
;
1115 const struct gm_group
*g2
= (const struct gm_group
*)arg2
;
1117 if (g1
->group_addr
.s_addr
== g2
->group_addr
.s_addr
)
1123 void pim_igmp_if_init(struct pim_interface
*pim_ifp
, struct interface
*ifp
)
1127 pim_ifp
->gm_socket_list
= list_new();
1128 pim_ifp
->gm_socket_list
->del
= (void (*)(void *))igmp_sock_free
;
1130 pim_ifp
->gm_group_list
= list_new();
1131 pim_ifp
->gm_group_list
->del
= (void (*)(void *))igmp_group_free
;
1133 snprintf(hash_name
, sizeof(hash_name
), "IGMP %s hash", ifp
->name
);
1134 pim_ifp
->gm_group_hash
= hash_create(igmp_group_hash_key
,
1135 igmp_group_hash_equal
, hash_name
);
1138 void pim_igmp_if_reset(struct pim_interface
*pim_ifp
)
1140 struct listnode
*grp_node
, *grp_nextnode
;
1141 struct gm_group
*grp
;
1143 for (ALL_LIST_ELEMENTS(pim_ifp
->gm_group_list
, grp_node
, grp_nextnode
,
1145 igmp_group_delete(grp
);
1149 void pim_igmp_if_fini(struct pim_interface
*pim_ifp
)
1151 pim_igmp_if_reset(pim_ifp
);
1153 assert(pim_ifp
->gm_group_list
);
1154 assert(!listcount(pim_ifp
->gm_group_list
));
1156 list_delete(&pim_ifp
->gm_group_list
);
1157 hash_free(pim_ifp
->gm_group_hash
);
1159 list_delete(&pim_ifp
->gm_socket_list
);
1162 static struct gm_sock
*igmp_sock_new(int fd
, struct in_addr ifaddr
,
1163 struct interface
*ifp
, int mtrace_only
)
1165 struct pim_interface
*pim_ifp
;
1166 struct gm_sock
*igmp
;
1168 pim_ifp
= ifp
->info
;
1170 if (PIM_DEBUG_GM_TRACE
) {
1172 "Creating IGMP socket fd=%d for address %pI4 on interface %s",
1173 fd
, &ifaddr
, ifp
->name
);
1176 igmp
= XCALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
1179 igmp
->interface
= ifp
;
1180 igmp
->ifaddr
= ifaddr
;
1181 igmp
->querier_addr
= ifaddr
;
1182 igmp
->t_igmp_read
= NULL
;
1183 igmp
->t_igmp_query_timer
= NULL
;
1184 igmp
->t_other_querier_timer
= NULL
; /* no other querier present */
1185 igmp
->querier_robustness_variable
=
1186 pim_ifp
->gm_default_robustness_variable
;
1187 igmp
->sock_creation
= pim_time_monotonic_sec();
1189 igmp_stats_init(&igmp
->igmp_stats
);
1192 igmp
->mtrace_only
= mtrace_only
;
1196 igmp
->mtrace_only
= false;
1199 igmp_startup_mode_on() will reset QQI:
1201 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
1203 igmp_startup_mode_on(igmp
);
1204 pim_igmp_general_query_on(igmp
);
1209 static void igmp_read_on(struct gm_sock
*igmp
);
1211 static void pim_igmp_read(struct thread
*t
)
1214 struct gm_sock
*igmp
= (struct gm_sock
*)THREAD_ARG(t
);
1215 struct sockaddr_storage from
;
1216 struct sockaddr_storage to
;
1217 socklen_t fromlen
= sizeof(from
);
1218 socklen_t tolen
= sizeof(to
);
1219 ifindex_t ifindex
= -1;
1223 len
= pim_socket_recvfromto(igmp
->fd
, buf
, sizeof(buf
), &from
,
1224 &fromlen
, &to
, &tolen
, &ifindex
);
1228 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
1239 static void igmp_read_on(struct gm_sock
*igmp
)
1242 if (PIM_DEBUG_GM_TRACE_DETAIL
) {
1243 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
1246 thread_add_read(router
->master
, pim_igmp_read
, igmp
, igmp
->fd
,
1247 &igmp
->t_igmp_read
);
1250 struct gm_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
1251 struct in_addr ifaddr
, struct interface
*ifp
,
1254 struct gm_sock
*igmp
;
1255 struct sockaddr_in sin
;
1258 fd
= igmp_sock_open(ifaddr
, ifp
);
1260 zlog_warn("Could not open IGMP socket for %pI4 on %s",
1261 &ifaddr
, ifp
->name
);
1265 sin
.sin_family
= AF_INET
;
1266 sin
.sin_addr
= ifaddr
;
1268 if (bind(fd
, (struct sockaddr
*) &sin
, sizeof(sin
)) != 0) {
1269 zlog_warn("Could not bind IGMP socket for %pI4 on %s: %s(%d)",
1270 &ifaddr
, ifp
->name
, strerror(errno
), errno
);
1276 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
, mtrace_only
);
1280 listnode_add(igmp_sock_list
, igmp
);
1282 #ifdef IGMP_SOCK_DUMP
1283 igmp_sock_dump(igmp_sock_array
);
1290 RFC 3376: 6.5. Switching Router Filter-Modes
1292 When a router's filter-mode for a group is EXCLUDE and the group
1293 timer expires, the router filter-mode for the group transitions to
1296 A router uses source records with running source timers as its state
1297 for the switch to a filter-mode of INCLUDE. If there are any source
1298 records with source timers greater than zero (i.e., requested to be
1299 forwarded), a router switches to filter-mode of INCLUDE using those
1300 source records. Source records whose timers are zero (from the
1301 previous EXCLUDE mode) are deleted.
1303 static void igmp_group_timer(struct thread
*t
)
1305 struct gm_group
*group
;
1307 group
= THREAD_ARG(t
);
1309 if (PIM_DEBUG_GM_TRACE
) {
1310 char group_str
[INET_ADDRSTRLEN
];
1311 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1313 zlog_debug("%s: Timer for group %s on interface %s", __func__
,
1314 group_str
, group
->interface
->name
);
1317 assert(group
->group_filtermode_isexcl
);
1319 group
->group_filtermode_isexcl
= 0;
1321 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1322 igmp_anysource_forward_stop(group
);
1324 igmp_source_delete_expired(group
->group_source_list
);
1326 assert(!group
->group_filtermode_isexcl
);
1329 RFC 3376: 6.2.2. Definition of Group Timers
1331 If there are no more source records for the group, delete group
1334 if (listcount(group
->group_source_list
) < 1) {
1335 igmp_group_delete_empty_include(group
);
1339 static void group_timer_off(struct gm_group
*group
)
1341 if (!group
->t_group_timer
)
1344 if (PIM_DEBUG_GM_TRACE
) {
1345 char group_str
[INET_ADDRSTRLEN
];
1346 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1348 zlog_debug("Cancelling TIMER event for group %s on %s",
1349 group_str
, group
->interface
->name
);
1351 THREAD_OFF(group
->t_group_timer
);
1354 void igmp_group_timer_on(struct gm_group
*group
, long interval_msec
,
1357 group_timer_off(group
);
1359 if (PIM_DEBUG_GM_EVENTS
) {
1360 char group_str
[INET_ADDRSTRLEN
];
1361 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1364 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1365 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
1370 RFC 3376: 6.2.2. Definition of Group Timers
1372 The group timer is only used when a group is in EXCLUDE mode and
1373 it represents the time for the *filter-mode* of the group to
1374 expire and switch to INCLUDE mode.
1376 assert(group
->group_filtermode_isexcl
);
1378 thread_add_timer_msec(router
->master
, igmp_group_timer
, group
,
1379 interval_msec
, &group
->t_group_timer
);
1382 struct gm_group
*find_group_by_addr(struct gm_sock
*igmp
,
1383 struct in_addr group_addr
)
1385 struct gm_group lookup
;
1386 struct pim_interface
*pim_ifp
= igmp
->interface
->info
;
1388 lookup
.group_addr
.s_addr
= group_addr
.s_addr
;
1390 return hash_lookup(pim_ifp
->gm_group_hash
, &lookup
);
1393 struct gm_group
*igmp_add_group_by_addr(struct gm_sock
*igmp
,
1394 struct in_addr group_addr
)
1396 struct gm_group
*group
;
1397 struct pim_interface
*pim_ifp
= igmp
->interface
->info
;
1399 group
= find_group_by_addr(igmp
, group_addr
);
1404 if (!pim_is_group_224_4(group_addr
)) {
1405 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1410 if (pim_is_group_224_0_0_0_24(group_addr
)) {
1411 if (PIM_DEBUG_GM_TRACE
)
1413 "%s: Group specified %pI4 is part of 224.0.0.0/24",
1414 __func__
, &group_addr
);
1418 Non-existant group is created as INCLUDE {empty}:
1420 RFC 3376 - 5.1. Action on Change of Interface State
1422 If no interface state existed for that multicast address before
1423 the change (i.e., the change consisted of creating a new
1424 per-interface record), or if no state exists after the change
1425 (i.e., the change consisted of deleting a per-interface record),
1426 then the "non-existent" state is considered to have a filter mode
1427 of INCLUDE and an empty source list.
1430 group
= XCALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1432 group
->group_source_list
= list_new();
1433 group
->group_source_list
->del
= (void (*)(void *))igmp_source_free
;
1435 group
->t_group_timer
= NULL
;
1436 group
->t_group_query_retransmit_timer
= NULL
;
1437 group
->group_specific_query_retransmit_count
= 0;
1438 group
->group_addr
= group_addr
;
1439 group
->interface
= igmp
->interface
;
1440 group
->last_igmp_v1_report_dsec
= -1;
1441 group
->last_igmp_v2_report_dsec
= -1;
1442 group
->group_creation
= pim_time_monotonic_sec();
1443 group
->igmp_version
= IGMP_DEFAULT_VERSION
;
1445 /* initialize new group as INCLUDE {empty} */
1446 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1448 listnode_add(pim_ifp
->gm_group_list
, group
);
1449 group
= hash_get(pim_ifp
->gm_group_hash
, group
, hash_alloc_intern
);
1451 if (PIM_DEBUG_GM_TRACE
) {
1452 char group_str
[INET_ADDRSTRLEN
];
1453 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1456 "Creating new IGMP group %s on socket %d interface %s",
1457 group_str
, igmp
->fd
, igmp
->interface
->name
);
1460 igmp_group_count_incr(pim_ifp
);
1463 RFC 3376: 6.2.2. Definition of Group Timers
1465 The group timer is only used when a group is in EXCLUDE mode and
1466 it represents the time for the *filter-mode* of the group to
1467 expire and switch to INCLUDE mode.
1469 assert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1470 assert(!group
->t_group_timer
); /* group timer == 0 */
1472 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1473 igmp_anysource_forward_stop(group
);
1478 void igmp_send_query(int igmp_version
, struct gm_group
*group
, char *query_buf
,
1479 int query_buf_size
, int num_sources
,
1480 struct in_addr dst_addr
, struct in_addr group_addr
,
1481 int query_max_response_time_dsec
, uint8_t s_flag
,
1482 struct gm_sock
*igmp
)
1484 if (pim_addr_is_any(group_addr
) &&
1485 ntohl(dst_addr
.s_addr
) == INADDR_ALLHOSTS_GROUP
)
1486 igmp
->igmp_stats
.general_queries_sent
++;
1488 igmp
->igmp_stats
.group_queries_sent
++;
1490 if (igmp_version
== 3) {
1491 igmp_v3_send_query(group
, igmp
->fd
, igmp
->interface
->name
,
1492 query_buf
, query_buf_size
, num_sources
,
1493 dst_addr
, group_addr
,
1494 query_max_response_time_dsec
, s_flag
,
1495 igmp
->querier_robustness_variable
,
1496 igmp
->querier_query_interval
);
1497 } else if (igmp_version
== 2) {
1498 igmp_v2_send_query(group
, igmp
->fd
, igmp
->interface
->name
,
1499 query_buf
, dst_addr
, group_addr
,
1500 query_max_response_time_dsec
);
1504 void igmp_send_query_on_intf(struct interface
*ifp
, int igmp_ver
)
1506 struct pim_interface
*pim_ifp
= ifp
->info
;
1507 struct listnode
*sock_node
= NULL
;
1508 struct gm_sock
*igmp
= NULL
;
1509 struct in_addr dst_addr
;
1510 struct in_addr group_addr
;
1517 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1519 query_buf_size
= IGMP_V12_MSG_SIZE
;
1521 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
1522 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
1524 if (PIM_DEBUG_GM_TRACE
)
1525 zlog_debug("Issuing general query on request on %s", ifp
->name
);
1527 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_socket_list
, sock_node
, igmp
)) {
1529 char query_buf
[query_buf_size
];
1532 igmp_ver
, 0 /* igmp_group */, query_buf
,
1533 sizeof(query_buf
), 0 /* num_sources */, dst_addr
,
1534 group_addr
, pim_ifp
->gm_query_max_response_time_dsec
,
1535 1 /* s_flag: always set for general queries */, igmp
);