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__
, __PRETTY_FUNCTION__
, fd
,
68 inet_ntoa(ifaddr
), PIM_ALL_ROUTERS
, errno
,
69 safe_strerror(errno
));
74 IGMP routers periodically send IGMP general queries to
76 IGMP routers must receive general queries for querier election.
78 if (inet_aton(PIM_ALL_SYSTEMS
, &group
)) {
79 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
))
83 "%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
84 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
85 PIM_ALL_SYSTEMS
, errno
, safe_strerror(errno
));
88 if (inet_aton(PIM_ALL_IGMP_ROUTERS
, &group
)) {
89 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
)) {
94 "%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
95 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
96 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
102 "IGMP socket fd=%d could not join any group on interface address %s",
103 fd
, inet_ntoa(ifaddr
));
111 #undef IGMP_SOCK_DUMP
113 #ifdef IGMP_SOCK_DUMP
114 static void igmp_sock_dump(array_t
*igmp_sock_array
)
116 int size
= array_size(igmp_sock_array
);
117 for (int i
= 0; i
< size
; ++i
) {
119 struct igmp_sock
*igmp
= array_get(igmp_sock_array
, i
);
121 zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d", __FILE__
,
122 __PRETTY_FUNCTION__
, i
, size
,
123 inet_ntoa(igmp
->ifaddr
), igmp
->fd
);
128 struct igmp_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
129 struct in_addr ifaddr
)
131 struct listnode
*sock_node
;
132 struct igmp_sock
*igmp
;
134 #ifdef IGMP_SOCK_DUMP
135 igmp_sock_dump(igmp_sock_list
);
138 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
139 if (ifaddr
.s_addr
== igmp
->ifaddr
.s_addr
)
145 struct igmp_sock
*igmp_sock_lookup_by_fd(struct list
*igmp_sock_list
, int fd
)
147 struct listnode
*sock_node
;
148 struct igmp_sock
*igmp
;
150 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
157 static int pim_igmp_other_querier_expire(struct thread
*t
)
159 struct igmp_sock
*igmp
;
161 igmp
= THREAD_ARG(t
);
163 zassert(!igmp
->t_igmp_query_timer
);
165 if (PIM_DEBUG_IGMP_TRACE
) {
166 char ifaddr_str
[INET_ADDRSTRLEN
];
167 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
169 zlog_debug("%s: Querier %s resuming", __PRETTY_FUNCTION__
,
174 We are the current querier, then
175 re-start sending general queries.
176 RFC 2236 - sec 7 Other Querier
177 present timer expired (Send General
178 Query, Set Gen. Query. timer)
180 pim_igmp_general_query(t
);
185 void pim_igmp_other_querier_timer_on(struct igmp_sock
*igmp
)
187 long other_querier_present_interval_msec
;
188 struct pim_interface
*pim_ifp
;
191 zassert(igmp
->interface
);
192 zassert(igmp
->interface
->info
);
194 pim_ifp
= igmp
->interface
->info
;
196 if (igmp
->t_other_querier_timer
) {
198 There is other querier present already,
199 then reset the other-querier-present timer.
202 if (PIM_DEBUG_IGMP_TRACE
) {
203 char ifaddr_str
[INET_ADDRSTRLEN
];
204 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
207 "Querier %s resetting TIMER event for Other-Querier-Present",
210 THREAD_OFF(igmp
->t_other_querier_timer
);
213 We are the current querier, then stop sending general queries:
214 igmp->t_igmp_query_timer = NULL;
216 pim_igmp_general_query_off(igmp
);
220 Since this socket is starting the other-querier-present timer,
221 there should not be periodic query timer for this socket.
223 zassert(!igmp
->t_igmp_query_timer
);
226 RFC 3376: 8.5. Other Querier Present Interval
228 The Other Querier Present Interval is the length of time that must
229 pass before a multicast router decides that there is no longer
230 another multicast router which should be the querier. This value
231 MUST be ((the Robustness Variable) times (the Query Interval)) plus
232 (one half of one Query Response Interval).
234 other_querier_present_interval_msec = \
235 igmp->querier_robustness_variable * \
236 1000 * igmp->querier_query_interval + \
237 100 * (pim_ifp->query_max_response_time_dsec >> 1);
239 other_querier_present_interval_msec
= PIM_IGMP_OQPI_MSEC(
240 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
241 pim_ifp
->igmp_query_max_response_time_dsec
);
243 if (PIM_DEBUG_IGMP_TRACE
) {
244 char ifaddr_str
[INET_ADDRSTRLEN
];
245 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
248 "Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
249 ifaddr_str
, other_querier_present_interval_msec
/ 1000,
250 other_querier_present_interval_msec
% 1000);
253 thread_add_timer_msec(router
->master
, pim_igmp_other_querier_expire
,
254 igmp
, other_querier_present_interval_msec
,
255 &igmp
->t_other_querier_timer
);
258 void pim_igmp_other_querier_timer_off(struct igmp_sock
*igmp
)
262 if (PIM_DEBUG_IGMP_TRACE
) {
263 if (igmp
->t_other_querier_timer
) {
264 char ifaddr_str
[INET_ADDRSTRLEN
];
265 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
268 "IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
269 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
272 THREAD_OFF(igmp
->t_other_querier_timer
);
275 static int igmp_recv_query(struct igmp_sock
*igmp
, int query_version
,
276 int max_resp_code
, struct in_addr from
,
277 const char *from_str
, char *igmp_msg
,
280 struct interface
*ifp
;
281 struct pim_interface
*pim_ifp
;
282 struct in_addr group_addr
;
283 uint16_t recv_checksum
;
286 if (igmp
->mtrace_only
)
289 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
291 ifp
= igmp
->interface
;
294 recv_checksum
= *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
296 /* for computing checksum */
297 *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
299 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
300 if (checksum
!= recv_checksum
) {
302 "Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
303 query_version
, from_str
, ifp
->name
, recv_checksum
,
308 /* Collecting IGMP Rx stats */
309 switch (query_version
) {
311 igmp
->rx_stats
.query_v1
++;
314 igmp
->rx_stats
.query_v2
++;
317 igmp
->rx_stats
.query_v3
++;
320 igmp
->rx_stats
.unsupported
++;
324 * RFC 3376 defines some guidelines on operating in backwards
325 * compatibility with older versions of IGMP but there are some gaps in
328 * - once we drop from say version 3 to version 2 we will never go back
329 * to version 3 even if the node that TXed an IGMP v2 query upgrades
332 * - The node with the lowest IP is the querier so we will only know to
333 * drop from v3 to v2 if the node that is the querier is also the one
334 * that is running igmp v2. If a non-querier only supports igmp v2
335 * we will have no way of knowing.
337 * For now we will simplify things and inform the user that they need to
338 * configure all PIM routers to use the same version of IGMP.
340 if (query_version
!= pim_ifp
->igmp_version
) {
342 "Recv IGMP query v%d from %s on %s but we are using v%d, please "
343 "configure all PIM routers on this subnet to use the same "
345 query_version
, from_str
, ifp
->name
,
346 pim_ifp
->igmp_version
);
350 if (PIM_DEBUG_IGMP_PACKETS
) {
351 char group_str
[INET_ADDRSTRLEN
];
352 pim_inet4_dump("<group?>", group_addr
, group_str
,
354 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
355 query_version
, from_str
, ifp
->name
, group_str
);
359 RFC 3376: 6.6.2. Querier Election
361 When a router receives a query with a lower IP address, it sets
362 the Other-Querier-Present timer to Other Querier Present Interval
363 and ceases to send queries on the network if it was the previously
366 if (ntohl(from
.s_addr
) < ntohl(igmp
->ifaddr
.s_addr
)) {
368 if (PIM_DEBUG_IGMP_TRACE
) {
369 char ifaddr_str
[INET_ADDRSTRLEN
];
370 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
373 "%s: local address %s (%u) lost querier election to %s (%u)",
374 ifp
->name
, ifaddr_str
,
375 ntohl(igmp
->ifaddr
.s_addr
), from_str
,
379 pim_igmp_other_querier_timer_on(igmp
);
382 /* IGMP version 3 is the only one where we process the RXed query */
383 if (query_version
== 3) {
384 igmp_v3_recv_query(igmp
, from_str
, igmp_msg
);
390 static void on_trace(const char *label
, struct interface
*ifp
,
393 if (PIM_DEBUG_IGMP_TRACE
) {
394 char from_str
[INET_ADDRSTRLEN
];
395 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
396 zlog_debug("%s: from %s on %s", label
, from_str
, ifp
->name
);
400 static int igmp_v1_recv_report(struct igmp_sock
*igmp
, struct in_addr from
,
401 const char *from_str
, char *igmp_msg
,
404 struct interface
*ifp
= igmp
->interface
;
405 struct igmp_group
*group
;
406 struct in_addr group_addr
;
408 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
);
410 if (igmp
->mtrace_only
)
413 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
415 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
416 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
420 /* Collecting IGMP Rx stats */
421 igmp
->rx_stats
.report_v1
++;
423 if (PIM_DEBUG_IGMP_TRACE
) {
424 zlog_warn("%s %s: FIXME WRITEME", __FILE__
,
425 __PRETTY_FUNCTION__
);
428 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
430 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
433 /* non-existant group is created as INCLUDE {empty} */
434 group
= igmp_add_group_by_addr(igmp
, group_addr
);
439 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
444 int pim_igmp_packet(struct igmp_sock
*igmp
, char *buf
, size_t len
)
447 size_t ip_hlen
; /* ip header length in bytes */
451 char from_str
[INET_ADDRSTRLEN
];
452 char to_str
[INET_ADDRSTRLEN
];
454 if (len
< sizeof(*ip_hdr
)) {
455 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len
,
460 ip_hdr
= (struct ip
*)buf
;
462 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
463 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
465 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
467 if (PIM_DEBUG_IGMP_PACKETS
) {
469 "Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
470 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hlen
,
474 igmp_msg
= buf
+ ip_hlen
;
475 msg_type
= *igmp_msg
;
476 igmp_msg_len
= len
- ip_hlen
;
478 if (PIM_DEBUG_IGMP_PACKETS
) {
480 "Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
481 from_str
, to_str
, igmp
->interface
->name
, ip_hdr
->ip_ttl
,
482 msg_type
, igmp_msg_len
);
485 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
486 zlog_warn("IGMP message size=%d shorter than minimum=%d",
487 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
492 case PIM_IGMP_MEMBERSHIP_QUERY
: {
493 int max_resp_code
= igmp_msg
[1];
497 RFC 3376: 7.1. Query Version Distinctions
498 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
500 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
502 IGMPv3 Query: length >= 12 octets
505 if (igmp_msg_len
== 8) {
506 query_version
= max_resp_code
? 2 : 1;
507 } else if (igmp_msg_len
>= 12) {
510 zlog_warn("Unknown IGMP query version");
514 return igmp_recv_query(igmp
, query_version
, max_resp_code
,
515 ip_hdr
->ip_src
, from_str
, igmp_msg
,
519 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
520 return igmp_v3_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
521 igmp_msg
, igmp_msg_len
);
523 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
524 return igmp_v2_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
525 igmp_msg
, igmp_msg_len
);
527 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
528 return igmp_v1_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
529 igmp_msg
, igmp_msg_len
);
531 case PIM_IGMP_V2_LEAVE_GROUP
:
532 return igmp_v2_recv_leave(igmp
, ip_hdr
->ip_src
, from_str
,
533 igmp_msg
, igmp_msg_len
);
535 case PIM_IGMP_MTRACE_RESPONSE
:
536 return igmp_mtrace_recv_response(igmp
, ip_hdr
, ip_hdr
->ip_src
,
539 case PIM_IGMP_MTRACE_QUERY_REQUEST
:
540 return igmp_mtrace_recv_qry_req(igmp
, ip_hdr
, ip_hdr
->ip_src
,
545 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
547 /* Collecting IGMP Rx stats */
548 igmp
->rx_stats
.unsupported
++;
553 void pim_igmp_general_query_on(struct igmp_sock
*igmp
)
555 struct pim_interface
*pim_ifp
;
560 Since this socket is starting as querier,
561 there should not exist a timer for other-querier-present.
563 zassert(!igmp
->t_other_querier_timer
);
564 pim_ifp
= igmp
->interface
->info
;
568 RFC 3376: 8.6. Startup Query Interval
570 The Startup Query Interval is the interval between General Queries
571 sent by a Querier on startup. Default: 1/4 the Query Interval.
572 The first one should be sent out immediately instead of 125/4
575 startup_mode
= igmp
->startup_query_count
> 0;
578 * If this is the first time we are sending a query on a
579 * newly configured igmp interface send it out in 1 second
580 * just to give the entire world a tiny bit of time to settle
581 * else the query interval is:
582 * query_interval = pim_ifp->igmp_default_query_interval >> 2;
584 if (igmp
->startup_query_count
585 == igmp
->querier_robustness_variable
)
588 query_interval
= PIM_IGMP_SQI(
589 pim_ifp
->igmp_default_query_interval
);
591 --igmp
->startup_query_count
;
593 query_interval
= igmp
->querier_query_interval
;
596 if (PIM_DEBUG_IGMP_TRACE
) {
597 char ifaddr_str
[INET_ADDRSTRLEN
];
598 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
601 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
602 ifaddr_str
, query_interval
,
603 startup_mode
? "startup" : "non-startup", igmp
->fd
);
605 igmp
->t_igmp_query_timer
= NULL
;
606 thread_add_timer(router
->master
, pim_igmp_general_query
, igmp
,
607 query_interval
, &igmp
->t_igmp_query_timer
);
610 void pim_igmp_general_query_off(struct igmp_sock
*igmp
)
614 if (PIM_DEBUG_IGMP_TRACE
) {
615 if (igmp
->t_igmp_query_timer
) {
616 char ifaddr_str
[INET_ADDRSTRLEN
];
617 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
620 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
621 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
624 THREAD_OFF(igmp
->t_igmp_query_timer
);
627 /* Issue IGMP general query */
628 static int pim_igmp_general_query(struct thread
*t
)
630 struct igmp_sock
*igmp
;
631 struct in_addr dst_addr
;
632 struct in_addr group_addr
;
633 struct pim_interface
*pim_ifp
;
636 igmp
= THREAD_ARG(t
);
638 zassert(igmp
->interface
);
639 zassert(igmp
->interface
->info
);
641 pim_ifp
= igmp
->interface
->info
;
643 if (pim_ifp
->igmp_version
== 3) {
644 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
646 query_buf_size
= IGMP_V12_MSG_SIZE
;
649 char query_buf
[query_buf_size
];
652 RFC3376: 4.1.12. IP Destination Addresses for Queries
654 In IGMPv3, General Queries are sent with an IP destination address
655 of 224.0.0.1, the all-systems multicast address. Group-Specific
656 and Group-and-Source-Specific Queries are sent with an IP
657 destination address equal to the multicast address of interest.
660 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
661 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
663 if (PIM_DEBUG_IGMP_TRACE
) {
664 char querier_str
[INET_ADDRSTRLEN
];
665 char dst_str
[INET_ADDRSTRLEN
];
666 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
667 sizeof(querier_str
));
668 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
669 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
670 querier_str
, dst_str
, igmp
->interface
->name
);
673 igmp_send_query(pim_ifp
->igmp_version
, 0 /* igmp_group */, igmp
->fd
,
674 igmp
->interface
->name
, query_buf
, sizeof(query_buf
),
675 0 /* num_sources */, dst_addr
, group_addr
,
676 pim_ifp
->igmp_query_max_response_time_dsec
,
677 1 /* s_flag: always set for general queries */,
678 igmp
->querier_robustness_variable
,
679 igmp
->querier_query_interval
);
681 pim_igmp_general_query_on(igmp
);
686 static void sock_close(struct igmp_sock
*igmp
)
688 pim_igmp_other_querier_timer_off(igmp
);
689 pim_igmp_general_query_off(igmp
);
691 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
692 if (igmp
->t_igmp_read
) {
694 "Cancelling READ event on IGMP socket %s fd=%d on interface %s",
695 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
696 igmp
->interface
->name
);
699 THREAD_OFF(igmp
->t_igmp_read
);
701 if (close(igmp
->fd
)) {
704 "Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
705 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
706 igmp
->interface
->name
, errno
, safe_strerror(errno
));
709 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
710 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
711 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
712 igmp
->interface
->name
);
716 void igmp_startup_mode_on(struct igmp_sock
*igmp
)
718 struct pim_interface
*pim_ifp
;
720 pim_ifp
= igmp
->interface
->info
;
723 RFC 3376: 8.7. Startup Query Count
725 The Startup Query Count is the number of Queries sent out on
726 startup, separated by the Startup Query Interval. Default: the
729 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
732 Since we're (re)starting, reset QQI to default Query Interval
734 igmp
->querier_query_interval
= pim_ifp
->igmp_default_query_interval
;
737 static void igmp_group_free(struct igmp_group
*group
)
739 list_delete(&group
->group_source_list
);
741 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
744 void igmp_group_delete(struct igmp_group
*group
)
746 struct listnode
*src_node
;
747 struct listnode
*src_nextnode
;
748 struct igmp_source
*src
;
750 if (PIM_DEBUG_IGMP_TRACE
) {
751 char group_str
[INET_ADDRSTRLEN
];
752 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
754 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
755 group_str
, group
->group_igmp_sock
->fd
,
756 group
->group_igmp_sock
->interface
->name
);
759 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
,
761 igmp_source_delete(src
);
764 if (group
->t_group_query_retransmit_timer
) {
765 THREAD_OFF(group
->t_group_query_retransmit_timer
);
768 group_timer_off(group
);
769 listnode_delete(group
->group_igmp_sock
->igmp_group_list
, group
);
770 hash_release(group
->group_igmp_sock
->igmp_group_hash
, group
);
772 igmp_group_free(group
);
775 void igmp_group_delete_empty_include(struct igmp_group
*group
)
777 zassert(!group
->group_filtermode_isexcl
);
778 zassert(!listcount(group
->group_source_list
));
780 igmp_group_delete(group
);
783 void igmp_sock_free(struct igmp_sock
*igmp
)
785 zassert(!igmp
->t_igmp_read
);
786 zassert(!igmp
->t_igmp_query_timer
);
787 zassert(!igmp
->t_other_querier_timer
);
788 zassert(igmp
->igmp_group_list
);
789 zassert(!listcount(igmp
->igmp_group_list
));
791 list_delete(&igmp
->igmp_group_list
);
792 hash_free(igmp
->igmp_group_hash
);
794 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
797 void igmp_sock_delete(struct igmp_sock
*igmp
)
799 struct pim_interface
*pim_ifp
;
800 struct listnode
*grp_node
;
801 struct listnode
*grp_nextnode
;
802 struct igmp_group
*grp
;
804 for (ALL_LIST_ELEMENTS(igmp
->igmp_group_list
, grp_node
, grp_nextnode
,
806 igmp_group_delete(grp
);
811 pim_ifp
= igmp
->interface
->info
;
813 listnode_delete(pim_ifp
->igmp_socket_list
, igmp
);
815 igmp_sock_free(igmp
);
818 void igmp_sock_delete_all(struct interface
*ifp
)
820 struct pim_interface
*pim_ifp
;
821 struct listnode
*igmp_node
, *igmp_nextnode
;
822 struct igmp_sock
*igmp
;
826 for (ALL_LIST_ELEMENTS(pim_ifp
->igmp_socket_list
, igmp_node
,
827 igmp_nextnode
, igmp
)) {
828 igmp_sock_delete(igmp
);
832 static unsigned int igmp_group_hash_key(const void *arg
)
834 const struct igmp_group
*group
= arg
;
836 return jhash_1word(group
->group_addr
.s_addr
, 0);
839 static bool igmp_group_hash_equal(const void *arg1
, const void *arg2
)
841 const struct igmp_group
*g1
= (const struct igmp_group
*)arg1
;
842 const struct igmp_group
*g2
= (const struct igmp_group
*)arg2
;
844 if (g1
->group_addr
.s_addr
== g2
->group_addr
.s_addr
)
850 static struct igmp_sock
*igmp_sock_new(int fd
, struct in_addr ifaddr
,
851 struct interface
*ifp
, int mtrace_only
)
853 struct pim_interface
*pim_ifp
;
854 struct igmp_sock
*igmp
;
859 if (PIM_DEBUG_IGMP_TRACE
) {
861 "Creating IGMP socket fd=%d for address %s on interface %s",
862 fd
, inet_ntoa(ifaddr
), ifp
->name
);
865 igmp
= XCALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
867 igmp
->igmp_group_list
= list_new();
868 igmp
->igmp_group_list
->del
= (void (*)(void *))igmp_group_free
;
870 snprintf(hash_name
, 64, "IGMP %s hash", ifp
->name
);
871 igmp
->igmp_group_hash
= hash_create(igmp_group_hash_key
,
872 igmp_group_hash_equal
, hash_name
);
875 igmp
->interface
= ifp
;
876 igmp
->ifaddr
= ifaddr
;
877 igmp
->t_igmp_read
= NULL
;
878 igmp
->t_igmp_query_timer
= NULL
;
879 igmp
->t_other_querier_timer
= NULL
; /* no other querier present */
880 igmp
->querier_robustness_variable
=
881 pim_ifp
->igmp_default_robustness_variable
;
882 igmp
->sock_creation
= pim_time_monotonic_sec();
884 igmp_stats_init(&igmp
->rx_stats
);
887 igmp
->mtrace_only
= mtrace_only
;
891 igmp
->mtrace_only
= false;
894 igmp_startup_mode_on() will reset QQI:
896 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
898 igmp_startup_mode_on(igmp
);
899 pim_igmp_general_query_on(igmp
);
904 static void igmp_read_on(struct igmp_sock
*igmp
);
906 static int pim_igmp_read(struct thread
*t
)
909 struct igmp_sock
*igmp
= (struct igmp_sock
*)THREAD_ARG(t
);
910 struct sockaddr_in from
;
911 struct sockaddr_in to
;
912 socklen_t fromlen
= sizeof(from
);
913 socklen_t tolen
= sizeof(to
);
914 ifindex_t ifindex
= -1;
918 len
= pim_socket_recvfromto(igmp
->fd
, buf
, sizeof(buf
), &from
,
919 &fromlen
, &to
, &tolen
, &ifindex
);
923 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
935 static void igmp_read_on(struct igmp_sock
*igmp
)
938 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
939 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
942 igmp
->t_igmp_read
= NULL
;
943 thread_add_read(router
->master
, pim_igmp_read
, igmp
, igmp
->fd
,
947 struct igmp_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
948 struct in_addr ifaddr
,
949 struct interface
*ifp
,
952 struct pim_interface
*pim_ifp
;
953 struct igmp_sock
*igmp
;
958 fd
= igmp_sock_open(ifaddr
, ifp
, pim_ifp
->options
);
960 zlog_warn("Could not open IGMP socket for %s on %s",
961 inet_ntoa(ifaddr
), ifp
->name
);
965 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
, mtrace_only
);
969 listnode_add(igmp_sock_list
, igmp
);
971 #ifdef IGMP_SOCK_DUMP
972 igmp_sock_dump(igmp_sock_array
);
979 RFC 3376: 6.5. Switching Router Filter-Modes
981 When a router's filter-mode for a group is EXCLUDE and the group
982 timer expires, the router filter-mode for the group transitions to
985 A router uses source records with running source timers as its state
986 for the switch to a filter-mode of INCLUDE. If there are any source
987 records with source timers greater than zero (i.e., requested to be
988 forwarded), a router switches to filter-mode of INCLUDE using those
989 source records. Source records whose timers are zero (from the
990 previous EXCLUDE mode) are deleted.
992 static int igmp_group_timer(struct thread
*t
)
994 struct igmp_group
*group
;
996 group
= THREAD_ARG(t
);
998 if (PIM_DEBUG_IGMP_TRACE
) {
999 char group_str
[INET_ADDRSTRLEN
];
1000 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1002 zlog_debug("%s: Timer for group %s on interface %s",
1003 __PRETTY_FUNCTION__
, group_str
,
1004 group
->group_igmp_sock
->interface
->name
);
1007 zassert(group
->group_filtermode_isexcl
);
1009 group
->group_filtermode_isexcl
= 0;
1011 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1012 igmp_anysource_forward_stop(group
);
1014 igmp_source_delete_expired(group
->group_source_list
);
1016 zassert(!group
->group_filtermode_isexcl
);
1019 RFC 3376: 6.2.2. Definition of Group Timers
1021 If there are no more source records for the group, delete group
1024 if (listcount(group
->group_source_list
) < 1) {
1025 igmp_group_delete_empty_include(group
);
1031 static void group_timer_off(struct igmp_group
*group
)
1033 if (!group
->t_group_timer
)
1036 if (PIM_DEBUG_IGMP_TRACE
) {
1037 char group_str
[INET_ADDRSTRLEN
];
1038 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1040 zlog_debug("Cancelling TIMER event for group %s on %s",
1041 group_str
, group
->group_igmp_sock
->interface
->name
);
1043 THREAD_OFF(group
->t_group_timer
);
1046 void igmp_group_timer_on(struct igmp_group
*group
, long interval_msec
,
1049 group_timer_off(group
);
1051 if (PIM_DEBUG_IGMP_EVENTS
) {
1052 char group_str
[INET_ADDRSTRLEN
];
1053 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1056 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1057 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
1062 RFC 3376: 6.2.2. Definition of Group Timers
1064 The group timer is only used when a group is in EXCLUDE mode and
1065 it represents the time for the *filter-mode* of the group to
1066 expire and switch to INCLUDE mode.
1068 zassert(group
->group_filtermode_isexcl
);
1070 thread_add_timer_msec(router
->master
, igmp_group_timer
, group
,
1071 interval_msec
, &group
->t_group_timer
);
1074 struct igmp_group
*find_group_by_addr(struct igmp_sock
*igmp
,
1075 struct in_addr group_addr
)
1077 struct igmp_group lookup
;
1079 lookup
.group_addr
.s_addr
= group_addr
.s_addr
;
1081 return hash_lookup(igmp
->igmp_group_hash
, &lookup
);
1084 struct igmp_group
*igmp_add_group_by_addr(struct igmp_sock
*igmp
,
1085 struct in_addr group_addr
)
1087 struct igmp_group
*group
;
1089 group
= find_group_by_addr(igmp
, group_addr
);
1094 if (!pim_is_group_224_4(group_addr
)) {
1095 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1096 __PRETTY_FUNCTION__
);
1100 if (pim_is_group_224_0_0_0_24(group_addr
)) {
1101 zlog_warn("%s: Group specified is part of 224.0.0.0/24",
1102 __PRETTY_FUNCTION__
);
1106 Non-existant group is created as INCLUDE {empty}:
1108 RFC 3376 - 5.1. Action on Change of Interface State
1110 If no interface state existed for that multicast address before
1111 the change (i.e., the change consisted of creating a new
1112 per-interface record), or if no state exists after the change
1113 (i.e., the change consisted of deleting a per-interface record),
1114 then the "non-existent" state is considered to have a filter mode
1115 of INCLUDE and an empty source list.
1118 group
= XCALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1120 group
->group_source_list
= list_new();
1121 group
->group_source_list
->del
= (void (*)(void *))igmp_source_free
;
1123 group
->t_group_timer
= NULL
;
1124 group
->t_group_query_retransmit_timer
= NULL
;
1125 group
->group_specific_query_retransmit_count
= 0;
1126 group
->group_addr
= group_addr
;
1127 group
->group_igmp_sock
= igmp
;
1128 group
->last_igmp_v1_report_dsec
= -1;
1129 group
->last_igmp_v2_report_dsec
= -1;
1130 group
->group_creation
= pim_time_monotonic_sec();
1131 group
->igmp_version
= IGMP_DEFAULT_VERSION
;
1133 /* initialize new group as INCLUDE {empty} */
1134 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1136 listnode_add(igmp
->igmp_group_list
, group
);
1137 group
= hash_get(igmp
->igmp_group_hash
, group
, hash_alloc_intern
);
1139 if (PIM_DEBUG_IGMP_TRACE
) {
1140 char group_str
[INET_ADDRSTRLEN
];
1141 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1144 "Creating new IGMP group %s on socket %d interface %s",
1145 group_str
, igmp
->fd
, igmp
->interface
->name
);
1149 RFC 3376: 6.2.2. Definition of Group Timers
1151 The group timer is only used when a group is in EXCLUDE mode and
1152 it represents the time for the *filter-mode* of the group to
1153 expire and switch to INCLUDE mode.
1155 zassert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1156 zassert(!group
->t_group_timer
); /* group timer == 0 */
1158 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1159 igmp_anysource_forward_stop(group
);
1164 void igmp_send_query(int igmp_version
, struct igmp_group
*group
, int fd
,
1165 const char *ifname
, char *query_buf
, int query_buf_size
,
1166 int num_sources
, struct in_addr dst_addr
,
1167 struct in_addr group_addr
,
1168 int query_max_response_time_dsec
, uint8_t s_flag
,
1169 uint8_t querier_robustness_variable
,
1170 uint16_t querier_query_interval
)
1172 if (igmp_version
== 3) {
1173 igmp_v3_send_query(group
, fd
, ifname
, query_buf
, query_buf_size
,
1174 num_sources
, dst_addr
, group_addr
,
1175 query_max_response_time_dsec
, s_flag
,
1176 querier_robustness_variable
,
1177 querier_query_interval
);
1178 } else if (igmp_version
== 2) {
1179 igmp_v2_send_query(group
, fd
, ifname
, query_buf
, dst_addr
,
1180 group_addr
, query_max_response_time_dsec
);