Line data Source code
1 : /* $Id: gmp_router.c 514187 2012-05-06 12:25:25Z ib-builder $
2 : *
3 : * gmp_router.c - IGMP/MLD Router-Side Support
4 : *
5 : * Dave Katz, March 2008
6 : *
7 : * Copyright (c) 2008, Juniper Networks, Inc.
8 : * All rights reserved.
9 : *
10 : * This module defines the top-level routines for the router-side support
11 : * for GMP.
12 : */
13 : #include "gmpx_basic_types.h"
14 : #include "gmp.h"
15 : #include "gmpx_environment.h"
16 : #include "gmp_externs.h"
17 : #include "gmp_private.h"
18 : #include "gmp_router.h"
19 : #include "gmpr_private.h"
20 : #include "gmp_trace.h"
21 : #include "gmpr_trace.h"
22 :
23 : /*
24 : * Global storage. There should be very little here.
25 : */
26 : gmpx_block_tag gmpr_instance_tag;
27 : gmpx_block_tag gmpr_client_tag;
28 : gmpx_block_tag gmpr_intf_tag;
29 : gmpx_block_tag gmpr_host_tag;
30 : gmpx_block_tag gmpr_host_group_tag;
31 : gmpx_block_tag gmpr_host_group_addr_tag;
32 : gmpx_block_tag gmpr_group_tag;
33 : gmpx_block_tag gmpr_ogroup_tag;
34 : gmpx_block_tag gmpr_group_addr_entry_tag;
35 : gmpx_block_tag gmpr_ogroup_addr_entry_tag;
36 : gmpx_block_tag gmpr_notification_tag;
37 : gmpx_block_tag gmpr_host_notification_tag;
38 : gmpx_block_tag gmpr_global_group_tag;
39 : gmpx_block_tag gmpr_intf_list_tag;
40 : gmpx_block_tag gmpr_intf_group_tag;
41 : gmpx_block_tag gmpr_intf_host_tag;
42 :
43 : static boolean gr_initialized; /* TRUE if we've initialized */
44 :
45 :
46 : /*
47 : * gmpr_init
48 : *
49 : * Initialize GMP Router code
50 : *
51 : * Called when the first instance is created.
52 : *
53 : * Returns 0 if all OK, or -1 if out of memory.
54 : */
55 : static int
56 125 : gmpr_init (void)
57 : {
58 : gmp_proto proto;
59 :
60 : /* Initialize the interface tree. */
61 :
62 375 : for (proto = 0; proto < GMP_NUM_PROTOS; proto++) {
63 250 : gmpr_global_intf_tree[proto] =
64 250 : gmpx_patroot_init(sizeof(gmpx_intf_id),
65 : GMPX_PATRICIA_OFFSET(gmpr_intf,
66 : rintf_global_patnode,
67 : rintf_id));
68 250 : if (!gmpr_global_intf_tree[proto])
69 0 : return -1; /* Out of memory. */
70 : }
71 :
72 125 : gr_initialized = TRUE;
73 :
74 : /* Do common initialization. */
75 :
76 125 : gmp_common_init();
77 :
78 : /* Create the instance thread. */
79 :
80 125 : thread_new_circular_thread(&gmpr_global_instance_thread);
81 :
82 : /* Create memory blocks. */
83 :
84 125 : gmpr_instance_tag = gmpx_malloc_block_create(sizeof(gmpr_instance),
85 : "GMP router instance");
86 125 : gmpr_client_tag = gmpx_malloc_block_create(sizeof(gmpr_client),
87 : "GMP router client");
88 125 : gmpr_intf_tag = gmpx_malloc_block_create(sizeof(gmpr_intf),
89 : "GMP router intf");
90 125 : gmpr_host_tag = gmpx_malloc_block_create(sizeof(gmpr_host),
91 : "GMP router host");
92 125 : gmpr_host_group_tag = gmpx_malloc_block_create(sizeof(gmpr_host_group),
93 : "GMP router host group");
94 125 : gmpr_host_group_addr_tag =
95 125 : gmpx_malloc_block_create(sizeof(gmpr_host_group_addr),
96 : "GMP router host group address");
97 125 : gmpr_group_tag = gmpx_malloc_block_create(sizeof(gmpr_group),
98 : "GMP router group");
99 125 : gmpr_ogroup_tag = gmpx_malloc_block_create(sizeof(gmpr_ogroup),
100 : "GMP router output group");
101 125 : gmpr_group_addr_entry_tag =
102 125 : gmpx_malloc_block_create(sizeof(gmpr_group_addr_entry),
103 : "GMP router group address entry");
104 125 : gmpr_ogroup_addr_entry_tag =
105 125 : gmpx_malloc_block_create(sizeof(gmpr_ogroup_addr_entry),
106 : "GMP router output group address entry");
107 125 : gmpr_notification_tag =
108 125 : gmpx_malloc_block_create(sizeof(gmpr_client_notification),
109 : "GMP router notification");
110 125 : gmpr_host_notification_tag =
111 125 : gmpx_malloc_block_create(sizeof(gmpr_client_host_notification),
112 : "GMP router host notification");
113 125 : gmpr_global_group_tag =
114 125 : gmpx_malloc_block_create(sizeof(gmpr_global_group),
115 : "GMP router global group");
116 125 : gmpr_intf_list_tag =
117 125 : gmpx_malloc_block_create(sizeof(gmpr_client_intf_list),
118 : "GMP router interface list");
119 125 : gmpr_intf_group_tag =
120 125 : gmpx_malloc_block_create(sizeof(gmpr_intf_group_entry),
121 : "GMP router interface group");
122 125 : gmpr_intf_host_tag =
123 125 : gmpx_malloc_block_create(sizeof(gmpr_intf_host_entry),
124 : "GMP router interface host");
125 125 : return 0;
126 : }
127 :
128 :
129 : /*
130 : * gmpr_create_instance
131 : *
132 : * Create a router-side GMP instance.
133 : *
134 : * Returns an instance ID (really a pointer), or zero if out of memory.
135 : */
136 : gmp_instance_id
137 125 : gmpr_create_instance (gmp_proto proto, void *inst_context,
138 : gmpr_instance_context *cb_context)
139 : {
140 : gmpr_instance *instance;
141 :
142 : /* If we're not initialized yet, do it now. */
143 :
144 125 : if (!gr_initialized) {
145 125 : if (gmpr_init() < 0)
146 0 : return NULL; /* Out of memory. */
147 : }
148 :
149 125 : gr_initialized = TRUE;
150 :
151 : /* Create the instance. */
152 :
153 125 : instance = gmpr_instance_create(proto, inst_context);
154 :
155 : /* Copy over the context block. */
156 :
157 125 : if (cb_context) {
158 125 : memmove(&instance->rinst_cb_context, cb_context,
159 : sizeof(gmpr_instance_context));
160 : }
161 :
162 125 : return instance;
163 : }
164 :
165 :
166 : /*
167 : * gmpr_destroy_instance
168 : *
169 : * Destroy an instance and all things associated with it.
170 : */
171 : void
172 125 : gmpr_destroy_instance (gmp_instance_id instance_id)
173 : {
174 : gmpr_instance *instance;
175 :
176 : /* Get the instance block. */
177 :
178 125 : instance = gmpr_get_instance(instance_id);
179 :
180 : /* Destroy it. */
181 :
182 125 : gmpr_instance_destroy(instance);
183 125 : }
184 :
185 :
186 : /*
187 : * gmpr_register
188 : *
189 : * Register a router-side client with GMP.
190 : *
191 : * Returns a client ID for the client to identify itself with, or 0 if
192 : * we're maxed out on clients.
193 : */
194 : gmp_client_id
195 125 : gmpr_register (gmp_instance_id instance_id, void *client_context,
196 : gmpr_client_context *cb_context)
197 : {
198 : gmpr_client *client;
199 : gmpr_instance *instance;
200 :
201 : /* Get the instance. */
202 :
203 125 : instance = gmpr_get_instance(instance_id);
204 :
205 : /* Create a client. */
206 :
207 125 : client = gmpr_create_client(instance);
208 125 : client->rclient_context = client_context;
209 :
210 : /* Copy over the context block. */
211 :
212 125 : memmove(&client->rclient_cb_context, cb_context,
213 : sizeof(gmpr_client_context));
214 :
215 : /* If the client supplied a host callback, turn on host tracking. */
216 :
217 125 : if (cb_context->rctx_host_notif_cb)
218 125 : instance->rinst_host_tracking = TRUE;
219 :
220 125 : return client;
221 : }
222 :
223 :
224 : /*
225 : * gmpr_detach
226 : *
227 : * A client is going away. Clean up.
228 : */
229 : void
230 125 : gmpr_detach (gmp_client_id client_id)
231 : {
232 : gmpr_client *client;
233 :
234 : /* Get the client. */
235 :
236 125 : client = gmpr_get_client(client_id);
237 :
238 : /* Get rid of it. */
239 :
240 125 : gmpr_destroy_client(client);
241 125 : }
242 :
243 :
244 : /*
245 : * gmpr_refresh
246 : *
247 : * Refresh all group state. This enqueues all state for the client.
248 : *
249 : * If flush is TRUE, we first flush out all source notifications, which
250 : * has the effect of getting rid of any pending source deletions.
251 : */
252 : void
253 0 : gmpr_refresh (gmp_client_id client_id, boolean flush)
254 : {
255 : gmpr_client *client;
256 : gmpr_instance *instance;
257 :
258 : /* Get the client. */
259 :
260 0 : client = gmpr_get_client(client_id);
261 0 : instance = client->rclient_instance;
262 :
263 : /* Trace it. */
264 :
265 0 : gmpr_trace(instance, GMPR_TRACE_CLIENT_NOTIFY,
266 : "Client %u refresh", client->rclient_ordinal);
267 :
268 : /* Enqueue everything. */
269 :
270 0 : gmpr_client_enqueue_all_groups(client, flush);
271 :
272 : /* Enqueue the end-of-refresh marker. */
273 :
274 0 : gmpr_enqueue_refresh_end(client);
275 :
276 : /* Kick the clients. */
277 :
278 0 : gmpr_alert_clients(client->rclient_instance);
279 0 : }
280 :
281 :
282 : /*
283 : * gmpr_refresh_intf
284 : *
285 : * Refresh all group state for a single interface. This enqueues all
286 : * state for the client.
287 : *
288 : * If flush is TRUE, we first flush out any pending source notifications,
289 : * which has the effect of getting rid of any pending source deletions.
290 : */
291 : void
292 0 : gmpr_refresh_intf (gmp_client_id client_id, gmpx_intf_id intf_id,
293 : boolean flush)
294 : {
295 : gmpr_intf *intf;
296 : gmpr_client *client;
297 :
298 : /* Get the client. */
299 :
300 0 : client = gmpr_get_client(client_id);
301 :
302 : /* Look up the interface. */
303 :
304 0 : intf = gmpr_intf_lookup(client->rclient_instance, intf_id);
305 0 : if (!intf)
306 0 : return;
307 :
308 : /* Enqueue everything. */
309 :
310 0 : gmpr_client_enqueue_all_intf_groups(client, intf, flush);
311 0 : gmpr_alert_clients(client->rclient_instance);
312 : }
313 :
314 :
315 : /*
316 : * gmpr_refresh_host_state
317 : *
318 : * Refresh all host group state. This enqueues all state for the client.
319 : */
320 : void
321 0 : gmpr_refresh_host_state (gmp_client_id client_id)
322 : {
323 : gmpr_client *client;
324 :
325 : /* Get the client. */
326 :
327 0 : client = gmpr_get_client(client_id);
328 :
329 : /* Enqueue everything. */
330 :
331 0 : gmpr_client_enqueue_all_host_groups(client);
332 0 : gmpr_alert_host_clients(client->rclient_instance);
333 0 : }
334 :
335 :
336 : /*
337 : * gmpr_set_intf_params
338 : *
339 : * Set parameters for an interface.
340 : *
341 : * Returns 0 if OK, -1 if out of memory, or 1 if the interface hasn't
342 : * been attached.
343 : */
344 : int
345 1708 : gmpr_set_intf_params (gmp_instance_id instance_id, gmpx_intf_id intf_id,
346 : gmpr_intf_params *params)
347 : {
348 : gmpr_instance *instance;
349 :
350 : /* Get the instance. */
351 :
352 1708 : instance = gmpr_get_instance(instance_id);
353 :
354 : /* Go do the work. */
355 :
356 1708 : return gmpr_intf_set_params(instance, intf_id, params);
357 : }
358 :
359 : void
360 0 : gmpr_chk_grp_limit (gmp_instance_id instance_id,
361 : gmpx_intf_id intf_id)
362 : {
363 : gmpr_intf *intf;
364 : // gmpr_instance *instance;
365 :
366 : // instance = gmpr_get_instance(instance_id);
367 0 : intf = gmpr_intf_lookup(instance_id, intf_id);
368 0 : gmpr_check_grp_limit(intf, FALSE);
369 0 : }
370 :
371 : /*
372 : * gmpr_immediate_leave_intf_count
373 : */
374 : static int
375 0 : gmpr_immediate_leave_intf_count (gmpr_instance *instance)
376 : {
377 0 : int intf_count = 0;
378 0 : gmpr_intf *intf = NULL;
379 :
380 0 : intf = gmpr_next_instance_intf(instance, NULL);
381 0 : while (intf) {
382 0 : if (intf->rintf_fast_leaves) {
383 0 : intf_count++;
384 : }
385 0 : intf = gmpr_next_instance_intf(instance, intf);
386 : }
387 0 : return intf_count;
388 : }
389 :
390 : /*
391 : * gmpr_intf_disable_host_tracking
392 : *
393 : * Disable host tracking for an interface
394 : *
395 : * Returns 0 if OK, or 1 if the interface hasn't
396 : * been attached.
397 : */
398 : int
399 0 : gmpr_disable_host_tracking (gmp_instance_id instance_id,
400 : gmpx_intf_id intf_id)
401 : {
402 : gmpr_instance *instance;
403 : gmpr_intf *intf;
404 :
405 : /* Get the instance. */
406 0 : instance = gmpr_get_instance(instance_id);
407 0 : intf = gmpr_intf_lookup(instance, intf_id);
408 :
409 : /* If interface doesn't exist. */
410 0 : if (!intf)
411 0 : return 1;
412 :
413 : /* Blast all of the hosts on the interface. */
414 0 : gmpr_destroy_intf_hosts(intf);
415 :
416 : /* Disable host tracking if this is the last interface */
417 0 : if (gmpr_immediate_leave_intf_count(instance) == 0)
418 0 : instance->rinst_host_tracking = FALSE;
419 :
420 0 : return 0;
421 : }
422 :
423 : /*
424 : * gmpr_attach_intf
425 : *
426 : * Bind to an interface, in preparation for later interest.
427 : *
428 : * Returns 0 if OK, -1 if out of memory, or 1 if the interface already
429 : * is bound.
430 : */
431 : int
432 1708 : gmpr_attach_intf (gmp_instance_id instance_id, gmpx_intf_id intf_id)
433 : {
434 : gmpr_instance *instance;
435 :
436 : /* Go do the work. */
437 :
438 1708 : instance = gmpr_get_instance(instance_id);
439 1708 : return gmpr_attach_intf_internal(instance, intf_id);
440 : }
441 :
442 :
443 : /*
444 : * gmpr_detach_intf
445 : *
446 : * Unbind an interface. This flushes all previous listen requests
447 : * on the interface.
448 : *
449 : * Returns 0 if all OK, or 1 if the interface doesn't exist.
450 : */
451 : int
452 1708 : gmpr_detach_intf (gmp_instance_id instance_id, gmpx_intf_id intf_id)
453 : {
454 : gmpr_instance *instance;
455 :
456 : /* Go do the work. */
457 :
458 1708 : instance = gmpr_get_instance(instance_id);
459 1708 : return gmpr_detach_intf_internal(instance, intf_id);
460 : }
461 :
462 :
463 : /*
464 : * gmpr_get_notification
465 : *
466 : * Get the next notification for this client.
467 : *
468 : * Returns a pointer to the notification block, or NULL if no more
469 : * notifications.
470 : *
471 : * If last_notification isn't NULL, the client is returning a previous
472 : * notification block, which we can re-use if we like.
473 : */
474 : gmpr_client_notification *
475 272 : gmpr_get_notification (gmp_client_id client_id,
476 : gmpr_client_notification *last_notification)
477 : {
478 : gmpr_client *client;
479 :
480 : /* Get the client. */
481 :
482 272 : client = gmpr_get_client(client_id);
483 :
484 : /* Do the work. */
485 :
486 272 : return gmpr_client_get_notification(client, last_notification);
487 : }
488 :
489 :
490 : /*
491 : * gmpr_get_host_notification
492 : *
493 : * Get the next host notification for this client.
494 : *
495 : * Returns a pointer to the notification block, or NULL if no more
496 : * notifications.
497 : *
498 : * If last_notification isn't NULL, the client is returning a previous
499 : * notification block, which we can re-use if we like.
500 : */
501 : gmpr_client_host_notification *
502 350 : gmpr_get_host_notification (gmp_client_id client_id,
503 : gmpr_client_host_notification *last_notification)
504 : {
505 : gmpr_client *client;
506 :
507 : /* Get the client. */
508 :
509 350 : client = gmpr_get_client(client_id);
510 :
511 : /* Do the work. */
512 :
513 350 : return gmpr_client_get_host_notification(client, last_notification);
514 : }
515 :
516 :
517 : /*
518 : * gmpr_return_notification
519 : *
520 : * Free up a previously-delivered notification.
521 : */
522 : void
523 0 : gmpr_return_notification (gmpr_client_notification *notification)
524 : {
525 :
526 : /* Do the work. */
527 :
528 0 : gmpr_free_notification(notification);
529 0 : }
530 :
531 :
532 : /*
533 : * gmpr_return_host_notification
534 : *
535 : * Free up a previously-delivered host notification.
536 : */
537 : void
538 0 : gmpr_return_host_notification (gmpr_client_host_notification *host_notif)
539 : {
540 :
541 : /* No-brainer. */
542 :
543 0 : gmpr_client_free_host_notification(host_notif);
544 0 : }
545 :
546 :
547 : /*
548 : * gmpr_notification_last_sg
549 : *
550 : * Return the value of notif_last_sg given a notification.
551 : */
552 : boolean
553 0 : gmpr_notification_last_sg (gmpr_client_notification *notification)
554 : {
555 0 : if (notification && notification->notif_last_sg) {
556 0 : return TRUE;
557 : } else {
558 0 : return FALSE;
559 : }
560 : }
561 :
562 : /*
563 : * gmpr_add_intf_list_entry
564 : *
565 : * Add an interface index to an interface list.
566 : *
567 : * May allocate further entries as necessary.
568 : *
569 : * Returns a pointer to the current list entry, or NULL if out of memory.
570 : */
571 : static gmpr_client_intf_list *
572 0 : gmpr_add_intf_list_entry(gmpr_client_intf_list *cur_intf_list,
573 : gmpx_intf_id intf_id)
574 : {
575 : gmpr_client_intf_list *next_list;
576 :
577 : /* See if the entry will fit in the current entry. */
578 :
579 0 : if (cur_intf_list->gci_intf_count >= GMPR_INTF_LIST_SIZE) {
580 :
581 : /* Entry is full. Allocate a new one and link 'em. */
582 :
583 0 : next_list = gmpx_malloc_block(gmpr_intf_list_tag);
584 0 : if (!next_list)
585 0 : return NULL; /* Out of memory */
586 :
587 0 : cur_intf_list->gci_next = next_list;
588 0 : cur_intf_list = next_list;
589 : }
590 :
591 : /* Add the entry to the array. */
592 :
593 0 : cur_intf_list->gci_intfs[cur_intf_list->gci_intf_count++] = intf_id;
594 :
595 0 : return cur_intf_list;
596 : }
597 :
598 :
599 : /*
600 : * gmpr_get_intf_list
601 : *
602 : * Get the list of interfaces corresponding to a (S,G) or (*,G).
603 : *
604 : * If a (*,G) is specified (by virtue of a NULL source address pointer)
605 : * we will return an interface only if its state is Exclude{}. If an (S,G)
606 : * is specified, we return an interface if either the source is included in
607 : * an Include list, or is not listed in an Exclude list.
608 : *
609 : * If the search type is LOOSE, we will match a (*,G) for any group in
610 : * Exclude mode, whether or not there are any sources.
611 : *
612 : * Returns a pointer to an interface list, or NULL if out of memory.
613 : */
614 : gmpr_client_intf_list *
615 0 : gmpr_get_intf_list (gmp_instance_id instance_id, uint8_t *group_addr,
616 : uint8_t *source_addr, gmpr_intf_list_match match_type)
617 : {
618 : gmpr_instance *instance;
619 : gmpr_global_group *global_group;
620 : gmpr_client_intf_list *intf_list_head, *cur_intf_list;
621 : task_thread *thread_ptr;
622 : gmpr_ogroup *group;
623 : gmp_addr_cat_entry *cat_entry;
624 :
625 : /* Allocate the block we're returning. */
626 :
627 0 : intf_list_head = gmpx_malloc_block(gmpr_intf_list_tag);
628 0 : if (!intf_list_head)
629 0 : return NULL; /* Out of memory */
630 0 : cur_intf_list = intf_list_head;
631 :
632 : /* Get the instance. */
633 :
634 0 : instance = gmpr_get_instance(instance_id);
635 :
636 : /* Look up the global group entry. */
637 :
638 0 : global_group = gmpr_lookup_global_group(instance, group_addr);
639 :
640 : /* Bail if we don't know of the group. */
641 :
642 0 : if (!global_group)
643 0 : return intf_list_head;
644 :
645 : /*
646 : * Look up the address catalog entry for the source address. It may
647 : * or may not be there.
648 : */
649 0 : cat_entry = NULL;
650 0 : if (source_addr) {
651 0 : cat_entry = gmp_lookup_addr_cat_entry(&instance->rinst_addr_cat,
652 : source_addr);
653 : }
654 :
655 : /* Walk the group thread. */
656 :
657 0 : thread_ptr = NULL;
658 : while (TRUE) {
659 0 : thread_ptr = thread_circular_thread_next(
660 : &global_group->global_group_head, thread_ptr);
661 0 : group = gmpr_global_thread_to_group(thread_ptr);
662 0 : if (!group)
663 0 : break;
664 :
665 : /*
666 : * Found a group. See if there was no source address
667 : * specified (we're looking for a (*,G) entry).
668 : */
669 0 : if (!source_addr) {
670 :
671 : /*
672 : * No source address--we're looking for (*,G). Bail if the
673 : * group isn't in Exclude mode.
674 : */
675 0 : if ((group->rogroup_filter_mode != GMP_FILTER_MODE_EXCLUDE)) {
676 0 : continue;
677 : }
678 :
679 : /*
680 : * Exclude mode. Bail if there are sources present,
681 : * unless we're doing a loose search.
682 : */
683 0 : if (!gmp_addr_list_empty(&group->rogroup_excl_src_addr) &&
684 : (match_type == INTF_LIST_STRICT)) {
685 0 : continue;
686 : }
687 :
688 : } else {
689 :
690 : /* (S,G) specified. Skip if the source isn't active. */
691 :
692 0 : if (!cat_entry ||
693 0 : !gmpr_source_ord_is_active(group, cat_entry->adcat_ent_ord)) {
694 0 : continue;
695 : }
696 : }
697 :
698 : /* Looks like we should report the interface. Add it to the list. */
699 :
700 : cur_intf_list =
701 0 : gmpr_add_intf_list_entry(cur_intf_list,
702 0 : group->rogroup_intf->rintf_id);
703 : }
704 :
705 0 : return intf_list_head;
706 : }
707 :
708 :
709 : /*
710 : * gmpr_free_intf_list
711 : *
712 : * Free a previously-allocated interface list.
713 : */
714 : void
715 0 : gmpr_free_intf_list (gmpr_client_intf_list *intf_list)
716 : {
717 : gmpr_client_intf_list *next;
718 :
719 : /* Simple linked list traversal. */
720 :
721 0 : while (intf_list) {
722 0 : next = intf_list->gci_next;
723 0 : gmpx_free_block(gmpr_intf_list_tag, intf_list);
724 0 : intf_list = next;
725 : }
726 0 : }
727 :
728 :
729 : /*
730 : * gmpr_is_forwarding_channel
731 : *
732 : * Returns TRUE if GMP is recommending forwarding of the specified (S,G)
733 : * channel on the interface, or FALSE if not. If the source pointer is
734 : * NULL, we test for (*,G).
735 : *
736 : * If "exact" is TRUE, we return TRUE only if the channel as specified is
737 : * to be forwarded. If FALSE, we return TRUE if *any* (S,G) is being
738 : * forwarded when a (*,G) request is made.
739 : */
740 : boolean
741 0 : gmpr_is_forwarding_channel (gmp_instance_id instance_id, gmpx_intf_id intf_id,
742 : const uint8_t *source_addr,
743 : const uint8_t *group_addr, boolean exact)
744 : {
745 : gmpr_instance *instance;
746 : gmpr_intf *intf;
747 : gmpr_ogroup *group;
748 :
749 : /* Get the instance and interface. */
750 :
751 0 : instance = gmpr_get_instance(instance_id);
752 0 : intf = gmpr_intf_lookup(instance, intf_id);
753 0 : if (!intf)
754 0 : return FALSE;
755 :
756 : /* Get the group. */
757 :
758 0 : group = gmpr_ogroup_lookup(intf, group_addr);
759 0 : if (!group)
760 0 : return FALSE;
761 :
762 : /* If the group is a (*,G) join, we match anything. */
763 :
764 0 : if (gmpr_group_forwards_all_sources(group))
765 0 : return TRUE;
766 :
767 : /*
768 : * If we're doing an inexact (*,G) match and any part of the group
769 : * is being forwarded, we have a match.
770 : */
771 0 : if (!exact && !source_addr && gmpr_ogroup_is_active(group))
772 0 : return TRUE;
773 :
774 : /* If we're doing a (*,G) test, we didn't match. */
775 :
776 0 : if (!source_addr)
777 0 : return FALSE;
778 :
779 : /* See if we're forwarding the specified source. */
780 :
781 0 : return gmpr_group_forwards_source(group, source_addr);
782 : }
783 :
784 :
785 : /*
786 : * gmpr_update_intf_state
787 : *
788 : * Update the interface state. If the address is NULL, the interface
789 : * is going down.
790 : */
791 : void
792 3416 : gmpr_update_intf_state (gmp_instance_id instance_id, gmpx_intf_id intf_id,
793 : const uint8_t *intf_addr)
794 : {
795 : gmpr_instance *instance;
796 : gmpr_intf *intf;
797 : boolean was_up;
798 :
799 : /* Get the instance and interface. */
800 :
801 3416 : instance = gmpr_get_instance(instance_id);
802 3416 : intf = gmpr_intf_lookup(instance, intf_id);
803 3416 : if (!intf)
804 0 : return;
805 :
806 : /*
807 : * If the address is there, mark the interface up. Store the new
808 : * address and make ourselves querier if the address has changed
809 : * or the interface has just come up.
810 : */
811 3416 : was_up = intf->rintf_up;
812 3416 : if (intf_addr) {
813 1708 : intf->rintf_up = TRUE;
814 :
815 1708 : if (!was_up || memcmp(intf_addr, intf->rintf_local_addr.gmp_addr,
816 0 : instance->rinst_addrlen)) {
817 :
818 : /* Make ourselves querier. */
819 :
820 1708 : memmove(intf->rintf_local_addr.gmp_addr, intf_addr, instance->rinst_addrlen);
821 1708 : gmpr_update_querier(intf, &intf->rintf_local_addr, TRUE);
822 : }
823 :
824 : } else {
825 : /* No address. Mark the interface down and zap the querier address */
826 :
827 1708 : intf->rintf_up = FALSE;
828 1708 : intf->rintf_querier = FALSE;
829 1708 : memset(intf->rintf_local_addr.gmp_addr, 0, instance->rinst_addrlen);
830 : }
831 :
832 : /* If the up/down status changed, update any associated output groups. */
833 :
834 3416 : if (was_up != intf->rintf_up)
835 3416 : gmpr_update_intf_output_groups(intf);
836 :
837 : /*
838 : * If the interface went down, blast any input groups on the
839 : * interface, and note that we no longer have any transmissions
840 : * pending.
841 : */
842 :
843 3416 : if (was_up && !intf->rintf_up) {
844 1708 : gmpr_flush_intf_input_groups(intf);
845 1708 : intf->rintf_xmit_pending = FALSE;
846 : }
847 :
848 : /*
849 : * If the interface came up, kick the transmitter in case
850 : * something is waiting to go. Also send out startup queries,
851 : * since the interface just came back up.
852 : */
853 3416 : if (!was_up && intf->rintf_up) {
854 1708 : gmpr_setup_initial_query_timer(intf);
855 1708 : gmpr_kick_xmit(intf);
856 : }
857 : }
858 :
859 :
860 : /*
861 : * Context structure for gmpr_build_group_cb
862 : */
863 : typedef struct gmpr_build_group_cb_context_ {
864 : gmpr_instance *gbg_instance; /* Instance pointer */
865 : gmpr_ogroup *gbg_ogroup; /* Output group */
866 : gmpr_intf_group_entry *gbg_group_ent; /* Group entry */
867 : } gmpr_build_group_cb_context;
868 :
869 :
870 : /*
871 : * gmpr_build_group_cb
872 : *
873 : * Callback from vector walk to build a list of sources for a group.
874 : */
875 : static boolean
876 0 : gmpr_build_group_cb (void *context, bv_bitnum_t bitnum,
877 : boolean new_val GMPX_UNUSED,
878 : boolean old_val GMPX_UNUSED)
879 : {
880 : gmpr_instance *instance;
881 : gmpr_build_group_cb_context *ctx;
882 : gmpr_intf_group_entry *group_entry;
883 : gmp_addr_cat_entry *cat_entry;
884 : gmpr_ogroup *group;
885 :
886 0 : ctx = context;
887 0 : instance = ctx->gbg_instance;
888 0 : group_entry = ctx->gbg_group_ent;
889 0 : group = ctx->gbg_ogroup;
890 :
891 : /* Look up the address catalog entry. */
892 :
893 0 : cat_entry = gmp_get_addr_cat_by_ordinal(&instance->rinst_addr_cat, bitnum);
894 0 : gmpx_assert(cat_entry);
895 :
896 : /* Bail if the source isn't active. */
897 :
898 0 : if (!gmpr_source_ord_is_active(group, bitnum))
899 0 : return FALSE;
900 :
901 : /* Stick the source address into the address thread. */
902 :
903 0 : gmp_enqueue_addr_thread_addr(group_entry->gig_sources,
904 0 : cat_entry->adcat_ent_addr.gmp_addr,
905 : instance->rinst_addrlen);
906 :
907 0 : return FALSE;
908 : }
909 :
910 :
911 : /*
912 : * gmpr_get_intf_groups
913 : *
914 : * Get a list of all groups and sources on an interface. Returns a pointer
915 : * to a group entry, to which other group entries are linked. Returns NULL
916 : * if there are no groups on the interface.
917 : *
918 : * The pointer should be returned via gmpr_destroy_intf_group() when the
919 : * caller is done.
920 : *
921 : * **** THIS IS NONSCALABLE AND HEINOUS AND SHOULD BE REMOVED AS SOON
922 : * **** AS PIM WORKS REASONABLY!!!!
923 : */
924 : gmpr_intf_group_entry *
925 0 : gmpr_get_intf_groups (gmp_instance_id instance_id, gmpx_intf_id intf_id)
926 : {
927 : gmpr_instance *instance;
928 : gmpr_intf *intf;
929 : gmpr_ogroup *group;
930 : gmpr_intf_group_entry *group_list;
931 : gmpr_intf_group_entry *cur_group;
932 : gmp_addr_list *addr_list;
933 : gmpr_build_group_cb_context ctx;
934 :
935 : /* Get the instance and interface. */
936 :
937 0 : instance = gmpr_get_instance(instance_id);
938 0 : intf = gmpr_intf_lookup(instance, intf_id);
939 0 : if (!intf)
940 0 : return NULL;
941 :
942 : /* Walk all groups on the interface. */
943 :
944 0 : group_list = NULL;
945 0 : group = NULL;
946 : while (TRUE) {
947 :
948 : /* Get the next one. Bail out if done. */
949 :
950 0 : group = gmpr_next_oif_group(intf, group);
951 0 : if (!group)
952 0 : break;
953 :
954 : /* Got a group. Allocate an entry. */
955 :
956 0 : cur_group = gmpx_malloc_block(gmpr_intf_group_tag);
957 0 : if (!cur_group)
958 0 : break; /* Out of memory */
959 :
960 : /* Set the group address and filter mode. */
961 :
962 0 : memmove(cur_group->gig_group_addr.gmp_addr, group->rogroup_addr.gmp_addr,
963 0 : instance->rinst_addrlen);
964 0 : cur_group->gig_filter_mode = group->rogroup_filter_mode;
965 :
966 : /* If there are sources, copy them in as well. */
967 :
968 0 : addr_list = gmpr_ogroup_source_list(group);
969 0 : if (!gmp_addr_list_empty(addr_list)) {
970 :
971 : /* Got sources. Create the address thread. */
972 :
973 0 : cur_group->gig_sources = gmp_alloc_addr_thread();
974 0 : ctx.gbg_instance = instance;
975 0 : ctx.gbg_group_ent = cur_group;
976 0 : ctx.gbg_ogroup = group;
977 0 : gmp_addr_vect_walk(&addr_list->addr_vect, gmpr_build_group_cb,
978 : &ctx);
979 : }
980 :
981 : /* Link the new entry to the list. */
982 :
983 0 : if (group_list)
984 0 : cur_group->gig_next = group_list;
985 0 : group_list = cur_group;
986 : }
987 :
988 0 : return group_list;
989 : }
990 :
991 :
992 : /*
993 : * gmpr_get_host_groups
994 : *
995 : * Get a list of all groups and sources on an interface for the specified
996 : * host. Returns a pointer to a group entry, to which other group entries
997 : * are linked. Returns NULL if there are no groups on the interface.
998 : *
999 : * The pointer should be returned via gmpr_destroy_intf_group() when the
1000 : * caller is done.
1001 : *
1002 : * While the per-interface version of this code does not scale and is
1003 : * considered to be heinous, a single host should not have too much join
1004 : * state, so this should be OK.
1005 : */
1006 : gmpr_intf_group_entry *
1007 0 : gmpr_get_host_groups (gmp_instance_id instance_id,
1008 : gmpx_intf_id intf_id,
1009 : const uint8_t *host_addr)
1010 : {
1011 : gmpr_instance *instance;
1012 : gmpr_intf *intf;
1013 : gmpr_intf_group_entry *group_list;
1014 : gmpr_intf_group_entry *cur_group;
1015 : gmp_addr_list *addr_list;
1016 : gmpr_host *host;
1017 : gmpx_patnode *host_group_node;
1018 : gmpr_host_group *host_group;
1019 : gmp_addr_cat_entry *cat_entry;
1020 : gmpr_host_group_addr *host_group_addr;
1021 : gmp_addr_list_entry *addr_entry;
1022 :
1023 : /* Get the instance. */
1024 :
1025 0 : instance = gmpr_get_instance(instance_id);
1026 :
1027 : /* If no matching interface, return NULL */
1028 :
1029 0 : intf = gmpr_intf_lookup(instance, intf_id);
1030 0 : if (!intf)
1031 0 : return NULL;
1032 :
1033 : /* If no matching host, return NULL */
1034 :
1035 0 : host = gmpr_lookup_host(intf, host_addr);
1036 0 : if (!host)
1037 0 : return NULL;
1038 :
1039 : /* Got a host. Walk all host groups on the host. */
1040 :
1041 0 : group_list = NULL;
1042 0 : host_group_node = NULL;
1043 :
1044 : while (TRUE) {
1045 0 : host_group_node = gmpx_patricia_get_next(host->rhost_group_root,
1046 : host_group_node);
1047 0 : host_group = gmpr_patnode_to_host_group(host_group_node);
1048 :
1049 : /* Bail if done. */
1050 :
1051 0 : if (!host_group)
1052 0 : break;
1053 :
1054 : /* Only want the active ones */
1055 :
1056 0 : if (!gmpr_host_group_active(host_group))
1057 0 : continue;
1058 :
1059 : /* Got a group. Allocate an entry. */
1060 :
1061 0 : cur_group = gmpx_malloc_block(gmpr_intf_group_tag);
1062 0 : if (!cur_group)
1063 0 : break; /* Out of memory */
1064 :
1065 : /* Set the group address. */
1066 :
1067 0 : memmove(cur_group->gig_group_addr.gmp_addr,
1068 0 : host_group->rhgroup_addr.gmp_addr,
1069 0 : instance->rinst_addrlen);
1070 :
1071 : /* If there are sources, copy them in as well. */
1072 :
1073 0 : addr_list = &host_group->rhgroup_addrs;
1074 0 : if (!gmp_addr_list_empty(addr_list)) {
1075 : /* Walk address list */
1076 :
1077 : /* Got sources. Create the address thread. */
1078 :
1079 0 : cur_group->gig_sources = gmp_alloc_addr_thread();
1080 :
1081 0 : addr_entry = NULL;
1082 : while (TRUE) {
1083 0 : addr_entry = gmp_addr_list_next_entry(addr_list, addr_entry);
1084 : host_group_addr =
1085 0 : gmpr_addr_entry_to_host_group_entry(addr_entry);
1086 0 : if (!host_group_addr || !host_group_addr->rhga_source)
1087 : break;
1088 :
1089 : cat_entry =
1090 0 : gmp_get_addr_cat_by_ordinal(&instance->rinst_addr_cat,
1091 0 : host_group_addr->rhga_source->
1092 : rgroup_addr_entry.addr_ent_ord);
1093 0 : gmpx_assert(cat_entry);
1094 :
1095 0 : gmp_enqueue_addr_thread_addr(cur_group->gig_sources,
1096 0 : cat_entry->adcat_ent_addr.gmp_addr,
1097 : instance->rinst_addrlen);
1098 : }
1099 : }
1100 :
1101 : /* Link the new entry to the list. */
1102 :
1103 0 : if (group_list)
1104 0 : cur_group->gig_next = group_list;
1105 0 : group_list = cur_group;
1106 : }
1107 :
1108 0 : return group_list;
1109 : }
1110 :
1111 :
1112 : /*
1113 : * gmpr_host_is_active
1114 : *
1115 : * Determine if a host has active groups.
1116 : */
1117 : static boolean
1118 0 : gmpr_host_is_active (gmpr_host *host)
1119 : {
1120 : gmpx_patnode *host_group_node;
1121 : gmpr_host_group *host_group;
1122 :
1123 : /* Walk all host groups on the host. */
1124 0 : host_group_node = NULL;
1125 :
1126 : while (TRUE) {
1127 0 : host_group_node = gmpx_patricia_get_next(host->rhost_group_root,
1128 : host_group_node);
1129 0 : host_group = gmpr_patnode_to_host_group(host_group_node);
1130 :
1131 : /* Bail if done. */
1132 :
1133 0 : if (!host_group)
1134 0 : break;
1135 :
1136 : /* Found an active one, so the host is active */
1137 :
1138 0 : if (gmpr_host_group_active(host_group))
1139 0 : return TRUE;
1140 : }
1141 :
1142 : /* Host has no active groups */
1143 :
1144 0 : return FALSE;
1145 : }
1146 :
1147 :
1148 : /*
1149 : * gmpr_get_intf_hosts
1150 : *
1151 : * Get a list of all active hosts on the specified interface. Returns a
1152 : * pointer to a host entry, to which other host entries are linked.
1153 : * Returns NULL* if there are no active hosts on the interface.
1154 : *
1155 : * The pointer should be returned via gmpr_destroy_host_group() when the
1156 : * caller is done.
1157 : *
1158 : * This could be heinous and does not scale if there are lots of hosts
1159 : * on the interface.
1160 : */
1161 : gmpr_intf_host_entry *
1162 0 : gmpr_get_intf_hosts (gmp_instance_id instance_id, gmpx_intf_id intf_id)
1163 : {
1164 : gmpr_instance *instance;
1165 : gmpr_host *host;
1166 : gmpr_intf *intf;
1167 : gmpr_intf_host_entry *host_list;
1168 : gmpr_intf_host_entry *cur_host;
1169 : gmpx_patnode *host_node;
1170 :
1171 : /* Get the instance. */
1172 :
1173 0 : instance = gmpr_get_instance(instance_id);
1174 :
1175 : /* If no matching interface, return NULL */
1176 :
1177 0 : intf = gmpr_intf_lookup(instance, intf_id);
1178 0 : if (!intf)
1179 0 : return NULL;
1180 :
1181 : /* Walk all hosts on the interface */
1182 0 : host_list = NULL;
1183 0 : host_node = NULL;
1184 :
1185 : while (TRUE) {
1186 0 : host_node = gmpx_patricia_get_next(intf->rintf_host_root, host_node);
1187 0 : host = gmpr_patnode_to_host(host_node);
1188 :
1189 : /* Bail if done. */
1190 :
1191 0 : if (!host)
1192 0 : break;
1193 :
1194 : /* Ignore hosts that are not active. */
1195 :
1196 0 : if (!gmpr_host_is_active(host)) {
1197 0 : continue;
1198 : }
1199 :
1200 : /* Got an active host. Allocate an entry. */
1201 :
1202 0 : cur_host = gmpx_malloc_block(gmpr_intf_host_tag);
1203 0 : if (!cur_host)
1204 0 : break; /* Out of memory */
1205 :
1206 : /* Set the host address */
1207 :
1208 0 : memmove(cur_host->gih_host_addr.gmp_addr, host->rhost_addr.gmp_addr,
1209 0 : instance->rinst_addrlen);
1210 :
1211 : /*
1212 : * Link the new entry to the list.
1213 : */
1214 0 : if (host_list)
1215 0 : cur_host->gih_next = host_list;
1216 0 : host_list = cur_host;
1217 : }
1218 :
1219 0 : return host_list;
1220 : }
1221 :
1222 :
1223 : /*
1224 : * gmpr_destroy_intf_host
1225 : *
1226 : * Destroy the structure returned by gmpr_get_intf_hosts.
1227 : *
1228 : * Tolerates NULL pointers.
1229 : */
1230 : void
1231 0 : gmpr_destroy_intf_host (gmpr_intf_host_entry *host_list)
1232 : {
1233 : gmpr_intf_host_entry *next;
1234 :
1235 : /* Walk the list. */
1236 :
1237 0 : while (host_list) {
1238 0 : next = host_list->gih_next;
1239 :
1240 : /* Free the entry. */
1241 :
1242 0 : gmpx_free_block(gmpr_intf_host_tag, host_list);
1243 :
1244 0 : host_list = next;
1245 : }
1246 0 : }
1247 :
1248 :
1249 : /*
1250 : * gmpr_destroy_intf_group
1251 : *
1252 : * Destroy the structure returned by gmpr_get_intf_groups.
1253 : *
1254 : * Tolerates NULL pointers.
1255 : */
1256 : void
1257 0 : gmpr_destroy_intf_group (gmpr_intf_group_entry *group_list)
1258 : {
1259 : gmpr_intf_group_entry *next;
1260 :
1261 : /* Walk the list. */
1262 :
1263 0 : while (group_list) {
1264 0 : next = group_list->gig_next;
1265 :
1266 : /* Destroy the address thread, if there. */
1267 :
1268 0 : gmp_destroy_addr_thread(group_list->gig_sources);
1269 :
1270 : /* Free the entry. */
1271 :
1272 0 : gmpx_free_block(gmpr_intf_group_tag, group_list);
1273 :
1274 0 : group_list = next;
1275 : }
1276 0 : }
1277 :
1278 :
1279 : /*
1280 : * gmpr_is_initialized
1281 : *
1282 : * Returns TRUE if GMP router support has been initialized, or FALSE if not.
1283 : *
1284 : * This lets external callbacks know if it's safe to do stuff in here.
1285 : */
1286 : boolean
1287 0 : gmpr_is_initialized (void)
1288 : {
1289 0 : return gr_initialized;
1290 : }
1291 :
1292 :
1293 : /*
1294 : * gmpr_timeout_group_range
1295 : *
1296 : * Prematurely age a range of groups on an interface.
1297 : *
1298 : * send_query is TRUE if we should immediately send a general query on the
1299 : * interface.
1300 : */
1301 : void
1302 0 : gmpr_timeout_group_range (gmp_instance_id instance_id, gmpx_intf_id intf_id,
1303 : const uint8_t *group_addr, uint32_t pfx_len,
1304 : boolean send_query)
1305 : {
1306 : gmpr_instance *instance;
1307 : gmpr_intf *intf;
1308 : gmpr_group *group;
1309 : gmpr_group *next_group;
1310 : uint8_t last_byte_mask;
1311 : uint32_t pfx_full_bytes;
1312 : uint32_t pfx_len_bytes;
1313 : uint32_t extra_bits;
1314 : uint32_t bit_ix;
1315 :
1316 : /* Get the instance and interface. */
1317 :
1318 0 : instance = gmpr_get_instance(instance_id);
1319 0 : intf = gmpr_intf_lookup(instance, intf_id);
1320 0 : if (!intf)
1321 0 : return;
1322 :
1323 : /* Normalize the prefix length to bytes. */
1324 :
1325 0 : pfx_len_bytes = (pfx_len + 7) / 8;
1326 0 : gmpx_assert(pfx_len_bytes <= instance->rinst_addrlen);
1327 0 : pfx_full_bytes = pfx_len / 8;
1328 :
1329 : /* If the last byte isn't full, set up a mask for it. */
1330 :
1331 0 : extra_bits = pfx_len % 8;
1332 0 : last_byte_mask = 0;
1333 0 : if (extra_bits) {
1334 0 : for (bit_ix = 0; bit_ix < extra_bits; bit_ix++) {
1335 0 : last_byte_mask |= (0x80 >> bit_ix);
1336 : }
1337 : }
1338 :
1339 : /* Walk all groups on the interface. */
1340 :
1341 0 : group = gmpr_next_intf_group(intf, NULL);
1342 0 : while (group) {
1343 0 : next_group = gmpr_next_intf_group(intf, group);
1344 :
1345 : /* Compare the full bytes. */
1346 :
1347 0 : if (!memcmp(group_addr, group->rgroup_addr.gmp_addr, pfx_full_bytes)) {
1348 :
1349 : /* The full bytes match. If there's a partial byte, try that. */
1350 :
1351 0 : if (!extra_bits ||
1352 0 : (group_addr[pfx_full_bytes] ==
1353 0 : (group->rgroup_addr.gmp_addr[pfx_full_bytes] &
1354 : last_byte_mask))) {
1355 :
1356 : /* If we got here, we've got a match. Blast the timers. */
1357 :
1358 0 : gmpr_timeout_group(group);
1359 : }
1360 : }
1361 :
1362 0 : group = next_group;
1363 : }
1364 :
1365 : /* If we're supposed to send an immediate query, whack the timer. */
1366 :
1367 0 : if (send_query)
1368 0 : gmpx_start_timer(intf->rintf_query_timer, 0, 0);
1369 : }
1370 :
1371 :
1372 : /*
1373 : * gmpr_sg_is_excluded
1374 : *
1375 : * Returns TRUE if the (intf, s, g) tuple is on an Exclude list, or FALSE
1376 : * if not.
1377 : */
1378 : boolean
1379 0 : gmpr_sg_is_excluded (gmp_instance_id instance_id, gmpx_intf_id intf_id,
1380 : const uint8_t *group_addr, const uint8_t *source_addr)
1381 : {
1382 : gmpr_instance *instance;
1383 : gmpr_intf *intf;
1384 : gmpr_group *group;
1385 : gmp_addr_cat_entry *cat_entry;
1386 :
1387 : /* Get the instance and interface. */
1388 :
1389 0 : instance = gmpr_get_instance(instance_id);
1390 0 : intf = gmpr_intf_lookup(instance, intf_id);
1391 0 : if (!intf)
1392 0 : return FALSE;
1393 :
1394 : /* Look up the address catalog entry for the source address. */
1395 :
1396 0 : cat_entry = gmp_lookup_addr_cat_entry(&instance->rinst_addr_cat,
1397 : source_addr);
1398 0 : if (!cat_entry)
1399 0 : return FALSE;
1400 :
1401 : /* Look up the group. */
1402 :
1403 0 : group = gmpr_group_lookup(intf, group_addr);
1404 0 : if (!group)
1405 0 : return FALSE;
1406 :
1407 : /* If the source is in the stopped-timer list, it is being excluded. */
1408 :
1409 0 : return gmp_addr_in_list(&group->rgroup_src_addr_stopped,
1410 : cat_entry->adcat_ent_ord);
1411 : }
1412 :
1413 :
1414 : /*
1415 : * gmpr_update_trace_flags
1416 : *
1417 : * Update trace flags for an instance.
1418 : */
1419 : void
1420 125 : gmpr_update_trace_flags (gmp_instance_id instance_id, uint32_t trace_flags)
1421 : {
1422 : gmpr_instance *instance;
1423 :
1424 : /* Get the instance. */
1425 :
1426 125 : instance = gmpr_get_instance(instance_id);
1427 :
1428 : /* Update the trace flags. */
1429 :
1430 125 : instance->rinst_traceflags = trace_flags;
1431 125 : }
1432 :
1433 :
1434 : /*
1435 : * gmpr_query_sequence_internal
1436 : *
1437 : * Trigger an initial query sequence. "force" is TRUE if we should force
1438 : * the queries even if query transmission is suppressed.
1439 : */
1440 : static void
1441 0 : gmpr_query_sequence_internal (gmp_instance_id instance_id, gmpx_intf_id intf_id,
1442 : boolean force)
1443 : {
1444 : gmpr_instance *instance;
1445 : gmpr_intf *intf;
1446 :
1447 : /* Get the instance and interface. */
1448 :
1449 0 : instance = gmpr_get_instance(instance_id);
1450 0 : intf = gmpr_intf_lookup(instance, intf_id);
1451 0 : if (intf) {
1452 :
1453 : /*
1454 : * Set the flag that overrides query suppression according to
1455 : * the force parameter.
1456 : */
1457 0 : intf->rintf_gen_query_requested = force;
1458 :
1459 : /* Launch the queries. */
1460 :
1461 0 : gmpr_setup_initial_query_timer(intf);
1462 : }
1463 0 : }
1464 :
1465 :
1466 : /*
1467 : * gmpr_force_general_queries
1468 : *
1469 : * Trigger a sequence of general queries to be sent on an interface.
1470 : * The queries are sent regardless of whether queries are normally
1471 : * suppressed on the interface. This is typically only used when
1472 : * general queries are otherwise suppressed.
1473 : */
1474 : void
1475 0 : gmpr_force_general_queries (gmp_instance_id instance_id, gmpx_intf_id intf_id)
1476 : {
1477 0 : gmpr_query_sequence_internal(instance_id, intf_id, TRUE);
1478 0 : }
1479 :
1480 :
1481 : /*
1482 : * gmpr_request_general_queries
1483 : *
1484 : * Trigger a general query sequence on an interface. The queries will
1485 : * not be sent if queries are suppressed on the interface.
1486 : */
1487 : void
1488 0 : gmpr_request_general_queries (gmp_instance_id instance_id, gmpx_intf_id intf_id)
1489 : {
1490 0 : gmpr_query_sequence_internal(instance_id, intf_id, FALSE);
1491 0 : }
1492 :
1493 :
1494 : /*
1495 : * gmpr_one_query_internal
1496 : *
1497 : * Trigger the transmission of a single general query on an interface.
1498 : * "force" is TRUE if we should force the query even if query
1499 : * transmission is suppressed.
1500 : */
1501 : static void
1502 0 : gmpr_one_query_internal (gmp_instance_id instance_id, gmpx_intf_id intf_id,
1503 : boolean force)
1504 : {
1505 : gmpr_instance *instance;
1506 : gmpr_intf *intf;
1507 :
1508 : /* Get the instance and interface. */
1509 :
1510 0 : instance = gmpr_get_instance(instance_id);
1511 0 : intf = gmpr_intf_lookup(instance, intf_id);
1512 0 : if (intf) {
1513 :
1514 : /* Set the flag and trigger the transmission. */
1515 :
1516 0 : intf->rintf_gen_query_requested = force;
1517 0 : gmpr_trigger_one_query(intf);
1518 : }
1519 0 : }
1520 :
1521 :
1522 : /*
1523 : * gmpr_force_one_general_query
1524 : *
1525 : * Trigger the transmission of a single general query on an interface.
1526 : * The query is sent regardless of whether queries are normally
1527 : * suppressed on the interface. This is typically only used when
1528 : * general queries are otherwise suppressed.
1529 : */
1530 : void
1531 0 : gmpr_force_one_general_query (gmp_instance_id instance_id, gmpx_intf_id intf_id)
1532 : {
1533 0 : gmpr_one_query_internal(instance_id, intf_id, TRUE);
1534 0 : }
1535 :
1536 :
1537 : /*
1538 : * gmpr_request_one_general_query
1539 : *
1540 : * Trigger the transmission of a single general query on an interface.
1541 : * The query will not be sent if queries are suppressed on the
1542 : * interface.
1543 : */
1544 : void
1545 0 : gmpr_request_one_general_query (gmp_instance_id instance_id,
1546 : gmpx_intf_id intf_id)
1547 : {
1548 0 : gmpr_one_query_internal(instance_id, intf_id, FALSE);
1549 0 : }
1550 :
1551 :
1552 : /*
1553 : * gmpr_notify_oif_map_change
1554 : *
1555 : * Update the OIF mappings for an input interface. This is done when the
1556 : * OIF map for that interface changes, or any other circumstance where
1557 : * the input->output interface mapping needs to be updated.
1558 : */
1559 : void
1560 0 : gmpr_notify_oif_map_change (gmp_proto proto, gmpx_intf_id intf_id)
1561 : {
1562 : gmpr_intf *intf;
1563 :
1564 : /* Get the interface. */
1565 :
1566 0 : intf = gmpr_intf_lookup_global(proto, intf_id);
1567 :
1568 : /* Bail if there's no interface. */
1569 :
1570 0 : if (!intf)
1571 0 : return;
1572 :
1573 : /* Do the deed. */
1574 :
1575 0 : gmpr_notify_oif_map_change_internal(intf);
1576 : }
|