2 * Copyright (c) 1998 by the University of Oregon.
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
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
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.
29 * Other copyrights might apply to parts of this software and are so
30 * noted when applicable.
33 * Questions concerning this software should be directed to
34 * Kurt Windisch (kurtw@antc.uoregon.edu)
36 * $Id: route.c,v 1.3 1999/10/27 11:40:30 jinmei Exp $
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".
43 * The pimd program is COPYRIGHT 1998 by University of Southern California.
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".
49 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
50 * Leland Stanford Junior University.
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));
62 u_int32 default_source_preference = DEFAULT_LOCAL_PREF;
63 u_int32 default_source_metric = DEFAULT_LOCAL_METRIC;
66 /* Return the iif for given address */
69 struct sockaddr_in6 *address;
73 k_req_incoming(address, &rpfc);
74 if (IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
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,
86 struct sockaddr_in6 *source;
89 pim_nbr_entry_t *pim_nbr;
90 struct sockaddr_in6 *next_hop_router_addr;
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))
104 return (pim_nbr_entry_t *)NULL;
107 /* TODO: check again the exact setup if the source is local or directly
110 /* TODO: XXX: change the metric and preference for all (S,G) entries per
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.
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.
126 set_incoming(srcentry_ptr, srctype)
127 srcentry_t *srcentry_ptr;
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;
136 /* Preference will be 0 if directly connected */
137 srcentry_ptr->preference = 0;
138 srcentry_ptr->metric = 0;
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;
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));
159 srcentry_ptr->incoming = rpfc.iif;
160 neighbor_addr = &rpfc.rpfneighbor;
163 /* The source is directly connected.
165 srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
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;
175 * The upstream router must be a (PIM router) neighbor, otherwise we
176 * are in big trouble ;-)
178 for (n = v->uv_pim_neighbors; n != NULL; n = n->next) {
179 if (inet6_lessthan(neighbor_addr, &n->address))
181 if (inet6_equal(neighbor_addr, &n->address)) {
183 *The upstream router is found in the list of neighbors.
186 srcentry_ptr->upstream = n;
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));
197 /* TODO: control the number of messages! */
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;
208 /* Set the leaves in a new mrtentry */
209 void set_leaves(mrtentry_ptr)
210 mrtentry_t *mrtentry_ptr;
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);
222 /* Handle new receiver
224 * TODO: XXX: currently `source` is not used. Will be used with IGMPv3 where
225 * we have source-specific Join/Prune.
228 add_leaf(vifi, source, group)
230 struct sockaddr_in6 *source;
231 struct sockaddr_in6 *group;
233 grpentry_t *grpentry_ptr;
234 mrtentry_t *mrtentry_srcs;
238 grpentry_ptr = find_group(group);
239 if (grpentry_ptr == (grpentry_t *)NULL)
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) {
247 /* if applicable, add the vif to the leaves */
248 if (mrtentry_srcs->incoming == vifi)
251 if(!(IF_ISSET(vifi, &mrtentry_srcs->leaves))) {
254 log(LOG_DEBUG, 0, "Adding leaf vif %d for src %s group %s",
256 inet6_fmt(&mrtentry_srcs->source->address.sin6_addr),
257 inet6_fmt(&group->sin6_addr));
259 IF_COPY(&mrtentry_srcs->leaves, &new_leaves);
260 IF_SET(vifi, &new_leaves); /* Add the leaf */
263 change_interfaces(mrtentry_srcs,
264 mrtentry_srcs->incoming,
265 &mrtentry_srcs->pruned_oifs,
267 &mrtentry_srcs->asserted_oifs);
269 /* Handle transition from negative cache */
270 if(state_change == 1)
271 trigger_join_alert(mrtentry_srcs);
278 * TODO: XXX: currently `source` is not used. To be used with IGMPv3 where
279 * we have source-specific joins/prunes.
282 delete_leaf(vifi, source, group)
284 struct sockaddr_in6 *source;
285 struct sockaddr_in6 *group;
287 grpentry_t *grpentry_ptr;
288 mrtentry_t *mrtentry_srcs;
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;
299 grpentry_ptr = find_group(group);
300 if (grpentry_ptr == (grpentry_t *)NULL)
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) {
308 /* if applicable, delete the vif from the leaves */
309 if (mrtentry_srcs->incoming == vifi)
312 if(IF_ISSET(vifi, &mrtentry_srcs->leaves)) {
315 log(LOG_DEBUG, 0, "Deleting leaf vif %d for src %s, group %s",
317 inet6_fmt(&mrtentry_srcs->source->address.sin6_addr),
318 inet6_fmt(&group->sin6_addr));
320 IF_COPY(&mrtentry_srcs->leaves, &new_leaves);
321 IF_CLR(vifi, &new_leaves); /* Remove the leaf */
324 change_interfaces(mrtentry_srcs,
325 mrtentry_srcs->incoming,
326 &mrtentry_srcs->pruned_oifs,
328 &mrtentry_srcs->asserted_oifs);
330 /* Handle transition to negative cache */
331 if(state_change == -1)
332 trigger_prune_alert(mrtentry_srcs);
338 calc_oifs(mrtentry_ptr, oifs_ptr)
339 mrtentry_t *mrtentry_ptr;
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
352 if (mrtentry_ptr == (mrtentry_t *)NULL) {
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);
368 * Set the iif, join/prune/leaves/asserted interfaces. Calculate and
370 * Return 1 if oifs change from NULL to not-NULL.
371 * Return -1 if oifs change from non-NULL to NULL
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.
378 change_interfaces(mrtentry_ptr, new_iif, new_pruned_oifs,
379 new_leaves_, new_asserted_oifs)
380 mrtentry_t *mrtentry_ptr;
382 if_set *new_pruned_oifs;
384 if_set *new_asserted_oifs;
386 if_set old_pruned_oifs; /* unnecessary? */
387 if_set old_leaves; /* unnecessary? */
389 if_set new_real_oifs; /* The result oifs */
390 if_set old_real_oifs;
391 if_set old_asserted_oifs; /* unnecessary? */
395 if (mrtentry_ptr == (mrtentry_t *)NULL)
398 IF_COPY(new_leaves_, &new_leaves);
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);
405 IF_COPY(&mrtentry_ptr->oifs, &old_real_oifs);
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);
413 if (IF_ISEMPTY(&old_real_oifs)) {
414 if (IF_ISEMPTY(&new_real_oifs))
419 if (IF_ISEMPTY(&new_real_oifs))
425 if ((IF_SAME(&new_real_oifs, &old_real_oifs))
426 && (new_iif == old_iif))
427 return 0; /* Nothing to change */
429 IF_COPY(&new_real_oifs, &mrtentry_ptr->oifs);
431 k_chg_mfc(mld6_socket, &mrtentry_ptr->source->address,
432 &mrtentry_ptr->group->group, new_iif, &new_real_oifs);
435 rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
438 return (return_value);
442 /* TODO: implement it. Required to allow changing of the physical interfaces
443 * configuration without need to restart pimd.
446 delete_vif_from_mrt(vifi)
454 max_prune_timeout(mrtentry_ptr)
455 mrtentry_t *mrtentry_ptr;
459 /* XXX: I don't understand how the variable works...(jinmei@kame.net) */
460 u_int16 time_left = 0;
462 u_int16 max_holdtime = 0;
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];
471 /* XXX: This is original. But does it have any meaning? */
472 max_holdtime = time_left;
475 if(max_holdtime == 0)
476 max_holdtime = (u_int16)PIM_JOIN_PRUNE_HOLDTIME;
478 return(max_holdtime);
483 process_kernel_call()
485 register struct mrt6msg *im; /* igmpmsg control struct */
487 im = (struct mrt6msg *) mld6_recv_buf;
489 switch (im->im6_msgtype) {
490 case MRT6MSG_NOCACHE:
491 process_cache_miss(im);
493 case MRT6MSG_WRONGMIF:
494 process_wrong_iif(im);
498 log(LOG_DEBUG, 0, "Unknown kernel_call code, %d", im->im6_msgtype);
506 * 1. Create (S,G) entry (find_route(CREATE))
507 * a. set iif and oifs
510 process_cache_miss(im)
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;
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.
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]);
528 log(LOG_DEBUG, 0, "Cache miss, src %s, dst %s",
529 inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr));
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))
536 /* Create the (S,G) entry */
537 mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
538 if (mrtentry_ptr == (mrtentry_t *)NULL)
540 mrtentry_ptr->flags &= ~MRTF_NEW;
543 set_leaves(mrtentry_ptr);
544 calc_oifs(mrtentry_ptr, &(mrtentry_ptr->oifs));
546 /* Add it to the kernel */
547 k_chg_mfc(mld6_socket, &source, &group, mrtentry_ptr->incoming,
548 &mrtentry_ptr->oifs);
551 rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
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);
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
567 process_wrong_iif(im)
570 static struct sockaddr_in6 source = {sizeof(source), AF_INET6};
571 static struct sockaddr_in6 group = {sizeof(group), AF_INET6};
573 mrtentry_t *mrtentry_ptr;
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]);
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))
586 mrtentry_ptr = find_route(&source, &group, MRTF_SG, DONT_CREATE);
587 if(mrtentry_ptr == (mrtentry_t *)NULL)
590 /* Ratelimit prunes or asserts */
592 if(uvifs[mifi].uv_flags & VIFF_POINT_TO_POINT) {
594 /* Wrong vif on P2P interface - rate-limit prunes */
596 if(mrtentry_ptr->last_prune[mifi] == virtual_time)
597 /* Skip due to rate-limiting */
599 mrtentry_ptr->last_prune[mifi] = virtual_time;
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);
607 "Can't send wrongvif prune on p2p %s: no remote address",
608 uvifs[mifi].uv_lcl_addr);
613 /* Wrong vif on LAN interface - rate-limit asserts */
615 if(mrtentry_ptr->last_assert[mifi] == virtual_time)
616 /* Skip due to rate-limiting */
618 mrtentry_ptr->last_assert[mifi] = virtual_time;
620 /* Send the assert */
621 send_pim6_assert(&source, &group, mifi, mrtentry_ptr);
627 trigger_prune_alert(mrtentry_ptr)
628 mrtentry_t *mrtentry_ptr;
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));
635 /* Set the entry timer to the max of the prune timers */
636 SET_TIMER(mrtentry_ptr->timer, max_prune_timeout(mrtentry_ptr));
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);
646 trigger_join_alert(mrtentry_ptr)
647 mrtentry_t *mrtentry_ptr;
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));
654 /* Refresh the entry timer */
655 SET_TIMER(mrtentry_ptr->timer, PIM_DATA_TIMEOUT);
658 send_pim6_graft(mrtentry_ptr);