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
, ifindex
, 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
);
721 * When pim starts up we are joining the 224.0.0.13 and 224.0.0.22 multicast
722 * groups. This is causing the kernel to create a igmp packet that pim
723 * turns around and receives. Therefor if we are the originator
724 * of the igmp packet then we can probably just ignore it.
726 if (ip_hdr
->ip_src
.s_addr
== igmp
->ifaddr
.s_addr
)
728 if (PIM_DEBUG_IGMP_PACKETS
)
729 zlog_debug ("Received IGMP packet from myself, ignoring");
733 if (ip_hdr
->ip_p
!= PIM_IP_PROTO_IGMP
) {
734 zlog_warn("IP packet protocol=%d is not IGMP=%d",
735 ip_hdr
->ip_p
, PIM_IP_PROTO_IGMP
);
739 if (ip_hlen
< PIM_IP_HEADER_MIN_LEN
) {
740 zlog_warn("IP packet header size=%zu shorter than minimum=%d",
741 ip_hlen
, PIM_IP_HEADER_MIN_LEN
);
744 if (ip_hlen
> PIM_IP_HEADER_MAX_LEN
) {
745 zlog_warn("IP packet header size=%zu greater than maximum=%d",
746 ip_hlen
, PIM_IP_HEADER_MAX_LEN
);
750 igmp_msg
= buf
+ ip_hlen
;
751 msg_type
= *igmp_msg
;
752 igmp_msg_len
= len
- ip_hlen
;
754 if (PIM_DEBUG_IGMP_PACKETS
) {
755 zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
756 from_str
, to_str
, igmp
->interface
->name
, ip_hdr
->ip_ttl
, msg_type
,
760 if (igmp_msg_len
< PIM_IGMP_MIN_LEN
) {
761 zlog_warn("IGMP message size=%d shorter than minimum=%d",
762 igmp_msg_len
, PIM_IGMP_MIN_LEN
);
767 case PIM_IGMP_MEMBERSHIP_QUERY
:
769 int max_resp_code
= igmp_msg
[1];
773 RFC 3376: 7.1. Query Version Distinctions
774 IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
775 IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
776 IGMPv3 Query: length >= 12 octets
779 if (igmp_msg_len
== 8) {
780 query_version
= max_resp_code
? 2 : 1;
782 else if (igmp_msg_len
>= 12) {
786 zlog_warn("Unknown IGMP query version");
790 return recv_igmp_query(igmp
, query_version
, max_resp_code
,
791 ip_hdr
->ip_src
, from_str
,
792 igmp_msg
, igmp_msg_len
);
795 case PIM_IGMP_V3_MEMBERSHIP_REPORT
:
796 return igmp_v3_report(igmp
, ip_hdr
->ip_src
, from_str
,
797 igmp_msg
, igmp_msg_len
);
799 case PIM_IGMP_V2_MEMBERSHIP_REPORT
:
800 return igmp_v2_report(igmp
, ip_hdr
->ip_src
, from_str
,
801 igmp_msg
, igmp_msg_len
);
803 case PIM_IGMP_V1_MEMBERSHIP_REPORT
:
804 return igmp_v1_report(igmp
, ip_hdr
->ip_src
, from_str
,
805 igmp_msg
, igmp_msg_len
);
807 case PIM_IGMP_V2_LEAVE_GROUP
:
808 return igmp_v2_leave(igmp
, ip_hdr
->ip_src
, from_str
,
809 igmp_msg
, igmp_msg_len
);
812 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type
);
817 static int pim_igmp_general_query(struct thread
*t
);
819 void pim_igmp_general_query_on(struct igmp_sock
*igmp
)
821 struct pim_interface
*pim_ifp
;
826 zassert(igmp
->interface
);
829 Since this socket is starting as querier,
830 there should not exist a timer for other-querier-present.
832 zassert(!igmp
->t_other_querier_timer
);
833 pim_ifp
= igmp
->interface
->info
;
837 RFC 3376: 8.6. Startup Query Interval
839 The Startup Query Interval is the interval between General Queries
840 sent by a Querier on startup. Default: 1/4 the Query Interval.
842 startup_mode
= igmp
->startup_query_count
> 0;
844 --igmp
->startup_query_count
;
846 /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
847 query_interval
= PIM_IGMP_SQI(pim_ifp
->igmp_default_query_interval
);
850 query_interval
= igmp
->querier_query_interval
;
853 if (PIM_DEBUG_IGMP_TRACE
) {
854 char ifaddr_str
[100];
855 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
856 zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
859 startup_mode
? "startup" : "non-startup",
862 igmp
->t_igmp_query_timer
= 0;
863 zassert(!igmp
->t_igmp_query_timer
);
864 THREAD_TIMER_ON(master
, igmp
->t_igmp_query_timer
,
865 pim_igmp_general_query
,
866 igmp
, query_interval
);
869 void pim_igmp_general_query_off(struct igmp_sock
*igmp
)
873 if (PIM_DEBUG_IGMP_TRACE
) {
874 if (igmp
->t_igmp_query_timer
) {
875 char ifaddr_str
[100];
876 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
877 zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
878 ifaddr_str
, igmp
->fd
, igmp
->interface
->name
);
881 THREAD_OFF(igmp
->t_igmp_query_timer
);
882 zassert(!igmp
->t_igmp_query_timer
);
885 /* Issue IGMP general query */
886 static int pim_igmp_general_query(struct thread
*t
)
888 char query_buf
[PIM_IGMP_BUFSIZE_WRITE
];
889 struct igmp_sock
*igmp
;
890 struct in_addr dst_addr
;
891 struct in_addr group_addr
;
892 struct pim_interface
*pim_ifp
;
896 igmp
= THREAD_ARG(t
);
899 zassert(igmp
->interface
);
900 zassert(igmp
->interface
->info
);
902 pim_ifp
= igmp
->interface
->info
;
905 RFC3376: 4.1.12. IP Destination Addresses for Queries
907 In IGMPv3, General Queries are sent with an IP destination address
908 of 224.0.0.1, the all-systems multicast address. Group-Specific
909 and Group-and-Source-Specific Queries are sent with an IP
910 destination address equal to the multicast address of interest.
913 dst_addr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
914 group_addr
.s_addr
= PIM_NET_INADDR_ANY
;
916 if (PIM_DEBUG_IGMP_TRACE
) {
917 char querier_str
[100];
919 pim_inet4_dump("<querier?>", igmp
->ifaddr
, querier_str
,
920 sizeof(querier_str
));
921 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
922 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
923 querier_str
, dst_str
, igmp
->interface
->name
);
926 pim_igmp_send_membership_query(0 /* igmp_group */,
928 igmp
->interface
->name
,
934 pim_ifp
->igmp_query_max_response_time_dsec
,
935 1 /* s_flag: always set for general queries */,
936 igmp
->querier_robustness_variable
,
937 igmp
->querier_query_interval
);
939 pim_igmp_general_query_on(igmp
);
944 static int pim_igmp_read(struct thread
*t
);
946 static void igmp_read_on(struct igmp_sock
*igmp
)
950 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
951 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
954 igmp
->t_igmp_read
= 0;
955 zassert(!igmp
->t_igmp_read
);
956 THREAD_READ_ON(master
, igmp
->t_igmp_read
, pim_igmp_read
, igmp
, igmp
->fd
);
959 static int pim_igmp_read(struct thread
*t
)
961 struct igmp_sock
*igmp
;
963 struct sockaddr_in from
;
964 struct sockaddr_in to
;
965 socklen_t fromlen
= sizeof(from
);
966 socklen_t tolen
= sizeof(to
);
967 uint8_t buf
[PIM_IGMP_BUFSIZE_READ
];
970 int result
= -1; /* defaults to bad */
974 igmp
= THREAD_ARG(t
);
980 zassert(fd
== igmp
->fd
);
982 len
= pim_socket_recvfromto(fd
, buf
, sizeof(buf
),
987 zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
988 fd
, errno
, safe_strerror(errno
));
992 if (PIM_DEBUG_IGMP_PACKETS
) {
996 if (!inet_ntop(AF_INET
, &from
.sin_addr
, from_str
, sizeof(from_str
)))
997 sprintf(from_str
, "<from?>");
998 if (!inet_ntop(AF_INET
, &to
.sin_addr
, to_str
, sizeof(to_str
)))
999 sprintf(to_str
, "<to?>");
1001 zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
1002 len
, from_str
, to_str
, fd
, ifindex
, igmp
->interface
->ifindex
);
1005 #ifdef PIM_CHECK_RECV_IFINDEX_SANITY
1006 /* ifindex sanity check */
1007 if (ifindex
!= (int) igmp
->interface
->ifindex
) {
1010 struct interface
*ifp
;
1012 if (!inet_ntop(AF_INET
, &from
.sin_addr
, from_str
, sizeof(from_str
)))
1013 sprintf(from_str
, "<from?>");
1014 if (!inet_ntop(AF_INET
, &to
.sin_addr
, to_str
, sizeof(to_str
)))
1015 sprintf(to_str
, "<to?>");
1017 ifp
= if_lookup_by_index(ifindex
);
1019 zassert(ifindex
== (int) ifp
->ifindex
);
1022 #ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
1023 zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
1024 from_str
, to_str
, fd
,
1025 ifindex
, ifp
? ifp
->name
: "<if-notfound>",
1026 igmp
->interface
->ifindex
, igmp
->interface
->name
);
1032 if (pim_igmp_packet(igmp
, (char *)buf
, len
)) {
1036 result
= 0; /* good */
1044 static void sock_close(struct igmp_sock
*igmp
)
1046 pim_igmp_other_querier_timer_off(igmp
);
1047 pim_igmp_general_query_off(igmp
);
1049 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
1050 if (igmp
->t_igmp_read
) {
1051 zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
1052 inet_ntoa(igmp
->ifaddr
), igmp
->fd
,
1053 igmp
->interface
->name
);
1056 THREAD_OFF(igmp
->t_igmp_read
);
1057 zassert(!igmp
->t_igmp_read
);
1059 if (close(igmp
->fd
)) {
1060 zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
1061 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
,
1062 errno
, safe_strerror(errno
));
1065 if (PIM_DEBUG_IGMP_TRACE_DETAIL
) {
1066 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
1067 inet_ntoa(igmp
->ifaddr
), igmp
->fd
, igmp
->interface
->name
);
1071 void igmp_startup_mode_on(struct igmp_sock
*igmp
)
1073 struct pim_interface
*pim_ifp
;
1075 pim_ifp
= igmp
->interface
->info
;
1078 RFC 3376: 8.7. Startup Query Count
1080 The Startup Query Count is the number of Queries sent out on
1081 startup, separated by the Startup Query Interval. Default: the
1082 Robustness Variable.
1084 igmp
->startup_query_count
= igmp
->querier_robustness_variable
;
1087 Since we're (re)starting, reset QQI to default Query Interval
1089 igmp
->querier_query_interval
= pim_ifp
->igmp_default_query_interval
;
1092 static void igmp_group_free(struct igmp_group
*group
)
1094 list_free(group
->group_source_list
);
1096 XFREE(MTYPE_PIM_IGMP_GROUP
, group
);
1099 static void igmp_group_delete(struct igmp_group
*group
)
1101 struct listnode
*src_node
;
1102 struct listnode
*src_nextnode
;
1103 struct igmp_source
*src
;
1105 if (PIM_DEBUG_IGMP_TRACE
) {
1106 char group_str
[100];
1107 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1108 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
1110 group
->group_igmp_sock
->fd
,
1111 group
->group_igmp_sock
->interface
->name
);
1114 for (ALL_LIST_ELEMENTS(group
->group_source_list
, src_node
, src_nextnode
, src
)) {
1115 igmp_source_delete(src
);
1118 if (group
->t_group_query_retransmit_timer
) {
1119 THREAD_OFF(group
->t_group_query_retransmit_timer
);
1122 group_timer_off(group
);
1123 listnode_delete(group
->group_igmp_sock
->igmp_group_list
, group
);
1124 igmp_group_free(group
);
1127 void igmp_group_delete_empty_include(struct igmp_group
*group
)
1129 zassert(!group
->group_filtermode_isexcl
);
1130 zassert(!listcount(group
->group_source_list
));
1132 igmp_group_delete(group
);
1135 void igmp_sock_free(struct igmp_sock
*igmp
)
1137 zassert(!igmp
->t_igmp_read
);
1138 zassert(!igmp
->t_igmp_query_timer
);
1139 zassert(!igmp
->t_other_querier_timer
);
1140 zassert(igmp
->igmp_group_list
);
1141 zassert(!listcount(igmp
->igmp_group_list
));
1143 list_free(igmp
->igmp_group_list
);
1145 XFREE(MTYPE_PIM_IGMP_SOCKET
, igmp
);
1148 void igmp_sock_delete(struct igmp_sock
*igmp
)
1150 struct pim_interface
*pim_ifp
;
1151 struct listnode
*grp_node
;
1152 struct listnode
*grp_nextnode
;
1153 struct igmp_group
*grp
;
1155 for (ALL_LIST_ELEMENTS(igmp
->igmp_group_list
, grp_node
, grp_nextnode
, grp
)) {
1156 igmp_group_delete(grp
);
1161 pim_ifp
= igmp
->interface
->info
;
1163 listnode_delete(pim_ifp
->igmp_socket_list
, igmp
);
1165 igmp_sock_free(igmp
);
1168 static struct igmp_sock
*igmp_sock_new(int fd
,
1169 struct in_addr ifaddr
,
1170 struct interface
*ifp
)
1172 struct pim_interface
*pim_ifp
;
1173 struct igmp_sock
*igmp
;
1175 pim_ifp
= ifp
->info
;
1177 if (PIM_DEBUG_IGMP_TRACE
) {
1178 zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
1179 fd
, inet_ntoa(ifaddr
), ifp
->name
);
1182 igmp
= XMALLOC(MTYPE_PIM_IGMP_SOCKET
, sizeof(*igmp
));
1184 zlog_warn("%s %s: XMALLOC() failure",
1185 __FILE__
, __PRETTY_FUNCTION__
);
1189 igmp
->igmp_group_list
= list_new();
1190 if (!igmp
->igmp_group_list
) {
1191 zlog_err("%s %s: failure: igmp_group_list = list_new()",
1192 __FILE__
, __PRETTY_FUNCTION__
);
1195 igmp
->igmp_group_list
->del
= (void (*)(void *)) igmp_group_free
;
1198 igmp
->interface
= ifp
;
1199 igmp
->ifaddr
= ifaddr
;
1200 igmp
->t_igmp_read
= 0;
1201 igmp
->t_igmp_query_timer
= 0;
1202 igmp
->t_other_querier_timer
= 0; /* no other querier present */
1203 igmp
->querier_robustness_variable
= pim_ifp
->igmp_default_robustness_variable
;
1204 igmp
->sock_creation
= pim_time_monotonic_sec();
1207 igmp_startup_mode_on() will reset QQI:
1209 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
1211 igmp_startup_mode_on(igmp
);
1214 pim_igmp_general_query_on(igmp
);
1219 struct igmp_sock
*pim_igmp_sock_add(struct list
*igmp_sock_list
,
1220 struct in_addr ifaddr
,
1221 struct interface
*ifp
)
1223 struct pim_interface
*pim_ifp
;
1224 struct igmp_sock
*igmp
;
1227 pim_ifp
= ifp
->info
;
1229 fd
= igmp_sock_open(ifaddr
, ifp
->ifindex
, pim_ifp
->options
);
1231 zlog_warn("Could not open IGMP socket for %s on %s",
1232 inet_ntoa(ifaddr
), ifp
->name
);
1236 igmp
= igmp_sock_new(fd
, ifaddr
, ifp
);
1238 zlog_err("%s %s: igmp_sock_new() failure",
1239 __FILE__
, __PRETTY_FUNCTION__
);
1244 listnode_add(igmp_sock_list
, igmp
);
1246 #ifdef IGMP_SOCK_DUMP
1247 igmp_sock_dump(igmp_sock_array
);
1254 RFC 3376: 6.5. Switching Router Filter-Modes
1256 When a router's filter-mode for a group is EXCLUDE and the group
1257 timer expires, the router filter-mode for the group transitions to
1260 A router uses source records with running source timers as its state
1261 for the switch to a filter-mode of INCLUDE. If there are any source
1262 records with source timers greater than zero (i.e., requested to be
1263 forwarded), a router switches to filter-mode of INCLUDE using those
1264 source records. Source records whose timers are zero (from the
1265 previous EXCLUDE mode) are deleted.
1267 static int igmp_group_timer(struct thread
*t
)
1269 struct igmp_group
*group
;
1272 group
= THREAD_ARG(t
);
1275 if (PIM_DEBUG_IGMP_TRACE
) {
1276 char group_str
[100];
1277 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1278 zlog_debug("%s: Timer for group %s on interface %s",
1279 __PRETTY_FUNCTION__
,
1280 group_str
, group
->group_igmp_sock
->interface
->name
);
1283 zassert(group
->group_filtermode_isexcl
);
1285 group
->t_group_timer
= NULL
;
1286 group
->group_filtermode_isexcl
= 0;
1288 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1289 igmp_anysource_forward_stop(group
);
1291 igmp_source_delete_expired(group
->group_source_list
);
1293 zassert(!group
->t_group_timer
);
1294 zassert(!group
->group_filtermode_isexcl
);
1297 RFC 3376: 6.2.2. Definition of Group Timers
1299 If there are no more source records for the group, delete group
1302 if (listcount(group
->group_source_list
) < 1) {
1303 igmp_group_delete_empty_include(group
);
1309 static void group_timer_off(struct igmp_group
*group
)
1311 if (!group
->t_group_timer
)
1314 if (PIM_DEBUG_IGMP_TRACE
) {
1315 char group_str
[100];
1316 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1317 zlog_debug("Cancelling TIMER event for group %s on %s",
1318 group_str
, group
->group_igmp_sock
->interface
->name
);
1321 THREAD_OFF(group
->t_group_timer
);
1322 zassert(!group
->t_group_timer
);
1325 void igmp_group_timer_on(struct igmp_group
*group
,
1326 long interval_msec
, const char *ifname
)
1328 group_timer_off(group
);
1330 if (PIM_DEBUG_IGMP_EVENTS
) {
1331 char group_str
[100];
1332 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1333 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1334 interval_msec
/ 1000,
1335 interval_msec
% 1000,
1340 RFC 3376: 6.2.2. Definition of Group Timers
1342 The group timer is only used when a group is in EXCLUDE mode and
1343 it represents the time for the *filter-mode* of the group to
1344 expire and switch to INCLUDE mode.
1346 zassert(group
->group_filtermode_isexcl
);
1348 THREAD_TIMER_MSEC_ON(master
, group
->t_group_timer
,
1350 group
, interval_msec
);
1353 static struct igmp_group
*find_group_by_addr(struct igmp_sock
*igmp
,
1354 struct in_addr group_addr
)
1356 struct igmp_group
*group
;
1357 struct listnode
*node
;
1359 for (ALL_LIST_ELEMENTS_RO(igmp
->igmp_group_list
, node
, group
))
1360 if (group_addr
.s_addr
== group
->group_addr
.s_addr
)
1366 struct igmp_group
*igmp_add_group_by_addr(struct igmp_sock
*igmp
,
1367 struct in_addr group_addr
)
1369 struct igmp_group
*group
;
1371 group
= find_group_by_addr(igmp
, group_addr
);
1377 Non-existant group is created as INCLUDE {empty}:
1379 RFC 3376 - 5.1. Action on Change of Interface State
1381 If no interface state existed for that multicast address before
1382 the change (i.e., the change consisted of creating a new
1383 per-interface record), or if no state exists after the change
1384 (i.e., the change consisted of deleting a per-interface record),
1385 then the "non-existent" state is considered to have a filter mode
1386 of INCLUDE and an empty source list.
1389 group
= XMALLOC(MTYPE_PIM_IGMP_GROUP
, sizeof(*group
));
1391 zlog_warn("%s %s: XMALLOC() failure",
1392 __FILE__
, __PRETTY_FUNCTION__
);
1393 return 0; /* error, not found, could not create */
1396 group
->group_source_list
= list_new();
1397 if (!group
->group_source_list
) {
1398 zlog_warn("%s %s: list_new() failure",
1399 __FILE__
, __PRETTY_FUNCTION__
);
1400 XFREE(MTYPE_PIM_IGMP_GROUP
, group
); /* discard group */
1401 return 0; /* error, not found, could not initialize */
1403 group
->group_source_list
->del
= (void (*)(void *)) igmp_source_free
;
1405 group
->t_group_timer
= NULL
;
1406 group
->t_group_query_retransmit_timer
= NULL
;
1407 group
->group_specific_query_retransmit_count
= 0;
1408 group
->group_addr
= group_addr
;
1409 group
->group_igmp_sock
= igmp
;
1410 group
->last_igmp_v1_report_dsec
= -1;
1411 group
->last_igmp_v2_report_dsec
= -1;
1412 group
->group_creation
= pim_time_monotonic_sec();
1414 /* initialize new group as INCLUDE {empty} */
1415 group
->group_filtermode_isexcl
= 0; /* 0=INCLUDE, 1=EXCLUDE */
1417 listnode_add(igmp
->igmp_group_list
, group
);
1419 if (PIM_DEBUG_IGMP_TRACE
) {
1420 char group_str
[100];
1421 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1422 zlog_debug("Creating new IGMP group %s on socket %d interface %s",
1423 group_str
, igmp
->fd
, igmp
->interface
->name
);
1427 RFC 3376: 6.2.2. Definition of Group Timers
1429 The group timer is only used when a group is in EXCLUDE mode and
1430 it represents the time for the *filter-mode* of the group to
1431 expire and switch to INCLUDE mode.
1433 zassert(!group
->group_filtermode_isexcl
); /* INCLUDE mode */
1434 zassert(!group
->t_group_timer
); /* group timer == 0 */
1436 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1437 igmp_anysource_forward_stop(group
);