static struct rtentry * rt_mpath_selectrte(struct rtentry *rte, uint32_t hash) { struct radix_node *rn0, *rn; uint32_t total_weight; struct rtentry *rt; int64_t weight; /* beyond here, we use rn as the master copy */ rn0 = rn = (struct radix_node *)rte; rt = rte; /* gw selection by Modulo-N Hash (RFC2991) XXX need improvement? */ total_weight = rn_mpath_count(rn0); hash += hashjitter; hash %= total_weight; for (weight = abs((int32_t)hash); rt != NULL && weight >= rt->rt_weight; weight -= (rt == NULL) ? 0 : rt->rt_weight) { /* stay within the multipath routes */ if (rn->rn_dupedkey && rn->rn_mask != rn->rn_dupedkey->rn_mask) break; rn = rn->rn_dupedkey; rt = (struct rtentry *)rn; } return (rt); }
void rtalloc_mpath_fib(struct route *ro, uint32_t hash, u_int fibnum) { struct radix_node *rn0, *rn; u_int32_t n; struct rtentry *rt; int64_t weight; /* * XXX we don't attempt to lookup cached route again; what should * be done for sendto(3) case? */ if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP) && RT_LINK_IS_UP(ro->ro_rt->rt_ifp)) return; ro->ro_rt = rtalloc1_fib(&ro->ro_dst, 1, 0, fibnum); /* if the route does not exist or it is not multipath, don't care */ if (ro->ro_rt == NULL) return; if (rn_mpath_next((struct radix_node *)ro->ro_rt) == NULL) { RT_UNLOCK(ro->ro_rt); return; } /* beyond here, we use rn as the master copy */ rn0 = rn = (struct radix_node *)ro->ro_rt; n = rn_mpath_count(rn0); /* gw selection by Modulo-N Hash (RFC2991) XXX need improvement? */ hash += hashjitter; hash %= n; for (weight = abs((int32_t)hash), rt = ro->ro_rt; weight >= rt->rt_weight && rn; weight -= rt->rt_weight) { /* stay within the multipath routes */ if (rn->rn_dupedkey && rn->rn_mask != rn->rn_dupedkey->rn_mask) break; rn = rn->rn_dupedkey; rt = (struct rtentry *)rn; } /* XXX try filling rt_gwroute and avoid unreachable gw */ /* gw selection has failed - there must be only zero weight routes */ if (!rn) { RT_UNLOCK(ro->ro_rt); ro->ro_rt = NULL; return; } if (ro->ro_rt != rt) { RTFREE_LOCKED(ro->ro_rt); ro->ro_rt = (struct rtentry *)rn; RT_LOCK(ro->ro_rt); RT_ADDREF(ro->ro_rt); } RT_UNLOCK(ro->ro_rt); }
/* * allocate a route, potentially using multipath to select the peer. */ void rtalloc_mpath(struct route *ro, u_int32_t *srcaddrp) { #if defined(INET) || defined(INET6) struct radix_node *rn; int hash, npaths, threshold; #endif /* * return a cached entry if it is still valid, otherwise we increase * the risk of disrupting local flows. */ if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) return; ro->ro_rt = rtalloc1(&ro->ro_dst, RT_REPORT, ro->ro_tableid); /* if the route does not exist or it is not multipath, don't care */ if (!ro->ro_rt || !(ro->ro_rt->rt_flags & RTF_MPATH)) return; /* check if multipath routing is enabled for the specified protocol */ if (!(0 #ifdef INET || (ipmultipath && ro->ro_dst.sa_family == AF_INET) #endif #ifdef INET6 || (ip6_multipath && ro->ro_dst.sa_family == AF_INET6) #endif )) return; #if defined(INET) || defined(INET6) /* gw selection by Hash-Threshold (RFC 2992) */ rn = (struct radix_node *)ro->ro_rt; npaths = rn_mpath_count(rn); hash = rn_mpath_hash(ro, srcaddrp) & 0xffff; threshold = 1 + (0xffff / npaths); while (hash > threshold && rn) { /* stay within the multipath routes */ if (rn_mpath_next(rn, 0) == NULL) break; rn = rn->rn_dupedkey; hash -= threshold; } /* XXX try filling rt_gwroute and avoid unreachable gw */ /* if gw selection fails, use the first match (default) */ if (!rn) return; rtfree(ro->ro_rt); ro->ro_rt = (struct rtentry *)rn; ro->ro_rt->rt_refcnt++; #endif }
void rtalloc_mpath(struct route *ro, int hash) { struct radix_node *rn0, *rn; int n; /* * XXX we don't attempt to lookup cached route again; what should * be done for sendto(3) case? */ if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) return; /* XXX */ #ifdef __FreeBSD__ ro->ro_rt = rtalloc1(&ro->ro_dst, 1, 0UL); #else ro->ro_rt = rtalloc1(&ro->ro_dst, 1); #endif /* if the route does not exist or it is not multipath, don't care */ if (!ro->ro_rt || !rn_mpath_next((struct radix_node *)ro->ro_rt)) return; /* beyond here, we use rn as the master copy */ rn0 = rn = (struct radix_node *)ro->ro_rt; n = rn_mpath_count(rn0); /* gw selection by Modulo-N Hash (RFC2991) XXX need improvement? */ hash += hashjitter; hash %= n; while (hash-- > 0 && rn) { /* stay within the multipath routes */ if (rn->rn_dupedkey && rn->rn_mask != rn->rn_dupedkey->rn_mask) break; rn = rn->rn_dupedkey; } /* XXX try filling rt_gwroute and avoid unreachable gw */ /* if gw selection fails, use the first match (default) */ if (!rn) return; rtfree(ro->ro_rt); ro->ro_rt = (struct rtentry *)rn; ro->ro_rt->rt_refcnt++; }
void rn_mpath_reprio(struct radix_node *rn, int newprio) { struct radix_node *prev = rn->rn_p; struct radix_node *next = rn->rn_dupedkey; struct radix_node *t, *tt, *saved_tt, *head; struct rtentry *rt = (struct rtentry *)rn; int mid, oldprio, prioinv = 0; oldprio = rt->rt_priority; rt->rt_priority = newprio; /* same prio, no change needed */ if (oldprio == newprio) return; if (rn_mpath_next(rn, 1) == NULL) { /* no need to move node, route is alone */ if (prev->rn_mask != rn->rn_mask) return; /* ... or route is last and prio gets bigger */ if (oldprio < newprio) return; } /* remove node from dupedkey list and reinsert at correct place */ if (prev->rn_dupedkey == rn) { prev->rn_dupedkey = next; if (next) next->rn_p = prev; else next = prev; } else { if (next == NULL) panic("next == NULL"); next->rn_p = prev; if (prev->rn_l == rn) prev->rn_l = next; else prev->rn_r = next; } /* re-insert rn at the right spot, so first rewind to the head */ for (tt = next; tt->rn_p->rn_dupedkey == tt; tt = tt->rn_p) ; saved_tt = tt; /* * Stolen from radix.c rn_addroute(). * This is nasty code with a certain amount of magic and dragons. * t is the element where the re-priorized rn is inserted -- before * or after depending on prioinv. saved_tt points to the head of the * dupedkey chain and tt is a bit of a helper * * First we skip with tt to the start of the mpath group then we * search the right spot to enter our node. */ for (; tt; tt = tt->rn_dupedkey) if (rn->rn_mask == tt->rn_mask) break; head = tt; /* store current head entry for rn_mklist check */ tt = rn_mpath_prio(tt, newprio); if (((struct rtentry *)tt)->rt_priority != newprio) { if (((struct rtentry *)tt)->rt_priority > newprio) prioinv = 1; t = tt; } else { mid = rn_mpath_count(tt) / 2; do { t = tt; tt = rn_mpath_next(tt, 0); } while (tt && --mid > 0); } /* insert rn before or after t depending on prioinv, tt and saved_tt */ if (tt == saved_tt && prioinv) { /* link in at head of list */ rn->rn_dupedkey = tt; rn->rn_p = tt->rn_p; tt->rn_p = rn; if (rn->rn_p->rn_l == tt) rn->rn_p->rn_l = rn; else rn->rn_p->rn_r = rn; } else if (prioinv == 1) { rn->rn_dupedkey = t; t->rn_p->rn_dupedkey = rn; rn->rn_p = t->rn_p; t->rn_p = rn; } else { rn->rn_dupedkey = t->rn_dupedkey; t->rn_dupedkey = rn; rn->rn_p = t; if (rn->rn_dupedkey) rn->rn_dupedkey->rn_p = rn; } #ifdef RN_DEBUG /* readd at head of creation list */ for (t = rn_clist; t && t->rn_ybro != rn; t = t->rn_ybro) ; if (t) t->rn_ybro = rn->rn_ybro; rn->rn_ybro = rn_clist; rn_clist = rn; #endif if (rn->rn_mklist && rn->rn_flags & RNF_NORMAL) { /* the rn_mklist needs to be fixed if the best route changed */ if (rn->rn_mklist->rm_leaf != rn) { if (rn->rn_mklist->rm_leaf->rn_p == rn) /* changed route is now best */ rn->rn_mklist->rm_leaf = rn; } else { if (rn->rn_dupedkey != head) /* rn moved behind head, so head is new head */ rn->rn_mklist->rm_leaf = head; } } }