]> git.sommitrealweird.co.uk Git - quagga-debian.git/blob - pimd/pim_igmpv3.c
New upstream release and new maintainer
[quagga-debian.git] / pimd / pim_igmpv3.c
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 struct igmp_source *
481 source_new (struct igmp_group *group,
482             struct in_addr src_addr)
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                group->group_igmp_sock->interface->name);
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                = NULL;
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            = NULL;
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 {
526   struct igmp_source *src;
527
528   src = igmp_find_source_by_addr(group, src_addr);
529   if (src) {
530     return src;
531   }
532
533   src = source_new(group, src_addr);
534   if (!src) {
535     return 0;
536   }
537
538   return src;
539 }
540
541 static void allow(struct igmp_sock *igmp, struct in_addr from,
542                   struct in_addr group_addr,
543                   int num_sources, struct in_addr *sources)
544 {
545   struct igmp_group *group;
546   int    i;
547
548   /* non-existant group is created as INCLUDE {empty} */
549   group = igmp_add_group_by_addr(igmp, group_addr);
550   if (!group) {
551     return;
552   }
553
554   /* scan received sources */
555   for (i = 0; i < num_sources; ++i) {
556     struct igmp_source *source;
557     struct in_addr     *src_addr;
558
559     src_addr = sources + i;
560
561     source = add_source_by_addr(igmp, group, *src_addr);
562     if (!source) {
563       continue;
564     }
565
566     /*
567       RFC 3376: 6.4.1. Reception of Current-State Records
568
569       When receiving IS_IN reports for groups in EXCLUDE mode is
570       sources should be moved from set with (timers = 0) to set with
571       (timers > 0).
572
573       igmp_source_reset_gmi() below, resetting the source timers to
574       GMI, accomplishes this.
575     */
576     igmp_source_reset_gmi(igmp, group, source);
577
578   } /* scan received sources */
579 }
580
581 void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
582                         struct in_addr group_addr,
583                         int num_sources, struct in_addr *sources)
584 {
585   on_trace(__PRETTY_FUNCTION__,
586            igmp->interface, from, group_addr, num_sources, sources);
587
588   allow(igmp, from, group_addr, num_sources, sources);
589 }
590
591 static void isex_excl(struct igmp_group *group,
592                       int num_sources, struct in_addr *sources)
593 {
594   int     i;
595
596   /* EXCLUDE mode */
597   zassert(group->group_filtermode_isexcl);
598   
599   /* E.1: set deletion flag for known sources (X,Y) */
600   source_mark_delete_flag(group->group_source_list);
601
602   /* scan received sources (A) */
603   for (i = 0; i < num_sources; ++i) {
604     struct igmp_source *source;
605     struct in_addr     *src_addr;
606
607     src_addr = sources + i;
608
609     /* E.2: lookup reported source from (A) in (X,Y) */
610     source = igmp_find_source_by_addr(group, *src_addr);
611     if (source) {
612       /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
613       IGMP_SOURCE_DONT_DELETE(source->source_flags);
614     }
615     else {
616       /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
617       source = source_new(group, *src_addr);
618       if (!source) {
619         /* ugh, internal malloc failure, skip source */
620         continue;
621       }
622       zassert(!source->t_source_timer); /* timer == 0 */
623       igmp_source_reset_gmi(group->group_igmp_sock, group, source);
624       zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
625     }
626
627   } /* scan received sources */
628
629   /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
630   source_delete_by_flag(group->group_source_list);
631 }
632
633 static void isex_incl(struct igmp_group *group,
634                       int num_sources, struct in_addr *sources)
635 {
636   int i;
637
638   /* INCLUDE mode */
639   zassert(!group->group_filtermode_isexcl);
640   
641   /* I.1: set deletion flag for known sources (A) */
642   source_mark_delete_flag(group->group_source_list);
643
644   /* scan received sources (B) */
645   for (i = 0; i < num_sources; ++i) {
646     struct igmp_source *source;
647     struct in_addr     *src_addr;
648
649     src_addr = sources + i;
650
651     /* I.2: lookup reported source (B) */
652     source = igmp_find_source_by_addr(group, *src_addr);
653     if (source) {
654       /* I.3: if found, clear deletion flag (A*B) */
655       IGMP_SOURCE_DONT_DELETE(source->source_flags);
656     }
657     else {
658       /* I.4: if not found, create source with timer=0 (B-A) */
659       source = source_new(group, *src_addr);
660       if (!source) {
661         /* ugh, internal malloc failure, skip source */
662         continue;
663       }
664       zassert(!source->t_source_timer); /* (B-A) timer=0 */
665     }
666
667   } /* scan received sources */
668
669   /* I.5: delete all sources marked with deletion flag (A-B) */
670   source_delete_by_flag(group->group_source_list);
671
672   group->group_filtermode_isexcl = 1; /* boolean=true */
673
674   zassert(group->group_filtermode_isexcl);
675
676   group_exclude_fwd_anysrc_ifempty(group);
677 }
678
679 void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
680                         struct in_addr group_addr,
681                         int num_sources, struct in_addr *sources)
682 {
683   struct interface *ifp = igmp->interface;
684   struct igmp_group *group;
685
686   on_trace(__PRETTY_FUNCTION__,
687            ifp, from, group_addr, num_sources, sources);
688
689   /* non-existant group is created as INCLUDE {empty} */
690   group = igmp_add_group_by_addr(igmp, group_addr);
691   if (!group) {
692     return;
693   }
694
695   if (group->group_filtermode_isexcl) {
696     /* EXCLUDE mode */
697     isex_excl(group, num_sources, sources);
698   }
699   else {
700     /* INCLUDE mode */
701     isex_incl(group, num_sources, sources);
702     zassert(group->group_filtermode_isexcl);
703   }
704
705   zassert(group->group_filtermode_isexcl);
706
707   igmp_group_reset_gmi(group);
708 }
709
710 static void toin_incl(struct igmp_group *group,
711                       int num_sources, struct in_addr *sources)
712 {
713   struct igmp_sock *igmp = group->group_igmp_sock;
714   int num_sources_tosend = listcount(group->group_source_list);
715   int i;
716
717   /* Set SEND flag for all known sources (A) */
718   source_mark_send_flag(group->group_source_list);
719
720   /* Scan received sources (B) */
721   for (i = 0; i < num_sources; ++i) {
722     struct igmp_source *source;
723     struct in_addr     *src_addr;
724
725     src_addr = sources + i;
726
727     /* Lookup reported source (B) */
728     source = igmp_find_source_by_addr(group, *src_addr);
729     if (source) {
730       /* If found, clear SEND flag (A*B) */
731       IGMP_SOURCE_DONT_SEND(source->source_flags);
732       --num_sources_tosend;
733     }
734     else {
735       /* If not found, create new source */
736       source = source_new(group, *src_addr);
737       if (!source) {
738         /* ugh, internal malloc failure, skip source */
739         continue;
740       }
741     }
742
743     /* (B)=GMI */
744     igmp_source_reset_gmi(igmp, group, source);
745   }
746
747   /* Send sources marked with SEND flag: Q(G,A-B) */
748   if (num_sources_tosend > 0) {
749     source_query_send_by_flag(group, num_sources_tosend);
750   }
751 }
752
753 static void toin_excl(struct igmp_group *group,
754                       int num_sources, struct in_addr *sources)
755 {
756   struct igmp_sock *igmp = group->group_igmp_sock;
757   int num_sources_tosend;
758   int i;
759
760   /* Set SEND flag for X (sources with timer > 0) */
761   num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
762
763   /* Scan received sources (A) */
764   for (i = 0; i < num_sources; ++i) {
765     struct igmp_source *source;
766     struct in_addr     *src_addr;
767
768     src_addr = sources + i;
769
770     /* Lookup reported source (A) */
771     source = igmp_find_source_by_addr(group, *src_addr);
772     if (source) {
773       if (source->t_source_timer) {
774         /* If found and timer running, clear SEND flag (X*A) */
775         IGMP_SOURCE_DONT_SEND(source->source_flags);
776         --num_sources_tosend;
777       }
778     }
779     else {
780       /* If not found, create new source */
781       source = source_new(group, *src_addr);
782       if (!source) {
783         /* ugh, internal malloc failure, skip source */
784         continue;
785       }
786     }
787
788     /* (A)=GMI */
789     igmp_source_reset_gmi(igmp, group, source);
790   }
791
792   /* Send sources marked with SEND flag: Q(G,X-A) */
793   if (num_sources_tosend > 0) {
794     source_query_send_by_flag(group, num_sources_tosend);
795   }
796
797   /* Send Q(G) */
798   group_query_send(group);
799 }
800
801 void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
802                         struct in_addr group_addr,
803                         int num_sources, struct in_addr *sources)
804 {
805   struct interface *ifp = igmp->interface;
806   struct igmp_group *group;
807
808   on_trace(__PRETTY_FUNCTION__,
809            ifp, from, group_addr, num_sources, sources);
810
811   /* non-existant group is created as INCLUDE {empty} */
812   group = igmp_add_group_by_addr(igmp, group_addr);
813   if (!group) {
814     return;
815   }
816
817   if (group->group_filtermode_isexcl) {
818     /* EXCLUDE mode */
819     toin_excl(group, num_sources, sources);
820   }
821   else {
822     /* INCLUDE mode */
823     toin_incl(group, num_sources, sources);
824   }
825 }
826
827 static void toex_incl(struct igmp_group *group,
828                       int num_sources, struct in_addr *sources)
829 {
830   int num_sources_tosend = 0;
831   int i;
832
833   zassert(!group->group_filtermode_isexcl);
834
835   /* Set DELETE flag for all known sources (A) */
836   source_mark_delete_flag(group->group_source_list);
837
838   /* Clear off SEND flag from all known sources (A) */
839   source_clear_send_flag(group->group_source_list);
840
841   /* Scan received sources (B) */
842   for (i = 0; i < num_sources; ++i) {
843     struct igmp_source *source;
844     struct in_addr     *src_addr;
845
846     src_addr = sources + i;
847
848     /* Lookup reported source (B) */
849     source = igmp_find_source_by_addr(group, *src_addr);
850     if (source) {
851       /* If found, clear deletion flag: (A*B) */
852       IGMP_SOURCE_DONT_DELETE(source->source_flags);
853       /* and set SEND flag (A*B) */
854       IGMP_SOURCE_DO_SEND(source->source_flags);
855       ++num_sources_tosend;
856     }
857     else {
858       /* If source not found, create source with timer=0: (B-A)=0 */
859       source = source_new(group, *src_addr);
860       if (!source) {
861         /* ugh, internal malloc failure, skip source */
862         continue;
863       }
864       zassert(!source->t_source_timer); /* (B-A) timer=0 */
865     }
866
867   } /* Scan received sources (B) */
868
869   group->group_filtermode_isexcl = 1; /* boolean=true */
870
871   /* Delete all sources marked with DELETE flag (A-B) */
872   source_delete_by_flag(group->group_source_list);
873
874   /* Send sources marked with SEND flag: Q(G,A*B) */
875   if (num_sources_tosend > 0) {
876     source_query_send_by_flag(group, num_sources_tosend);
877   }
878
879   zassert(group->group_filtermode_isexcl);
880
881   group_exclude_fwd_anysrc_ifempty(group);
882 }
883
884 static void toex_excl(struct igmp_group *group,
885                       int num_sources, struct in_addr *sources)
886 {
887   int num_sources_tosend = 0;
888   int i;
889
890   /* set DELETE flag for all known sources (X,Y) */
891   source_mark_delete_flag(group->group_source_list);
892
893   /* clear off SEND flag from all known sources (X,Y) */
894   source_clear_send_flag(group->group_source_list);
895
896   /* scan received sources (A) */
897   for (i = 0; i < num_sources; ++i) {
898     struct igmp_source *source;
899     struct in_addr     *src_addr;
900     
901     src_addr = sources + i;
902     
903     /* lookup reported source (A) in known sources (X,Y) */
904     source = igmp_find_source_by_addr(group, *src_addr);
905     if (source) {
906       /* if found, clear off DELETE flag from reported source (A) */
907       IGMP_SOURCE_DONT_DELETE(source->source_flags);
908     }
909     else {
910       /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
911       long group_timer_msec;
912       source = source_new(group, *src_addr);
913       if (!source) {
914         /* ugh, internal malloc failure, skip source */
915         continue;
916       }
917
918       zassert(!source->t_source_timer); /* timer == 0 */
919       group_timer_msec = igmp_group_timer_remain_msec(group);
920       igmp_source_timer_on(group, source, group_timer_msec);
921       zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
922
923       /* make sure source is created with DELETE flag unset */
924       zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
925     }
926
927     /* make sure reported source has DELETE flag unset */
928     zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
929
930     if (source->t_source_timer) {
931       /* if source timer>0 mark SEND flag: Q(G,A-Y) */
932       IGMP_SOURCE_DO_SEND(source->source_flags);
933       ++num_sources_tosend;
934     }
935
936   } /* scan received sources (A) */
937
938   /*
939     delete all sources marked with DELETE flag:
940     Delete (X-A)
941     Delete (Y-A)
942   */
943   source_delete_by_flag(group->group_source_list);
944  
945   /* send sources marked with SEND flag: Q(G,A-Y) */
946   if (num_sources_tosend > 0) {
947     source_query_send_by_flag(group, num_sources_tosend);
948   }
949 }
950
951 void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
952                         struct in_addr group_addr,
953                         int num_sources, struct in_addr *sources)
954 {
955   struct interface *ifp = igmp->interface;
956   struct igmp_group *group;
957
958   on_trace(__PRETTY_FUNCTION__,
959            ifp, from, group_addr, num_sources, sources);
960
961   /* non-existant group is created as INCLUDE {empty} */
962   group = igmp_add_group_by_addr(igmp, group_addr);
963   if (!group) {
964     return;
965   }
966
967   if (group->group_filtermode_isexcl) {
968     /* EXCLUDE mode */
969     toex_excl(group, num_sources, sources);
970   }
971   else {
972     /* INCLUDE mode */
973     toex_incl(group, num_sources, sources);
974     zassert(group->group_filtermode_isexcl);
975   }
976   zassert(group->group_filtermode_isexcl);
977
978   /* Group Timer=GMI */
979   igmp_group_reset_gmi(group);
980 }
981
982 void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
983                          struct in_addr group_addr,
984                          int num_sources, struct in_addr *sources)
985 {
986   on_trace(__PRETTY_FUNCTION__,
987            igmp->interface, from, group_addr, num_sources, sources);
988
989   allow(igmp, from, group_addr, num_sources, sources);
990 }
991
992 /*
993   RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
994
995   When transmitting a group specific query, if the group timer is
996   larger than LMQT, the "Suppress Router-Side Processing" bit is set
997   in the query message.
998 */
999 static void group_retransmit_group(struct igmp_group *group)
1000 {
1001   char                  query_buf[PIM_IGMP_BUFSIZE_WRITE];
1002   struct igmp_sock     *igmp;
1003   struct pim_interface *pim_ifp;
1004   long                  lmqc;      /* Last Member Query Count */
1005   long                  lmqi_msec; /* Last Member Query Interval */
1006   long                  lmqt_msec; /* Last Member Query Time */
1007   int                   s_flag;
1008
1009   igmp = group->group_igmp_sock;
1010   pim_ifp = igmp->interface->info;
1011
1012   lmqc      = igmp->querier_robustness_variable;
1013   lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1014   lmqt_msec = lmqc * lmqi_msec;
1015
1016   /*
1017     RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1018     
1019     When transmitting a group specific query, if the group timer is
1020     larger than LMQT, the "Suppress Router-Side Processing" bit is set
1021     in the query message.
1022   */
1023   s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1024
1025   if (PIM_DEBUG_IGMP_TRACE) {
1026     char group_str[100];
1027     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1028     zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1029                group_str, igmp->interface->name, s_flag,
1030                group->group_specific_query_retransmit_count);
1031   }
1032
1033   /*
1034     RFC3376: 4.1.12. IP Destination Addresses for Queries
1035
1036     Group-Specific and Group-and-Source-Specific Queries are sent with
1037     an IP destination address equal to the multicast address of
1038     interest.
1039   */
1040
1041   pim_igmp_send_membership_query(group,
1042                                  igmp->fd,
1043                                  igmp->interface->name,
1044                                  query_buf,
1045                                  sizeof(query_buf),
1046                                  0 /* num_sources_tosend */,
1047                                  group->group_addr /* dst_addr */,
1048                                  group->group_addr /* group_addr */,
1049                                  pim_ifp->igmp_specific_query_max_response_time_dsec,
1050                                  s_flag,
1051                                  igmp->querier_robustness_variable,
1052                                  igmp->querier_query_interval);
1053 }
1054
1055 /*
1056   RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1057
1058   When building a group and source specific query for a group G, two
1059   separate query messages are sent for the group.  The first one has
1060   the "Suppress Router-Side Processing" bit set and contains all the
1061   sources with retransmission state and timers greater than LMQT.  The
1062   second has the "Suppress Router-Side Processing" bit clear and
1063   contains all the sources with retransmission state and timers lower
1064   or equal to LMQT.  If either of the two calculated messages does not
1065   contain any sources, then its transmission is suppressed.
1066  */
1067 static int group_retransmit_sources(struct igmp_group *group,
1068                                     int send_with_sflag_set)
1069 {
1070   struct igmp_sock     *igmp;
1071   struct pim_interface *pim_ifp;
1072   long                  lmqc;      /* Last Member Query Count */
1073   long                  lmqi_msec; /* Last Member Query Interval */
1074   long                  lmqt_msec; /* Last Member Query Time */
1075   char                  query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1076   char                  query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1077   int                   query_buf1_max_sources;
1078   int                   query_buf2_max_sources;
1079   struct in_addr       *source_addr1;
1080   struct in_addr       *source_addr2;
1081   int                   num_sources_tosend1;
1082   int                   num_sources_tosend2;
1083   struct listnode      *src_node;
1084   struct igmp_source   *src;
1085   int                   num_retransmit_sources_left = 0;
1086   
1087   query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1088   query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1089   
1090   source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1091   source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1092
1093   igmp = group->group_igmp_sock;
1094   pim_ifp = igmp->interface->info;
1095
1096   lmqc      = igmp->querier_robustness_variable;
1097   lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1098   lmqt_msec = lmqc * lmqi_msec;
1099
1100   /* Scan all group sources */
1101   for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1102
1103     /* Source has retransmission state? */
1104     if (src->source_query_retransmit_count < 1)
1105       continue;
1106
1107     if (--src->source_query_retransmit_count > 0) {
1108       ++num_retransmit_sources_left;
1109     }
1110
1111     /* Copy source address into appropriate query buffer */
1112     if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1113       *source_addr1 = src->source_addr;
1114       ++source_addr1;
1115     }
1116     else {
1117       *source_addr2 = src->source_addr;
1118       ++source_addr2;
1119     }
1120
1121   }
1122   
1123   num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1124   num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1125
1126   if (PIM_DEBUG_IGMP_TRACE) {
1127     char group_str[100];
1128     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1129     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",
1130                group_str, igmp->interface->name,
1131                num_sources_tosend1,
1132                num_sources_tosend2,
1133                send_with_sflag_set,
1134                num_retransmit_sources_left);
1135   }
1136
1137   if (num_sources_tosend1 > 0) {
1138     /*
1139       Send group-and-source-specific query with s_flag set and all
1140       sources with timers greater than LMQT.
1141     */
1142
1143     if (send_with_sflag_set) {
1144
1145       query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1146       if (num_sources_tosend1 > query_buf1_max_sources) {
1147         char group_str[100];
1148         pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1149         zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1150                   __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1151                   num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
1152       }
1153       else {
1154         /*
1155           RFC3376: 4.1.12. IP Destination Addresses for Queries
1156       
1157           Group-Specific and Group-and-Source-Specific Queries are sent with
1158           an IP destination address equal to the multicast address of
1159           interest.
1160         */
1161     
1162         pim_igmp_send_membership_query(group,
1163                                        igmp->fd,
1164                                        igmp->interface->name,
1165                                        query_buf1,
1166                                        sizeof(query_buf1),
1167                                        num_sources_tosend1,
1168                                        group->group_addr,
1169                                        group->group_addr,
1170                                        pim_ifp->igmp_specific_query_max_response_time_dsec,
1171                                        1 /* s_flag */,
1172                                        igmp->querier_robustness_variable,
1173                                        igmp->querier_query_interval);
1174     
1175       }
1176
1177     } /* send_with_sflag_set */
1178
1179   }
1180
1181   if (num_sources_tosend2 > 0) {
1182     /*
1183       Send group-and-source-specific query with s_flag clear and all
1184       sources with timers lower or equal to LMQT.
1185     */
1186   
1187     query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1188     if (num_sources_tosend2 > query_buf2_max_sources) {
1189       char group_str[100];
1190       pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1191       zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1192                 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1193                 num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
1194     }
1195     else {
1196       /*
1197         RFC3376: 4.1.12. IP Destination Addresses for Queries
1198
1199         Group-Specific and Group-and-Source-Specific Queries are sent with
1200         an IP destination address equal to the multicast address of
1201         interest.
1202       */
1203
1204       pim_igmp_send_membership_query(group,
1205                                      igmp->fd,
1206                                      igmp->interface->name,
1207                                      query_buf2,
1208                                      sizeof(query_buf2),
1209                                      num_sources_tosend2,
1210                                      group->group_addr,
1211                                      group->group_addr,
1212                                      pim_ifp->igmp_specific_query_max_response_time_dsec,
1213                                      0 /* s_flag */,
1214                                      igmp->querier_robustness_variable,
1215                                      igmp->querier_query_interval);
1216
1217     }
1218   }
1219
1220   return num_retransmit_sources_left;
1221 }
1222
1223 static int igmp_group_retransmit(struct thread *t)
1224 {
1225   struct igmp_group *group;
1226   int num_retransmit_sources_left;
1227   int send_with_sflag_set; /* boolean */
1228
1229   zassert(t);
1230   group = THREAD_ARG(t);
1231   zassert(group);
1232
1233   if (PIM_DEBUG_IGMP_TRACE) {
1234     char group_str[100];
1235     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1236     zlog_debug("group_retransmit_timer: group %s on %s",
1237                group_str, group->group_igmp_sock->interface->name);
1238   }
1239
1240   /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1241   if (group->group_specific_query_retransmit_count > 0) {
1242
1243     /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1244     group_retransmit_group(group);
1245     --group->group_specific_query_retransmit_count;
1246
1247     /*
1248       RFC3376: 6.6.3.2
1249       If a group specific query is scheduled to be transmitted at the
1250       same time as a group and source specific query for the same group,
1251       then transmission of the group and source specific message with the
1252       "Suppress Router-Side Processing" bit set may be suppressed.
1253     */
1254     send_with_sflag_set = 0; /* boolean=false */
1255   }
1256   else {
1257     send_with_sflag_set = 1; /* boolean=true */
1258   }
1259
1260   /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1261   num_retransmit_sources_left = group_retransmit_sources(group,
1262                                                          send_with_sflag_set);
1263
1264   group->t_group_query_retransmit_timer = 0;
1265
1266   /*
1267     Keep group retransmit timer running if there is any retransmit
1268     counter pending
1269   */
1270   if ((num_retransmit_sources_left > 0) ||
1271       (group->group_specific_query_retransmit_count > 0)) {
1272     group_retransmit_timer_on(group);
1273   }
1274
1275   return 0;
1276 }
1277
1278 /*
1279   group_retransmit_timer_on:
1280   if group retransmit timer isn't running, starts it;
1281   otherwise, do nothing
1282 */
1283 static void group_retransmit_timer_on(struct igmp_group *group)
1284 {
1285   struct igmp_sock     *igmp;
1286   struct pim_interface *pim_ifp;
1287   long                  lmqi_msec; /* Last Member Query Interval */
1288
1289   /* if group retransmit timer is running, do nothing */
1290   if (group->t_group_query_retransmit_timer) {
1291     return;
1292   }
1293
1294   igmp = group->group_igmp_sock;
1295   pim_ifp = igmp->interface->info;
1296
1297   lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1298
1299   if (PIM_DEBUG_IGMP_TRACE) {
1300     char group_str[100];
1301     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1302     zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1303                lmqi_msec / 1000,
1304                lmqi_msec % 1000,
1305                group_str,
1306                igmp->interface->name);
1307   }
1308
1309   THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
1310                        igmp_group_retransmit,
1311                        group, lmqi_msec);
1312 }
1313
1314 static long igmp_group_timer_remain_msec(struct igmp_group *group)
1315 {
1316   return pim_time_timer_remain_msec(group->t_group_timer);
1317 }
1318
1319 static long igmp_source_timer_remain_msec(struct igmp_source *source)
1320 {
1321   return pim_time_timer_remain_msec(source->t_source_timer);
1322 }
1323
1324 /*
1325   RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1326 */
1327 static void group_query_send(struct igmp_group *group)
1328 {
1329   long              lmqc;    /* Last Member Query Count */
1330
1331   lmqc = group->group_igmp_sock->querier_robustness_variable;
1332
1333   /* lower group timer to lmqt */
1334   igmp_group_timer_lower_to_lmqt(group);
1335
1336   /* reset retransmission counter */
1337   group->group_specific_query_retransmit_count = lmqc;
1338
1339   /* immediately send group specific query (decrease retransmit counter by 1)*/
1340   group_retransmit_group(group);
1341
1342   /* make sure group retransmit timer is running */
1343   group_retransmit_timer_on(group);
1344 }
1345
1346 /*
1347   RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1348 */
1349 static void source_query_send_by_flag(struct igmp_group *group,
1350                                       int num_sources_tosend)
1351 {
1352   struct igmp_sock     *igmp;
1353   struct pim_interface *pim_ifp;
1354   struct listnode      *src_node;
1355   struct igmp_source   *src;
1356   long                  lmqc;      /* Last Member Query Count */
1357   long                  lmqi_msec; /* Last Member Query Interval */
1358   long                  lmqt_msec; /* Last Member Query Time */
1359
1360   zassert(num_sources_tosend > 0);
1361
1362   igmp = group->group_igmp_sock;
1363   pim_ifp = igmp->interface->info;
1364
1365   lmqc      = igmp->querier_robustness_variable;
1366   lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1367   lmqt_msec = lmqc * lmqi_msec;
1368
1369   /*
1370     RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1371
1372     (...) for each of the sources in X of group G, with source timer larger
1373     than LMQT:
1374     o Set number of retransmissions for each source to [Last Member
1375     Query Count].
1376     o Lower source timer to LMQT.
1377   */
1378   for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1379     if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1380       /* source "src" in X of group G */
1381       if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1382         src->source_query_retransmit_count = lmqc;
1383         igmp_source_timer_lower_to_lmqt(src);
1384       }
1385     }
1386   }
1387
1388   /* send group-and-source specific queries */
1389   group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1390
1391   /* make sure group retransmit timer is running */
1392   group_retransmit_timer_on(group);
1393 }
1394
1395 static void block_excl(struct igmp_group *group,
1396                        int num_sources, struct in_addr *sources)
1397 {
1398   int num_sources_tosend = 0;
1399   int i;
1400
1401   /* 1. clear off SEND flag from all known sources (X,Y) */
1402   source_clear_send_flag(group->group_source_list);
1403
1404   /* 2. scan received sources (A) */
1405   for (i = 0; i < num_sources; ++i) {
1406     struct igmp_source *source;
1407     struct in_addr     *src_addr;
1408     
1409     src_addr = sources + i;
1410     
1411     /* lookup reported source (A) in known sources (X,Y) */
1412     source = igmp_find_source_by_addr(group, *src_addr);
1413     if (!source) {
1414       /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1415       long group_timer_msec;
1416       source = source_new(group, *src_addr);
1417       if (!source) {
1418         /* ugh, internal malloc failure, skip source */
1419         continue;
1420       }
1421
1422       zassert(!source->t_source_timer); /* timer == 0 */
1423       group_timer_msec = igmp_group_timer_remain_msec(group);
1424       igmp_source_timer_on(group, source, group_timer_msec);
1425       zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1426     }
1427
1428     if (source->t_source_timer) {
1429       /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1430       IGMP_SOURCE_DO_SEND(source->source_flags);
1431       ++num_sources_tosend;
1432     }
1433   }
1434  
1435   /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1436   if (num_sources_tosend > 0) {
1437     source_query_send_by_flag(group, num_sources_tosend);
1438   }
1439 }
1440
1441 static void block_incl(struct igmp_group *group,
1442                        int num_sources, struct in_addr *sources)
1443 {
1444   int num_sources_tosend = 0;
1445   int i;
1446
1447   /* 1. clear off SEND flag from all known sources (B) */
1448   source_clear_send_flag(group->group_source_list);
1449
1450   /* 2. scan received sources (A) */
1451   for (i = 0; i < num_sources; ++i) {
1452     struct igmp_source *source;
1453     struct in_addr     *src_addr;
1454     
1455     src_addr = sources + i;
1456     
1457     /* lookup reported source (A) in known sources (B) */
1458     source = igmp_find_source_by_addr(group, *src_addr);
1459     if (source) {
1460       /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1461       IGMP_SOURCE_DO_SEND(source->source_flags);
1462       ++num_sources_tosend;
1463     }
1464   } 
1465  
1466   /* 4. send sources marked with SEND flag: Q(G,A*B) */
1467   if (num_sources_tosend > 0) {
1468     source_query_send_by_flag(group, num_sources_tosend);
1469   }
1470 }
1471
1472 void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
1473                          struct in_addr group_addr,
1474                          int num_sources, struct in_addr *sources)
1475 {
1476   struct interface *ifp = igmp->interface;
1477   struct igmp_group *group;
1478
1479   on_trace(__PRETTY_FUNCTION__,
1480            ifp, from, group_addr, num_sources, sources);
1481
1482   /* non-existant group is created as INCLUDE {empty} */
1483   group = igmp_add_group_by_addr(igmp, group_addr);
1484   if (!group) {
1485     return;
1486   }
1487
1488   if (group->group_filtermode_isexcl) {
1489     /* EXCLUDE mode */
1490     block_excl(group, num_sources, sources);
1491   }
1492   else {
1493     /* INCLUDE mode */
1494     block_incl(group, num_sources, sources);
1495   }
1496 }
1497
1498 void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
1499 {
1500   struct igmp_sock     *igmp;
1501   struct interface     *ifp;
1502   struct pim_interface *pim_ifp;
1503   char                 *ifname;
1504   int   lmqi_dsec; /* Last Member Query Interval */
1505   int   lmqc;      /* Last Member Query Count */
1506   int   lmqt_msec; /* Last Member Query Time */
1507
1508   /*
1509     RFC 3376: 6.2.2. Definition of Group Timers
1510     
1511     The group timer is only used when a group is in EXCLUDE mode and
1512     it represents the time for the *filter-mode* of the group to
1513     expire and switch to INCLUDE mode.
1514   */
1515   if (!group->group_filtermode_isexcl) {
1516     return;
1517   }
1518
1519   igmp    = group->group_igmp_sock;
1520   ifp     = igmp->interface;
1521   pim_ifp = ifp->info;
1522   ifname  = ifp->name;
1523
1524   lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
1525   lmqc      = igmp->querier_robustness_variable;
1526   lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1527
1528   if (PIM_DEBUG_IGMP_TRACE) {
1529     char group_str[100];
1530     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1531     zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1532                __PRETTY_FUNCTION__,
1533                group_str, ifname,
1534                lmqc, lmqi_dsec, lmqt_msec);
1535   }
1536
1537   zassert(group->group_filtermode_isexcl);
1538
1539   igmp_group_timer_on(group, lmqt_msec, ifname);
1540 }
1541
1542 void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
1543 {
1544   struct igmp_group    *group;
1545   struct igmp_sock     *igmp;
1546   struct interface     *ifp;
1547   struct pim_interface *pim_ifp;
1548   char                 *ifname;
1549   int   lmqi_dsec; /* Last Member Query Interval */
1550   int   lmqc;      /* Last Member Query Count */
1551   int   lmqt_msec; /* Last Member Query Time */
1552
1553   group   = source->source_group;
1554   igmp    = group->group_igmp_sock;
1555   ifp     = igmp->interface;
1556   pim_ifp = ifp->info;
1557   ifname  = ifp->name;
1558
1559   lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
1560   lmqc      = igmp->querier_robustness_variable;
1561   lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1562
1563   if (PIM_DEBUG_IGMP_TRACE) {
1564     char group_str[100];
1565     char source_str[100];
1566     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1567     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1568     zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1569                __PRETTY_FUNCTION__,
1570                group_str, source_str, ifname,
1571                lmqc, lmqi_dsec, lmqt_msec);
1572   }
1573
1574   igmp_source_timer_on(group, source, lmqt_msec);
1575 }
1576
1577 /*
1578   Copy sources to message:
1579     
1580   struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
1581   if (num_sources > 0) {
1582   struct listnode    *node;
1583   struct igmp_source *src;
1584   int                 i = 0;
1585
1586   for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
1587   sources[i++] = src->source_addr;
1588   }
1589   }
1590 */
1591 void pim_igmp_send_membership_query(struct igmp_group *group,
1592                                     int fd,
1593                                     const char *ifname,
1594                                     char *query_buf,
1595                                     int query_buf_size,
1596                                     int num_sources,
1597                                     struct in_addr dst_addr,
1598                                     struct in_addr group_addr,
1599                                     int query_max_response_time_dsec,
1600                                     uint8_t s_flag,
1601                                     uint8_t querier_robustness_variable,
1602                                     uint16_t querier_query_interval)
1603 {
1604   ssize_t             msg_size;
1605   uint8_t             max_resp_code;
1606   uint8_t             qqic;
1607   ssize_t             sent;
1608   struct sockaddr_in  to;
1609   socklen_t           tolen;
1610   uint16_t            checksum;
1611
1612   zassert(num_sources >= 0);
1613
1614   msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1615   if (msg_size > query_buf_size) {
1616     zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1617              __FILE__, __PRETTY_FUNCTION__,
1618              msg_size, query_buf_size);
1619     return;
1620   }
1621
1622   s_flag = PIM_FORCE_BOOLEAN(s_flag);
1623   zassert((s_flag == 0) || (s_flag == 1));
1624
1625   max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1626   qqic          = igmp_msg_encode16to8(querier_query_interval);
1627
1628   /*
1629     RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1630
1631     If non-zero, the QRV field contains the [Robustness Variable]
1632     value used by the querier, i.e., the sender of the Query.  If the
1633     querier's [Robustness Variable] exceeds 7, the maximum value of
1634     the QRV field, the QRV is set to zero.
1635   */
1636   if (querier_robustness_variable > 7) {
1637     querier_robustness_variable = 0;
1638   }
1639
1640   query_buf[0]                                         = PIM_IGMP_MEMBERSHIP_QUERY;
1641   query_buf[1]                                         = max_resp_code;
1642   *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET)   = 0; /* for computing checksum */
1643   memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
1644
1645   query_buf[8]                                         = (s_flag << 3) | querier_robustness_variable;
1646   query_buf[9]                                         = qqic;
1647   *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
1648
1649   checksum = in_cksum(query_buf, msg_size);
1650   *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
1651
1652   if (PIM_DEBUG_IGMP_PACKETS) {
1653     char dst_str[100];
1654     char group_str[100];
1655     pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1656     pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1657     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",
1658                __PRETTY_FUNCTION__,
1659                dst_str, ifname, group_str, num_sources,
1660                msg_size, s_flag, querier_robustness_variable,
1661                querier_query_interval, qqic, checksum);
1662   }
1663
1664   memset(&to, 0, sizeof(to));
1665   to.sin_family = AF_INET;
1666   to.sin_addr = dst_addr;
1667   tolen = sizeof(to);
1668
1669   sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1670                 (struct sockaddr *)&to, tolen);
1671   if (sent != (ssize_t) msg_size) {
1672     int e = errno;
1673     char dst_str[100];
1674     char group_str[100];
1675     pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1676     pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1677     if (sent < 0) {
1678       zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1679                 __PRETTY_FUNCTION__,
1680                 dst_str, ifname, group_str, msg_size,
1681                 e, safe_strerror(e));
1682     }
1683     else {
1684       zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
1685                 __PRETTY_FUNCTION__,
1686                 dst_str, ifname, group_str,
1687                 msg_size, sent);
1688     }
1689     return;
1690   }
1691
1692   /*
1693     s_flag sanity test: s_flag must be set for general queries
1694
1695     RFC 3376: 6.6.1. Timer Updates
1696
1697     When a router sends or receives a query with a clear Suppress
1698     Router-Side Processing flag, it must update its timers to reflect
1699     the correct timeout values for the group or sources being queried.
1700
1701     General queries don't trigger timer update.
1702   */
1703   if (!s_flag) {
1704     /* general query? */
1705     if (PIM_INADDR_IS_ANY(group_addr)) {
1706       char dst_str[100];
1707       char group_str[100];
1708       pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1709       pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1710       zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1711                 __PRETTY_FUNCTION__,
1712                 dst_str, ifname, group_str, num_sources);
1713     }
1714   }
1715
1716 }