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