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
30 #include "pim_igmpv2.h"
31 #include "pim_igmpv3.h"
32 #include "pim_igmp_mtrace.h"
33 #include "pim_iface.h"
35 #include "pim_mroute.h"
39 #include "pim_zebra.h"
41 static void group_timer_off(struct igmp_group
*group
);
42 static int pim_igmp_general_query(struct thread
*t
);
44 /* This socket is used for TXing IGMP packets only, IGMP RX happens
47 static int igmp_sock_open(struct in_addr ifaddr
, struct interface
*ifp
,
54 fd
= pim_socket_mcast(IPPROTO_IGMP
, ifaddr
, ifp
, 1);
59 if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options
)) {
60 if (inet_aton(PIM_ALL_ROUTERS
, &group
)) {
61 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
))
65 "%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
66 __FILE__
, __PRETTY_FUNCTION__
, fd
,
67 inet_ntoa(ifaddr
), PIM_ALL_ROUTERS
, errno
,
68 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__
, __PRETTY_FUNCTION__
, 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__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
95 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
100 "IGMP socket fd=%d could not join any group on interface address %s",
101 fd
, inet_ntoa(ifaddr
));
109 #undef IGMP_SOCK_DUMP
111 #ifdef IGMP_SOCK_DUMP
112 static void igmp_sock_dump(array_t
*igmp_sock_array
)
114 int size
= array_size(igmp_sock_array
);
115 for (int i
= 0; i
< size
; ++i
) {
117 struct igmp_sock
*igmp
= array_get(igmp_sock_array
, i
);
119 zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d", __FILE__
,
120 __PRETTY_FUNCTION__
, i
, size
,
121 inet_ntoa(igmp
->ifaddr
), igmp
->fd
);
126 struct igmp_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
127 struct in_addr ifaddr
)
129 struct listnode
*sock_node
;
130 struct igmp_sock
*igmp
;
132 #ifdef IGMP_SOCK_DUMP
133 igmp_sock_dump(igmp_sock_list
);
136 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
137 if (ifaddr
.s_addr
== igmp
->ifaddr
.s_addr
)
143 struct igmp_sock
*igmp_sock_lookup_by_fd(struct list
*igmp_sock_list
, int fd
)
145 struct listnode
*sock_node
;
146 struct igmp_sock
*igmp
;
148 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
155 static int pim_igmp_other_querier_expire(struct thread
*t
)
157 struct igmp_sock
*igmp
;
159 igmp
= THREAD_ARG(t
);
161 zassert(!igmp
->t_igmp_query_timer
);
163 if (PIM_DEBUG_IGMP_TRACE
) {
164 char ifaddr_str
[INET_ADDRSTRLEN
];
165 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
167 zlog_debug("%s: Querier %s resuming", __PRETTY_FUNCTION__
,
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(master
, pim_igmp_other_querier_expire
, igmp
,
252 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 /* Collecting IGMP Rx stats */
307 switch (query_version
) {
309 igmp
->rx_stats
.query_v1
++;
312 igmp
->rx_stats
.query_v2
++;
315 igmp
->rx_stats
.query_v3
++;
318 igmp
->rx_stats
.unsupported
++;
322 * RFC 3376 defines some guidelines on operating in backwards
323 * compatibility with older versions of IGMP but there are some gaps in
326 * - once we drop from say version 3 to version 2 we will never go back
327 * to version 3 even if the node that TXed an IGMP v2 query upgrades
330 * - The node with the lowest IP is the querier so we will only know to
331 * drop from v3 to v2 if the node that is the querier is also the one
332 * that is running igmp v2. If a non-querier only supports igmp v2
333 * we will have no way of knowing.
335 * For now we will simplify things and inform the user that they need to
336 * configure all PIM routers to use the same version of IGMP.
338 if (query_version
!= pim_ifp
->igmp_version
) {
340 "Recv IGMP query v%d from %s on %s but we are using v%d, please "
341 "configure all PIM routers on this subnet to use the same "
343 query_version
, from_str
, ifp
->name
,
344 pim_ifp
->igmp_version
);
348 if (PIM_DEBUG_IGMP_PACKETS
) {
349 char group_str
[INET_ADDRSTRLEN
];
350 pim_inet4_dump("<group?>", group_addr
, group_str
,
352 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
353 query_version
, from_str
, ifp
->name
, group_str
);
357 RFC 3376: 6.6.2. Querier Election
359 When a router receives a query with a lower IP address, it sets
360 the Other-Querier-Present timer to Other Querier Present Interval
361 and ceases to send queries on the network if it was the previously
364 if (ntohl(from
.s_addr
) < ntohl(igmp
->ifaddr
.s_addr
)) {
366 if (PIM_DEBUG_IGMP_TRACE
) {
367 char ifaddr_str
[INET_ADDRSTRLEN
];
368 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
371 "%s: local address %s (%u) lost querier election to %s (%u)",
372 ifp
->name
, ifaddr_str
,
373 ntohl(igmp
->ifaddr
.s_addr
), from_str
,
377 pim_igmp_other_querier_timer_on(igmp
);
380 /* IGMP version 3 is the only one where we process the RXed query */
381 if (query_version
== 3) {
382 igmp_v3_recv_query(igmp
, from_str
, igmp_msg
);
388 static void on_trace(const char *label
, struct interface
*ifp
,
391 if (PIM_DEBUG_IGMP_TRACE
) {
392 char from_str
[INET_ADDRSTRLEN
];
393 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
394 zlog_debug("%s: from %s on %s", label
, from_str
, ifp
->name
);
398 static int igmp_v1_recv_report(struct igmp_sock
*igmp
, struct in_addr from
,
399 const char *from_str
, char *igmp_msg
,
402 struct interface
*ifp
= igmp
->interface
;
403 struct igmp_group
*group
;
404 struct in_addr group_addr
;
406 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
);
408 if (igmp
->mtrace_only
)
411 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
413 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
414 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
418 /* Collecting IGMP Rx stats */
419 igmp
->rx_stats
.report_v1
++;
421 if (PIM_DEBUG_IGMP_TRACE
) {
422 zlog_warn("%s %s: FIXME WRITEME", __FILE__
,
423 __PRETTY_FUNCTION__
);
426 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
428 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
431 /* non-existant group is created as INCLUDE {empty} */
432 group
= igmp_add_group_by_addr(igmp
, group_addr
);
437 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
442 int pim_igmp_packet(struct igmp_sock
*igmp
, char *buf
, size_t len
)
445 size_t ip_hlen
; /* ip header length in bytes */
449 char from_str
[INET_ADDRSTRLEN
];
450 char to_str
[INET_ADDRSTRLEN
];
452 if (len
< sizeof(*ip_hdr
)) {
453 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len
,
458 ip_hdr
= (struct ip
*)buf
;
460 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
461 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
463 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
465 if (PIM_DEBUG_IGMP_PACKETS
) {
467 "Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
468 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hlen
,
472 igmp_msg
= buf
+ ip_hlen
;
473 msg_type
= *igmp_msg
;
474 igmp_msg_len
= len
- ip_hlen
;
476 if (PIM_DEBUG_IGMP_PACKETS
) {
478 "Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
479 from_str
, to_str
, igmp
->interface
->name
, ip_hdr
->ip_ttl
,
480 msg_type
, igmp_msg_len
);
483 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
484 zlog_warn("IGMP message size=%d shorter than minimum=%d",
485 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
490 case PIM_IGMP_MEMBERSHIP_QUERY
: {
491 int max_resp_code
= igmp_msg
[1];
495 RFC 3376: 7.1. Query Version Distinctions
496 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
498 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
500 IGMPv3 Query: length >= 12 octets
503 if (igmp_msg_len
== 8) {
504 query_version
= max_resp_code
? 2 : 1;
505 } else if (igmp_msg_len
>= 12) {
508 zlog_warn("Unknown IGMP query version");
512 return igmp_recv_query(igmp
, query_version
, max_resp_code
,
513 ip_hdr
->ip_src
, from_str
, igmp_msg
,
517 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
518 return igmp_v3_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
519 igmp_msg
, igmp_msg_len
);
521 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
522 return igmp_v2_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
523 igmp_msg
, igmp_msg_len
);
525 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
526 return igmp_v1_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
527 igmp_msg
, igmp_msg_len
);
529 case PIM_IGMP_V2_LEAVE_GROUP
:
530 return igmp_v2_recv_leave(igmp
, ip_hdr
->ip_src
, from_str
,
531 igmp_msg
, igmp_msg_len
);
533 case PIM_IGMP_MTRACE_RESPONSE
:
534 return igmp_mtrace_recv_response(igmp
, ip_hdr
, ip_hdr
->ip_src
,
537 case PIM_IGMP_MTRACE_QUERY_REQUEST
:
538 return igmp_mtrace_recv_qry_req(igmp
, ip_hdr
, ip_hdr
->ip_src
,
543 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
545 /* Collecting IGMP Rx stats */
546 igmp
->rx_stats
.unsupported
++;
551 void pim_igmp_general_query_on(struct igmp_sock
*igmp
)
553 struct pim_interface
*pim_ifp
;
558 Since this socket is starting as querier,
559 there should not exist a timer for other-querier-present.
561 zassert(!igmp
->t_other_querier_timer
);
562 pim_ifp
= igmp
->interface
->info
;
566 RFC 3376: 8.6. Startup Query Interval
568 The Startup Query Interval is the interval between General Queries
569 sent by a Querier on startup. Default: 1/4 the Query Interval.
570 The first one should be sent out immediately instead of 125/4
573 startup_mode
= igmp
->startup_query_count
> 0;
576 * If this is the first time we are sending a query on a
577 * newly configured igmp interface send it out in 1 second
578 * just to give the entire world a tiny bit of time to settle
579 * else the query interval is:
580 * query_interval = pim_ifp->igmp_default_query_interval >> 2;
582 if (igmp
->startup_query_count
583 == igmp
->querier_robustness_variable
)
586 query_interval
= PIM_IGMP_SQI(
587 pim_ifp
->igmp_default_query_interval
);
589 --igmp
->startup_query_count
;
591 query_interval
= igmp
->querier_query_interval
;
594 if (PIM_DEBUG_IGMP_TRACE
) {
595 char ifaddr_str
[INET_ADDRSTRLEN
];
596 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
599 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
600 ifaddr_str
, query_interval
,
601 startup_mode
? "startup" : "non-startup", igmp
->fd
);
603 igmp
->t_igmp_query_timer
= NULL
;
604 thread_add_timer(master
, pim_igmp_general_query
, igmp
, query_interval
,
605 &igmp
->t_igmp_query_timer
);
608 void pim_igmp_general_query_off(struct igmp_sock
*igmp
)
612 if (PIM_DEBUG_IGMP_TRACE
) {
613 if (igmp
->t_igmp_query_timer
) {
614 char ifaddr_str
[INET_ADDRSTRLEN
];
615 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
618 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
619 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
622 THREAD_OFF(igmp
->t_igmp_query_timer
);
625 /* Issue IGMP general query */
626 static int pim_igmp_general_query(struct thread
*t
)
628 struct igmp_sock
*igmp
;
629 struct in_addr dst_addr
;
630 struct in_addr group_addr
;
631 struct pim_interface
*pim_ifp
;
634 igmp
= THREAD_ARG(t
);
636 zassert(igmp
->interface
);
637 zassert(igmp
->interface
->info
);
639 pim_ifp
= igmp
->interface
->info
;
641 if (pim_ifp
->igmp_version
== 3) {
642 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
644 query_buf_size
= IGMP_V12_MSG_SIZE
;
647 char query_buf
[query_buf_size
];
650 RFC3376: 4.1.12. IP Destination Addresses for Queries
652 In IGMPv3, General Queries are sent with an IP destination address
653 of 224.0.0.1, the all-systems multicast address. Group-Specific
654 and Group-and-Source-Specific Queries are sent with an IP
655 destination address equal to the multicast address of interest.
658 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
659 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
661 if (PIM_DEBUG_IGMP_TRACE
) {
662 char querier_str
[INET_ADDRSTRLEN
];
663 char dst_str
[INET_ADDRSTRLEN
];
664 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
665 sizeof(querier_str
));
666 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
667 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
668 querier_str
, dst_str
, igmp
->interface
->name
);
671 igmp_send_query(pim_ifp
->igmp_version
, 0 /* igmp_group */, igmp
->fd
,
672 igmp
->interface
->name
, query_buf
, sizeof(query_buf
),
673 0 /* num_sources */, dst_addr
, group_addr
,
674 pim_ifp
->igmp_query_max_response_time_dsec
,
675 1 /* s_flag: always set for general queries */,
676 igmp
->querier_robustness_variable
,
677 igmp
->querier_query_interval
);
679 pim_igmp_general_query_on(igmp
);
684 static void sock_close(struct igmp_sock
*igmp
)
686 pim_igmp_other_querier_timer_off(igmp
);
687 pim_igmp_general_query_off(igmp
);
689 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
690 if (igmp
->t_igmp_read
) {
692 "Cancelling READ event on IGMP socket %s fd=%d on interface %s",
693 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
694 igmp
->interface
->name
);
697 THREAD_OFF(igmp
->t_igmp_read
);
699 if (close(igmp
->fd
)) {
701 "Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
702 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
703 igmp
->interface
->name
, errno
, safe_strerror(errno
));
706 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
707 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
708 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
709 igmp
->interface
->name
);
713 void igmp_startup_mode_on(struct igmp_sock
*igmp
)
715 struct pim_interface
*pim_ifp
;
717 pim_ifp
= igmp
->interface
->info
;
720 RFC 3376: 8.7. Startup Query Count
722 The Startup Query Count is the number of Queries sent out on
723 startup, separated by the Startup Query Interval. Default: the
726 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
729 Since we're (re)starting, reset QQI to default Query Interval
731 igmp
->querier_query_interval
= pim_ifp
->igmp_default_query_interval
;
734 static void igmp_group_free(struct igmp_group
*group
)
736 list_delete_and_null(&group
->group_source_list
);
738 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
741 static void igmp_group_delete(struct igmp_group
*group
)
743 struct listnode
*src_node
;
744 struct listnode
*src_nextnode
;
745 struct igmp_source
*src
;
747 if (PIM_DEBUG_IGMP_TRACE
) {
748 char group_str
[INET_ADDRSTRLEN
];
749 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
751 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
752 group_str
, group
->group_igmp_sock
->fd
,
753 group
->group_igmp_sock
->interface
->name
);
756 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
,
758 igmp_source_delete(src
);
761 if (group
->t_group_query_retransmit_timer
) {
762 THREAD_OFF(group
->t_group_query_retransmit_timer
);
765 group_timer_off(group
);
766 listnode_delete(group
->group_igmp_sock
->igmp_group_list
, group
);
767 hash_release(group
->group_igmp_sock
->igmp_group_hash
, group
);
769 igmp_group_free(group
);
772 void igmp_group_delete_empty_include(struct igmp_group
*group
)
774 zassert(!group
->group_filtermode_isexcl
);
775 zassert(!listcount(group
->group_source_list
));
777 igmp_group_delete(group
);
780 void igmp_sock_free(struct igmp_sock
*igmp
)
782 zassert(!igmp
->t_igmp_read
);
783 zassert(!igmp
->t_igmp_query_timer
);
784 zassert(!igmp
->t_other_querier_timer
);
785 zassert(igmp
->igmp_group_list
);
786 zassert(!listcount(igmp
->igmp_group_list
));
788 list_delete_and_null(&igmp
->igmp_group_list
);
789 hash_free(igmp
->igmp_group_hash
);
791 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
794 void igmp_sock_delete(struct igmp_sock
*igmp
)
796 struct pim_interface
*pim_ifp
;
797 struct listnode
*grp_node
;
798 struct listnode
*grp_nextnode
;
799 struct igmp_group
*grp
;
801 for (ALL_LIST_ELEMENTS(igmp
->igmp_group_list
, grp_node
, grp_nextnode
,
803 igmp_group_delete(grp
);
808 pim_ifp
= igmp
->interface
->info
;
810 listnode_delete(pim_ifp
->igmp_socket_list
, igmp
);
812 igmp_sock_free(igmp
);
815 void igmp_sock_delete_all(struct interface
*ifp
)
817 struct pim_interface
*pim_ifp
;
818 struct listnode
*igmp_node
, *igmp_nextnode
;
819 struct igmp_sock
*igmp
;
823 for (ALL_LIST_ELEMENTS(pim_ifp
->igmp_socket_list
, igmp_node
,
824 igmp_nextnode
, igmp
)) {
825 igmp_sock_delete(igmp
);
829 static unsigned int igmp_group_hash_key(void *arg
)
831 struct igmp_group
*group
= (struct igmp_group
*)arg
;
833 return jhash_1word(group
->group_addr
.s_addr
, 0);
836 static int igmp_group_hash_equal(const void *arg1
, const void *arg2
)
838 const struct igmp_group
*g1
= (const struct igmp_group
*)arg1
;
839 const struct igmp_group
*g2
= (const struct igmp_group
*)arg2
;
841 if (g1
->group_addr
.s_addr
== g2
->group_addr
.s_addr
)
847 static struct igmp_sock
*igmp_sock_new(int fd
, struct in_addr ifaddr
,
848 struct interface
*ifp
, int mtrace_only
)
850 struct pim_interface
*pim_ifp
;
851 struct igmp_sock
*igmp
;
856 if (PIM_DEBUG_IGMP_TRACE
) {
858 "Creating IGMP socket fd=%d for address %s on interface %s",
859 fd
, inet_ntoa(ifaddr
), ifp
->name
);
862 igmp
= XCALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
864 zlog_warn("%s %s: XCALLOC() failure", __FILE__
,
865 __PRETTY_FUNCTION__
);
869 igmp
->igmp_group_list
= list_new();
870 if (!igmp
->igmp_group_list
) {
871 zlog_err("%s %s: failure: igmp_group_list = list_new()",
872 __FILE__
, __PRETTY_FUNCTION__
);
875 igmp
->igmp_group_list
->del
= (void (*)(void *))igmp_group_free
;
877 snprintf(hash_name
, 64, "IGMP %s hash", ifp
->name
);
878 igmp
->igmp_group_hash
= hash_create(igmp_group_hash_key
,
879 igmp_group_hash_equal
, hash_name
);
882 igmp
->interface
= ifp
;
883 igmp
->ifaddr
= ifaddr
;
884 igmp
->t_igmp_read
= NULL
;
885 igmp
->t_igmp_query_timer
= NULL
;
886 igmp
->t_other_querier_timer
= NULL
; /* no other querier present */
887 igmp
->querier_robustness_variable
=
888 pim_ifp
->igmp_default_robustness_variable
;
889 igmp
->sock_creation
= pim_time_monotonic_sec();
891 igmp_stats_init(&igmp
->rx_stats
);
894 igmp
->mtrace_only
= mtrace_only
;
898 igmp
->mtrace_only
= false;
901 igmp_startup_mode_on() will reset QQI:
903 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
905 igmp_startup_mode_on(igmp
);
906 pim_igmp_general_query_on(igmp
);
911 static void igmp_read_on(struct igmp_sock
*igmp
);
913 static int pim_igmp_read(struct thread
*t
)
916 struct igmp_sock
*igmp
= (struct igmp_sock
*)THREAD_ARG(t
);
917 struct sockaddr_in from
;
918 struct sockaddr_in to
;
919 socklen_t fromlen
= sizeof(from
);
920 socklen_t tolen
= sizeof(to
);
921 ifindex_t ifindex
= -1;
926 len
= pim_socket_recvfromto(igmp
->fd
, buf
, sizeof(buf
), &from
,
927 &fromlen
, &to
, &tolen
, &ifindex
);
931 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
943 static void igmp_read_on(struct igmp_sock
*igmp
)
946 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
947 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
950 igmp
->t_igmp_read
= NULL
;
951 thread_add_read(master
, pim_igmp_read
, igmp
, igmp
->fd
,
955 struct igmp_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
956 struct in_addr ifaddr
,
957 struct interface
*ifp
,
960 struct pim_interface
*pim_ifp
;
961 struct igmp_sock
*igmp
;
966 fd
= igmp_sock_open(ifaddr
, ifp
, pim_ifp
->options
);
968 zlog_warn("Could not open IGMP socket for %s on %s",
969 inet_ntoa(ifaddr
), ifp
->name
);
973 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
, mtrace_only
);
975 zlog_err("%s %s: igmp_sock_new() failure", __FILE__
,
976 __PRETTY_FUNCTION__
);
983 listnode_add(igmp_sock_list
, igmp
);
985 #ifdef IGMP_SOCK_DUMP
986 igmp_sock_dump(igmp_sock_array
);
993 RFC 3376: 6.5. Switching Router Filter-Modes
995 When a router's filter-mode for a group is EXCLUDE and the group
996 timer expires, the router filter-mode for the group transitions to
999 A router uses source records with running source timers as its state
1000 for the switch to a filter-mode of INCLUDE. If there are any source
1001 records with source timers greater than zero (i.e., requested to be
1002 forwarded), a router switches to filter-mode of INCLUDE using those
1003 source records. Source records whose timers are zero (from the
1004 previous EXCLUDE mode) are deleted.
1006 static int igmp_group_timer(struct thread
*t
)
1008 struct igmp_group
*group
;
1010 group
= THREAD_ARG(t
);
1012 if (PIM_DEBUG_IGMP_TRACE
) {
1013 char group_str
[INET_ADDRSTRLEN
];
1014 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1016 zlog_debug("%s: Timer for group %s on interface %s",
1017 __PRETTY_FUNCTION__
, group_str
,
1018 group
->group_igmp_sock
->interface
->name
);
1021 zassert(group
->group_filtermode_isexcl
);
1023 group
->group_filtermode_isexcl
= 0;
1025 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1026 igmp_anysource_forward_stop(group
);
1028 igmp_source_delete_expired(group
->group_source_list
);
1030 zassert(!group
->group_filtermode_isexcl
);
1033 RFC 3376: 6.2.2. Definition of Group Timers
1035 If there are no more source records for the group, delete group
1038 if (listcount(group
->group_source_list
) < 1) {
1039 igmp_group_delete_empty_include(group
);
1045 static void group_timer_off(struct igmp_group
*group
)
1047 if (!group
->t_group_timer
)
1050 if (PIM_DEBUG_IGMP_TRACE
) {
1051 char group_str
[INET_ADDRSTRLEN
];
1052 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1054 zlog_debug("Cancelling TIMER event for group %s on %s",
1055 group_str
, group
->group_igmp_sock
->interface
->name
);
1057 THREAD_OFF(group
->t_group_timer
);
1060 void igmp_group_timer_on(struct igmp_group
*group
, long interval_msec
,
1063 group_timer_off(group
);
1065 if (PIM_DEBUG_IGMP_EVENTS
) {
1066 char group_str
[INET_ADDRSTRLEN
];
1067 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1070 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1071 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
1076 RFC 3376: 6.2.2. Definition of Group Timers
1078 The group timer is only used when a group is in EXCLUDE mode and
1079 it represents the time for the *filter-mode* of the group to
1080 expire and switch to INCLUDE mode.
1082 zassert(group
->group_filtermode_isexcl
);
1084 thread_add_timer_msec(master
, igmp_group_timer
, group
, interval_msec
,
1085 &group
->t_group_timer
);
1088 struct igmp_group
*find_group_by_addr(struct igmp_sock
*igmp
,
1089 struct in_addr group_addr
)
1091 struct igmp_group lookup
;
1093 lookup
.group_addr
.s_addr
= group_addr
.s_addr
;
1095 return hash_lookup(igmp
->igmp_group_hash
, &lookup
);
1098 struct igmp_group
*igmp_add_group_by_addr(struct igmp_sock
*igmp
,
1099 struct in_addr group_addr
)
1101 struct igmp_group
*group
;
1103 group
= find_group_by_addr(igmp
, group_addr
);
1108 if (!pim_is_group_224_4(group_addr
)) {
1109 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1110 __PRETTY_FUNCTION__
);
1114 if (pim_is_group_224_0_0_0_24(group_addr
)) {
1115 zlog_warn("%s: Group specified is part of 224.0.0.0/24",
1116 __PRETTY_FUNCTION__
);
1120 Non-existant group is created as INCLUDE {empty}:
1122 RFC 3376 - 5.1. Action on Change of Interface State
1124 If no interface state existed for that multicast address before
1125 the change (i.e., the change consisted of creating a new
1126 per-interface record), or if no state exists after the change
1127 (i.e., the change consisted of deleting a per-interface record),
1128 then the "non-existent" state is considered to have a filter mode
1129 of INCLUDE and an empty source list.
1132 group
= XCALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1134 zlog_warn("%s %s: XCALLOC() failure", __FILE__
,
1135 __PRETTY_FUNCTION__
);
1136 return NULL
; /* error, not found, could not create */
1139 group
->group_source_list
= list_new();
1140 if (!group
->group_source_list
) {
1141 zlog_warn("%s %s: list_new() failure", __FILE__
,
1142 __PRETTY_FUNCTION__
);
1143 XFREE(MTYPE_PIM_IGMP_GROUP
, group
); /* discard group */
1144 return NULL
; /* error, not found, could not initialize */
1146 group
->group_source_list
->del
= (void (*)(void *))igmp_source_free
;
1148 group
->t_group_timer
= NULL
;
1149 group
->t_group_query_retransmit_timer
= NULL
;
1150 group
->group_specific_query_retransmit_count
= 0;
1151 group
->group_addr
= group_addr
;
1152 group
->group_igmp_sock
= igmp
;
1153 group
->last_igmp_v1_report_dsec
= -1;
1154 group
->last_igmp_v2_report_dsec
= -1;
1155 group
->group_creation
= pim_time_monotonic_sec();
1156 group
->igmp_version
= IGMP_DEFAULT_VERSION
;
1158 /* initialize new group as INCLUDE {empty} */
1159 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1161 listnode_add(igmp
->igmp_group_list
, group
);
1162 group
= hash_get(igmp
->igmp_group_hash
, group
, hash_alloc_intern
);
1164 if (PIM_DEBUG_IGMP_TRACE
) {
1165 char group_str
[INET_ADDRSTRLEN
];
1166 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1169 "Creating new IGMP group %s on socket %d interface %s",
1170 group_str
, igmp
->fd
, igmp
->interface
->name
);
1174 RFC 3376: 6.2.2. Definition of Group Timers
1176 The group timer is only used when a group is in EXCLUDE mode and
1177 it represents the time for the *filter-mode* of the group to
1178 expire and switch to INCLUDE mode.
1180 zassert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1181 zassert(!group
->t_group_timer
); /* group timer == 0 */
1183 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1184 igmp_anysource_forward_stop(group
);
1189 void igmp_send_query(int igmp_version
, struct igmp_group
*group
, int fd
,
1190 const char *ifname
, char *query_buf
, int query_buf_size
,
1191 int num_sources
, struct in_addr dst_addr
,
1192 struct in_addr group_addr
,
1193 int query_max_response_time_dsec
, uint8_t s_flag
,
1194 uint8_t querier_robustness_variable
,
1195 uint16_t querier_query_interval
)
1197 if (igmp_version
== 3) {
1198 igmp_v3_send_query(group
, fd
, ifname
, query_buf
, query_buf_size
,
1199 num_sources
, dst_addr
, group_addr
,
1200 query_max_response_time_dsec
, s_flag
,
1201 querier_robustness_variable
,
1202 querier_query_interval
);
1203 } else if (igmp_version
== 2) {
1204 igmp_v2_send_query(group
, fd
, ifname
, query_buf
, dst_addr
,
1205 group_addr
, query_max_response_time_dsec
);