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"
30 #include "pim_instance.h"
32 #include "pim_igmpv2.h"
33 #include "pim_igmpv3.h"
34 #include "pim_igmp_mtrace.h"
35 #include "pim_iface.h"
37 #include "pim_mroute.h"
44 static void group_timer_off(struct gm_group
*group
);
45 static void pim_igmp_general_query(struct thread
*t
);
47 void igmp_anysource_forward_start(struct pim_instance
*pim
,
48 struct gm_group
*group
)
50 struct gm_source
*source
;
51 struct in_addr src_addr
= {.s_addr
= 0};
52 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
53 assert(group
->group_filtermode_isexcl
);
54 assert(listcount(group
->group_source_list
) < 1);
56 source
= igmp_get_source_by_addr(group
, src_addr
, NULL
);
58 zlog_warn("%s: Failure to create * source", __func__
);
62 igmp_source_forward_start(pim
, source
);
65 void igmp_anysource_forward_stop(struct gm_group
*group
)
67 struct gm_source
*source
;
68 struct in_addr star
= {.s_addr
= 0};
70 source
= igmp_find_source_by_addr(group
, star
);
72 igmp_source_forward_stop(source
);
75 static void igmp_source_forward_reevaluate_one(struct pim_instance
*pim
,
76 struct gm_source
*source
,
80 struct gm_group
*group
= source
->source_group
;
82 memset(&sg
, 0, sizeof(sg
));
83 sg
.src
= source
->source_addr
;
84 sg
.grp
= group
->group_addr
;
86 /** if there is no PIM state **/
87 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
88 if (pim_addr_is_any(source
->source_addr
)) {
90 if (PIM_DEBUG_PIM_EVENTS
)
92 "local membership del for %pSG as G is now SSM",
94 igmp_source_forward_stop(source
);
98 if (PIM_DEBUG_PIM_EVENTS
)
100 "local membership del for %pSG as G is now ASM",
102 igmp_source_forward_stop(source
);
106 if (!pim_addr_is_any(source
->source_addr
) && (is_grp_ssm
)) {
107 if (PIM_DEBUG_PIM_EVENTS
)
109 "local membership add for %pSG as G is now SSM",
111 igmp_source_forward_start(pim
, source
);
116 void igmp_source_forward_reevaluate_all(struct pim_instance
*pim
)
118 struct interface
*ifp
;
120 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
121 struct pim_interface
*pim_ifp
= ifp
->info
;
122 struct listnode
*grpnode
, *grp_nextnode
;
123 struct gm_group
*grp
;
124 struct pim_ifchannel
*ch
, *ch_temp
;
129 /* scan igmp groups */
130 for (ALL_LIST_ELEMENTS(pim_ifp
->gm_group_list
, grpnode
,
131 grp_nextnode
, grp
)) {
132 struct listnode
*srcnode
;
133 struct gm_source
*src
;
139 * EXCLUDE mode does not apply to SSM addresses,
140 * and an SSM-aware router will ignore
141 * MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE
142 * requests in the SSM range.
144 is_grp_ssm
= pim_is_grp_ssm(pim
, grp
->group_addr
);
145 if (is_grp_ssm
&& grp
->group_filtermode_isexcl
) {
146 igmp_group_delete(grp
);
148 /* scan group sources */
149 for (ALL_LIST_ELEMENTS_RO(
150 grp
->group_source_list
, srcnode
,
152 igmp_source_forward_reevaluate_one(
153 pim
, src
, is_grp_ssm
);
154 } /* scan group sources */
156 } /* scan igmp groups */
158 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
,
160 if (pim_is_grp_ssm(pim
, ch
->sg
.grp
)) {
161 if (pim_addr_is_any(ch
->sg
.src
))
162 pim_ifchannel_delete(ch
);
165 } /* scan interfaces */
168 void igmp_source_forward_start(struct pim_instance
*pim
,
169 struct gm_source
*source
)
171 struct gm_group
*group
;
174 memset(&sg
, 0, sizeof(sg
));
175 sg
.src
= source
->source_addr
;
176 sg
.grp
= source
->source_group
->group_addr
;
178 if (PIM_DEBUG_IGMP_TRACE
) {
179 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__
, &sg
,
180 source
->source_group
->interface
->name
,
181 IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
));
185 * PIM state should not be allowed for ASM group with valid source
188 if ((!pim_is_grp_ssm(pim
, source
->source_group
->group_addr
)) &&
189 !pim_addr_is_any(source
->source_addr
)) {
191 "%s: (S,G)=%pSG ASM range having source address, not allowed to create PIM state",
196 /* Prevent IGMP interface from installing multicast route multiple
198 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
202 group
= source
->source_group
;
204 if (tib_sg_gm_join(pim
, sg
, group
->interface
,
205 &source
->source_channel_oil
))
206 IGMP_SOURCE_DO_FORWARDING(source
->source_flags
);
210 igmp_source_forward_stop: stop forwarding, but keep the source
211 igmp_source_delete: stop forwarding, and delete the source
213 void igmp_source_forward_stop(struct gm_source
*source
)
215 struct pim_interface
*pim_oif
;
216 struct gm_group
*group
;
219 memset(&sg
, 0, sizeof(sg
));
220 sg
.src
= source
->source_addr
;
221 sg
.grp
= source
->source_group
->group_addr
;
223 if (PIM_DEBUG_IGMP_TRACE
) {
224 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__
, &sg
,
225 source
->source_group
->interface
->name
,
226 IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
));
229 /* Prevent IGMP interface from removing multicast route multiple
231 if (!IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
235 group
= source
->source_group
;
236 pim_oif
= group
->interface
->info
;
238 tib_sg_gm_prune(pim_oif
->pim
, sg
, group
->interface
,
239 &source
->source_channel_oil
);
240 IGMP_SOURCE_DONT_FORWARDING(source
->source_flags
);
243 /* This socket is used for TXing IGMP packets only, IGMP RX happens
244 * in pim_mroute_msg()
246 static int igmp_sock_open(struct in_addr ifaddr
, struct interface
*ifp
)
250 struct in_addr group
;
251 struct pim_interface
*pim_ifp
= ifp
->info
;
253 fd
= pim_socket_mcast(IPPROTO_IGMP
, ifaddr
, ifp
, 1);
258 if (inet_aton(PIM_ALL_ROUTERS
, &group
)) {
259 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
, pim_ifp
))
263 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
264 __FILE__
, __func__
, fd
, &ifaddr
, PIM_ALL_ROUTERS
, errno
,
265 safe_strerror(errno
));
269 IGMP routers periodically send IGMP general queries to
271 IGMP routers must receive general queries for querier election.
273 if (inet_aton(PIM_ALL_SYSTEMS
, &group
)) {
274 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
, pim_ifp
))
278 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
279 __FILE__
, __func__
, fd
, &ifaddr
,
280 PIM_ALL_SYSTEMS
, errno
, safe_strerror(errno
));
283 if (inet_aton(PIM_ALL_IGMP_ROUTERS
, &group
)) {
284 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
,
290 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
291 __FILE__
, __func__
, fd
, &ifaddr
,
292 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
298 "IGMP socket fd=%d could not join any group on interface address %pI4",
307 #undef IGMP_SOCK_DUMP
309 #ifdef IGMP_SOCK_DUMP
310 static void igmp_sock_dump(array_t
*igmp_sock_array
)
312 int size
= array_size(igmp_sock_array
);
313 for (int i
= 0; i
< size
; ++i
) {
315 struct gm_sock
*igmp
= array_get(igmp_sock_array
, i
);
317 zlog_debug("%s %s: [%d/%d] igmp_addr=%pI4 fd=%d", __FILE__
,
318 __func__
, i
, size
, &igmp
->ifaddr
,
324 struct gm_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
325 struct in_addr ifaddr
)
327 struct listnode
*sock_node
;
328 struct gm_sock
*igmp
;
330 #ifdef IGMP_SOCK_DUMP
331 igmp_sock_dump(igmp_sock_list
);
334 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
335 if (ifaddr
.s_addr
== igmp
->ifaddr
.s_addr
)
341 static void pim_igmp_other_querier_expire(struct thread
*t
)
343 struct gm_sock
*igmp
;
345 igmp
= THREAD_ARG(t
);
347 assert(!igmp
->t_igmp_query_timer
);
349 if (PIM_DEBUG_IGMP_TRACE
) {
350 char ifaddr_str
[INET_ADDRSTRLEN
];
351 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
353 zlog_debug("%s: Querier %s resuming", __func__
, ifaddr_str
);
355 /* Mark the interface address as querier address */
356 igmp
->querier_addr
= igmp
->ifaddr
;
359 We are the current querier, then
360 re-start sending general queries.
361 RFC 2236 - sec 7 Other Querier
362 present timer expired (Send General
363 Query, Set Gen. Query. timer)
365 pim_igmp_general_query(t
);
368 void pim_igmp_other_querier_timer_on(struct gm_sock
*igmp
)
370 long other_querier_present_interval_msec
;
371 struct pim_interface
*pim_ifp
;
374 assert(igmp
->interface
);
375 assert(igmp
->interface
->info
);
377 pim_ifp
= igmp
->interface
->info
;
379 if (igmp
->t_other_querier_timer
) {
381 There is other querier present already,
382 then reset the other-querier-present timer.
385 if (PIM_DEBUG_IGMP_TRACE
) {
386 char ifaddr_str
[INET_ADDRSTRLEN
];
387 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
390 "Querier %s resetting TIMER event for Other-Querier-Present",
393 THREAD_OFF(igmp
->t_other_querier_timer
);
396 We are the current querier, then stop sending general queries:
397 igmp->t_igmp_query_timer = NULL;
399 pim_igmp_general_query_off(igmp
);
403 Since this socket is starting the other-querier-present timer,
404 there should not be periodic query timer for this socket.
406 assert(!igmp
->t_igmp_query_timer
);
409 RFC 3376: 8.5. Other Querier Present Interval
411 The Other Querier Present Interval is the length of time that must
412 pass before a multicast router decides that there is no longer
413 another multicast router which should be the querier. This value
414 MUST be ((the Robustness Variable) times (the Query Interval)) plus
415 (one half of one Query Response Interval).
417 other_querier_present_interval_msec = \
418 igmp->querier_robustness_variable * \
419 1000 * igmp->querier_query_interval + \
420 100 * (pim_ifp->query_max_response_time_dsec >> 1);
422 other_querier_present_interval_msec
= PIM_IGMP_OQPI_MSEC(
423 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
424 pim_ifp
->gm_query_max_response_time_dsec
);
426 if (PIM_DEBUG_IGMP_TRACE
) {
427 char ifaddr_str
[INET_ADDRSTRLEN
];
428 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
431 "Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
432 ifaddr_str
, other_querier_present_interval_msec
/ 1000,
433 other_querier_present_interval_msec
% 1000);
436 thread_add_timer_msec(router
->master
, pim_igmp_other_querier_expire
,
437 igmp
, other_querier_present_interval_msec
,
438 &igmp
->t_other_querier_timer
);
441 void pim_igmp_other_querier_timer_off(struct gm_sock
*igmp
)
445 if (PIM_DEBUG_IGMP_TRACE
) {
446 if (igmp
->t_other_querier_timer
) {
447 char ifaddr_str
[INET_ADDRSTRLEN
];
448 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
451 "IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
452 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
455 THREAD_OFF(igmp
->t_other_querier_timer
);
458 int igmp_validate_checksum(char *igmp_msg
, int igmp_msg_len
)
460 uint16_t recv_checksum
;
463 IGMP_GET_INT16((unsigned char *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
),
466 /* Clear the checksum field */
467 memset(igmp_msg
+ IGMP_CHECKSUM_OFFSET
, 0, 2);
469 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
470 if (ntohs(checksum
) != recv_checksum
) {
471 zlog_warn("Invalid checksum received %x, calculated %x",
472 recv_checksum
, ntohs(checksum
));
479 static int igmp_recv_query(struct gm_sock
*igmp
, int query_version
,
480 int max_resp_code
, struct in_addr from
,
481 const char *from_str
, char *igmp_msg
,
484 struct interface
*ifp
;
485 struct pim_interface
*pim_ifp
;
486 struct in_addr group_addr
;
488 if (igmp
->mtrace_only
)
491 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
493 ifp
= igmp
->interface
;
496 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
498 "Recv IGMP query v%d from %s on %s with invalid checksum",
499 query_version
, from_str
, ifp
->name
);
503 if (!pim_if_connected_to_source(ifp
, from
)) {
504 if (PIM_DEBUG_IGMP_PACKETS
)
505 zlog_debug("Recv IGMP query on interface: %s from a non-connected source: %s",
506 ifp
->name
, from_str
);
510 if (if_address_is_local(&from
, AF_INET
, ifp
->vrf
->vrf_id
)) {
511 if (PIM_DEBUG_IGMP_PACKETS
)
512 zlog_debug("Recv IGMP query on interface: %s from ourself %s",
513 ifp
->name
, from_str
);
517 /* Collecting IGMP Rx stats */
518 switch (query_version
) {
520 igmp
->igmp_stats
.query_v1
++;
523 igmp
->igmp_stats
.query_v2
++;
526 igmp
->igmp_stats
.query_v3
++;
529 igmp
->igmp_stats
.unsupported
++;
533 * RFC 3376 defines some guidelines on operating in backwards
534 * compatibility with older versions of IGMP but there are some gaps in
537 * - once we drop from say version 3 to version 2 we will never go back
538 * to version 3 even if the node that TXed an IGMP v2 query upgrades
541 * - The node with the lowest IP is the querier so we will only know to
542 * drop from v3 to v2 if the node that is the querier is also the one
543 * that is running igmp v2. If a non-querier only supports igmp v2
544 * we will have no way of knowing.
546 * For now we will simplify things and inform the user that they need to
547 * configure all PIM routers to use the same version of IGMP.
549 if (query_version
!= pim_ifp
->igmp_version
) {
551 "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",
552 query_version
, from_str
, ifp
->name
,
553 pim_ifp
->igmp_version
);
557 if (PIM_DEBUG_IGMP_PACKETS
) {
558 char group_str
[INET_ADDRSTRLEN
];
559 pim_inet4_dump("<group?>", group_addr
, group_str
,
561 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
562 query_version
, from_str
, ifp
->name
, group_str
);
566 RFC 3376: 6.6.2. Querier Election
568 When a router receives a query with a lower IP address, it sets
569 the Other-Querier-Present timer to Other Querier Present Interval
570 and ceases to send queries on the network if it was the previously
573 if (ntohl(from
.s_addr
) < ntohl(igmp
->ifaddr
.s_addr
)) {
575 /* As per RFC 2236 section 3:
576 * When a Querier receives a Leave Group message for a group
577 * that has group members on the reception interface, it sends
578 * [Last Member Query Count] Group-Specific Queries every [Last
579 * Member Query Interval] to the group being left. These
580 * Group-Specific Queries have their Max Response time set to
581 * [Last Member Query Interval]. If no Reports are received
582 * after the response time of the last query expires, the
583 * routers assume that the group has no local members, as above.
584 * Any Querier to non-Querier transition is ignored during this
585 * time; the same router keeps sending the Group-Specific
588 const struct gm_group
*group
;
589 const struct listnode
*grpnode
;
591 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_group_list
, grpnode
,
593 if (!group
->t_group_query_retransmit_timer
)
596 if (PIM_DEBUG_IGMP_TRACE
)
598 "%s: lower address query packet from %s is ignored when last member query interval timer is running",
599 ifp
->name
, from_str
);
603 if (PIM_DEBUG_IGMP_TRACE
) {
604 char ifaddr_str
[INET_ADDRSTRLEN
];
605 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
608 "%s: local address %s (%u) lost querier election to %s (%u)",
609 ifp
->name
, ifaddr_str
,
610 ntohl(igmp
->ifaddr
.s_addr
), from_str
,
613 if (ntohl(from
.s_addr
) < ntohl(igmp
->querier_addr
.s_addr
))
614 igmp
->querier_addr
.s_addr
= from
.s_addr
;
616 pim_igmp_other_querier_timer_on(igmp
);
619 /* IGMP version 3 is the only one where we process the RXed query */
620 if (query_version
== 3) {
621 igmp_v3_recv_query(igmp
, from_str
, igmp_msg
);
627 static void on_trace(const char *label
, struct interface
*ifp
,
630 if (PIM_DEBUG_IGMP_TRACE
) {
631 char from_str
[INET_ADDRSTRLEN
];
632 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
633 zlog_debug("%s: from %s on %s", label
, from_str
, ifp
->name
);
637 static int igmp_v1_recv_report(struct gm_sock
*igmp
, struct in_addr from
,
638 const char *from_str
, char *igmp_msg
,
641 struct interface
*ifp
= igmp
->interface
;
642 struct gm_group
*group
;
643 struct in_addr group_addr
;
645 on_trace(__func__
, igmp
->interface
, from
);
647 if (igmp
->mtrace_only
)
650 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
652 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
653 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
657 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
659 "Recv IGMP report v1 from %s on %s with invalid checksum",
660 from_str
, ifp
->name
);
664 /* Collecting IGMP Rx stats */
665 igmp
->igmp_stats
.report_v1
++;
667 if (PIM_DEBUG_IGMP_TRACE
) {
668 zlog_warn("%s %s: FIXME WRITEME", __FILE__
, __func__
);
671 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
673 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
676 /* non-existent group is created as INCLUDE {empty} */
677 group
= igmp_add_group_by_addr(igmp
, group_addr
);
682 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
687 bool pim_igmp_verify_header(struct ip
*ip_hdr
, size_t len
, size_t *hlen
)
692 size_t ip_hlen
; /* ip header length in bytes */
694 if (len
< sizeof(*ip_hdr
)) {
695 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len
,
700 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
705 "IGMP packet header claims size %zu, but we only have %zu bytes",
710 igmp_msg
= (char *)ip_hdr
+ ip_hlen
;
711 igmp_msg_len
= len
- ip_hlen
;
712 msg_type
= *igmp_msg
;
714 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
715 zlog_warn("IGMP message size=%d shorter than minimum=%d",
716 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
720 if ((msg_type
!= PIM_IGMP_MTRACE_RESPONSE
)
721 && (msg_type
!= PIM_IGMP_MTRACE_QUERY_REQUEST
)) {
722 if (ip_hdr
->ip_ttl
!= 1) {
724 "Recv IGMP packet with invalid ttl=%u, discarding the packet",
733 int pim_igmp_packet(struct gm_sock
*igmp
, char *buf
, size_t len
)
735 struct ip
*ip_hdr
= (struct ip
*)buf
;
736 size_t ip_hlen
; /* ip header length in bytes */
740 char from_str
[INET_ADDRSTRLEN
];
741 char to_str
[INET_ADDRSTRLEN
];
743 if (!pim_igmp_verify_header(ip_hdr
, len
, &ip_hlen
))
746 igmp_msg
= buf
+ ip_hlen
;
747 igmp_msg_len
= len
- ip_hlen
;
748 msg_type
= *igmp_msg
;
750 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
751 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
753 if (PIM_DEBUG_IGMP_PACKETS
) {
755 "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
756 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hdr
->ip_ttl
,
757 msg_type
, igmp_msg_len
);
761 case PIM_IGMP_MEMBERSHIP_QUERY
: {
762 int max_resp_code
= igmp_msg
[1];
766 RFC 3376: 7.1. Query Version Distinctions
767 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
769 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
771 IGMPv3 Query: length >= 12 octets
774 if (igmp_msg_len
== 8) {
775 query_version
= max_resp_code
? 2 : 1;
776 } else if (igmp_msg_len
>= 12) {
779 zlog_warn("Unknown IGMP query version");
783 return igmp_recv_query(igmp
, query_version
, max_resp_code
,
784 ip_hdr
->ip_src
, from_str
, igmp_msg
,
788 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
789 return igmp_v3_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
790 igmp_msg
, igmp_msg_len
);
792 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
793 return igmp_v2_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
794 igmp_msg
, igmp_msg_len
);
796 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
797 return igmp_v1_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
798 igmp_msg
, igmp_msg_len
);
800 case PIM_IGMP_V2_LEAVE_GROUP
:
801 return igmp_v2_recv_leave(igmp
, ip_hdr
, from_str
, igmp_msg
,
804 case PIM_IGMP_MTRACE_RESPONSE
:
805 return igmp_mtrace_recv_response(igmp
, ip_hdr
, ip_hdr
->ip_src
,
808 case PIM_IGMP_MTRACE_QUERY_REQUEST
:
809 return igmp_mtrace_recv_qry_req(igmp
, ip_hdr
, ip_hdr
->ip_src
,
814 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
816 /* Collecting IGMP Rx stats */
817 igmp
->igmp_stats
.unsupported
++;
822 void pim_igmp_general_query_on(struct gm_sock
*igmp
)
824 struct pim_interface
*pim_ifp
;
829 Since this socket is starting as querier,
830 there should not exist a timer for other-querier-present.
832 assert(!igmp
->t_other_querier_timer
);
833 pim_ifp
= igmp
->interface
->info
;
837 RFC 3376: 8.6. Startup Query Interval
839 The Startup Query Interval is the interval between General Queries
840 sent by a Querier on startup. Default: 1/4 the Query Interval.
841 The first one should be sent out immediately instead of 125/4
844 startup_mode
= igmp
->startup_query_count
> 0;
847 * If this is the first time we are sending a query on a
848 * newly configured igmp interface send it out in 1 second
849 * just to give the entire world a tiny bit of time to settle
850 * else the query interval is:
851 * query_interval = pim_ifp->gm_default_query_interval >> 2;
853 if (igmp
->startup_query_count
==
854 igmp
->querier_robustness_variable
)
857 query_interval
= PIM_IGMP_SQI(
858 pim_ifp
->gm_default_query_interval
);
860 --igmp
->startup_query_count
;
862 query_interval
= igmp
->querier_query_interval
;
865 if (PIM_DEBUG_IGMP_TRACE
) {
866 char ifaddr_str
[INET_ADDRSTRLEN
];
867 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
870 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
871 ifaddr_str
, query_interval
,
872 startup_mode
? "startup" : "non-startup", igmp
->fd
);
874 thread_add_timer(router
->master
, pim_igmp_general_query
, igmp
,
875 query_interval
, &igmp
->t_igmp_query_timer
);
878 void pim_igmp_general_query_off(struct gm_sock
*igmp
)
882 if (PIM_DEBUG_IGMP_TRACE
) {
883 if (igmp
->t_igmp_query_timer
) {
884 char ifaddr_str
[INET_ADDRSTRLEN
];
885 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
888 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
889 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
892 THREAD_OFF(igmp
->t_igmp_query_timer
);
895 /* Issue IGMP general query */
896 static void pim_igmp_general_query(struct thread
*t
)
898 struct gm_sock
*igmp
;
899 struct in_addr dst_addr
;
900 struct in_addr group_addr
;
901 struct pim_interface
*pim_ifp
;
904 igmp
= THREAD_ARG(t
);
906 assert(igmp
->interface
);
907 assert(igmp
->interface
->info
);
909 pim_ifp
= igmp
->interface
->info
;
911 if (pim_ifp
->igmp_version
== 3) {
912 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
914 query_buf_size
= IGMP_V12_MSG_SIZE
;
917 char query_buf
[query_buf_size
];
920 RFC3376: 4.1.12. IP Destination Addresses for Queries
922 In IGMPv3, General Queries are sent with an IP destination address
923 of 224.0.0.1, the all-systems multicast address. Group-Specific
924 and Group-and-Source-Specific Queries are sent with an IP
925 destination address equal to the multicast address of interest.
928 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
929 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
931 if (PIM_DEBUG_IGMP_TRACE
) {
932 char querier_str
[INET_ADDRSTRLEN
];
933 char dst_str
[INET_ADDRSTRLEN
];
934 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
935 sizeof(querier_str
));
936 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
937 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
938 querier_str
, dst_str
, igmp
->interface
->name
);
941 igmp_send_query(pim_ifp
->igmp_version
, 0 /* igmp_group */, query_buf
,
942 sizeof(query_buf
), 0 /* num_sources */, dst_addr
,
943 group_addr
, pim_ifp
->gm_query_max_response_time_dsec
,
944 1 /* s_flag: always set for general queries */, igmp
);
946 pim_igmp_general_query_on(igmp
);
949 static void sock_close(struct gm_sock
*igmp
)
951 pim_igmp_other_querier_timer_off(igmp
);
952 pim_igmp_general_query_off(igmp
);
954 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
955 if (igmp
->t_igmp_read
) {
957 "Cancelling READ event on IGMP socket %pI4 fd=%d on interface %s",
958 &igmp
->ifaddr
, igmp
->fd
,
959 igmp
->interface
->name
);
962 THREAD_OFF(igmp
->t_igmp_read
);
964 if (close(igmp
->fd
)) {
967 "Failure closing IGMP socket %pI4 fd=%d on interface %s: errno=%d: %s",
968 &igmp
->ifaddr
, igmp
->fd
,
969 igmp
->interface
->name
, errno
, safe_strerror(errno
));
972 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
973 zlog_debug("Deleted IGMP socket %pI4 fd=%d on interface %s",
974 &igmp
->ifaddr
, igmp
->fd
,
975 igmp
->interface
->name
);
979 void igmp_startup_mode_on(struct gm_sock
*igmp
)
981 struct pim_interface
*pim_ifp
;
983 pim_ifp
= igmp
->interface
->info
;
986 RFC 3376: 8.7. Startup Query Count
988 The Startup Query Count is the number of Queries sent out on
989 startup, separated by the Startup Query Interval. Default: the
992 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
995 Since we're (re)starting, reset QQI to default Query Interval
997 igmp
->querier_query_interval
= pim_ifp
->gm_default_query_interval
;
1000 static void igmp_group_free(struct gm_group
*group
)
1002 list_delete(&group
->group_source_list
);
1004 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
1007 static void igmp_group_count_incr(struct pim_interface
*pim_ifp
)
1009 uint32_t group_count
= listcount(pim_ifp
->gm_group_list
);
1011 ++pim_ifp
->pim
->gm_group_count
;
1012 if (pim_ifp
->pim
->gm_group_count
== pim_ifp
->pim
->gm_watermark_limit
) {
1014 "IGMP group count reached watermark limit: %u(vrf: %s)",
1015 pim_ifp
->pim
->gm_group_count
,
1016 VRF_LOGNAME(pim_ifp
->pim
->vrf
));
1019 if (pim_ifp
->igmp_peak_group_count
< group_count
)
1020 pim_ifp
->igmp_peak_group_count
= group_count
;
1023 static void igmp_group_count_decr(struct pim_interface
*pim_ifp
)
1025 if (pim_ifp
->pim
->gm_group_count
== 0) {
1026 zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
1027 VRF_LOGNAME(pim_ifp
->pim
->vrf
));
1031 --pim_ifp
->pim
->gm_group_count
;
1034 void igmp_group_delete(struct gm_group
*group
)
1036 struct listnode
*src_node
;
1037 struct listnode
*src_nextnode
;
1038 struct gm_source
*src
;
1039 struct pim_interface
*pim_ifp
= group
->interface
->info
;
1041 if (PIM_DEBUG_IGMP_TRACE
) {
1042 char group_str
[INET_ADDRSTRLEN
];
1043 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1045 zlog_debug("Deleting IGMP group %s from interface %s",
1046 group_str
, group
->interface
->name
);
1049 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
,
1051 igmp_source_delete(src
);
1054 THREAD_OFF(group
->t_group_query_retransmit_timer
);
1056 group_timer_off(group
);
1057 igmp_group_count_decr(pim_ifp
);
1058 listnode_delete(pim_ifp
->gm_group_list
, group
);
1059 hash_release(pim_ifp
->gm_group_hash
, group
);
1061 igmp_group_free(group
);
1064 void igmp_group_delete_empty_include(struct gm_group
*group
)
1066 assert(!group
->group_filtermode_isexcl
);
1067 assert(!listcount(group
->group_source_list
));
1069 igmp_group_delete(group
);
1072 void igmp_sock_free(struct gm_sock
*igmp
)
1074 assert(!igmp
->t_igmp_read
);
1075 assert(!igmp
->t_igmp_query_timer
);
1076 assert(!igmp
->t_other_querier_timer
);
1078 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
1081 void igmp_sock_delete(struct gm_sock
*igmp
)
1083 struct pim_interface
*pim_ifp
;
1087 pim_ifp
= igmp
->interface
->info
;
1089 listnode_delete(pim_ifp
->gm_socket_list
, igmp
);
1091 igmp_sock_free(igmp
);
1093 if (!listcount(pim_ifp
->gm_socket_list
))
1094 pim_igmp_if_reset(pim_ifp
);
1097 void igmp_sock_delete_all(struct interface
*ifp
)
1099 struct pim_interface
*pim_ifp
;
1100 struct listnode
*igmp_node
, *igmp_nextnode
;
1101 struct gm_sock
*igmp
;
1103 pim_ifp
= ifp
->info
;
1105 for (ALL_LIST_ELEMENTS(pim_ifp
->gm_socket_list
, igmp_node
,
1106 igmp_nextnode
, igmp
)) {
1107 igmp_sock_delete(igmp
);
1111 static unsigned int igmp_group_hash_key(const void *arg
)
1113 const struct gm_group
*group
= arg
;
1115 return jhash_1word(group
->group_addr
.s_addr
, 0);
1118 static bool igmp_group_hash_equal(const void *arg1
, const void *arg2
)
1120 const struct gm_group
*g1
= (const struct gm_group
*)arg1
;
1121 const struct gm_group
*g2
= (const struct gm_group
*)arg2
;
1123 if (g1
->group_addr
.s_addr
== g2
->group_addr
.s_addr
)
1129 void pim_igmp_if_init(struct pim_interface
*pim_ifp
, struct interface
*ifp
)
1133 pim_ifp
->gm_socket_list
= list_new();
1134 pim_ifp
->gm_socket_list
->del
= (void (*)(void *))igmp_sock_free
;
1136 pim_ifp
->gm_group_list
= list_new();
1137 pim_ifp
->gm_group_list
->del
= (void (*)(void *))igmp_group_free
;
1139 snprintf(hash_name
, sizeof(hash_name
), "IGMP %s hash", ifp
->name
);
1140 pim_ifp
->gm_group_hash
= hash_create(igmp_group_hash_key
,
1141 igmp_group_hash_equal
, hash_name
);
1144 void pim_igmp_if_reset(struct pim_interface
*pim_ifp
)
1146 struct listnode
*grp_node
, *grp_nextnode
;
1147 struct gm_group
*grp
;
1149 for (ALL_LIST_ELEMENTS(pim_ifp
->gm_group_list
, grp_node
, grp_nextnode
,
1151 igmp_group_delete(grp
);
1155 void pim_igmp_if_fini(struct pim_interface
*pim_ifp
)
1157 pim_igmp_if_reset(pim_ifp
);
1159 assert(pim_ifp
->gm_group_list
);
1160 assert(!listcount(pim_ifp
->gm_group_list
));
1162 list_delete(&pim_ifp
->gm_group_list
);
1163 hash_free(pim_ifp
->gm_group_hash
);
1165 list_delete(&pim_ifp
->gm_socket_list
);
1168 static struct gm_sock
*igmp_sock_new(int fd
, struct in_addr ifaddr
,
1169 struct interface
*ifp
, int mtrace_only
)
1171 struct pim_interface
*pim_ifp
;
1172 struct gm_sock
*igmp
;
1174 pim_ifp
= ifp
->info
;
1176 if (PIM_DEBUG_IGMP_TRACE
) {
1178 "Creating IGMP socket fd=%d for address %pI4 on interface %s",
1179 fd
, &ifaddr
, ifp
->name
);
1182 igmp
= XCALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
1185 igmp
->interface
= ifp
;
1186 igmp
->ifaddr
= ifaddr
;
1187 igmp
->querier_addr
= ifaddr
;
1188 igmp
->t_igmp_read
= NULL
;
1189 igmp
->t_igmp_query_timer
= NULL
;
1190 igmp
->t_other_querier_timer
= NULL
; /* no other querier present */
1191 igmp
->querier_robustness_variable
=
1192 pim_ifp
->gm_default_robustness_variable
;
1193 igmp
->sock_creation
= pim_time_monotonic_sec();
1195 igmp_stats_init(&igmp
->igmp_stats
);
1198 igmp
->mtrace_only
= mtrace_only
;
1202 igmp
->mtrace_only
= false;
1205 igmp_startup_mode_on() will reset QQI:
1207 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
1209 igmp_startup_mode_on(igmp
);
1210 pim_igmp_general_query_on(igmp
);
1215 static void igmp_read_on(struct gm_sock
*igmp
);
1217 static void pim_igmp_read(struct thread
*t
)
1220 struct gm_sock
*igmp
= (struct gm_sock
*)THREAD_ARG(t
);
1221 struct sockaddr_storage from
;
1222 struct sockaddr_storage to
;
1223 socklen_t fromlen
= sizeof(from
);
1224 socklen_t tolen
= sizeof(to
);
1225 ifindex_t ifindex
= -1;
1229 len
= pim_socket_recvfromto(igmp
->fd
, buf
, sizeof(buf
), &from
,
1230 &fromlen
, &to
, &tolen
, &ifindex
);
1234 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
1245 static void igmp_read_on(struct gm_sock
*igmp
)
1248 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
1249 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
1252 thread_add_read(router
->master
, pim_igmp_read
, igmp
, igmp
->fd
,
1253 &igmp
->t_igmp_read
);
1256 struct gm_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
1257 struct in_addr ifaddr
, struct interface
*ifp
,
1260 struct gm_sock
*igmp
;
1261 struct sockaddr_in sin
;
1264 fd
= igmp_sock_open(ifaddr
, ifp
);
1266 zlog_warn("Could not open IGMP socket for %pI4 on %s",
1267 &ifaddr
, ifp
->name
);
1271 sin
.sin_family
= AF_INET
;
1272 sin
.sin_addr
= ifaddr
;
1274 if (bind(fd
, (struct sockaddr
*) &sin
, sizeof(sin
)) != 0) {
1275 zlog_warn("Could not bind IGMP socket for %pI4 on %s: %s(%d)",
1276 &ifaddr
, ifp
->name
, strerror(errno
), errno
);
1282 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
, mtrace_only
);
1286 listnode_add(igmp_sock_list
, igmp
);
1288 #ifdef IGMP_SOCK_DUMP
1289 igmp_sock_dump(igmp_sock_array
);
1296 RFC 3376: 6.5. Switching Router Filter-Modes
1298 When a router's filter-mode for a group is EXCLUDE and the group
1299 timer expires, the router filter-mode for the group transitions to
1302 A router uses source records with running source timers as its state
1303 for the switch to a filter-mode of INCLUDE. If there are any source
1304 records with source timers greater than zero (i.e., requested to be
1305 forwarded), a router switches to filter-mode of INCLUDE using those
1306 source records. Source records whose timers are zero (from the
1307 previous EXCLUDE mode) are deleted.
1309 static void igmp_group_timer(struct thread
*t
)
1311 struct gm_group
*group
;
1313 group
= THREAD_ARG(t
);
1315 if (PIM_DEBUG_IGMP_TRACE
) {
1316 char group_str
[INET_ADDRSTRLEN
];
1317 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1319 zlog_debug("%s: Timer for group %s on interface %s", __func__
,
1320 group_str
, group
->interface
->name
);
1323 assert(group
->group_filtermode_isexcl
);
1325 group
->group_filtermode_isexcl
= 0;
1327 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1328 igmp_anysource_forward_stop(group
);
1330 igmp_source_delete_expired(group
->group_source_list
);
1332 assert(!group
->group_filtermode_isexcl
);
1335 RFC 3376: 6.2.2. Definition of Group Timers
1337 If there are no more source records for the group, delete group
1340 if (listcount(group
->group_source_list
) < 1) {
1341 igmp_group_delete_empty_include(group
);
1345 static void group_timer_off(struct gm_group
*group
)
1347 if (!group
->t_group_timer
)
1350 if (PIM_DEBUG_IGMP_TRACE
) {
1351 char group_str
[INET_ADDRSTRLEN
];
1352 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1354 zlog_debug("Cancelling TIMER event for group %s on %s",
1355 group_str
, group
->interface
->name
);
1357 THREAD_OFF(group
->t_group_timer
);
1360 void igmp_group_timer_on(struct gm_group
*group
, long interval_msec
,
1363 group_timer_off(group
);
1365 if (PIM_DEBUG_IGMP_EVENTS
) {
1366 char group_str
[INET_ADDRSTRLEN
];
1367 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1370 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1371 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
1376 RFC 3376: 6.2.2. Definition of Group Timers
1378 The group timer is only used when a group is in EXCLUDE mode and
1379 it represents the time for the *filter-mode* of the group to
1380 expire and switch to INCLUDE mode.
1382 assert(group
->group_filtermode_isexcl
);
1384 thread_add_timer_msec(router
->master
, igmp_group_timer
, group
,
1385 interval_msec
, &group
->t_group_timer
);
1388 struct gm_group
*find_group_by_addr(struct gm_sock
*igmp
,
1389 struct in_addr group_addr
)
1391 struct gm_group lookup
;
1392 struct pim_interface
*pim_ifp
= igmp
->interface
->info
;
1394 lookup
.group_addr
.s_addr
= group_addr
.s_addr
;
1396 return hash_lookup(pim_ifp
->gm_group_hash
, &lookup
);
1399 struct gm_group
*igmp_add_group_by_addr(struct gm_sock
*igmp
,
1400 struct in_addr group_addr
)
1402 struct gm_group
*group
;
1403 struct pim_interface
*pim_ifp
= igmp
->interface
->info
;
1405 group
= find_group_by_addr(igmp
, group_addr
);
1410 if (!pim_is_group_224_4(group_addr
)) {
1411 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1416 if (pim_is_group_224_0_0_0_24(group_addr
)) {
1417 if (PIM_DEBUG_IGMP_TRACE
)
1419 "%s: Group specified %pI4 is part of 224.0.0.0/24",
1420 __func__
, &group_addr
);
1424 Non-existant group is created as INCLUDE {empty}:
1426 RFC 3376 - 5.1. Action on Change of Interface State
1428 If no interface state existed for that multicast address before
1429 the change (i.e., the change consisted of creating a new
1430 per-interface record), or if no state exists after the change
1431 (i.e., the change consisted of deleting a per-interface record),
1432 then the "non-existent" state is considered to have a filter mode
1433 of INCLUDE and an empty source list.
1436 group
= XCALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1438 group
->group_source_list
= list_new();
1439 group
->group_source_list
->del
= (void (*)(void *))igmp_source_free
;
1441 group
->t_group_timer
= NULL
;
1442 group
->t_group_query_retransmit_timer
= NULL
;
1443 group
->group_specific_query_retransmit_count
= 0;
1444 group
->group_addr
= group_addr
;
1445 group
->interface
= igmp
->interface
;
1446 group
->last_igmp_v1_report_dsec
= -1;
1447 group
->last_igmp_v2_report_dsec
= -1;
1448 group
->group_creation
= pim_time_monotonic_sec();
1449 group
->igmp_version
= IGMP_DEFAULT_VERSION
;
1451 /* initialize new group as INCLUDE {empty} */
1452 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1454 listnode_add(pim_ifp
->gm_group_list
, group
);
1455 group
= hash_get(pim_ifp
->gm_group_hash
, group
, hash_alloc_intern
);
1457 if (PIM_DEBUG_IGMP_TRACE
) {
1458 char group_str
[INET_ADDRSTRLEN
];
1459 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1462 "Creating new IGMP group %s on socket %d interface %s",
1463 group_str
, igmp
->fd
, igmp
->interface
->name
);
1466 igmp_group_count_incr(pim_ifp
);
1469 RFC 3376: 6.2.2. Definition of Group Timers
1471 The group timer is only used when a group is in EXCLUDE mode and
1472 it represents the time for the *filter-mode* of the group to
1473 expire and switch to INCLUDE mode.
1475 assert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1476 assert(!group
->t_group_timer
); /* group timer == 0 */
1478 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1479 igmp_anysource_forward_stop(group
);
1484 void igmp_send_query(int igmp_version
, struct gm_group
*group
, char *query_buf
,
1485 int query_buf_size
, int num_sources
,
1486 struct in_addr dst_addr
, struct in_addr group_addr
,
1487 int query_max_response_time_dsec
, uint8_t s_flag
,
1488 struct gm_sock
*igmp
)
1490 if (pim_addr_is_any(group_addr
) &&
1491 ntohl(dst_addr
.s_addr
) == INADDR_ALLHOSTS_GROUP
)
1492 igmp
->igmp_stats
.general_queries_sent
++;
1494 igmp
->igmp_stats
.group_queries_sent
++;
1496 if (igmp_version
== 3) {
1497 igmp_v3_send_query(group
, igmp
->fd
, igmp
->interface
->name
,
1498 query_buf
, query_buf_size
, num_sources
,
1499 dst_addr
, group_addr
,
1500 query_max_response_time_dsec
, s_flag
,
1501 igmp
->querier_robustness_variable
,
1502 igmp
->querier_query_interval
);
1503 } else if (igmp_version
== 2) {
1504 igmp_v2_send_query(group
, igmp
->fd
, igmp
->interface
->name
,
1505 query_buf
, dst_addr
, group_addr
,
1506 query_max_response_time_dsec
);
1510 void igmp_send_query_on_intf(struct interface
*ifp
, int igmp_ver
)
1512 struct pim_interface
*pim_ifp
= ifp
->info
;
1513 struct listnode
*sock_node
= NULL
;
1514 struct gm_sock
*igmp
= NULL
;
1515 struct in_addr dst_addr
;
1516 struct in_addr group_addr
;
1523 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1525 query_buf_size
= IGMP_V12_MSG_SIZE
;
1527 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
1528 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
1530 if (PIM_DEBUG_IGMP_TRACE
)
1531 zlog_debug("Issuing general query on request on %s", ifp
->name
);
1533 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_socket_list
, sock_node
, igmp
)) {
1535 char query_buf
[query_buf_size
];
1538 igmp_ver
, 0 /* igmp_group */, query_buf
,
1539 sizeof(query_buf
), 0 /* num_sources */, dst_addr
,
1540 group_addr
, pim_ifp
->gm_query_max_response_time_dsec
,
1541 1 /* s_flag: always set for general queries */, igmp
);