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,
30 #include "pim_igmpv3.h"
31 #include "pim_iface.h"
33 #include "pim_mroute.h"
37 #include "pim_zebra.h"
39 #define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1)
40 #define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2)
41 #define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
42 #define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
43 #define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5)
44 #define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6)
46 static void group_timer_off(struct igmp_group
*group
);
48 static int igmp_sock_open(struct in_addr ifaddr
, ifindex_t ifindex
, uint32_t pim_options
)
54 fd
= pim_socket_mcast(IPPROTO_IGMP
, ifaddr
, ifindex
, 1 /* loop=true */);
58 if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options
)) {
59 if (inet_aton(PIM_ALL_ROUTERS
, &group
)) {
60 if (!pim_socket_join(fd
, group
, ifaddr
, ifindex
))
64 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
65 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
66 PIM_ALL_ROUTERS
, errno
, safe_strerror(errno
));
71 IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
72 IGMP routers must receive general queries for querier election.
74 if (inet_aton(PIM_ALL_SYSTEMS
, &group
)) {
75 if (!pim_socket_join(fd
, group
, ifaddr
, ifindex
))
79 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
80 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
81 PIM_ALL_SYSTEMS
, errno
, safe_strerror(errno
));
84 if (inet_aton(PIM_ALL_IGMP_ROUTERS
, &group
)) {
85 if (!pim_socket_join(fd
, group
, ifaddr
, ifindex
)) {
90 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
91 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
92 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
96 zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
97 fd
, inet_ntoa(ifaddr
));
105 #undef IGMP_SOCK_DUMP
107 #ifdef IGMP_SOCK_DUMP
108 static void igmp_sock_dump(array_t
*igmp_sock_array
)
110 int size
= array_size(igmp_sock_array
);
111 for (int i
= 0; i
< size
; ++i
) {
113 struct igmp_sock
*igmp
= array_get(igmp_sock_array
, i
);
115 zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
116 __FILE__
, __PRETTY_FUNCTION__
,
118 inet_ntoa(igmp
->ifaddr
),
124 struct igmp_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
125 struct in_addr ifaddr
)
127 struct listnode
*sock_node
;
128 struct igmp_sock
*igmp
;
130 #ifdef IGMP_SOCK_DUMP
131 igmp_sock_dump(igmp_sock_list
);
134 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
135 if (ifaddr
.s_addr
== igmp
->ifaddr
.s_addr
)
141 struct igmp_sock
*igmp_sock_lookup_by_fd(struct list
*igmp_sock_list
,
144 struct listnode
*sock_node
;
145 struct igmp_sock
*igmp
;
147 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
154 static int pim_igmp_other_querier_expire(struct thread
*t
)
156 struct igmp_sock
*igmp
;
159 igmp
= THREAD_ARG(t
);
162 zassert(igmp
->t_other_querier_timer
);
163 zassert(!igmp
->t_igmp_query_timer
);
165 if (PIM_DEBUG_IGMP_TRACE
) {
166 char ifaddr_str
[100];
167 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
168 zlog_debug("%s: Querier %s resuming",
173 igmp
->t_other_querier_timer
= NULL
;
176 We are the current querier, then
177 re-start sending general queries.
179 pim_igmp_general_query_on(igmp
);
184 void pim_igmp_other_querier_timer_on(struct igmp_sock
*igmp
)
186 long other_querier_present_interval_msec
;
187 struct pim_interface
*pim_ifp
;
190 zassert(igmp
->interface
);
191 zassert(igmp
->interface
->info
);
193 pim_ifp
= igmp
->interface
->info
;
195 if (igmp
->t_other_querier_timer
) {
197 There is other querier present already,
198 then reset the other-querier-present timer.
201 if (PIM_DEBUG_IGMP_TRACE
) {
202 char ifaddr_str
[100];
203 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
204 zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
208 THREAD_OFF(igmp
->t_other_querier_timer
);
209 zassert(!igmp
->t_other_querier_timer
);
213 We are the current querier, then stop sending general queries:
214 igmp->t_igmp_query_timer = NULL;
216 pim_igmp_general_query_off(igmp
);
220 Since this socket is starting the other-querier-present timer,
221 there should not be periodic query timer for this socket.
223 zassert(!igmp
->t_igmp_query_timer
);
226 RFC 3376: 8.5. Other Querier Present Interval
228 The Other Querier Present Interval is the length of time that must
229 pass before a multicast router decides that there is no longer
230 another multicast router which should be the querier. This value
231 MUST be ((the Robustness Variable) times (the Query Interval)) plus
232 (one half of one Query Response Interval).
234 other_querier_present_interval_msec = \
235 igmp->querier_robustness_variable * \
236 1000 * igmp->querier_query_interval + \
237 100 * (pim_ifp->query_max_response_time_dsec >> 1);
239 other_querier_present_interval_msec
=
240 PIM_IGMP_OQPI_MSEC(igmp
->querier_robustness_variable
,
241 igmp
->querier_query_interval
,
242 pim_ifp
->igmp_query_max_response_time_dsec
);
244 if (PIM_DEBUG_IGMP_TRACE
) {
245 char ifaddr_str
[100];
246 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
247 zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
249 other_querier_present_interval_msec
/ 1000,
250 other_querier_present_interval_msec
% 1000);
253 THREAD_TIMER_MSEC_ON(master
, igmp
->t_other_querier_timer
,
254 pim_igmp_other_querier_expire
,
255 igmp
, other_querier_present_interval_msec
);
258 void pim_igmp_other_querier_timer_off(struct igmp_sock
*igmp
)
262 if (PIM_DEBUG_IGMP_TRACE
) {
263 if (igmp
->t_other_querier_timer
) {
264 char ifaddr_str
[100];
265 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
266 zlog_debug("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
);
271 zassert(!igmp
->t_other_querier_timer
);
274 static int recv_igmp_query(struct igmp_sock
*igmp
, int query_version
,
276 struct in_addr from
, const char *from_str
,
277 char *igmp_msg
, int igmp_msg_len
)
279 struct interface
*ifp
;
280 struct pim_interface
*pim_ifp
;
281 uint8_t resv_s_qrv
= 0;
284 struct in_addr group_addr
;
285 uint16_t recv_checksum
;
289 //group_addr = *(struct in_addr *)(igmp_msg + 4);
290 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
292 ifp
= igmp
->interface
;
295 recv_checksum
= *(uint16_t *) (igmp_msg
+ IGMP_V3_CHECKSUM_OFFSET
);
297 /* for computing checksum */
298 *(uint16_t *) (igmp_msg
+ IGMP_V3_CHECKSUM_OFFSET
) = 0;
300 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
301 if (checksum
!= recv_checksum
) {
302 zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
303 query_version
, from_str
, ifp
->name
, recv_checksum
, checksum
);
307 if (PIM_DEBUG_IGMP_PACKETS
) {
309 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
310 zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s",
311 query_version
, from_str
, ifp
->name
,
312 igmp_msg_len
, checksum
, group_str
);
316 RFC 3376: 6.6.2. Querier Election
318 When a router receives a query with a lower IP address, it sets
319 the Other-Querier-Present timer to Other Querier Present Interval
320 and ceases to send queries on the network if it was the previously
323 if (ntohl(from
.s_addr
) < ntohl(igmp
->ifaddr
.s_addr
)) {
325 if (PIM_DEBUG_IGMP_TRACE
) {
326 char ifaddr_str
[100];
327 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
328 zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
330 ifaddr_str
, ntohl(igmp
->ifaddr
.s_addr
),
331 from_str
, ntohl(from
.s_addr
));
334 pim_igmp_other_querier_timer_on(igmp
);
337 if (query_version
== 3) {
339 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
341 Routers adopt the QRV value from the most recently received Query
342 as their own [Robustness Variable] value, unless that most
343 recently received QRV was zero, in which case the receivers use
344 the default [Robustness Variable] value specified in section 8.1
345 or a statically configured value.
347 resv_s_qrv
= igmp_msg
[8];
348 qrv
= 7 & resv_s_qrv
;
349 igmp
->querier_robustness_variable
= qrv
? qrv
: pim_ifp
->igmp_default_robustness_variable
;
353 RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
355 Multicast routers that are not the current querier adopt the QQI
356 value from the most recently received Query as their own [Query
357 Interval] value, unless that most recently received QQI was zero,
358 in which case the receiving routers use the default.
360 if (igmp
->t_other_querier_timer
&& query_version
== 3) {
361 /* other querier present */
365 qqi
= igmp_msg_decode8to16(qqic
);
366 igmp
->querier_query_interval
= qqi
? qqi
: pim_ifp
->igmp_default_query_interval
;
368 if (PIM_DEBUG_IGMP_TRACE
) {
369 char ifaddr_str
[100];
370 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
371 zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
373 qqi
? "recv-non-default" : "default",
374 igmp
->querier_query_interval
,
381 RFC 3376: 6.6.1. Timer Updates
383 When a router sends or receives a query with a clear Suppress
384 Router-Side Processing flag, it must update its timers to reflect
385 the correct timeout values for the group or sources being queried.
387 General queries don't trigger timer update.
389 if (query_version
== 3) {
390 s_flag
= (1 << 3) & resv_s_qrv
;
393 /* Neither V1 nor V2 have this field. Pimd should really go into
394 * a compatibility mode here and run as V2 (or V1) but it doesn't
395 * so for now, lets just set the flag to suppress these timer updates.
401 /* s_flag is clear */
403 if (PIM_INADDR_IS_ANY(group_addr
)) {
404 /* this is a general query */
406 /* log that general query should have the s_flag set */
407 zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
408 query_version
, from_str
, ifp
->name
);
411 struct igmp_group
*group
;
413 /* this is a non-general query: perform timer updates */
415 group
= find_group_by_addr(igmp
, group_addr
);
417 int recv_num_sources
= ntohs(*(uint16_t *)(igmp_msg
+ IGMP_V3_NUMSOURCES_OFFSET
));
420 RFC 3376: 6.6.1. Timer Updates
421 Query Q(G,A): Source Timer for sources in A are lowered to LMQT
422 Query Q(G): Group Timer is lowered to LMQT
424 if (recv_num_sources
< 1) {
425 /* Query Q(G): Group Timer is lowered to LMQT */
427 igmp_group_timer_lower_to_lmqt(group
);
430 /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
432 /* Scan sources in query and lower their timers to LMQT */
433 struct in_addr
*sources
= (struct in_addr
*)(igmp_msg
+ IGMP_V3_SOURCES_OFFSET
);
434 for (i
= 0; i
< recv_num_sources
; ++i
) {
435 //struct in_addr src_addr = sources[i];
436 //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr);
437 struct in_addr src_addr
;
438 struct igmp_source
*src
;
439 memcpy(&src_addr
, sources
+ i
, sizeof(struct in_addr
));
440 src
= igmp_find_source_by_addr(group
, src_addr
);
442 igmp_source_timer_lower_to_lmqt(src
);
450 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
451 zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update",
452 query_version
, from_str
, ifp
->name
, group_str
);
455 } /* s_flag is clear: timer updates */
460 static int igmp_v3_report(struct igmp_sock
*igmp
,
461 struct in_addr from
, const char *from_str
,
462 char *igmp_msg
, int igmp_msg_len
)
464 uint16_t recv_checksum
;
467 uint8_t *group_record
;
468 uint8_t *report_pastend
= (uint8_t *) igmp_msg
+ igmp_msg_len
;
469 struct interface
*ifp
= igmp
->interface
;
473 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
474 zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
475 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V3_MSG_MIN_SIZE
);
479 recv_checksum
= *(uint16_t *) (igmp_msg
+ IGMP_V3_CHECKSUM_OFFSET
);
481 /* for computing checksum */
482 *(uint16_t *) (igmp_msg
+ IGMP_V3_CHECKSUM_OFFSET
) = 0;
484 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
485 if (checksum
!= recv_checksum
) {
486 zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
487 from_str
, ifp
->name
, recv_checksum
, checksum
);
491 num_groups
= ntohs(*(uint16_t *) (igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
492 if (num_groups
< 1) {
493 zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
494 from_str
, ifp
->name
);
498 if (PIM_DEBUG_IGMP_PACKETS
) {
499 zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
500 from_str
, ifp
->name
, igmp_msg_len
, checksum
, num_groups
);
503 group_record
= (uint8_t *) igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
506 for (i
= 0; i
< num_groups
; ++i
) {
507 struct in_addr rec_group
;
517 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
) > report_pastend
) {
518 zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
519 from_str
, ifp
->name
);
523 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
524 rec_auxdatalen
= group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
525 rec_num_sources
= ntohs(* (uint16_t *) (group_record
+ IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
527 //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET);
528 memcpy(&rec_group
, group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
, sizeof(struct in_addr
));
530 if (PIM_DEBUG_IGMP_PACKETS
) {
531 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
532 from_str
, ifp
->name
, i
, rec_type
, rec_auxdatalen
, rec_num_sources
, inet_ntoa(rec_group
));
537 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
539 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
541 if ((src
+ 4) > report_pastend
) {
542 zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
543 from_str
, ifp
->name
);
547 if (PIM_DEBUG_IGMP_PACKETS
) {
550 if (!inet_ntop(AF_INET
, src
, src_str
, sizeof(src_str
)))
551 sprintf(src_str
, "<source?>");
553 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
554 from_str
, ifp
->name
, i
, inet_ntoa(rec_group
), src_str
);
556 } /* for (sources) */
559 lncb
.family
= AF_INET
;
560 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
564 g
.u
.prefix4
= rec_group
;
567 * If we receive a igmp report with the group in 224.0.0.0/24
568 * then we should ignore it
570 if (prefix_match(&lncb
, &g
))
575 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
576 igmpv3_report_isin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
578 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
579 igmpv3_report_isex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
581 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
582 igmpv3_report_toin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
584 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
585 igmpv3_report_toex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
587 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
588 igmpv3_report_allow(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
590 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
591 igmpv3_report_block(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
594 zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
595 from_str
, ifp
->name
, rec_type
);
598 group_record
+= 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
601 } /* for (group records) */
606 static void on_trace(const char *label
,
607 struct interface
*ifp
, struct in_addr from
)
609 if (PIM_DEBUG_IGMP_TRACE
) {
611 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
612 zlog_debug("%s: from %s on %s",
613 label
, from_str
, ifp
->name
);
617 static int igmp_v2_report(struct igmp_sock
*igmp
,
618 struct in_addr from
, const char *from_str
,
619 char *igmp_msg
, int igmp_msg_len
)
621 struct interface
*ifp
= igmp
->interface
;
622 struct igmp_group
*group
;
623 struct in_addr group_addr
;
625 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
);
627 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
628 zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d",
629 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
633 if (PIM_DEBUG_IGMP_TRACE
) {
634 zlog_warn("%s %s: FIXME WRITEME",
635 __FILE__
, __PRETTY_FUNCTION__
);
638 //group_addr = *(struct in_addr *)(igmp_msg + 4);
639 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
641 /* non-existant group is created as INCLUDE {empty} */
642 group
= igmp_add_group_by_addr(igmp
, group_addr
);
647 group
->last_igmp_v2_report_dsec
= pim_time_monotonic_dsec();
652 static int igmp_v2_leave(struct igmp_sock
*igmp
,
653 struct in_addr from
, const char *from_str
,
654 char *igmp_msg
, int igmp_msg_len
)
656 struct interface
*ifp
= igmp
->interface
;
658 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
);
660 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
661 zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d",
662 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
666 if (PIM_DEBUG_IGMP_TRACE
) {
667 zlog_warn("%s %s: FIXME WRITEME",
668 __FILE__
, __PRETTY_FUNCTION__
);
674 static int igmp_v1_report(struct igmp_sock
*igmp
,
675 struct in_addr from
, const char *from_str
,
676 char *igmp_msg
, int igmp_msg_len
)
678 struct interface
*ifp
= igmp
->interface
;
679 struct igmp_group
*group
;
680 struct in_addr group_addr
;
682 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
);
684 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
685 zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
686 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
690 if (PIM_DEBUG_IGMP_TRACE
) {
691 zlog_warn("%s %s: FIXME WRITEME",
692 __FILE__
, __PRETTY_FUNCTION__
);
695 //group_addr = *(struct in_addr *)(igmp_msg + 4);
696 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
698 /* non-existant group is created as INCLUDE {empty} */
699 group
= igmp_add_group_by_addr(igmp
, group_addr
);
704 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
709 int pim_igmp_packet(struct igmp_sock
*igmp
, char *buf
, size_t len
)
712 size_t ip_hlen
; /* ip header length in bytes */
719 if (len
< sizeof(*ip_hdr
)) {
720 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
721 len
, sizeof(*ip_hdr
));
725 ip_hdr
= (struct ip
*) buf
;
727 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
728 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
730 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
732 if (PIM_DEBUG_IGMP_PACKETS
) {
733 zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
734 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hlen
, ip_hdr
->ip_p
);
737 if (ip_hdr
->ip_p
!= PIM_IP_PROTO_IGMP
) {
738 zlog_warn("IP packet protocol=%d is not IGMP=%d",
739 ip_hdr
->ip_p
, PIM_IP_PROTO_IGMP
);
743 if (ip_hlen
< PIM_IP_HEADER_MIN_LEN
) {
744 zlog_warn("IP packet header size=%zu shorter than minimum=%d",
745 ip_hlen
, PIM_IP_HEADER_MIN_LEN
);
748 if (ip_hlen
> PIM_IP_HEADER_MAX_LEN
) {
749 zlog_warn("IP packet header size=%zu greater than maximum=%d",
750 ip_hlen
, PIM_IP_HEADER_MAX_LEN
);
754 igmp_msg
= buf
+ ip_hlen
;
755 msg_type
= *igmp_msg
;
756 igmp_msg_len
= len
- ip_hlen
;
758 if (PIM_DEBUG_IGMP_PACKETS
) {
759 zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
760 from_str
, to_str
, igmp
->interface
->name
, ip_hdr
->ip_ttl
, msg_type
,
764 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
765 zlog_warn("IGMP message size=%d shorter than minimum=%d",
766 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
771 case PIM_IGMP_MEMBERSHIP_QUERY
:
773 int max_resp_code
= igmp_msg
[1];
777 RFC 3376: 7.1. Query Version Distinctions
778 IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
779 IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
780 IGMPv3 Query: length >= 12 octets
783 if (igmp_msg_len
== 8) {
784 query_version
= max_resp_code
? 2 : 1;
786 else if (igmp_msg_len
>= 12) {
790 zlog_warn("Unknown IGMP query version");
794 return recv_igmp_query(igmp
, query_version
, max_resp_code
,
795 ip_hdr
->ip_src
, from_str
,
796 igmp_msg
, igmp_msg_len
);
799 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
800 return igmp_v3_report(igmp
, ip_hdr
->ip_src
, from_str
,
801 igmp_msg
, igmp_msg_len
);
803 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
804 return igmp_v2_report(igmp
, ip_hdr
->ip_src
, from_str
,
805 igmp_msg
, igmp_msg_len
);
807 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
808 return igmp_v1_report(igmp
, ip_hdr
->ip_src
, from_str
,
809 igmp_msg
, igmp_msg_len
);
811 case PIM_IGMP_V2_LEAVE_GROUP
:
812 return igmp_v2_leave(igmp
, ip_hdr
->ip_src
, from_str
,
813 igmp_msg
, igmp_msg_len
);
816 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
821 static int pim_igmp_general_query(struct thread
*t
);
823 void pim_igmp_general_query_on(struct igmp_sock
*igmp
)
825 struct pim_interface
*pim_ifp
;
830 zassert(igmp
->interface
);
833 Since this socket is starting as querier,
834 there should not exist a timer for other-querier-present.
836 zassert(!igmp
->t_other_querier_timer
);
837 pim_ifp
= igmp
->interface
->info
;
841 RFC 3376: 8.6. Startup Query Interval
843 The Startup Query Interval is the interval between General Queries
844 sent by a Querier on startup. Default: 1/4 the Query Interval.
846 startup_mode
= igmp
->startup_query_count
> 0;
848 --igmp
->startup_query_count
;
850 /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
851 query_interval
= PIM_IGMP_SQI(pim_ifp
->igmp_default_query_interval
);
854 query_interval
= igmp
->querier_query_interval
;
857 if (PIM_DEBUG_IGMP_TRACE
) {
858 char ifaddr_str
[100];
859 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
860 zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
863 startup_mode
? "startup" : "non-startup",
866 igmp
->t_igmp_query_timer
= NULL
;
867 zassert(!igmp
->t_igmp_query_timer
);
868 THREAD_TIMER_ON(master
, igmp
->t_igmp_query_timer
,
869 pim_igmp_general_query
,
870 igmp
, query_interval
);
873 void pim_igmp_general_query_off(struct igmp_sock
*igmp
)
877 if (PIM_DEBUG_IGMP_TRACE
) {
878 if (igmp
->t_igmp_query_timer
) {
879 char ifaddr_str
[100];
880 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
881 zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
882 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
885 THREAD_OFF(igmp
->t_igmp_query_timer
);
886 zassert(!igmp
->t_igmp_query_timer
);
889 /* Issue IGMP general query */
890 static int pim_igmp_general_query(struct thread
*t
)
892 char query_buf
[PIM_IGMP_BUFSIZE_WRITE
];
893 struct igmp_sock
*igmp
;
894 struct in_addr dst_addr
;
895 struct in_addr group_addr
;
896 struct pim_interface
*pim_ifp
;
900 igmp
= THREAD_ARG(t
);
903 zassert(igmp
->interface
);
904 zassert(igmp
->interface
->info
);
906 pim_ifp
= igmp
->interface
->info
;
909 RFC3376: 4.1.12. IP Destination Addresses for Queries
911 In IGMPv3, General Queries are sent with an IP destination address
912 of 224.0.0.1, the all-systems multicast address. Group-Specific
913 and Group-and-Source-Specific Queries are sent with an IP
914 destination address equal to the multicast address of interest.
917 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
918 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
920 if (PIM_DEBUG_IGMP_TRACE
) {
921 char querier_str
[100];
923 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
924 sizeof(querier_str
));
925 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
926 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
927 querier_str
, dst_str
, igmp
->interface
->name
);
930 pim_igmp_send_membership_query(0 /* igmp_group */,
932 igmp
->interface
->name
,
938 pim_ifp
->igmp_query_max_response_time_dsec
,
939 1 /* s_flag: always set for general queries */,
940 igmp
->querier_robustness_variable
,
941 igmp
->querier_query_interval
);
943 pim_igmp_general_query_on(igmp
);
948 static int pim_igmp_read(struct thread
*t
);
950 static void igmp_read_on(struct igmp_sock
*igmp
)
954 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
955 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
958 igmp
->t_igmp_read
= NULL
;
959 zassert(!igmp
->t_igmp_read
);
960 THREAD_READ_ON(master
, igmp
->t_igmp_read
, pim_igmp_read
, igmp
, igmp
->fd
);
963 static int pim_igmp_read(struct thread
*t
)
965 struct igmp_sock
*igmp
;
967 struct sockaddr_in from
;
968 struct sockaddr_in to
;
969 socklen_t fromlen
= sizeof(from
);
970 socklen_t tolen
= sizeof(to
);
971 uint8_t buf
[PIM_IGMP_BUFSIZE_READ
];
973 ifindex_t ifindex
= -1;
974 int result
= -1; /* defaults to bad */
978 igmp
= THREAD_ARG(t
);
984 zassert(fd
== igmp
->fd
);
986 len
= pim_socket_recvfromto(fd
, buf
, sizeof(buf
),
991 zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
992 fd
, errno
, safe_strerror(errno
));
996 if (PIM_DEBUG_IGMP_PACKETS
) {
1000 if (!inet_ntop(AF_INET
, &from
.sin_addr
, from_str
, sizeof(from_str
)))
1001 sprintf(from_str
, "<from?>");
1002 if (!inet_ntop(AF_INET
, &to
.sin_addr
, to_str
, sizeof(to_str
)))
1003 sprintf(to_str
, "<to?>");
1005 zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
1006 len
, from_str
, to_str
, fd
, ifindex
, igmp
->interface
->ifindex
);
1009 #ifdef PIM_CHECK_RECV_IFINDEX_SANITY
1010 /* ifindex sanity check */
1011 if (ifindex
!= igmp
->interface
->ifindex
) {
1014 struct interface
*ifp
;
1016 if (!inet_ntop(AF_INET
, &from
.sin_addr
, from_str
, sizeof(from_str
)))
1017 sprintf(from_str
, "<from?>");
1018 if (!inet_ntop(AF_INET
, &to
.sin_addr
, to_str
, sizeof(to_str
)))
1019 sprintf(to_str
, "<to?>");
1021 ifp
= if_lookup_by_index(ifindex
);
1023 zassert(ifindex
== ifp
->ifindex
);
1026 #ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
1027 zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
1028 from_str
, to_str
, fd
,
1029 ifindex
, ifp
? ifp
->name
: "<if-notfound>",
1030 igmp
->interface
->ifindex
, igmp
->interface
->name
);
1036 if (pim_igmp_packet(igmp
, (char *)buf
, len
)) {
1040 result
= 0; /* good */
1048 static void sock_close(struct igmp_sock
*igmp
)
1050 pim_igmp_other_querier_timer_off(igmp
);
1051 pim_igmp_general_query_off(igmp
);
1053 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
1054 if (igmp
->t_igmp_read
) {
1055 zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
1056 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
1057 igmp
->interface
->name
);
1060 THREAD_OFF(igmp
->t_igmp_read
);
1061 zassert(!igmp
->t_igmp_read
);
1063 if (close(igmp
->fd
)) {
1064 zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
1065 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
,
1066 errno
, safe_strerror(errno
));
1069 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
1070 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
1071 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
);
1075 void igmp_startup_mode_on(struct igmp_sock
*igmp
)
1077 struct pim_interface
*pim_ifp
;
1079 pim_ifp
= igmp
->interface
->info
;
1082 RFC 3376: 8.7. Startup Query Count
1084 The Startup Query Count is the number of Queries sent out on
1085 startup, separated by the Startup Query Interval. Default: the
1086 Robustness Variable.
1088 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
1091 Since we're (re)starting, reset QQI to default Query Interval
1093 igmp
->querier_query_interval
= pim_ifp
->igmp_default_query_interval
;
1096 static void igmp_group_free(struct igmp_group
*group
)
1098 list_free(group
->group_source_list
);
1100 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
1103 static void igmp_group_delete(struct igmp_group
*group
)
1105 struct listnode
*src_node
;
1106 struct listnode
*src_nextnode
;
1107 struct igmp_source
*src
;
1109 if (PIM_DEBUG_IGMP_TRACE
) {
1110 char group_str
[100];
1111 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1112 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
1114 group
->group_igmp_sock
->fd
,
1115 group
->group_igmp_sock
->interface
->name
);
1118 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
, src
)) {
1119 igmp_source_delete(src
);
1122 if (group
->t_group_query_retransmit_timer
) {
1123 THREAD_OFF(group
->t_group_query_retransmit_timer
);
1126 group_timer_off(group
);
1127 listnode_delete(group
->group_igmp_sock
->igmp_group_list
, group
);
1128 igmp_group_free(group
);
1131 void igmp_group_delete_empty_include(struct igmp_group
*group
)
1133 zassert(!group
->group_filtermode_isexcl
);
1134 zassert(!listcount(group
->group_source_list
));
1136 igmp_group_delete(group
);
1139 void igmp_sock_free(struct igmp_sock
*igmp
)
1141 zassert(!igmp
->t_igmp_read
);
1142 zassert(!igmp
->t_igmp_query_timer
);
1143 zassert(!igmp
->t_other_querier_timer
);
1144 zassert(igmp
->igmp_group_list
);
1145 zassert(!listcount(igmp
->igmp_group_list
));
1147 list_free(igmp
->igmp_group_list
);
1149 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
1152 void igmp_sock_delete(struct igmp_sock
*igmp
)
1154 struct pim_interface
*pim_ifp
;
1155 struct listnode
*grp_node
;
1156 struct listnode
*grp_nextnode
;
1157 struct igmp_group
*grp
;
1159 for (ALL_LIST_ELEMENTS(igmp
->igmp_group_list
, grp_node
, grp_nextnode
, grp
)) {
1160 igmp_group_delete(grp
);
1165 pim_ifp
= igmp
->interface
->info
;
1167 listnode_delete(pim_ifp
->igmp_socket_list
, igmp
);
1169 igmp_sock_free(igmp
);
1172 static struct igmp_sock
*igmp_sock_new(int fd
,
1173 struct in_addr ifaddr
,
1174 struct interface
*ifp
)
1176 struct pim_interface
*pim_ifp
;
1177 struct igmp_sock
*igmp
;
1179 pim_ifp
= ifp
->info
;
1181 if (PIM_DEBUG_IGMP_TRACE
) {
1182 zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
1183 fd
, inet_ntoa(ifaddr
), ifp
->name
);
1186 igmp
= XCALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
1188 zlog_warn("%s %s: XCALLOC() failure",
1189 __FILE__
, __PRETTY_FUNCTION__
);
1193 igmp
->igmp_group_list
= list_new();
1194 if (!igmp
->igmp_group_list
) {
1195 zlog_err("%s %s: failure: igmp_group_list = list_new()",
1196 __FILE__
, __PRETTY_FUNCTION__
);
1199 igmp
->igmp_group_list
->del
= (void (*)(void *)) igmp_group_free
;
1202 igmp
->interface
= ifp
;
1203 igmp
->ifaddr
= ifaddr
;
1204 igmp
->t_igmp_read
= NULL
;
1205 igmp
->t_igmp_query_timer
= NULL
;
1206 igmp
->t_other_querier_timer
= NULL
; /* no other querier present */
1207 igmp
->querier_robustness_variable
= pim_ifp
->igmp_default_robustness_variable
;
1208 igmp
->sock_creation
= pim_time_monotonic_sec();
1211 igmp_startup_mode_on() will reset QQI:
1213 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
1215 igmp_startup_mode_on(igmp
);
1218 pim_igmp_general_query_on(igmp
);
1223 struct igmp_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
1224 struct in_addr ifaddr
,
1225 struct interface
*ifp
)
1227 struct pim_interface
*pim_ifp
;
1228 struct igmp_sock
*igmp
;
1231 pim_ifp
= ifp
->info
;
1233 fd
= igmp_sock_open(ifaddr
, ifp
->ifindex
, pim_ifp
->options
);
1235 zlog_warn("Could not open IGMP socket for %s on %s",
1236 inet_ntoa(ifaddr
), ifp
->name
);
1240 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
);
1242 zlog_err("%s %s: igmp_sock_new() failure",
1243 __FILE__
, __PRETTY_FUNCTION__
);
1248 listnode_add(igmp_sock_list
, igmp
);
1250 #ifdef IGMP_SOCK_DUMP
1251 igmp_sock_dump(igmp_sock_array
);
1258 RFC 3376: 6.5. Switching Router Filter-Modes
1260 When a router's filter-mode for a group is EXCLUDE and the group
1261 timer expires, the router filter-mode for the group transitions to
1264 A router uses source records with running source timers as its state
1265 for the switch to a filter-mode of INCLUDE. If there are any source
1266 records with source timers greater than zero (i.e., requested to be
1267 forwarded), a router switches to filter-mode of INCLUDE using those
1268 source records. Source records whose timers are zero (from the
1269 previous EXCLUDE mode) are deleted.
1271 static int igmp_group_timer(struct thread
*t
)
1273 struct igmp_group
*group
;
1276 group
= THREAD_ARG(t
);
1279 if (PIM_DEBUG_IGMP_TRACE
) {
1280 char group_str
[100];
1281 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1282 zlog_debug("%s: Timer for group %s on interface %s",
1283 __PRETTY_FUNCTION__
,
1284 group_str
, group
->group_igmp_sock
->interface
->name
);
1287 zassert(group
->group_filtermode_isexcl
);
1289 group
->t_group_timer
= NULL
;
1290 group
->group_filtermode_isexcl
= 0;
1292 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1293 igmp_anysource_forward_stop(group
);
1295 igmp_source_delete_expired(group
->group_source_list
);
1297 zassert(!group
->t_group_timer
);
1298 zassert(!group
->group_filtermode_isexcl
);
1301 RFC 3376: 6.2.2. Definition of Group Timers
1303 If there are no more source records for the group, delete group
1306 if (listcount(group
->group_source_list
) < 1) {
1307 igmp_group_delete_empty_include(group
);
1313 static void group_timer_off(struct igmp_group
*group
)
1315 if (!group
->t_group_timer
)
1318 if (PIM_DEBUG_IGMP_TRACE
) {
1319 char group_str
[100];
1320 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1321 zlog_debug("Cancelling TIMER event for group %s on %s",
1322 group_str
, group
->group_igmp_sock
->interface
->name
);
1325 THREAD_OFF(group
->t_group_timer
);
1326 zassert(!group
->t_group_timer
);
1329 void igmp_group_timer_on(struct igmp_group
*group
,
1330 long interval_msec
, const char *ifname
)
1332 group_timer_off(group
);
1334 if (PIM_DEBUG_IGMP_EVENTS
) {
1335 char group_str
[100];
1336 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1337 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1338 interval_msec
/ 1000,
1339 interval_msec
% 1000,
1344 RFC 3376: 6.2.2. Definition of Group Timers
1346 The group timer is only used when a group is in EXCLUDE mode and
1347 it represents the time for the *filter-mode* of the group to
1348 expire and switch to INCLUDE mode.
1350 zassert(group
->group_filtermode_isexcl
);
1352 THREAD_TIMER_MSEC_ON(master
, group
->t_group_timer
,
1354 group
, interval_msec
);
1358 find_group_by_addr (struct igmp_sock
*igmp
,
1359 struct in_addr group_addr
)
1361 struct igmp_group
*group
;
1362 struct listnode
*node
;
1364 for (ALL_LIST_ELEMENTS_RO(igmp
->igmp_group_list
, node
, group
))
1365 if (group_addr
.s_addr
== group
->group_addr
.s_addr
)
1371 struct igmp_group
*igmp_add_group_by_addr(struct igmp_sock
*igmp
,
1372 struct in_addr group_addr
)
1374 struct igmp_group
*group
;
1376 group
= find_group_by_addr(igmp
, group_addr
);
1382 Non-existant group is created as INCLUDE {empty}:
1384 RFC 3376 - 5.1. Action on Change of Interface State
1386 If no interface state existed for that multicast address before
1387 the change (i.e., the change consisted of creating a new
1388 per-interface record), or if no state exists after the change
1389 (i.e., the change consisted of deleting a per-interface record),
1390 then the "non-existent" state is considered to have a filter mode
1391 of INCLUDE and an empty source list.
1394 group
= XCALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1396 zlog_warn("%s %s: XCALLOC() failure",
1397 __FILE__
, __PRETTY_FUNCTION__
);
1398 return 0; /* error, not found, could not create */
1401 group
->group_source_list
= list_new();
1402 if (!group
->group_source_list
) {
1403 zlog_warn("%s %s: list_new() failure",
1404 __FILE__
, __PRETTY_FUNCTION__
);
1405 XFREE(MTYPE_PIM_IGMP_GROUP
, group
); /* discard group */
1406 return 0; /* error, not found, could not initialize */
1408 group
->group_source_list
->del
= (void (*)(void *)) igmp_source_free
;
1410 group
->t_group_timer
= NULL
;
1411 group
->t_group_query_retransmit_timer
= NULL
;
1412 group
->group_specific_query_retransmit_count
= 0;
1413 group
->group_addr
= group_addr
;
1414 group
->group_igmp_sock
= igmp
;
1415 group
->last_igmp_v1_report_dsec
= -1;
1416 group
->last_igmp_v2_report_dsec
= -1;
1417 group
->group_creation
= pim_time_monotonic_sec();
1419 /* initialize new group as INCLUDE {empty} */
1420 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1422 listnode_add(igmp
->igmp_group_list
, group
);
1424 if (PIM_DEBUG_IGMP_TRACE
) {
1425 char group_str
[100];
1426 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1427 zlog_debug("Creating new IGMP group %s on socket %d interface %s",
1428 group_str
, igmp
->fd
, igmp
->interface
->name
);
1432 RFC 3376: 6.2.2. Definition of Group Timers
1434 The group timer is only used when a group is in EXCLUDE mode and
1435 it represents the time for the *filter-mode* of the group to
1436 expire and switch to INCLUDE mode.
1438 zassert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1439 zassert(!group
->t_group_timer
); /* group timer == 0 */
1441 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1442 igmp_anysource_forward_stop(group
);