]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmpv3.c
pimd: reformat after dropping indent levels
[mirror_frr.git] / pimd / pim_igmpv3.c
CommitLineData
12e41d03 1/*
896014f4
DL
2 * PIM for Quagga
3 * Copyright (C) 2008 Everton da Silva Marques
4 *
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.
9 *
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.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
12e41d03
DL
19
20#include <zebra.h>
21#include "log.h"
22#include "memory.h"
744d91b3 23#include "if.h"
3613d898 24#include "lib_errors.h"
12e41d03
DL
25
26#include "pimd.h"
27#include "pim_iface.h"
28#include "pim_igmp.h"
29#include "pim_igmpv3.h"
30#include "pim_str.h"
31#include "pim_util.h"
32#include "pim_time.h"
33#include "pim_zebra.h"
34#include "pim_oil.h"
35
36static void group_retransmit_timer_on(struct igmp_group *group);
37static long igmp_group_timer_remain_msec(struct igmp_group *group);
38static long igmp_source_timer_remain_msec(struct igmp_source *source);
39static void group_query_send(struct igmp_group *group);
40static void source_query_send_by_flag(struct igmp_group *group,
41 int num_sources_tosend);
42
d62a17ae 43static void on_trace(const char *label, struct interface *ifp,
44 struct in_addr from, struct in_addr group_addr,
12e41d03
DL
45 int num_sources, struct in_addr *sources)
46{
d62a17ae 47 if (PIM_DEBUG_IGMP_TRACE) {
48 char from_str[INET_ADDRSTRLEN];
49 char group_str[INET_ADDRSTRLEN];
12e41d03 50
d62a17ae 51 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
52 pim_inet4_dump("<group?>", group_addr, group_str,
53 sizeof(group_str));
12e41d03 54
d62a17ae 55 zlog_debug("%s: from %s on %s: group=%s sources=%d", label,
56 from_str, ifp->name, group_str, num_sources);
57 }
12e41d03
DL
58}
59
dda4d23c
DL
60static inline long igmp_gmi_msec(struct igmp_group *group)
61{
62 struct pim_interface *pim_ifp = group->interface->info;
63 struct igmp_sock *igmp;
64 struct listnode *sock_node;
65
66 long qrv = 0, qqi = 0;
67
68 for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
69 qrv = MAX(qrv, igmp->querier_robustness_variable);
70 qqi = MAX(qqi, igmp->querier_query_interval);
71 }
72 return PIM_IGMP_GMI_MSEC(qrv, qqi,
73 pim_ifp->igmp_query_max_response_time_dsec);
74}
75
12e41d03
DL
76void igmp_group_reset_gmi(struct igmp_group *group)
77{
d62a17ae 78 long group_membership_interval_msec;
d62a17ae 79 struct interface *ifp;
80
dda4d23c 81 ifp = group->interface;
d62a17ae 82
83 /*
84 RFC 3376: 8.4. Group Membership Interval
85
86 The Group Membership Interval is the amount of time that must pass
87 before a multicast router decides there are no more members of a
88 group or a particular source on a network.
89
90 This value MUST be ((the Robustness Variable) times (the Query
91 Interval)) plus (one Query Response Interval).
92
93 group_membership_interval_msec = querier_robustness_variable *
94 (1000 * querier_query_interval) +
95 100 * query_response_interval_dsec;
96 */
dda4d23c 97 group_membership_interval_msec = igmp_gmi_msec(group);
d62a17ae 98
99 if (PIM_DEBUG_IGMP_TRACE) {
100 char group_str[INET_ADDRSTRLEN];
101 pim_inet4_dump("<group?>", group->group_addr, group_str,
102 sizeof(group_str));
103 zlog_debug(
104 "Resetting group %s timer to GMI=%ld.%03ld sec on %s",
105 group_str, group_membership_interval_msec / 1000,
106 group_membership_interval_msec % 1000, ifp->name);
107 }
108
109 /*
110 RFC 3376: 6.2.2. Definition of Group Timers
111
112 The group timer is only used when a group is in EXCLUDE mode and
113 it represents the time for the *filter-mode* of the group to
114 expire and switch to INCLUDE mode.
115 */
df5dfb77 116 assert(group->group_filtermode_isexcl);
d62a17ae 117
118 igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
12e41d03
DL
119}
120
121static int igmp_source_timer(struct thread *t)
122{
d62a17ae 123 struct igmp_source *source;
124 struct igmp_group *group;
125
126 source = THREAD_ARG(t);
127
128 group = source->source_group;
129
130 if (PIM_DEBUG_IGMP_TRACE) {
131 char group_str[INET_ADDRSTRLEN];
132 char source_str[INET_ADDRSTRLEN];
133 pim_inet4_dump("<group?>", group->group_addr, group_str,
134 sizeof(group_str));
135 pim_inet4_dump("<source?>", source->source_addr, source_str,
136 sizeof(source_str));
137 zlog_debug(
138 "%s: Source timer expired for group %s source %s on %s",
15569c58 139 __func__, group_str, source_str,
dda4d23c 140 group->interface->name);
d62a17ae 141 }
142
143 /*
144 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
145
146 Group
147 Filter-Mode Source Timer Value Action
148 ----------- ------------------ ------
149 INCLUDE TIMER == 0 Suggest to stop forwarding
150 traffic from source and
151 remove source record. If
152 there are no more source
153 records for the group, delete
154 group record.
155
156 EXCLUDE TIMER == 0 Suggest to not forward
157 traffic from source
158 (DO NOT remove record)
159
160 Source timer switched from (T > 0) to (T == 0): disable forwarding.
161 */
162
163 if (group->group_filtermode_isexcl) {
164 /* EXCLUDE mode */
165
166 igmp_source_forward_stop(source);
167 } else {
168 /* INCLUDE mode */
169
170 /* igmp_source_delete() will stop forwarding source */
171 igmp_source_delete(source);
172
173 /*
174 If there are no more source records for the group, delete
175 group
176 record.
177 */
178 if (!listcount(group->group_source_list)) {
179 igmp_group_delete_empty_include(group);
180 }
181 }
182
183 return 0;
12e41d03
DL
184}
185
186static void source_timer_off(struct igmp_group *group,
187 struct igmp_source *source)
188{
d62a17ae 189 if (!source->t_source_timer)
190 return;
191
192 if (PIM_DEBUG_IGMP_TRACE) {
193 char group_str[INET_ADDRSTRLEN];
194 char source_str[INET_ADDRSTRLEN];
195 pim_inet4_dump("<group?>", group->group_addr, group_str,
196 sizeof(group_str));
197 pim_inet4_dump("<source?>", source->source_addr, source_str,
198 sizeof(source_str));
199 zlog_debug(
200 "Cancelling TIMER event for group %s source %s on %s",
a1a4295a 201 group_str, source_str, group->interface->name);
d62a17ae 202 }
203
204 THREAD_OFF(source->t_source_timer);
12e41d03
DL
205}
206
207static void igmp_source_timer_on(struct igmp_group *group,
d62a17ae 208 struct igmp_source *source, long interval_msec)
12e41d03 209{
d62a17ae 210 source_timer_off(group, source);
dda4d23c 211 struct pim_interface *pim_ifp = group->interface->info;
d62a17ae 212
213 if (PIM_DEBUG_IGMP_EVENTS) {
214 char group_str[INET_ADDRSTRLEN];
215 char source_str[INET_ADDRSTRLEN];
216 pim_inet4_dump("<group?>", group->group_addr, group_str,
217 sizeof(group_str));
218 pim_inet4_dump("<source?>", source->source_addr, source_str,
219 sizeof(source_str));
220 zlog_debug(
221 "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
222 interval_msec / 1000, interval_msec % 1000, group_str,
dda4d23c 223 source_str, group->interface->name);
d62a17ae 224 }
225
36417fcc
DS
226 thread_add_timer_msec(router->master, igmp_source_timer, source,
227 interval_msec, &source->t_source_timer);
d62a17ae 228
229 /*
230 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
231
232 Source timer switched from (T == 0) to (T > 0): enable forwarding.
233 */
e52e3223 234 igmp_source_forward_start(pim_ifp->pim, source);
12e41d03
DL
235}
236
dda4d23c 237void igmp_source_reset_gmi(struct igmp_group *group, struct igmp_source *source)
12e41d03 238{
d62a17ae 239 long group_membership_interval_msec;
d62a17ae 240 struct interface *ifp;
241
dda4d23c 242 ifp = group->interface;
d62a17ae 243
dda4d23c 244 group_membership_interval_msec = igmp_gmi_msec(group);
d62a17ae 245
246 if (PIM_DEBUG_IGMP_TRACE) {
247 char group_str[INET_ADDRSTRLEN];
248 char source_str[INET_ADDRSTRLEN];
249
250 pim_inet4_dump("<group?>", group->group_addr, group_str,
251 sizeof(group_str));
252 pim_inet4_dump("<source?>", source->source_addr, source_str,
253 sizeof(source_str));
254
255 zlog_debug(
256 "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
257 source_str, group_membership_interval_msec / 1000,
258 group_membership_interval_msec % 1000, group_str,
259 ifp->name);
260 }
261
262 igmp_source_timer_on(group, source, group_membership_interval_msec);
12e41d03
DL
263}
264
d2ccd60b 265static void source_mark_delete_flag(struct igmp_group *group)
12e41d03 266{
d62a17ae 267 struct listnode *src_node;
268 struct igmp_source *src;
12e41d03 269
d62a17ae 270 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
271 IGMP_SOURCE_DO_DELETE(src->source_flags);
272 }
12e41d03
DL
273}
274
d62a17ae 275static void source_mark_send_flag(struct igmp_group *group)
12e41d03 276{
d62a17ae 277 struct listnode *src_node;
278 struct igmp_source *src;
12e41d03 279
d62a17ae 280 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
281 IGMP_SOURCE_DO_SEND(src->source_flags);
282 }
12e41d03
DL
283}
284
d62a17ae 285static int source_mark_send_flag_by_timer(struct igmp_group *group)
12e41d03 286{
d62a17ae 287 struct listnode *src_node;
288 struct igmp_source *src;
289 int num_marked_sources = 0;
290
291 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
292 /* Is source timer running? */
293 if (src->t_source_timer) {
294 IGMP_SOURCE_DO_SEND(src->source_flags);
295 ++num_marked_sources;
296 } else {
297 IGMP_SOURCE_DONT_SEND(src->source_flags);
298 }
299 }
300
301 return num_marked_sources;
12e41d03
DL
302}
303
304static void source_clear_send_flag(struct list *source_list)
305{
d62a17ae 306 struct listnode *src_node;
307 struct igmp_source *src;
12e41d03 308
d62a17ae 309 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
310 IGMP_SOURCE_DONT_SEND(src->source_flags);
311 }
12e41d03
DL
312}
313
314/*
315 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
316*/
317static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
318{
dda4d23c 319 struct pim_interface *pim_ifp = group->interface->info;
e52e3223 320
df5dfb77 321 assert(group->group_filtermode_isexcl);
12e41d03 322
d62a17ae 323 if (listcount(group->group_source_list) < 1) {
e52e3223 324 igmp_anysource_forward_start(pim_ifp->pim, group);
d62a17ae 325 }
12e41d03
DL
326}
327
328void igmp_source_free(struct igmp_source *source)
329{
d62a17ae 330 /* make sure there is no source timer running */
df5dfb77 331 assert(!source->t_source_timer);
12e41d03 332
d62a17ae 333 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
12e41d03
DL
334}
335
336static void source_channel_oil_detach(struct igmp_source *source)
337{
d62a17ae 338 if (source->source_channel_oil) {
15569c58 339 pim_channel_oil_del(source->source_channel_oil, __func__);
d62a17ae 340 source->source_channel_oil = NULL;
341 }
12e41d03
DL
342}
343
344/*
345 igmp_source_delete: stop fowarding, and delete the source
346 igmp_source_forward_stop: stop fowarding, but keep the source
347*/
348void igmp_source_delete(struct igmp_source *source)
349{
d62a17ae 350 struct igmp_group *group;
351 struct in_addr src;
352
353 group = source->source_group;
354
355 if (PIM_DEBUG_IGMP_TRACE) {
356 char group_str[INET_ADDRSTRLEN];
357 char source_str[INET_ADDRSTRLEN];
358 pim_inet4_dump("<group?>", group->group_addr, group_str,
359 sizeof(group_str));
360 pim_inet4_dump("<source?>", source->source_addr, source_str,
361 sizeof(source_str));
362 zlog_debug(
dda4d23c 363 "Deleting IGMP source %s for group %s from interface %s c_oil ref_count %d",
a1a4295a 364 source_str, group_str, group->interface->name,
d62a17ae 365 source->source_channel_oil
366 ? source->source_channel_oil->oil_ref_count
367 : 0);
368 }
369
370 source_timer_off(group, source);
371 igmp_source_forward_stop(source);
372
373 /* sanity check that forwarding has been disabled */
374 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
375 char group_str[INET_ADDRSTRLEN];
376 char source_str[INET_ADDRSTRLEN];
377 pim_inet4_dump("<group?>", group->group_addr, group_str,
378 sizeof(group_str));
379 pim_inet4_dump("<source?>", source->source_addr, source_str,
380 sizeof(source_str));
381 zlog_warn(
dda4d23c 382 "%s: forwarding=ON(!) IGMP source %s for group %s from interface %s",
15569c58 383 __func__, source_str, group_str,
dda4d23c 384 group->interface->name);
d62a17ae 385 /* warning only */
386 }
387
388 source_channel_oil_detach(source);
389
390 /*
391 notice that listnode_delete() can't be moved
392 into igmp_source_free() because the later is
393 called by list_delete_all_node()
394 */
395 listnode_delete(group->group_source_list, source);
396
397 src.s_addr = source->source_addr.s_addr;
398 igmp_source_free(source);
399
400 /* Group source list is empty and current source is * then
401 *,G group going away so do not trigger start */
402 if (group->group_filtermode_isexcl
403 && (listcount(group->group_source_list) != 0)
404 && src.s_addr != INADDR_ANY) {
405 group_exclude_fwd_anysrc_ifempty(group);
406 }
12e41d03
DL
407}
408
409static void source_delete_by_flag(struct list *source_list)
410{
d62a17ae 411 struct listnode *src_node;
412 struct listnode *src_nextnode;
413 struct igmp_source *src;
414
415 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
416 if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
417 igmp_source_delete(src);
12e41d03
DL
418}
419
420void igmp_source_delete_expired(struct list *source_list)
421{
d62a17ae 422 struct listnode *src_node;
423 struct listnode *src_nextnode;
424 struct igmp_source *src;
425
426 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
427 if (!src->t_source_timer)
428 igmp_source_delete(src);
12e41d03
DL
429}
430
431struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
432 struct in_addr src_addr)
433{
d62a17ae 434 struct listnode *src_node;
435 struct igmp_source *src;
12e41d03 436
d62a17ae 437 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
438 if (src_addr.s_addr == src->source_addr.s_addr)
439 return src;
12e41d03 440
d62a17ae 441 return 0;
12e41d03
DL
442}
443
d62a17ae 444struct igmp_source *source_new(struct igmp_group *group,
445 struct in_addr src_addr)
12e41d03 446{
d62a17ae 447 struct igmp_source *src;
448
449 if (PIM_DEBUG_IGMP_TRACE) {
450 char group_str[INET_ADDRSTRLEN];
451 char source_str[INET_ADDRSTRLEN];
452 pim_inet4_dump("<group?>", group->group_addr, group_str,
453 sizeof(group_str));
454 pim_inet4_dump("<source?>", src_addr, source_str,
455 sizeof(source_str));
456 zlog_debug(
dda4d23c
DL
457 "Creating new IGMP source %s for group %s on interface %s",
458 source_str, group_str, group->interface->name);
d62a17ae 459 }
460
461 src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
d62a17ae 462
463 src->t_source_timer = NULL;
464 src->source_group = group; /* back pointer */
465 src->source_addr = src_addr;
466 src->source_creation = pim_time_monotonic_sec();
467 src->source_flags = 0;
468 src->source_query_retransmit_count = 0;
469 src->source_channel_oil = NULL;
470
471 listnode_add(group->group_source_list, src);
472
473 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
474 igmp_anysource_forward_stop(group);
475
476 return src;
12e41d03
DL
477}
478
479static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
480 struct igmp_group *group,
9bd7302f 481 struct in_addr src_addr)
12e41d03 482{
d62a17ae 483 struct igmp_source *src;
12e41d03 484
d62a17ae 485 src = igmp_find_source_by_addr(group, src_addr);
486 if (src) {
487 return src;
488 }
12e41d03 489
d62a17ae 490 src = source_new(group, src_addr);
12e41d03 491
d62a17ae 492 return src;
12e41d03
DL
493}
494
495static void allow(struct igmp_sock *igmp, struct in_addr from,
d62a17ae 496 struct in_addr group_addr, int num_sources,
497 struct in_addr *sources)
12e41d03 498{
d62a17ae 499 struct igmp_source *source;
500 struct igmp_group *group;
501 int i;
502
fcf5d438
SG
503 if (num_sources == 0) {
504 /*
505 RFC 3376: 3.1. Socket-State
506 If the requested filter mode is INCLUDE *and* the requested
507 source list is empty, then the entry corresponding to the
508 requested interface and multicast address is deleted if
509 present. If no such entry is present, the request is ignored.
510 So, deleting the group present.
511 */
512 group = find_group_by_addr(igmp, group_addr);
26d0cbbb
JAG
513 if (!group) {
514 return;
515 }
516 if (group->group_filtermode_isexcl) {
517 if (listcount(group->group_source_list) == 1) {
518 struct in_addr star = {.s_addr = INADDR_ANY};
519
520 source = igmp_find_source_by_addr(group, star);
521 if (source)
dda4d23c 522 igmp_source_reset_gmi(group, source);
26d0cbbb
JAG
523 }
524 } else {
fcf5d438 525 igmp_group_delete(group);
26d0cbbb
JAG
526 }
527
fcf5d438
SG
528 return;
529 }
530
d62a17ae 531 /* non-existant group is created as INCLUDE {empty} */
532 group = igmp_add_group_by_addr(igmp, group_addr);
533 if (!group) {
534 return;
535 }
536
537 /* scan received sources */
538 for (i = 0; i < num_sources; ++i) {
539 struct in_addr *src_addr;
540
541 src_addr = sources + i;
542
543 source = add_source_by_addr(igmp, group, *src_addr);
544 if (!source) {
545 continue;
546 }
547
548 /*
549 RFC 3376: 6.4.1. Reception of Current-State Records
550
551 When receiving IS_IN reports for groups in EXCLUDE mode is
552 sources should be moved from set with (timers = 0) to set with
553 (timers > 0).
554
555 igmp_source_reset_gmi() below, resetting the source timers to
556 GMI, accomplishes this.
557 */
dda4d23c 558 igmp_source_reset_gmi(group, source);
d62a17ae 559
560 } /* scan received sources */
12e41d03
DL
561}
562
563void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
d62a17ae 564 struct in_addr group_addr, int num_sources,
565 struct in_addr *sources)
12e41d03 566{
15569c58
DA
567 on_trace(__func__, igmp->interface, from, group_addr, num_sources,
568 sources);
12e41d03 569
d62a17ae 570 allow(igmp, from, group_addr, num_sources, sources);
12e41d03
DL
571}
572
d62a17ae 573static void isex_excl(struct igmp_group *group, int num_sources,
574 struct in_addr *sources)
12e41d03 575{
d62a17ae 576 struct igmp_source *source;
577 int i;
578
579 /* EXCLUDE mode */
df5dfb77 580 assert(group->group_filtermode_isexcl);
d62a17ae 581
582 /* E.1: set deletion flag for known sources (X,Y) */
583 source_mark_delete_flag(group);
584
585 /* scan received sources (A) */
586 for (i = 0; i < num_sources; ++i) {
587 struct in_addr *src_addr;
588
589 src_addr = sources + i;
590
591 /* E.2: lookup reported source from (A) in (X,Y) */
592 source = igmp_find_source_by_addr(group, *src_addr);
593 if (source) {
594 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
595 IGMP_SOURCE_DONT_DELETE(source->source_flags);
596 } else {
597 /* E.4: if not found, create source with timer=GMI:
598 * (A-X-Y) */
599 source = source_new(group, *src_addr);
df5dfb77 600 assert(!source->t_source_timer); /* timer == 0 */
dda4d23c 601 igmp_source_reset_gmi(group, source);
df5dfb77 602 assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
d62a17ae 603 }
604
605 } /* scan received sources */
606
607 /*
608 * If we are in isexcl mode and num_sources == 0
609 * than that means we have a *,g entry that
610 * needs to be handled
611 */
612 if (group->group_filtermode_isexcl && num_sources == 0) {
613 struct in_addr star = {.s_addr = INADDR_ANY};
614 source = igmp_find_source_by_addr(group, star);
615 if (source) {
616 IGMP_SOURCE_DONT_DELETE(source->source_flags);
dda4d23c 617 igmp_source_reset_gmi(group, source);
d62a17ae 618 }
619 }
620
621 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
622 source_delete_by_flag(group->group_source_list);
12e41d03
DL
623}
624
d62a17ae 625static void isex_incl(struct igmp_group *group, int num_sources,
626 struct in_addr *sources)
12e41d03 627{
d62a17ae 628 int i;
629
630 /* INCLUDE mode */
df5dfb77 631 assert(!group->group_filtermode_isexcl);
d62a17ae 632
633 /* I.1: set deletion flag for known sources (A) */
634 source_mark_delete_flag(group);
635
636 /* scan received sources (B) */
637 for (i = 0; i < num_sources; ++i) {
638 struct igmp_source *source;
639 struct in_addr *src_addr;
640
641 src_addr = sources + i;
642
643 /* I.2: lookup reported source (B) */
644 source = igmp_find_source_by_addr(group, *src_addr);
645 if (source) {
646 /* I.3: if found, clear deletion flag (A*B) */
647 IGMP_SOURCE_DONT_DELETE(source->source_flags);
648 } else {
649 /* I.4: if not found, create source with timer=0 (B-A)
650 */
651 source = source_new(group, *src_addr);
df5dfb77 652 assert(!source->t_source_timer); /* (B-A) timer=0 */
d62a17ae 653 }
654
655 } /* scan received sources */
656
657 /* I.5: delete all sources marked with deletion flag (A-B) */
658 source_delete_by_flag(group->group_source_list);
659
660 group->group_filtermode_isexcl = 1; /* boolean=true */
661
df5dfb77 662 assert(group->group_filtermode_isexcl);
d62a17ae 663
664 group_exclude_fwd_anysrc_ifempty(group);
12e41d03
DL
665}
666
667void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
d62a17ae 668 struct in_addr group_addr, int num_sources,
669 struct in_addr *sources, int from_igmp_v2_report)
12e41d03 670{
d62a17ae 671 struct interface *ifp = igmp->interface;
672 struct igmp_group *group;
673
15569c58 674 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
d62a17ae 675
b0f525a8
QY
676 if (pim_is_group_filtered(ifp->info, &group_addr))
677 return;
678
d62a17ae 679 /* non-existant group is created as INCLUDE {empty} */
680 group = igmp_add_group_by_addr(igmp, group_addr);
681 if (!group) {
682 return;
683 }
684
685 /* So we can display how we learned the group in our show command output
686 */
687 if (from_igmp_v2_report)
688 group->igmp_version = 2;
689
690 if (group->group_filtermode_isexcl) {
691 /* EXCLUDE mode */
692 isex_excl(group, num_sources, sources);
693 } else {
694 /* INCLUDE mode */
695 isex_incl(group, num_sources, sources);
df5dfb77 696 assert(group->group_filtermode_isexcl);
d62a17ae 697 }
698
df5dfb77 699 assert(group->group_filtermode_isexcl);
d62a17ae 700
701 igmp_group_reset_gmi(group);
12e41d03
DL
702}
703
d62a17ae 704static void toin_incl(struct igmp_group *group, int num_sources,
705 struct in_addr *sources)
12e41d03 706{
d62a17ae 707 int num_sources_tosend = listcount(group->group_source_list);
708 int i;
709
710 /* Set SEND flag for all known sources (A) */
711 source_mark_send_flag(group);
712
713 /* Scan received sources (B) */
714 for (i = 0; i < num_sources; ++i) {
715 struct igmp_source *source;
716 struct in_addr *src_addr;
717
718 src_addr = sources + i;
719
720 /* Lookup reported source (B) */
721 source = igmp_find_source_by_addr(group, *src_addr);
722 if (source) {
723 /* If found, clear SEND flag (A*B) */
724 IGMP_SOURCE_DONT_SEND(source->source_flags);
725 --num_sources_tosend;
726 } else {
727 /* If not found, create new source */
728 source = source_new(group, *src_addr);
d62a17ae 729 }
730
731 /* (B)=GMI */
dda4d23c 732 igmp_source_reset_gmi(group, source);
d62a17ae 733 }
734
735 /* Send sources marked with SEND flag: Q(G,A-B) */
736 if (num_sources_tosend > 0) {
737 source_query_send_by_flag(group, num_sources_tosend);
738 }
12e41d03
DL
739}
740
d62a17ae 741static void toin_excl(struct igmp_group *group, int num_sources,
742 struct in_addr *sources)
12e41d03 743{
d62a17ae 744 int num_sources_tosend;
745 int i;
746
747 /* Set SEND flag for X (sources with timer > 0) */
748 num_sources_tosend = source_mark_send_flag_by_timer(group);
749
750 /* Scan received sources (A) */
751 for (i = 0; i < num_sources; ++i) {
752 struct igmp_source *source;
753 struct in_addr *src_addr;
754
755 src_addr = sources + i;
756
757 /* Lookup reported source (A) */
758 source = igmp_find_source_by_addr(group, *src_addr);
759 if (source) {
760 if (source->t_source_timer) {
761 /* If found and timer running, clear SEND flag
762 * (X*A) */
763 IGMP_SOURCE_DONT_SEND(source->source_flags);
764 --num_sources_tosend;
765 }
766 } else {
767 /* If not found, create new source */
768 source = source_new(group, *src_addr);
d62a17ae 769 }
770
771 /* (A)=GMI */
dda4d23c 772 igmp_source_reset_gmi(group, source);
d62a17ae 773 }
774
775 /* Send sources marked with SEND flag: Q(G,X-A) */
776 if (num_sources_tosend > 0) {
777 source_query_send_by_flag(group, num_sources_tosend);
778 }
779
780 /* Send Q(G) */
781 group_query_send(group);
12e41d03
DL
782}
783
784void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
d62a17ae 785 struct in_addr group_addr, int num_sources,
786 struct in_addr *sources)
12e41d03 787{
d62a17ae 788 struct interface *ifp = igmp->interface;
789 struct igmp_group *group;
790
15569c58 791 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
d62a17ae 792
793 /*
794 * If the requested filter mode is INCLUDE *and* the requested source
795 * list is empty, then the entry corresponding to the requested
796 * interface and multicast address is deleted if present. If no such
797 * entry is present, the request is ignored.
798 */
799 if (num_sources) {
800 /* non-existant group is created as INCLUDE {empty} */
801 group = igmp_add_group_by_addr(igmp, group_addr);
802 if (!group) {
803 return;
804 }
805 } else {
806 group = find_group_by_addr(igmp, group_addr);
807 if (!group)
808 return;
809 }
810
811 if (group->group_filtermode_isexcl) {
812 /* EXCLUDE mode */
813 toin_excl(group, num_sources, sources);
814 } else {
815 /* INCLUDE mode */
816 toin_incl(group, num_sources, sources);
817 }
12e41d03
DL
818}
819
d62a17ae 820static void toex_incl(struct igmp_group *group, int num_sources,
821 struct in_addr *sources)
12e41d03 822{
d62a17ae 823 int num_sources_tosend = 0;
824 int i;
825
df5dfb77 826 assert(!group->group_filtermode_isexcl);
d62a17ae 827
828 /* Set DELETE flag for all known sources (A) */
829 source_mark_delete_flag(group);
830
831 /* Clear off SEND flag from all known sources (A) */
832 source_clear_send_flag(group->group_source_list);
833
834 /* Scan received sources (B) */
835 for (i = 0; i < num_sources; ++i) {
836 struct igmp_source *source;
837 struct in_addr *src_addr;
838
839 src_addr = sources + i;
840
841 /* Lookup reported source (B) */
842 source = igmp_find_source_by_addr(group, *src_addr);
843 if (source) {
844 /* If found, clear deletion flag: (A*B) */
845 IGMP_SOURCE_DONT_DELETE(source->source_flags);
846 /* and set SEND flag (A*B) */
847 IGMP_SOURCE_DO_SEND(source->source_flags);
848 ++num_sources_tosend;
849 } else {
850 /* If source not found, create source with timer=0:
851 * (B-A)=0 */
852 source = source_new(group, *src_addr);
df5dfb77 853 assert(!source->t_source_timer); /* (B-A) timer=0 */
d62a17ae 854 }
855
856 } /* Scan received sources (B) */
857
858 group->group_filtermode_isexcl = 1; /* boolean=true */
859
860 /* Delete all sources marked with DELETE flag (A-B) */
861 source_delete_by_flag(group->group_source_list);
862
863 /* Send sources marked with SEND flag: Q(G,A*B) */
864 if (num_sources_tosend > 0) {
865 source_query_send_by_flag(group, num_sources_tosend);
866 }
867
df5dfb77 868 assert(group->group_filtermode_isexcl);
d62a17ae 869
870 group_exclude_fwd_anysrc_ifempty(group);
12e41d03
DL
871}
872
d62a17ae 873static void toex_excl(struct igmp_group *group, int num_sources,
874 struct in_addr *sources)
12e41d03 875{
d62a17ae 876 int num_sources_tosend = 0;
877 int i;
878
879 /* set DELETE flag for all known sources (X,Y) */
880 source_mark_delete_flag(group);
881
882 /* clear off SEND flag from all known sources (X,Y) */
883 source_clear_send_flag(group->group_source_list);
884
885 if (num_sources == 0) {
886 struct igmp_source *source;
887 struct in_addr any = {.s_addr = INADDR_ANY};
888
889 source = igmp_find_source_by_addr(group, any);
890 if (source)
891 IGMP_SOURCE_DONT_DELETE(source->source_flags);
892 }
893
894 /* scan received sources (A) */
895 for (i = 0; i < num_sources; ++i) {
896 struct igmp_source *source;
897 struct in_addr *src_addr;
898
899 src_addr = sources + i;
900
901 /* lookup reported source (A) in known sources (X,Y) */
902 source = igmp_find_source_by_addr(group, *src_addr);
903 if (source) {
904 /* if found, clear off DELETE flag from reported source
905 * (A) */
906 IGMP_SOURCE_DONT_DELETE(source->source_flags);
907 } else {
908 /* if not found, create source with Group Timer:
909 * (A-X-Y)=Group Timer */
910 long group_timer_msec;
911 source = source_new(group, *src_addr);
d62a17ae 912
df5dfb77 913 assert(!source->t_source_timer); /* timer == 0 */
d62a17ae 914 group_timer_msec = igmp_group_timer_remain_msec(group);
915 igmp_source_timer_on(group, source, group_timer_msec);
df5dfb77 916 assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
d62a17ae 917
918 /* make sure source is created with DELETE flag unset */
df5dfb77 919 assert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
d62a17ae 920 }
921
922 /* make sure reported source has DELETE flag unset */
df5dfb77 923 assert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
d62a17ae 924
925 if (source->t_source_timer) {
926 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
927 IGMP_SOURCE_DO_SEND(source->source_flags);
928 ++num_sources_tosend;
929 }
930
931 } /* scan received sources (A) */
932
933 /*
934 delete all sources marked with DELETE flag:
935 Delete (X-A)
936 Delete (Y-A)
937 */
938 source_delete_by_flag(group->group_source_list);
939
940 /* send sources marked with SEND flag: Q(G,A-Y) */
941 if (num_sources_tosend > 0) {
942 source_query_send_by_flag(group, num_sources_tosend);
943 }
12e41d03
DL
944}
945
946void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
d62a17ae 947 struct in_addr group_addr, int num_sources,
948 struct in_addr *sources)
12e41d03 949{
d62a17ae 950 struct interface *ifp = igmp->interface;
951 struct igmp_group *group;
952
15569c58 953 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
d62a17ae 954
955 /* non-existant group is created as INCLUDE {empty} */
956 group = igmp_add_group_by_addr(igmp, group_addr);
957 if (!group) {
958 return;
959 }
960
961 if (group->group_filtermode_isexcl) {
962 /* EXCLUDE mode */
963 toex_excl(group, num_sources, sources);
964 } else {
965 /* INCLUDE mode */
966 toex_incl(group, num_sources, sources);
df5dfb77 967 assert(group->group_filtermode_isexcl);
d62a17ae 968 }
df5dfb77 969 assert(group->group_filtermode_isexcl);
d62a17ae 970
971 /* Group Timer=GMI */
972 igmp_group_reset_gmi(group);
12e41d03
DL
973}
974
975void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
d62a17ae 976 struct in_addr group_addr, int num_sources,
977 struct in_addr *sources)
12e41d03 978{
15569c58
DA
979 on_trace(__func__, igmp->interface, from, group_addr, num_sources,
980 sources);
12e41d03 981
d62a17ae 982 allow(igmp, from, group_addr, num_sources, sources);
12e41d03
DL
983}
984
dda4d23c
DL
985static void igmp_send_query_group(struct igmp_group *group, char *query_buf,
986 size_t query_buf_size, int num_sources,
987 int s_flag)
988{
989 struct interface *ifp = group->interface;
990 struct pim_interface *pim_ifp = ifp->info;
991 struct igmp_sock *igmp;
992 struct listnode *sock_node;
993
994 for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
a1a4295a
DL
995 igmp_send_query(
996 pim_ifp->igmp_version, group, igmp->fd, ifp->name,
997 query_buf, query_buf_size, num_sources,
998 group->group_addr, group->group_addr,
999 pim_ifp->igmp_specific_query_max_response_time_dsec,
1000 s_flag, igmp->querier_robustness_variable,
1001 igmp->querier_query_interval);
dda4d23c
DL
1002 }
1003}
1004
12e41d03
DL
1005/*
1006 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1007
1008 When transmitting a group specific query, if the group timer is
1009 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1010 in the query message.
1011*/
1012static void group_retransmit_group(struct igmp_group *group)
1013{
d62a17ae 1014 struct pim_interface *pim_ifp;
1015 long lmqc; /* Last Member Query Count */
1016 long lmqi_msec; /* Last Member Query Interval */
1017 long lmqt_msec; /* Last Member Query Time */
1018 int s_flag;
1019 int query_buf_size;
1020
dda4d23c 1021 pim_ifp = group->interface->info;
d62a17ae 1022
1023 if (pim_ifp->igmp_version == 3) {
1024 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
1025 } else {
1026 query_buf_size = IGMP_V12_MSG_SIZE;
1027 }
1028
1029 char query_buf[query_buf_size];
1030
59115451 1031 lmqc = pim_ifp->igmp_last_member_query_count;
d62a17ae 1032 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1033 lmqt_msec = lmqc * lmqi_msec;
1034
1035 /*
1036 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1037
1038 When transmitting a group specific query, if the group timer is
1039 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1040 in the query message.
1041 */
1042 s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1043
1044 if (PIM_DEBUG_IGMP_TRACE) {
1045 char group_str[INET_ADDRSTRLEN];
1046 pim_inet4_dump("<group?>", group->group_addr, group_str,
1047 sizeof(group_str));
1048 zlog_debug(
1049 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
dda4d23c 1050 group_str, group->interface->name, s_flag,
d62a17ae 1051 group->group_specific_query_retransmit_count);
1052 }
1053
1054 /*
1055 RFC3376: 4.1.12. IP Destination Addresses for Queries
1056
1057 Group-Specific and Group-and-Source-Specific Queries are sent with
1058 an IP destination address equal to the multicast address of
1059 interest.
1060 */
1061
dda4d23c 1062 igmp_send_query_group(group, query_buf, sizeof(query_buf), 0, s_flag);
12e41d03
DL
1063}
1064
1065/*
1066 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1067
1068 When building a group and source specific query for a group G, two
1069 separate query messages are sent for the group. The first one has
1070 the "Suppress Router-Side Processing" bit set and contains all the
1071 sources with retransmission state and timers greater than LMQT. The
1072 second has the "Suppress Router-Side Processing" bit clear and
1073 contains all the sources with retransmission state and timers lower
1074 or equal to LMQT. If either of the two calculated messages does not
1075 contain any sources, then its transmission is suppressed.
1076 */
1077static int group_retransmit_sources(struct igmp_group *group,
1078 int send_with_sflag_set)
1079{
d62a17ae 1080 struct pim_interface *pim_ifp;
1081 long lmqc; /* Last Member Query Count */
1082 long lmqi_msec; /* Last Member Query Interval */
1083 long lmqt_msec; /* Last Member Query Time */
1084 char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1085 char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1086 int query_buf1_max_sources;
1087 int query_buf2_max_sources;
1088 struct in_addr *source_addr1;
1089 struct in_addr *source_addr2;
1090 int num_sources_tosend1;
1091 int num_sources_tosend2;
1092 struct listnode *src_node;
1093 struct igmp_source *src;
1094 int num_retransmit_sources_left = 0;
1095
1096 source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1097 source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1098
dda4d23c 1099 pim_ifp = group->interface->info;
d62a17ae 1100
59115451 1101 lmqc = pim_ifp->igmp_last_member_query_count;
d62a17ae 1102 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1103 lmqt_msec = lmqc * lmqi_msec;
1104
1105 /* Scan all group sources */
1106 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1107
1108 /* Source has retransmission state? */
1109 if (src->source_query_retransmit_count < 1)
1110 continue;
1111
1112 if (--src->source_query_retransmit_count > 0) {
1113 ++num_retransmit_sources_left;
1114 }
1115
1116 /* Copy source address into appropriate query buffer */
1117 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1118 *source_addr1 = src->source_addr;
1119 ++source_addr1;
1120 } else {
1121 *source_addr2 = src->source_addr;
1122 ++source_addr2;
1123 }
1124 }
1125
1126 num_sources_tosend1 =
1127 source_addr1
1128 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1129 num_sources_tosend2 =
1130 source_addr2
1131 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1132
1133 if (PIM_DEBUG_IGMP_TRACE) {
1134 char group_str[INET_ADDRSTRLEN];
1135 pim_inet4_dump("<group?>", group->group_addr, group_str,
1136 sizeof(group_str));
1137 zlog_debug(
1138 "retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
dda4d23c 1139 group_str, group->interface->name, num_sources_tosend1,
d62a17ae 1140 num_sources_tosend2, send_with_sflag_set,
1141 num_retransmit_sources_left);
1142 }
1143
1144 if (num_sources_tosend1 > 0) {
1145 /*
1146 Send group-and-source-specific query with s_flag set and all
1147 sources with timers greater than LMQT.
1148 */
1149
1150 if (send_with_sflag_set) {
1151
1152 query_buf1_max_sources =
1153 (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET)
1154 >> 2;
1155 if (num_sources_tosend1 > query_buf1_max_sources) {
1156 char group_str[INET_ADDRSTRLEN];
1157 pim_inet4_dump("<group?>", group->group_addr,
1158 group_str, sizeof(group_str));
1159 zlog_warn(
1160 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
5e81f5dd 1161 __func__, group_str,
dda4d23c 1162 group->interface->name,
d62a17ae 1163 num_sources_tosend1, sizeof(query_buf1),
1164 query_buf1_max_sources);
1165 } else {
1166 /*
1167 RFC3376: 4.1.12. IP Destination Addresses for
1168 Queries
1169
1170 Group-Specific and Group-and-Source-Specific
1171 Queries are sent with
1172 an IP destination address equal to the
1173 multicast address of
1174 interest.
1175 */
1176
a1a4295a
DL
1177 igmp_send_query_group(
1178 group, query_buf1, sizeof(query_buf1),
1179 num_sources_tosend1, 1 /* s_flag */);
d62a17ae 1180 }
1181
1182 } /* send_with_sflag_set */
1183 }
1184
1185 if (num_sources_tosend2 > 0) {
1186 /*
1187 Send group-and-source-specific query with s_flag clear and all
1188 sources with timers lower or equal to LMQT.
1189 */
1190
1191 query_buf2_max_sources =
1192 (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1193 if (num_sources_tosend2 > query_buf2_max_sources) {
1194 char group_str[INET_ADDRSTRLEN];
1195 pim_inet4_dump("<group?>", group->group_addr, group_str,
1196 sizeof(group_str));
1197 zlog_warn(
1198 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
dda4d23c 1199 __func__, group_str, group->interface->name,
5e81f5dd
DS
1200 num_sources_tosend2, sizeof(query_buf2),
1201 query_buf2_max_sources);
d62a17ae 1202 } else {
1203 /*
1204 RFC3376: 4.1.12. IP Destination Addresses for Queries
1205
1206 Group-Specific and Group-and-Source-Specific Queries
1207 are sent with
1208 an IP destination address equal to the multicast
1209 address of
1210 interest.
1211 */
1212
a1a4295a
DL
1213 igmp_send_query_group(
1214 group, query_buf2, sizeof(query_buf2),
1215 num_sources_tosend2, 0 /* s_flag */);
d62a17ae 1216 }
1217 }
1218
1219 return num_retransmit_sources_left;
12e41d03
DL
1220}
1221
1222static int igmp_group_retransmit(struct thread *t)
1223{
d62a17ae 1224 struct igmp_group *group;
1225 int num_retransmit_sources_left;
1226 int send_with_sflag_set; /* boolean */
1227
1228 group = THREAD_ARG(t);
1229
1230 if (PIM_DEBUG_IGMP_TRACE) {
1231 char group_str[INET_ADDRSTRLEN];
1232 pim_inet4_dump("<group?>", group->group_addr, group_str,
1233 sizeof(group_str));
1234 zlog_debug("group_retransmit_timer: group %s on %s", group_str,
dda4d23c 1235 group->interface->name);
d62a17ae 1236 }
1237
1238 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1239 if (group->group_specific_query_retransmit_count > 0) {
1240
1241 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1242 group_retransmit_group(group);
1243 --group->group_specific_query_retransmit_count;
1244
1245 /*
1246 RFC3376: 6.6.3.2
1247 If a group specific query is scheduled to be transmitted at
1248 the
1249 same time as a group and source specific query for the same
1250 group,
1251 then transmission of the group and source specific message
1252 with the
1253 "Suppress Router-Side Processing" bit set may be suppressed.
1254 */
1255 send_with_sflag_set = 0; /* boolean=false */
1256 } else {
1257 send_with_sflag_set = 1; /* boolean=true */
1258 }
1259
1260 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1261 num_retransmit_sources_left =
1262 group_retransmit_sources(group, send_with_sflag_set);
1263
1264 /*
1265 Keep group retransmit timer running if there is any retransmit
1266 counter pending
1267 */
1268 if ((num_retransmit_sources_left > 0)
1269 || (group->group_specific_query_retransmit_count > 0)) {
1270 group_retransmit_timer_on(group);
1271 }
1272
1273 return 0;
12e41d03
DL
1274}
1275
1276/*
1277 group_retransmit_timer_on:
1278 if group retransmit timer isn't running, starts it;
1279 otherwise, do nothing
1280*/
1281static void group_retransmit_timer_on(struct igmp_group *group)
1282{
d62a17ae 1283 struct pim_interface *pim_ifp;
1284 long lmqi_msec; /* Last Member Query Interval */
1285
1286 /* if group retransmit timer is running, do nothing */
1287 if (group->t_group_query_retransmit_timer) {
1288 return;
1289 }
1290
dda4d23c 1291 pim_ifp = group->interface->info;
d62a17ae 1292
1293 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1294
1295 if (PIM_DEBUG_IGMP_TRACE) {
1296 char group_str[INET_ADDRSTRLEN];
1297 pim_inet4_dump("<group?>", group->group_addr, group_str,
1298 sizeof(group_str));
1299 zlog_debug(
1300 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1301 lmqi_msec / 1000, lmqi_msec % 1000, group_str,
dda4d23c 1302 group->interface->name);
d62a17ae 1303 }
1304
36417fcc
DS
1305 thread_add_timer_msec(router->master, igmp_group_retransmit, group,
1306 lmqi_msec,
d62a17ae 1307 &group->t_group_query_retransmit_timer);
12e41d03
DL
1308}
1309
1310static long igmp_group_timer_remain_msec(struct igmp_group *group)
1311{
d62a17ae 1312 return pim_time_timer_remain_msec(group->t_group_timer);
12e41d03
DL
1313}
1314
1315static long igmp_source_timer_remain_msec(struct igmp_source *source)
1316{
d62a17ae 1317 return pim_time_timer_remain_msec(source->t_source_timer);
12e41d03
DL
1318}
1319
1320/*
1321 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1322*/
1323static void group_query_send(struct igmp_group *group)
1324{
59115451 1325 struct pim_interface *pim_ifp;
d62a17ae 1326 long lmqc; /* Last Member Query Count */
12e41d03 1327
dda4d23c 1328 pim_ifp = group->interface->info;
59115451 1329 lmqc = pim_ifp->igmp_last_member_query_count;
12e41d03 1330
d62a17ae 1331 /* lower group timer to lmqt */
1332 igmp_group_timer_lower_to_lmqt(group);
12e41d03 1333
d62a17ae 1334 /* reset retransmission counter */
1335 group->group_specific_query_retransmit_count = lmqc;
12e41d03 1336
d62a17ae 1337 /* immediately send group specific query (decrease retransmit counter by
1338 * 1)*/
1339 group_retransmit_group(group);
12e41d03 1340
d62a17ae 1341 /* make sure group retransmit timer is running */
1342 group_retransmit_timer_on(group);
12e41d03
DL
1343}
1344
1345/*
1346 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1347*/
1348static void source_query_send_by_flag(struct igmp_group *group,
1349 int num_sources_tosend)
1350{
d62a17ae 1351 struct pim_interface *pim_ifp;
1352 struct listnode *src_node;
1353 struct igmp_source *src;
1354 long lmqc; /* Last Member Query Count */
1355 long lmqi_msec; /* Last Member Query Interval */
1356 long lmqt_msec; /* Last Member Query Time */
1357
df5dfb77 1358 assert(num_sources_tosend > 0);
d62a17ae 1359
dda4d23c 1360 pim_ifp = group->interface->info;
d62a17ae 1361
59115451 1362 lmqc = pim_ifp->igmp_last_member_query_count;
d62a17ae 1363 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1364 lmqt_msec = lmqc * lmqi_msec;
1365
1366 /*
1367 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1368 Queries
1369
1370 (...) for each of the sources in X of group G, with source timer
1371 larger
1372 than LMQT:
1373 o Set number of retransmissions for each source to [Last Member
1374 Query Count].
1375 o Lower source timer to LMQT.
1376 */
1377 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1378 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1379 /* source "src" in X of group G */
1380 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1381 src->source_query_retransmit_count = lmqc;
1382 igmp_source_timer_lower_to_lmqt(src);
1383 }
1384 }
1385 }
1386
1387 /* send group-and-source specific queries */
1388 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1389
1390 /* make sure group retransmit timer is running */
1391 group_retransmit_timer_on(group);
12e41d03
DL
1392}
1393
d62a17ae 1394static void block_excl(struct igmp_group *group, int num_sources,
1395 struct in_addr *sources)
12e41d03 1396{
d62a17ae 1397 int num_sources_tosend = 0;
1398 int i;
1399
1400 /* 1. clear off SEND flag from all known sources (X,Y) */
1401 source_clear_send_flag(group->group_source_list);
1402
1403 /* 2. scan received sources (A) */
1404 for (i = 0; i < num_sources; ++i) {
1405 struct igmp_source *source;
1406 struct in_addr *src_addr;
1407
1408 src_addr = sources + i;
1409
1410 /* lookup reported source (A) in known sources (X,Y) */
1411 source = igmp_find_source_by_addr(group, *src_addr);
1412 if (!source) {
1413 /* 3: if not found, create source with Group Timer:
1414 * (A-X-Y)=Group Timer */
1415 long group_timer_msec;
1416 source = source_new(group, *src_addr);
d62a17ae 1417
df5dfb77 1418 assert(!source->t_source_timer); /* timer == 0 */
d62a17ae 1419 group_timer_msec = igmp_group_timer_remain_msec(group);
1420 igmp_source_timer_on(group, source, group_timer_msec);
df5dfb77 1421 assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
d62a17ae 1422 }
1423
1424 if (source->t_source_timer) {
1425 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1426 IGMP_SOURCE_DO_SEND(source->source_flags);
1427 ++num_sources_tosend;
1428 }
1429 }
1430
1431 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1432 if (num_sources_tosend > 0) {
1433 source_query_send_by_flag(group, num_sources_tosend);
1434 }
12e41d03
DL
1435}
1436
d62a17ae 1437static void block_incl(struct igmp_group *group, int num_sources,
1438 struct in_addr *sources)
12e41d03 1439{
d62a17ae 1440 int num_sources_tosend = 0;
1441 int i;
1442
1443 /* 1. clear off SEND flag from all known sources (B) */
1444 source_clear_send_flag(group->group_source_list);
1445
1446 /* 2. scan received sources (A) */
1447 for (i = 0; i < num_sources; ++i) {
1448 struct igmp_source *source;
1449 struct in_addr *src_addr;
1450
1451 src_addr = sources + i;
1452
1453 /* lookup reported source (A) in known sources (B) */
1454 source = igmp_find_source_by_addr(group, *src_addr);
1455 if (source) {
1456 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1457 IGMP_SOURCE_DO_SEND(source->source_flags);
1458 ++num_sources_tosend;
1459 }
1460 }
1461
1462 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1463 if (num_sources_tosend > 0) {
1464 source_query_send_by_flag(group, num_sources_tosend);
1465 }
12e41d03
DL
1466}
1467
1468void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
d62a17ae 1469 struct in_addr group_addr, int num_sources,
1470 struct in_addr *sources)
12e41d03 1471{
d62a17ae 1472 struct interface *ifp = igmp->interface;
1473 struct igmp_group *group;
1474
15569c58 1475 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
d62a17ae 1476
1477 /* non-existant group is created as INCLUDE {empty} */
1478 group = igmp_add_group_by_addr(igmp, group_addr);
1479 if (!group) {
1480 return;
1481 }
1482
1483 if (group->group_filtermode_isexcl) {
1484 /* EXCLUDE mode */
1485 block_excl(group, num_sources, sources);
1486 } else {
1487 /* INCLUDE mode */
1488 block_incl(group, num_sources, sources);
1489 }
12e41d03
DL
1490}
1491
1492void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
1493{
d62a17ae 1494 struct interface *ifp;
1495 struct pim_interface *pim_ifp;
1496 char *ifname;
1497 int lmqi_dsec; /* Last Member Query Interval */
1498 int lmqc; /* Last Member Query Count */
1499 int lmqt_msec; /* Last Member Query Time */
1500
1501 /*
1502 RFC 3376: 6.2.2. Definition of Group Timers
1503
1504 The group timer is only used when a group is in EXCLUDE mode and
1505 it represents the time for the *filter-mode* of the group to
1506 expire and switch to INCLUDE mode.
1507 */
1508 if (!group->group_filtermode_isexcl) {
1509 return;
1510 }
1511
dda4d23c 1512 ifp = group->interface;
d62a17ae 1513 pim_ifp = ifp->info;
1514 ifname = ifp->name;
1515
1516 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
59115451 1517 lmqc = pim_ifp->igmp_last_member_query_count;
d62a17ae 1518 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1519 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1520
1521 if (PIM_DEBUG_IGMP_TRACE) {
1522 char group_str[INET_ADDRSTRLEN];
1523 pim_inet4_dump("<group?>", group->group_addr, group_str,
1524 sizeof(group_str));
1525 zlog_debug(
1526 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
15569c58 1527 __func__, group_str, ifname, lmqc, lmqi_dsec,
d62a17ae 1528 lmqt_msec);
1529 }
1530
df5dfb77 1531 assert(group->group_filtermode_isexcl);
d62a17ae 1532
1533 igmp_group_timer_on(group, lmqt_msec, ifname);
12e41d03
DL
1534}
1535
1536void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
1537{
d62a17ae 1538 struct igmp_group *group;
d62a17ae 1539 struct interface *ifp;
1540 struct pim_interface *pim_ifp;
1541 char *ifname;
1542 int lmqi_dsec; /* Last Member Query Interval */
1543 int lmqc; /* Last Member Query Count */
1544 int lmqt_msec; /* Last Member Query Time */
1545
1546 group = source->source_group;
dda4d23c 1547 ifp = group->interface;
d62a17ae 1548 pim_ifp = ifp->info;
1549 ifname = ifp->name;
1550
1551 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
59115451 1552 lmqc = pim_ifp->igmp_last_member_query_count;
d62a17ae 1553 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1554 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1555
1556 if (PIM_DEBUG_IGMP_TRACE) {
1557 char group_str[INET_ADDRSTRLEN];
1558 char source_str[INET_ADDRSTRLEN];
1559 pim_inet4_dump("<group?>", group->group_addr, group_str,
1560 sizeof(group_str));
1561 pim_inet4_dump("<source?>", source->source_addr, source_str,
1562 sizeof(source_str));
1563 zlog_debug(
1564 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
15569c58
DA
1565 __func__, group_str, source_str, ifname, lmqc,
1566 lmqi_dsec, lmqt_msec);
d62a17ae 1567 }
1568
1569 igmp_source_timer_on(group, source, lmqt_msec);
12e41d03
DL
1570}
1571
d62a17ae 1572void igmp_v3_send_query(struct igmp_group *group, int fd, const char *ifname,
1573 char *query_buf, int query_buf_size, int num_sources,
1574 struct in_addr dst_addr, struct in_addr group_addr,
1575 int query_max_response_time_dsec, uint8_t s_flag,
1576 uint8_t querier_robustness_variable,
1577 uint16_t querier_query_interval)
12e41d03 1578{
d62a17ae 1579 ssize_t msg_size;
1580 uint8_t max_resp_code;
1581 uint8_t qqic;
1582 ssize_t sent;
1583 struct sockaddr_in to;
1584 socklen_t tolen;
1585 uint16_t checksum;
1586
df5dfb77 1587 assert(num_sources >= 0);
d62a17ae 1588
1589 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1590 if (msg_size > query_buf_size) {
af4c2728 1591 flog_err(
450971aa 1592 EC_LIB_DEVELOPMENT,
d62a17ae 1593 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
15569c58 1594 __FILE__, __func__, msg_size, query_buf_size);
d62a17ae 1595 return;
1596 }
1597
1598 s_flag = PIM_FORCE_BOOLEAN(s_flag);
df5dfb77 1599 assert((s_flag == 0) || (s_flag == 1));
d62a17ae 1600
1601 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1602 qqic = igmp_msg_encode16to8(querier_query_interval);
1603
1604 /*
1605 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1606
1607 If non-zero, the QRV field contains the [Robustness Variable]
1608 value used by the querier, i.e., the sender of the Query. If the
1609 querier's [Robustness Variable] exceeds 7, the maximum value of
1610 the QRV field, the QRV is set to zero.
1611 */
1612 if (querier_robustness_variable > 7) {
1613 querier_robustness_variable = 0;
1614 }
1615
1616 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1617 query_buf[1] = max_resp_code;
1618 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) =
1619 0; /* for computing checksum */
1620 memcpy(query_buf + 4, &group_addr, sizeof(struct in_addr));
1621
1622 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1623 query_buf[9] = qqic;
1624 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) =
1625 htons(num_sources);
1626
1627 checksum = in_cksum(query_buf, msg_size);
1628 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
1629
1630 if (PIM_DEBUG_IGMP_PACKETS) {
1631 char dst_str[INET_ADDRSTRLEN];
1632 char group_str[INET_ADDRSTRLEN];
1633 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1634 pim_inet4_dump("<group?>", group_addr, group_str,
1635 sizeof(group_str));
1636 zlog_debug(
1637 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1638 dst_str, ifname, group_str, num_sources, msg_size,
1639 s_flag, querier_robustness_variable,
1640 querier_query_interval, qqic);
1641 }
1642
1643 memset(&to, 0, sizeof(to));
1644 to.sin_family = AF_INET;
1645 to.sin_addr = dst_addr;
1646 tolen = sizeof(to);
1647
1648 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1649 (struct sockaddr *)&to, tolen);
1650 if (sent != (ssize_t)msg_size) {
1651 char dst_str[INET_ADDRSTRLEN];
1652 char group_str[INET_ADDRSTRLEN];
1653 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1654 pim_inet4_dump("<group?>", group_addr, group_str,
1655 sizeof(group_str));
1656 if (sent < 0) {
1657 zlog_warn(
1658 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1659 dst_str, ifname, group_str, msg_size, errno,
1660 safe_strerror(errno));
1661 } else {
1662 zlog_warn(
1663 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1664 dst_str, ifname, group_str, msg_size, sent);
1665 }
1666 return;
1667 }
1668
1669 /*
1670 s_flag sanity test: s_flag must be set for general queries
1671
1672 RFC 3376: 6.6.1. Timer Updates
1673
1674 When a router sends or receives a query with a clear Suppress
1675 Router-Side Processing flag, it must update its timers to reflect
1676 the correct timeout values for the group or sources being queried.
1677
1678 General queries don't trigger timer update.
1679 */
1680 if (!s_flag) {
1681 /* general query? */
1682 if (PIM_INADDR_IS_ANY(group_addr)) {
1683 char dst_str[INET_ADDRSTRLEN];
1684 char group_str[INET_ADDRSTRLEN];
1685 pim_inet4_dump("<dst?>", dst_addr, dst_str,
1686 sizeof(dst_str));
1687 pim_inet4_dump("<group?>", group_addr, group_str,
1688 sizeof(group_str));
1689 zlog_warn(
1690 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
15569c58 1691 __func__, dst_str, ifname, group_str,
d62a17ae 1692 num_sources);
1693 }
1694 }
b05b72e8
DW
1695}
1696
d62a17ae 1697void igmp_v3_recv_query(struct igmp_sock *igmp, const char *from_str,
1698 char *igmp_msg)
b05b72e8 1699{
d62a17ae 1700 struct interface *ifp;
1701 struct pim_interface *pim_ifp;
1702 struct in_addr group_addr;
1703 uint8_t resv_s_qrv = 0;
1704 uint8_t s_flag = 0;
1705 uint8_t qrv = 0;
1706 int i;
1707
1708 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
1709 ifp = igmp->interface;
1710 pim_ifp = ifp->info;
1711
1712 /*
1713 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1714 *
1715 * Routers adopt the QRV value from the most recently received Query
1716 * as their own [Robustness Variable] value, unless that most
1717 * recently received QRV was zero, in which case the receivers use
1718 * the default [Robustness Variable] value specified in section 8.1
1719 * or a statically configured value.
1720 */
1721 resv_s_qrv = igmp_msg[8];
1722 qrv = 7 & resv_s_qrv;
1723 igmp->querier_robustness_variable =
1724 qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
1725
1726 /*
1727 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1728 *
1729 * Multicast routers that are not the current querier adopt the QQI
1730 * value from the most recently received Query as their own [Query
1731 * Interval] value, unless that most recently received QQI was zero,
1732 * in which case the receiving routers use the default.
1733 */
1734 if (igmp->t_other_querier_timer) {
1735 /* other querier present */
1736 uint8_t qqic;
1737 uint16_t qqi;
1738 qqic = igmp_msg[9];
1739 qqi = igmp_msg_decode8to16(qqic);
1740 igmp->querier_query_interval =
1741 qqi ? qqi : pim_ifp->igmp_default_query_interval;
1742
1743 if (PIM_DEBUG_IGMP_TRACE) {
1744 char ifaddr_str[INET_ADDRSTRLEN];
1745 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
1746 sizeof(ifaddr_str));
1747 zlog_debug(
1748 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1749 ifaddr_str,
1750 qqi ? "recv-non-default" : "default",
1751 igmp->querier_query_interval, qqic, from_str);
1752 }
1753 }
1754
1755 /*
1756 * RFC 3376: 6.6.1. Timer Updates
1757 *
1758 * When a router sends or receives a query with a clear Suppress
1759 * Router-Side Processing flag, it must update its timers to reflect
1760 * the correct timeout values for the group or sources being queried.
1761 *
1762 * General queries don't trigger timer update.
1763 */
1764 s_flag = (1 << 3) & resv_s_qrv;
1765
1766 if (!s_flag) {
1767 /* s_flag is clear */
1768
1769 if (PIM_INADDR_IS_ANY(group_addr)) {
1770 /* this is a general query */
1771 /* log that general query should have the s_flag set */
1772 zlog_warn(
1773 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1774 from_str, ifp->name);
1775 } else {
1776 struct igmp_group *group;
1777
1778 /* this is a non-general query: perform timer updates */
1779
1780 group = find_group_by_addr(igmp, group_addr);
1781 if (group) {
1782 int recv_num_sources = ntohs(*(
1783 uint16_t
1784 *)(igmp_msg
1785 + IGMP_V3_NUMSOURCES_OFFSET));
1786
1787 /*
1788 * RFC 3376: 6.6.1. Timer Updates
1789 * Query Q(G,A): Source Timer for sources in A
1790 * are lowered to LMQT
1791 * Query Q(G): Group Timer is lowered to LMQT
1792 */
1793 if (recv_num_sources < 1) {
1794 /* Query Q(G): Group Timer is lowered to
1795 * LMQT */
1796
1797 igmp_group_timer_lower_to_lmqt(group);
1798 } else {
1799 /* Query Q(G,A): Source Timer for
1800 * sources in A are lowered to LMQT */
1801
1802 /* Scan sources in query and lower their
1803 * timers to LMQT */
1804 struct in_addr *sources =
1805 (struct in_addr
1806 *)(igmp_msg
1807 + IGMP_V3_SOURCES_OFFSET);
1808 for (i = 0; i < recv_num_sources; ++i) {
1809 struct in_addr src_addr;
1810 struct igmp_source *src;
1811 memcpy(&src_addr, sources + i,
1812 sizeof(struct in_addr));
1813 src = igmp_find_source_by_addr(
1814 group, src_addr);
1815 if (src) {
1816 igmp_source_timer_lower_to_lmqt(
1817 src);
1818 }
1819 }
1820 }
1821 } else {
1822 char group_str[INET_ADDRSTRLEN];
1823 pim_inet4_dump("<group?>", group_addr,
1824 group_str, sizeof(group_str));
1825 zlog_warn(
1826 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1827 from_str, ifp->name, group_str);
1828 }
1829 }
1830 } /* s_flag is clear: timer updates */
b05b72e8
DW
1831}
1832
d62a17ae 1833int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from,
1834 const char *from_str, char *igmp_msg, int igmp_msg_len)
b05b72e8 1835{
d62a17ae 1836 int num_groups;
1837 uint8_t *group_record;
1838 uint8_t *report_pastend = (uint8_t *)igmp_msg + igmp_msg_len;
1839 struct interface *ifp = igmp->interface;
1840 int i;
1841 int local_ncb = 0;
b0f525a8
QY
1842 struct pim_interface *pim_ifp;
1843
f83f3966
MS
1844 if (igmp->mtrace_only)
1845 return 0;
1846
b0f525a8 1847 pim_ifp = igmp->interface->info;
d62a17ae 1848
1849 if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
1850 zlog_warn(
1851 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1852 from_str, ifp->name, igmp_msg_len,
1853 IGMP_V3_MSG_MIN_SIZE);
1854 return -1;
1855 }
1856
9041c30a 1857 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
d62a17ae 1858 zlog_warn(
9041c30a
MR
1859 "Recv IGMPv3 report from %s on %s with invalid checksum",
1860 from_str, ifp->name);
d62a17ae 1861 return -1;
1862 }
1863
21313cbf
MS
1864 /* Collecting IGMP Rx stats */
1865 igmp->rx_stats.report_v3++;
1866
d62a17ae 1867 num_groups = ntohs(
1868 *(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
1869 if (num_groups < 1) {
1870 zlog_warn(
1871 "Recv IGMP report v3 from %s on %s: missing group records",
1872 from_str, ifp->name);
1873 return -1;
1874 }
1875
1876 if (PIM_DEBUG_IGMP_PACKETS) {
1877 zlog_debug(
9041c30a
MR
1878 "Recv IGMP report v3 from %s on %s: size=%d groups=%d",
1879 from_str, ifp->name, igmp_msg_len, num_groups);
d62a17ae 1880 }
1881
1882 group_record = (uint8_t *)igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
1883
1884 /* Scan groups */
1885 for (i = 0; i < num_groups; ++i) {
1886 struct in_addr rec_group;
1887 uint8_t *sources;
1888 uint8_t *src;
1889 int rec_type;
1890 int rec_auxdatalen;
1891 int rec_num_sources;
1892 int j;
1893 struct prefix lncb;
1894 struct prefix g;
b0f525a8 1895 bool filtered = false;
d62a17ae 1896
1897 if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE)
1898 > report_pastend) {
1899 zlog_warn(
1900 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1901 from_str, ifp->name);
1902 return -1;
1903 }
1904
1905 rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
1906 rec_auxdatalen =
1907 group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
1908 rec_num_sources = ntohs(*(
1909 uint16_t *)(group_record
1910 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
1911
1912 memcpy(&rec_group,
1913 group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET,
1914 sizeof(struct in_addr));
1915
1916 if (PIM_DEBUG_IGMP_PACKETS) {
1917 zlog_debug(
ee2bbf7c 1918 " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4",
d62a17ae 1919 from_str, ifp->name, i, rec_type,
1920 rec_auxdatalen, rec_num_sources,
ee2bbf7c 1921 &rec_group);
d62a17ae 1922 }
1923
1924 /* Scan sources */
1925
1926 sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
1927
1928 for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
1929
1930 if ((src + 4) > report_pastend) {
1931 zlog_warn(
1932 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1933 from_str, ifp->name);
1934 return -1;
1935 }
1936
1937 if (PIM_DEBUG_IGMP_PACKETS) {
1938 char src_str[200];
1939
1940 if (!inet_ntop(AF_INET, src, src_str,
1941 sizeof(src_str)))
772270f3
QY
1942 snprintf(src_str, sizeof(src_str),
1943 "<source?>");
d62a17ae 1944
1945 zlog_debug(
ee2bbf7c 1946 " Recv IGMP report v3 from %s on %s: record=%d group=%pI4 source=%s",
d62a17ae 1947 from_str, ifp->name, i,
ee2bbf7c 1948 &rec_group, src_str);
d62a17ae 1949 }
1950 } /* for (sources) */
1951
1952
1953 lncb.family = AF_INET;
1954 lncb.u.prefix4.s_addr = 0x000000E0;
1955 lncb.prefixlen = 24;
1956
1957 g.family = AF_INET;
1958 g.u.prefix4 = rec_group;
12256b84 1959 g.prefixlen = IPV4_MAX_BITLEN;
b0f525a8
QY
1960
1961 /* determine filtering status for group */
1962 filtered = pim_is_group_filtered(ifp->info, &rec_group);
1963
1964 if (PIM_DEBUG_IGMP_PACKETS && filtered)
1965 zlog_debug(
ee2bbf7c
MS
1966 "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
1967 &rec_group, from_str, ifp->name,
b0f525a8
QY
1968 pim_ifp->boundary_oil_plist);
1969
d62a17ae 1970 /*
1971 * If we receive a igmp report with the group in 224.0.0.0/24
1972 * then we should ignore it
1973 */
1974 if (prefix_match(&lncb, &g))
1975 local_ncb = 1;
1976
b0f525a8 1977 if (!local_ncb && !filtered)
d62a17ae 1978 switch (rec_type) {
1979 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
1980 igmpv3_report_isin(igmp, from, rec_group,
1981 rec_num_sources,
1982 (struct in_addr *)sources);
1983 break;
1984 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
1985 igmpv3_report_isex(
1986 igmp, from, rec_group, rec_num_sources,
1987 (struct in_addr *)sources, 0);
1988 break;
1989 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
1990 igmpv3_report_toin(igmp, from, rec_group,
1991 rec_num_sources,
1992 (struct in_addr *)sources);
1993 break;
1994 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
1995 igmpv3_report_toex(igmp, from, rec_group,
1996 rec_num_sources,
1997 (struct in_addr *)sources);
1998 break;
1999 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
2000 igmpv3_report_allow(igmp, from, rec_group,
2001 rec_num_sources,
2002 (struct in_addr *)sources);
2003 break;
2004 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
2005 igmpv3_report_block(igmp, from, rec_group,
2006 rec_num_sources,
2007 (struct in_addr *)sources);
2008 break;
2009 default:
2010 zlog_warn(
2011 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2012 from_str, ifp->name, rec_type);
2013 }
2014
2015 group_record +=
2016 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
2017 local_ncb = 0;
2018
2019 } /* for (group records) */
2020
2021 return 0;
12e41d03 2022}