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:
parent
db61e6ef45
commit
9a1b64d5a0
143
sys/net/route.c
143
sys/net/route.c
@ -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.
|
||||
|
@ -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 *);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user