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,
20 $QuaggaId: $Format:%an, %ai, %h$ $
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 struct igmp_group
*find_group_by_addr(struct igmp_sock
*igmp
,
49 struct in_addr group_addr
);
51 static int igmp_sock_open(struct in_addr ifaddr
, int ifindex
, uint32_t pim_options
)
57 fd
= pim_socket_mcast(IPPROTO_IGMP
, ifaddr
, 1 /* loop=true */);
61 if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options
)) {
62 if (inet_aton(PIM_ALL_ROUTERS
, &group
)) {
63 if (!pim_socket_join(fd
, group
, ifaddr
, ifindex
))
67 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
68 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
69 PIM_ALL_ROUTERS
, errno
, safe_strerror(errno
));
74 IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
75 IGMP routers must receive general queries for querier election.
77 if (inet_aton(PIM_ALL_SYSTEMS
, &group
)) {
78 if (!pim_socket_join(fd
, group
, ifaddr
, ifindex
))
82 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
83 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
84 PIM_ALL_SYSTEMS
, errno
, safe_strerror(errno
));
87 if (inet_aton(PIM_ALL_IGMP_ROUTERS
, &group
)) {
88 if (!pim_socket_join(fd
, group
, ifaddr
, ifindex
)) {
93 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
94 __FILE__
, __PRETTY_FUNCTION__
, fd
, inet_ntoa(ifaddr
),
95 PIM_ALL_IGMP_ROUTERS
, errno
, safe_strerror(errno
));
99 zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
100 fd
, inet_ntoa(ifaddr
));
108 #undef IGMP_SOCK_DUMP
110 #ifdef IGMP_SOCK_DUMP
111 static void igmp_sock_dump(array_t
*igmp_sock_array
)
113 int size
= array_size(igmp_sock_array
);
114 for (int i
= 0; i
< size
; ++i
) {
116 struct igmp_sock
*igmp
= array_get(igmp_sock_array
, i
);
118 zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
119 __FILE__
, __PRETTY_FUNCTION__
,
121 inet_ntoa(igmp
->ifaddr
),
127 struct igmp_sock
*pim_igmp_sock_lookup_ifaddr(struct list
*igmp_sock_list
,
128 struct in_addr ifaddr
)
130 struct listnode
*sock_node
;
131 struct igmp_sock
*igmp
;
133 #ifdef IGMP_SOCK_DUMP
134 igmp_sock_dump(igmp_sock_list
);
137 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
138 if (ifaddr
.s_addr
== igmp
->ifaddr
.s_addr
)
144 struct igmp_sock
*igmp_sock_lookup_by_fd(struct list
*igmp_sock_list
,
147 struct listnode
*sock_node
;
148 struct igmp_sock
*igmp
;
150 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list
, sock_node
, igmp
))
157 static int pim_igmp_other_querier_expire(struct thread
*t
)
159 struct igmp_sock
*igmp
;
162 igmp
= THREAD_ARG(t
);
165 zassert(igmp
->t_other_querier_timer
);
166 zassert(!igmp
->t_igmp_query_timer
);
168 if (PIM_DEBUG_IGMP_TRACE
) {
169 char ifaddr_str
[100];
170 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
171 zlog_debug("%s: Querier %s resuming",
176 igmp
->t_other_querier_timer
= 0;
179 We are the current querier, then
180 re-start sending general queries.
182 pim_igmp_general_query_on(igmp
);
187 void pim_igmp_other_querier_timer_on(struct igmp_sock
*igmp
)
189 long other_querier_present_interval_msec
;
190 struct pim_interface
*pim_ifp
;
193 zassert(igmp
->interface
);
194 zassert(igmp
->interface
->info
);
196 pim_ifp
= igmp
->interface
->info
;
198 if (igmp
->t_other_querier_timer
) {
200 There is other querier present already,
201 then reset the other-querier-present timer.
204 if (PIM_DEBUG_IGMP_TRACE
) {
205 char ifaddr_str
[100];
206 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
207 zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
211 THREAD_OFF(igmp
->t_other_querier_timer
);
212 zassert(!igmp
->t_other_querier_timer
);
216 We are the current querier, then stop sending general queries:
217 igmp->t_igmp_query_timer = 0;
219 pim_igmp_general_query_off(igmp
);
223 Since this socket is starting the other-querier-present timer,
224 there should not be periodic query timer for this socket.
226 zassert(!igmp
->t_igmp_query_timer
);
229 RFC 3376: 8.5. Other Querier Present Interval
231 The Other Querier Present Interval is the length of time that must
232 pass before a multicast router decides that there is no longer
233 another multicast router which should be the querier. This value
234 MUST be ((the Robustness Variable) times (the Query Interval)) plus
235 (one half of one Query Response Interval).
237 other_querier_present_interval_msec = \
238 igmp->querier_robustness_variable * \
239 1000 * igmp->querier_query_interval + \
240 100 * (pim_ifp->query_max_response_time_dsec >> 1);
242 other_querier_present_interval_msec
=
243 PIM_IGMP_OQPI_MSEC(igmp
->querier_robustness_variable
,
244 igmp
->querier_query_interval
,
245 pim_ifp
->igmp_query_max_response_time_dsec
);
247 if (PIM_DEBUG_IGMP_TRACE
) {
248 char ifaddr_str
[100];
249 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
250 zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
252 other_querier_present_interval_msec
/ 1000,
253 other_querier_present_interval_msec
% 1000);
256 THREAD_TIMER_MSEC_ON(master
, igmp
->t_other_querier_timer
,
257 pim_igmp_other_querier_expire
,
258 igmp
, other_querier_present_interval_msec
);
261 void pim_igmp_other_querier_timer_off(struct igmp_sock
*igmp
)
265 if (PIM_DEBUG_IGMP_TRACE
) {
266 if (igmp
->t_other_querier_timer
) {
267 char ifaddr_str
[100];
268 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
269 zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
270 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
273 THREAD_OFF(igmp
->t_other_querier_timer
);
274 zassert(!igmp
->t_other_querier_timer
);
277 static int recv_igmp_query(struct igmp_sock
*igmp
, int query_version
,
279 struct in_addr from
, const char *from_str
,
280 char *igmp_msg
, int igmp_msg_len
)
282 struct interface
*ifp
;
283 struct pim_interface
*pim_ifp
;
284 uint8_t resv_s_qrv
= 0;
287 struct in_addr group_addr
;
288 uint16_t recv_checksum
;
292 //group_addr = *(struct in_addr *)(igmp_msg + 4);
293 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
295 ifp
= igmp
->interface
;
298 recv_checksum
= *(uint16_t *) (igmp_msg
+ IGMP_V3_CHECKSUM_OFFSET
);
300 /* for computing checksum */
301 *(uint16_t *) (igmp_msg
+ IGMP_V3_CHECKSUM_OFFSET
) = 0;
303 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
304 if (checksum
!= recv_checksum
) {
305 zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
306 query_version
, from_str
, ifp
->name
, recv_checksum
, checksum
);
310 if (PIM_DEBUG_IGMP_PACKETS
) {
312 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
313 zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s",
314 query_version
, from_str
, ifp
->name
,
315 igmp_msg_len
, checksum
, group_str
);
319 RFC 3376: 6.6.2. Querier Election
321 When a router receives a query with a lower IP address, it sets
322 the Other-Querier-Present timer to Other Querier Present Interval
323 and ceases to send queries on the network if it was the previously
326 if (ntohl(from
.s_addr
) < ntohl(igmp
->ifaddr
.s_addr
)) {
328 if (PIM_DEBUG_IGMP_TRACE
) {
329 char ifaddr_str
[100];
330 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
331 zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
333 ifaddr_str
, ntohl(igmp
->ifaddr
.s_addr
),
334 from_str
, ntohl(from
.s_addr
));
337 pim_igmp_other_querier_timer_on(igmp
);
340 if (query_version
== 3) {
342 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
344 Routers adopt the QRV value from the most recently received Query
345 as their own [Robustness Variable] value, unless that most
346 recently received QRV was zero, in which case the receivers use
347 the default [Robustness Variable] value specified in section 8.1
348 or a statically configured value.
350 resv_s_qrv
= igmp_msg
[8];
351 qrv
= 7 & resv_s_qrv
;
352 igmp
->querier_robustness_variable
= qrv
? qrv
: pim_ifp
->igmp_default_robustness_variable
;
356 RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
358 Multicast routers that are not the current querier adopt the QQI
359 value from the most recently received Query as their own [Query
360 Interval] value, unless that most recently received QQI was zero,
361 in which case the receiving routers use the default.
363 if (igmp
->t_other_querier_timer
&& query_version
== 3) {
364 /* other querier present */
368 qqi
= igmp_msg_decode8to16(qqic
);
369 igmp
->querier_query_interval
= qqi
? qqi
: pim_ifp
->igmp_default_query_interval
;
371 if (PIM_DEBUG_IGMP_TRACE
) {
372 char ifaddr_str
[100];
373 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
374 zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
376 qqi
? "recv-non-default" : "default",
377 igmp
->querier_query_interval
,
384 RFC 3376: 6.6.1. Timer Updates
386 When a router sends or receives a query with a clear Suppress
387 Router-Side Processing flag, it must update its timers to reflect
388 the correct timeout values for the group or sources being queried.
390 General queries don't trigger timer update.
392 if (query_version
== 3) {
393 s_flag
= (1 << 3) & resv_s_qrv
;
396 /* Neither V1 nor V2 have this field. Pimd should really go into
397 * a compatibility mode here and run as V2 (or V1) but it doesn't
398 * so for now, lets just set the flag to suppress these timer updates.
404 /* s_flag is clear */
406 if (PIM_INADDR_IS_ANY(group_addr
)) {
407 /* this is a general query */
409 /* log that general query should have the s_flag set */
410 zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
411 query_version
, from_str
, ifp
->name
);
414 struct igmp_group
*group
;
416 /* this is a non-general query: perform timer updates */
418 group
= find_group_by_addr(igmp
, group_addr
);
420 int recv_num_sources
= ntohs(*(uint16_t *)(igmp_msg
+ IGMP_V3_NUMSOURCES_OFFSET
));
423 RFC 3376: 6.6.1. Timer Updates
424 Query Q(G,A): Source Timer for sources in A are lowered to LMQT
425 Query Q(G): Group Timer is lowered to LMQT
427 if (recv_num_sources
< 1) {
428 /* Query Q(G): Group Timer is lowered to LMQT */
430 igmp_group_timer_lower_to_lmqt(group
);
433 /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
435 /* Scan sources in query and lower their timers to LMQT */
436 struct in_addr
*sources
= (struct in_addr
*)(igmp_msg
+ IGMP_V3_SOURCES_OFFSET
);
437 for (i
= 0; i
< recv_num_sources
; ++i
) {
438 //struct in_addr src_addr = sources[i];
439 //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr);
440 struct in_addr src_addr
;
441 struct igmp_source
*src
;
442 memcpy(&src_addr
, sources
+ i
, sizeof(struct in_addr
));
443 src
= igmp_find_source_by_addr(group
, src_addr
);
445 igmp_source_timer_lower_to_lmqt(src
);
453 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
454 zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update",
455 query_version
, from_str
, ifp
->name
, group_str
);
458 } /* s_flag is clear: timer updates */
463 static int igmp_v3_report(struct igmp_sock
*igmp
,
464 struct in_addr from
, const char *from_str
,
465 char *igmp_msg
, int igmp_msg_len
)
467 uint16_t recv_checksum
;
470 uint8_t *group_record
;
471 uint8_t *report_pastend
= (uint8_t *) igmp_msg
+ igmp_msg_len
;
472 struct interface
*ifp
= igmp
->interface
;
475 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
476 zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
477 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V3_MSG_MIN_SIZE
);
481 recv_checksum
= *(uint16_t *) (igmp_msg
+ IGMP_V3_CHECKSUM_OFFSET
);
483 /* for computing checksum */
484 *(uint16_t *) (igmp_msg
+ IGMP_V3_CHECKSUM_OFFSET
) = 0;
486 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
487 if (checksum
!= recv_checksum
) {
488 zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
489 from_str
, ifp
->name
, recv_checksum
, checksum
);
493 num_groups
= ntohs(*(uint16_t *) (igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
494 if (num_groups
< 1) {
495 zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
496 from_str
, ifp
->name
);
500 if (PIM_DEBUG_IGMP_PACKETS
) {
501 zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
502 from_str
, ifp
->name
, igmp_msg_len
, checksum
, num_groups
);
505 group_record
= (uint8_t *) igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
508 for (i
= 0; i
< num_groups
; ++i
) {
509 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 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
560 igmpv3_report_isin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
562 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
563 igmpv3_report_isex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
565 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
566 igmpv3_report_toin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
568 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
569 igmpv3_report_toex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
571 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
572 igmpv3_report_allow(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
574 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
575 igmpv3_report_block(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
578 zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
579 from_str
, ifp
->name
, rec_type
);
582 group_record
+= 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
584 } /* for (group records) */
589 static void on_trace(const char *label
,
590 struct interface
*ifp
, struct in_addr from
)
592 if (PIM_DEBUG_IGMP_TRACE
) {
594 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
595 zlog_debug("%s: from %s on %s",
596 label
, from_str
, ifp
->name
);
600 static int igmp_v2_report(struct igmp_sock
*igmp
,
601 struct in_addr from
, const char *from_str
,
602 char *igmp_msg
, int igmp_msg_len
)
604 struct interface
*ifp
= igmp
->interface
;
605 struct igmp_group
*group
;
606 struct in_addr group_addr
;
608 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
);
610 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
611 zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d",
612 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
616 if (PIM_DEBUG_IGMP_TRACE
) {
617 zlog_warn("%s %s: FIXME WRITEME",
618 __FILE__
, __PRETTY_FUNCTION__
);
621 //group_addr = *(struct in_addr *)(igmp_msg + 4);
622 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
624 /* non-existant group is created as INCLUDE {empty} */
625 group
= igmp_add_group_by_addr(igmp
, group_addr
);
630 group
->last_igmp_v2_report_dsec
= pim_time_monotonic_dsec();
635 static int igmp_v2_leave(struct igmp_sock
*igmp
,
636 struct in_addr from
, const char *from_str
,
637 char *igmp_msg
, int igmp_msg_len
)
639 struct interface
*ifp
= igmp
->interface
;
641 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
);
643 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
644 zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d",
645 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
649 if (PIM_DEBUG_IGMP_TRACE
) {
650 zlog_warn("%s %s: FIXME WRITEME",
651 __FILE__
, __PRETTY_FUNCTION__
);
657 static int igmp_v1_report(struct igmp_sock
*igmp
,
658 struct in_addr from
, const char *from_str
,
659 char *igmp_msg
, int igmp_msg_len
)
661 struct interface
*ifp
= igmp
->interface
;
662 struct igmp_group
*group
;
663 struct in_addr group_addr
;
665 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
);
667 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
668 zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
669 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V12_MSG_SIZE
);
673 if (PIM_DEBUG_IGMP_TRACE
) {
674 zlog_warn("%s %s: FIXME WRITEME",
675 __FILE__
, __PRETTY_FUNCTION__
);
678 //group_addr = *(struct in_addr *)(igmp_msg + 4);
679 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
681 /* non-existant group is created as INCLUDE {empty} */
682 group
= igmp_add_group_by_addr(igmp
, group_addr
);
687 group
->last_igmp_v1_report_dsec
= pim_time_monotonic_dsec();
692 int pim_igmp_packet(struct igmp_sock
*igmp
, char *buf
, size_t len
)
695 size_t ip_hlen
; /* ip header length in bytes */
702 if (len
< sizeof(*ip_hdr
)) {
703 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
704 len
, sizeof(*ip_hdr
));
708 ip_hdr
= (struct ip
*) buf
;
710 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, from_str
, sizeof(from_str
));
711 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, to_str
, sizeof(to_str
));
713 ip_hlen
= ip_hdr
->ip_hl
<< 2; /* ip_hl gives length in 4-byte words */
715 if (PIM_DEBUG_IGMP_PACKETS
) {
716 zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
717 from_str
, to_str
, igmp
->interface
->name
, len
, ip_hlen
, ip_hdr
->ip_p
);
720 if (ip_hdr
->ip_p
!= PIM_IP_PROTO_IGMP
) {
721 zlog_warn("IP packet protocol=%d is not IGMP=%d",
722 ip_hdr
->ip_p
, PIM_IP_PROTO_IGMP
);
726 if (ip_hlen
< PIM_IP_HEADER_MIN_LEN
) {
727 zlog_warn("IP packet header size=%zu shorter than minimum=%d",
728 ip_hlen
, PIM_IP_HEADER_MIN_LEN
);
731 if (ip_hlen
> PIM_IP_HEADER_MAX_LEN
) {
732 zlog_warn("IP packet header size=%zu greater than maximum=%d",
733 ip_hlen
, PIM_IP_HEADER_MAX_LEN
);
737 igmp_msg
= buf
+ ip_hlen
;
738 msg_type
= *igmp_msg
;
739 igmp_msg_len
= len
- ip_hlen
;
741 if (PIM_DEBUG_IGMP_PACKETS
) {
742 zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
743 from_str
, to_str
, igmp
->interface
->name
, ip_hdr
->ip_ttl
, msg_type
,
747 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
748 zlog_warn("IGMP message size=%d shorter than minimum=%d",
749 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
754 case PIM_IGMP_MEMBERSHIP_QUERY
:
756 int max_resp_code
= igmp_msg
[1];
760 RFC 3376: 7.1. Query Version Distinctions
761 IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
762 IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
763 IGMPv3 Query: length >= 12 octets
766 if (igmp_msg_len
== 8) {
767 query_version
= max_resp_code
? 2 : 1;
769 else if (igmp_msg_len
>= 12) {
773 zlog_warn("Unknown IGMP query version");
777 return recv_igmp_query(igmp
, query_version
, max_resp_code
,
778 ip_hdr
->ip_src
, from_str
,
779 igmp_msg
, igmp_msg_len
);
782 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
783 return igmp_v3_report(igmp
, ip_hdr
->ip_src
, from_str
,
784 igmp_msg
, igmp_msg_len
);
786 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
787 return igmp_v2_report(igmp
, ip_hdr
->ip_src
, from_str
,
788 igmp_msg
, igmp_msg_len
);
790 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
791 return igmp_v1_report(igmp
, ip_hdr
->ip_src
, from_str
,
792 igmp_msg
, igmp_msg_len
);
794 case PIM_IGMP_V2_LEAVE_GROUP
:
795 return igmp_v2_leave(igmp
, ip_hdr
->ip_src
, from_str
,
796 igmp_msg
, igmp_msg_len
);
799 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
804 static int pim_igmp_general_query(struct thread
*t
);
806 void pim_igmp_general_query_on(struct igmp_sock
*igmp
)
808 struct pim_interface
*pim_ifp
;
813 zassert(igmp
->interface
);
816 Since this socket is starting as querier,
817 there should not exist a timer for other-querier-present.
819 zassert(!igmp
->t_other_querier_timer
);
820 pim_ifp
= igmp
->interface
->info
;
824 RFC 3376: 8.6. Startup Query Interval
826 The Startup Query Interval is the interval between General Queries
827 sent by a Querier on startup. Default: 1/4 the Query Interval.
829 startup_mode
= igmp
->startup_query_count
> 0;
831 --igmp
->startup_query_count
;
833 /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
834 query_interval
= PIM_IGMP_SQI(pim_ifp
->igmp_default_query_interval
);
837 query_interval
= igmp
->querier_query_interval
;
840 if (PIM_DEBUG_IGMP_TRACE
) {
841 char ifaddr_str
[100];
842 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
843 zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
846 startup_mode
? "startup" : "non-startup",
849 igmp
->t_igmp_query_timer
= 0;
850 zassert(!igmp
->t_igmp_query_timer
);
851 THREAD_TIMER_ON(master
, igmp
->t_igmp_query_timer
,
852 pim_igmp_general_query
,
853 igmp
, query_interval
);
856 void pim_igmp_general_query_off(struct igmp_sock
*igmp
)
860 if (PIM_DEBUG_IGMP_TRACE
) {
861 if (igmp
->t_igmp_query_timer
) {
862 char ifaddr_str
[100];
863 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
864 zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
865 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
868 THREAD_OFF(igmp
->t_igmp_query_timer
);
869 zassert(!igmp
->t_igmp_query_timer
);
872 /* Issue IGMP general query */
873 static int pim_igmp_general_query(struct thread
*t
)
875 char query_buf
[PIM_IGMP_BUFSIZE_WRITE
];
876 struct igmp_sock
*igmp
;
877 struct in_addr dst_addr
;
878 struct in_addr group_addr
;
879 struct pim_interface
*pim_ifp
;
883 igmp
= THREAD_ARG(t
);
886 zassert(igmp
->interface
);
887 zassert(igmp
->interface
->info
);
889 pim_ifp
= igmp
->interface
->info
;
892 RFC3376: 4.1.12. IP Destination Addresses for Queries
894 In IGMPv3, General Queries are sent with an IP destination address
895 of 224.0.0.1, the all-systems multicast address. Group-Specific
896 and Group-and-Source-Specific Queries are sent with an IP
897 destination address equal to the multicast address of interest.
900 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
901 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
903 if (PIM_DEBUG_IGMP_TRACE
) {
904 char querier_str
[100];
906 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
907 sizeof(querier_str
));
908 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
909 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
910 querier_str
, dst_str
, igmp
->interface
->name
);
913 pim_igmp_send_membership_query(0 /* igmp_group */,
915 igmp
->interface
->name
,
921 pim_ifp
->igmp_query_max_response_time_dsec
,
922 1 /* s_flag: always set for general queries */,
923 igmp
->querier_robustness_variable
,
924 igmp
->querier_query_interval
);
926 pim_igmp_general_query_on(igmp
);
931 static int pim_igmp_read(struct thread
*t
);
933 static void igmp_read_on(struct igmp_sock
*igmp
)
937 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
938 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
941 igmp
->t_igmp_read
= 0;
942 zassert(!igmp
->t_igmp_read
);
943 THREAD_READ_ON(master
, igmp
->t_igmp_read
, pim_igmp_read
, igmp
, igmp
->fd
);
946 static int pim_igmp_read(struct thread
*t
)
948 struct igmp_sock
*igmp
;
950 struct sockaddr_in from
;
951 struct sockaddr_in to
;
952 socklen_t fromlen
= sizeof(from
);
953 socklen_t tolen
= sizeof(to
);
954 uint8_t buf
[PIM_IGMP_BUFSIZE_READ
];
957 int result
= -1; /* defaults to bad */
961 igmp
= THREAD_ARG(t
);
967 zassert(fd
== igmp
->fd
);
969 len
= pim_socket_recvfromto(fd
, buf
, sizeof(buf
),
974 zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
975 fd
, errno
, safe_strerror(errno
));
979 if (PIM_DEBUG_IGMP_PACKETS
) {
983 if (!inet_ntop(AF_INET
, &from
.sin_addr
, from_str
, sizeof(from_str
)))
984 sprintf(from_str
, "<from?>");
985 if (!inet_ntop(AF_INET
, &to
.sin_addr
, to_str
, sizeof(to_str
)))
986 sprintf(to_str
, "<to?>");
988 zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
989 len
, from_str
, to_str
, fd
, ifindex
, igmp
->interface
->ifindex
);
992 #ifdef PIM_CHECK_RECV_IFINDEX_SANITY
993 /* ifindex sanity check */
994 if (ifindex
!= (int) igmp
->interface
->ifindex
) {
997 struct interface
*ifp
;
999 if (!inet_ntop(AF_INET
, &from
.sin_addr
, from_str
, sizeof(from_str
)))
1000 sprintf(from_str
, "<from?>");
1001 if (!inet_ntop(AF_INET
, &to
.sin_addr
, to_str
, sizeof(to_str
)))
1002 sprintf(to_str
, "<to?>");
1004 ifp
= if_lookup_by_index(ifindex
);
1006 zassert(ifindex
== (int) ifp
->ifindex
);
1009 #ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
1010 zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
1011 from_str
, to_str
, fd
,
1012 ifindex
, ifp
? ifp
->name
: "<if-notfound>",
1013 igmp
->interface
->ifindex
, igmp
->interface
->name
);
1019 if (pim_igmp_packet(igmp
, (char *)buf
, len
)) {
1023 result
= 0; /* good */
1031 static void sock_close(struct igmp_sock
*igmp
)
1033 pim_igmp_other_querier_timer_off(igmp
);
1034 pim_igmp_general_query_off(igmp
);
1036 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
1037 if (igmp
->t_igmp_read
) {
1038 zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
1039 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
1040 igmp
->interface
->name
);
1043 THREAD_OFF(igmp
->t_igmp_read
);
1044 zassert(!igmp
->t_igmp_read
);
1046 if (close(igmp
->fd
)) {
1047 zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
1048 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
,
1049 errno
, safe_strerror(errno
));
1052 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
1053 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
1054 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
);
1058 void igmp_startup_mode_on(struct igmp_sock
*igmp
)
1060 struct pim_interface
*pim_ifp
;
1062 pim_ifp
= igmp
->interface
->info
;
1065 RFC 3376: 8.7. Startup Query Count
1067 The Startup Query Count is the number of Queries sent out on
1068 startup, separated by the Startup Query Interval. Default: the
1069 Robustness Variable.
1071 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
1074 Since we're (re)starting, reset QQI to default Query Interval
1076 igmp
->querier_query_interval
= pim_ifp
->igmp_default_query_interval
;
1079 static void igmp_group_free(struct igmp_group
*group
)
1081 zassert(!group
->t_group_query_retransmit_timer
);
1082 zassert(!group
->t_group_timer
);
1083 zassert(group
->group_source_list
);
1084 zassert(!listcount(group
->group_source_list
));
1086 list_free(group
->group_source_list
);
1088 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
1091 static void igmp_group_delete(struct igmp_group
*group
)
1093 struct listnode
*src_node
;
1094 struct listnode
*src_nextnode
;
1095 struct igmp_source
*src
;
1097 if (PIM_DEBUG_IGMP_TRACE
) {
1098 char group_str
[100];
1099 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1100 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
1102 group
->group_igmp_sock
->fd
,
1103 group
->group_igmp_sock
->interface
->name
);
1106 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
, src
)) {
1107 igmp_source_delete(src
);
1110 if (group
->t_group_query_retransmit_timer
) {
1111 THREAD_OFF(group
->t_group_query_retransmit_timer
);
1112 zassert(!group
->t_group_query_retransmit_timer
);
1115 group_timer_off(group
);
1116 listnode_delete(group
->group_igmp_sock
->igmp_group_list
, group
);
1117 igmp_group_free(group
);
1120 void igmp_group_delete_empty_include(struct igmp_group
*group
)
1122 zassert(!group
->group_filtermode_isexcl
);
1123 zassert(!listcount(group
->group_source_list
));
1125 igmp_group_delete(group
);
1128 void igmp_sock_free(struct igmp_sock
*igmp
)
1130 zassert(!igmp
->t_igmp_read
);
1131 zassert(!igmp
->t_igmp_query_timer
);
1132 zassert(!igmp
->t_other_querier_timer
);
1133 zassert(igmp
->igmp_group_list
);
1134 zassert(!listcount(igmp
->igmp_group_list
));
1136 list_free(igmp
->igmp_group_list
);
1138 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
1141 void igmp_sock_delete(struct igmp_sock
*igmp
)
1143 struct pim_interface
*pim_ifp
;
1144 struct listnode
*grp_node
;
1145 struct listnode
*grp_nextnode
;
1146 struct igmp_group
*grp
;
1148 for (ALL_LIST_ELEMENTS(igmp
->igmp_group_list
, grp_node
, grp_nextnode
, grp
)) {
1149 igmp_group_delete(grp
);
1154 pim_ifp
= igmp
->interface
->info
;
1156 listnode_delete(pim_ifp
->igmp_socket_list
, igmp
);
1158 igmp_sock_free(igmp
);
1161 static struct igmp_sock
*igmp_sock_new(int fd
,
1162 struct in_addr ifaddr
,
1163 struct interface
*ifp
)
1165 struct pim_interface
*pim_ifp
;
1166 struct igmp_sock
*igmp
;
1168 pim_ifp
= ifp
->info
;
1170 if (PIM_DEBUG_IGMP_TRACE
) {
1171 zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
1172 fd
, inet_ntoa(ifaddr
), ifp
->name
);
1175 igmp
= XMALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
1177 zlog_warn("%s %s: XMALLOC() failure",
1178 __FILE__
, __PRETTY_FUNCTION__
);
1182 igmp
->igmp_group_list
= list_new();
1183 if (!igmp
->igmp_group_list
) {
1184 zlog_err("%s %s: failure: igmp_group_list = list_new()",
1185 __FILE__
, __PRETTY_FUNCTION__
);
1188 igmp
->igmp_group_list
->del
= (void (*)(void *)) igmp_group_free
;
1191 igmp
->interface
= ifp
;
1192 igmp
->ifaddr
= ifaddr
;
1193 igmp
->t_igmp_read
= 0;
1194 igmp
->t_igmp_query_timer
= 0;
1195 igmp
->t_other_querier_timer
= 0; /* no other querier present */
1196 igmp
->querier_robustness_variable
= pim_ifp
->igmp_default_robustness_variable
;
1197 igmp
->sock_creation
= pim_time_monotonic_sec();
1200 igmp_startup_mode_on() will reset QQI:
1202 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
1204 igmp_startup_mode_on(igmp
);
1207 pim_igmp_general_query_on(igmp
);
1212 struct igmp_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
1213 struct in_addr ifaddr
,
1214 struct interface
*ifp
)
1216 struct pim_interface
*pim_ifp
;
1217 struct igmp_sock
*igmp
;
1220 pim_ifp
= ifp
->info
;
1222 fd
= igmp_sock_open(ifaddr
, ifp
->ifindex
, pim_ifp
->options
);
1224 zlog_warn("Could not open IGMP socket for %s on %s",
1225 inet_ntoa(ifaddr
), ifp
->name
);
1229 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
);
1231 zlog_err("%s %s: igmp_sock_new() failure",
1232 __FILE__
, __PRETTY_FUNCTION__
);
1237 listnode_add(igmp_sock_list
, igmp
);
1239 #ifdef IGMP_SOCK_DUMP
1240 igmp_sock_dump(igmp_sock_array
);
1247 RFC 3376: 6.5. Switching Router Filter-Modes
1249 When a router's filter-mode for a group is EXCLUDE and the group
1250 timer expires, the router filter-mode for the group transitions to
1253 A router uses source records with running source timers as its state
1254 for the switch to a filter-mode of INCLUDE. If there are any source
1255 records with source timers greater than zero (i.e., requested to be
1256 forwarded), a router switches to filter-mode of INCLUDE using those
1257 source records. Source records whose timers are zero (from the
1258 previous EXCLUDE mode) are deleted.
1260 static int igmp_group_timer(struct thread
*t
)
1262 struct igmp_group
*group
;
1265 group
= THREAD_ARG(t
);
1268 if (PIM_DEBUG_IGMP_TRACE
) {
1269 char group_str
[100];
1270 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1271 zlog_debug("%s: Timer for group %s on interface %s",
1272 __PRETTY_FUNCTION__
,
1273 group_str
, group
->group_igmp_sock
->interface
->name
);
1276 zassert(group
->group_filtermode_isexcl
);
1278 group
->t_group_timer
= 0;
1279 group
->group_filtermode_isexcl
= 0;
1281 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1282 igmp_anysource_forward_stop(group
);
1284 igmp_source_delete_expired(group
->group_source_list
);
1286 zassert(!group
->t_group_timer
);
1287 zassert(!group
->group_filtermode_isexcl
);
1290 RFC 3376: 6.2.2. Definition of Group Timers
1292 If there are no more source records for the group, delete group
1295 if (listcount(group
->group_source_list
) < 1) {
1296 igmp_group_delete_empty_include(group
);
1302 static void group_timer_off(struct igmp_group
*group
)
1304 if (!group
->t_group_timer
)
1307 if (PIM_DEBUG_IGMP_TRACE
) {
1308 char group_str
[100];
1309 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1310 zlog_debug("Cancelling TIMER event for group %s on %s",
1311 group_str
, group
->group_igmp_sock
->interface
->name
);
1314 THREAD_OFF(group
->t_group_timer
);
1315 zassert(!group
->t_group_timer
);
1318 void igmp_group_timer_on(struct igmp_group
*group
,
1319 long interval_msec
, const char *ifname
)
1321 group_timer_off(group
);
1323 if (PIM_DEBUG_IGMP_EVENTS
) {
1324 char group_str
[100];
1325 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1326 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1327 interval_msec
/ 1000,
1328 interval_msec
% 1000,
1333 RFC 3376: 6.2.2. Definition of Group Timers
1335 The group timer is only used when a group is in EXCLUDE mode and
1336 it represents the time for the *filter-mode* of the group to
1337 expire and switch to INCLUDE mode.
1339 zassert(group
->group_filtermode_isexcl
);
1341 THREAD_TIMER_MSEC_ON(master
, group
->t_group_timer
,
1343 group
, interval_msec
);
1346 static struct igmp_group
*find_group_by_addr(struct igmp_sock
*igmp
,
1347 struct in_addr group_addr
)
1349 struct igmp_group
*group
;
1350 struct listnode
*node
;
1352 for (ALL_LIST_ELEMENTS_RO(igmp
->igmp_group_list
, node
, group
))
1353 if (group_addr
.s_addr
== group
->group_addr
.s_addr
)
1359 struct igmp_group
*igmp_add_group_by_addr(struct igmp_sock
*igmp
,
1360 struct in_addr group_addr
)
1362 struct igmp_group
*group
;
1364 group
= find_group_by_addr(igmp
, group_addr
);
1370 Non-existant group is created as INCLUDE {empty}:
1372 RFC 3376 - 5.1. Action on Change of Interface State
1374 If no interface state existed for that multicast address before
1375 the change (i.e., the change consisted of creating a new
1376 per-interface record), or if no state exists after the change
1377 (i.e., the change consisted of deleting a per-interface record),
1378 then the "non-existent" state is considered to have a filter mode
1379 of INCLUDE and an empty source list.
1382 group
= XMALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1384 zlog_warn("%s %s: XMALLOC() failure",
1385 __FILE__
, __PRETTY_FUNCTION__
);
1386 return 0; /* error, not found, could not create */
1389 group
->group_source_list
= list_new();
1390 if (!group
->group_source_list
) {
1391 zlog_warn("%s %s: list_new() failure",
1392 __FILE__
, __PRETTY_FUNCTION__
);
1393 XFREE(MTYPE_PIM_IGMP_GROUP
, group
); /* discard group */
1394 return 0; /* error, not found, could not initialize */
1396 group
->group_source_list
->del
= (void (*)(void *)) igmp_source_free
;
1398 group
->t_group_timer
= NULL
;
1399 group
->t_group_query_retransmit_timer
= NULL
;
1400 group
->group_specific_query_retransmit_count
= 0;
1401 group
->group_addr
= group_addr
;
1402 group
->group_igmp_sock
= igmp
;
1403 group
->last_igmp_v1_report_dsec
= -1;
1404 group
->last_igmp_v2_report_dsec
= -1;
1405 group
->group_creation
= pim_time_monotonic_sec();
1407 /* initialize new group as INCLUDE {empty} */
1408 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1410 listnode_add(igmp
->igmp_group_list
, group
);
1412 if (PIM_DEBUG_IGMP_TRACE
) {
1413 char group_str
[100];
1414 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1415 zlog_debug("Creating new IGMP group %s on socket %d interface %s",
1416 group_str
, igmp
->fd
, igmp
->interface
->name
);
1420 RFC 3376: 6.2.2. Definition of Group Timers
1422 The group timer is only used when a group is in EXCLUDE mode and
1423 it represents the time for the *filter-mode* of the group to
1424 expire and switch to INCLUDE mode.
1426 zassert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1427 zassert(!group
->t_group_timer
); /* group timer == 0 */
1429 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1430 igmp_anysource_forward_stop(group
);