]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pim6dd/route.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / usr.sbin / pim6dd / route.c
1 /*
2  *  Copyright (c) 1998 by the University of Oregon.
3  *  All rights reserved.
4  *
5  *  Permission to use, copy, modify, and distribute this software and
6  *  its documentation in source and binary forms for lawful
7  *  purposes and without fee is hereby granted, provided
8  *  that the above copyright notice appear in all copies and that both
9  *  the copyright notice and this permission notice appear in supporting
10  *  documentation, and that any documentation, advertising materials,
11  *  and other materials related to such distribution and use acknowledge
12  *  that the software was developed by the University of Oregon.
13  *  The name of the University of Oregon may not be used to endorse or
14  *  promote products derived from this software without specific prior
15  *  written permission.
16  *
17  *  THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
18  *  ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  THIS SOFTWARE IS
19  *  PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
20  *  INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
21  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
22  *  NON-INFRINGEMENT.
23  *
24  *  IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
25  *  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
26  *  TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
27  *  THE USE OR PERFORMANCE OF THIS SOFTWARE.
28  *
29  *  Other copyrights might apply to parts of this software and are so
30  *  noted when applicable.
31  */
32 /*
33  *  Questions concerning this software should be directed to
34  *  Kurt Windisch (kurtw@antc.uoregon.edu)
35  *
36  *  $Id: route.c,v 1.3 1999/10/27 11:40:30 jinmei Exp $
37  */
38 /*
39  * Part of this program has been derived from PIM sparse-mode pimd.
40  * The pimd program is covered by the license in the accompanying file
41  * named "LICENSE.pimd".
42  *
43  * The pimd program is COPYRIGHT 1998 by University of Southern California.
44  *
45  * Part of this program has been derived from mrouted.
46  * The mrouted program is covered by the license in the accompanying file
47  * named "LICENSE.mrouted".
48  *
49  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
50  * Leland Stanford Junior University.
51  *
52  * $FreeBSD$
53  */
54
55 #include "defs.h"
56
57
58 static u_int16 max_prune_timeout __P((mrtentry_t *));
59 static void   process_cache_miss  __P((struct mrt6msg *im));
60 static void   process_wrong_iif   __P((struct mrt6msg *im));
61
62 u_int32         default_source_preference = DEFAULT_LOCAL_PREF;
63 u_int32         default_source_metric     = DEFAULT_LOCAL_METRIC;
64
65
66 /* Return the iif for given address */
67 vifi_t
68 get_iif(address)
69     struct sockaddr_in6 *address;
70 {
71     struct rpfctl rpfc;
72
73     k_req_incoming(address, &rpfc);
74     if (IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
75         return (NO_VIF);
76     return (rpfc.iif);
77 }
78
79 /* Return the PIM neighbor toward a source */
80 /* If route not found or if a local source or if a directly connected source,
81  * but is not PIM router, or if the first hop router is not a PIM router,
82  * then return NULL.
83  */
84 pim_nbr_entry_t *
85 find_pim6_nbr(source)
86     struct sockaddr_in6 *source;
87 {
88     struct rpfctl rpfc;
89     pim_nbr_entry_t *pim_nbr;
90     struct sockaddr_in6 *next_hop_router_addr;
91
92     if (local_address(source) != NO_VIF)
93         return (pim_nbr_entry_t *)NULL;
94     k_req_incoming(source, &rpfc);
95     if ((IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
96         || (rpfc.iif == NO_VIF))
97         return (pim_nbr_entry_t *)NULL;
98     next_hop_router_addr = &rpfc.rpfneighbor;
99     for (pim_nbr = uvifs[rpfc.iif].uv_pim_neighbors;
100          pim_nbr != (pim_nbr_entry_t *)NULL;
101          pim_nbr = pim_nbr->next)
102         if (inet6_equal(&pim_nbr->address, next_hop_router_addr))
103             return(pim_nbr);
104     return (pim_nbr_entry_t *)NULL;
105 }
106
107 /* TODO: check again the exact setup if the source is local or directly
108  * connected!!!
109  */
110 /* TODO: XXX: change the metric and preference for all (S,G) entries per
111  * source?
112  */
113 /* PIMDM TODO - If possible, this would be the place to correct set the
114  * source's preference and metric to that obtained from the kernel
115  * and/or unicast routing protocol.  For now, set it to the configured
116  * default for local pref/metric.
117  */
118 /*
119  * Set the iif, upstream router, preference and metric for the route
120  * toward the source. Return TRUE is the route was found, othewise FALSE.
121  * If srctype==PIM_IIF_SOURCE and if the source is directly connected
122  * then the "upstream" is set to NULL.
123  * Note that srctype is a hold-over from the PIM-SM daemon and is unused.
124  */
125 int
126 set_incoming(srcentry_ptr, srctype)
127     srcentry_t *srcentry_ptr;
128     int srctype;
129 {
130     struct rpfctl rpfc;
131     struct sockaddr_in6 *source = &srcentry_ptr->address;
132     struct sockaddr_in6 *neighbor_addr;
133     register struct uvif *v;
134     register pim_nbr_entry_t *n;
135
136     /* Preference will be 0 if directly connected */
137     srcentry_ptr->preference = 0;
138     srcentry_ptr->metric = 0;
139
140     if ((srcentry_ptr->incoming = local_address(source)) != NO_VIF) {
141         /* The source is a local address */
142         /* TODO: set the upstream to myself? */
143         srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
144         return (TRUE);
145     }
146
147     if ((srcentry_ptr->incoming = find_vif_direct(source)) == NO_VIF) {
148     /* TODO: probably need to check the case if the iif is disabled */
149         /* Use the lastest resource: the kernel unicast routing table */
150         k_req_incoming(source, &rpfc);
151         if ((rpfc.iif == NO_VIF) ||
152             IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr)) {
153             /* couldn't find a route */
154             IF_DEBUG(DEBUG_PIM_MRT | DEBUG_RPF)
155                 log(LOG_DEBUG, 0, "NO ROUTE found for %s",
156                     inet6_fmt(&source->sin6_addr));
157             return(FALSE);
158         }
159         srcentry_ptr->incoming = rpfc.iif;
160         neighbor_addr = &rpfc.rpfneighbor;
161     }
162     else {
163         /* The source is directly connected.
164          */
165         srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
166         return (TRUE);
167     }
168
169     /* set the preference for sources that aren't directly connected. */
170     v = &uvifs[srcentry_ptr->incoming];
171     srcentry_ptr->preference = v->uv_local_pref;
172     srcentry_ptr->metric = v->uv_local_metric;
173
174     /*
175      * The upstream router must be a (PIM router) neighbor, otherwise we
176      * are in big trouble ;-)
177      */
178     for (n = v->uv_pim_neighbors; n != NULL; n = n->next) {
179         if (inet6_lessthan(neighbor_addr, &n->address))
180             continue;
181         if (inet6_equal(neighbor_addr, &n->address)) {
182             /*
183              *The upstream router is found in the list of neighbors.
184              * We are safe!
185              */
186             srcentry_ptr->upstream = n;
187             IF_DEBUG(DEBUG_RPF)
188                 log(LOG_DEBUG, 0,
189                     "For src %s, iif is %d, next hop router is %s",
190                     inet6_fmt(&source->sin6_addr), srcentry_ptr->incoming,
191                     inet6_fmt(&neighbor_addr->sin6_addr));
192             return(TRUE);
193         }
194         else break;
195     }
196
197     /* TODO: control the number of messages! */
198     log(LOG_INFO, 0,
199         "For src %s, iif is %d, next hop router is %s: NOT A PIM ROUTER",
200         inet6_fmt(&source->sin6_addr), srcentry_ptr->incoming,
201         inet6_fmt(&neighbor_addr->sin6_addr));
202     srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
203
204     return(FALSE);
205 }
206
207
208 /* Set the leaves in a new mrtentry */
209 void set_leaves(mrtentry_ptr)
210      mrtentry_t *mrtentry_ptr;
211 {
212     vifi_t vifi;
213     struct uvif *v;
214
215     /* Check for a group report on each vif */
216     for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v)
217         if(check_multicast_listener(v, &mrtentry_ptr->group->group))
218             IF_SET(vifi, &mrtentry_ptr->leaves);
219 }
220
221
222 /* Handle new receiver
223  *
224  * TODO: XXX: currently `source` is not used. Will be used with IGMPv3 where
225  * we have source-specific Join/Prune.
226  */
227 void
228 add_leaf(vifi, source, group)
229     vifi_t vifi;
230     struct sockaddr_in6 *source;
231     struct sockaddr_in6 *group;
232 {
233     grpentry_t *grpentry_ptr;
234     mrtentry_t *mrtentry_srcs;
235     if_set new_leaves;
236     int state_change;
237
238     grpentry_ptr = find_group(group);
239     if (grpentry_ptr == (grpentry_t *)NULL)
240         return;
241
242     /* walk the source list for the group and add vif to oiflist */
243     for (mrtentry_srcs = grpentry_ptr->mrtlink;
244          mrtentry_srcs != (mrtentry_t *)NULL;
245          mrtentry_srcs = mrtentry_srcs->grpnext) {
246
247         /* if applicable, add the vif to the leaves */
248         if (mrtentry_srcs->incoming == vifi)
249             continue;
250
251         if(!(IF_ISSET(vifi, &mrtentry_srcs->leaves))) {
252
253             IF_DEBUG(DEBUG_MRT)
254                 log(LOG_DEBUG, 0, "Adding leaf vif %d for src %s group %s",
255                     vifi,
256                     inet6_fmt(&mrtentry_srcs->source->address.sin6_addr),
257                     inet6_fmt(&group->sin6_addr));
258
259             IF_COPY(&mrtentry_srcs->leaves, &new_leaves);
260             IF_SET(vifi, &new_leaves);    /* Add the leaf */
261
262             state_change =
263                 change_interfaces(mrtentry_srcs,
264                                   mrtentry_srcs->incoming,
265                                   &mrtentry_srcs->pruned_oifs,
266                                   &new_leaves,
267                                   &mrtentry_srcs->asserted_oifs);
268
269             /* Handle transition from negative cache */
270             if(state_change == 1)
271                 trigger_join_alert(mrtentry_srcs);
272         }
273     }
274 }
275
276
277 /*
278  * TODO: XXX: currently `source` is not used. To be used with IGMPv3 where
279  * we have source-specific joins/prunes.
280  */
281 void
282 delete_leaf(vifi, source, group)
283     vifi_t vifi;
284     struct sockaddr_in6 *source;
285     struct sockaddr_in6 *group;
286 {
287     grpentry_t *grpentry_ptr;
288     mrtentry_t *mrtentry_srcs;
289     if_set new_leaves;
290     int state_change;
291
292     /* mrtentry_t *mrtentry_ptr;
293      * mrtentry_t *mrtentry_srcs;
294      * vifbitmap_t new_oifs;
295      * vifbitmap_t old_oifs;
296      * vifbitmap_t new_leaves;
297      */
298
299     grpentry_ptr = find_group(group);
300     if (grpentry_ptr == (grpentry_t *)NULL)
301         return;
302
303     /* walk the source list for the group and delete vif to leaves */
304     for (mrtentry_srcs = grpentry_ptr->mrtlink;
305          mrtentry_srcs != (mrtentry_t *)NULL;
306          mrtentry_srcs = mrtentry_srcs->grpnext) {
307
308         /* if applicable, delete the vif from the leaves */
309         if (mrtentry_srcs->incoming == vifi)
310             continue;
311
312         if(IF_ISSET(vifi, &mrtentry_srcs->leaves)) {
313
314             IF_DEBUG(DEBUG_MRT)
315                 log(LOG_DEBUG, 0, "Deleting leaf vif %d for src %s, group %s",
316                     vifi,
317                     inet6_fmt(&mrtentry_srcs->source->address.sin6_addr),
318                     inet6_fmt(&group->sin6_addr));
319
320             IF_COPY(&mrtentry_srcs->leaves, &new_leaves);
321             IF_CLR(vifi, &new_leaves);    /* Remove the leaf */
322
323             state_change =
324                 change_interfaces(mrtentry_srcs,
325                                   mrtentry_srcs->incoming,
326                                   &mrtentry_srcs->pruned_oifs,
327                                   &new_leaves,
328                                   &mrtentry_srcs->asserted_oifs);
329
330             /* Handle transition to negative cache */
331             if(state_change == -1)
332                 trigger_prune_alert(mrtentry_srcs);
333         }
334     }
335 }
336
337 void
338 calc_oifs(mrtentry_ptr, oifs_ptr)
339     mrtentry_t *mrtentry_ptr;
340     if_set *oifs_ptr;
341 {
342     if_set oifs;
343
344     /*
345      * oifs =
346      * ((nbr_ifs - my_prune) + my_leaves) - my_filters - incoming_interface,
347      * i.e.`leaves` have higher priority than `prunes`, but lower than `filters'.
348      * Asserted oifs (those that lost assert) are handled as pruned oifs.
349      * The incoming interface is always deleted from the oifs
350      */
351
352     if (mrtentry_ptr == (mrtentry_t *)NULL) {
353         IF_ZERO(oifs_ptr);
354         return;
355     }
356
357     IF_COPY(&nbr_mifs, &oifs);
358     IF_CLR_MASK(&oifs, &mrtentry_ptr->pruned_oifs);
359     IF_MERGE(&oifs, &mrtentry_ptr->leaves, &oifs);
360     IF_CLR_MASK(&oifs, &mrtentry_ptr->asserted_oifs);
361     IF_CLR_MASK(&oifs, &mrtentry_ptr->filter_oifs);
362     IF_CLR(mrtentry_ptr->incoming, &oifs);
363     IF_COPY(&oifs, oifs_ptr);
364 }
365
366
367 /*
368  * Set the iif, join/prune/leaves/asserted interfaces. Calculate and
369  * set the oifs.
370  * Return 1 if oifs change from NULL to not-NULL.
371  * Return -1 if oifs change from non-NULL to NULL
372  *  else return 0
373  * If the iif change or if the oifs change from NULL to non-NULL
374  * or vice-versa, then schedule that mrtentry join/prune timer to
375  * timeout immediately.
376  */
377 int
378 change_interfaces(mrtentry_ptr, new_iif, new_pruned_oifs,
379                   new_leaves_, new_asserted_oifs)
380     mrtentry_t *mrtentry_ptr;
381     vifi_t new_iif;
382     if_set *new_pruned_oifs;
383     if_set *new_leaves_;
384     if_set *new_asserted_oifs;
385 {
386     if_set old_pruned_oifs;     /* unnecessary? */
387     if_set old_leaves;          /* unnecessary? */
388     if_set new_leaves;
389     if_set new_real_oifs;    /* The result oifs */
390     if_set old_real_oifs;
391     if_set old_asserted_oifs;   /* unnecessary? */
392     vifi_t      old_iif;
393     int return_value;
394
395     if (mrtentry_ptr == (mrtentry_t *)NULL)
396         return (0);
397
398     IF_COPY(new_leaves_, &new_leaves);
399
400     old_iif = mrtentry_ptr->incoming;
401     IF_COPY(&mrtentry_ptr->leaves, &old_leaves);
402     IF_COPY(&mrtentry_ptr->pruned_oifs, &old_pruned_oifs);
403     IF_COPY(&mrtentry_ptr->asserted_oifs, &old_asserted_oifs);
404
405     IF_COPY(&mrtentry_ptr->oifs, &old_real_oifs);
406
407     mrtentry_ptr->incoming = new_iif;
408     IF_COPY(new_pruned_oifs, &mrtentry_ptr->pruned_oifs);
409     IF_COPY(&new_leaves, &mrtentry_ptr->leaves);
410     IF_COPY(new_asserted_oifs, &mrtentry_ptr->asserted_oifs);
411     calc_oifs(mrtentry_ptr, &new_real_oifs);
412
413     if (IF_ISEMPTY(&old_real_oifs)) {
414         if (IF_ISEMPTY(&new_real_oifs))
415             return_value = 0;
416         else
417             return_value = 1;
418     } else {
419         if (IF_ISEMPTY(&new_real_oifs))
420             return_value = -1;
421         else
422             return_value = 0;
423     }
424
425     if ((IF_SAME(&new_real_oifs, &old_real_oifs))
426         && (new_iif == old_iif))
427         return 0;                  /* Nothing to change */
428
429     IF_COPY(&new_real_oifs, &mrtentry_ptr->oifs);
430
431     k_chg_mfc(mld6_socket, &mrtentry_ptr->source->address,
432               &mrtentry_ptr->group->group, new_iif, &new_real_oifs);
433
434 #ifdef RSRR
435     rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
436 #endif /* RSRR */
437
438     return (return_value);
439 }
440
441
442 /* TODO: implement it. Required to allow changing of the physical interfaces
443  * configuration without need to restart pimd.
444  */
445 int
446 delete_vif_from_mrt(vifi)
447 vifi_t vifi;
448 {
449     return TRUE;
450 }
451
452
453 static u_int16
454 max_prune_timeout(mrtentry_ptr)
455      mrtentry_t *mrtentry_ptr;
456 {
457     vifi_t vifi;
458 #if 0
459     /* XXX: I don't understand how the variable works...(jinmei@kame.net) */
460     u_int16 time_left = 0;
461 #endif
462     u_int16 max_holdtime = 0;
463
464     for(vifi=0; vifi < numvifs; ++vifi)
465         if(IF_ISSET(vifi, &mrtentry_ptr->pruned_oifs) &&
466            mrtentry_ptr->prune_timers[vifi])
467             /* XXX - too expensive ? */
468             if(mrtentry_ptr->prune_timers[vifi] > max_holdtime)
469                     max_holdtime = mrtentry_ptr->prune_timers[vifi];
470 #if 0
471                     /* XXX: This is original. But does it have any meaning? */
472                     max_holdtime = time_left;
473 #endif
474
475     if(max_holdtime == 0)
476         max_holdtime = (u_int16)PIM_JOIN_PRUNE_HOLDTIME;
477
478     return(max_holdtime);
479 }
480
481
482 void
483 process_kernel_call()
484 {
485     register struct mrt6msg *im; /* igmpmsg control struct */
486
487     im = (struct mrt6msg *) mld6_recv_buf;
488
489     switch (im->im6_msgtype) {
490      case MRT6MSG_NOCACHE:
491         process_cache_miss(im);
492         break;
493     case MRT6MSG_WRONGMIF:
494         process_wrong_iif(im);
495         break;
496     default:
497         IF_DEBUG(DEBUG_KERN)
498             log(LOG_DEBUG, 0, "Unknown kernel_call code, %d", im->im6_msgtype);
499         break;
500     }
501 }
502
503
504 /*
505  * Protocol actions:
506  *   1. Create (S,G) entry (find_route(CREATE))
507  *      a. set iif and oifs
508  */
509 static void
510 process_cache_miss(im)
511     struct mrt6msg *im;
512 {
513     static struct sockaddr_in6 source = {sizeof(source), AF_INET6};
514     static struct sockaddr_in6 group = {sizeof(group), AF_INET6};
515     mrtentry_t *mrtentry_ptr;
516
517     /*
518      * When there is a cache miss, we check only the header of the packet
519      * (and only it should be sent up by the kernel.
520      */
521
522     group.sin6_addr  = im->im6_dst;
523     source.sin6_addr = im->im6_src;
524     group.sin6_scope_id = inet6_uvif2scopeid(&group, &uvifs[im->im6_mif]);
525     source.sin6_scope_id = inet6_uvif2scopeid(&source, &uvifs[im->im6_mif]);
526
527     IF_DEBUG(DEBUG_MFC)
528         log(LOG_DEBUG, 0, "Cache miss, src %s, dst %s",
529             inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr));
530
531     /* Don't create routing entries for the LAN scoped addresses */
532     if (IN6_IS_ADDR_MC_NODELOCAL(&group.sin6_addr) ||/* sanity? */
533         IN6_IS_ADDR_MC_LINKLOCAL(&group.sin6_addr))
534         return;
535
536     /* Create the (S,G) entry */
537     mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
538     if (mrtentry_ptr == (mrtentry_t *)NULL)
539         return;
540     mrtentry_ptr->flags &= ~MRTF_NEW;
541
542     /* Set oifs */
543     set_leaves(mrtentry_ptr);
544     calc_oifs(mrtentry_ptr, &(mrtentry_ptr->oifs));
545
546     /* Add it to the kernel */
547     k_chg_mfc(mld6_socket, &source, &group, mrtentry_ptr->incoming,
548               &mrtentry_ptr->oifs);
549
550 #ifdef RSRR
551     rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
552 #endif /* RSRR */
553
554     /* No need to call change_interfaces, but check for NULL oiflist */
555     if(IF_ISEMPTY(&mrtentry_ptr->oifs))
556         trigger_prune_alert(mrtentry_ptr);
557 }
558
559
560 /*
561  * A multicast packet has been received on wrong iif by the kernel.
562  * If the packet was received on a point-to-point interface, rate-limit
563  * prunes.  if the packet was received on a LAN interface, rate-limit
564  * asserts.
565  */
566 static void
567 process_wrong_iif(im)
568     struct mrt6msg *im;
569 {
570     static struct sockaddr_in6 source = {sizeof(source), AF_INET6};
571     static struct sockaddr_in6 group = {sizeof(group), AF_INET6};
572     mifi_t  mifi;
573     mrtentry_t *mrtentry_ptr;
574
575     group.sin6_addr  = im->im6_dst;
576     source.sin6_addr = im->im6_src;
577     mifi   = (mifi_t)im->im6_mif;
578     group.sin6_scope_id = inet6_uvif2scopeid(&group, &uvifs[mifi]);
579     source.sin6_scope_id = inet6_uvif2scopeid(&source, &uvifs[mifi]);
580
581     /* PIMDM TODO Don't create routing entries for the LAN scoped addresses */
582     if (IN6_IS_ADDR_MC_NODELOCAL(&group.sin6_addr) ||/* sanity? */
583         IN6_IS_ADDR_MC_LINKLOCAL(&group.sin6_addr))
584         return;
585
586     mrtentry_ptr = find_route(&source, &group, MRTF_SG, DONT_CREATE);
587     if(mrtentry_ptr == (mrtentry_t *)NULL)
588         return;
589
590     /* Ratelimit prunes or asserts */
591 #ifdef notyet
592     if(uvifs[mifi].uv_flags & VIFF_POINT_TO_POINT) {
593
594         /* Wrong vif on P2P interface - rate-limit prunes */
595
596         if(mrtentry_ptr->last_prune[mifi] == virtual_time)
597             /* Skip due to rate-limiting */
598             return;
599         mrtentry_ptr->last_prune[mifi] = virtual_time;
600
601         if(uvifs[mifi].uv_rmt_addr)
602             send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE, mifi,
603                         uvifs[mifi].uv_rmt_addr,
604                         max_prune_timeout(mrtentry_ptr), 0);
605         else
606             log(LOG_WARNING, 0,
607                 "Can't send wrongvif prune on p2p %s: no remote address",
608                 uvifs[mifi].uv_lcl_addr);
609     } else
610 #endif
611     {
612
613         /* Wrong vif on LAN interface - rate-limit asserts */
614
615         if(mrtentry_ptr->last_assert[mifi] == virtual_time)
616             /* Skip due to rate-limiting */
617             return;
618         mrtentry_ptr->last_assert[mifi] = virtual_time;
619
620         /* Send the assert */
621         send_pim6_assert(&source, &group, mifi, mrtentry_ptr);
622     }
623 }
624
625
626 void
627 trigger_prune_alert(mrtentry_ptr)
628      mrtentry_t *mrtentry_ptr;
629 {
630     IF_DEBUG(DEBUG_MRT)
631         log(LOG_DEBUG, 0, "Now negative cache for src %s, grp %s - pruning",
632             inet6_fmt(&mrtentry_ptr->source->address.sin6_addr),
633             inet6_fmt(&mrtentry_ptr->group->group.sin6_addr));
634
635     /* Set the entry timer to the max of the prune timers */
636     SET_TIMER(mrtentry_ptr->timer, max_prune_timeout(mrtentry_ptr));
637
638     /* Send a prune */
639     if(mrtentry_ptr->upstream)
640         send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE, mrtentry_ptr->incoming,
641                      &mrtentry_ptr->upstream->address,
642                      max_prune_timeout(mrtentry_ptr), 0);
643 }
644
645 void
646 trigger_join_alert(mrtentry_ptr)
647      mrtentry_t *mrtentry_ptr;
648 {
649     IF_DEBUG(DEBUG_MRT)
650         log(LOG_DEBUG, 0, "Now forwarding state for src %s, grp %s - grafting",
651             inet6_fmt(&mrtentry_ptr->source->address.sin6_addr),
652             inet6_fmt(&mrtentry_ptr->group->group.sin6_addr));
653
654     /* Refresh the entry timer */
655     SET_TIMER(mrtentry_ptr->timer, PIM_DATA_TIMEOUT);
656
657     /* Send graft */
658     send_pim6_graft(mrtentry_ptr);
659 }