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