]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmpv3.c
pimd: Display ifp name in pim_channel_oil_dump
[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
36417fcc
DS
217 thread_add_timer_msec(router->master, igmp_source_timer, source,
218 interval_msec, &source->t_source_timer);
d62a17ae 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
59115451 1000 lmqc = pim_ifp->igmp_last_member_query_count;
d62a17ae 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
59115451 1079 lmqc = pim_ifp->igmp_last_member_query_count;
d62a17ae 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
36417fcc
DS
1297 thread_add_timer_msec(router->master, igmp_group_retransmit, group,
1298 lmqi_msec,
d62a17ae 1299 &group->t_group_query_retransmit_timer);
12e41d03
DL
1300}
1301
1302static long igmp_group_timer_remain_msec(struct igmp_group *group)
1303{
d62a17ae 1304 return pim_time_timer_remain_msec(group->t_group_timer);
12e41d03
DL
1305}
1306
1307static long igmp_source_timer_remain_msec(struct igmp_source *source)
1308{
d62a17ae 1309 return pim_time_timer_remain_msec(source->t_source_timer);
12e41d03
DL
1310}
1311
1312/*
1313 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1314*/
1315static void group_query_send(struct igmp_group *group)
1316{
59115451
SP
1317 struct pim_interface *pim_ifp;
1318 struct igmp_sock *igmp;
d62a17ae 1319 long lmqc; /* Last Member Query Count */
12e41d03 1320
59115451
SP
1321 igmp = group->group_igmp_sock;
1322 pim_ifp = igmp->interface->info;
1323 lmqc = pim_ifp->igmp_last_member_query_count;
12e41d03 1324
d62a17ae 1325 /* lower group timer to lmqt */
1326 igmp_group_timer_lower_to_lmqt(group);
12e41d03 1327
d62a17ae 1328 /* reset retransmission counter */
1329 group->group_specific_query_retransmit_count = lmqc;
12e41d03 1330
d62a17ae 1331 /* immediately send group specific query (decrease retransmit counter by
1332 * 1)*/
1333 group_retransmit_group(group);
12e41d03 1334
d62a17ae 1335 /* make sure group retransmit timer is running */
1336 group_retransmit_timer_on(group);
12e41d03
DL
1337}
1338
1339/*
1340 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1341*/
1342static void source_query_send_by_flag(struct igmp_group *group,
1343 int num_sources_tosend)
1344{
d62a17ae 1345 struct igmp_sock *igmp;
1346 struct pim_interface *pim_ifp;
1347 struct listnode *src_node;
1348 struct igmp_source *src;
1349 long lmqc; /* Last Member Query Count */
1350 long lmqi_msec; /* Last Member Query Interval */
1351 long lmqt_msec; /* Last Member Query Time */
1352
1353 zassert(num_sources_tosend > 0);
1354
1355 igmp = group->group_igmp_sock;
1356 pim_ifp = igmp->interface->info;
1357
59115451 1358 lmqc = pim_ifp->igmp_last_member_query_count;
d62a17ae 1359 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1360 lmqt_msec = lmqc * lmqi_msec;
1361
1362 /*
1363 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1364 Queries
1365
1366 (...) for each of the sources in X of group G, with source timer
1367 larger
1368 than LMQT:
1369 o Set number of retransmissions for each source to [Last Member
1370 Query Count].
1371 o Lower source timer to LMQT.
1372 */
1373 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1374 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1375 /* source "src" in X of group G */
1376 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1377 src->source_query_retransmit_count = lmqc;
1378 igmp_source_timer_lower_to_lmqt(src);
1379 }
1380 }
1381 }
1382
1383 /* send group-and-source specific queries */
1384 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1385
1386 /* make sure group retransmit timer is running */
1387 group_retransmit_timer_on(group);
12e41d03
DL
1388}
1389
d62a17ae 1390static void block_excl(struct igmp_group *group, int num_sources,
1391 struct in_addr *sources)
12e41d03 1392{
d62a17ae 1393 int num_sources_tosend = 0;
1394 int i;
1395
1396 /* 1. clear off SEND flag from all known sources (X,Y) */
1397 source_clear_send_flag(group->group_source_list);
1398
1399 /* 2. scan received sources (A) */
1400 for (i = 0; i < num_sources; ++i) {
1401 struct igmp_source *source;
1402 struct in_addr *src_addr;
1403
1404 src_addr = sources + i;
1405
1406 /* lookup reported source (A) in known sources (X,Y) */
1407 source = igmp_find_source_by_addr(group, *src_addr);
1408 if (!source) {
1409 /* 3: if not found, create source with Group Timer:
1410 * (A-X-Y)=Group Timer */
1411 long group_timer_msec;
1412 source = source_new(group, *src_addr);
d62a17ae 1413
1414 zassert(!source->t_source_timer); /* timer == 0 */
1415 group_timer_msec = igmp_group_timer_remain_msec(group);
1416 igmp_source_timer_on(group, source, group_timer_msec);
1417 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1418 }
1419
1420 if (source->t_source_timer) {
1421 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1422 IGMP_SOURCE_DO_SEND(source->source_flags);
1423 ++num_sources_tosend;
1424 }
1425 }
1426
1427 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1428 if (num_sources_tosend > 0) {
1429 source_query_send_by_flag(group, num_sources_tosend);
1430 }
12e41d03
DL
1431}
1432
d62a17ae 1433static void block_incl(struct igmp_group *group, int num_sources,
1434 struct in_addr *sources)
12e41d03 1435{
d62a17ae 1436 int num_sources_tosend = 0;
1437 int i;
1438
1439 /* 1. clear off SEND flag from all known sources (B) */
1440 source_clear_send_flag(group->group_source_list);
1441
1442 /* 2. scan received sources (A) */
1443 for (i = 0; i < num_sources; ++i) {
1444 struct igmp_source *source;
1445 struct in_addr *src_addr;
1446
1447 src_addr = sources + i;
1448
1449 /* lookup reported source (A) in known sources (B) */
1450 source = igmp_find_source_by_addr(group, *src_addr);
1451 if (source) {
1452 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1453 IGMP_SOURCE_DO_SEND(source->source_flags);
1454 ++num_sources_tosend;
1455 }
1456 }
1457
1458 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1459 if (num_sources_tosend > 0) {
1460 source_query_send_by_flag(group, num_sources_tosend);
1461 }
12e41d03
DL
1462}
1463
1464void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
d62a17ae 1465 struct in_addr group_addr, int num_sources,
1466 struct in_addr *sources)
12e41d03 1467{
d62a17ae 1468 struct interface *ifp = igmp->interface;
1469 struct igmp_group *group;
1470
1471 on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources,
1472 sources);
1473
1474 /* non-existant group is created as INCLUDE {empty} */
1475 group = igmp_add_group_by_addr(igmp, group_addr);
1476 if (!group) {
1477 return;
1478 }
1479
1480 if (group->group_filtermode_isexcl) {
1481 /* EXCLUDE mode */
1482 block_excl(group, num_sources, sources);
1483 } else {
1484 /* INCLUDE mode */
1485 block_incl(group, num_sources, sources);
1486 }
12e41d03
DL
1487}
1488
1489void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
1490{
d62a17ae 1491 struct igmp_sock *igmp;
1492 struct interface *ifp;
1493 struct pim_interface *pim_ifp;
1494 char *ifname;
1495 int lmqi_dsec; /* Last Member Query Interval */
1496 int lmqc; /* Last Member Query Count */
1497 int lmqt_msec; /* Last Member Query Time */
1498
1499 /*
1500 RFC 3376: 6.2.2. Definition of Group Timers
1501
1502 The group timer is only used when a group is in EXCLUDE mode and
1503 it represents the time for the *filter-mode* of the group to
1504 expire and switch to INCLUDE mode.
1505 */
1506 if (!group->group_filtermode_isexcl) {
1507 return;
1508 }
1509
1510 igmp = group->group_igmp_sock;
1511 ifp = igmp->interface;
1512 pim_ifp = ifp->info;
1513 ifname = ifp->name;
1514
1515 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
59115451 1516 lmqc = pim_ifp->igmp_last_member_query_count;
d62a17ae 1517 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1518 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1519
1520 if (PIM_DEBUG_IGMP_TRACE) {
1521 char group_str[INET_ADDRSTRLEN];
1522 pim_inet4_dump("<group?>", group->group_addr, group_str,
1523 sizeof(group_str));
1524 zlog_debug(
1525 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1526 __PRETTY_FUNCTION__, group_str, ifname, lmqc, lmqi_dsec,
1527 lmqt_msec);
1528 }
1529
1530 zassert(group->group_filtermode_isexcl);
1531
1532 igmp_group_timer_on(group, lmqt_msec, ifname);
12e41d03
DL
1533}
1534
1535void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
1536{
d62a17ae 1537 struct igmp_group *group;
1538 struct igmp_sock *igmp;
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;
1547 igmp = group->group_igmp_sock;
1548 ifp = igmp->interface;
1549 pim_ifp = ifp->info;
1550 ifname = ifp->name;
1551
1552 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
59115451 1553 lmqc = pim_ifp->igmp_last_member_query_count;
d62a17ae 1554 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1555 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1556
1557 if (PIM_DEBUG_IGMP_TRACE) {
1558 char group_str[INET_ADDRSTRLEN];
1559 char source_str[INET_ADDRSTRLEN];
1560 pim_inet4_dump("<group?>", group->group_addr, group_str,
1561 sizeof(group_str));
1562 pim_inet4_dump("<source?>", source->source_addr, source_str,
1563 sizeof(source_str));
1564 zlog_debug(
1565 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1566 __PRETTY_FUNCTION__, group_str, source_str, ifname,
1567 lmqc, lmqi_dsec, lmqt_msec);
1568 }
1569
1570 igmp_source_timer_on(group, source, lmqt_msec);
12e41d03
DL
1571}
1572
d62a17ae 1573void igmp_v3_send_query(struct igmp_group *group, int fd, const char *ifname,
1574 char *query_buf, int query_buf_size, int num_sources,
1575 struct in_addr dst_addr, struct in_addr group_addr,
1576 int query_max_response_time_dsec, uint8_t s_flag,
1577 uint8_t querier_robustness_variable,
1578 uint16_t querier_query_interval)
12e41d03 1579{
d62a17ae 1580 ssize_t msg_size;
1581 uint8_t max_resp_code;
1582 uint8_t qqic;
1583 ssize_t sent;
1584 struct sockaddr_in to;
1585 socklen_t tolen;
1586 uint16_t checksum;
1587
1588 zassert(num_sources >= 0);
1589
1590 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1591 if (msg_size > query_buf_size) {
af4c2728 1592 flog_err(
450971aa 1593 EC_LIB_DEVELOPMENT,
d62a17ae 1594 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1595 __FILE__, __PRETTY_FUNCTION__, msg_size,
1596 query_buf_size);
1597 return;
1598 }
1599
1600 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1601 zassert((s_flag == 0) || (s_flag == 1));
1602
1603 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1604 qqic = igmp_msg_encode16to8(querier_query_interval);
1605
1606 /*
1607 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1608
1609 If non-zero, the QRV field contains the [Robustness Variable]
1610 value used by the querier, i.e., the sender of the Query. If the
1611 querier's [Robustness Variable] exceeds 7, the maximum value of
1612 the QRV field, the QRV is set to zero.
1613 */
1614 if (querier_robustness_variable > 7) {
1615 querier_robustness_variable = 0;
1616 }
1617
1618 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1619 query_buf[1] = max_resp_code;
1620 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) =
1621 0; /* for computing checksum */
1622 memcpy(query_buf + 4, &group_addr, sizeof(struct in_addr));
1623
1624 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1625 query_buf[9] = qqic;
1626 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) =
1627 htons(num_sources);
1628
1629 checksum = in_cksum(query_buf, msg_size);
1630 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
1631
1632 if (PIM_DEBUG_IGMP_PACKETS) {
1633 char dst_str[INET_ADDRSTRLEN];
1634 char group_str[INET_ADDRSTRLEN];
1635 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1636 pim_inet4_dump("<group?>", group_addr, group_str,
1637 sizeof(group_str));
1638 zlog_debug(
1639 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1640 dst_str, ifname, group_str, num_sources, msg_size,
1641 s_flag, querier_robustness_variable,
1642 querier_query_interval, qqic);
1643 }
1644
1645 memset(&to, 0, sizeof(to));
1646 to.sin_family = AF_INET;
1647 to.sin_addr = dst_addr;
1648 tolen = sizeof(to);
1649
1650 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1651 (struct sockaddr *)&to, tolen);
1652 if (sent != (ssize_t)msg_size) {
1653 char dst_str[INET_ADDRSTRLEN];
1654 char group_str[INET_ADDRSTRLEN];
1655 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1656 pim_inet4_dump("<group?>", group_addr, group_str,
1657 sizeof(group_str));
1658 if (sent < 0) {
1659 zlog_warn(
1660 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1661 dst_str, ifname, group_str, msg_size, errno,
1662 safe_strerror(errno));
1663 } else {
1664 zlog_warn(
1665 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1666 dst_str, ifname, group_str, msg_size, sent);
1667 }
1668 return;
1669 }
1670
1671 /*
1672 s_flag sanity test: s_flag must be set for general queries
1673
1674 RFC 3376: 6.6.1. Timer Updates
1675
1676 When a router sends or receives a query with a clear Suppress
1677 Router-Side Processing flag, it must update its timers to reflect
1678 the correct timeout values for the group or sources being queried.
1679
1680 General queries don't trigger timer update.
1681 */
1682 if (!s_flag) {
1683 /* general query? */
1684 if (PIM_INADDR_IS_ANY(group_addr)) {
1685 char dst_str[INET_ADDRSTRLEN];
1686 char group_str[INET_ADDRSTRLEN];
1687 pim_inet4_dump("<dst?>", dst_addr, dst_str,
1688 sizeof(dst_str));
1689 pim_inet4_dump("<group?>", group_addr, group_str,
1690 sizeof(group_str));
1691 zlog_warn(
1692 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1693 __PRETTY_FUNCTION__, dst_str, ifname, group_str,
1694 num_sources);
1695 }
1696 }
b05b72e8
DW
1697}
1698
d62a17ae 1699void igmp_v3_recv_query(struct igmp_sock *igmp, const char *from_str,
1700 char *igmp_msg)
b05b72e8 1701{
d62a17ae 1702 struct interface *ifp;
1703 struct pim_interface *pim_ifp;
1704 struct in_addr group_addr;
1705 uint8_t resv_s_qrv = 0;
1706 uint8_t s_flag = 0;
1707 uint8_t qrv = 0;
1708 int i;
1709
1710 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
1711 ifp = igmp->interface;
1712 pim_ifp = ifp->info;
1713
1714 /*
1715 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1716 *
1717 * Routers adopt the QRV value from the most recently received Query
1718 * as their own [Robustness Variable] value, unless that most
1719 * recently received QRV was zero, in which case the receivers use
1720 * the default [Robustness Variable] value specified in section 8.1
1721 * or a statically configured value.
1722 */
1723 resv_s_qrv = igmp_msg[8];
1724 qrv = 7 & resv_s_qrv;
1725 igmp->querier_robustness_variable =
1726 qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
1727
1728 /*
1729 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1730 *
1731 * Multicast routers that are not the current querier adopt the QQI
1732 * value from the most recently received Query as their own [Query
1733 * Interval] value, unless that most recently received QQI was zero,
1734 * in which case the receiving routers use the default.
1735 */
1736 if (igmp->t_other_querier_timer) {
1737 /* other querier present */
1738 uint8_t qqic;
1739 uint16_t qqi;
1740 qqic = igmp_msg[9];
1741 qqi = igmp_msg_decode8to16(qqic);
1742 igmp->querier_query_interval =
1743 qqi ? qqi : pim_ifp->igmp_default_query_interval;
1744
1745 if (PIM_DEBUG_IGMP_TRACE) {
1746 char ifaddr_str[INET_ADDRSTRLEN];
1747 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
1748 sizeof(ifaddr_str));
1749 zlog_debug(
1750 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1751 ifaddr_str,
1752 qqi ? "recv-non-default" : "default",
1753 igmp->querier_query_interval, qqic, from_str);
1754 }
1755 }
1756
1757 /*
1758 * RFC 3376: 6.6.1. Timer Updates
1759 *
1760 * When a router sends or receives a query with a clear Suppress
1761 * Router-Side Processing flag, it must update its timers to reflect
1762 * the correct timeout values for the group or sources being queried.
1763 *
1764 * General queries don't trigger timer update.
1765 */
1766 s_flag = (1 << 3) & resv_s_qrv;
1767
1768 if (!s_flag) {
1769 /* s_flag is clear */
1770
1771 if (PIM_INADDR_IS_ANY(group_addr)) {
1772 /* this is a general query */
1773 /* log that general query should have the s_flag set */
1774 zlog_warn(
1775 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1776 from_str, ifp->name);
1777 } else {
1778 struct igmp_group *group;
1779
1780 /* this is a non-general query: perform timer updates */
1781
1782 group = find_group_by_addr(igmp, group_addr);
1783 if (group) {
1784 int recv_num_sources = ntohs(*(
1785 uint16_t
1786 *)(igmp_msg
1787 + IGMP_V3_NUMSOURCES_OFFSET));
1788
1789 /*
1790 * RFC 3376: 6.6.1. Timer Updates
1791 * Query Q(G,A): Source Timer for sources in A
1792 * are lowered to LMQT
1793 * Query Q(G): Group Timer is lowered to LMQT
1794 */
1795 if (recv_num_sources < 1) {
1796 /* Query Q(G): Group Timer is lowered to
1797 * LMQT */
1798
1799 igmp_group_timer_lower_to_lmqt(group);
1800 } else {
1801 /* Query Q(G,A): Source Timer for
1802 * sources in A are lowered to LMQT */
1803
1804 /* Scan sources in query and lower their
1805 * timers to LMQT */
1806 struct in_addr *sources =
1807 (struct in_addr
1808 *)(igmp_msg
1809 + IGMP_V3_SOURCES_OFFSET);
1810 for (i = 0; i < recv_num_sources; ++i) {
1811 struct in_addr src_addr;
1812 struct igmp_source *src;
1813 memcpy(&src_addr, sources + i,
1814 sizeof(struct in_addr));
1815 src = igmp_find_source_by_addr(
1816 group, src_addr);
1817 if (src) {
1818 igmp_source_timer_lower_to_lmqt(
1819 src);
1820 }
1821 }
1822 }
1823 } else {
1824 char group_str[INET_ADDRSTRLEN];
1825 pim_inet4_dump("<group?>", group_addr,
1826 group_str, sizeof(group_str));
1827 zlog_warn(
1828 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1829 from_str, ifp->name, group_str);
1830 }
1831 }
1832 } /* s_flag is clear: timer updates */
b05b72e8
DW
1833}
1834
d62a17ae 1835int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from,
1836 const char *from_str, char *igmp_msg, int igmp_msg_len)
b05b72e8 1837{
d62a17ae 1838 uint16_t recv_checksum;
1839 uint16_t checksum;
1840 int num_groups;
1841 uint8_t *group_record;
1842 uint8_t *report_pastend = (uint8_t *)igmp_msg + igmp_msg_len;
1843 struct interface *ifp = igmp->interface;
1844 int i;
1845 int local_ncb = 0;
b0f525a8
QY
1846 struct pim_interface *pim_ifp;
1847
f83f3966
MS
1848 if (igmp->mtrace_only)
1849 return 0;
1850
b0f525a8 1851 pim_ifp = igmp->interface->info;
d62a17ae 1852
1853 if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
1854 zlog_warn(
1855 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1856 from_str, ifp->name, igmp_msg_len,
1857 IGMP_V3_MSG_MIN_SIZE);
1858 return -1;
1859 }
1860
1861 recv_checksum = *(uint16_t *)(igmp_msg + IGMP_CHECKSUM_OFFSET);
1862
1863 /* for computing checksum */
1864 *(uint16_t *)(igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
1865
1866 checksum = in_cksum(igmp_msg, igmp_msg_len);
1867 if (checksum != recv_checksum) {
1868 zlog_warn(
1869 "Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1870 from_str, ifp->name, recv_checksum, checksum);
1871 return -1;
1872 }
1873
21313cbf
MS
1874 /* Collecting IGMP Rx stats */
1875 igmp->rx_stats.report_v3++;
1876
d62a17ae 1877 num_groups = ntohs(
1878 *(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
1879 if (num_groups < 1) {
1880 zlog_warn(
1881 "Recv IGMP report v3 from %s on %s: missing group records",
1882 from_str, ifp->name);
1883 return -1;
1884 }
1885
1886 if (PIM_DEBUG_IGMP_PACKETS) {
1887 zlog_debug(
1888 "Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1889 from_str, ifp->name, igmp_msg_len, checksum,
1890 num_groups);
1891 }
1892
1893 group_record = (uint8_t *)igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
1894
1895 /* Scan groups */
1896 for (i = 0; i < num_groups; ++i) {
1897 struct in_addr rec_group;
1898 uint8_t *sources;
1899 uint8_t *src;
1900 int rec_type;
1901 int rec_auxdatalen;
1902 int rec_num_sources;
1903 int j;
1904 struct prefix lncb;
1905 struct prefix g;
b0f525a8 1906 bool filtered = false;
d62a17ae 1907
1908 if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE)
1909 > report_pastend) {
1910 zlog_warn(
1911 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1912 from_str, ifp->name);
1913 return -1;
1914 }
1915
1916 rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
1917 rec_auxdatalen =
1918 group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
1919 rec_num_sources = ntohs(*(
1920 uint16_t *)(group_record
1921 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
1922
1923 memcpy(&rec_group,
1924 group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET,
1925 sizeof(struct in_addr));
1926
1927 if (PIM_DEBUG_IGMP_PACKETS) {
1928 zlog_debug(
1929 "Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1930 from_str, ifp->name, i, rec_type,
1931 rec_auxdatalen, rec_num_sources,
1932 inet_ntoa(rec_group));
1933 }
1934
1935 /* Scan sources */
1936
1937 sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
1938
1939 for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
1940
1941 if ((src + 4) > report_pastend) {
1942 zlog_warn(
1943 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1944 from_str, ifp->name);
1945 return -1;
1946 }
1947
1948 if (PIM_DEBUG_IGMP_PACKETS) {
1949 char src_str[200];
1950
1951 if (!inet_ntop(AF_INET, src, src_str,
1952 sizeof(src_str)))
1953 sprintf(src_str, "<source?>");
1954
1955 zlog_debug(
1956 "Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1957 from_str, ifp->name, i,
1958 inet_ntoa(rec_group), src_str);
1959 }
1960 } /* for (sources) */
1961
1962
1963 lncb.family = AF_INET;
1964 lncb.u.prefix4.s_addr = 0x000000E0;
1965 lncb.prefixlen = 24;
1966
1967 g.family = AF_INET;
1968 g.u.prefix4 = rec_group;
1969 g.prefixlen = 32;
b0f525a8
QY
1970
1971 /* determine filtering status for group */
1972 filtered = pim_is_group_filtered(ifp->info, &rec_group);
1973
1974 if (PIM_DEBUG_IGMP_PACKETS && filtered)
1975 zlog_debug(
1976 "Filtering IGMPv3 group record %s from %s on %s per prefix-list %s",
1977 inet_ntoa(rec_group), from_str, ifp->name,
1978 pim_ifp->boundary_oil_plist);
1979
d62a17ae 1980 /*
1981 * If we receive a igmp report with the group in 224.0.0.0/24
1982 * then we should ignore it
1983 */
1984 if (prefix_match(&lncb, &g))
1985 local_ncb = 1;
1986
b0f525a8 1987 if (!local_ncb && !filtered)
d62a17ae 1988 switch (rec_type) {
1989 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
1990 igmpv3_report_isin(igmp, from, rec_group,
1991 rec_num_sources,
1992 (struct in_addr *)sources);
1993 break;
1994 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
1995 igmpv3_report_isex(
1996 igmp, from, rec_group, rec_num_sources,
1997 (struct in_addr *)sources, 0);
1998 break;
1999 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
2000 igmpv3_report_toin(igmp, from, rec_group,
2001 rec_num_sources,
2002 (struct in_addr *)sources);
2003 break;
2004 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
2005 igmpv3_report_toex(igmp, from, rec_group,
2006 rec_num_sources,
2007 (struct in_addr *)sources);
2008 break;
2009 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
2010 igmpv3_report_allow(igmp, from, rec_group,
2011 rec_num_sources,
2012 (struct in_addr *)sources);
2013 break;
2014 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
2015 igmpv3_report_block(igmp, from, rec_group,
2016 rec_num_sources,
2017 (struct in_addr *)sources);
2018 break;
2019 default:
2020 zlog_warn(
2021 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2022 from_str, ifp->name, rec_type);
2023 }
2024
2025 group_record +=
2026 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
2027 local_ncb = 0;
2028
2029 } /* for (group records) */
2030
2031 return 0;
12e41d03 2032}