Add rib_lookup_info() to provide API for retrieving individual route

entries data in unified format.

There are control plane functions that require information other than
  just next-hop data (e.g. individual rtentry fields like flags or
  prefix/mask). Given that the goal is to avoid rte reference/refcounting,
  re-use rt_addrinfo structure to store most rte fields. If caller wants
  to retrieve key/mask or gateway (which are sockaddrs and are allocated
  separately), it needs to provide sufficient-sized sockaddrs structures
  w/ ther pointers saved in passed rt_addrinfo.

Convert:
  * lltable new records checks (in_lltable_rtcheck(),
    nd6_is_new_addr_neighbor().
  * rtsock pre-add/change route check.
  * IPv6 NS ND-proxy check (RADIX_MPATH code was eliminated because
     1) we don't support RTF_ANNOUNCE ND-proxy for networks and there should
       not be multiple host routes for such hosts 2) if we have multiple
       routes we should inspect them (which is not done). 3) the entire idea
       of abusing KRT as storage for ND proxy seems odd. Userland programs
       should be used for that purpose).
This commit is contained in:
Alexander V. Chernikov 2016-01-04 15:03:20 +00:00
parent db61e6ef45
commit 9a1b64d5a0
6 changed files with 237 additions and 66 deletions

View File

@ -147,6 +147,8 @@ static void rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info);
static struct radix_node *rt_mpath_unlink(struct radix_node_head *rnh,
struct rt_addrinfo *info, struct rtentry *rto, int *perror);
#endif
static int rt_exportinfo(struct rtentry *rt, struct rt_addrinfo *info,
int flags);
struct if_mtuinfo
{
@ -831,6 +833,147 @@ rtrequest_fib(int req,
}
/*
* Copy most of @rt data into @info.
*
* If @flags contains NHR_COPY, copies dst,netmask and gw to the
* pointers specified by @info structure. Assume such pointers
* are zeroed sockaddr-like structures with sa_len field initialized
* to reflect size of the provided buffer. if no NHR_COPY is specified,
* point dst,netmask and gw @info fields to appropriate @rt values.
*
* if @flags contains NHR_REF, do refcouting on rt_ifp.
*
* Returns 0 on success.
*/
int
rt_exportinfo(struct rtentry *rt, struct rt_addrinfo *info, int flags)
{
struct rt_metrics *rmx;
struct sockaddr *src, *dst;
int sa_len;
if (flags & NHR_COPY) {
/* Copy destination if dst is non-zero */
src = rt_key(rt);
dst = info->rti_info[RTAX_DST];
sa_len = src->sa_len;
if (src != NULL && dst != NULL) {
if (src->sa_len > dst->sa_len)
return (ENOMEM);
memcpy(dst, src, src->sa_len);
info->rti_addrs |= RTA_DST;
}
/* Copy mask if set && dst is non-zero */
src = rt_mask(rt);
dst = info->rti_info[RTAX_NETMASK];
if (src != NULL && dst != NULL) {
/*
* Radix stores different value in sa_len,
* assume rt_mask() to have the same length
* as rt_key()
*/
if (sa_len > dst->sa_len)
return (ENOMEM);
memcpy(dst, src, src->sa_len);
info->rti_addrs |= RTA_NETMASK;
}
/* Copy gateway is set && dst is non-zero */
src = rt->rt_gateway;
dst = info->rti_info[RTAX_GATEWAY];
if ((rt->rt_flags & RTF_GATEWAY) && src != NULL && dst != NULL){
if (src->sa_len > dst->sa_len)
return (ENOMEM);
memcpy(dst, src, src->sa_len);
info->rti_addrs |= RTA_GATEWAY;
}
} else {
info->rti_info[RTAX_DST] = rt_key(rt);
info->rti_addrs |= RTA_DST;
if (rt_mask(rt) != NULL) {
info->rti_info[RTAX_NETMASK] = rt_mask(rt);
info->rti_addrs |= RTA_NETMASK;
}
if (rt->rt_flags & RTF_GATEWAY) {
info->rti_info[RTAX_GATEWAY] = rt->rt_gateway;
info->rti_addrs |= RTA_GATEWAY;
}
}
rmx = info->rti_rmx;
if (rmx != NULL) {
info->rti_mflags |= RTV_MTU;
rmx->rmx_mtu = rt->rt_mtu;
}
info->rti_flags = rt->rt_flags;
info->rti_ifp = rt->rt_ifp;
info->rti_ifa = rt->rt_ifa;
if (flags & NHR_REF) {
/* Do 'traditional' refcouting */
if_ref(info->rti_ifp);
}
return (0);
}
/*
* Lookups up route entry for @dst in RIB database for fib @fibnum.
* Exports entry data to @info using rt_exportinfo().
*
* if @flags contains NHR_REF, refcouting is performed on rt_ifp.
* All references can be released later by calling rib_free_info()
*
* Returns 0 on success.
* Returns ENOENT for lookup failure, ENOMEM for export failure.
*/
int
rib_lookup_info(uint32_t fibnum, const struct sockaddr *dst, uint32_t flags,
uint32_t flowid, struct rt_addrinfo *info)
{
struct radix_node_head *rh;
struct radix_node *rn;
struct rtentry *rt;
int error;
KASSERT((fibnum < rt_numfibs), ("rib_lookup_rte: bad fibnum"));
rh = rt_tables_get_rnh(fibnum, dst->sa_family);
if (rh == NULL)
return (ENOENT);
RADIX_NODE_HEAD_RLOCK(rh);
rn = rh->rnh_matchaddr(__DECONST(void *, dst), rh);
if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
rt = RNTORT(rn);
/* Ensure route & ifp is UP */
if (RT_LINK_IS_UP(rt->rt_ifp)) {
flags = (flags & NHR_REF) | NHR_COPY;
error = rt_exportinfo(rt, info, flags);
RADIX_NODE_HEAD_RUNLOCK(rh);
return (error);
}
}
RADIX_NODE_HEAD_RUNLOCK(rh);
return (ENOENT);
}
/*
* Releases all references acquired by rib_lookup_info() when
* called with NHR_REF flags.
*/
void
rib_free_info(struct rt_addrinfo *info)
{
if_rele(info->rti_ifp);
}
/*
* Iterates over all existing fibs in system calling
* @setwa_f function prior to traversing each fib.

View File

@ -197,6 +197,9 @@ struct rtentry {
#define NHR_IFAIF 0x01 /* Return ifa_ifp interface */
#define NHR_REF 0x02 /* For future use */
/* Control plane route request flags */
#define NHR_COPY 0x100 /* Copy rte data */
/* rte<>nhop translation */
static inline uint16_t
fib_rte_to_nh_flags(int rt_flags)
@ -460,6 +463,9 @@ void rtredirect_fib(struct sockaddr *, struct sockaddr *,
int rtrequest_fib(int, struct sockaddr *,
struct sockaddr *, struct sockaddr *, int, struct rtentry **, u_int);
int rtrequest1_fib(int, struct rt_addrinfo *, struct rtentry **, u_int);
int rib_lookup_info(uint32_t, const struct sockaddr *, uint32_t, uint32_t,
struct rt_addrinfo *);
void rib_free_info(struct rt_addrinfo *info);
#include <sys/eventhandler.h>
typedef void (*rtevent_redirect_fn)(void *, struct rtentry *, struct rtentry *, struct sockaddr *);

View File

@ -614,11 +614,16 @@ route_output(struct mbuf *m, struct socket *so, ...)
*/
if (info.rti_info[RTAX_GATEWAY] != NULL &&
info.rti_info[RTAX_GATEWAY]->sa_family != AF_LINK) {
struct route gw_ro;
struct rt_addrinfo ginfo;
struct sockaddr *gdst;
bzero(&ginfo, sizeof(ginfo));
bzero(&ss, sizeof(ss));
ss.ss_len = sizeof(ss);
ginfo.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&ss;
gdst = info.rti_info[RTAX_GATEWAY];
bzero(&gw_ro, sizeof(gw_ro));
gw_ro.ro_dst = *info.rti_info[RTAX_GATEWAY];
rtalloc_ign_fib(&gw_ro, 0, fibnum);
/*
* A host route through the loopback interface is
* installed for each interface adddress. In pre 8.0
@ -629,14 +634,14 @@ route_output(struct mbuf *m, struct socket *so, ...)
* AF_LINK sa_family type of the rt_gateway, and the
* rt_ifp has the IFF_LOOPBACK flag set.
*/
if (gw_ro.ro_rt != NULL &&
gw_ro.ro_rt->rt_gateway->sa_family == AF_LINK &&
gw_ro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) {
info.rti_flags &= ~RTF_GATEWAY;
info.rti_flags |= RTF_GWFLAG_COMPAT;
if (rib_lookup_info(fibnum, gdst, NHR_REF, 0, &ginfo) == 0) {
if (ss.ss_family == AF_LINK &&
ginfo.rti_ifp->if_flags & IFF_LOOPBACK) {
info.rti_flags &= ~RTF_GATEWAY;
info.rti_flags |= RTF_GWFLAG_COMPAT;
}
rib_free_info(&ginfo);
}
if (gw_ro.ro_rt != NULL)
RTFREE(gw_ro.ro_rt);
}
switch (rtm->rtm_type) {

View File

@ -1106,34 +1106,48 @@ in_lltable_free_entry(struct lltable *llt, struct llentry *lle)
static int
in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr)
{
struct rtentry *rt;
struct rt_addrinfo info;
struct sockaddr_in rt_key, rt_mask;
struct sockaddr rt_gateway;
int rt_flags;
KASSERT(l3addr->sa_family == AF_INET,
("sin_family %d", l3addr->sa_family));
/* XXX rtalloc1_fib should take a const param */
rt = rtalloc1_fib(__DECONST(struct sockaddr *, l3addr), 0, 0,
ifp->if_fib);
bzero(&rt_key, sizeof(rt_key));
rt_key.sin_len = sizeof(rt_key);
bzero(&rt_mask, sizeof(rt_mask));
rt_mask.sin_len = sizeof(rt_mask);
bzero(&rt_gateway, sizeof(rt_gateway));
rt_gateway.sa_len = sizeof(rt_gateway);
if (rt == NULL)
bzero(&info, sizeof(info));
info.rti_info[RTAX_DST] = (struct sockaddr *)&rt_key;
info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&rt_mask;
info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&rt_gateway;
if (rib_lookup_info(ifp->if_fib, l3addr, NHR_REF, 0, &info) != 0)
return (EINVAL);
rt_flags = info.rti_flags;
/*
* If the gateway for an existing host route matches the target L3
* address, which is a special route inserted by some implementation
* such as MANET, and the interface is of the correct type, then
* allow for ARP to proceed.
*/
if (rt->rt_flags & RTF_GATEWAY) {
if (!(rt->rt_flags & RTF_HOST) || !rt->rt_ifp ||
rt->rt_ifp->if_type != IFT_ETHER ||
(rt->rt_ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) != 0 ||
memcmp(rt->rt_gateway->sa_data, l3addr->sa_data,
if (rt_flags & RTF_GATEWAY) {
if (!(rt_flags & RTF_HOST) || !info.rti_ifp ||
info.rti_ifp->if_type != IFT_ETHER ||
(info.rti_ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) != 0 ||
memcmp(rt_gateway.sa_data, l3addr->sa_data,
sizeof(in_addr_t)) != 0) {
RTFREE_LOCKED(rt);
rib_free_info(&info);
return (EINVAL);
}
}
rib_free_info(&info);
/*
* Make sure that at least the destination address is covered
@ -1142,21 +1156,19 @@ in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr
* on one interface and the corresponding outgoing packet leaves
* another interface.
*/
if (!(rt->rt_flags & RTF_HOST) && rt->rt_ifp != ifp) {
if (!(rt_flags & RTF_HOST) && info.rti_ifp != ifp) {
const char *sa, *mask, *addr, *lim;
int len;
mask = (const char *)rt_mask(rt);
mask = (const char *)&rt_mask;
/*
* Just being extra cautious to avoid some custom
* code getting into trouble.
*/
if (mask == NULL) {
RTFREE_LOCKED(rt);
if ((info.rti_addrs & RTA_NETMASK) == 0)
return (EINVAL);
}
sa = (const char *)rt_key(rt);
sa = (const char *)&rt_key;
addr = (const char *)l3addr;
len = ((const struct sockaddr_in *)l3addr)->sin_len;
lim = addr + len;
@ -1167,13 +1179,11 @@ in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr
log(LOG_INFO, "IPv4 address: \"%s\" is not on the network\n",
inet_ntoa(((const struct sockaddr_in *)l3addr)->sin_addr));
#endif
RTFREE_LOCKED(rt);
return (EINVAL);
}
}
}
RTFREE_LOCKED(rt);
return (0);
}

View File

@ -1210,6 +1210,10 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
{
struct nd_prefix *pr;
struct ifaddr *dstaddr;
struct rt_addrinfo info;
struct sockaddr_in6 rt_key;
struct sockaddr *dst6;
int fibnum;
/*
* A link-local address is always a neighbor.
@ -1234,6 +1238,13 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
return (0);
}
bzero(&rt_key, sizeof(rt_key));
bzero(&info, sizeof(info));
info.rti_info[RTAX_DST] = (struct sockaddr *)&rt_key;
/* Always use the default FIB here. XXME - why? */
fibnum = RT_DEFAULT_FIB;
/*
* If the address matches one of our addresses,
* it should be a neighbor.
@ -1245,12 +1256,13 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
continue;
if (!(pr->ndpr_stateflags & NDPRF_ONLINK)) {
struct rtentry *rt;
/* Always use the default FIB here. */
rt = in6_rtalloc1((struct sockaddr *)&pr->ndpr_prefix,
0, 0, RT_DEFAULT_FIB);
if (rt == NULL)
dst6 = (struct sockaddr *)&pr->ndpr_prefix;
/* Restore length field before retrying lookup */
rt_key.sin6_len = sizeof(rt_key);
if (rib_lookup_info(fibnum, dst6, 0, 0, &info) != 0)
continue;
/*
* This is the case where multiple interfaces
@ -1263,11 +1275,8 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
* differ.
*/
if (!IN6_ARE_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr,
&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr)) {
RTFREE_LOCKED(rt);
&rt_key.sin6_addr))
continue;
}
RTFREE_LOCKED(rt);
}
if (IN6_ARE_MASKED_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr,

View File

@ -248,37 +248,35 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len)
/* (2) check. */
if (ifa == NULL) {
struct route_in6 ro;
int need_proxy;
struct sockaddr_dl rt_gateway;
struct rt_addrinfo info;
struct sockaddr_in6 dst6;
bzero(&ro, sizeof(ro));
ro.ro_dst.sin6_len = sizeof(struct sockaddr_in6);
ro.ro_dst.sin6_family = AF_INET6;
ro.ro_dst.sin6_addr = taddr6;
bzero(&dst6, sizeof(dst6));
dst6.sin6_len = sizeof(struct sockaddr_in6);
dst6.sin6_family = AF_INET6;
dst6.sin6_addr = taddr6;
bzero(&rt_gateway, sizeof(rt_gateway));
rt_gateway.sdl_len = sizeof(rt_gateway);
bzero(&info, sizeof(info));
info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&rt_gateway;
/* Always use the default FIB. */
#ifdef RADIX_MPATH
rtalloc_mpath_fib((struct route *)&ro, ntohl(taddr6.s6_addr32[3]),
RT_DEFAULT_FIB);
#else
in6_rtalloc(&ro, RT_DEFAULT_FIB);
#endif
need_proxy = (ro.ro_rt &&
(ro.ro_rt->rt_flags & RTF_ANNOUNCE) != 0 &&
ro.ro_rt->rt_gateway->sa_family == AF_LINK);
if (ro.ro_rt != NULL) {
if (need_proxy)
proxydl = *SDL(ro.ro_rt->rt_gateway);
RTFREE(ro.ro_rt);
}
if (need_proxy) {
/*
* proxy NDP for single entry
*/
ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
if (ifa)
proxy = 1;
if (rib_lookup_info(RT_DEFAULT_FIB, (struct sockaddr *)&dst6,
0, 0, &info) == 0) {
if ((info.rti_flags & RTF_ANNOUNCE) != 0 &&
rt_gateway.sdl_family == AF_LINK) {
/*
* proxy NDP for single entry
*/
proxydl = *SDL(&rt_gateway);
ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(
ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
if (ifa)
proxy = 1;
}
}
}
if (ifa == NULL) {