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_iface.h"
34 #include "pim_mroute.h"
38 #include "pim_zebra.h"
40 static void group_timer_off(struct igmp_group
*group
);
42 /* This socket is used for TXing IGMP packets only, IGMP RX happens
45 static int igmp_sock_open(struct in_addr ifaddr
, struct interface
*ifp
, uint32_t pim_options
)
51 fd
= pim_socket_mcast(IPPROTO_IGMP
, ifaddr
, ifp
, 1);
56 if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options
)) {
57 if (inet_aton(PIM_ALL_ROUTERS
, &group
)) {
58 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
))
62 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
63 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
64 PIM_ALL_ROUTERS
, errno
, safe_strerror(errno
));
69 IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
70 IGMP routers must receive general queries for querier election.
72 if (inet_aton(PIM_ALL_SYSTEMS
, &group
)) {
73 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
))
77 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
78 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
79 PIM_ALL_SYSTEMS
, errno
, safe_strerror(errno
));
82 if (inet_aton(PIM_ALL_IGMP_ROUTERS
, &group
)) {
83 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
)) {
88 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
89 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
90 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
94 zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
95 fd
, inet_ntoa(ifaddr
));
103 #undef IGMP_SOCK_DUMP
105 #ifdef IGMP_SOCK_DUMP
106 static void igmp_sock_dump(array_t
*igmp_sock_array
)
108 int size
= array_size(igmp_sock_array
);
109 for (int i
= 0; i
< size
; ++i
) {
111 struct igmp_sock
*igmp
= array_get(igmp_sock_array
, i
);
113 zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
114 __FILE__
, __PRETTY_FUNCTION__
,
116 inet_ntoa(igmp
->ifaddr
),
122 struct igmp_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
123 struct in_addr ifaddr
)
125 struct listnode
*sock_node
;
126 struct igmp_sock
*igmp
;
128 #ifdef IGMP_SOCK_DUMP
129 igmp_sock_dump(igmp_sock_list
);
132 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
133 if (ifaddr
.s_addr
== igmp
->ifaddr
.s_addr
)
139 struct igmp_sock
*igmp_sock_lookup_by_fd(struct list
*igmp_sock_list
,
142 struct listnode
*sock_node
;
143 struct igmp_sock
*igmp
;
145 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
152 static int pim_igmp_other_querier_expire(struct thread
*t
)
154 struct igmp_sock
*igmp
;
156 igmp
= THREAD_ARG(t
);
158 zassert(igmp
->t_other_querier_timer
);
159 zassert(!igmp
->t_igmp_query_timer
);
161 if (PIM_DEBUG_IGMP_TRACE
) {
162 char ifaddr_str
[INET_ADDRSTRLEN
];
163 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
164 zlog_debug("%s: Querier %s resuming",
169 igmp
->t_other_querier_timer
= NULL
;
172 We are the current querier, then
173 re-start sending general queries.
175 pim_igmp_general_query_on(igmp
);
180 void pim_igmp_other_querier_timer_on(struct igmp_sock
*igmp
)
182 long other_querier_present_interval_msec
;
183 struct pim_interface
*pim_ifp
;
186 zassert(igmp
->interface
);
187 zassert(igmp
->interface
->info
);
189 pim_ifp
= igmp
->interface
->info
;
191 if (igmp
->t_other_querier_timer
) {
193 There is other querier present already,
194 then reset the other-querier-present timer.
197 if (PIM_DEBUG_IGMP_TRACE
) {
198 char ifaddr_str
[INET_ADDRSTRLEN
];
199 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
200 zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
204 THREAD_OFF(igmp
->t_other_querier_timer
);
205 zassert(!igmp
->t_other_querier_timer
);
209 We are the current querier, then stop sending general queries:
210 igmp->t_igmp_query_timer = NULL;
212 pim_igmp_general_query_off(igmp
);
216 Since this socket is starting the other-querier-present timer,
217 there should not be periodic query timer for this socket.
219 zassert(!igmp
->t_igmp_query_timer
);
222 RFC 3376: 8.5. Other Querier Present Interval
224 The Other Querier Present Interval is the length of time that must
225 pass before a multicast router decides that there is no longer
226 another multicast router which should be the querier. This value
227 MUST be ((the Robustness Variable) times (the Query Interval)) plus
228 (one half of one Query Response Interval).
230 other_querier_present_interval_msec = \
231 igmp->querier_robustness_variable * \
232 1000 * igmp->querier_query_interval + \
233 100 * (pim_ifp->query_max_response_time_dsec >> 1);
235 other_querier_present_interval_msec
=
236 PIM_IGMP_OQPI_MSEC(igmp
->querier_robustness_variable
,
237 igmp
->querier_query_interval
,
238 pim_ifp
->igmp_query_max_response_time_dsec
);
240 if (PIM_DEBUG_IGMP_TRACE
) {
241 char ifaddr_str
[INET_ADDRSTRLEN
];
242 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
243 zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
245 other_querier_present_interval_msec
/ 1000,
246 other_querier_present_interval_msec
% 1000);
249 thread_add_timer_msec(master
, pim_igmp_other_querier_expire
, igmp
,
250 other_querier_present_interval_msec
,
251 &igmp
->t_other_querier_timer
);
254 void pim_igmp_other_querier_timer_off(struct igmp_sock
*igmp
)
258 if (PIM_DEBUG_IGMP_TRACE
) {
259 if (igmp
->t_other_querier_timer
) {
260 char ifaddr_str
[INET_ADDRSTRLEN
];
261 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
262 zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
263 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
266 THREAD_OFF(igmp
->t_other_querier_timer
);
267 zassert(!igmp
->t_other_querier_timer
);
271 igmp_recv_query(struct igmp_sock
*igmp
, int query_version
,
273 struct in_addr from
, const char *from_str
,
274 char *igmp_msg
, int igmp_msg_len
)
276 struct interface
*ifp
;
277 struct pim_interface
*pim_ifp
;
278 struct in_addr group_addr
;
279 uint16_t recv_checksum
;
282 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
284 ifp
= igmp
->interface
;
287 recv_checksum
= *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
289 /* for computing checksum */
290 *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
292 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
293 if (checksum
!= recv_checksum
) {
294 zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
295 query_version
, from_str
, ifp
->name
, recv_checksum
, checksum
);
299 /* RFC 3376 defines some guidelines on operating in backwards compatibility
300 * with older versions of IGMP but there are some gaps in the logic:
302 * - once we drop from say version 3 to version 2 we will never go back to
303 * version 3 even if the node that TXed an IGMP v2 query upgrades to v3
305 * - The node with the lowest IP is the querier so we will only know to drop
306 * from v3 to v2 if the node that is the querier is also the one that is
307 * running igmp v2. If a non-querier only supports igmp v2 we will have
310 * For now we will simplify things and inform the user that they need to
311 * configure all PIM routers to use the same version of IGMP.
313 if (query_version
!= pim_ifp
->igmp_version
) {
314 zlog_warn("Recv IGMP query v%d from %s on %s but we are using v%d, please "
315 "configure all PIM routers on this subnet to use the same "
317 query_version
, from_str
, ifp
->name
, pim_ifp
->igmp_version
);
321 if (PIM_DEBUG_IGMP_PACKETS
) {
322 char group_str
[INET_ADDRSTRLEN
];
323 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
324 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
325 query_version
, from_str
, ifp
->name
, group_str
);
329 RFC 3376: 6.6.2. Querier Election
331 When a router receives a query with a lower IP address, it sets
332 the Other-Querier-Present timer to Other Querier Present Interval
333 and ceases to send queries on the network if it was the previously
336 if (ntohl(from
.s_addr
) < ntohl(igmp
->ifaddr
.s_addr
)) {
338 if (PIM_DEBUG_IGMP_TRACE
) {
339 char ifaddr_str
[INET_ADDRSTRLEN
];
340 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
341 zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
343 ifaddr_str
, ntohl(igmp
->ifaddr
.s_addr
),
344 from_str
, ntohl(from
.s_addr
));
347 pim_igmp_other_querier_timer_on(igmp
);
350 /* IGMP version 3 is the only one where we process the RXed query */
351 if (query_version
== 3) {
352 igmp_v3_recv_query(igmp
, from_str
, igmp_msg
);
358 static void on_trace(const char *label
,
359 struct interface
*ifp
, struct in_addr from
)
361 if (PIM_DEBUG_IGMP_TRACE
) {
362 char from_str
[INET_ADDRSTRLEN
];
363 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
364 zlog_debug("%s: from %s on %s",
365 label
, from_str
, ifp
->name
);
370 igmp_v1_recv_report (struct igmp_sock
*igmp
,
371 struct in_addr from
, const char *from_str
,
372 char *igmp_msg
, int igmp_msg_len
)
374 struct interface
*ifp
= igmp
->interface
;
375 struct igmp_group
*group
;
376 struct in_addr group_addr
;
378 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
);
380 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
381 zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
382 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
386 if (PIM_DEBUG_IGMP_TRACE
) {
387 zlog_warn("%s %s: FIXME WRITEME",
388 __FILE__
, __PRETTY_FUNCTION__
);
391 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
393 /* non-existant group is created as INCLUDE {empty} */
394 group
= igmp_add_group_by_addr(igmp
, group_addr
);
399 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
404 int pim_igmp_packet(struct igmp_sock
*igmp
, char *buf
, size_t len
)
407 size_t ip_hlen
; /* ip header length in bytes */
411 char from_str
[INET_ADDRSTRLEN
];
412 char to_str
[INET_ADDRSTRLEN
];
414 if (len
< sizeof(*ip_hdr
)) {
415 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
416 len
, sizeof(*ip_hdr
));
420 ip_hdr
= (struct ip
*) buf
;
422 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
423 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
425 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
427 if (PIM_DEBUG_IGMP_PACKETS
) {
428 zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
429 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hlen
, ip_hdr
->ip_p
);
432 igmp_msg
= buf
+ ip_hlen
;
433 msg_type
= *igmp_msg
;
434 igmp_msg_len
= len
- ip_hlen
;
436 if (PIM_DEBUG_IGMP_PACKETS
) {
437 zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
438 from_str
, to_str
, igmp
->interface
->name
, ip_hdr
->ip_ttl
, msg_type
,
442 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
443 zlog_warn("IGMP message size=%d shorter than minimum=%d",
444 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
449 case PIM_IGMP_MEMBERSHIP_QUERY
:
451 int max_resp_code
= igmp_msg
[1];
455 RFC 3376: 7.1. Query Version Distinctions
456 IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
457 IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
458 IGMPv3 Query: length >= 12 octets
461 if (igmp_msg_len
== 8) {
462 query_version
= max_resp_code
? 2 : 1;
464 else if (igmp_msg_len
>= 12) {
468 zlog_warn("Unknown IGMP query version");
472 return igmp_recv_query(igmp
, query_version
, max_resp_code
,
473 ip_hdr
->ip_src
, from_str
,
474 igmp_msg
, igmp_msg_len
);
477 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
478 return igmp_v3_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
479 igmp_msg
, igmp_msg_len
);
481 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
482 return igmp_v2_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
483 igmp_msg
, igmp_msg_len
);
485 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
486 return igmp_v1_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
487 igmp_msg
, igmp_msg_len
);
489 case PIM_IGMP_V2_LEAVE_GROUP
:
490 return igmp_v2_recv_leave(igmp
, ip_hdr
->ip_src
, from_str
,
491 igmp_msg
, igmp_msg_len
);
494 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
499 static int pim_igmp_general_query(struct thread
*t
);
501 void pim_igmp_general_query_on(struct igmp_sock
*igmp
)
503 struct pim_interface
*pim_ifp
;
508 Since this socket is starting as querier,
509 there should not exist a timer for other-querier-present.
511 zassert(!igmp
->t_other_querier_timer
);
512 pim_ifp
= igmp
->interface
->info
;
516 RFC 3376: 8.6. Startup Query Interval
518 The Startup Query Interval is the interval between General Queries
519 sent by a Querier on startup. Default: 1/4 the Query Interval.
520 The first one should be sent out immediately instead of 125/4
523 startup_mode
= igmp
->startup_query_count
> 0;
526 * If this is the first time we are sending a query on a
527 * newly configured igmp interface send it out in 1 second
528 * just to give the entire world a tiny bit of time to settle
529 * else the query interval is:
530 * query_interval = pim_ifp->igmp_default_query_interval >> 2;
532 if (igmp
->startup_query_count
== igmp
->querier_robustness_variable
)
535 query_interval
= PIM_IGMP_SQI(pim_ifp
->igmp_default_query_interval
);
537 --igmp
->startup_query_count
;
540 query_interval
= igmp
->querier_query_interval
;
543 if (PIM_DEBUG_IGMP_TRACE
) {
544 char ifaddr_str
[INET_ADDRSTRLEN
];
545 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
546 zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
549 startup_mode
? "startup" : "non-startup",
552 igmp
->t_igmp_query_timer
= NULL
;
553 thread_add_timer(master
, pim_igmp_general_query
, igmp
, query_interval
,
554 &igmp
->t_igmp_query_timer
);
557 void pim_igmp_general_query_off(struct igmp_sock
*igmp
)
561 if (PIM_DEBUG_IGMP_TRACE
) {
562 if (igmp
->t_igmp_query_timer
) {
563 char ifaddr_str
[INET_ADDRSTRLEN
];
564 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
565 zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
566 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
569 THREAD_OFF(igmp
->t_igmp_query_timer
);
572 /* Issue IGMP general query */
573 static int pim_igmp_general_query(struct thread
*t
)
575 struct igmp_sock
*igmp
;
576 struct in_addr dst_addr
;
577 struct in_addr group_addr
;
578 struct pim_interface
*pim_ifp
;
581 igmp
= THREAD_ARG(t
);
583 zassert(igmp
->interface
);
584 zassert(igmp
->interface
->info
);
586 pim_ifp
= igmp
->interface
->info
;
588 if (pim_ifp
->igmp_version
== 3) {
589 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
591 query_buf_size
= IGMP_V12_MSG_SIZE
;
594 char query_buf
[query_buf_size
];
597 RFC3376: 4.1.12. IP Destination Addresses for Queries
599 In IGMPv3, General Queries are sent with an IP destination address
600 of 224.0.0.1, the all-systems multicast address. Group-Specific
601 and Group-and-Source-Specific Queries are sent with an IP
602 destination address equal to the multicast address of interest.
605 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
606 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
608 if (PIM_DEBUG_IGMP_TRACE
) {
609 char querier_str
[INET_ADDRSTRLEN
];
610 char dst_str
[INET_ADDRSTRLEN
];
611 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
612 sizeof(querier_str
));
613 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
614 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
615 querier_str
, dst_str
, igmp
->interface
->name
);
618 igmp_send_query (pim_ifp
->igmp_version
,
621 igmp
->interface
->name
,
627 pim_ifp
->igmp_query_max_response_time_dsec
,
628 1 /* s_flag: always set for general queries */,
629 igmp
->querier_robustness_variable
,
630 igmp
->querier_query_interval
);
632 pim_igmp_general_query_on(igmp
);
637 static void sock_close(struct igmp_sock
*igmp
)
639 pim_igmp_other_querier_timer_off(igmp
);
640 pim_igmp_general_query_off(igmp
);
642 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
643 if (igmp
->t_igmp_read
) {
644 zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
645 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
646 igmp
->interface
->name
);
649 THREAD_OFF(igmp
->t_igmp_read
);
651 if (close(igmp
->fd
)) {
652 zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
653 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
,
654 errno
, safe_strerror(errno
));
657 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
658 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
659 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
);
663 void igmp_startup_mode_on(struct igmp_sock
*igmp
)
665 struct pim_interface
*pim_ifp
;
667 pim_ifp
= igmp
->interface
->info
;
670 RFC 3376: 8.7. Startup Query Count
672 The Startup Query Count is the number of Queries sent out on
673 startup, separated by the Startup Query Interval. Default: the
676 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
679 Since we're (re)starting, reset QQI to default Query Interval
681 igmp
->querier_query_interval
= pim_ifp
->igmp_default_query_interval
;
684 static void igmp_group_free(struct igmp_group
*group
)
686 list_free(group
->group_source_list
);
688 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
691 static void igmp_group_delete(struct igmp_group
*group
)
693 struct listnode
*src_node
;
694 struct listnode
*src_nextnode
;
695 struct igmp_source
*src
;
697 if (PIM_DEBUG_IGMP_TRACE
) {
698 char group_str
[INET_ADDRSTRLEN
];
699 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
700 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
702 group
->group_igmp_sock
->fd
,
703 group
->group_igmp_sock
->interface
->name
);
706 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
, src
)) {
707 igmp_source_delete(src
);
710 if (group
->t_group_query_retransmit_timer
) {
711 THREAD_OFF(group
->t_group_query_retransmit_timer
);
714 group_timer_off(group
);
715 listnode_delete(group
->group_igmp_sock
->igmp_group_list
, group
);
716 hash_release (group
->group_igmp_sock
->igmp_group_hash
, group
);
718 igmp_group_free(group
);
721 void igmp_group_delete_empty_include(struct igmp_group
*group
)
723 zassert(!group
->group_filtermode_isexcl
);
724 zassert(!listcount(group
->group_source_list
));
726 igmp_group_delete(group
);
729 void igmp_sock_free(struct igmp_sock
*igmp
)
731 zassert(!igmp
->t_igmp_read
);
732 zassert(!igmp
->t_igmp_query_timer
);
733 zassert(!igmp
->t_other_querier_timer
);
734 zassert(igmp
->igmp_group_list
);
735 zassert(!listcount(igmp
->igmp_group_list
));
737 list_free(igmp
->igmp_group_list
);
738 hash_free(igmp
->igmp_group_hash
);
740 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
743 void igmp_sock_delete(struct igmp_sock
*igmp
)
745 struct pim_interface
*pim_ifp
;
746 struct listnode
*grp_node
;
747 struct listnode
*grp_nextnode
;
748 struct igmp_group
*grp
;
750 for (ALL_LIST_ELEMENTS(igmp
->igmp_group_list
, grp_node
, grp_nextnode
, grp
)) {
751 igmp_group_delete(grp
);
756 pim_ifp
= igmp
->interface
->info
;
758 listnode_delete(pim_ifp
->igmp_socket_list
, igmp
);
760 igmp_sock_free(igmp
);
764 igmp_sock_delete_all (struct interface
*ifp
)
766 struct pim_interface
*pim_ifp
;
767 struct listnode
*igmp_node
, *igmp_nextnode
;
768 struct igmp_sock
*igmp
;
772 for (ALL_LIST_ELEMENTS (pim_ifp
->igmp_socket_list
, igmp_node
,
773 igmp_nextnode
, igmp
))
775 igmp_sock_delete(igmp
);
780 igmp_group_hash_key (void *arg
)
782 struct igmp_group
*group
= (struct igmp_group
*)arg
;
784 return jhash_1word(group
->group_addr
.s_addr
, 0);
788 igmp_group_hash_equal (const void *arg1
, const void *arg2
)
790 const struct igmp_group
*g1
= (const struct igmp_group
*)arg1
;
791 const struct igmp_group
*g2
= (const struct igmp_group
*)arg2
;
793 if (g1
->group_addr
.s_addr
== g2
->group_addr
.s_addr
)
799 static struct igmp_sock
*igmp_sock_new(int fd
,
800 struct in_addr ifaddr
,
801 struct interface
*ifp
)
803 struct pim_interface
*pim_ifp
;
804 struct igmp_sock
*igmp
;
808 if (PIM_DEBUG_IGMP_TRACE
) {
809 zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
810 fd
, inet_ntoa(ifaddr
), ifp
->name
);
813 igmp
= XCALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
815 zlog_warn("%s %s: XCALLOC() failure",
816 __FILE__
, __PRETTY_FUNCTION__
);
820 igmp
->igmp_group_list
= list_new();
821 if (!igmp
->igmp_group_list
) {
822 zlog_err("%s %s: failure: igmp_group_list = list_new()",
823 __FILE__
, __PRETTY_FUNCTION__
);
826 igmp
->igmp_group_list
->del
= (void (*)(void *)) igmp_group_free
;
828 igmp
->igmp_group_hash
= hash_create (igmp_group_hash_key
,
829 igmp_group_hash_equal
);
832 igmp
->interface
= ifp
;
833 igmp
->ifaddr
= ifaddr
;
834 igmp
->t_igmp_read
= NULL
;
835 igmp
->t_igmp_query_timer
= NULL
;
836 igmp
->t_other_querier_timer
= NULL
; /* no other querier present */
837 igmp
->querier_robustness_variable
= pim_ifp
->igmp_default_robustness_variable
;
838 igmp
->sock_creation
= pim_time_monotonic_sec();
841 igmp_startup_mode_on() will reset QQI:
843 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
845 igmp_startup_mode_on(igmp
);
846 pim_igmp_general_query_on(igmp
);
851 static void igmp_read_on (struct igmp_sock
*igmp
);
854 pim_igmp_read (struct thread
*t
)
857 struct igmp_sock
*igmp
= (struct igmp_sock
*)THREAD_ARG(t
);
858 struct sockaddr_in from
;
859 struct sockaddr_in to
;
860 socklen_t fromlen
= sizeof(from
);
861 socklen_t tolen
= sizeof(to
);
862 ifindex_t ifindex
= -1;
868 len
= pim_socket_recvfromto(igmp
->fd
, buf
, sizeof(buf
),
876 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
889 igmp_read_on (struct igmp_sock
*igmp
)
892 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
893 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
896 igmp
->t_igmp_read
= NULL
;
897 thread_add_read(master
, pim_igmp_read
, igmp
, igmp
->fd
, &igmp
->t_igmp_read
);
901 struct igmp_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
902 struct in_addr ifaddr
,
903 struct interface
*ifp
)
905 struct pim_interface
*pim_ifp
;
906 struct igmp_sock
*igmp
;
911 fd
= igmp_sock_open(ifaddr
, ifp
, pim_ifp
->options
);
913 zlog_warn("Could not open IGMP socket for %s on %s",
914 inet_ntoa(ifaddr
), ifp
->name
);
918 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
);
920 zlog_err("%s %s: igmp_sock_new() failure",
921 __FILE__
, __PRETTY_FUNCTION__
);
928 listnode_add(igmp_sock_list
, igmp
);
930 #ifdef IGMP_SOCK_DUMP
931 igmp_sock_dump(igmp_sock_array
);
938 RFC 3376: 6.5. Switching Router Filter-Modes
940 When a router's filter-mode for a group is EXCLUDE and the group
941 timer expires, the router filter-mode for the group transitions to
944 A router uses source records with running source timers as its state
945 for the switch to a filter-mode of INCLUDE. If there are any source
946 records with source timers greater than zero (i.e., requested to be
947 forwarded), a router switches to filter-mode of INCLUDE using those
948 source records. Source records whose timers are zero (from the
949 previous EXCLUDE mode) are deleted.
951 static int igmp_group_timer(struct thread
*t
)
953 struct igmp_group
*group
;
955 group
= THREAD_ARG(t
);
957 if (PIM_DEBUG_IGMP_TRACE
) {
958 char group_str
[INET_ADDRSTRLEN
];
959 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
960 zlog_debug("%s: Timer for group %s on interface %s",
962 group_str
, group
->group_igmp_sock
->interface
->name
);
965 zassert(group
->group_filtermode_isexcl
);
967 group
->t_group_timer
= NULL
;
968 group
->group_filtermode_isexcl
= 0;
970 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
971 igmp_anysource_forward_stop(group
);
973 igmp_source_delete_expired(group
->group_source_list
);
975 zassert(!group
->t_group_timer
);
976 zassert(!group
->group_filtermode_isexcl
);
979 RFC 3376: 6.2.2. Definition of Group Timers
981 If there are no more source records for the group, delete group
984 if (listcount(group
->group_source_list
) < 1) {
985 igmp_group_delete_empty_include(group
);
991 static void group_timer_off(struct igmp_group
*group
)
993 if (!group
->t_group_timer
)
996 if (PIM_DEBUG_IGMP_TRACE
) {
997 char group_str
[INET_ADDRSTRLEN
];
998 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
999 zlog_debug("Cancelling TIMER event for group %s on %s",
1000 group_str
, group
->group_igmp_sock
->interface
->name
);
1003 THREAD_OFF(group
->t_group_timer
);
1004 zassert(!group
->t_group_timer
);
1007 void igmp_group_timer_on(struct igmp_group
*group
,
1008 long interval_msec
, const char *ifname
)
1010 group_timer_off(group
);
1012 if (PIM_DEBUG_IGMP_EVENTS
) {
1013 char group_str
[INET_ADDRSTRLEN
];
1014 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1015 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1016 interval_msec
/ 1000,
1017 interval_msec
% 1000,
1022 RFC 3376: 6.2.2. Definition of Group Timers
1024 The group timer is only used when a group is in EXCLUDE mode and
1025 it represents the time for the *filter-mode* of the group to
1026 expire and switch to INCLUDE mode.
1028 zassert(group
->group_filtermode_isexcl
);
1030 thread_add_timer_msec(master
, igmp_group_timer
, group
, interval_msec
,
1031 &group
->t_group_timer
);
1035 find_group_by_addr (struct igmp_sock
*igmp
,
1036 struct in_addr group_addr
)
1038 struct igmp_group lookup
;
1040 lookup
.group_addr
.s_addr
= group_addr
.s_addr
;
1042 return hash_lookup(igmp
->igmp_group_hash
, &lookup
);
1045 struct igmp_group
*igmp_add_group_by_addr(struct igmp_sock
*igmp
,
1046 struct in_addr group_addr
)
1048 struct igmp_group
*group
;
1050 group
= find_group_by_addr(igmp
, group_addr
);
1055 if (!pim_is_group_224_4 (group_addr
))
1057 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1058 __PRETTY_FUNCTION__
);
1062 if (pim_is_group_224_0_0_0_24 (group_addr
))
1064 zlog_warn("%s: Group specified is part of 224.0.0.0/24",
1065 __PRETTY_FUNCTION__
);
1069 Non-existant group is created as INCLUDE {empty}:
1071 RFC 3376 - 5.1. Action on Change of Interface State
1073 If no interface state existed for that multicast address before
1074 the change (i.e., the change consisted of creating a new
1075 per-interface record), or if no state exists after the change
1076 (i.e., the change consisted of deleting a per-interface record),
1077 then the "non-existent" state is considered to have a filter mode
1078 of INCLUDE and an empty source list.
1081 group
= XCALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1083 zlog_warn("%s %s: XCALLOC() failure",
1084 __FILE__
, __PRETTY_FUNCTION__
);
1085 return NULL
; /* error, not found, could not create */
1088 group
->group_source_list
= list_new();
1089 if (!group
->group_source_list
) {
1090 zlog_warn("%s %s: list_new() failure",
1091 __FILE__
, __PRETTY_FUNCTION__
);
1092 XFREE(MTYPE_PIM_IGMP_GROUP
, group
); /* discard group */
1093 return NULL
; /* error, not found, could not initialize */
1095 group
->group_source_list
->del
= (void (*)(void *)) igmp_source_free
;
1097 group
->t_group_timer
= NULL
;
1098 group
->t_group_query_retransmit_timer
= NULL
;
1099 group
->group_specific_query_retransmit_count
= 0;
1100 group
->group_addr
= group_addr
;
1101 group
->group_igmp_sock
= igmp
;
1102 group
->last_igmp_v1_report_dsec
= -1;
1103 group
->last_igmp_v2_report_dsec
= -1;
1104 group
->group_creation
= pim_time_monotonic_sec();
1105 group
->igmp_version
= IGMP_DEFAULT_VERSION
;
1107 /* initialize new group as INCLUDE {empty} */
1108 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1110 listnode_add(igmp
->igmp_group_list
, group
);
1111 group
= hash_get (igmp
->igmp_group_hash
, group
, hash_alloc_intern
);
1113 if (PIM_DEBUG_IGMP_TRACE
) {
1114 char group_str
[INET_ADDRSTRLEN
];
1115 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1116 zlog_debug("Creating new IGMP group %s on socket %d interface %s",
1117 group_str
, igmp
->fd
, igmp
->interface
->name
);
1121 RFC 3376: 6.2.2. Definition of Group Timers
1123 The group timer is only used when a group is in EXCLUDE mode and
1124 it represents the time for the *filter-mode* of the group to
1125 expire and switch to INCLUDE mode.
1127 zassert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1128 zassert(!group
->t_group_timer
); /* group timer == 0 */
1130 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1131 igmp_anysource_forward_stop(group
);
1137 igmp_send_query (int igmp_version
,
1138 struct igmp_group
*group
,
1144 struct in_addr dst_addr
,
1145 struct in_addr group_addr
,
1146 int query_max_response_time_dsec
,
1148 uint8_t querier_robustness_variable
,
1149 uint16_t querier_query_interval
)
1151 if (igmp_version
== 3) {
1152 igmp_v3_send_query (group
, fd
, ifname
, query_buf
,
1153 query_buf_size
, num_sources
,
1154 dst_addr
, group_addr
,
1155 query_max_response_time_dsec
, s_flag
,
1156 querier_robustness_variable
,
1157 querier_query_interval
);
1158 } else if (igmp_version
== 2) {
1159 igmp_v2_send_query (group
, fd
, ifname
, query_buf
,
1160 dst_addr
, group_addr
,
1161 query_max_response_time_dsec
);