Line data Source code
1 : /* $Id: gmpr_engine.c 514187 2012-05-06 12:25:25Z ib-builder $
2 : *
3 : * gmpr_engine.c - IGMP/MLD Router-Side generic protocol engine
4 : *
5 : * Dave Katz, March 2008
6 : *
7 : * Copyright (c) 2008, Juniper Networks, Inc.
8 : * All rights reserved.
9 : *
10 : * This module contains the protocol engine for router-side GMP.
11 : */
12 : #include "gmpx_basic_types.h"
13 : #include "gmp.h"
14 : #include "gmpx_environment.h"
15 : #include "gmp_externs.h"
16 : #include "gmp_private.h"
17 : #include "gmp_router.h"
18 : #include "gmpr_private.h"
19 : #include "gmpr_trace.h"
20 :
21 :
22 : /*
23 : * gmpr_process_query_packet
24 : *
25 : * Process a received query packet.
26 : */
27 : static void
28 0 : gmpr_process_query_packet(gmpr_intf *intf, gmp_packet *packet)
29 : {
30 : gmpr_instance *instance;
31 : gmp_query_packet *query_pkt;
32 : gmpr_group *group;
33 : gmp_addr_thread_entry *addr_thread_entry;
34 : gmp_addr_string *source_addr;
35 : gmp_addr_cat_entry *cat_entry;
36 : gmp_addr_list_entry *addr_entry;
37 : gmpr_group_addr_entry *group_addr;
38 : int addr_compare;
39 :
40 : /* Bail if this is IGMPv1; we ignore queries in this case. */
41 :
42 0 : if (intf->rintf_ver == GMP_VERSION_BASIC)
43 0 : return;
44 :
45 0 : instance = intf->rintf_instance;
46 0 : query_pkt = &packet->gmp_packet_contents.gmp_packet_query;
47 :
48 : /* Post a warning if the version does not match. */
49 :
50 0 : if (intf->rintf_ver != packet->gmp_packet_version ) {
51 0 : gmpr_post_event(instance, GMP_VERSION_MISMATCH, intf->rintf_id,
52 : gmp_untranslate_version(instance->rinst_proto,
53 : intf->rintf_ver),
54 : gmp_untranslate_version(instance->rinst_proto,
55 : packet->gmp_packet_version));
56 : }
57 :
58 : /*
59 : * If the source of the query is all zeroes, ignore it. It's a snooping
60 : * switch trying to elicit state from hosts, and is not the elected
61 : * querier.
62 : */
63 0 : if (gmp_addr_is_zero(&packet->gmp_packet_src_addr,
64 : instance->rinst_addrlen)) {
65 0 : return;
66 : }
67 :
68 : /*
69 : * The source has a real address. If we have one too, do the election.
70 : * If we don't have an address, it means that we're a snooping switch
71 : * and don't do our own queries, so we elect the other guy as querier.
72 : */
73 0 : if (!gmp_addr_is_zero(&intf->rintf_local_addr, instance->rinst_addrlen)) {
74 :
75 : /* Compare addresses with the querier. */
76 :
77 0 : addr_compare = memcmp(packet->gmp_packet_src_addr.gmp_addr,
78 0 : intf->rintf_local_addr.gmp_addr,
79 0 : instance->rinst_addrlen);
80 :
81 : /* If the querier has a higher address, or is us, bail. */
82 :
83 0 : if (addr_compare >= 0)
84 0 : return;
85 : }
86 :
87 : /* Update the querier status. */
88 :
89 0 : gmpr_update_querier(intf, &packet->gmp_packet_src_addr, FALSE);
90 :
91 : /* Update the robustness variable. */
92 :
93 0 : gmpr_intf_update_robustness(intf, query_pkt->gmp_query_qrv);
94 :
95 : /* Update the query interval if this packet carries QQI. */
96 :
97 0 : if (packet->gmp_packet_version == GMP_VERSION_SOURCES)
98 0 : gmpr_intf_update_query_ivl(intf, query_pkt->gmp_query_qqi);
99 :
100 : /* Start the other-querier timer now that we've updated the intervals. */
101 :
102 0 : gmpx_start_timer(intf->rintf_other_querier_present,
103 : intf->rintf_other_querier_ivl, 0);
104 :
105 : /*
106 : * If the suppress-router-side-processing flag is clear and a
107 : * group is present, take a look at the rest of the packet.
108 : */
109 0 : if (!query_pkt->gmp_query_suppress && query_pkt->gmp_query_group_query) {
110 :
111 : /* Look up the group. */
112 :
113 0 : group = gmpr_group_lookup(intf, query_pkt->gmp_query_group.gmp_addr);
114 0 : if (group) {
115 :
116 : /* Got a group. See if this is a GSS query. */
117 :
118 0 : if (query_pkt->gmp_query_rcv_srcs) {
119 :
120 : /*
121 : * GSS query. Walk the source address list, updating
122 : * the source timers.
123 : */
124 0 : addr_thread_entry = NULL;
125 : while (TRUE) {
126 0 : source_addr = gmp_next_addr_thread_addr(
127 : query_pkt->gmp_query_rcv_srcs,
128 : &addr_thread_entry);
129 0 : if (!source_addr)
130 0 : break;
131 :
132 : /* Got a source address. Look up the catalog entry. */
133 :
134 : cat_entry =
135 0 : gmp_lookup_addr_cat_entry(&instance->rinst_addr_cat,
136 0 : source_addr->gmp_addr);
137 :
138 : /*
139 : * If there's a catalog entry, look up the source
140 : * on the running-timer list.
141 : */
142 0 : if (cat_entry) {
143 0 : addr_entry = gmp_lookup_addr_entry(
144 : &group->rgroup_src_addr_running,
145 : cat_entry->adcat_ent_ord);
146 :
147 : /* If the entry is found, update the timer. */
148 :
149 0 : if (addr_entry) {
150 : group_addr =
151 0 : gmpr_addr_entry_to_group_entry(addr_entry);
152 0 : gmpx_start_timer(group_addr->rgroup_addr_timer,
153 : intf->rintf_lmqt, 0);
154 : }
155 : }
156 : }
157 :
158 : } else {
159 :
160 : /* Group-specific only. Update the group timer. */
161 :
162 0 : gmpx_start_timer(group->rgroup_group_timer, intf->rintf_lmqt,
163 : 0);
164 : }
165 : }
166 : }
167 : }
168 :
169 :
170 : /*
171 : * gmpr_enqueue_group_query
172 : *
173 : * Enqueues a group query.
174 : */
175 : static void
176 27 : gmpr_enqueue_group_query (gmpr_group *group)
177 : {
178 : gmpr_intf *intf;
179 :
180 27 : intf = group->rgroup_intf;
181 :
182 : /* Bail if this version doesn't support group queries. */
183 :
184 27 : if (group->rgroup_compatibility_mode < GMP_VERSION_LEAVES)
185 0 : return;
186 :
187 : /* Bail if we're doing fast leaves on this interface. */
188 :
189 27 : if (intf->rintf_fast_leaves)
190 0 : return;
191 :
192 : /* Lower the group timer to LMQT. */
193 :
194 27 : if (!gmpx_timer_running(group->rgroup_group_timer) ||
195 27 : (gmpx_timer_time_remaining(group->rgroup_group_timer) >
196 27 : intf->rintf_lmqt)) {
197 16 : gmpx_start_timer(group->rgroup_group_timer, intf->rintf_lmqt, 0);
198 : }
199 :
200 : /* Bail if we're suppressing GS/GSS queries on this interface. */
201 :
202 27 : if (intf->rintf_suppress_gs_query)
203 0 : return;
204 :
205 : /* Set the retransmission count. */
206 :
207 27 : group->rgroup_query_rexmit_count = intf->rintf_lmq_count;
208 :
209 : /* Kick the query timer. */
210 :
211 27 : gmpx_start_timer(group->rgroup_query_timer, 0, 0);
212 : }
213 :
214 :
215 : /*
216 : * gmpr_create_running_list_entry
217 : *
218 : * Create a new entry for the running-timer list and create a timer
219 : * for it.
220 : *
221 : * Returns a pointer to the new entry, or NULL if out of memory or the
222 : * channel limit was hit.
223 : */
224 : static gmpr_group_addr_entry *
225 33 : gmpr_create_running_list_entry (gmpr_group *group, bv_bitnum_t bitnum)
226 : {
227 : gmp_addr_list_entry *addr_entry;
228 :
229 : /* Enqueue a new entry. */
230 :
231 33 : addr_entry = gmp_create_addr_list_entry(&group->rgroup_src_addr_running,
232 : bitnum);
233 33 : return gmpr_addr_entry_to_group_entry(addr_entry);
234 : }
235 :
236 :
237 : /*
238 : * gmpr_copy_reporter
239 : *
240 : * Copy the last reporter address from the group to a source. Whenever
241 : * a group is mentioned in a report, the reporter address is written into
242 : * the group entry. When set operations result, they are peppered with calls
243 : * to this routine to put the reporter's address into the source. The group
244 : * happens to be a convenient place that we know was just updated.
245 : */
246 : static void
247 121 : gmpr_copy_reporter (gmpr_group *group,
248 : gmpr_group_addr_entry *group_addr_entry)
249 : {
250 121 : memmove(group_addr_entry->rgroup_addr_last_reporter.gmp_addr,
251 121 : group->rgroup_last_reporter.gmp_addr,
252 121 : group->rgroup_intf->rintf_instance->rinst_addrlen);
253 121 : }
254 :
255 :
256 : /*
257 : * gmpr_move_include_cb
258 : *
259 : * Vector walk callback to move entries to the include list. We look
260 : * up the address in the stopped-timer list, and move it to the
261 : * running-timer list if it's there. If the entry is in the
262 : * running-timer list, we update the timer. If it's not there, we add
263 : * it.
264 : */
265 : static boolean
266 0 : gmpr_move_include_cb (void *context, bv_bitnum_t bitnum,
267 : boolean new_val GMPX_UNUSED,
268 : boolean old_val GMPX_UNUSED)
269 : {
270 : gmpr_group *group;
271 : gmp_addr_list_entry *addr_entry;
272 : gmpr_group_addr_entry *group_addr_entry;
273 :
274 0 : group = context;
275 :
276 : /* See if the address is present on the stopped-timer list. */
277 :
278 0 : if (gmp_addr_in_list(&group->rgroup_src_addr_stopped, bitnum)) {
279 :
280 : /* Entry is in the stopped list list. Look it up. */
281 :
282 0 : addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_stopped,
283 : bitnum);
284 0 : gmpx_assert(addr_entry);
285 :
286 : /* Move it to the running list. */
287 :
288 0 : gmp_move_addr_list_entry(&group->rgroup_src_addr_running, addr_entry);
289 :
290 : /* Update the OIF, as the source is no longer excluded. */
291 :
292 0 : group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
293 0 : gmpr_update_source_oif(group_addr_entry, OIF_DELETE);
294 :
295 : } else {
296 :
297 : /*
298 : * Not in the stopped-timer list. See if the address is
299 : * present in the running-timer list. If it is, look it up.
300 : * If not, allocate a new entry and put it into the list.
301 : */
302 0 : if (!gmp_addr_in_list(&group->rgroup_src_addr_running, bitnum)) {
303 :
304 : /* Not in the list. Allocate a new one and put it in there. */
305 :
306 0 : group_addr_entry = gmpr_create_running_list_entry(group, bitnum);
307 0 : if (!group_addr_entry)
308 0 : return FALSE; /* Out of memory or limit hit */
309 :
310 : } else {
311 :
312 : /* Entry is in the list. Look it up. */
313 :
314 0 : addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
315 : bitnum);
316 0 : gmpx_assert(addr_entry);
317 0 : group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
318 : }
319 : }
320 :
321 : /* Bump the timer. */
322 :
323 0 : gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
324 0 : group->rgroup_intf->rintf_group_membership_ivl, 0);
325 :
326 : /* Copy the reporter address from the group. */
327 :
328 0 : gmpr_copy_reporter(group, group_addr_entry);
329 :
330 0 : return FALSE;
331 : }
332 :
333 :
334 : /*
335 : * gmpr_move_include
336 : *
337 : * Move the source list from the stopped-timer to the running-timer
338 : * list, creating new entries for any sources not present on either list.
339 : *
340 : * Bumps the source timer of each source listed in the record.
341 : */
342 : static void
343 0 : gmpr_move_include (gmpr_group *group, gmp_addr_vect *source_vect)
344 : {
345 : /* Walk the vector. The callback does all the work. */
346 :
347 0 : gmp_addr_vect_walk(source_vect, gmpr_move_include_cb, group);
348 0 : }
349 :
350 :
351 : /*
352 : * gmpr_delete_include_cb
353 : *
354 : * Vector walk for culling the running-timer list. We delete any
355 : * entry that we're passed.
356 : */
357 : static boolean
358 0 : gmpr_delete_include_cb (void *context, bv_bitnum_t bitnum,
359 : boolean new_val GMPX_UNUSED,
360 : boolean old_val GMPX_UNUSED)
361 : {
362 : gmpr_group *group;
363 : gmp_addr_list_entry *addr_entry;
364 :
365 0 : group = context;
366 :
367 : /* Look up the entry. */
368 :
369 0 : addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
370 : bitnum);
371 0 : gmpx_assert(addr_entry);
372 :
373 : /* Delete it. The callback will free the timer. */
374 :
375 0 : gmp_delete_addr_list_entry(addr_entry);
376 :
377 0 : return FALSE;
378 : }
379 :
380 :
381 : /*
382 : * gmpr_delete_exclude_cb
383 : *
384 : * Vector walk for culling the stopped-timer list. We move any
385 : * entry we're passed to the deleted list and notify the clients,
386 : * since the source is no longer being excluded.
387 : */
388 : static boolean
389 0 : gmpr_delete_exclude_cb (void *context, bv_bitnum_t bitnum,
390 : boolean new_val GMPX_UNUSED,
391 : boolean old_val GMPX_UNUSED)
392 : {
393 : gmpr_group *group;
394 : gmp_addr_list_entry *addr_entry;
395 : gmpr_group_addr_entry *group_addr;
396 :
397 0 : group = context;
398 :
399 : /* Look up the entry. */
400 :
401 0 : addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_stopped,
402 : bitnum);
403 0 : gmpx_assert(addr_entry);
404 :
405 : /* Delink any OIF entry. */
406 :
407 0 : group_addr = gmpr_addr_entry_to_group_entry(addr_entry);
408 0 : gmpr_update_source_oif(group_addr, OIF_DELETE);
409 :
410 : /* Delete the source. */
411 :
412 0 : gmp_delete_addr_list_entry(addr_entry);
413 :
414 0 : return FALSE;
415 : }
416 :
417 :
418 : /*
419 : * gmpr_add_exclude_cb
420 : *
421 : * Vector walk callback for adding a source to the exclude
422 : * (stopped-timer) list. We assume that this is a new source.
423 : */
424 : static boolean
425 0 : gmpr_add_exclude_cb (void *context, bv_bitnum_t bitnum,
426 : boolean new_val GMPX_UNUSED,
427 : boolean old_val GMPX_UNUSED)
428 : {
429 : gmpr_group *group;
430 : gmp_addr_list_entry *addr_entry;
431 : gmpr_group_addr_entry *group_addr;
432 :
433 0 : group = context;
434 :
435 : /* Allocate a new entry and put it in the stopped list. */
436 :
437 0 : addr_entry = gmp_create_addr_list_entry(&group->rgroup_src_addr_stopped,
438 : bitnum);
439 0 : if (!addr_entry)
440 0 : return FALSE; /* No memory or limit hit */
441 :
442 : /* Copy the reporter address from the group. */
443 :
444 0 : group_addr = gmpr_addr_entry_to_group_entry(addr_entry);
445 0 : gmpr_copy_reporter(group, group_addr);
446 :
447 0 : return FALSE;
448 : }
449 :
450 :
451 : /*
452 : * gmpr_add_include_cb
453 : *
454 : * Vector walk callback for adding a set of sources to the include
455 : * list. We look up the address in the timer-running list, add it if
456 : * it's not there, and bump up the timer.
457 : */
458 : static boolean
459 59 : gmpr_add_include_cb (void *context, bv_bitnum_t bitnum,
460 : boolean new_val GMPX_UNUSED,
461 : boolean old_val GMPX_UNUSED)
462 : {
463 : gmpr_group *group;
464 : gmp_addr_list_entry *addr_entry;
465 : gmpr_group_addr_entry *group_addr_entry;
466 :
467 59 : group = context;
468 :
469 : /*
470 : * See if the address is present in the running-timer list. If it is,
471 : * look it up. If not, allocate a new entry and put it into the list.
472 : */
473 59 : if (!gmp_addr_in_list(&group->rgroup_src_addr_running, bitnum)) {
474 :
475 : /* Not in the list. Allocate a new one and put it in there. */
476 :
477 33 : group_addr_entry = gmpr_create_running_list_entry(group, bitnum);
478 33 : if (!group_addr_entry)
479 0 : return FALSE; /* Out of memory or limit hit */
480 33 : addr_entry = &group_addr_entry->rgroup_addr_entry;
481 :
482 : /* Update the OIF. */
483 :
484 33 : gmpr_update_source_oif(group_addr_entry, OIF_UPDATE);
485 :
486 : } else {
487 :
488 : /* Entry is in the list. Look it up. */
489 :
490 26 : addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
491 : bitnum);
492 26 : gmpx_assert(addr_entry);
493 : }
494 :
495 59 : group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
496 :
497 : /* Bump the timer. */
498 :
499 59 : gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
500 59 : group->rgroup_intf->rintf_group_membership_ivl, 0);
501 :
502 : /* Copy the reporter address from the group. */
503 :
504 59 : gmpr_copy_reporter(group, group_addr_entry);
505 :
506 59 : return FALSE;
507 : }
508 :
509 :
510 : /*
511 : * gmpr_enqueue_gss_query
512 : *
513 : * Do the work to set up a GSS query for a (group,source) pair.
514 : */
515 : static void
516 39 : gmpr_enqueue_gss_query (gmpr_group *group,
517 : gmpr_group_addr_entry *group_addr_entry)
518 : {
519 : gmpr_intf *intf;
520 :
521 39 : intf = group->rgroup_intf;
522 :
523 : /* Drop the source timer if appropriate. */
524 :
525 39 : if (!gmpx_timer_running(group_addr_entry->rgroup_addr_timer) ||
526 39 : (gmpx_timer_time_remaining(group_addr_entry->rgroup_addr_timer) >
527 39 : intf->rintf_lmqt)) {
528 39 : gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
529 : intf->rintf_lmqt, 0);
530 : }
531 :
532 : /* Bail if the current version doesn't support GSS queries. */
533 :
534 39 : if (group->rgroup_compatibility_mode < GMP_VERSION_SOURCES)
535 0 : return;
536 :
537 : /* Bail if we're doing fast leaves on this interface. */
538 :
539 39 : if (intf->rintf_fast_leaves)
540 0 : return;
541 :
542 : /* Bail if we're suppressing GS and GSS queries. */
543 :
544 39 : if (intf->rintf_suppress_gs_query)
545 0 : return;
546 :
547 : /* Bump the rexmit count back up. */
548 :
549 39 : group_addr_entry->rgroup_addr_rexmit_count = intf->rintf_lmq_count;
550 :
551 : /* Set the GSS timer to expire immediately if it's not already running. */
552 :
553 39 : if (!gmpx_timer_running(group->rgroup_gss_query_timer))
554 15 : gmpx_start_timer(group->rgroup_gss_query_timer, 0, 0);
555 : }
556 :
557 :
558 : /*
559 : * gmpr_send_gss_query_cb
560 : *
561 : * Vector callback to send a group-and-source specific query.
562 : */
563 : static boolean
564 62 : gmpr_send_gss_query_cb (void *context, bv_bitnum_t bitnum,
565 : boolean new_val GMPX_UNUSED,
566 : boolean old_val GMPX_UNUSED)
567 : {
568 : gmpr_group *group;
569 : gmp_addr_list_entry *addr_entry;
570 : gmpr_group_addr_entry *group_addr_entry;
571 : gmpr_intf *intf;
572 :
573 62 : group = context;
574 62 : intf = group->rgroup_intf;
575 :
576 : /* Look up the entry in the running-timer list. It better be there. */
577 :
578 62 : addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
579 : bitnum);
580 62 : gmpx_assert(addr_entry);
581 62 : group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
582 :
583 : /* Process the source if the source timer is greater than LMQT. */
584 :
585 62 : if (gmpx_timer_time_remaining(group_addr_entry->rgroup_addr_timer) >
586 62 : intf->rintf_lmqt) {
587 :
588 : /* Enqueue the source for the GSS query. */
589 :
590 39 : gmpr_enqueue_gss_query(group, group_addr_entry);
591 : }
592 :
593 : /* Copy the reporter address from the group. */
594 :
595 62 : gmpr_copy_reporter(group, group_addr_entry);
596 :
597 62 : return FALSE;
598 : }
599 :
600 :
601 : /*
602 : * gmpr_process_state_chg_ex_in
603 : *
604 : * Process a state-change record of TO_IN type with a filter state of
605 : * Exclude.
606 : *
607 : * We do a bunch of set math.
608 : */
609 : static void
610 27 : gmpr_process_state_chg_ex_in (gmpr_group *group, gmp_addr_vect *source_vect)
611 : {
612 : /*
613 : * First, form the set (X-A) and send a query out on each member.
614 : * These are all of the addresses on the running-timer list that
615 : * were not mentioned in the record.
616 : */
617 27 : if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
618 : source_vect, NULL, gmpr_send_gss_query_cb, group,
619 : BV_CALL_SET) < 0)
620 0 : return; /* Out of memory */
621 :
622 : /*
623 : * Now walk the source list, moving any matching entry from the
624 : * stopped-timer list to the running-timer list, and creating
625 : * new entries for anything not found in either list.
626 : */
627 27 : gmp_addr_vect_walk(source_vect, gmpr_move_include_cb, group);
628 :
629 : /* Finally, enqueue a group-specific query. */
630 :
631 27 : gmpr_enqueue_group_query(group);
632 : }
633 :
634 :
635 : /*
636 : * gmpr_chg_ex_ex_cb
637 : *
638 : * Vector walk to determine new running-timer entries for the
639 : * Exclude/Exclude state-change case. We are given an entry on the
640 : * new source list, and add it to the running-timer list if it is not
641 : * found in either the running-timer or stopped-timer lists.
642 : *
643 : * This does not trigger a state change, since the source will still be
644 : * received whether the entry is in the running-timer list or it is not
645 : * in any list.
646 : */
647 : static boolean
648 0 : gmpr_chg_ex_ex_cb (void *context, bv_bitnum_t bitnum,
649 : boolean new_val GMPX_UNUSED, boolean old_val GMPX_UNUSED)
650 : {
651 : gmpr_group *group;
652 : gmpr_group_addr_entry *group_addr_entry;
653 :
654 0 : group = context;
655 :
656 : /* See if the address is present in the running-timer list. */
657 :
658 0 : if (!gmp_addr_in_list(&group->rgroup_src_addr_running, bitnum)) {
659 :
660 : /*
661 : * Not in the running-timer list. See if it's in the
662 : * stopped-timer list.
663 : */
664 0 : if (!gmp_addr_in_list(&group->rgroup_src_addr_stopped, bitnum)) {
665 :
666 : /*
667 : * Not in either list. Allocate a new one and put it in
668 : * the running-timer list.
669 : */
670 0 : group_addr_entry = gmpr_create_running_list_entry(group, bitnum);
671 0 : if (!group_addr_entry)
672 0 : return FALSE; /* Out of memory or limit hit */
673 0 : gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
674 : gmpx_timer_time_remaining(group->rgroup_group_timer), 0);
675 :
676 : /* Copy the reporter address from the group. */
677 :
678 0 : gmpr_copy_reporter(group, group_addr_entry);
679 : }
680 : }
681 :
682 0 : return FALSE;
683 : }
684 :
685 :
686 : /*
687 : * gmpr_process_state_chg_ex_ex
688 : *
689 : * Process a state-change record of TO_EX type with a filter state of
690 : * Exclude.
691 : *
692 : * We do a bunch of set math.
693 : */
694 : static void
695 0 : gmpr_process_state_chg_ex_ex (gmpr_group *group, gmp_addr_vect *source_vect)
696 : {
697 : /*
698 : * OK, this one is really nutty. By the spec, set A is the set of
699 : * addresses in the new report, X is the set in the running-timer
700 : * list, and Y is the set in the stopped-timer list. By definition,
701 : * X and Y are non-overlapping.
702 : *
703 : * First, we delete X-A. This eliminates all addresses from the
704 : * running-timer list, except for (A*X).
705 : */
706 0 : if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
707 : source_vect, NULL, gmpr_delete_include_cb, group,
708 : BV_CALL_SET) < 0)
709 0 : return; /* Out of memory */
710 :
711 : /*
712 : * Now, delete Y-A. This eliminates all addresses from the stopped-timer
713 : * list, except for (A*Y).
714 : */
715 0 : if (gmp_addr_vect_minus(&group->rgroup_src_addr_stopped.addr_vect,
716 : source_vect, NULL, gmpr_delete_exclude_cb, group,
717 : BV_CALL_SET) < 0)
718 0 : return; /* Out of memory */
719 :
720 : /*
721 : * Next, walk A, adding to the running list any address not found
722 : * on what remains of the running and stopped lists. This forms the
723 : * set (A-X-Y). The net result is that the running list contains
724 : * (A-X-Y) + (A*X), which is the same as (A-Y). Whew.
725 : */
726 0 : gmp_addr_vect_walk(source_vect, gmpr_chg_ex_ex_cb, group);
727 :
728 : /* Now send a query on everything on the running list, which is (A-Y). */
729 :
730 0 : gmp_addr_vect_walk(&group->rgroup_src_addr_running.addr_vect,
731 : gmpr_send_gss_query_cb, group);
732 :
733 : /* Finally, restart the group timer. */
734 :
735 0 : gmpx_start_timer(group->rgroup_group_timer,
736 0 : group->rgroup_intf->rintf_group_membership_ivl, 0);
737 : }
738 :
739 :
740 : /*
741 : * gmpr_state_chg_ex_block_cb
742 : *
743 : * Vector callback for the receipt of a BLOCK record in Exclude state.
744 : *
745 : * We're called for each source in the BLOCK list that is not in the
746 : * stopped-timer list.
747 : */
748 : static boolean
749 0 : gmpr_state_chg_ex_block_cb (void *context, bv_bitnum_t bitnum,
750 : boolean new_val GMPX_UNUSED,
751 : boolean old_val GMPX_UNUSED)
752 : {
753 : gmpr_group *group;
754 : gmp_addr_list_entry *addr_entry;
755 : gmpr_group_addr_entry *group_addr_entry;
756 : gmpr_intf *intf;
757 :
758 0 : group = context;
759 0 : intf = group->rgroup_intf;
760 :
761 : /* If the entry is not in the running-timer list, create it. */
762 :
763 0 : if (!gmp_addr_in_list(&group->rgroup_src_addr_running, bitnum)) {
764 :
765 : /*
766 : * Not in the list. Allocate a new one and put it in the
767 : * running-timer list.
768 : */
769 0 : group_addr_entry = gmpr_create_running_list_entry(group, bitnum);
770 0 : if (!group_addr_entry)
771 0 : return FALSE; /* Out of memory or limit hit */
772 0 : addr_entry = &group_addr_entry->rgroup_addr_entry;
773 0 : gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
774 : gmpx_timer_time_remaining(group->rgroup_group_timer),
775 : 0);
776 :
777 : } else {
778 :
779 : /* Look up the entry in the running-timer list. It better be there. */
780 :
781 0 : addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
782 : bitnum);
783 0 : gmpx_assert(addr_entry);
784 0 : group_addr_entry = gmpr_addr_entry_to_group_entry(addr_entry);
785 : }
786 :
787 : /* Send a query if the source if the source timer is greater than LMQT. */
788 :
789 0 : if (gmpx_timer_time_remaining(group_addr_entry->rgroup_addr_timer) >
790 0 : intf->rintf_lmqt) {
791 :
792 : /* Enqueue the source for the GSS query. */
793 :
794 0 : gmpr_enqueue_gss_query(group, group_addr_entry);
795 : }
796 :
797 : /* Copy the reporter address from the group. */
798 :
799 0 : gmpr_copy_reporter(group, group_addr_entry);
800 :
801 0 : return FALSE;
802 : }
803 :
804 :
805 : /*
806 : * gmpr_process_state_chg_ex_block
807 : *
808 : * Process a state-change record of type BLOCK with a filter state of
809 : * Exclude.
810 : */
811 : static void
812 0 : gmpr_process_state_chg_ex_block (gmpr_group *group, gmp_addr_vect *source_vect)
813 : {
814 : /*
815 : * Form the set (A-Y) (all addresses mentioned in the BLOCK that are
816 : * not in the stopped-timer list.) Add these to the running-timer list,
817 : * setting the timers on those not already in that list. Also, send
818 : * a query on each of them.
819 : */
820 0 : if (gmp_addr_vect_minus(source_vect,
821 : &group->rgroup_src_addr_stopped.addr_vect,
822 : NULL, gmpr_state_chg_ex_block_cb, group,
823 : BV_CALL_SET) < 0)
824 0 : return; /* Out of memory */
825 : }
826 :
827 :
828 : /*
829 : * gmpr_process_state_chg_in_ex
830 : *
831 : * Process a state-change record of type TO_EX with a filter state of
832 : * Include.
833 : */
834 : static void
835 0 : gmpr_process_state_chg_in_ex (gmpr_group *group, gmp_addr_vect *source_vect)
836 : {
837 0 : gmpx_assert(gmp_addr_list_empty(&group->rgroup_src_addr_stopped));
838 :
839 : /* Change the filter mode. */
840 :
841 0 : group->rgroup_filter_mode = GMP_FILTER_MODE_EXCLUDE;
842 :
843 : /*
844 : * Form the set (B-A) (all newly-mentioned sources) and stick them
845 : * straight into the stopped-timer list.
846 : */
847 0 : if (gmp_addr_vect_minus(source_vect,
848 : &group->rgroup_src_addr_running.addr_vect, NULL,
849 : gmpr_add_exclude_cb, group, BV_CALL_SET) < 0)
850 0 : return; /* Out of memory */
851 :
852 : /*
853 : * Form the set (A-B) (all addresses mentioned in the Include set
854 : * that are not in the TO_IN set) and delete them.
855 : */
856 0 : if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
857 : source_vect, NULL, gmpr_delete_include_cb, group,
858 : BV_CALL_SET) < 0)
859 0 : return; /* Out of memory */
860 :
861 : /*
862 : * Send a query for (A*B), which is what's left on the running-timer
863 : * list.
864 : */
865 0 : gmp_addr_vect_walk(&group->rgroup_src_addr_running.addr_vect,
866 : gmpr_send_gss_query_cb, group);
867 :
868 : /* Restart the group timer. */
869 :
870 0 : gmpx_start_timer(group->rgroup_group_timer,
871 0 : group->rgroup_intf->rintf_group_membership_ivl, 0);
872 :
873 : /* Update the OIF. */
874 :
875 0 : gmpr_update_oif_mode_change(group);
876 : }
877 :
878 :
879 : /*
880 : * gmpr_process_state_chg_in_in
881 : *
882 : * Process a state-change record of type TO_IN with a filter state of
883 : * Include.
884 : */
885 : static void
886 0 : gmpr_process_state_chg_in_in (gmpr_group *group, gmp_addr_vect *source_vect)
887 : {
888 0 : gmpx_assert(gmp_addr_list_empty(&group->rgroup_src_addr_stopped));
889 :
890 : /* Add any new sources to the Include (running-timer) list. */
891 :
892 0 : gmp_addr_vect_walk(source_vect, gmpr_add_include_cb, group);
893 :
894 : /*
895 : * Form the set (A-B) (all addresses mentioned in the Include set
896 : * that are not in the TO_IN set) and send a Query on them.
897 : */
898 0 : if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
899 : source_vect, NULL, gmpr_send_gss_query_cb, group,
900 : BV_CALL_SET) < 0)
901 0 : return; /* Out of memory */
902 : }
903 :
904 :
905 : /*
906 : * gmpr_process_state_chg_in_block
907 : *
908 : * Process a state-change record of type BLOCK with a filter state of
909 : * Include.
910 : */
911 : static void
912 30 : gmpr_process_state_chg_in_block (gmpr_group *group, gmp_addr_vect *source_vect)
913 : {
914 : /*
915 : * Form the set (A*B) (all addresses mentioned in the BLOCK that are
916 : * currently in the Include set) and send a Query on them.
917 : */
918 30 : gmpx_assert(gmp_addr_list_empty(&group->rgroup_src_addr_stopped));
919 30 : if (gmp_addr_vect_inter(&group->rgroup_src_addr_running.addr_vect,
920 : source_vect, NULL, gmpr_send_gss_query_cb, group,
921 : BV_CALL_SET) < 0)
922 0 : return; /* Out of memory */
923 : }
924 :
925 :
926 : /*
927 : * gmpr_add_include
928 : *
929 : * Process a current-state record of type IS_IN or a state-change
930 : * record of ALLOW with a filter state of Include.
931 : *
932 : * Update the timer-running list to include any new sources in the
933 : * record, and bump the source timer of each source listed in the
934 : * record.
935 : */
936 : static void
937 29 : gmpr_add_include (gmpr_group *group, gmp_addr_vect *source_vect)
938 : {
939 : /* Walk the vector. The callback does all the work. */
940 :
941 29 : gmpx_assert(gmp_addr_list_empty(&group->rgroup_src_addr_stopped));
942 29 : gmp_addr_vect_walk(source_vect, gmpr_add_include_cb, group);
943 29 : }
944 :
945 :
946 : /*
947 : * gmpr_process_state_chg_rcrd
948 : *
949 : * Process a state-change record.
950 : */
951 : static void
952 57 : gmpr_process_state_chg_rcrd (gmpr_group *group,
953 : gmp_report_rectype rec_type,
954 : gmp_addr_vect *source_vect)
955 : {
956 : /*
957 : * Process based on our current filter state, combined with the
958 : * received record type.
959 : */
960 57 : switch (group->rgroup_filter_mode) {
961 30 : case GMP_FILTER_MODE_INCLUDE:
962 : switch (rec_type) {
963 0 : case GMP_RPT_ALLOW:
964 0 : gmpr_add_include(group, source_vect);
965 0 : break;
966 :
967 30 : case GMP_RPT_BLOCK:
968 30 : gmpr_process_state_chg_in_block(group, source_vect);
969 30 : break;
970 :
971 0 : case GMP_RPT_TO_IN:
972 0 : gmpr_process_state_chg_in_in(group, source_vect);
973 0 : break;
974 :
975 0 : case GMP_RPT_TO_EX:
976 0 : gmpr_process_state_chg_in_ex(group, source_vect);
977 0 : break;
978 :
979 0 : default:
980 0 : gmpx_assert(FALSE);
981 : break;
982 : }
983 30 : break;
984 :
985 27 : case GMP_FILTER_MODE_EXCLUDE:
986 : switch (rec_type) {
987 0 : case GMP_RPT_ALLOW:
988 0 : gmpr_move_include(group, source_vect);
989 0 : break;
990 :
991 0 : case GMP_RPT_BLOCK:
992 0 : gmpr_process_state_chg_ex_block(group, source_vect);
993 0 : break;
994 :
995 27 : case GMP_RPT_TO_IN:
996 27 : gmpr_process_state_chg_ex_in(group, source_vect);
997 27 : break;
998 :
999 0 : case GMP_RPT_TO_EX:
1000 0 : gmpr_process_state_chg_ex_ex(group, source_vect);
1001 0 : break;
1002 :
1003 0 : default:
1004 0 : gmpx_assert(FALSE);
1005 : break;
1006 : }
1007 27 : break;
1008 :
1009 0 : default:
1010 0 : gmpx_assert(FALSE);
1011 : }
1012 57 : }
1013 :
1014 :
1015 : /*
1016 : * gmpr_ex_ex_new_cb
1017 : *
1018 : * Vector walk to determine new running-timer entries for the
1019 : * Exclude/Exclude case. We are given an entry on the new source
1020 : * list, and add it to the running-timer list if it is not found in
1021 : * either the running-timer or stopped-timer lists.
1022 : *
1023 : * This does not trigger a state change, since the source will still be
1024 : * received whether the entry is in the running-timer list or it is not
1025 : * in any list.
1026 : */
1027 : static boolean
1028 0 : gmpr_ex_ex_new_cb (void *context, bv_bitnum_t bitnum,
1029 : boolean new_val GMPX_UNUSED, boolean old_val GMPX_UNUSED)
1030 : {
1031 : gmpr_group *group;
1032 : gmpr_group_addr_entry *group_addr_entry;
1033 :
1034 0 : group = context;
1035 :
1036 : /* See if the address is present in the running-timer list. */
1037 :
1038 0 : if (!gmp_addr_in_list(&group->rgroup_src_addr_running, bitnum)) {
1039 :
1040 : /*
1041 : * Not in the running-timer list. See if it's in the
1042 : * stopped-timer list.
1043 : */
1044 0 : if (!gmp_addr_in_list(&group->rgroup_src_addr_stopped, bitnum)) {
1045 :
1046 : /*
1047 : * Not in either list. Allocate a new one and put it in
1048 : * the running-timer list.
1049 : */
1050 0 : group_addr_entry = gmpr_create_running_list_entry(group, bitnum);
1051 0 : if (!group_addr_entry)
1052 0 : return FALSE; /* Out of memory or limit hit */
1053 0 : gmpx_start_timer(group_addr_entry->rgroup_addr_timer,
1054 0 : group->rgroup_intf->rintf_group_membership_ivl,
1055 : 0);
1056 :
1057 : /* Copy the reporter address from the group. */
1058 :
1059 0 : gmpr_copy_reporter(group, group_addr_entry);
1060 : }
1061 : }
1062 :
1063 0 : return FALSE;
1064 : }
1065 :
1066 :
1067 : /*
1068 : * gmpr_process_cur_state_ex_ex
1069 : *
1070 : * Process a current-state record of IS_EX type with a filter state of
1071 : * Exclude.
1072 : *
1073 : * We do a bunch of set math.
1074 : */
1075 : static void
1076 27 : gmpr_process_cur_state_ex_ex (gmpr_group *group, gmp_addr_vect *source_vect)
1077 : {
1078 : /*
1079 : * OK, this one is really nutty. By the spec, set A is the set of
1080 : * addresses in the new report, X is the set in the running-timer
1081 : * list, and Y is the set in the stopped-timer list. By definition,
1082 : * X and Y are non-overlapping.
1083 : *
1084 : * First, we delete X-A. This eliminates all addresses from the
1085 : * running-timer list, except for (A*X).
1086 : */
1087 27 : if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
1088 : source_vect, NULL, gmpr_delete_include_cb, group,
1089 : BV_CALL_SET) < 0)
1090 0 : return; /* Out of memory */
1091 :
1092 : /*
1093 : * Now, delete Y-A. This eliminates all addresses from the stopped-timer
1094 : * list, except for (A*Y).
1095 : */
1096 27 : if (gmp_addr_vect_minus(&group->rgroup_src_addr_stopped.addr_vect,
1097 : source_vect, NULL, gmpr_delete_exclude_cb, group,
1098 : BV_CALL_SET) < 0)
1099 0 : return; /* Out of memory */
1100 :
1101 : /*
1102 : * Next, walk A, adding to the running list any address not found
1103 : * on what remains of the running and stopped lists. This forms the
1104 : * set (A-X-Y). The net result is that the running list contains
1105 : * (A-X-Y) + (A*X), which is the same as (A-Y). Whew.
1106 : */
1107 27 : gmp_addr_vect_walk(source_vect, gmpr_ex_ex_new_cb, group);
1108 :
1109 : /* Finally, restart the group timer. */
1110 :
1111 27 : gmpx_start_timer(group->rgroup_group_timer,
1112 27 : group->rgroup_intf->rintf_group_membership_ivl, 0);
1113 : }
1114 :
1115 :
1116 : /*
1117 : * gmpr_in_ex_running_cb
1118 : *
1119 : * Vector walk callback for Include/Exclude current-state. We're getting
1120 : * called for any entry on the running-timer list that's not on the exclude
1121 : * list. We delete such entries from the running-timer list.
1122 : */
1123 : static boolean
1124 0 : gmpr_in_ex_running_cb (void *context, bv_bitnum_t bitnum,
1125 : boolean new_val GMPX_UNUSED,
1126 : boolean old_val GMPX_UNUSED)
1127 : {
1128 : gmpr_group *group;
1129 : gmp_addr_list_entry *addr_entry;
1130 :
1131 0 : group = context;
1132 :
1133 : /* Look up the entry. */
1134 :
1135 0 : addr_entry = gmp_lookup_addr_entry(&group->rgroup_src_addr_running,
1136 : bitnum);
1137 0 : gmpx_assert(addr_entry);
1138 :
1139 : /* Delete it. The callback will free the timer. */
1140 :
1141 0 : gmp_delete_addr_list_entry(addr_entry);
1142 :
1143 0 : return FALSE;
1144 : }
1145 :
1146 :
1147 : /*
1148 : * gmpr_process_cur_state_in_ex
1149 : *
1150 : * Process a current-state record of IS_EX type with a filter state of
1151 : * Include.
1152 : *
1153 : * We switch filter modes from Include to Exclude and do a bunch of
1154 : * set math.
1155 : */
1156 : static void
1157 22 : gmpr_process_cur_state_in_ex (gmpr_group *group, gmp_addr_vect *source_vect)
1158 : {
1159 : /* Change the filter mode to Exclude. */
1160 :
1161 22 : group->rgroup_filter_mode = GMP_FILTER_MODE_EXCLUDE;
1162 :
1163 : /*
1164 : * Calculate (B-A), where B is the set of sources in the new record
1165 : * and A is the set of sources in the running-timer list. These
1166 : * new entries are put into the stopped-timer list.
1167 : */
1168 22 : gmpx_assert(gmp_addr_list_empty(&group->rgroup_src_addr_stopped));
1169 22 : if (gmp_addr_vect_minus(source_vect,
1170 : &group->rgroup_src_addr_running.addr_vect, NULL,
1171 : gmpr_add_exclude_cb, group, BV_CALL_SET) < 0)
1172 0 : return; /* Out of memory */
1173 :
1174 : /*
1175 : * Now calculate (A-B). The resultant entries are deleted from
1176 : * the running list.
1177 : */
1178 22 : if (gmp_addr_vect_minus(&group->rgroup_src_addr_running.addr_vect,
1179 : source_vect, NULL, gmpr_in_ex_running_cb, group,
1180 : BV_CALL_SET) < 0)
1181 0 : return; /* Out of memory */
1182 :
1183 : /* Restart the group timer. */
1184 :
1185 22 : gmpx_start_timer(group->rgroup_group_timer,
1186 22 : group->rgroup_intf->rintf_group_membership_ivl, 0);
1187 :
1188 : /* Update the OIF. */
1189 :
1190 22 : gmpr_update_oif_mode_change(group);
1191 : }
1192 :
1193 :
1194 : /*
1195 : * gmpr_process_cur_state_rcrd
1196 : *
1197 : * Process a current-state record.
1198 : */
1199 : static void
1200 78 : gmpr_process_cur_state_rcrd (gmpr_group *group,
1201 : gmp_report_rectype rec_type,
1202 : gmp_addr_vect *source_vect)
1203 : {
1204 : /*
1205 : * Process based on our current filter state, combined with the
1206 : * received record type.
1207 : */
1208 78 : switch (group->rgroup_filter_mode) {
1209 51 : case GMP_FILTER_MODE_INCLUDE:
1210 :
1211 : switch (rec_type) {
1212 29 : case GMP_RPT_IS_IN:
1213 29 : gmpr_add_include(group, source_vect);
1214 29 : break;
1215 :
1216 22 : case GMP_RPT_IS_EX:
1217 22 : gmpr_process_cur_state_in_ex(group, source_vect);
1218 22 : break;
1219 :
1220 0 : default:
1221 0 : gmpx_assert(FALSE);
1222 : break;
1223 : }
1224 51 : break;
1225 :
1226 27 : case GMP_FILTER_MODE_EXCLUDE:
1227 :
1228 : switch (rec_type) {
1229 0 : case GMP_RPT_IS_IN:
1230 0 : gmpr_move_include(group, source_vect);
1231 0 : break;
1232 :
1233 27 : case GMP_RPT_IS_EX:
1234 27 : gmpr_process_cur_state_ex_ex(group, source_vect);
1235 27 : break;
1236 :
1237 0 : default:
1238 0 : gmpx_assert(FALSE);
1239 : break;
1240 : }
1241 27 : break;
1242 :
1243 0 : default:
1244 0 : gmpx_assert(FALSE);
1245 : }
1246 78 : }
1247 :
1248 :
1249 : /*
1250 : * gmpr_update_version_compatibility_mode
1251 : *
1252 : * Update the version compatibility mode for this group based on the
1253 : * received packet.
1254 : */
1255 : static void
1256 135 : gmpr_update_version_compatibility_mode (gmpr_group *group, gmp_version ver)
1257 : {
1258 : gmpr_intf *intf;
1259 : uint32_t old_host_ivl;
1260 :
1261 135 : intf = group->rgroup_intf;
1262 :
1263 : /* Calculate the older-host-present interval. */
1264 :
1265 135 : old_host_ivl = (intf->rintf_robustness * intf->rintf_query_ivl) +
1266 135 : intf->rintf_query_resp_ivl;
1267 :
1268 : /* Start the appropriate timer. */
1269 :
1270 135 : if (ver == GMP_VERSION_BASIC) {
1271 1 : gmpx_start_timer(group->rgroup_basic_host_present, old_host_ivl, 0);
1272 134 : } else if (ver == GMP_VERSION_LEAVES) {
1273 75 : gmpx_start_timer(group->rgroup_leaves_host_present, old_host_ivl, 0);
1274 : }
1275 :
1276 : /* Evaluate the group version. */
1277 :
1278 135 : gmpr_evaluate_group_version(group);
1279 135 : }
1280 :
1281 :
1282 : /*
1283 : * gmpr_harmonize_report_version
1284 : *
1285 : * Harmonize the report based on the current group compatibility version.
1286 : * We may modify the report contents, or even discard it.
1287 : *
1288 : * Returns TRUE if the group record should continue to be processed, or
1289 : * FALSE if it should be ignored.
1290 : */
1291 : static boolean
1292 140 : gmpr_harmonize_report_version (gmp_version group_version,
1293 : gmp_report_group_record *group_rcrd)
1294 : {
1295 : /* Bail if we're running the latest. */
1296 :
1297 140 : if (group_version == GMP_VERSION_SOURCES)
1298 60 : return TRUE;
1299 :
1300 : /* LEAVES or BASIC version. Ignore any BLOCK messages. */
1301 :
1302 80 : if (group_rcrd->gmp_rpt_type == GMP_RPT_BLOCK)
1303 0 : return FALSE;
1304 :
1305 : /*
1306 : * If the record is a non-null TO_IN or IS_IN or ALLOW, change it to a null
1307 : * TO_EX.
1308 : */
1309 80 : if (group_rcrd->gmp_rpt_rcv_srcs &&
1310 0 : (group_rcrd->gmp_rpt_type == GMP_RPT_TO_IN ||
1311 0 : group_rcrd->gmp_rpt_type == GMP_RPT_IS_IN ||
1312 0 : group_rcrd->gmp_rpt_type == GMP_RPT_ALLOW)) {
1313 0 : gmp_destroy_addr_thread(group_rcrd->gmp_rpt_rcv_srcs);
1314 0 : group_rcrd->gmp_rpt_rcv_srcs = NULL;
1315 0 : group_rcrd->gmp_rpt_type = GMP_RPT_TO_EX;
1316 : }
1317 :
1318 : /* If the record is a non-null TO_EX or IS_EX, strip the sources. */
1319 :
1320 80 : if (group_rcrd->gmp_rpt_rcv_srcs &&
1321 0 : (group_rcrd->gmp_rpt_type == GMP_RPT_TO_EX ||
1322 0 : group_rcrd->gmp_rpt_type == GMP_RPT_IS_EX)) {
1323 0 : gmp_destroy_addr_thread(group_rcrd->gmp_rpt_rcv_srcs);
1324 0 : group_rcrd->gmp_rpt_rcv_srcs = NULL;
1325 : }
1326 :
1327 : /* If we're using the BASIC version, ignore any TO_IN or IS_IN messages. */
1328 :
1329 80 : if (group_version == GMP_VERSION_BASIC) {
1330 1 : if (group_rcrd->gmp_rpt_type == GMP_RPT_TO_IN ||
1331 1 : group_rcrd->gmp_rpt_type == GMP_RPT_IS_IN) {
1332 0 : return FALSE;
1333 : }
1334 : }
1335 :
1336 80 : return TRUE;
1337 : }
1338 :
1339 :
1340 : /*
1341 : * gmpr_process_report_packet
1342 : *
1343 : * Process a received report packet.
1344 : */
1345 : static void
1346 136 : gmpr_process_report_packet(gmpr_intf *intf, gmp_packet *packet)
1347 : {
1348 : gmpr_instance *instance;
1349 : gmpr_group *group;
1350 : gmp_report_packet *rpt_pkt;
1351 : gmp_report_group_record *group_rcrd;
1352 : task_thread *thread_ptr;
1353 : gmp_addr_vect source_vect;
1354 : gmp_addr_thread_entry *thread_entry;
1355 : gmp_addr_string *addr;
1356 : uint8_t *group_addr;
1357 : gmpr_instance_context *ctx;
1358 : gmp_version group_version;
1359 : boolean got_sources;
1360 :
1361 136 : instance = intf->rintf_instance;
1362 136 : ctx = &instance->rinst_cb_context;
1363 136 : rpt_pkt = &packet->gmp_packet_contents.gmp_packet_report;
1364 136 : gmp_init_addr_vector(&source_vect, &instance->rinst_addr_cat);
1365 :
1366 136 : gmpr_trace_agent("Process report packet : file : %s, line : %.",
1367 : __FILE__, __LINE__);
1368 :
1369 : /* Walk all of the groups in the report. */
1370 :
1371 276 : FOR_ALL_CIRCULAR_THREAD_ENTRIES(&rpt_pkt->gmp_report_group_head,
1372 : thread_ptr) {
1373 140 : group_rcrd = gmp_thread_to_report_group_record(thread_ptr);
1374 :
1375 : /* Look up the group. It may not be there. */
1376 :
1377 140 : group_addr = group_rcrd->gmp_rpt_group.gmp_addr;
1378 140 : group = gmpr_group_lookup(intf, group_addr);
1379 :
1380 : /*
1381 : * Modify the received report based on the compatibility mode.
1382 : * We will skip the record entirely if it says to.
1383 : */
1384 140 : group_version = gmpr_group_version(intf, group);
1385 140 : if (gmpr_harmonize_report_version(group_version, group_rcrd)) {
1386 :
1387 : /*
1388 : * We didn't toss the record. If there are any sources
1389 : * present, walk them.
1390 : */
1391 140 : if (group_rcrd->gmp_rpt_rcv_srcs &&
1392 59 : group_rcrd->gmp_rpt_rcv_srcs->gmp_addr_thread_count) {
1393 :
1394 59 : thread_entry = NULL;
1395 : while (TRUE) {
1396 134 : addr =
1397 193 : gmp_next_addr_thread_addr(group_rcrd->gmp_rpt_rcv_srcs,
1398 : &thread_entry);
1399 193 : if (!addr)
1400 59 : break;
1401 :
1402 : /* See if the group and source pass policy. */
1403 :
1404 134 : if (ctx->rctx_policy_cb) {
1405 134 : if (!(*ctx->rctx_policy_cb)(instance->rinst_context,
1406 : intf->rintf_id, group_addr,
1407 134 : addr->gmp_addr,
1408 : packet->gmp_packet_attr)) {
1409 12 : continue;
1410 : }
1411 : }
1412 :
1413 : /* Got an address. Add it to the vector. */
1414 :
1415 122 : if (gmp_addr_vect_set(&source_vect, addr) < 0)
1416 0 : return; /* Out of memory */
1417 : }
1418 :
1419 : /*
1420 : * If we get here without any sources, it means that they
1421 : * were all blocked by policy, so we should skip the
1422 : * record and go on.
1423 : */
1424 59 : if (gmp_addr_vect_empty(&source_vect))
1425 0 : continue;
1426 :
1427 : } else {
1428 :
1429 : /* No sources. See if the group passes policy. */
1430 :
1431 81 : if (ctx->rctx_policy_cb) {
1432 81 : if (!(*ctx->rctx_policy_cb)(instance->rinst_context,
1433 : intf->rintf_id, group_addr,
1434 : NULL,
1435 : packet->gmp_packet_attr)) {
1436 0 : continue;
1437 : }
1438 : }
1439 :
1440 : /*
1441 : * Ignore the group if this is a join and the group is
1442 : * SSM-only.
1443 : */
1444 81 : if (group_rcrd->gmp_rpt_type == GMP_RPT_TO_EX ||
1445 81 : group_rcrd->gmp_rpt_type == GMP_RPT_IS_EX) {
1446 49 : if (ctx->rctx_ssm_check_cb) {
1447 49 : if (!(*ctx->rctx_ssm_check_cb)(instance->rinst_context,
1448 : intf->rintf_id,
1449 : group_addr)) {
1450 0 : continue;
1451 : }
1452 : }
1453 : }
1454 : }
1455 :
1456 : /*
1457 : * If we get here, the record is valid. If there is no group,
1458 : * and the record is some kind of leave, skip to the next
1459 : * record without creating anything.
1460 : */
1461 140 : got_sources = !gmp_addr_vect_empty(&source_vect);
1462 140 : if (!group &&
1463 42 : ((group_rcrd->gmp_rpt_type == GMP_RPT_TO_IN && !got_sources) ||
1464 37 : (group_rcrd->gmp_rpt_type == GMP_RPT_IS_IN && !got_sources) ||
1465 37 : (group_rcrd->gmp_rpt_type == GMP_RPT_BLOCK))) {
1466 5 : goto next_record;
1467 : }
1468 :
1469 : /* Create a group if we don't have one yet. */
1470 :
1471 135 : if (!group)
1472 37 : group = gmpr_group_create(intf, &group_rcrd->gmp_rpt_group);
1473 :
1474 : /*
1475 : * Now check to see if we have a group. If we do, process
1476 : * the record. If not, skip it, as we are out of memory or have
1477 : * hit the group limit for the interface.
1478 : */
1479 135 : if (group) {
1480 :
1481 : /* Update the version compatibility mode. */
1482 :
1483 135 : gmpr_update_version_compatibility_mode(group,
1484 : packet->gmp_packet_version);
1485 :
1486 : /* Note the reporter's address. */
1487 :
1488 135 : memmove(group->rgroup_last_reporter.gmp_addr,
1489 135 : packet->gmp_packet_src_addr.gmp_addr,
1490 135 : instance->rinst_addrlen);
1491 :
1492 : /* See if this is a current-state or state-change record. */
1493 :
1494 135 : if (group_rcrd->gmp_rpt_type == GMP_RPT_IS_IN ||
1495 106 : group_rcrd->gmp_rpt_type == GMP_RPT_IS_EX) {
1496 :
1497 : /* Current-state record. Process it. */
1498 :
1499 78 : gmpr_process_cur_state_rcrd(group, group_rcrd->gmp_rpt_type,
1500 : &source_vect);
1501 :
1502 : } else {
1503 :
1504 : /* State-change record. Process it. */
1505 :
1506 57 : gmpr_process_state_chg_rcrd(group, group_rcrd->gmp_rpt_type,
1507 : &source_vect);
1508 : }
1509 :
1510 : /* Do host processing, if appropriate. */
1511 :
1512 135 : gmpr_host_process_report(packet->gmp_packet_src_addr.gmp_addr,
1513 : group_rcrd->gmp_rpt_type, group,
1514 : &source_vect);
1515 : }
1516 :
1517 0 : next_record:
1518 :
1519 : /* Clean up the source vector. */
1520 :
1521 140 : gmp_addr_vect_clean(&source_vect);
1522 : }
1523 : }
1524 : }
1525 :
1526 :
1527 : /*
1528 : * gmpr_packet_rcv_callback
1529 : *
1530 : * Callback from generic packet handling to process a packet, provided
1531 : * in the generic form. All syntax checking has already occurred.
1532 : */
1533 : static void
1534 136 : gmpr_packet_rcv_callback (gmpx_intf_id intf_id, gmp_packet *packet)
1535 : {
1536 : gmpr_intf *intf;
1537 :
1538 : /* Look up the interface. */
1539 :
1540 136 : intf = gmpr_intf_lookup_global(packet->gmp_packet_proto, intf_id);
1541 :
1542 : /* Bail if no interface. */
1543 :
1544 136 : if (!intf)
1545 0 : return;
1546 :
1547 : /* Return if passive receive. */
1548 :
1549 136 : if (intf->rintf_passive_receive)
1550 0 : return;
1551 :
1552 : /* Tease them apart by type. */
1553 :
1554 136 : switch (packet->gmp_packet_type) {
1555 0 : case GMP_QUERY_PACKET:
1556 0 : gmpr_process_query_packet(intf, packet);
1557 0 : break;
1558 :
1559 136 : case GMP_REPORT_PACKET:
1560 136 : gmpr_process_report_packet(intf, packet);
1561 136 : break;
1562 :
1563 0 : default:
1564 0 : gmpx_assert(FALSE);
1565 : }
1566 :
1567 : /* Pass along any notifications. */
1568 :
1569 136 : gmpr_alert_clients(intf->rintf_instance);
1570 136 : gmpr_alert_host_clients(intf->rintf_instance);
1571 : }
1572 :
1573 :
1574 : /*
1575 : * gmpr_send_gss_query
1576 : *
1577 : * Send a GSS query on an interface, if appropriate.
1578 : *
1579 : * Returns a pointer to the packet, or NULL if out of memory.
1580 : */
1581 : static gmp_packet *
1582 88 : gmpr_send_gss_query (gmpr_group *group)
1583 : {
1584 : gmpr_instance *instance;
1585 : gmpr_intf *intf;
1586 : gmp_addr_list *addr_list;
1587 : gmp_packet *packet;
1588 : gmp_query_packet *query_packet;
1589 : boolean hi_timer;
1590 :
1591 88 : intf = group->rgroup_intf;
1592 :
1593 : /* Bail if we don't need to send a GSS query. */
1594 :
1595 88 : if (!group->rgroup_send_gss_query)
1596 54 : return NULL;
1597 :
1598 : /* Bail if we're not the querier. */
1599 :
1600 34 : if (!intf->rintf_querier) {
1601 0 : group->rgroup_send_gss_query = FALSE;
1602 0 : return NULL;
1603 : }
1604 :
1605 : /*
1606 : * There are two lists of sources to query, the lo-timer list and the
1607 : * hi-timer list. Take a look at the lo-timer list first.
1608 : */
1609 34 : addr_list = &group->rgroup_query_lo_timers;
1610 34 : if (!gmp_xmit_addr_list_empty(addr_list)) {
1611 :
1612 : /* Something on the low timer list. Flag it. */
1613 :
1614 17 : hi_timer = FALSE;
1615 :
1616 : } else {
1617 :
1618 : /* Try the hi-timer list. */
1619 :
1620 17 : addr_list = &group->rgroup_query_hi_timers;
1621 :
1622 17 : if (!gmp_xmit_addr_list_empty(addr_list)) {
1623 :
1624 : /* Something on the high timer list. Flag it. */
1625 :
1626 0 : hi_timer = TRUE;
1627 :
1628 : } else {
1629 :
1630 : /* Nothing found. Bail. */
1631 :
1632 17 : group->rgroup_send_gss_query = FALSE;
1633 17 : return NULL;
1634 : }
1635 : }
1636 :
1637 : /* Something to send. Get a packet header and initialize it. */
1638 :
1639 17 : instance = intf->rintf_instance;
1640 17 : packet = gmpp_create_packet_header(group->rgroup_compatibility_mode,
1641 : GMP_QUERY_PACKET,
1642 : instance->rinst_proto);
1643 17 : if (!packet)
1644 0 : return NULL; /* Out of memory */
1645 :
1646 : /*
1647 : * Fill in the packet. We set the S bit if we're sending the high-timer
1648 : * list.
1649 : */
1650 17 : query_packet = &packet->gmp_packet_contents.gmp_packet_query;
1651 17 : query_packet->gmp_query_max_resp = intf->rintf_lmq_ivl;
1652 17 : query_packet->gmp_query_group_query = TRUE;
1653 17 : memmove(&query_packet->gmp_query_group, &group->rgroup_addr, sizeof(gmp_addr_string));
1654 17 : query_packet->gmp_query_qrv = intf->rintf_robustness;
1655 17 : query_packet->gmp_query_qqi = intf->rintf_query_ivl;
1656 17 : query_packet->gmp_query_suppress = hi_timer;
1657 17 : query_packet->gmp_query_xmit_srcs = addr_list;
1658 17 : query_packet->gmp_query_group_id = group;
1659 :
1660 17 : return packet;
1661 : }
1662 :
1663 :
1664 : /*
1665 : * gmpr_send_group_query
1666 : *
1667 : * Send a group query on an interface, if appropriate.
1668 : *
1669 : * Returns a pointer to the packet, or NULL if out of memory.
1670 : */
1671 : static gmp_packet *
1672 71 : gmpr_send_group_query (gmpr_group *group)
1673 : {
1674 : gmpr_instance *instance;
1675 : gmpr_intf *intf;
1676 : gmp_packet *packet;
1677 : gmp_query_packet *query_packet;
1678 :
1679 71 : intf = group->rgroup_intf;
1680 :
1681 : /* Bail if we don't need to send a group query. */
1682 :
1683 71 : if (!group->rgroup_send_group_query)
1684 44 : return NULL;
1685 :
1686 27 : group->rgroup_send_group_query = FALSE;
1687 :
1688 : /* Bail if we're not the querier. */
1689 :
1690 27 : if (!intf->rintf_querier)
1691 0 : return NULL;
1692 :
1693 : /* Something to send. Get a packet header and initialize it. */
1694 :
1695 27 : instance = intf->rintf_instance;
1696 27 : packet = gmpp_create_packet_header(group->rgroup_compatibility_mode,
1697 : GMP_QUERY_PACKET,
1698 : instance->rinst_proto);
1699 27 : if (!packet)
1700 0 : return NULL; /* Out of memory */
1701 :
1702 : /* Fill in the packet. */
1703 :
1704 27 : query_packet = &packet->gmp_packet_contents.gmp_packet_query;
1705 27 : query_packet->gmp_query_max_resp = intf->rintf_lmq_ivl;
1706 27 : query_packet->gmp_query_group_query = TRUE;
1707 27 : memmove(&query_packet->gmp_query_group, &group->rgroup_addr, sizeof(gmp_addr_string));
1708 27 : query_packet->gmp_query_qrv = intf->rintf_robustness;
1709 27 : query_packet->gmp_query_qqi = intf->rintf_query_ivl;
1710 27 : query_packet->gmp_query_group_id = group;
1711 :
1712 : /* Set the S bit if the group timer is larger than LMQT. */
1713 :
1714 27 : query_packet->gmp_query_suppress =
1715 27 : (gmpx_timer_time_remaining(group->rgroup_group_timer) >
1716 27 : intf->rintf_lmqt);
1717 :
1718 27 : return packet;
1719 : }
1720 :
1721 :
1722 : /*
1723 : * gmpr_send_gen_query
1724 : *
1725 : * Send a general query packet on an interface, if appropriate.
1726 : *
1727 : * Returns a pointer to the packet, or NULL if out of memory.
1728 : */
1729 : static gmp_packet *
1730 3546 : gmpr_send_gen_query (gmpr_intf *intf)
1731 : {
1732 : gmpr_instance *instance;
1733 : gmp_packet *packet;
1734 : gmp_query_packet *query_packet;
1735 :
1736 3546 : packet = NULL;
1737 :
1738 : /* Do so only if we need to. */
1739 :
1740 3546 : if (intf->rintf_send_gen_query) {
1741 :
1742 : /*
1743 : * See if we're supposed to be querier. This is the case
1744 : * the other_querier_present timer is stopped, and if we're not
1745 : * disabled and running IGMP version 1.
1746 : */
1747 875 : if (intf->rintf_querier &&
1748 875 : !(intf->rintf_ver == GMP_VERSION_BASIC &&
1749 6 : !intf->rintf_querier_enabled)) {
1750 :
1751 : /* Guess we need to. Get a packet header and initialize it. */
1752 :
1753 875 : instance = intf->rintf_instance;
1754 875 : packet = gmpp_create_packet_header(intf->rintf_ver,
1755 : GMP_QUERY_PACKET,
1756 : instance->rinst_proto);
1757 875 : if (!packet)
1758 0 : return NULL; /* Out of memory */
1759 :
1760 875 : query_packet = &packet->gmp_packet_contents.gmp_packet_query;
1761 875 : query_packet->gmp_query_max_resp = intf->rintf_query_resp_ivl;
1762 875 : query_packet->gmp_query_group_query = FALSE;
1763 875 : query_packet->gmp_query_qrv = intf->rintf_robustness;
1764 875 : query_packet->gmp_query_qqi = intf->rintf_query_ivl;
1765 : }
1766 :
1767 : }
1768 3546 : intf->rintf_send_gen_query = FALSE;
1769 3546 : return packet;
1770 : }
1771 :
1772 :
1773 : /*
1774 : * gmpr_packet_free_callback
1775 : *
1776 : * Callback from the packet handler when it is done with a packet structure.
1777 : */
1778 : static void
1779 919 : gmpr_packet_free_callback (gmp_packet *packet)
1780 : {
1781 : /* Free the packet. */
1782 :
1783 919 : gmpp_destroy_packet(packet);
1784 919 : }
1785 :
1786 :
1787 : /*
1788 : * gmpr_group_done_callback
1789 : *
1790 : * Callback from the packet handler when it is done processing a group.
1791 : */
1792 : static void
1793 44 : gmpr_group_done_callback (void *group_id)
1794 : {
1795 : gmpr_group *group;
1796 :
1797 44 : group = group_id;
1798 :
1799 : /* Flush the query lists if there's nothing left to send. */
1800 :
1801 44 : if (gmp_xmit_addr_list_empty(&group->rgroup_query_hi_timers))
1802 44 : gmp_flush_addr_list(&group->rgroup_query_hi_timers);
1803 44 : if (gmp_xmit_addr_list_empty(&group->rgroup_query_lo_timers))
1804 44 : gmp_flush_addr_list(&group->rgroup_query_lo_timers);
1805 :
1806 : /*
1807 : * Try to free the group. This will happen if there's no longer any
1808 : * interest in this group and we've sent all of the necessary messages.
1809 : */
1810 44 : gmpr_attempt_group_free(group);
1811 44 : }
1812 :
1813 :
1814 : /*
1815 : * gmpr_xmit_callback
1816 : *
1817 : * Callback from the packet handler when it is ready to send a packet.
1818 : *
1819 : * Returns a pointer to a generic packet to send, or NULL if there's nothing
1820 : * to send.
1821 : *
1822 : * Also returns a pointer to a packet to send.
1823 : */
1824 : static gmp_packet *
1825 3546 : gmpr_xmit_callback (gmpx_intf_id intf_id, gmp_proto proto,
1826 : uint32_t buffer_len GMPX_UNUSED)
1827 : {
1828 : gmpr_intf *intf;
1829 : gmpr_group *group;
1830 : gmp_packet *packet;
1831 :
1832 : /* Look up the interface. */
1833 :
1834 3546 : intf = gmpr_intf_lookup_global(proto, intf_id);
1835 3546 : if (!intf) /* No interface! */
1836 0 : return NULL;
1837 :
1838 : /*
1839 : * Got an interface. By default, turn off the "xmit pending" flag.
1840 : * We'll turn it back on if we end up returning a real packet.
1841 : */
1842 3546 : packet = NULL;
1843 3546 : intf->rintf_xmit_pending = FALSE;
1844 :
1845 : /* If we need to send a general query, do so. */
1846 :
1847 3546 : packet = gmpr_send_gen_query(intf);
1848 :
1849 : /* If nothing yet, take a look at the group transmit list. */
1850 :
1851 3546 : if (!packet) {
1852 :
1853 : /*
1854 : * Start pulling groups off of the interface transmit list. Normally
1855 : * we will use the first one, but it's possible for a group to be
1856 : * overtaken by events and no longer have anything to say.
1857 : */
1858 : while (TRUE) {
1859 :
1860 : /* Grab the first group off of the transmit list. */
1861 :
1862 2715 : group = gmpr_first_group_xmit(intf);
1863 2715 : if (!group)
1864 2627 : break;
1865 :
1866 : /* Got a group. See if we need to send a gss query. */
1867 :
1868 88 : packet = gmpr_send_gss_query(group);
1869 :
1870 : /* If nothing yet, see if we need to send a group query. */
1871 :
1872 88 : if (!packet)
1873 71 : packet = gmpr_send_group_query(group);
1874 :
1875 : /*
1876 : * If we've got a packet, bail from the loop. Otherwise,
1877 : * it looks like there was nothing for this group after all,
1878 : * so we dequeue it and try the next one.
1879 : */
1880 88 : if (packet)
1881 44 : break;
1882 44 : gmpr_dequeue_group_xmit(group);
1883 : }
1884 : }
1885 :
1886 : /*
1887 : * If we're actually returning a packet, note that we still have
1888 : * a transmission pending.
1889 : */
1890 3546 : if (packet)
1891 919 : intf->rintf_xmit_pending = TRUE;
1892 :
1893 3546 : return packet;
1894 : }
1895 :
1896 :
1897 : /*
1898 : * gmpr_register_packet_handler
1899 : *
1900 : * Register us with the generic packet handler.
1901 : */
1902 : void
1903 125 : gmpr_register_packet_handler (void)
1904 : {
1905 : /* Call the packet handler registration routines with the right stuff. */
1906 :
1907 125 : gmpp_register(GMP_ROLE_ROUTER, gmpr_xmit_callback,
1908 : gmpr_packet_rcv_callback, gmpr_group_done_callback,
1909 : gmpr_packet_free_callback);
1910 125 : }
1911 :
1912 :
1913 : /*
1914 : * gmpr_group_timer_expiry
1915 : *
1916 : * Called when a group timer expires.
1917 : */
1918 : void
1919 11 : gmpr_group_timer_expiry (gmpx_timer *timer, void *context)
1920 : {
1921 : gmpr_instance *instance;
1922 : gmpr_group *group;
1923 :
1924 11 : group = context;
1925 11 : instance = group->rgroup_intf->rintf_instance;
1926 11 : gmpx_stop_timer(timer);
1927 :
1928 11 : gmpr_trace_agent("Group Timer Expiry : file : %s, line : %.",
1929 : __FILE__, __LINE__);
1930 :
1931 : /*
1932 : * A group timer expiry means that there are no more active
1933 : * Exclude callers out there. We flush the stopped-timer list,
1934 : * switch to Include mode, and update the OIF. If the
1935 : * running-timer list is empty, the group will be deleted after
1936 : * the OIF update.
1937 : */
1938 11 : gmp_flush_addr_list(&group->rgroup_src_addr_stopped);
1939 11 : group->rgroup_filter_mode = GMP_FILTER_MODE_INCLUDE;
1940 11 : gmpr_update_oif_mode_change(group);
1941 11 : gmpr_alert_clients(instance);
1942 11 : instance->rinst_group_timeout++;
1943 11 : }
1944 :
1945 :
1946 : /*
1947 : * gmpr_source_timer_expiry
1948 : *
1949 : * Called when a source timer expires.
1950 : */
1951 : void
1952 27 : gmpr_source_timer_expiry (gmpx_timer *timer, void *context)
1953 : {
1954 : gmpr_group *group;
1955 : gmpr_group_addr_entry *group_addr_entry;
1956 : gmp_addr_list_entry *addr_entry;
1957 : gmpr_instance *instance;
1958 :
1959 27 : gmpx_stop_timer(timer);
1960 27 : group_addr_entry = context;
1961 27 : addr_entry = &group_addr_entry->rgroup_addr_entry;
1962 27 : group = group_addr_entry->rgroup_addr_group;
1963 27 : instance = group->rgroup_intf->rintf_instance;
1964 :
1965 : /* Check the filter mode. */
1966 :
1967 27 : gmpr_trace_agent("Source Timer Expiry : file : %s, line : %.",
1968 : __FILE__, __LINE__);
1969 :
1970 27 : if (group->rgroup_filter_mode == GMP_FILTER_MODE_INCLUDE) {
1971 :
1972 : /*
1973 : * Include mode. The source is going away. Update the OIF and
1974 : * delete the entry.
1975 : */
1976 27 : gmpr_update_source_oif(group_addr_entry, OIF_DELETE);
1977 27 : gmp_delete_addr_list_entry(addr_entry);
1978 :
1979 : /* Now see if there's anything left in the running list. */
1980 :
1981 27 : if (gmp_addr_list_empty(&group->rgroup_src_addr_running)) {
1982 :
1983 : /*
1984 : * The running list is now empty. This means that the last source
1985 : * went away, and we should instead delete the whole group.
1986 : */
1987 10 : gmpr_update_group_oif(group, OIF_DELETE);
1988 10 : gmpr_attempt_group_free(group);
1989 : }
1990 :
1991 : } else {
1992 :
1993 : /*
1994 : * Exclude mode. Move the entry from the running timer list
1995 : * to the stopped timer list.
1996 : */
1997 0 : gmp_move_addr_list_entry(&group->rgroup_src_addr_stopped, addr_entry);
1998 0 : gmpr_update_source_oif(group_addr_entry, OIF_UPDATE);
1999 : }
2000 :
2001 27 : gmpr_alert_clients(instance);
2002 27 : }
2003 :
2004 :
2005 : /*
2006 : * gmpr_gss_query_timer_expiry
2007 : *
2008 : * Called when a GSS query transmission timer expires.
2009 : */
2010 : void
2011 25 : gmpr_gss_query_timer_expiry (gmpx_timer *timer, void *context)
2012 : {
2013 : gmpr_intf *intf;
2014 : gmpr_group *group;
2015 : gmp_addr_list *addr_list;
2016 : gmp_addr_list *running_list;
2017 : gmp_addr_list_entry *addr_entry, *new_addr_entry;
2018 : gmpr_group_addr_entry *group_addr;
2019 : boolean found_something;
2020 :
2021 25 : gmpx_stop_timer(timer);
2022 25 : group = context;
2023 25 : intf = group->rgroup_intf;
2024 :
2025 : /*
2026 : * Flush the low-timer and high-timer query lists. They're probably
2027 : * empty anyhow.
2028 : */
2029 25 : gmp_flush_addr_list(&group->rgroup_query_lo_timers);
2030 25 : gmp_flush_addr_list(&group->rgroup_query_hi_timers);
2031 :
2032 : /*
2033 : * Walk the running-timer list, enqueueing entries onto the low-timer
2034 : * or high-timer query lists for each entry with a nonzero retransmit
2035 : * count.
2036 : */
2037 25 : found_something = FALSE;
2038 25 : running_list = &group->rgroup_src_addr_running;
2039 25 : addr_entry = NULL;
2040 : while (TRUE) {
2041 85 : addr_entry = gmp_addr_list_next_entry(running_list, addr_entry);
2042 85 : group_addr = gmpr_addr_entry_to_group_entry(addr_entry);
2043 85 : if (!group_addr)
2044 25 : break;
2045 :
2046 : /* Process entries with nonzero retransmit counts. */
2047 :
2048 60 : if (group_addr->rgroup_addr_rexmit_count) {
2049 :
2050 36 : found_something = TRUE;
2051 :
2052 : /* Decrement the retransmission count. */
2053 :
2054 36 : group_addr->rgroup_addr_rexmit_count--;
2055 :
2056 : /*
2057 : * Stick the entry into the low or high timer lists,
2058 : * depending on the remaining time compared to LMQT.
2059 : */
2060 36 : if (gmpx_timer_time_remaining(group_addr->rgroup_addr_timer) <=
2061 36 : intf->rintf_lmqt) {
2062 36 : addr_list = &group->rgroup_query_lo_timers;
2063 : } else {
2064 0 : addr_list = &group->rgroup_query_hi_timers;
2065 : }
2066 : new_addr_entry =
2067 36 : gmp_create_addr_list_entry(addr_list,
2068 : addr_entry->addr_ent_ord);
2069 36 : gmp_enqueue_xmit_addr_entry(new_addr_entry);
2070 : }
2071 : }
2072 :
2073 : /*
2074 : * Enqueue the group, set the flag, kick the transmitter if we
2075 : * found something.
2076 : */
2077 25 : if (found_something) {
2078 17 : gmpr_enqueue_group_xmit(group);
2079 17 : group->rgroup_send_gss_query = TRUE;
2080 17 : gmpr_kick_xmit(group->rgroup_intf);
2081 17 : gmpx_start_timer(group->rgroup_gss_query_timer,
2082 17 : group->rgroup_intf->rintf_lmq_ivl, 0);
2083 : }
2084 :
2085 : /*
2086 : * Try tossing the group. We may have just cleaned up the last
2087 : * bits keeping it alive by flushing the lo and hi timer lists.
2088 : */
2089 25 : gmpr_attempt_group_free(group);
2090 25 : }
2091 :
2092 :
2093 : /*
2094 : * gmpr_group_query_timer_expiry
2095 : *
2096 : * Called when a group query transmission timer expires.
2097 : */
2098 : void
2099 27 : gmpr_group_query_timer_expiry (gmpx_timer *timer, void *context)
2100 : {
2101 : gmpr_group *group;
2102 : gmpr_intf *intf;
2103 :
2104 27 : gmpx_stop_timer(timer);
2105 27 : group = context;
2106 27 : intf = group->rgroup_intf;
2107 :
2108 : /* Decrement the retransmit count. */
2109 :
2110 27 : gmpx_assert(group->rgroup_query_rexmit_count);
2111 27 : group->rgroup_query_rexmit_count--;
2112 :
2113 : /*
2114 : * Enqueue the group, set the flag and kick the transmitter. When
2115 : * we get called back, we'll actually form the packet.
2116 : */
2117 27 : gmpr_enqueue_group_xmit(group);
2118 27 : group->rgroup_send_group_query = TRUE;
2119 27 : gmpr_kick_xmit(intf);
2120 :
2121 : /* Restart the timer if the rexmit count is still nonzero. */
2122 :
2123 27 : if (group->rgroup_query_rexmit_count)
2124 0 : gmpx_start_timer(group->rgroup_query_timer, intf->rintf_lmq_ivl, 0);
2125 27 : }
2126 :
2127 :
2128 : /*
2129 : * gmpr_last_host_addr_ref_gone
2130 : *
2131 : * Called when the last host reference to a (S,G) is going away and we're
2132 : * doing fast leave processing. We act as if the source timer has expired.
2133 : * We can get away with this because we only track host sources when in
2134 : * Include mode.
2135 : */
2136 : void
2137 0 : gmpr_last_host_addr_ref_gone (gmpr_group_addr_entry *group_addr_entry)
2138 : {
2139 : /* Just set the source timer to expire immediately. */
2140 :
2141 0 : gmpx_start_timer(group_addr_entry->rgroup_addr_timer, 0, 0);
2142 0 : }
2143 :
2144 :
2145 : /*
2146 : * gmpr_last_host_group_ref_gone
2147 : *
2148 : * Called when the last host reference to a group is going away and we're
2149 : * doing fast leave processing. We switch to Include mode, flush the
2150 : * source lists, and post the group notification.
2151 : */
2152 : void
2153 0 : gmpr_last_host_group_ref_gone (gmpr_group *group)
2154 : {
2155 : gmpr_instance *instance;
2156 :
2157 : /* Bail if the group is already gone. */
2158 :
2159 0 : if (!gmpr_group_is_active(group))
2160 0 : return;
2161 :
2162 0 : instance = group->rgroup_intf->rintf_instance;
2163 :
2164 : /* Force the group to Include{} state. */
2165 :
2166 0 : gmp_flush_addr_list(&group->rgroup_src_addr_running);
2167 0 : gmp_flush_addr_list(&group->rgroup_src_addr_stopped);
2168 0 : group->rgroup_filter_mode = GMP_FILTER_MODE_INCLUDE;
2169 0 : gmpx_stop_timer(group->rgroup_group_timer);
2170 :
2171 : /* Update the OIF and try to delete the group. */
2172 :
2173 0 : gmpr_update_oif_mode_change(group);
2174 0 : gmpr_alert_clients(instance);
2175 : }
|