/* Gateway selection by Hash-Threshold (RFC 2992) */ struct rtentry * rn_mpath_select(struct rtentry *rt, uint32_t *srcaddrp) { struct radix_node *rn; int hash, npaths, threshold; rn = (struct radix_node *)rt; npaths = rn_mpath_active_count(rn); hash = rn_mpath_hash(rt_key(rt), srcaddrp) & 0xffff; threshold = 1 + (0xffff / npaths); while (hash > threshold && rn) { /* stay within the multipath routes */ rn = rn_mpath_next(rn, RMP_MODE_ACTIVE); hash -= threshold; } /* if gw selection fails, use the first match (default) */ if (rn != NULL) { rtfree(rt); rt = (struct rtentry *)rn; rt->rt_refcnt++; } return (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 }