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
);
41 static int pim_igmp_general_query(struct thread
*t
);
43 /* This socket is used for TXing IGMP packets only, IGMP RX happens
46 static int igmp_sock_open(struct in_addr ifaddr
, struct interface
*ifp
, uint32_t pim_options
)
52 fd
= pim_socket_mcast(IPPROTO_IGMP
, ifaddr
, ifp
, 1);
57 if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options
)) {
58 if (inet_aton(PIM_ALL_ROUTERS
, &group
)) {
59 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
))
63 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
64 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
65 PIM_ALL_ROUTERS
, errno
, safe_strerror(errno
));
70 IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
71 IGMP routers must receive general queries for querier election.
73 if (inet_aton(PIM_ALL_SYSTEMS
, &group
)) {
74 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
))
78 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
79 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
80 PIM_ALL_SYSTEMS
, errno
, safe_strerror(errno
));
83 if (inet_aton(PIM_ALL_IGMP_ROUTERS
, &group
)) {
84 if (!pim_socket_join(fd
, group
, ifaddr
, ifp
->ifindex
)) {
89 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
90 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
91 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
95 zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
96 fd
, inet_ntoa(ifaddr
));
104 #undef IGMP_SOCK_DUMP
106 #ifdef IGMP_SOCK_DUMP
107 static void igmp_sock_dump(array_t
*igmp_sock_array
)
109 int size
= array_size(igmp_sock_array
);
110 for (int i
= 0; i
< size
; ++i
) {
112 struct igmp_sock
*igmp
= array_get(igmp_sock_array
, i
);
114 zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
115 __FILE__
, __PRETTY_FUNCTION__
,
117 inet_ntoa(igmp
->ifaddr
),
123 struct igmp_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
124 struct in_addr ifaddr
)
126 struct listnode
*sock_node
;
127 struct igmp_sock
*igmp
;
129 #ifdef IGMP_SOCK_DUMP
130 igmp_sock_dump(igmp_sock_list
);
133 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
134 if (ifaddr
.s_addr
== igmp
->ifaddr
.s_addr
)
140 struct igmp_sock
*igmp_sock_lookup_by_fd(struct list
*igmp_sock_list
,
143 struct listnode
*sock_node
;
144 struct igmp_sock
*igmp
;
146 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
153 static int pim_igmp_other_querier_expire(struct thread
*t
)
155 struct igmp_sock
*igmp
;
157 igmp
= THREAD_ARG(t
);
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",
170 We are the current querier, then
171 re-start sending general queries.
172 RFC 2236 - sec 7 Other Querier
173 present timer expired (Send General
174 Query, Set Gen. Query. timer)
176 pim_igmp_general_query(t
);
181 void pim_igmp_other_querier_timer_on(struct igmp_sock
*igmp
)
183 long other_querier_present_interval_msec
;
184 struct pim_interface
*pim_ifp
;
187 zassert(igmp
->interface
);
188 zassert(igmp
->interface
->info
);
190 pim_ifp
= igmp
->interface
->info
;
192 if (igmp
->t_other_querier_timer
) {
194 There is other querier present already,
195 then reset the other-querier-present timer.
198 if (PIM_DEBUG_IGMP_TRACE
) {
199 char ifaddr_str
[INET_ADDRSTRLEN
];
200 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
201 zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
204 THREAD_OFF(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_add_timer_msec(master
, pim_igmp_other_querier_expire
, igmp
,
249 other_querier_present_interval_msec
,
250 &igmp
->t_other_querier_timer
);
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
);
269 igmp_recv_query(struct igmp_sock
*igmp
, int query_version
,
271 struct in_addr from
, const char *from_str
,
272 char *igmp_msg
, int igmp_msg_len
)
274 struct interface
*ifp
;
275 struct pim_interface
*pim_ifp
;
276 struct in_addr group_addr
;
277 uint16_t recv_checksum
;
280 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
282 ifp
= igmp
->interface
;
285 recv_checksum
= *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
287 /* for computing checksum */
288 *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
290 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
291 if (checksum
!= recv_checksum
) {
292 zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
293 query_version
, from_str
, ifp
->name
, recv_checksum
, checksum
);
297 /* RFC 3376 defines some guidelines on operating in backwards compatibility
298 * with older versions of IGMP but there are some gaps in the logic:
300 * - once we drop from say version 3 to version 2 we will never go back to
301 * version 3 even if the node that TXed an IGMP v2 query upgrades to v3
303 * - The node with the lowest IP is the querier so we will only know to drop
304 * from v3 to v2 if the node that is the querier is also the one that is
305 * running igmp v2. If a non-querier only supports igmp v2 we will have
308 * For now we will simplify things and inform the user that they need to
309 * configure all PIM routers to use the same version of IGMP.
311 if (query_version
!= pim_ifp
->igmp_version
) {
312 zlog_warn("Recv IGMP query v%d from %s on %s but we are using v%d, please "
313 "configure all PIM routers on this subnet to use the same "
315 query_version
, from_str
, ifp
->name
, pim_ifp
->igmp_version
);
319 if (PIM_DEBUG_IGMP_PACKETS
) {
320 char group_str
[INET_ADDRSTRLEN
];
321 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
322 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
323 query_version
, from_str
, ifp
->name
, group_str
);
327 RFC 3376: 6.6.2. Querier Election
329 When a router receives a query with a lower IP address, it sets
330 the Other-Querier-Present timer to Other Querier Present Interval
331 and ceases to send queries on the network if it was the previously
334 if (ntohl(from
.s_addr
) < ntohl(igmp
->ifaddr
.s_addr
)) {
336 if (PIM_DEBUG_IGMP_TRACE
) {
337 char ifaddr_str
[INET_ADDRSTRLEN
];
338 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
339 zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
341 ifaddr_str
, ntohl(igmp
->ifaddr
.s_addr
),
342 from_str
, ntohl(from
.s_addr
));
345 pim_igmp_other_querier_timer_on(igmp
);
348 /* IGMP version 3 is the only one where we process the RXed query */
349 if (query_version
== 3) {
350 igmp_v3_recv_query(igmp
, from_str
, igmp_msg
);
356 static void on_trace(const char *label
,
357 struct interface
*ifp
, struct in_addr from
)
359 if (PIM_DEBUG_IGMP_TRACE
) {
360 char from_str
[INET_ADDRSTRLEN
];
361 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
362 zlog_debug("%s: from %s on %s",
363 label
, from_str
, ifp
->name
);
368 igmp_v1_recv_report (struct igmp_sock
*igmp
,
369 struct in_addr from
, const char *from_str
,
370 char *igmp_msg
, int igmp_msg_len
)
372 struct interface
*ifp
= igmp
->interface
;
373 struct igmp_group
*group
;
374 struct in_addr group_addr
;
376 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
);
378 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
379 zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
380 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
384 if (PIM_DEBUG_IGMP_TRACE
) {
385 zlog_warn("%s %s: FIXME WRITEME",
386 __FILE__
, __PRETTY_FUNCTION__
);
389 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
391 /* non-existant group is created as INCLUDE {empty} */
392 group
= igmp_add_group_by_addr(igmp
, group_addr
);
397 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
402 int pim_igmp_packet(struct igmp_sock
*igmp
, char *buf
, size_t len
)
405 size_t ip_hlen
; /* ip header length in bytes */
409 char from_str
[INET_ADDRSTRLEN
];
410 char to_str
[INET_ADDRSTRLEN
];
412 if (len
< sizeof(*ip_hdr
)) {
413 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
414 len
, sizeof(*ip_hdr
));
418 ip_hdr
= (struct ip
*) buf
;
420 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
421 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
423 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
425 if (PIM_DEBUG_IGMP_PACKETS
) {
426 zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
427 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hlen
, ip_hdr
->ip_p
);
430 igmp_msg
= buf
+ ip_hlen
;
431 msg_type
= *igmp_msg
;
432 igmp_msg_len
= len
- ip_hlen
;
434 if (PIM_DEBUG_IGMP_PACKETS
) {
435 zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
436 from_str
, to_str
, igmp
->interface
->name
, ip_hdr
->ip_ttl
, msg_type
,
440 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
441 zlog_warn("IGMP message size=%d shorter than minimum=%d",
442 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
447 case PIM_IGMP_MEMBERSHIP_QUERY
:
449 int max_resp_code
= igmp_msg
[1];
453 RFC 3376: 7.1. Query Version Distinctions
454 IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
455 IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
456 IGMPv3 Query: length >= 12 octets
459 if (igmp_msg_len
== 8) {
460 query_version
= max_resp_code
? 2 : 1;
462 else if (igmp_msg_len
>= 12) {
466 zlog_warn("Unknown IGMP query version");
470 return igmp_recv_query(igmp
, query_version
, max_resp_code
,
471 ip_hdr
->ip_src
, from_str
,
472 igmp_msg
, igmp_msg_len
);
475 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
476 return igmp_v3_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
477 igmp_msg
, igmp_msg_len
);
479 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
480 return igmp_v2_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
481 igmp_msg
, igmp_msg_len
);
483 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
484 return igmp_v1_recv_report(igmp
, ip_hdr
->ip_src
, from_str
,
485 igmp_msg
, igmp_msg_len
);
487 case PIM_IGMP_V2_LEAVE_GROUP
:
488 return igmp_v2_recv_leave(igmp
, ip_hdr
->ip_src
, from_str
,
489 igmp_msg
, igmp_msg_len
);
492 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
497 void pim_igmp_general_query_on(struct igmp_sock
*igmp
)
499 struct pim_interface
*pim_ifp
;
504 Since this socket is starting as querier,
505 there should not exist a timer for other-querier-present.
507 zassert(!igmp
->t_other_querier_timer
);
508 pim_ifp
= igmp
->interface
->info
;
512 RFC 3376: 8.6. Startup Query Interval
514 The Startup Query Interval is the interval between General Queries
515 sent by a Querier on startup. Default: 1/4 the Query Interval.
516 The first one should be sent out immediately instead of 125/4
519 startup_mode
= igmp
->startup_query_count
> 0;
522 * If this is the first time we are sending a query on a
523 * newly configured igmp interface send it out in 1 second
524 * just to give the entire world a tiny bit of time to settle
525 * else the query interval is:
526 * query_interval = pim_ifp->igmp_default_query_interval >> 2;
528 if (igmp
->startup_query_count
== igmp
->querier_robustness_variable
)
531 query_interval
= PIM_IGMP_SQI(pim_ifp
->igmp_default_query_interval
);
533 --igmp
->startup_query_count
;
536 query_interval
= igmp
->querier_query_interval
;
539 if (PIM_DEBUG_IGMP_TRACE
) {
540 char ifaddr_str
[INET_ADDRSTRLEN
];
541 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
542 zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
545 startup_mode
? "startup" : "non-startup",
548 igmp
->t_igmp_query_timer
= NULL
;
549 thread_add_timer(master
, pim_igmp_general_query
, igmp
, query_interval
,
550 &igmp
->t_igmp_query_timer
);
553 void pim_igmp_general_query_off(struct igmp_sock
*igmp
)
557 if (PIM_DEBUG_IGMP_TRACE
) {
558 if (igmp
->t_igmp_query_timer
) {
559 char ifaddr_str
[INET_ADDRSTRLEN
];
560 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
561 zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
562 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
565 THREAD_OFF(igmp
->t_igmp_query_timer
);
568 /* Issue IGMP general query */
569 static int pim_igmp_general_query(struct thread
*t
)
571 struct igmp_sock
*igmp
;
572 struct in_addr dst_addr
;
573 struct in_addr group_addr
;
574 struct pim_interface
*pim_ifp
;
577 igmp
= THREAD_ARG(t
);
579 zassert(igmp
->interface
);
580 zassert(igmp
->interface
->info
);
582 pim_ifp
= igmp
->interface
->info
;
584 if (pim_ifp
->igmp_version
== 3) {
585 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
587 query_buf_size
= IGMP_V12_MSG_SIZE
;
590 char query_buf
[query_buf_size
];
593 RFC3376: 4.1.12. IP Destination Addresses for Queries
595 In IGMPv3, General Queries are sent with an IP destination address
596 of 224.0.0.1, the all-systems multicast address. Group-Specific
597 and Group-and-Source-Specific Queries are sent with an IP
598 destination address equal to the multicast address of interest.
601 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
602 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
604 if (PIM_DEBUG_IGMP_TRACE
) {
605 char querier_str
[INET_ADDRSTRLEN
];
606 char dst_str
[INET_ADDRSTRLEN
];
607 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
608 sizeof(querier_str
));
609 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
610 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
611 querier_str
, dst_str
, igmp
->interface
->name
);
614 igmp_send_query (pim_ifp
->igmp_version
,
617 igmp
->interface
->name
,
623 pim_ifp
->igmp_query_max_response_time_dsec
,
624 1 /* s_flag: always set for general queries */,
625 igmp
->querier_robustness_variable
,
626 igmp
->querier_query_interval
);
628 pim_igmp_general_query_on(igmp
);
633 static void sock_close(struct igmp_sock
*igmp
)
635 pim_igmp_other_querier_timer_off(igmp
);
636 pim_igmp_general_query_off(igmp
);
638 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
639 if (igmp
->t_igmp_read
) {
640 zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
641 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
642 igmp
->interface
->name
);
645 THREAD_OFF(igmp
->t_igmp_read
);
647 if (close(igmp
->fd
)) {
648 zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
649 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
,
650 errno
, safe_strerror(errno
));
653 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
654 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
655 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
);
659 void igmp_startup_mode_on(struct igmp_sock
*igmp
)
661 struct pim_interface
*pim_ifp
;
663 pim_ifp
= igmp
->interface
->info
;
666 RFC 3376: 8.7. Startup Query Count
668 The Startup Query Count is the number of Queries sent out on
669 startup, separated by the Startup Query Interval. Default: the
672 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
675 Since we're (re)starting, reset QQI to default Query Interval
677 igmp
->querier_query_interval
= pim_ifp
->igmp_default_query_interval
;
680 static void igmp_group_free(struct igmp_group
*group
)
682 list_free(group
->group_source_list
);
684 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
687 static void igmp_group_delete(struct igmp_group
*group
)
689 struct listnode
*src_node
;
690 struct listnode
*src_nextnode
;
691 struct igmp_source
*src
;
693 if (PIM_DEBUG_IGMP_TRACE
) {
694 char group_str
[INET_ADDRSTRLEN
];
695 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
696 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
698 group
->group_igmp_sock
->fd
,
699 group
->group_igmp_sock
->interface
->name
);
702 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
, src
)) {
703 igmp_source_delete(src
);
706 if (group
->t_group_query_retransmit_timer
) {
707 THREAD_OFF(group
->t_group_query_retransmit_timer
);
710 group_timer_off(group
);
711 listnode_delete(group
->group_igmp_sock
->igmp_group_list
, group
);
712 hash_release (group
->group_igmp_sock
->igmp_group_hash
, group
);
714 igmp_group_free(group
);
717 void igmp_group_delete_empty_include(struct igmp_group
*group
)
719 zassert(!group
->group_filtermode_isexcl
);
720 zassert(!listcount(group
->group_source_list
));
722 igmp_group_delete(group
);
725 void igmp_sock_free(struct igmp_sock
*igmp
)
727 zassert(!igmp
->t_igmp_read
);
728 zassert(!igmp
->t_igmp_query_timer
);
729 zassert(!igmp
->t_other_querier_timer
);
730 zassert(igmp
->igmp_group_list
);
731 zassert(!listcount(igmp
->igmp_group_list
));
733 list_free(igmp
->igmp_group_list
);
734 hash_free(igmp
->igmp_group_hash
);
736 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
739 void igmp_sock_delete(struct igmp_sock
*igmp
)
741 struct pim_interface
*pim_ifp
;
742 struct listnode
*grp_node
;
743 struct listnode
*grp_nextnode
;
744 struct igmp_group
*grp
;
746 for (ALL_LIST_ELEMENTS(igmp
->igmp_group_list
, grp_node
, grp_nextnode
, grp
)) {
747 igmp_group_delete(grp
);
752 pim_ifp
= igmp
->interface
->info
;
754 listnode_delete(pim_ifp
->igmp_socket_list
, igmp
);
756 igmp_sock_free(igmp
);
760 igmp_sock_delete_all (struct interface
*ifp
)
762 struct pim_interface
*pim_ifp
;
763 struct listnode
*igmp_node
, *igmp_nextnode
;
764 struct igmp_sock
*igmp
;
768 for (ALL_LIST_ELEMENTS (pim_ifp
->igmp_socket_list
, igmp_node
,
769 igmp_nextnode
, igmp
))
771 igmp_sock_delete(igmp
);
776 igmp_group_hash_key (void *arg
)
778 struct igmp_group
*group
= (struct igmp_group
*)arg
;
780 return jhash_1word(group
->group_addr
.s_addr
, 0);
784 igmp_group_hash_equal (const void *arg1
, const void *arg2
)
786 const struct igmp_group
*g1
= (const struct igmp_group
*)arg1
;
787 const struct igmp_group
*g2
= (const struct igmp_group
*)arg2
;
789 if (g1
->group_addr
.s_addr
== g2
->group_addr
.s_addr
)
795 static struct igmp_sock
*igmp_sock_new(int fd
,
796 struct in_addr ifaddr
,
797 struct interface
*ifp
)
799 struct pim_interface
*pim_ifp
;
800 struct igmp_sock
*igmp
;
804 if (PIM_DEBUG_IGMP_TRACE
) {
805 zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
806 fd
, inet_ntoa(ifaddr
), ifp
->name
);
809 igmp
= XCALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
811 zlog_warn("%s %s: XCALLOC() failure",
812 __FILE__
, __PRETTY_FUNCTION__
);
816 igmp
->igmp_group_list
= list_new();
817 if (!igmp
->igmp_group_list
) {
818 zlog_err("%s %s: failure: igmp_group_list = list_new()",
819 __FILE__
, __PRETTY_FUNCTION__
);
822 igmp
->igmp_group_list
->del
= (void (*)(void *)) igmp_group_free
;
824 igmp
->igmp_group_hash
= hash_create (igmp_group_hash_key
,
825 igmp_group_hash_equal
);
828 igmp
->interface
= ifp
;
829 igmp
->ifaddr
= ifaddr
;
830 igmp
->t_igmp_read
= NULL
;
831 igmp
->t_igmp_query_timer
= NULL
;
832 igmp
->t_other_querier_timer
= NULL
; /* no other querier present */
833 igmp
->querier_robustness_variable
= pim_ifp
->igmp_default_robustness_variable
;
834 igmp
->sock_creation
= pim_time_monotonic_sec();
837 igmp_startup_mode_on() will reset QQI:
839 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
841 igmp_startup_mode_on(igmp
);
842 pim_igmp_general_query_on(igmp
);
847 static void igmp_read_on (struct igmp_sock
*igmp
);
850 pim_igmp_read (struct thread
*t
)
853 struct igmp_sock
*igmp
= (struct igmp_sock
*)THREAD_ARG(t
);
854 struct sockaddr_in from
;
855 struct sockaddr_in to
;
856 socklen_t fromlen
= sizeof(from
);
857 socklen_t tolen
= sizeof(to
);
858 ifindex_t ifindex
= -1;
864 len
= pim_socket_recvfromto(igmp
->fd
, buf
, sizeof(buf
),
872 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
885 igmp_read_on (struct igmp_sock
*igmp
)
888 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
889 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
892 igmp
->t_igmp_read
= NULL
;
893 thread_add_read(master
, pim_igmp_read
, igmp
, igmp
->fd
, &igmp
->t_igmp_read
);
897 struct igmp_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
898 struct in_addr ifaddr
,
899 struct interface
*ifp
)
901 struct pim_interface
*pim_ifp
;
902 struct igmp_sock
*igmp
;
907 fd
= igmp_sock_open(ifaddr
, ifp
, pim_ifp
->options
);
909 zlog_warn("Could not open IGMP socket for %s on %s",
910 inet_ntoa(ifaddr
), ifp
->name
);
914 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
);
916 zlog_err("%s %s: igmp_sock_new() failure",
917 __FILE__
, __PRETTY_FUNCTION__
);
924 listnode_add(igmp_sock_list
, igmp
);
926 #ifdef IGMP_SOCK_DUMP
927 igmp_sock_dump(igmp_sock_array
);
934 RFC 3376: 6.5. Switching Router Filter-Modes
936 When a router's filter-mode for a group is EXCLUDE and the group
937 timer expires, the router filter-mode for the group transitions to
940 A router uses source records with running source timers as its state
941 for the switch to a filter-mode of INCLUDE. If there are any source
942 records with source timers greater than zero (i.e., requested to be
943 forwarded), a router switches to filter-mode of INCLUDE using those
944 source records. Source records whose timers are zero (from the
945 previous EXCLUDE mode) are deleted.
947 static int igmp_group_timer(struct thread
*t
)
949 struct igmp_group
*group
;
951 group
= THREAD_ARG(t
);
953 if (PIM_DEBUG_IGMP_TRACE
) {
954 char group_str
[INET_ADDRSTRLEN
];
955 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
956 zlog_debug("%s: Timer for group %s on interface %s",
958 group_str
, group
->group_igmp_sock
->interface
->name
);
961 zassert(group
->group_filtermode_isexcl
);
963 group
->group_filtermode_isexcl
= 0;
965 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
966 igmp_anysource_forward_stop(group
);
968 igmp_source_delete_expired(group
->group_source_list
);
970 zassert(!group
->group_filtermode_isexcl
);
973 RFC 3376: 6.2.2. Definition of Group Timers
975 If there are no more source records for the group, delete group
978 if (listcount(group
->group_source_list
) < 1) {
979 igmp_group_delete_empty_include(group
);
985 static void group_timer_off(struct igmp_group
*group
)
987 if (!group
->t_group_timer
)
990 if (PIM_DEBUG_IGMP_TRACE
) {
991 char group_str
[INET_ADDRSTRLEN
];
992 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
993 zlog_debug("Cancelling TIMER event for group %s on %s",
994 group_str
, group
->group_igmp_sock
->interface
->name
);
996 THREAD_OFF(group
->t_group_timer
);
999 void igmp_group_timer_on(struct igmp_group
*group
,
1000 long interval_msec
, const char *ifname
)
1002 group_timer_off(group
);
1004 if (PIM_DEBUG_IGMP_EVENTS
) {
1005 char group_str
[INET_ADDRSTRLEN
];
1006 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1007 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1008 interval_msec
/ 1000,
1009 interval_msec
% 1000,
1014 RFC 3376: 6.2.2. Definition of Group Timers
1016 The group timer is only used when a group is in EXCLUDE mode and
1017 it represents the time for the *filter-mode* of the group to
1018 expire and switch to INCLUDE mode.
1020 zassert(group
->group_filtermode_isexcl
);
1022 thread_add_timer_msec(master
, igmp_group_timer
, group
, interval_msec
,
1023 &group
->t_group_timer
);
1027 find_group_by_addr (struct igmp_sock
*igmp
,
1028 struct in_addr group_addr
)
1030 struct igmp_group lookup
;
1032 lookup
.group_addr
.s_addr
= group_addr
.s_addr
;
1034 return hash_lookup(igmp
->igmp_group_hash
, &lookup
);
1037 struct igmp_group
*igmp_add_group_by_addr(struct igmp_sock
*igmp
,
1038 struct in_addr group_addr
)
1040 struct igmp_group
*group
;
1042 group
= find_group_by_addr(igmp
, group_addr
);
1047 if (!pim_is_group_224_4 (group_addr
))
1049 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1050 __PRETTY_FUNCTION__
);
1054 if (pim_is_group_224_0_0_0_24 (group_addr
))
1056 zlog_warn("%s: Group specified is part of 224.0.0.0/24",
1057 __PRETTY_FUNCTION__
);
1061 Non-existant group is created as INCLUDE {empty}:
1063 RFC 3376 - 5.1. Action on Change of Interface State
1065 If no interface state existed for that multicast address before
1066 the change (i.e., the change consisted of creating a new
1067 per-interface record), or if no state exists after the change
1068 (i.e., the change consisted of deleting a per-interface record),
1069 then the "non-existent" state is considered to have a filter mode
1070 of INCLUDE and an empty source list.
1073 group
= XCALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1075 zlog_warn("%s %s: XCALLOC() failure",
1076 __FILE__
, __PRETTY_FUNCTION__
);
1077 return NULL
; /* error, not found, could not create */
1080 group
->group_source_list
= list_new();
1081 if (!group
->group_source_list
) {
1082 zlog_warn("%s %s: list_new() failure",
1083 __FILE__
, __PRETTY_FUNCTION__
);
1084 XFREE(MTYPE_PIM_IGMP_GROUP
, group
); /* discard group */
1085 return NULL
; /* error, not found, could not initialize */
1087 group
->group_source_list
->del
= (void (*)(void *)) igmp_source_free
;
1089 group
->t_group_timer
= NULL
;
1090 group
->t_group_query_retransmit_timer
= NULL
;
1091 group
->group_specific_query_retransmit_count
= 0;
1092 group
->group_addr
= group_addr
;
1093 group
->group_igmp_sock
= igmp
;
1094 group
->last_igmp_v1_report_dsec
= -1;
1095 group
->last_igmp_v2_report_dsec
= -1;
1096 group
->group_creation
= pim_time_monotonic_sec();
1097 group
->igmp_version
= IGMP_DEFAULT_VERSION
;
1099 /* initialize new group as INCLUDE {empty} */
1100 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1102 listnode_add(igmp
->igmp_group_list
, group
);
1103 group
= hash_get (igmp
->igmp_group_hash
, group
, hash_alloc_intern
);
1105 if (PIM_DEBUG_IGMP_TRACE
) {
1106 char group_str
[INET_ADDRSTRLEN
];
1107 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1108 zlog_debug("Creating new IGMP group %s on socket %d interface %s",
1109 group_str
, igmp
->fd
, igmp
->interface
->name
);
1113 RFC 3376: 6.2.2. Definition of Group Timers
1115 The group timer is only used when a group is in EXCLUDE mode and
1116 it represents the time for the *filter-mode* of the group to
1117 expire and switch to INCLUDE mode.
1119 zassert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1120 zassert(!group
->t_group_timer
); /* group timer == 0 */
1122 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1123 igmp_anysource_forward_stop(group
);
1129 igmp_send_query (int igmp_version
,
1130 struct igmp_group
*group
,
1136 struct in_addr dst_addr
,
1137 struct in_addr group_addr
,
1138 int query_max_response_time_dsec
,
1140 uint8_t querier_robustness_variable
,
1141 uint16_t querier_query_interval
)
1143 if (igmp_version
== 3) {
1144 igmp_v3_send_query (group
, fd
, ifname
, query_buf
,
1145 query_buf_size
, num_sources
,
1146 dst_addr
, group_addr
,
1147 query_max_response_time_dsec
, s_flag
,
1148 querier_robustness_variable
,
1149 querier_query_interval
);
1150 } else if (igmp_version
== 2) {
1151 igmp_v2_send_query (group
, fd
, ifname
, query_buf
,
1152 dst_addr
, group_addr
,
1153 query_max_response_time_dsec
);