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"
40 #include "pim_zebra.h"
42 static void group_timer_off(struct gm_group
*group
);
43 static int pim_igmp_general_query(struct thread
*t
);
45 /* This socket is used for TXing IGMP packets only, IGMP RX happens
48 static int igmp_sock_open(struct in_addr ifaddr
, struct interface
*ifp
,
55 fd
= pim_socket_mcast(IPPROTO_IGMP
, ifaddr
, ifp
, 1);
60 if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options
)) {
61 if (inet_aton(PIM_ALL_ROUTERS
, &group
)) {
62 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
))
66 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
67 __FILE__
, __func__
, fd
, &ifaddr
,
68 PIM_ALL_ROUTERS
, errno
, safe_strerror(errno
));
73 IGMP routers periodically send IGMP general queries to
75 IGMP routers must receive general queries for querier election.
77 if (inet_aton(PIM_ALL_SYSTEMS
, &group
)) {
78 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
))
82 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
83 __FILE__
, __func__
, fd
, &ifaddr
,
84 PIM_ALL_SYSTEMS
, errno
, safe_strerror(errno
));
87 if (inet_aton(PIM_ALL_IGMP_ROUTERS
, &group
)) {
88 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
)) {
93 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
94 __FILE__
, __func__
, fd
, &ifaddr
,
95 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
101 "IGMP socket fd=%d could not join any group on interface address %pI4",
110 #undef IGMP_SOCK_DUMP
112 #ifdef IGMP_SOCK_DUMP
113 static void igmp_sock_dump(array_t
*igmp_sock_array
)
115 int size
= array_size(igmp_sock_array
);
116 for (int i
= 0; i
< size
; ++i
) {
118 struct gm_sock
*igmp
= array_get(igmp_sock_array
, i
);
120 zlog_debug("%s %s: [%d/%d] igmp_addr=%pI4 fd=%d", __FILE__
,
121 __func__
, i
, size
, &igmp
->ifaddr
,
127 struct gm_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
128 struct in_addr ifaddr
)
130 struct listnode
*sock_node
;
131 struct gm_sock
*igmp
;
133 #ifdef IGMP_SOCK_DUMP
134 igmp_sock_dump(igmp_sock_list
);
137 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
138 if (ifaddr
.s_addr
== igmp
->ifaddr
.s_addr
)
144 static int pim_igmp_other_querier_expire(struct thread
*t
)
146 struct gm_sock
*igmp
;
148 igmp
= THREAD_ARG(t
);
150 assert(!igmp
->t_igmp_query_timer
);
152 if (PIM_DEBUG_IGMP_TRACE
) {
153 char ifaddr_str
[INET_ADDRSTRLEN
];
154 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
156 zlog_debug("%s: Querier %s resuming", __func__
, ifaddr_str
);
158 /* Mark the interface address as querier address */
159 igmp
->querier_addr
= igmp
->ifaddr
;
162 We are the current querier, then
163 re-start sending general queries.
164 RFC 2236 - sec 7 Other Querier
165 present timer expired (Send General
166 Query, Set Gen. Query. timer)
168 pim_igmp_general_query(t
);
173 void pim_igmp_other_querier_timer_on(struct gm_sock
*igmp
)
175 long other_querier_present_interval_msec
;
176 struct pim_interface
*pim_ifp
;
179 assert(igmp
->interface
);
180 assert(igmp
->interface
->info
);
182 pim_ifp
= igmp
->interface
->info
;
184 if (igmp
->t_other_querier_timer
) {
186 There is other querier present already,
187 then reset the other-querier-present timer.
190 if (PIM_DEBUG_IGMP_TRACE
) {
191 char ifaddr_str
[INET_ADDRSTRLEN
];
192 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
195 "Querier %s resetting TIMER event for Other-Querier-Present",
198 THREAD_OFF(igmp
->t_other_querier_timer
);
201 We are the current querier, then stop sending general queries:
202 igmp->t_igmp_query_timer = NULL;
204 pim_igmp_general_query_off(igmp
);
208 Since this socket is starting the other-querier-present timer,
209 there should not be periodic query timer for this socket.
211 assert(!igmp
->t_igmp_query_timer
);
214 RFC 3376: 8.5. Other Querier Present Interval
216 The Other Querier Present Interval is the length of time that must
217 pass before a multicast router decides that there is no longer
218 another multicast router which should be the querier. This value
219 MUST be ((the Robustness Variable) times (the Query Interval)) plus
220 (one half of one Query Response Interval).
222 other_querier_present_interval_msec = \
223 igmp->querier_robustness_variable * \
224 1000 * igmp->querier_query_interval + \
225 100 * (pim_ifp->query_max_response_time_dsec >> 1);
227 other_querier_present_interval_msec
= PIM_IGMP_OQPI_MSEC(
228 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
229 pim_ifp
->gm_query_max_response_time_dsec
);
231 if (PIM_DEBUG_IGMP_TRACE
) {
232 char ifaddr_str
[INET_ADDRSTRLEN
];
233 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
236 "Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
237 ifaddr_str
, other_querier_present_interval_msec
/ 1000,
238 other_querier_present_interval_msec
% 1000);
241 thread_add_timer_msec(router
->master
, pim_igmp_other_querier_expire
,
242 igmp
, other_querier_present_interval_msec
,
243 &igmp
->t_other_querier_timer
);
246 void pim_igmp_other_querier_timer_off(struct gm_sock
*igmp
)
250 if (PIM_DEBUG_IGMP_TRACE
) {
251 if (igmp
->t_other_querier_timer
) {
252 char ifaddr_str
[INET_ADDRSTRLEN
];
253 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
256 "IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
257 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
260 THREAD_OFF(igmp
->t_other_querier_timer
);
263 int igmp_validate_checksum(char *igmp_msg
, int igmp_msg_len
)
265 uint16_t recv_checksum
;
268 IGMP_GET_INT16((unsigned char *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
),
271 /* Clear the checksum field */
272 memset(igmp_msg
+ IGMP_CHECKSUM_OFFSET
, 0, 2);
274 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
275 if (ntohs(checksum
) != recv_checksum
) {
276 zlog_warn("Invalid checksum received %x, calculated %x",
277 recv_checksum
, ntohs(checksum
));
284 static int igmp_recv_query(struct gm_sock
*igmp
, int query_version
,
285 int max_resp_code
, struct in_addr from
,
286 const char *from_str
, char *igmp_msg
,
289 struct interface
*ifp
;
290 struct pim_interface
*pim_ifp
;
291 struct in_addr group_addr
;
293 if (igmp
->mtrace_only
)
296 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
298 ifp
= igmp
->interface
;
301 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
303 "Recv IGMP query v%d from %s on %s with invalid checksum",
304 query_version
, from_str
, ifp
->name
);
308 if (!pim_if_connected_to_source(ifp
, from
)) {
309 if (PIM_DEBUG_IGMP_PACKETS
)
310 zlog_debug("Recv IGMP query on interface: %s from a non-connected source: %s",
311 ifp
->name
, from_str
);
315 if (if_address_is_local(&from
, AF_INET
, ifp
->vrf
->vrf_id
)) {
316 if (PIM_DEBUG_IGMP_PACKETS
)
317 zlog_debug("Recv IGMP query on interface: %s from ourself %s",
318 ifp
->name
, from_str
);
322 /* Collecting IGMP Rx stats */
323 switch (query_version
) {
325 igmp
->rx_stats
.query_v1
++;
328 igmp
->rx_stats
.query_v2
++;
331 igmp
->rx_stats
.query_v3
++;
334 igmp
->rx_stats
.unsupported
++;
338 * RFC 3376 defines some guidelines on operating in backwards
339 * compatibility with older versions of IGMP but there are some gaps in
342 * - once we drop from say version 3 to version 2 we will never go back
343 * to version 3 even if the node that TXed an IGMP v2 query upgrades
346 * - The node with the lowest IP is the querier so we will only know to
347 * drop from v3 to v2 if the node that is the querier is also the one
348 * that is running igmp v2. If a non-querier only supports igmp v2
349 * we will have no way of knowing.
351 * For now we will simplify things and inform the user that they need to
352 * configure all PIM routers to use the same version of IGMP.
354 if (query_version
!= pim_ifp
->igmp_version
) {
356 "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",
357 query_version
, from_str
, ifp
->name
,
358 pim_ifp
->igmp_version
);
362 if (PIM_DEBUG_IGMP_PACKETS
) {
363 char group_str
[INET_ADDRSTRLEN
];
364 pim_inet4_dump("<group?>", group_addr
, group_str
,
366 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
367 query_version
, from_str
, ifp
->name
, group_str
);
371 RFC 3376: 6.6.2. Querier Election
373 When a router receives a query with a lower IP address, it sets
374 the Other-Querier-Present timer to Other Querier Present Interval
375 and ceases to send queries on the network if it was the previously
378 if (ntohl(from
.s_addr
) < ntohl(igmp
->ifaddr
.s_addr
)) {
380 if (PIM_DEBUG_IGMP_TRACE
) {
381 char ifaddr_str
[INET_ADDRSTRLEN
];
382 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
385 "%s: local address %s (%u) lost querier election to %s (%u)",
386 ifp
->name
, ifaddr_str
,
387 ntohl(igmp
->ifaddr
.s_addr
), from_str
,
390 if (ntohl(from
.s_addr
) < ntohl(igmp
->querier_addr
.s_addr
))
391 igmp
->querier_addr
.s_addr
= from
.s_addr
;
393 pim_igmp_other_querier_timer_on(igmp
);
396 /* IGMP version 3 is the only one where we process the RXed query */
397 if (query_version
== 3) {
398 igmp_v3_recv_query(igmp
, from_str
, igmp_msg
);
404 static void on_trace(const char *label
, struct interface
*ifp
,
407 if (PIM_DEBUG_IGMP_TRACE
) {
408 char from_str
[INET_ADDRSTRLEN
];
409 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
410 zlog_debug("%s: from %s on %s", label
, from_str
, ifp
->name
);
414 static int igmp_v1_recv_report(struct gm_sock
*igmp
, struct in_addr from
,
415 const char *from_str
, char *igmp_msg
,
418 struct interface
*ifp
= igmp
->interface
;
419 struct gm_group
*group
;
420 struct in_addr group_addr
;
422 on_trace(__func__
, igmp
->interface
, from
);
424 if (igmp
->mtrace_only
)
427 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
429 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
430 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
434 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
436 "Recv IGMP report v1 from %s on %s with invalid checksum",
437 from_str
, ifp
->name
);
441 /* Collecting IGMP Rx stats */
442 igmp
->rx_stats
.report_v1
++;
444 if (PIM_DEBUG_IGMP_TRACE
) {
445 zlog_warn("%s %s: FIXME WRITEME", __FILE__
, __func__
);
448 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
450 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
453 /* non-existant group is created as INCLUDE {empty} */
454 group
= igmp_add_group_by_addr(igmp
, group_addr
);
459 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
464 bool pim_igmp_verify_header(struct ip
*ip_hdr
, size_t len
, size_t *hlen
)
469 size_t ip_hlen
; /* ip header length in bytes */
471 if (len
< sizeof(*ip_hdr
)) {
472 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len
,
477 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
482 "IGMP packet header claims size %zu, but we only have %zu bytes",
487 igmp_msg
= (char *)ip_hdr
+ ip_hlen
;
488 igmp_msg_len
= len
- ip_hlen
;
489 msg_type
= *igmp_msg
;
491 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
492 zlog_warn("IGMP message size=%d shorter than minimum=%d",
493 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
497 if ((msg_type
!= PIM_IGMP_MTRACE_RESPONSE
)
498 && (msg_type
!= PIM_IGMP_MTRACE_QUERY_REQUEST
)) {
499 if (ip_hdr
->ip_ttl
!= 1) {
501 "Recv IGMP packet with invalid ttl=%u, discarding the packet",
507 if ((msg_type
== PIM_IGMP_V3_MEMBERSHIP_REPORT
)
508 || ((msg_type
== PIM_IGMP_MEMBERSHIP_QUERY
)
509 && (igmp_msg_len
>= IGMP_V3_SOURCES_OFFSET
))) {
510 /* All IGMPv3 messages must be received with TOS set to 0xC0*/
511 if (ip_hdr
->ip_tos
!= IPTOS_PREC_INTERNETCONTROL
) {
512 zlog_warn("Received IGMP Packet with invalid TOS %u",
521 int pim_igmp_packet(struct gm_sock
*igmp
, char *buf
, size_t len
)
523 struct ip
*ip_hdr
= (struct ip
*)buf
;
524 size_t ip_hlen
; /* ip header length in bytes */
528 char from_str
[INET_ADDRSTRLEN
];
529 char to_str
[INET_ADDRSTRLEN
];
531 if (!pim_igmp_verify_header(ip_hdr
, len
, &ip_hlen
))
534 igmp_msg
= buf
+ ip_hlen
;
535 igmp_msg_len
= len
- ip_hlen
;
536 msg_type
= *igmp_msg
;
538 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
539 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
541 if (PIM_DEBUG_IGMP_PACKETS
) {
543 "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
544 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hdr
->ip_ttl
,
545 msg_type
, igmp_msg_len
);
549 case PIM_IGMP_MEMBERSHIP_QUERY
: {
550 int max_resp_code
= igmp_msg
[1];
554 RFC 3376: 7.1. Query Version Distinctions
555 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
557 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
559 IGMPv3 Query: length >= 12 octets
562 if (igmp_msg_len
== 8) {
563 query_version
= max_resp_code
? 2 : 1;
564 } else if (igmp_msg_len
>= 12) {
567 zlog_warn("Unknown IGMP query version");
571 return igmp_recv_query(igmp
, query_version
, max_resp_code
,
572 ip_hdr
->ip_src
, from_str
, igmp_msg
,
576 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
577 return igmp_v3_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
578 igmp_msg
, igmp_msg_len
);
580 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
581 return igmp_v2_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
582 igmp_msg
, igmp_msg_len
);
584 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
585 return igmp_v1_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
586 igmp_msg
, igmp_msg_len
);
588 case PIM_IGMP_V2_LEAVE_GROUP
:
589 return igmp_v2_recv_leave(igmp
, ip_hdr
, from_str
, igmp_msg
,
592 case PIM_IGMP_MTRACE_RESPONSE
:
593 return igmp_mtrace_recv_response(igmp
, ip_hdr
, ip_hdr
->ip_src
,
596 case PIM_IGMP_MTRACE_QUERY_REQUEST
:
597 return igmp_mtrace_recv_qry_req(igmp
, ip_hdr
, ip_hdr
->ip_src
,
602 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
604 /* Collecting IGMP Rx stats */
605 igmp
->rx_stats
.unsupported
++;
610 void pim_igmp_general_query_on(struct gm_sock
*igmp
)
612 struct pim_interface
*pim_ifp
;
617 Since this socket is starting as querier,
618 there should not exist a timer for other-querier-present.
620 assert(!igmp
->t_other_querier_timer
);
621 pim_ifp
= igmp
->interface
->info
;
625 RFC 3376: 8.6. Startup Query Interval
627 The Startup Query Interval is the interval between General Queries
628 sent by a Querier on startup. Default: 1/4 the Query Interval.
629 The first one should be sent out immediately instead of 125/4
632 startup_mode
= igmp
->startup_query_count
> 0;
635 * If this is the first time we are sending a query on a
636 * newly configured igmp interface send it out in 1 second
637 * just to give the entire world a tiny bit of time to settle
638 * else the query interval is:
639 * query_interval = pim_ifp->gm_default_query_interval >> 2;
641 if (igmp
->startup_query_count
==
642 igmp
->querier_robustness_variable
)
645 query_interval
= PIM_IGMP_SQI(
646 pim_ifp
->gm_default_query_interval
);
648 --igmp
->startup_query_count
;
650 query_interval
= igmp
->querier_query_interval
;
653 if (PIM_DEBUG_IGMP_TRACE
) {
654 char ifaddr_str
[INET_ADDRSTRLEN
];
655 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
658 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
659 ifaddr_str
, query_interval
,
660 startup_mode
? "startup" : "non-startup", igmp
->fd
);
662 thread_add_timer(router
->master
, pim_igmp_general_query
, igmp
,
663 query_interval
, &igmp
->t_igmp_query_timer
);
666 void pim_igmp_general_query_off(struct gm_sock
*igmp
)
670 if (PIM_DEBUG_IGMP_TRACE
) {
671 if (igmp
->t_igmp_query_timer
) {
672 char ifaddr_str
[INET_ADDRSTRLEN
];
673 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
676 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
677 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
680 THREAD_OFF(igmp
->t_igmp_query_timer
);
683 /* Issue IGMP general query */
684 static int pim_igmp_general_query(struct thread
*t
)
686 struct gm_sock
*igmp
;
687 struct in_addr dst_addr
;
688 struct in_addr group_addr
;
689 struct pim_interface
*pim_ifp
;
692 igmp
= THREAD_ARG(t
);
694 assert(igmp
->interface
);
695 assert(igmp
->interface
->info
);
697 pim_ifp
= igmp
->interface
->info
;
699 if (pim_ifp
->igmp_version
== 3) {
700 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
702 query_buf_size
= IGMP_V12_MSG_SIZE
;
705 char query_buf
[query_buf_size
];
708 RFC3376: 4.1.12. IP Destination Addresses for Queries
710 In IGMPv3, General Queries are sent with an IP destination address
711 of 224.0.0.1, the all-systems multicast address. Group-Specific
712 and Group-and-Source-Specific Queries are sent with an IP
713 destination address equal to the multicast address of interest.
716 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
717 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
719 if (PIM_DEBUG_IGMP_TRACE
) {
720 char querier_str
[INET_ADDRSTRLEN
];
721 char dst_str
[INET_ADDRSTRLEN
];
722 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
723 sizeof(querier_str
));
724 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
725 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
726 querier_str
, dst_str
, igmp
->interface
->name
);
729 igmp_send_query(pim_ifp
->igmp_version
, 0 /* igmp_group */, igmp
->fd
,
730 igmp
->interface
->name
, query_buf
, sizeof(query_buf
),
731 0 /* num_sources */, dst_addr
, group_addr
,
732 pim_ifp
->gm_query_max_response_time_dsec
,
733 1 /* s_flag: always set for general queries */,
734 igmp
->querier_robustness_variable
,
735 igmp
->querier_query_interval
);
737 pim_igmp_general_query_on(igmp
);
742 static void sock_close(struct gm_sock
*igmp
)
744 pim_igmp_other_querier_timer_off(igmp
);
745 pim_igmp_general_query_off(igmp
);
747 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
748 if (igmp
->t_igmp_read
) {
750 "Cancelling READ event on IGMP socket %pI4 fd=%d on interface %s",
751 &igmp
->ifaddr
, igmp
->fd
,
752 igmp
->interface
->name
);
755 THREAD_OFF(igmp
->t_igmp_read
);
757 if (close(igmp
->fd
)) {
760 "Failure closing IGMP socket %pI4 fd=%d on interface %s: errno=%d: %s",
761 &igmp
->ifaddr
, igmp
->fd
,
762 igmp
->interface
->name
, errno
, safe_strerror(errno
));
765 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
766 zlog_debug("Deleted IGMP socket %pI4 fd=%d on interface %s",
767 &igmp
->ifaddr
, igmp
->fd
,
768 igmp
->interface
->name
);
772 void igmp_startup_mode_on(struct gm_sock
*igmp
)
774 struct pim_interface
*pim_ifp
;
776 pim_ifp
= igmp
->interface
->info
;
779 RFC 3376: 8.7. Startup Query Count
781 The Startup Query Count is the number of Queries sent out on
782 startup, separated by the Startup Query Interval. Default: the
785 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
788 Since we're (re)starting, reset QQI to default Query Interval
790 igmp
->querier_query_interval
= pim_ifp
->gm_default_query_interval
;
793 static void igmp_group_free(struct gm_group
*group
)
795 list_delete(&group
->group_source_list
);
797 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
800 static void igmp_group_count_incr(struct pim_interface
*pim_ifp
)
802 ++pim_ifp
->pim
->igmp_group_count
;
803 if (pim_ifp
->pim
->igmp_group_count
804 == pim_ifp
->pim
->igmp_watermark_limit
) {
806 "IGMP group count reached watermark limit: %u(vrf: %s)",
807 pim_ifp
->pim
->igmp_group_count
,
808 VRF_LOGNAME(pim_ifp
->pim
->vrf
));
812 static void igmp_group_count_decr(struct pim_interface
*pim_ifp
)
814 if (pim_ifp
->pim
->igmp_group_count
== 0) {
815 zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
816 VRF_LOGNAME(pim_ifp
->pim
->vrf
));
820 --pim_ifp
->pim
->igmp_group_count
;
823 void igmp_group_delete(struct gm_group
*group
)
825 struct listnode
*src_node
;
826 struct listnode
*src_nextnode
;
827 struct gm_source
*src
;
828 struct pim_interface
*pim_ifp
= group
->interface
->info
;
830 if (PIM_DEBUG_IGMP_TRACE
) {
831 char group_str
[INET_ADDRSTRLEN
];
832 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
834 zlog_debug("Deleting IGMP group %s from interface %s",
835 group_str
, group
->interface
->name
);
838 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
,
840 igmp_source_delete(src
);
843 THREAD_OFF(group
->t_group_query_retransmit_timer
);
845 group_timer_off(group
);
846 igmp_group_count_decr(pim_ifp
);
847 listnode_delete(pim_ifp
->gm_group_list
, group
);
848 hash_release(pim_ifp
->gm_group_hash
, group
);
850 igmp_group_free(group
);
853 void igmp_group_delete_empty_include(struct gm_group
*group
)
855 assert(!group
->group_filtermode_isexcl
);
856 assert(!listcount(group
->group_source_list
));
858 igmp_group_delete(group
);
861 void igmp_sock_free(struct gm_sock
*igmp
)
863 assert(!igmp
->t_igmp_read
);
864 assert(!igmp
->t_igmp_query_timer
);
865 assert(!igmp
->t_other_querier_timer
);
867 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
870 void igmp_sock_delete(struct gm_sock
*igmp
)
872 struct pim_interface
*pim_ifp
;
876 pim_ifp
= igmp
->interface
->info
;
878 listnode_delete(pim_ifp
->gm_socket_list
, igmp
);
880 igmp_sock_free(igmp
);
882 if (!listcount(pim_ifp
->gm_socket_list
))
883 pim_igmp_if_reset(pim_ifp
);
886 void igmp_sock_delete_all(struct interface
*ifp
)
888 struct pim_interface
*pim_ifp
;
889 struct listnode
*igmp_node
, *igmp_nextnode
;
890 struct gm_sock
*igmp
;
894 for (ALL_LIST_ELEMENTS(pim_ifp
->gm_socket_list
, igmp_node
,
895 igmp_nextnode
, igmp
)) {
896 igmp_sock_delete(igmp
);
900 static unsigned int igmp_group_hash_key(const void *arg
)
902 const struct gm_group
*group
= arg
;
904 return jhash_1word(group
->group_addr
.s_addr
, 0);
907 static bool igmp_group_hash_equal(const void *arg1
, const void *arg2
)
909 const struct gm_group
*g1
= (const struct gm_group
*)arg1
;
910 const struct gm_group
*g2
= (const struct gm_group
*)arg2
;
912 if (g1
->group_addr
.s_addr
== g2
->group_addr
.s_addr
)
918 void pim_igmp_if_init(struct pim_interface
*pim_ifp
, struct interface
*ifp
)
922 pim_ifp
->gm_socket_list
= list_new();
923 pim_ifp
->gm_socket_list
->del
= (void (*)(void *))igmp_sock_free
;
925 pim_ifp
->gm_group_list
= list_new();
926 pim_ifp
->gm_group_list
->del
= (void (*)(void *))igmp_group_free
;
928 snprintf(hash_name
, sizeof(hash_name
), "IGMP %s hash", ifp
->name
);
929 pim_ifp
->gm_group_hash
= hash_create(igmp_group_hash_key
,
930 igmp_group_hash_equal
, hash_name
);
933 void pim_igmp_if_reset(struct pim_interface
*pim_ifp
)
935 struct listnode
*grp_node
, *grp_nextnode
;
936 struct gm_group
*grp
;
938 for (ALL_LIST_ELEMENTS(pim_ifp
->gm_group_list
, grp_node
, grp_nextnode
,
940 igmp_group_delete(grp
);
944 void pim_igmp_if_fini(struct pim_interface
*pim_ifp
)
946 pim_igmp_if_reset(pim_ifp
);
948 assert(pim_ifp
->gm_group_list
);
949 assert(!listcount(pim_ifp
->gm_group_list
));
951 list_delete(&pim_ifp
->gm_group_list
);
952 hash_free(pim_ifp
->gm_group_hash
);
954 list_delete(&pim_ifp
->gm_socket_list
);
957 static struct gm_sock
*igmp_sock_new(int fd
, struct in_addr ifaddr
,
958 struct interface
*ifp
, int mtrace_only
)
960 struct pim_interface
*pim_ifp
;
961 struct gm_sock
*igmp
;
965 if (PIM_DEBUG_IGMP_TRACE
) {
967 "Creating IGMP socket fd=%d for address %pI4 on interface %s",
968 fd
, &ifaddr
, ifp
->name
);
971 igmp
= XCALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
974 igmp
->interface
= ifp
;
975 igmp
->ifaddr
= ifaddr
;
976 igmp
->querier_addr
= ifaddr
;
977 igmp
->t_igmp_read
= NULL
;
978 igmp
->t_igmp_query_timer
= NULL
;
979 igmp
->t_other_querier_timer
= NULL
; /* no other querier present */
980 igmp
->querier_robustness_variable
=
981 pim_ifp
->gm_default_robustness_variable
;
982 igmp
->sock_creation
= pim_time_monotonic_sec();
984 igmp_stats_init(&igmp
->rx_stats
);
987 igmp
->mtrace_only
= mtrace_only
;
991 igmp
->mtrace_only
= false;
994 igmp_startup_mode_on() will reset QQI:
996 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
998 igmp_startup_mode_on(igmp
);
999 pim_igmp_general_query_on(igmp
);
1004 static void igmp_read_on(struct gm_sock
*igmp
);
1006 static int pim_igmp_read(struct thread
*t
)
1009 struct gm_sock
*igmp
= (struct gm_sock
*)THREAD_ARG(t
);
1010 struct sockaddr_in from
;
1011 struct sockaddr_in to
;
1012 socklen_t fromlen
= sizeof(from
);
1013 socklen_t tolen
= sizeof(to
);
1014 ifindex_t ifindex
= -1;
1018 len
= pim_socket_recvfromto(igmp
->fd
, buf
, sizeof(buf
), &from
,
1019 &fromlen
, &to
, &tolen
, &ifindex
);
1023 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
1035 static void igmp_read_on(struct gm_sock
*igmp
)
1038 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
1039 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
1042 thread_add_read(router
->master
, pim_igmp_read
, igmp
, igmp
->fd
,
1043 &igmp
->t_igmp_read
);
1046 struct gm_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
1047 struct in_addr ifaddr
, struct interface
*ifp
,
1050 struct pim_interface
*pim_ifp
;
1051 struct gm_sock
*igmp
;
1052 struct sockaddr_in sin
;
1055 pim_ifp
= ifp
->info
;
1057 fd
= igmp_sock_open(ifaddr
, ifp
, pim_ifp
->options
);
1059 zlog_warn("Could not open IGMP socket for %pI4 on %s",
1060 &ifaddr
, ifp
->name
);
1064 sin
.sin_family
= AF_INET
;
1065 sin
.sin_addr
= ifaddr
;
1067 if (bind(fd
, (struct sockaddr
*) &sin
, sizeof(sin
)) != 0) {
1068 zlog_warn("Could not bind IGMP socket for %pI4 on %s: %s(%d)",
1069 &ifaddr
, ifp
->name
, strerror(errno
), errno
);
1075 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
, mtrace_only
);
1079 listnode_add(igmp_sock_list
, igmp
);
1081 #ifdef IGMP_SOCK_DUMP
1082 igmp_sock_dump(igmp_sock_array
);
1089 RFC 3376: 6.5. Switching Router Filter-Modes
1091 When a router's filter-mode for a group is EXCLUDE and the group
1092 timer expires, the router filter-mode for the group transitions to
1095 A router uses source records with running source timers as its state
1096 for the switch to a filter-mode of INCLUDE. If there are any source
1097 records with source timers greater than zero (i.e., requested to be
1098 forwarded), a router switches to filter-mode of INCLUDE using those
1099 source records. Source records whose timers are zero (from the
1100 previous EXCLUDE mode) are deleted.
1102 static int igmp_group_timer(struct thread
*t
)
1104 struct gm_group
*group
;
1106 group
= THREAD_ARG(t
);
1108 if (PIM_DEBUG_IGMP_TRACE
) {
1109 char group_str
[INET_ADDRSTRLEN
];
1110 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1112 zlog_debug("%s: Timer for group %s on interface %s", __func__
,
1113 group_str
, group
->interface
->name
);
1116 assert(group
->group_filtermode_isexcl
);
1118 group
->group_filtermode_isexcl
= 0;
1120 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1121 igmp_anysource_forward_stop(group
);
1123 igmp_source_delete_expired(group
->group_source_list
);
1125 assert(!group
->group_filtermode_isexcl
);
1128 RFC 3376: 6.2.2. Definition of Group Timers
1130 If there are no more source records for the group, delete group
1133 if (listcount(group
->group_source_list
) < 1) {
1134 igmp_group_delete_empty_include(group
);
1140 static void group_timer_off(struct gm_group
*group
)
1142 if (!group
->t_group_timer
)
1145 if (PIM_DEBUG_IGMP_TRACE
) {
1146 char group_str
[INET_ADDRSTRLEN
];
1147 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1149 zlog_debug("Cancelling TIMER event for group %s on %s",
1150 group_str
, group
->interface
->name
);
1152 THREAD_OFF(group
->t_group_timer
);
1155 void igmp_group_timer_on(struct gm_group
*group
, long interval_msec
,
1158 group_timer_off(group
);
1160 if (PIM_DEBUG_IGMP_EVENTS
) {
1161 char group_str
[INET_ADDRSTRLEN
];
1162 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1165 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1166 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
1171 RFC 3376: 6.2.2. Definition of Group Timers
1173 The group timer is only used when a group is in EXCLUDE mode and
1174 it represents the time for the *filter-mode* of the group to
1175 expire and switch to INCLUDE mode.
1177 assert(group
->group_filtermode_isexcl
);
1179 thread_add_timer_msec(router
->master
, igmp_group_timer
, group
,
1180 interval_msec
, &group
->t_group_timer
);
1183 struct gm_group
*find_group_by_addr(struct gm_sock
*igmp
,
1184 struct in_addr group_addr
)
1186 struct gm_group lookup
;
1187 struct pim_interface
*pim_ifp
= igmp
->interface
->info
;
1189 lookup
.group_addr
.s_addr
= group_addr
.s_addr
;
1191 return hash_lookup(pim_ifp
->gm_group_hash
, &lookup
);
1194 struct gm_group
*igmp_add_group_by_addr(struct gm_sock
*igmp
,
1195 struct in_addr group_addr
)
1197 struct gm_group
*group
;
1198 struct pim_interface
*pim_ifp
= igmp
->interface
->info
;
1200 group
= find_group_by_addr(igmp
, group_addr
);
1205 if (!pim_is_group_224_4(group_addr
)) {
1206 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1211 if (pim_is_group_224_0_0_0_24(group_addr
)) {
1212 if (PIM_DEBUG_IGMP_TRACE
)
1214 "%s: Group specified %pI4 is part of 224.0.0.0/24",
1215 __func__
, &group_addr
);
1219 Non-existant group is created as INCLUDE {empty}:
1221 RFC 3376 - 5.1. Action on Change of Interface State
1223 If no interface state existed for that multicast address before
1224 the change (i.e., the change consisted of creating a new
1225 per-interface record), or if no state exists after the change
1226 (i.e., the change consisted of deleting a per-interface record),
1227 then the "non-existent" state is considered to have a filter mode
1228 of INCLUDE and an empty source list.
1231 group
= XCALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1233 group
->group_source_list
= list_new();
1234 group
->group_source_list
->del
= (void (*)(void *))igmp_source_free
;
1236 group
->t_group_timer
= NULL
;
1237 group
->t_group_query_retransmit_timer
= NULL
;
1238 group
->group_specific_query_retransmit_count
= 0;
1239 group
->group_addr
= group_addr
;
1240 group
->interface
= igmp
->interface
;
1241 group
->last_igmp_v1_report_dsec
= -1;
1242 group
->last_igmp_v2_report_dsec
= -1;
1243 group
->group_creation
= pim_time_monotonic_sec();
1244 group
->igmp_version
= IGMP_DEFAULT_VERSION
;
1246 /* initialize new group as INCLUDE {empty} */
1247 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1249 listnode_add(pim_ifp
->gm_group_list
, group
);
1250 group
= hash_get(pim_ifp
->gm_group_hash
, group
, hash_alloc_intern
);
1252 if (PIM_DEBUG_IGMP_TRACE
) {
1253 char group_str
[INET_ADDRSTRLEN
];
1254 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1257 "Creating new IGMP group %s on socket %d interface %s",
1258 group_str
, igmp
->fd
, igmp
->interface
->name
);
1261 igmp_group_count_incr(pim_ifp
);
1264 RFC 3376: 6.2.2. Definition of Group Timers
1266 The group timer is only used when a group is in EXCLUDE mode and
1267 it represents the time for the *filter-mode* of the group to
1268 expire and switch to INCLUDE mode.
1270 assert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1271 assert(!group
->t_group_timer
); /* group timer == 0 */
1273 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1274 igmp_anysource_forward_stop(group
);
1279 void igmp_send_query(int igmp_version
, struct gm_group
*group
, int fd
,
1280 const char *ifname
, char *query_buf
, int query_buf_size
,
1281 int num_sources
, struct in_addr dst_addr
,
1282 struct in_addr group_addr
,
1283 int query_max_response_time_dsec
, uint8_t s_flag
,
1284 uint8_t querier_robustness_variable
,
1285 uint16_t querier_query_interval
)
1287 if (igmp_version
== 3) {
1288 igmp_v3_send_query(group
, fd
, ifname
, query_buf
, query_buf_size
,
1289 num_sources
, dst_addr
, group_addr
,
1290 query_max_response_time_dsec
, s_flag
,
1291 querier_robustness_variable
,
1292 querier_query_interval
);
1293 } else if (igmp_version
== 2) {
1294 igmp_v2_send_query(group
, fd
, ifname
, query_buf
, dst_addr
,
1295 group_addr
, query_max_response_time_dsec
);
1299 void igmp_send_query_on_intf(struct interface
*ifp
, int igmp_ver
)
1301 struct pim_interface
*pim_ifp
= ifp
->info
;
1302 struct listnode
*sock_node
= NULL
;
1303 struct gm_sock
*igmp
= NULL
;
1304 struct in_addr dst_addr
;
1305 struct in_addr group_addr
;
1312 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1314 query_buf_size
= IGMP_V12_MSG_SIZE
;
1316 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
1317 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
1319 if (PIM_DEBUG_IGMP_TRACE
)
1320 zlog_debug("Issuing general query on request on %s", ifp
->name
);
1322 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_socket_list
, sock_node
, igmp
)) {
1324 char query_buf
[query_buf_size
];
1326 igmp_send_query(igmp_ver
, 0 /* igmp_group */, igmp
->fd
,
1327 igmp
->interface
->name
, query_buf
,
1328 sizeof(query_buf
), 0 /* num_sources */,
1329 dst_addr
, group_addr
,
1330 pim_ifp
->gm_query_max_response_time_dsec
,
1331 1 /* s_flag: always set for general queries */,
1332 igmp
->querier_robustness_variable
,
1333 igmp
->querier_query_interval
);