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 igmp_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 %s: could not solve %s to group address: errno=%d: %s",
67 __FILE__
, __func__
, fd
, inet_ntoa(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 %s: could not solve %s to group address: errno=%d: %s",
83 __FILE__
, __func__
, fd
, inet_ntoa(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 %s: could not solve %s to group address: errno=%d: %s",
94 __FILE__
, __func__
, fd
, inet_ntoa(ifaddr
),
95 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
101 "IGMP socket fd=%d could not join any group on interface address %s",
102 fd
, inet_ntoa(ifaddr
));
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 igmp_sock
*igmp
= array_get(igmp_sock_array
, i
);
120 zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d", __FILE__
,
121 __func__
, i
, size
, inet_ntoa(igmp
->ifaddr
),
127 struct igmp_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
128 struct in_addr ifaddr
)
130 struct listnode
*sock_node
;
131 struct igmp_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 struct igmp_sock
*igmp_sock_lookup_by_fd(struct list
*igmp_sock_list
, int fd
)
146 struct listnode
*sock_node
;
147 struct igmp_sock
*igmp
;
149 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
156 static int pim_igmp_other_querier_expire(struct thread
*t
)
158 struct igmp_sock
*igmp
;
160 igmp
= THREAD_ARG(t
);
162 zassert(!igmp
->t_igmp_query_timer
);
164 if (PIM_DEBUG_IGMP_TRACE
) {
165 char ifaddr_str
[INET_ADDRSTRLEN
];
166 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
168 zlog_debug("%s: Querier %s resuming", __func__
, ifaddr_str
);
172 We are the current querier, then
173 re-start sending general queries.
174 RFC 2236 - sec 7 Other Querier
175 present timer expired (Send General
176 Query, Set Gen. Query. timer)
178 pim_igmp_general_query(t
);
183 void pim_igmp_other_querier_timer_on(struct igmp_sock
*igmp
)
185 long other_querier_present_interval_msec
;
186 struct pim_interface
*pim_ifp
;
189 zassert(igmp
->interface
);
190 zassert(igmp
->interface
->info
);
192 pim_ifp
= igmp
->interface
->info
;
194 if (igmp
->t_other_querier_timer
) {
196 There is other querier present already,
197 then reset the other-querier-present timer.
200 if (PIM_DEBUG_IGMP_TRACE
) {
201 char ifaddr_str
[INET_ADDRSTRLEN
];
202 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
205 "Querier %s resetting TIMER event for Other-Querier-Present",
208 THREAD_OFF(igmp
->t_other_querier_timer
);
211 We are the current querier, then stop sending general queries:
212 igmp->t_igmp_query_timer = NULL;
214 pim_igmp_general_query_off(igmp
);
218 Since this socket is starting the other-querier-present timer,
219 there should not be periodic query timer for this socket.
221 zassert(!igmp
->t_igmp_query_timer
);
224 RFC 3376: 8.5. Other Querier Present Interval
226 The Other Querier Present Interval is the length of time that must
227 pass before a multicast router decides that there is no longer
228 another multicast router which should be the querier. This value
229 MUST be ((the Robustness Variable) times (the Query Interval)) plus
230 (one half of one Query Response Interval).
232 other_querier_present_interval_msec = \
233 igmp->querier_robustness_variable * \
234 1000 * igmp->querier_query_interval + \
235 100 * (pim_ifp->query_max_response_time_dsec >> 1);
237 other_querier_present_interval_msec
= PIM_IGMP_OQPI_MSEC(
238 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
239 pim_ifp
->igmp_query_max_response_time_dsec
);
241 if (PIM_DEBUG_IGMP_TRACE
) {
242 char ifaddr_str
[INET_ADDRSTRLEN
];
243 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
246 "Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
247 ifaddr_str
, other_querier_present_interval_msec
/ 1000,
248 other_querier_present_interval_msec
% 1000);
251 thread_add_timer_msec(router
->master
, pim_igmp_other_querier_expire
,
252 igmp
, other_querier_present_interval_msec
,
253 &igmp
->t_other_querier_timer
);
256 void pim_igmp_other_querier_timer_off(struct igmp_sock
*igmp
)
260 if (PIM_DEBUG_IGMP_TRACE
) {
261 if (igmp
->t_other_querier_timer
) {
262 char ifaddr_str
[INET_ADDRSTRLEN
];
263 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
266 "IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
267 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
270 THREAD_OFF(igmp
->t_other_querier_timer
);
273 static int igmp_recv_query(struct igmp_sock
*igmp
, int query_version
,
274 int max_resp_code
, struct in_addr from
,
275 const char *from_str
, char *igmp_msg
,
278 struct interface
*ifp
;
279 struct pim_interface
*pim_ifp
;
280 struct in_addr group_addr
;
281 uint16_t recv_checksum
;
284 if (igmp
->mtrace_only
)
287 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
289 ifp
= igmp
->interface
;
292 recv_checksum
= *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
294 /* for computing checksum */
295 *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
297 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
298 if (checksum
!= recv_checksum
) {
300 "Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
301 query_version
, from_str
, ifp
->name
, recv_checksum
,
306 if (!pim_if_connected_to_source(ifp
, from
)) {
307 if (PIM_DEBUG_IGMP_PACKETS
)
308 zlog_debug("Recv IGMP query on interface: %s from a non-connected source: %s",
309 ifp
->name
, from_str
);
313 if (if_lookup_address(&from
, AF_INET
, ifp
->vrf_id
)) {
314 if (PIM_DEBUG_IGMP_PACKETS
)
315 zlog_debug("Recv IGMP query on interface: %s from ourself %s",
316 ifp
->name
, from_str
);
320 /* Collecting IGMP Rx stats */
321 switch (query_version
) {
323 igmp
->rx_stats
.query_v1
++;
326 igmp
->rx_stats
.query_v2
++;
329 igmp
->rx_stats
.query_v3
++;
332 igmp
->rx_stats
.unsupported
++;
336 * RFC 3376 defines some guidelines on operating in backwards
337 * compatibility with older versions of IGMP but there are some gaps in
340 * - once we drop from say version 3 to version 2 we will never go back
341 * to version 3 even if the node that TXed an IGMP v2 query upgrades
344 * - The node with the lowest IP is the querier so we will only know to
345 * drop from v3 to v2 if the node that is the querier is also the one
346 * that is running igmp v2. If a non-querier only supports igmp v2
347 * we will have no way of knowing.
349 * For now we will simplify things and inform the user that they need to
350 * configure all PIM routers to use the same version of IGMP.
352 if (query_version
!= pim_ifp
->igmp_version
) {
354 "Recv IGMP query v%d from %s on %s but we are using v%d, please "
355 "configure all PIM routers on this subnet to use the same "
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
,
391 pim_igmp_other_querier_timer_on(igmp
);
394 /* IGMP version 3 is the only one where we process the RXed query */
395 if (query_version
== 3) {
396 igmp_v3_recv_query(igmp
, from_str
, igmp_msg
);
402 static void on_trace(const char *label
, struct interface
*ifp
,
405 if (PIM_DEBUG_IGMP_TRACE
) {
406 char from_str
[INET_ADDRSTRLEN
];
407 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
408 zlog_debug("%s: from %s on %s", label
, from_str
, ifp
->name
);
412 static int igmp_v1_recv_report(struct igmp_sock
*igmp
, struct in_addr from
,
413 const char *from_str
, char *igmp_msg
,
416 struct interface
*ifp
= igmp
->interface
;
417 struct igmp_group
*group
;
418 struct in_addr group_addr
;
420 on_trace(__func__
, igmp
->interface
, from
);
422 if (igmp
->mtrace_only
)
425 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
427 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
428 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
432 /* Collecting IGMP Rx stats */
433 igmp
->rx_stats
.report_v1
++;
435 if (PIM_DEBUG_IGMP_TRACE
) {
436 zlog_warn("%s %s: FIXME WRITEME", __FILE__
, __func__
);
439 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
441 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
444 /* non-existant group is created as INCLUDE {empty} */
445 group
= igmp_add_group_by_addr(igmp
, group_addr
);
450 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
455 int pim_igmp_packet(struct igmp_sock
*igmp
, char *buf
, size_t len
)
458 size_t ip_hlen
; /* ip header length in bytes */
462 char from_str
[INET_ADDRSTRLEN
];
463 char to_str
[INET_ADDRSTRLEN
];
465 if (len
< sizeof(*ip_hdr
)) {
466 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len
,
471 ip_hdr
= (struct ip
*)buf
;
473 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
474 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
476 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
480 "IGMP packet header claims size %zu, but we only have %zu bytes",
485 igmp_msg
= buf
+ ip_hlen
;
486 igmp_msg_len
= len
- ip_hlen
;
488 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
489 zlog_warn("IGMP message size=%d shorter than minimum=%d",
490 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
494 msg_type
= *igmp_msg
;
496 if (PIM_DEBUG_IGMP_PACKETS
) {
498 "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
499 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hdr
->ip_ttl
,
500 msg_type
, igmp_msg_len
);
504 case PIM_IGMP_MEMBERSHIP_QUERY
: {
505 int max_resp_code
= igmp_msg
[1];
509 RFC 3376: 7.1. Query Version Distinctions
510 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
512 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
514 IGMPv3 Query: length >= 12 octets
517 if (igmp_msg_len
== 8) {
518 query_version
= max_resp_code
? 2 : 1;
519 } else if (igmp_msg_len
>= 12) {
522 zlog_warn("Unknown IGMP query version");
526 return igmp_recv_query(igmp
, query_version
, max_resp_code
,
527 ip_hdr
->ip_src
, from_str
, igmp_msg
,
531 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
532 return igmp_v3_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
533 igmp_msg
, igmp_msg_len
);
535 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
536 return igmp_v2_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
537 igmp_msg
, igmp_msg_len
);
539 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
540 return igmp_v1_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
541 igmp_msg
, igmp_msg_len
);
543 case PIM_IGMP_V2_LEAVE_GROUP
:
544 return igmp_v2_recv_leave(igmp
, ip_hdr
->ip_src
, from_str
,
545 igmp_msg
, igmp_msg_len
);
547 case PIM_IGMP_MTRACE_RESPONSE
:
548 return igmp_mtrace_recv_response(igmp
, ip_hdr
, ip_hdr
->ip_src
,
551 case PIM_IGMP_MTRACE_QUERY_REQUEST
:
552 return igmp_mtrace_recv_qry_req(igmp
, ip_hdr
, ip_hdr
->ip_src
,
557 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
559 /* Collecting IGMP Rx stats */
560 igmp
->rx_stats
.unsupported
++;
565 void pim_igmp_general_query_on(struct igmp_sock
*igmp
)
567 struct pim_interface
*pim_ifp
;
572 Since this socket is starting as querier,
573 there should not exist a timer for other-querier-present.
575 zassert(!igmp
->t_other_querier_timer
);
576 pim_ifp
= igmp
->interface
->info
;
580 RFC 3376: 8.6. Startup Query Interval
582 The Startup Query Interval is the interval between General Queries
583 sent by a Querier on startup. Default: 1/4 the Query Interval.
584 The first one should be sent out immediately instead of 125/4
587 startup_mode
= igmp
->startup_query_count
> 0;
590 * If this is the first time we are sending a query on a
591 * newly configured igmp interface send it out in 1 second
592 * just to give the entire world a tiny bit of time to settle
593 * else the query interval is:
594 * query_interval = pim_ifp->igmp_default_query_interval >> 2;
596 if (igmp
->startup_query_count
597 == igmp
->querier_robustness_variable
)
600 query_interval
= PIM_IGMP_SQI(
601 pim_ifp
->igmp_default_query_interval
);
603 --igmp
->startup_query_count
;
605 query_interval
= igmp
->querier_query_interval
;
608 if (PIM_DEBUG_IGMP_TRACE
) {
609 char ifaddr_str
[INET_ADDRSTRLEN
];
610 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
613 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
614 ifaddr_str
, query_interval
,
615 startup_mode
? "startup" : "non-startup", igmp
->fd
);
617 igmp
->t_igmp_query_timer
= NULL
;
618 thread_add_timer(router
->master
, pim_igmp_general_query
, igmp
,
619 query_interval
, &igmp
->t_igmp_query_timer
);
622 void pim_igmp_general_query_off(struct igmp_sock
*igmp
)
626 if (PIM_DEBUG_IGMP_TRACE
) {
627 if (igmp
->t_igmp_query_timer
) {
628 char ifaddr_str
[INET_ADDRSTRLEN
];
629 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
632 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
633 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
636 THREAD_OFF(igmp
->t_igmp_query_timer
);
639 /* Issue IGMP general query */
640 static int pim_igmp_general_query(struct thread
*t
)
642 struct igmp_sock
*igmp
;
643 struct in_addr dst_addr
;
644 struct in_addr group_addr
;
645 struct pim_interface
*pim_ifp
;
648 igmp
= THREAD_ARG(t
);
650 zassert(igmp
->interface
);
651 zassert(igmp
->interface
->info
);
653 pim_ifp
= igmp
->interface
->info
;
655 if (pim_ifp
->igmp_version
== 3) {
656 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
658 query_buf_size
= IGMP_V12_MSG_SIZE
;
661 char query_buf
[query_buf_size
];
664 RFC3376: 4.1.12. IP Destination Addresses for Queries
666 In IGMPv3, General Queries are sent with an IP destination address
667 of 224.0.0.1, the all-systems multicast address. Group-Specific
668 and Group-and-Source-Specific Queries are sent with an IP
669 destination address equal to the multicast address of interest.
672 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
673 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
675 if (PIM_DEBUG_IGMP_TRACE
) {
676 char querier_str
[INET_ADDRSTRLEN
];
677 char dst_str
[INET_ADDRSTRLEN
];
678 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
679 sizeof(querier_str
));
680 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
681 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
682 querier_str
, dst_str
, igmp
->interface
->name
);
685 igmp_send_query(pim_ifp
->igmp_version
, 0 /* igmp_group */, igmp
->fd
,
686 igmp
->interface
->name
, query_buf
, sizeof(query_buf
),
687 0 /* num_sources */, dst_addr
, group_addr
,
688 pim_ifp
->igmp_query_max_response_time_dsec
,
689 1 /* s_flag: always set for general queries */,
690 igmp
->querier_robustness_variable
,
691 igmp
->querier_query_interval
);
693 pim_igmp_general_query_on(igmp
);
698 static void sock_close(struct igmp_sock
*igmp
)
700 pim_igmp_other_querier_timer_off(igmp
);
701 pim_igmp_general_query_off(igmp
);
703 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
704 if (igmp
->t_igmp_read
) {
706 "Cancelling READ event on IGMP socket %s fd=%d on interface %s",
707 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
708 igmp
->interface
->name
);
711 THREAD_OFF(igmp
->t_igmp_read
);
713 if (close(igmp
->fd
)) {
716 "Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
717 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
718 igmp
->interface
->name
, errno
, safe_strerror(errno
));
721 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
722 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
723 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
724 igmp
->interface
->name
);
728 void igmp_startup_mode_on(struct igmp_sock
*igmp
)
730 struct pim_interface
*pim_ifp
;
732 pim_ifp
= igmp
->interface
->info
;
735 RFC 3376: 8.7. Startup Query Count
737 The Startup Query Count is the number of Queries sent out on
738 startup, separated by the Startup Query Interval. Default: the
741 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
744 Since we're (re)starting, reset QQI to default Query Interval
746 igmp
->querier_query_interval
= pim_ifp
->igmp_default_query_interval
;
749 static void igmp_group_free(struct igmp_group
*group
)
751 list_delete(&group
->group_source_list
);
753 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
756 static void igmp_group_count_incr(struct igmp_sock
*igmp
)
758 struct pim_interface
*pim_ifp
= igmp
->interface
->info
;
763 ++pim_ifp
->pim
->igmp_group_count
;
764 if (pim_ifp
->pim
->igmp_group_count
765 == pim_ifp
->pim
->igmp_watermark_limit
) {
767 "IGMP group count reached watermark limit: %u(vrf: %s)",
768 pim_ifp
->pim
->igmp_group_count
,
769 VRF_LOGNAME(pim_ifp
->pim
->vrf
));
773 static void igmp_group_count_decr(struct igmp_sock
*igmp
)
775 struct pim_interface
*pim_ifp
= igmp
->interface
->info
;
780 if (pim_ifp
->pim
->igmp_group_count
== 0) {
781 zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
782 VRF_LOGNAME(pim_ifp
->pim
->vrf
));
786 --pim_ifp
->pim
->igmp_group_count
;
789 void igmp_group_delete(struct igmp_group
*group
)
791 struct listnode
*src_node
;
792 struct listnode
*src_nextnode
;
793 struct igmp_source
*src
;
795 if (PIM_DEBUG_IGMP_TRACE
) {
796 char group_str
[INET_ADDRSTRLEN
];
797 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
799 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
800 group_str
, group
->group_igmp_sock
->fd
,
801 group
->group_igmp_sock
->interface
->name
);
804 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
,
806 igmp_source_delete(src
);
809 if (group
->t_group_query_retransmit_timer
) {
810 THREAD_OFF(group
->t_group_query_retransmit_timer
);
813 group_timer_off(group
);
814 igmp_group_count_decr(group
->group_igmp_sock
);
815 listnode_delete(group
->group_igmp_sock
->igmp_group_list
, group
);
816 hash_release(group
->group_igmp_sock
->igmp_group_hash
, group
);
818 igmp_group_free(group
);
821 void igmp_group_delete_empty_include(struct igmp_group
*group
)
823 zassert(!group
->group_filtermode_isexcl
);
824 zassert(!listcount(group
->group_source_list
));
826 igmp_group_delete(group
);
829 void igmp_sock_free(struct igmp_sock
*igmp
)
831 zassert(!igmp
->t_igmp_read
);
832 zassert(!igmp
->t_igmp_query_timer
);
833 zassert(!igmp
->t_other_querier_timer
);
834 zassert(igmp
->igmp_group_list
);
835 zassert(!listcount(igmp
->igmp_group_list
));
837 list_delete(&igmp
->igmp_group_list
);
838 hash_free(igmp
->igmp_group_hash
);
840 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
843 void igmp_sock_delete(struct igmp_sock
*igmp
)
845 struct pim_interface
*pim_ifp
;
846 struct listnode
*grp_node
;
847 struct listnode
*grp_nextnode
;
848 struct igmp_group
*grp
;
850 for (ALL_LIST_ELEMENTS(igmp
->igmp_group_list
, grp_node
, grp_nextnode
,
852 igmp_group_delete(grp
);
857 pim_ifp
= igmp
->interface
->info
;
859 listnode_delete(pim_ifp
->igmp_socket_list
, igmp
);
861 igmp_sock_free(igmp
);
864 void igmp_sock_delete_all(struct interface
*ifp
)
866 struct pim_interface
*pim_ifp
;
867 struct listnode
*igmp_node
, *igmp_nextnode
;
868 struct igmp_sock
*igmp
;
872 for (ALL_LIST_ELEMENTS(pim_ifp
->igmp_socket_list
, igmp_node
,
873 igmp_nextnode
, igmp
)) {
874 igmp_sock_delete(igmp
);
878 static unsigned int igmp_group_hash_key(const void *arg
)
880 const struct igmp_group
*group
= arg
;
882 return jhash_1word(group
->group_addr
.s_addr
, 0);
885 static bool igmp_group_hash_equal(const void *arg1
, const void *arg2
)
887 const struct igmp_group
*g1
= (const struct igmp_group
*)arg1
;
888 const struct igmp_group
*g2
= (const struct igmp_group
*)arg2
;
890 if (g1
->group_addr
.s_addr
== g2
->group_addr
.s_addr
)
896 static struct igmp_sock
*igmp_sock_new(int fd
, struct in_addr ifaddr
,
897 struct interface
*ifp
, int mtrace_only
)
899 struct pim_interface
*pim_ifp
;
900 struct igmp_sock
*igmp
;
905 if (PIM_DEBUG_IGMP_TRACE
) {
907 "Creating IGMP socket fd=%d for address %s on interface %s",
908 fd
, inet_ntoa(ifaddr
), ifp
->name
);
911 igmp
= XCALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
913 igmp
->igmp_group_list
= list_new();
914 igmp
->igmp_group_list
->del
= (void (*)(void *))igmp_group_free
;
916 snprintf(hash_name
, sizeof(hash_name
), "IGMP %s hash", ifp
->name
);
917 igmp
->igmp_group_hash
= hash_create(igmp_group_hash_key
,
918 igmp_group_hash_equal
, hash_name
);
921 igmp
->interface
= ifp
;
922 igmp
->ifaddr
= ifaddr
;
923 igmp
->t_igmp_read
= NULL
;
924 igmp
->t_igmp_query_timer
= NULL
;
925 igmp
->t_other_querier_timer
= NULL
; /* no other querier present */
926 igmp
->querier_robustness_variable
=
927 pim_ifp
->igmp_default_robustness_variable
;
928 igmp
->sock_creation
= pim_time_monotonic_sec();
930 igmp_stats_init(&igmp
->rx_stats
);
933 igmp
->mtrace_only
= mtrace_only
;
937 igmp
->mtrace_only
= false;
940 igmp_startup_mode_on() will reset QQI:
942 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
944 igmp_startup_mode_on(igmp
);
945 pim_igmp_general_query_on(igmp
);
950 static void igmp_read_on(struct igmp_sock
*igmp
);
952 static int pim_igmp_read(struct thread
*t
)
955 struct igmp_sock
*igmp
= (struct igmp_sock
*)THREAD_ARG(t
);
956 struct sockaddr_in from
;
957 struct sockaddr_in to
;
958 socklen_t fromlen
= sizeof(from
);
959 socklen_t tolen
= sizeof(to
);
960 ifindex_t ifindex
= -1;
964 len
= pim_socket_recvfromto(igmp
->fd
, buf
, sizeof(buf
), &from
,
965 &fromlen
, &to
, &tolen
, &ifindex
);
969 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
981 static void igmp_read_on(struct igmp_sock
*igmp
)
984 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
985 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
988 igmp
->t_igmp_read
= NULL
;
989 thread_add_read(router
->master
, pim_igmp_read
, igmp
, igmp
->fd
,
993 struct igmp_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
994 struct in_addr ifaddr
,
995 struct interface
*ifp
,
998 struct pim_interface
*pim_ifp
;
999 struct igmp_sock
*igmp
;
1002 pim_ifp
= ifp
->info
;
1004 fd
= igmp_sock_open(ifaddr
, ifp
, pim_ifp
->options
);
1006 zlog_warn("Could not open IGMP socket for %s on %s",
1007 inet_ntoa(ifaddr
), ifp
->name
);
1011 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
, mtrace_only
);
1015 listnode_add(igmp_sock_list
, igmp
);
1017 #ifdef IGMP_SOCK_DUMP
1018 igmp_sock_dump(igmp_sock_array
);
1025 RFC 3376: 6.5. Switching Router Filter-Modes
1027 When a router's filter-mode for a group is EXCLUDE and the group
1028 timer expires, the router filter-mode for the group transitions to
1031 A router uses source records with running source timers as its state
1032 for the switch to a filter-mode of INCLUDE. If there are any source
1033 records with source timers greater than zero (i.e., requested to be
1034 forwarded), a router switches to filter-mode of INCLUDE using those
1035 source records. Source records whose timers are zero (from the
1036 previous EXCLUDE mode) are deleted.
1038 static int igmp_group_timer(struct thread
*t
)
1040 struct igmp_group
*group
;
1042 group
= THREAD_ARG(t
);
1044 if (PIM_DEBUG_IGMP_TRACE
) {
1045 char group_str
[INET_ADDRSTRLEN
];
1046 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1048 zlog_debug("%s: Timer for group %s on interface %s", __func__
,
1049 group_str
, group
->group_igmp_sock
->interface
->name
);
1052 zassert(group
->group_filtermode_isexcl
);
1054 group
->group_filtermode_isexcl
= 0;
1056 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1057 igmp_anysource_forward_stop(group
);
1059 igmp_source_delete_expired(group
->group_source_list
);
1061 zassert(!group
->group_filtermode_isexcl
);
1064 RFC 3376: 6.2.2. Definition of Group Timers
1066 If there are no more source records for the group, delete group
1069 if (listcount(group
->group_source_list
) < 1) {
1070 igmp_group_delete_empty_include(group
);
1076 static void group_timer_off(struct igmp_group
*group
)
1078 if (!group
->t_group_timer
)
1081 if (PIM_DEBUG_IGMP_TRACE
) {
1082 char group_str
[INET_ADDRSTRLEN
];
1083 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1085 zlog_debug("Cancelling TIMER event for group %s on %s",
1086 group_str
, group
->group_igmp_sock
->interface
->name
);
1088 THREAD_OFF(group
->t_group_timer
);
1091 void igmp_group_timer_on(struct igmp_group
*group
, long interval_msec
,
1094 group_timer_off(group
);
1096 if (PIM_DEBUG_IGMP_EVENTS
) {
1097 char group_str
[INET_ADDRSTRLEN
];
1098 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1101 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1102 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
1107 RFC 3376: 6.2.2. Definition of Group Timers
1109 The group timer is only used when a group is in EXCLUDE mode and
1110 it represents the time for the *filter-mode* of the group to
1111 expire and switch to INCLUDE mode.
1113 zassert(group
->group_filtermode_isexcl
);
1115 thread_add_timer_msec(router
->master
, igmp_group_timer
, group
,
1116 interval_msec
, &group
->t_group_timer
);
1119 struct igmp_group
*find_group_by_addr(struct igmp_sock
*igmp
,
1120 struct in_addr group_addr
)
1122 struct igmp_group lookup
;
1124 lookup
.group_addr
.s_addr
= group_addr
.s_addr
;
1126 return hash_lookup(igmp
->igmp_group_hash
, &lookup
);
1129 struct igmp_group
*igmp_add_group_by_addr(struct igmp_sock
*igmp
,
1130 struct in_addr group_addr
)
1132 struct igmp_group
*group
;
1134 group
= find_group_by_addr(igmp
, group_addr
);
1139 if (!pim_is_group_224_4(group_addr
)) {
1140 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1145 if (pim_is_group_224_0_0_0_24(group_addr
)) {
1146 if (PIM_DEBUG_IGMP_TRACE
)
1148 "%s: Group specified %s is part of 224.0.0.0/24",
1149 __func__
, inet_ntoa(group_addr
));
1153 Non-existant group is created as INCLUDE {empty}:
1155 RFC 3376 - 5.1. Action on Change of Interface State
1157 If no interface state existed for that multicast address before
1158 the change (i.e., the change consisted of creating a new
1159 per-interface record), or if no state exists after the change
1160 (i.e., the change consisted of deleting a per-interface record),
1161 then the "non-existent" state is considered to have a filter mode
1162 of INCLUDE and an empty source list.
1165 group
= XCALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1167 group
->group_source_list
= list_new();
1168 group
->group_source_list
->del
= (void (*)(void *))igmp_source_free
;
1170 group
->t_group_timer
= NULL
;
1171 group
->t_group_query_retransmit_timer
= NULL
;
1172 group
->group_specific_query_retransmit_count
= 0;
1173 group
->group_addr
= group_addr
;
1174 group
->group_igmp_sock
= igmp
;
1175 group
->last_igmp_v1_report_dsec
= -1;
1176 group
->last_igmp_v2_report_dsec
= -1;
1177 group
->group_creation
= pim_time_monotonic_sec();
1178 group
->igmp_version
= IGMP_DEFAULT_VERSION
;
1180 /* initialize new group as INCLUDE {empty} */
1181 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1183 listnode_add(igmp
->igmp_group_list
, group
);
1184 group
= hash_get(igmp
->igmp_group_hash
, group
, hash_alloc_intern
);
1186 if (PIM_DEBUG_IGMP_TRACE
) {
1187 char group_str
[INET_ADDRSTRLEN
];
1188 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1191 "Creating new IGMP group %s on socket %d interface %s",
1192 group_str
, igmp
->fd
, igmp
->interface
->name
);
1195 igmp_group_count_incr(igmp
);
1198 RFC 3376: 6.2.2. Definition of Group Timers
1200 The group timer is only used when a group is in EXCLUDE mode and
1201 it represents the time for the *filter-mode* of the group to
1202 expire and switch to INCLUDE mode.
1204 zassert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1205 zassert(!group
->t_group_timer
); /* group timer == 0 */
1207 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1208 igmp_anysource_forward_stop(group
);
1213 void igmp_send_query(int igmp_version
, struct igmp_group
*group
, int fd
,
1214 const char *ifname
, char *query_buf
, int query_buf_size
,
1215 int num_sources
, struct in_addr dst_addr
,
1216 struct in_addr group_addr
,
1217 int query_max_response_time_dsec
, uint8_t s_flag
,
1218 uint8_t querier_robustness_variable
,
1219 uint16_t querier_query_interval
)
1221 if (igmp_version
== 3) {
1222 igmp_v3_send_query(group
, fd
, ifname
, query_buf
, query_buf_size
,
1223 num_sources
, dst_addr
, group_addr
,
1224 query_max_response_time_dsec
, s_flag
,
1225 querier_robustness_variable
,
1226 querier_query_interval
);
1227 } else if (igmp_version
== 2) {
1228 igmp_v2_send_query(group
, fd
, ifname
, query_buf
, dst_addr
,
1229 group_addr
, query_max_response_time_dsec
);
1233 void igmp_send_query_on_intf(struct interface
*ifp
, int igmp_ver
)
1235 struct pim_interface
*pim_ifp
= ifp
->info
;
1236 struct listnode
*sock_node
= NULL
;
1237 struct igmp_sock
*igmp
= NULL
;
1238 struct in_addr dst_addr
;
1239 struct in_addr group_addr
;
1246 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1248 query_buf_size
= IGMP_V12_MSG_SIZE
;
1250 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
1251 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
1253 if (PIM_DEBUG_IGMP_TRACE
)
1254 zlog_debug("Issuing general query on request on %s",
1257 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->igmp_socket_list
, sock_node
, igmp
)) {
1259 char query_buf
[query_buf_size
];
1261 igmp_send_query(igmp_ver
, 0 /* igmp_group */, igmp
->fd
,
1262 igmp
->interface
->name
, query_buf
,
1263 sizeof(query_buf
), 0 /* num_sources */,
1264 dst_addr
, group_addr
,
1265 pim_ifp
->igmp_query_max_response_time_dsec
,
1266 1 /* s_flag: always set for general queries */,
1267 igmp
->querier_robustness_variable
,
1268 igmp
->querier_query_interval
);