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