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