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