Instead of having a separate function to check for MPATH conflicts simply pass the gateway to rtable_insert(). Some regress tests outputs will need an update as we're now doing one more rtalloc(9) call in RTM_ADD.
This is a step towards MPATH by default. ok? Index: net/route.c =================================================================== RCS file: /cvs/src/sys/net/route.c,v retrieving revision 1.262 diff -u -p -r1.262 route.c --- net/route.c 25 Oct 2015 11:58:11 -0000 1.262 +++ net/route.c 25 Oct 2015 14:14:28 -0000 @@ -837,15 +837,6 @@ rtrequest1(int req, struct rt_addrinfo * else memcpy(ndst, info->rti_info[RTAX_DST], dlen); -#ifndef SMALL_KERNEL - /* Do not permit exactly the same dst/mask/gw pair. */ - if (rtable_mpath_conflict(tableid, ndst, - info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY], - prio, info->rti_flags & RTF_MPATH)) { - free(ndst, M_RTABLE, dlen); - return (EEXIST); - } -#endif rt = pool_get(&rtentry_pool, PR_NOWAIT | PR_ZERO); if (rt == NULL) { free(ndst, M_RTABLE, dlen); @@ -953,13 +944,15 @@ rtrequest1(int req, struct rt_addrinfo * } error = rtable_insert(tableid, ndst, - info->rti_info[RTAX_NETMASK], rt->rt_priority, rt); + info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY], + rt->rt_priority, rt); if (error != 0 && (crt = rtalloc(ndst, 0, tableid)) != NULL) { /* overwrite cloned route */ if ((crt->rt_flags & RTF_CLONED) != 0) { rtdeletemsg(crt, tableid); error = rtable_insert(tableid, ndst, info->rti_info[RTAX_NETMASK], + info->rti_info[RTAX_GATEWAY], rt->rt_priority, rt); } rtfree(crt); Index: net/rtable.c =================================================================== RCS file: /cvs/src/sys/net/rtable.c,v retrieving revision 1.14 diff -u -p -r1.14 rtable.c --- net/rtable.c 22 Oct 2015 17:19:38 -0000 1.14 +++ net/rtable.c 25 Oct 2015 14:13:57 -0000 @@ -292,7 +292,8 @@ rtable_match(unsigned int rtableid, stru int rtable_insert(unsigned int rtableid, struct sockaddr *dst, - struct sockaddr *mask, uint8_t prio, struct rtentry *rt) + struct sockaddr *mask, struct sockaddr *gateway, uint8_t prio, + struct rtentry *rt) { struct radix_node_head *rnh; struct radix_node *rn = (struct radix_node *)rt; @@ -301,6 +302,15 @@ rtable_insert(unsigned int rtableid, str if (rnh == NULL) return (EAFNOSUPPORT); +#ifndef SMALL_KERNEL + if (rnh->rnh_multipath) { + /* Do not permit exactly the same dst/mask/gw pair. */ + if (rt_mpath_conflict(rnh, dst, mask, gateway, prio, + ISSET(rt->rt_flags, RTF_MPATH))) + return (EEXIST); + } +#endif + rn = rn_addroute(dst, mask, rnh, rn, prio); if (rn == NULL) return (ESRCH); @@ -382,22 +392,6 @@ rtable_mpath_match(unsigned int rtableid return (rt); } -int -rtable_mpath_conflict(unsigned int rtableid, struct sockaddr *dst, - struct sockaddr *mask, struct sockaddr *gateway, uint8_t prio, int mpathok) -{ - struct radix_node_head *rnh; - - rnh = rtable_get(rtableid, dst->sa_family); - if (rnh == NULL) - return (EAFNOSUPPORT); - - if (rnh->rnh_multipath == 0) - return (0); - - return (rt_mpath_conflict(rnh, dst, mask, gateway, prio, mpathok)); -} - /* Gateway selection by Hash-Threshold (RFC 2992) */ struct rtentry * rtable_mpath_select(struct rtentry *rt, uint32_t hash) @@ -526,7 +520,8 @@ rtable_match(unsigned int rtableid, stru int rtable_insert(unsigned int rtableid, struct sockaddr *dst, - struct sockaddr *mask, uint8_t prio, struct rtentry *rt) + struct sockaddr *mask, struct sockaddr *gateway, uint8_t prio, + struct rtentry *rt) { #ifndef SMALL_KERNEL struct rtentry *mrt; @@ -545,6 +540,29 @@ rtable_insert(unsigned int rtableid, str if (plen == -1) return (EINVAL); +#ifndef SMALL_KERNEL + /* Do not permit exactly the same dst/mask/gw pair. */ + an = art_lookup(ar, addr, plen); + if (an != NULL && an->an_plen == plen && + !memcmp(an->an_dst, dst, dst->sa_len)) { + struct rtentry *mrt; + int mpathok = ISSET(rt->rt_flags, RTF_MPATH); + + LIST_FOREACH(mrt, &an->an_rtlist, rt_next) { + if (prio != RTP_ANY && + (mrt->rt_priority & RTP_MASK) != (prio & RTP_MASK)) + continue; + + if (!mpathok) + return (EEXIST); + + if (mrt->rt_gateway->sa_len == gateway->sa_len && + !memcmp(mrt->rt_gateway, gateway, gateway->sa_len)) + return (EEXIST); + } + } +#endif + an = pool_get(&an_pool, PR_NOWAIT | PR_ZERO); if (an == NULL) return (ENOBUFS); @@ -754,48 +772,6 @@ rtable_mpath_match(unsigned int rtableid rtfree(rt0); return (rt); -} - -int -rtable_mpath_conflict(unsigned int rtableid, struct sockaddr *dst, - struct sockaddr *mask, struct sockaddr *gateway, uint8_t prio, int mpathok) -{ - struct art_root *ar; - struct art_node *an; - struct rtentry *rt; - uint8_t *addr; - int plen; - - ar = rtable_get(rtableid, dst->sa_family); - if (ar == NULL) - return (EAFNOSUPPORT); - - addr = satoaddr(ar, dst); - plen = satoplen(ar, mask); - if (plen == -1) - return (EINVAL); - - an = art_lookup(ar, addr, plen); - /* Make sure we've got a perfect match. */ - if (an == NULL || an->an_plen != plen || - memcmp(an->an_dst, dst, dst->sa_len)) - return (0); - - LIST_FOREACH(rt, &an->an_rtlist, rt_next) { - if (prio != RTP_ANY && - (rt->rt_priority & RTP_MASK) != (prio & RTP_MASK)) - continue; - - if (!mpathok) - return (EEXIST); - - if (rt->rt_gateway->sa_len == gateway->sa_len && - memcmp(rt->rt_gateway, gateway, gateway->sa_len) == 0) - return (EEXIST); - } - - - return (0); } /* Gateway selection by Hash-Threshold (RFC 2992) */ Index: net/rtable.h =================================================================== RCS file: /cvs/src/sys/net/rtable.h,v retrieving revision 1.7 diff -u -p -r1.7 rtable.h --- net/rtable.h 22 Oct 2015 17:19:38 -0000 1.7 +++ net/rtable.h 25 Oct 2015 14:13:46 -0000 @@ -57,7 +57,8 @@ struct rtentry *rtable_lookup(unsigned i struct sockaddr *); struct rtentry *rtable_match(unsigned int, struct sockaddr *); int rtable_insert(unsigned int, struct sockaddr *, - struct sockaddr *, uint8_t, struct rtentry *); + struct sockaddr *, struct sockaddr *, uint8_t, + struct rtentry *); int rtable_delete(unsigned int, struct sockaddr *, struct sockaddr *, uint8_t, struct rtentry *); int rtable_walk(unsigned int, sa_family_t, @@ -66,8 +67,6 @@ int rtable_walk(unsigned int, sa_famil int rtable_mpath_capable(unsigned int, sa_family_t); struct rtentry *rtable_mpath_match(unsigned int, struct rtentry *, struct sockaddr *, uint8_t); -int rtable_mpath_conflict(unsigned int, struct sockaddr *, - struct sockaddr *, struct sockaddr *, uint8_t, int); struct rtentry *rtable_mpath_select(struct rtentry *, uint32_t); void rtable_mpath_reprio(struct rtentry *, uint8_t); #endif /* _NET_RTABLE_H_ */