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
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
29 #include "pim_igmpv2.h"
30 #include "pim_igmpv3.h"
31 #include "pim_iface.h"
33 #include "pim_mroute.h"
37 #include "pim_zebra.h"
39 static void group_timer_off(struct igmp_group
*group
);
41 /* This socket is used for TXing IGMP packets only, IGMP RX happens
44 static int igmp_sock_open(struct in_addr ifaddr
, ifindex_t ifindex
, uint32_t pim_options
)
50 fd
= pim_socket_mcast(IPPROTO_IGMP
, ifaddr
, ifindex
, 1);
55 if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options
)) {
56 if (inet_aton(PIM_ALL_ROUTERS
, &group
)) {
57 if (!pim_socket_join(fd
, group
, ifaddr
, ifindex
))
61 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
62 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
63 PIM_ALL_ROUTERS
, errno
, safe_strerror(errno
));
68 IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
69 IGMP routers must receive general queries for querier election.
71 if (inet_aton(PIM_ALL_SYSTEMS
, &group
)) {
72 if (!pim_socket_join(fd
, group
, ifaddr
, ifindex
))
76 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
77 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
78 PIM_ALL_SYSTEMS
, errno
, safe_strerror(errno
));
81 if (inet_aton(PIM_ALL_IGMP_ROUTERS
, &group
)) {
82 if (!pim_socket_join(fd
, group
, ifaddr
, ifindex
)) {
87 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
88 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
89 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
93 zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
94 fd
, inet_ntoa(ifaddr
));
102 #undef IGMP_SOCK_DUMP
104 #ifdef IGMP_SOCK_DUMP
105 static void igmp_sock_dump(array_t
*igmp_sock_array
)
107 int size
= array_size(igmp_sock_array
);
108 for (int i
= 0; i
< size
; ++i
) {
110 struct igmp_sock
*igmp
= array_get(igmp_sock_array
, i
);
112 zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
113 __FILE__
, __PRETTY_FUNCTION__
,
115 inet_ntoa(igmp
->ifaddr
),
121 struct igmp_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
122 struct in_addr ifaddr
)
124 struct listnode
*sock_node
;
125 struct igmp_sock
*igmp
;
127 #ifdef IGMP_SOCK_DUMP
128 igmp_sock_dump(igmp_sock_list
);
131 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
132 if (ifaddr
.s_addr
== igmp
->ifaddr
.s_addr
)
138 struct igmp_sock
*igmp_sock_lookup_by_fd(struct list
*igmp_sock_list
,
141 struct listnode
*sock_node
;
142 struct igmp_sock
*igmp
;
144 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
151 static int pim_igmp_other_querier_expire(struct thread
*t
)
153 struct igmp_sock
*igmp
;
155 igmp
= THREAD_ARG(t
);
157 zassert(igmp
->t_other_querier_timer
);
158 zassert(!igmp
->t_igmp_query_timer
);
160 if (PIM_DEBUG_IGMP_TRACE
) {
161 char ifaddr_str
[INET_ADDRSTRLEN
];
162 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
163 zlog_debug("%s: Querier %s resuming",
168 igmp
->t_other_querier_timer
= NULL
;
171 We are the current querier, then
172 re-start sending general queries.
174 pim_igmp_general_query_on(igmp
);
179 void pim_igmp_other_querier_timer_on(struct igmp_sock
*igmp
)
181 long other_querier_present_interval_msec
;
182 struct pim_interface
*pim_ifp
;
185 zassert(igmp
->interface
);
186 zassert(igmp
->interface
->info
);
188 pim_ifp
= igmp
->interface
->info
;
190 if (igmp
->t_other_querier_timer
) {
192 There is other querier present already,
193 then reset the other-querier-present timer.
196 if (PIM_DEBUG_IGMP_TRACE
) {
197 char ifaddr_str
[INET_ADDRSTRLEN
];
198 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
199 zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
203 THREAD_OFF(igmp
->t_other_querier_timer
);
204 zassert(!igmp
->t_other_querier_timer
);
208 We are the current querier, then stop sending general queries:
209 igmp->t_igmp_query_timer = NULL;
211 pim_igmp_general_query_off(igmp
);
215 Since this socket is starting the other-querier-present timer,
216 there should not be periodic query timer for this socket.
218 zassert(!igmp
->t_igmp_query_timer
);
221 RFC 3376: 8.5. Other Querier Present Interval
223 The Other Querier Present Interval is the length of time that must
224 pass before a multicast router decides that there is no longer
225 another multicast router which should be the querier. This value
226 MUST be ((the Robustness Variable) times (the Query Interval)) plus
227 (one half of one Query Response Interval).
229 other_querier_present_interval_msec = \
230 igmp->querier_robustness_variable * \
231 1000 * igmp->querier_query_interval + \
232 100 * (pim_ifp->query_max_response_time_dsec >> 1);
234 other_querier_present_interval_msec
=
235 PIM_IGMP_OQPI_MSEC(igmp
->querier_robustness_variable
,
236 igmp
->querier_query_interval
,
237 pim_ifp
->igmp_query_max_response_time_dsec
);
239 if (PIM_DEBUG_IGMP_TRACE
) {
240 char ifaddr_str
[INET_ADDRSTRLEN
];
241 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
242 zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
244 other_querier_present_interval_msec
/ 1000,
245 other_querier_present_interval_msec
% 1000);
248 THREAD_TIMER_MSEC_ON(master
, igmp
->t_other_querier_timer
,
249 pim_igmp_other_querier_expire
,
250 igmp
, other_querier_present_interval_msec
);
253 void pim_igmp_other_querier_timer_off(struct igmp_sock
*igmp
)
257 if (PIM_DEBUG_IGMP_TRACE
) {
258 if (igmp
->t_other_querier_timer
) {
259 char ifaddr_str
[INET_ADDRSTRLEN
];
260 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
261 zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
262 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
265 THREAD_OFF(igmp
->t_other_querier_timer
);
266 zassert(!igmp
->t_other_querier_timer
);
270 igmp_recv_query(struct igmp_sock
*igmp
, int query_version
,
272 struct in_addr from
, const char *from_str
,
273 char *igmp_msg
, int igmp_msg_len
)
275 struct interface
*ifp
;
276 struct pim_interface
*pim_ifp
;
277 struct in_addr group_addr
;
278 uint16_t recv_checksum
;
281 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
283 ifp
= igmp
->interface
;
286 recv_checksum
= *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
288 /* for computing checksum */
289 *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
291 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
292 if (checksum
!= recv_checksum
) {
293 zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
294 query_version
, from_str
, ifp
->name
, recv_checksum
, checksum
);
298 /* RFC 3376 defines some guidelines on operating in backwards compatibility
299 * with older versions of IGMP but there are some gaps in the logic:
301 * - once we drop from say version 3 to version 2 we will never go back to
302 * version 3 even if the node that TXed an IGMP v2 query upgrades to v3
304 * - The node with the lowest IP is the querier so we will only know to drop
305 * from v3 to v2 if the node that is the querier is also the one that is
306 * running igmp v2. If a non-querier only supports igmp v2 we will have
309 * For now we will simplify things and inform the user that they need to
310 * configure all PIM routers to use the same version of IGMP.
312 if (query_version
!= pim_ifp
->igmp_version
) {
313 zlog_warn("Recv IGMP query v%d from %s on %s but we are using v%d, please "
314 "configure all PIM routers on this subnet to use the same "
316 query_version
, from_str
, ifp
->name
, pim_ifp
->igmp_version
);
320 if (PIM_DEBUG_IGMP_PACKETS
) {
321 char group_str
[INET_ADDRSTRLEN
];
322 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
323 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
324 query_version
, from_str
, ifp
->name
, group_str
);
328 RFC 3376: 6.6.2. Querier Election
330 When a router receives a query with a lower IP address, it sets
331 the Other-Querier-Present timer to Other Querier Present Interval
332 and ceases to send queries on the network if it was the previously
335 if (ntohl(from
.s_addr
) < ntohl(igmp
->ifaddr
.s_addr
)) {
337 if (PIM_DEBUG_IGMP_TRACE
) {
338 char ifaddr_str
[INET_ADDRSTRLEN
];
339 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
340 zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
342 ifaddr_str
, ntohl(igmp
->ifaddr
.s_addr
),
343 from_str
, ntohl(from
.s_addr
));
346 pim_igmp_other_querier_timer_on(igmp
);
349 /* IGMP version 3 is the only one where we process the RXed query */
350 if (query_version
== 3) {
351 igmp_v3_recv_query(igmp
, from_str
, igmp_msg
);
357 static void on_trace(const char *label
,
358 struct interface
*ifp
, struct in_addr from
)
360 if (PIM_DEBUG_IGMP_TRACE
) {
361 char from_str
[INET_ADDRSTRLEN
];
362 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
363 zlog_debug("%s: from %s on %s",
364 label
, from_str
, ifp
->name
);
369 igmp_v1_recv_report (struct igmp_sock
*igmp
,
370 struct in_addr from
, const char *from_str
,
371 char *igmp_msg
, int igmp_msg_len
)
373 struct interface
*ifp
= igmp
->interface
;
374 struct igmp_group
*group
;
375 struct in_addr group_addr
;
377 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
);
379 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
380 zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
381 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
385 if (PIM_DEBUG_IGMP_TRACE
) {
386 zlog_warn("%s %s: FIXME WRITEME",
387 __FILE__
, __PRETTY_FUNCTION__
);
390 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
392 /* non-existant group is created as INCLUDE {empty} */
393 group
= igmp_add_group_by_addr(igmp
, group_addr
);
398 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
403 int pim_igmp_packet(struct igmp_sock
*igmp
, char *buf
, size_t len
)
406 size_t ip_hlen
; /* ip header length in bytes */
410 char from_str
[INET_ADDRSTRLEN
];
411 char to_str
[INET_ADDRSTRLEN
];
413 if (len
< sizeof(*ip_hdr
)) {
414 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
415 len
, sizeof(*ip_hdr
));
419 ip_hdr
= (struct ip
*) buf
;
421 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
422 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
424 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
426 if (PIM_DEBUG_IGMP_PACKETS
) {
427 zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
428 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hlen
, ip_hdr
->ip_p
);
431 if (ip_hdr
->ip_p
!= PIM_IP_PROTO_IGMP
) {
432 zlog_warn("IP packet protocol=%d is not IGMP=%d",
433 ip_hdr
->ip_p
, PIM_IP_PROTO_IGMP
);
437 if (ip_hlen
< PIM_IP_HEADER_MIN_LEN
) {
438 zlog_warn("IP packet header size=%zu shorter than minimum=%d",
439 ip_hlen
, PIM_IP_HEADER_MIN_LEN
);
442 if (ip_hlen
> PIM_IP_HEADER_MAX_LEN
) {
443 zlog_warn("IP packet header size=%zu greater than maximum=%d",
444 ip_hlen
, PIM_IP_HEADER_MAX_LEN
);
448 igmp_msg
= buf
+ ip_hlen
;
449 msg_type
= *igmp_msg
;
450 igmp_msg_len
= len
- ip_hlen
;
452 if (PIM_DEBUG_IGMP_PACKETS
) {
453 zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
454 from_str
, to_str
, igmp
->interface
->name
, ip_hdr
->ip_ttl
, msg_type
,
458 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
459 zlog_warn("IGMP message size=%d shorter than minimum=%d",
460 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
465 case PIM_IGMP_MEMBERSHIP_QUERY
:
467 int max_resp_code
= igmp_msg
[1];
471 RFC 3376: 7.1. Query Version Distinctions
472 IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
473 IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
474 IGMPv3 Query: length >= 12 octets
477 if (igmp_msg_len
== 8) {
478 query_version
= max_resp_code
? 2 : 1;
480 else if (igmp_msg_len
>= 12) {
484 zlog_warn("Unknown IGMP query version");
488 return igmp_recv_query(igmp
, query_version
, max_resp_code
,
489 ip_hdr
->ip_src
, from_str
,
490 igmp_msg
, igmp_msg_len
);
493 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
494 return igmp_v3_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
495 igmp_msg
, igmp_msg_len
);
497 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
498 return igmp_v2_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
499 igmp_msg
, igmp_msg_len
);
501 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
502 return igmp_v1_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
503 igmp_msg
, igmp_msg_len
);
505 case PIM_IGMP_V2_LEAVE_GROUP
:
506 return igmp_v2_recv_leave(igmp
, ip_hdr
->ip_src
, from_str
,
507 igmp_msg
, igmp_msg_len
);
510 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
515 static int pim_igmp_general_query(struct thread
*t
);
517 void pim_igmp_general_query_on(struct igmp_sock
*igmp
)
519 struct pim_interface
*pim_ifp
;
524 Since this socket is starting as querier,
525 there should not exist a timer for other-querier-present.
527 zassert(!igmp
->t_other_querier_timer
);
528 pim_ifp
= igmp
->interface
->info
;
532 RFC 3376: 8.6. Startup Query Interval
534 The Startup Query Interval is the interval between General Queries
535 sent by a Querier on startup. Default: 1/4 the Query Interval.
536 The first one should be sent out immediately instead of 125/4
539 startup_mode
= igmp
->startup_query_count
> 0;
542 * If this is the first time we are sending a query on a
543 * newly configured igmp interface send it out in 1 second
544 * just to give the entire world a tiny bit of time to settle
545 * else the query interval is:
546 * query_interval = pim_ifp->igmp_default_query_interval >> 2;
548 if (igmp
->startup_query_count
== igmp
->querier_robustness_variable
)
551 query_interval
= PIM_IGMP_SQI(pim_ifp
->igmp_default_query_interval
);
553 --igmp
->startup_query_count
;
556 query_interval
= igmp
->querier_query_interval
;
559 if (PIM_DEBUG_IGMP_TRACE
) {
560 char ifaddr_str
[INET_ADDRSTRLEN
];
561 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
562 zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
565 startup_mode
? "startup" : "non-startup",
568 igmp
->t_igmp_query_timer
= NULL
;
569 THREAD_TIMER_ON(master
, igmp
->t_igmp_query_timer
,
570 pim_igmp_general_query
,
571 igmp
, query_interval
);
574 void pim_igmp_general_query_off(struct igmp_sock
*igmp
)
578 if (PIM_DEBUG_IGMP_TRACE
) {
579 if (igmp
->t_igmp_query_timer
) {
580 char ifaddr_str
[INET_ADDRSTRLEN
];
581 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
582 zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
583 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
586 THREAD_OFF(igmp
->t_igmp_query_timer
);
589 /* Issue IGMP general query */
590 static int pim_igmp_general_query(struct thread
*t
)
592 struct igmp_sock
*igmp
;
593 struct in_addr dst_addr
;
594 struct in_addr group_addr
;
595 struct pim_interface
*pim_ifp
;
598 igmp
= THREAD_ARG(t
);
600 zassert(igmp
->interface
);
601 zassert(igmp
->interface
->info
);
603 pim_ifp
= igmp
->interface
->info
;
605 if (pim_ifp
->igmp_version
== 3) {
606 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
608 query_buf_size
= IGMP_V12_MSG_SIZE
;
611 char query_buf
[query_buf_size
];
614 RFC3376: 4.1.12. IP Destination Addresses for Queries
616 In IGMPv3, General Queries are sent with an IP destination address
617 of 224.0.0.1, the all-systems multicast address. Group-Specific
618 and Group-and-Source-Specific Queries are sent with an IP
619 destination address equal to the multicast address of interest.
622 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
623 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
625 if (PIM_DEBUG_IGMP_TRACE
) {
626 char querier_str
[INET_ADDRSTRLEN
];
627 char dst_str
[INET_ADDRSTRLEN
];
628 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
629 sizeof(querier_str
));
630 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
631 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
632 querier_str
, dst_str
, igmp
->interface
->name
);
635 igmp_send_query (pim_ifp
->igmp_version
,
638 igmp
->interface
->name
,
644 pim_ifp
->igmp_query_max_response_time_dsec
,
645 1 /* s_flag: always set for general queries */,
646 igmp
->querier_robustness_variable
,
647 igmp
->querier_query_interval
);
649 pim_igmp_general_query_on(igmp
);
654 static void sock_close(struct igmp_sock
*igmp
)
656 pim_igmp_other_querier_timer_off(igmp
);
657 pim_igmp_general_query_off(igmp
);
659 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
660 if (igmp
->t_igmp_read
) {
661 zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
662 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
663 igmp
->interface
->name
);
666 THREAD_OFF(igmp
->t_igmp_read
);
668 if (close(igmp
->fd
)) {
669 zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
670 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
,
671 errno
, safe_strerror(errno
));
674 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
675 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
676 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
);
680 void igmp_startup_mode_on(struct igmp_sock
*igmp
)
682 struct pim_interface
*pim_ifp
;
684 pim_ifp
= igmp
->interface
->info
;
687 RFC 3376: 8.7. Startup Query Count
689 The Startup Query Count is the number of Queries sent out on
690 startup, separated by the Startup Query Interval. Default: the
693 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
696 Since we're (re)starting, reset QQI to default Query Interval
698 igmp
->querier_query_interval
= pim_ifp
->igmp_default_query_interval
;
701 static void igmp_group_free(struct igmp_group
*group
)
703 list_free(group
->group_source_list
);
705 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
708 static void igmp_group_delete(struct igmp_group
*group
)
710 struct listnode
*src_node
;
711 struct listnode
*src_nextnode
;
712 struct igmp_source
*src
;
714 if (PIM_DEBUG_IGMP_TRACE
) {
715 char group_str
[INET_ADDRSTRLEN
];
716 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
717 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
719 group
->group_igmp_sock
->fd
,
720 group
->group_igmp_sock
->interface
->name
);
723 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
, src
)) {
724 igmp_source_delete(src
);
727 if (group
->t_group_query_retransmit_timer
) {
728 THREAD_OFF(group
->t_group_query_retransmit_timer
);
731 group_timer_off(group
);
732 listnode_delete(group
->group_igmp_sock
->igmp_group_list
, group
);
733 igmp_group_free(group
);
736 void igmp_group_delete_empty_include(struct igmp_group
*group
)
738 zassert(!group
->group_filtermode_isexcl
);
739 zassert(!listcount(group
->group_source_list
));
741 igmp_group_delete(group
);
744 void igmp_sock_free(struct igmp_sock
*igmp
)
746 zassert(!igmp
->t_igmp_read
);
747 zassert(!igmp
->t_igmp_query_timer
);
748 zassert(!igmp
->t_other_querier_timer
);
749 zassert(igmp
->igmp_group_list
);
750 zassert(!listcount(igmp
->igmp_group_list
));
752 list_free(igmp
->igmp_group_list
);
754 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
757 void igmp_sock_delete(struct igmp_sock
*igmp
)
759 struct pim_interface
*pim_ifp
;
760 struct listnode
*grp_node
;
761 struct listnode
*grp_nextnode
;
762 struct igmp_group
*grp
;
764 for (ALL_LIST_ELEMENTS(igmp
->igmp_group_list
, grp_node
, grp_nextnode
, grp
)) {
765 igmp_group_delete(grp
);
770 pim_ifp
= igmp
->interface
->info
;
772 listnode_delete(pim_ifp
->igmp_socket_list
, igmp
);
774 igmp_sock_free(igmp
);
778 igmp_sock_delete_all (struct interface
*ifp
)
780 struct pim_interface
*pim_ifp
;
781 struct listnode
*igmp_node
, *igmp_nextnode
;
782 struct igmp_sock
*igmp
;
786 for (ALL_LIST_ELEMENTS (pim_ifp
->igmp_socket_list
, igmp_node
,
787 igmp_nextnode
, igmp
))
789 igmp_sock_delete(igmp
);
793 static struct igmp_sock
*igmp_sock_new(int fd
,
794 struct in_addr ifaddr
,
795 struct interface
*ifp
)
797 struct pim_interface
*pim_ifp
;
798 struct igmp_sock
*igmp
;
802 if (PIM_DEBUG_IGMP_TRACE
) {
803 zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
804 fd
, inet_ntoa(ifaddr
), ifp
->name
);
807 igmp
= XCALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
809 zlog_warn("%s %s: XCALLOC() failure",
810 __FILE__
, __PRETTY_FUNCTION__
);
814 igmp
->igmp_group_list
= list_new();
815 if (!igmp
->igmp_group_list
) {
816 zlog_err("%s %s: failure: igmp_group_list = list_new()",
817 __FILE__
, __PRETTY_FUNCTION__
);
820 igmp
->igmp_group_list
->del
= (void (*)(void *)) igmp_group_free
;
823 igmp
->interface
= ifp
;
824 igmp
->ifaddr
= ifaddr
;
825 igmp
->t_igmp_read
= NULL
;
826 igmp
->t_igmp_query_timer
= NULL
;
827 igmp
->t_other_querier_timer
= NULL
; /* no other querier present */
828 igmp
->querier_robustness_variable
= pim_ifp
->igmp_default_robustness_variable
;
829 igmp
->sock_creation
= pim_time_monotonic_sec();
832 igmp_startup_mode_on() will reset QQI:
834 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
836 igmp_startup_mode_on(igmp
);
837 pim_igmp_general_query_on(igmp
);
842 static void igmp_read_on (struct igmp_sock
*igmp
);
845 pim_igmp_read (struct thread
*t
)
848 struct igmp_sock
*igmp
= (struct igmp_sock
*)THREAD_ARG(t
);
849 struct sockaddr_in from
;
850 struct sockaddr_in to
;
851 socklen_t fromlen
= sizeof(from
);
852 socklen_t tolen
= sizeof(to
);
853 ifindex_t ifindex
= -1;
859 len
= pim_socket_recvfromto(igmp
->fd
, buf
, sizeof(buf
),
867 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
882 igmp_read_on (struct igmp_sock
*igmp
)
885 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
886 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
889 igmp
->t_igmp_read
= NULL
;
890 THREAD_READ_ON(master
, igmp
->t_igmp_read
, pim_igmp_read
, igmp
, igmp
->fd
);
894 struct igmp_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
895 struct in_addr ifaddr
,
896 struct interface
*ifp
)
898 struct pim_interface
*pim_ifp
;
899 struct igmp_sock
*igmp
;
904 fd
= igmp_sock_open(ifaddr
, ifp
->ifindex
, pim_ifp
->options
);
906 zlog_warn("Could not open IGMP socket for %s on %s",
907 inet_ntoa(ifaddr
), ifp
->name
);
911 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
);
913 zlog_err("%s %s: igmp_sock_new() failure",
914 __FILE__
, __PRETTY_FUNCTION__
);
921 listnode_add(igmp_sock_list
, igmp
);
923 #ifdef IGMP_SOCK_DUMP
924 igmp_sock_dump(igmp_sock_array
);
931 RFC 3376: 6.5. Switching Router Filter-Modes
933 When a router's filter-mode for a group is EXCLUDE and the group
934 timer expires, the router filter-mode for the group transitions to
937 A router uses source records with running source timers as its state
938 for the switch to a filter-mode of INCLUDE. If there are any source
939 records with source timers greater than zero (i.e., requested to be
940 forwarded), a router switches to filter-mode of INCLUDE using those
941 source records. Source records whose timers are zero (from the
942 previous EXCLUDE mode) are deleted.
944 static int igmp_group_timer(struct thread
*t
)
946 struct igmp_group
*group
;
948 group
= THREAD_ARG(t
);
950 if (PIM_DEBUG_IGMP_TRACE
) {
951 char group_str
[INET_ADDRSTRLEN
];
952 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
953 zlog_debug("%s: Timer for group %s on interface %s",
955 group_str
, group
->group_igmp_sock
->interface
->name
);
958 zassert(group
->group_filtermode_isexcl
);
960 group
->t_group_timer
= NULL
;
961 group
->group_filtermode_isexcl
= 0;
963 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
964 igmp_anysource_forward_stop(group
);
966 igmp_source_delete_expired(group
->group_source_list
);
968 zassert(!group
->t_group_timer
);
969 zassert(!group
->group_filtermode_isexcl
);
972 RFC 3376: 6.2.2. Definition of Group Timers
974 If there are no more source records for the group, delete group
977 if (listcount(group
->group_source_list
) < 1) {
978 igmp_group_delete_empty_include(group
);
984 static void group_timer_off(struct igmp_group
*group
)
986 if (!group
->t_group_timer
)
989 if (PIM_DEBUG_IGMP_TRACE
) {
990 char group_str
[INET_ADDRSTRLEN
];
991 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
992 zlog_debug("Cancelling TIMER event for group %s on %s",
993 group_str
, group
->group_igmp_sock
->interface
->name
);
996 THREAD_OFF(group
->t_group_timer
);
997 zassert(!group
->t_group_timer
);
1000 void igmp_group_timer_on(struct igmp_group
*group
,
1001 long interval_msec
, const char *ifname
)
1003 group_timer_off(group
);
1005 if (PIM_DEBUG_IGMP_EVENTS
) {
1006 char group_str
[INET_ADDRSTRLEN
];
1007 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1008 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1009 interval_msec
/ 1000,
1010 interval_msec
% 1000,
1015 RFC 3376: 6.2.2. Definition of Group Timers
1017 The group timer is only used when a group is in EXCLUDE mode and
1018 it represents the time for the *filter-mode* of the group to
1019 expire and switch to INCLUDE mode.
1021 zassert(group
->group_filtermode_isexcl
);
1023 THREAD_TIMER_MSEC_ON(master
, group
->t_group_timer
,
1025 group
, interval_msec
);
1029 find_group_by_addr (struct igmp_sock
*igmp
,
1030 struct in_addr group_addr
)
1032 struct igmp_group
*group
;
1033 struct listnode
*node
;
1035 for (ALL_LIST_ELEMENTS_RO(igmp
->igmp_group_list
, node
, group
))
1036 if (group_addr
.s_addr
== group
->group_addr
.s_addr
)
1042 struct igmp_group
*igmp_add_group_by_addr(struct igmp_sock
*igmp
,
1043 struct in_addr group_addr
)
1045 struct igmp_group
*group
;
1047 group
= find_group_by_addr(igmp
, group_addr
);
1052 if (!pim_is_group_224_4 (group_addr
))
1054 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1055 __PRETTY_FUNCTION__
);
1059 if (pim_is_group_224_0_0_0_24 (group_addr
))
1061 zlog_warn("%s: Group specified is part of 224.0.0.0/24",
1062 __PRETTY_FUNCTION__
);
1066 Non-existant group is created as INCLUDE {empty}:
1068 RFC 3376 - 5.1. Action on Change of Interface State
1070 If no interface state existed for that multicast address before
1071 the change (i.e., the change consisted of creating a new
1072 per-interface record), or if no state exists after the change
1073 (i.e., the change consisted of deleting a per-interface record),
1074 then the "non-existent" state is considered to have a filter mode
1075 of INCLUDE and an empty source list.
1078 group
= XCALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1080 zlog_warn("%s %s: XCALLOC() failure",
1081 __FILE__
, __PRETTY_FUNCTION__
);
1082 return NULL
; /* error, not found, could not create */
1085 group
->group_source_list
= list_new();
1086 if (!group
->group_source_list
) {
1087 zlog_warn("%s %s: list_new() failure",
1088 __FILE__
, __PRETTY_FUNCTION__
);
1089 XFREE(MTYPE_PIM_IGMP_GROUP
, group
); /* discard group */
1090 return NULL
; /* error, not found, could not initialize */
1092 group
->group_source_list
->del
= (void (*)(void *)) igmp_source_free
;
1094 group
->t_group_timer
= NULL
;
1095 group
->t_group_query_retransmit_timer
= NULL
;
1096 group
->group_specific_query_retransmit_count
= 0;
1097 group
->group_addr
= group_addr
;
1098 group
->group_igmp_sock
= igmp
;
1099 group
->last_igmp_v1_report_dsec
= -1;
1100 group
->last_igmp_v2_report_dsec
= -1;
1101 group
->group_creation
= pim_time_monotonic_sec();
1102 group
->igmp_version
= IGMP_DEFAULT_VERSION
;
1104 /* initialize new group as INCLUDE {empty} */
1105 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1107 listnode_add(igmp
->igmp_group_list
, group
);
1109 if (PIM_DEBUG_IGMP_TRACE
) {
1110 char group_str
[INET_ADDRSTRLEN
];
1111 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1112 zlog_debug("Creating new IGMP group %s on socket %d interface %s",
1113 group_str
, igmp
->fd
, igmp
->interface
->name
);
1117 RFC 3376: 6.2.2. Definition of Group Timers
1119 The group timer is only used when a group is in EXCLUDE mode and
1120 it represents the time for the *filter-mode* of the group to
1121 expire and switch to INCLUDE mode.
1123 zassert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1124 zassert(!group
->t_group_timer
); /* group timer == 0 */
1126 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1127 igmp_anysource_forward_stop(group
);
1133 igmp_send_query (int igmp_version
,
1134 struct igmp_group
*group
,
1140 struct in_addr dst_addr
,
1141 struct in_addr group_addr
,
1142 int query_max_response_time_dsec
,
1144 uint8_t querier_robustness_variable
,
1145 uint16_t querier_query_interval
)
1147 if (igmp_version
== 3) {
1148 igmp_v3_send_query (group
, fd
, ifname
, query_buf
,
1149 query_buf_size
, num_sources
,
1150 dst_addr
, group_addr
,
1151 query_max_response_time_dsec
, s_flag
,
1152 querier_robustness_variable
,
1153 querier_query_interval
);
1154 } else if (igmp_version
== 2) {
1155 igmp_v2_send_query (group
, fd
, ifname
, query_buf
,
1156 dst_addr
, group_addr
,
1157 query_max_response_time_dsec
);