summaryrefslogtreecommitdiffstats
path: root/sys/net
diff options
context:
space:
mode:
authormelifaro <melifaro@FreeBSD.org>2016-01-04 15:03:20 +0000
committermelifaro <melifaro@FreeBSD.org>2016-01-04 15:03:20 +0000
commit31d78f6810b70b03dcf590bdcd1f8520d199099d (patch)
tree97043b0fe5b60ef63af4661bdd61f9c954d3bf5b /sys/net
parentff9e83788d7ed52342dcba4dff1e62fdf3cc985c (diff)
downloadFreeBSD-src-31d78f6810b70b03dcf590bdcd1f8520d199099d.zip
FreeBSD-src-31d78f6810b70b03dcf590bdcd1f8520d199099d.tar.gz
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).
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/route.c143
-rw-r--r--sys/net/route.h6
-rw-r--r--sys/net/rtsock.c27
3 files changed, 165 insertions, 11 deletions
diff --git a/sys/net/route.c b/sys/net/route.c
index 86f99ee..11a5b8d 100644
--- a/sys/net/route.c
+++ b/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
{
@@ -832,6 +834,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.
* Calls @wa_f function for each element in current fib.
diff --git a/sys/net/route.h b/sys/net/route.h
index 847722c..7c69e1c 100644
--- a/sys/net/route.h
+++ b/sys/net/route.h
@@ -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 *);
diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c
index 6fcbbc6..a5ca9fa 100644
--- a/sys/net/rtsock.c
+++ b/sys/net/rtsock.c
@@ -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) {
OpenPOWER on IntegriCloud