diff options
author | melifaro <melifaro@FreeBSD.org> | 2015-11-30 05:51:14 +0000 |
---|---|---|
committer | melifaro <melifaro@FreeBSD.org> | 2015-11-30 05:51:14 +0000 |
commit | e1984564835422629dc73f3551f3be70602c551a (patch) | |
tree | 0be60b29fa72a36767c89b7639430ba4896bcf2a /sys/net | |
parent | 48efdd4beccfc6adef1e3c7e8cb51a875a76a814 (diff) | |
download | FreeBSD-src-e1984564835422629dc73f3551f3be70602c551a.zip FreeBSD-src-e1984564835422629dc73f3551f3be70602c551a.tar.gz |
Add new rt_foreach_fib_walk_del() function for deleting route entries
by filter function instead of picking into routing table details in
each consumer.
Remove now-unused rt_expunge() (eliminating last external RTF_RNH_LOCKED
user).
This simplifies future nexthops/mulitipath changes and rtrequest1_fib()
locking refactoring.
Actual changes:
Add "rt_chain" field to permit rte grouping while doing batched delete
from routing table (thus growing rte 200->208 on amd64).
Add "rti_filter" / "rti_filterdata" / "rti_spare" fields to rt_addrinfo
to pass filter function to various routing subsystems in standard way.
Convert all rt_expunge() customers to new rt_addinfo-based api and eliminate
rt_expunge().
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/route.c | 443 | ||||
-rw-r--r-- | sys/net/route.h | 21 |
2 files changed, 259 insertions, 205 deletions
diff --git a/sys/net/route.c b/sys/net/route.c index 94f64c9..041dd92 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -139,7 +139,14 @@ static VNET_DEFINE(uma_zone_t, rtzone); /* Routing table UMA zone. */ static int rtrequest1_fib_change(struct radix_node_head *, struct rt_addrinfo *, struct rtentry **, u_int); static void rt_setmetrics(const struct rt_addrinfo *, struct rtentry *); -static int rt_ifdelroute(struct rtentry *rt, void *arg); +static int rt_ifdelroute(const struct rtentry *rt, void *arg); +static struct rtentry *rt_unlinkrte(struct radix_node_head *rnh, + struct rt_addrinfo *info, int *perror); +static void rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info); +#ifdef RADIX_MPATH +static struct radix_node *rt_mpath_unlink(struct radix_node_head *rnh, + struct rt_addrinfo *info, struct rtentry *rto, int *perror); +#endif struct if_mtuinfo { @@ -237,6 +244,7 @@ rtentry_ctor(void *mem, int size, void *arg, int how) bzero(rt, offsetof(struct rtentry, rt_endzero)); counter_u64_zero(rt->rt_pksent); + rt->rt_chain = NULL; return (0); } @@ -867,6 +875,108 @@ rt_foreach_fib_walk(int af, rt_setwarg_t *setwa_f, rt_walktree_f_t *wa_f, } } +struct rt_delinfo +{ + struct rt_addrinfo info; + struct radix_node_head *rnh; + struct rtentry *head; +}; + +/* + * Conditionally unlinks @rn from radix tree based + * on info data passed in @arg. + */ +static int +rt_checkdelroute(struct radix_node *rn, void *arg) +{ + struct rt_delinfo *di; + struct rt_addrinfo *info; + struct rtentry *rt; + int error; + + di = (struct rt_delinfo *)arg; + rt = (struct rtentry *)rn; + info = &di->info; + error = 0; + + info->rti_info[RTAX_DST] = rt_key(rt); + info->rti_info[RTAX_NETMASK] = rt_mask(rt); + info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; + + rt = rt_unlinkrte(di->rnh, info, &error); + if (rt == NULL) { + /* Either not allowed or not matched. Skip entry */ + return (0); + } + + /* Entry was unlinked. Add to the list and return */ + rt->rt_chain = di->head; + di->head = rt; + + return (0); +} + +/* + * Iterates over all existing fibs in system. + * Deletes each element for which @filter_f function returned + * non-zero value. + * If @af is not AF_UNSPEC, iterates over fibs in particular + * address family. + */ +void +rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg) +{ + struct radix_node_head *rnh; + struct rt_delinfo di; + struct rtentry *rt; + uint32_t fibnum; + int i, start, end; + + bzero(&di, sizeof(di)); + di.info.rti_filter = filter_f; + di.info.rti_filterdata = arg; + + for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { + /* Do we want some specific family? */ + if (af != AF_UNSPEC) { + start = af; + end = af; + } else { + start = 1; + end = AF_MAX; + } + + for (i = start; i <= end; i++) { + rnh = rt_tables_get_rnh(fibnum, i); + if (rnh == NULL) + continue; + di.rnh = rnh; + + RADIX_NODE_HEAD_LOCK(rnh); + rnh->rnh_walktree(rnh, rt_checkdelroute, &di); + RADIX_NODE_HEAD_UNLOCK(rnh); + + if (di.head == NULL) + continue; + + /* We might have something to reclaim */ + while (di.head != NULL) { + rt = di.head; + di.head = rt->rt_chain; + rt->rt_chain = NULL; + + /* TODO std rt -> rt_addrinfo export */ + di.info.rti_info[RTAX_DST] = rt_key(rt); + di.info.rti_info[RTAX_NETMASK] = rt_mask(rt); + + rt_notifydelete(rt, &di.info); + RTFREE_LOCKED(rt); + } + + } + } +} + /* * Delete Routes for a Network Interface * @@ -882,10 +992,9 @@ rt_foreach_fib_walk(int af, rt_setwarg_t *setwa_f, rt_walktree_f_t *wa_f, * errno failed - reason indicated */ static int -rt_ifdelroute(struct rtentry *rt, void *arg) +rt_ifdelroute(const struct rtentry *rt, void *arg) { struct ifnet *ifp = arg; - int err; if (rt->rt_ifp != ifp) return (0); @@ -897,14 +1006,7 @@ rt_ifdelroute(struct rtentry *rt, void *arg) if ((rt->rt_flags & RTF_UP) == 0) return (0); - err = rtrequest_fib(RTM_DELETE, rt_key(rt), rt->rt_gateway, - rt_mask(rt), - rt->rt_flags | RTF_RNH_LOCKED | RTF_PINNED, - (struct rtentry **) NULL, rt->rt_fibnum); - if (err != 0) - log(LOG_WARNING, "rt_ifdelroute: error %d\n", err); - - return (0); + return (1); } /* @@ -917,10 +1019,106 @@ void rt_flushifroutes(struct ifnet *ifp) { - rt_foreach_fib_walk(AF_UNSPEC, NULL, rt_ifdelroute, ifp); + rt_foreach_fib_walk_del(AF_UNSPEC, rt_ifdelroute, ifp); } /* + * Conditionally unlinks rtentry matching data inside @info from @rnh. + * Returns unlinked, locked and referenced @rtentry on success, + * Returns NULL and sets @perror to: + * ESRCH - if prefix was not found, + * EADDRINUSE - if trying to delete PINNED route without appropriate flag. + * ENOENT - if supplied filter function returned 0 (not matched). + */ +static struct rtentry * +rt_unlinkrte(struct radix_node_head *rnh, struct rt_addrinfo *info, int *perror) +{ + struct sockaddr *dst, *netmask; + struct rtentry *rt; + struct radix_node *rn; + + dst = info->rti_info[RTAX_DST]; + netmask = info->rti_info[RTAX_NETMASK]; + + rt = (struct rtentry *)rnh->rnh_lookup(dst, netmask, rnh); + if (rt == NULL) { + *perror = ESRCH; + return (NULL); + } + + if ((info->rti_flags & RTF_PINNED) == 0) { + /* Check if target route can be deleted */ + if (rt->rt_flags & RTF_PINNED) { + *perror = EADDRINUSE; + return (NULL); + } + } + + if (info->rti_filter != NULL) { + if (info->rti_filter(rt, info->rti_filterdata) == 0) { + /* Not matched */ + *perror = ENOENT; + return (NULL); + } + + /* + * Filter function requested rte deletion. + * Ease the caller work by filling in remaining info + * from that particular entry. + */ + info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; + } + + /* + * Remove the item from the tree and return it. + * Complain if it is not there and do no more processing. + */ + *perror = ESRCH; +#ifdef RADIX_MPATH + if (rn_mpath_capable(rnh)) + rn = rt_mpath_unlink(rnh, info, rt, perror); + else +#endif + rn = rnh->rnh_deladdr(dst, netmask, rnh); + if (rn == NULL) + return (NULL); + + if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) + panic ("rtrequest delete"); + + rt = RNTORT(rn); + RT_LOCK(rt); + RT_ADDREF(rt); + + *perror = 0; + + return (rt); +} + +static void +rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info) +{ + struct ifaddr *ifa; + + rt->rt_flags &= ~RTF_UP; + + /* + * give the protocol a chance to keep things in sync. + */ + ifa = rt->rt_ifa; + if (ifa != NULL && ifa->ifa_rtrequest != NULL) + ifa->ifa_rtrequest(RTM_DELETE, rt, info); + + /* + * One more rtentry floating around that is not + * linked to the routing table. rttrash will be decremented + * when RTFREE(rt) is eventually called. + */ + V_rttrash++; +} + + +/* * These (questionable) definitions of apparent local variables apply * to the next two functions. XXXXXX!!! */ @@ -975,87 +1173,6 @@ rt_getifa_fib(struct rt_addrinfo *info, u_int fibnum) return (error); } -/* - * Expunges references to a route that's about to be reclaimed. - * The route must be locked. - */ -int -rt_expunge(struct radix_node_head *rnh, struct rtentry *rt) -{ -#if !defined(RADIX_MPATH) - struct radix_node *rn; -#else - struct rt_addrinfo info; - int fib; - struct rtentry *rt0; -#endif - struct ifaddr *ifa; - int error = 0; - - RT_LOCK_ASSERT(rt); - RADIX_NODE_HEAD_LOCK_ASSERT(rnh); - -#ifdef RADIX_MPATH - fib = rt->rt_fibnum; - bzero(&info, sizeof(info)); - info.rti_ifp = rt->rt_ifp; - info.rti_flags = RTF_RNH_LOCKED; - info.rti_info[RTAX_DST] = rt_key(rt); - info.rti_info[RTAX_GATEWAY] = rt->rt_ifa->ifa_addr; - - RT_UNLOCK(rt); - error = rtrequest1_fib(RTM_DELETE, &info, &rt0, fib); - - if (error == 0 && rt0 != NULL) { - rt = rt0; - RT_LOCK(rt); - } else if (error != 0) { - RT_LOCK(rt); - return (error); - } -#else - /* - * Remove the item from the tree; it should be there, - * but when callers invoke us blindly it may not (sigh). - */ - rn = rnh->rnh_deladdr(rt_key(rt), rt_mask(rt), rnh); - if (rn == NULL) { - error = ESRCH; - goto bad; - } - KASSERT((rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) == 0, - ("unexpected flags 0x%x", rn->rn_flags)); - KASSERT(rt == RNTORT(rn), - ("lookup mismatch, rt %p rn %p", rt, rn)); -#endif /* RADIX_MPATH */ - - rt->rt_flags &= ~RTF_UP; - - /* - * Give the protocol a chance to keep things in sync. - */ - if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) { - struct rt_addrinfo info; - - bzero((caddr_t)&info, sizeof(info)); - info.rti_flags = rt->rt_flags; - info.rti_info[RTAX_DST] = rt_key(rt); - info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; - info.rti_info[RTAX_NETMASK] = rt_mask(rt); - ifa->ifa_rtrequest(RTM_DELETE, rt, &info); - } - - /* - * one more rtentry floating around that is not - * linked to the routing table. - */ - V_rttrash++; -#if !defined(RADIX_MPATH) -bad: -#endif - return (error); -} - static int if_updatemtu_cb(struct radix_node *rn, void *arg) { @@ -1172,26 +1289,32 @@ rt_print(char *buf, int buflen, struct rtentry *rt) #endif #ifdef RADIX_MPATH -static int -rn_mpath_update(int req, struct rt_addrinfo *info, - struct radix_node_head *rnh, struct rtentry **ret_nrt) +/* + * Deletes key for single-path routes, unlinks rtentry with + * gateway specified in @info from multi-path routes. + * + * Returnes unlinked entry. In case of failure, returns NULL + * and sets @perror to ESRCH. + */ +static struct radix_node * +rt_mpath_unlink(struct radix_node_head *rnh, struct rt_addrinfo *info, + struct rtentry *rto, int *perror) { /* * if we got multipath routes, we require users to specify * a matching RTAX_GATEWAY. */ - struct rtentry *rt, *rto = NULL; + struct rtentry *rt; // *rto = NULL; struct radix_node *rn; - int error = 0; + struct sockaddr *gw; - rn = rnh->rnh_lookup(dst, netmask, rnh); - if (rn == NULL) - return (ESRCH); - rto = rt = RNTORT(rn); + gw = info->rti_info[RTAX_GATEWAY]; + rt = rt_mpath_matchgate(rto, gw); + if (rt == NULL) { + *perror = ESRCH; + return (NULL); + } - rt = rt_mpath_matchgate(rt, gateway); - if (rt == NULL) - return (ESRCH); /* * this is the first entry in the chain */ @@ -1214,67 +1337,31 @@ rn_mpath_update(int req, struct rt_addrinfo *info, * check the case when there is only * one route in the chain. */ - if (gateway && - (rt->rt_gateway->sa_len != gateway->sa_len || - memcmp(rt->rt_gateway, gateway, gateway->sa_len))) - error = ESRCH; - else { - /* - * remove from tree before returning it - * to the caller - */ - rn = rnh->rnh_deladdr(dst, netmask, rnh); - KASSERT(rt == RNTORT(rn), ("radix node disappeared")); - goto gwdelete; + if (gw && + (rt->rt_gateway->sa_len != gw->sa_len || + memcmp(rt->rt_gateway, gw, gw->sa_len))) { + *perror = ESRCH; + return (NULL); } - } + /* * use the normal delete code to remove * the first entry */ - if (req != RTM_DELETE) - goto nondelete; - - error = ENOENT; - goto done; + rn = rnh->rnh_deladdr(dst, netmask, rnh); + *perror = 0; + return (rn); } /* * if the entry is 2nd and on up */ - if ((req == RTM_DELETE) && !rt_mpath_deldup(rto, rt)) + if (rt_mpath_deldup(rto, rt) == 0) panic ("rtrequest1: rt_mpath_deldup"); -gwdelete: - RT_LOCK(rt); - RT_ADDREF(rt); - if (req == RTM_DELETE) { - rt->rt_flags &= ~RTF_UP; - /* - * One more rtentry floating around that is not - * linked to the routing table. rttrash will be decremented - * when RTFREE(rt) is eventually called. - */ - V_rttrash++; - } - -nondelete: - if (req != RTM_DELETE) - panic("unrecognized request %d", req); - - - /* - * If the caller wants it, then it can have it, - * but it's up to it to free the rtentry as we won't be - * doing it. - */ - if (ret_nrt) { - *ret_nrt = rt; - RT_UNLOCK(rt); - } else - RTFREE_LOCKED(rt); -done: - return (error); + *perror = 0; + rn = (struct radix_node *)rt; + return (rn); } #endif @@ -1330,52 +1417,12 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, rt_maskedcopy(dst, (struct sockaddr *)&mdst, netmask); dst = (struct sockaddr *)&mdst; } -#ifdef RADIX_MPATH - if (rn_mpath_capable(rnh)) { - error = rn_mpath_update(req, info, rnh, ret_nrt); - /* - * "bad" holds true for the success case - * as well - */ - if (error != ENOENT) - goto bad; - error = 0; - } -#endif - if ((flags & RTF_PINNED) == 0) { - /* Check if target route can be deleted */ - rt = (struct rtentry *)rnh->rnh_lookup(dst, - netmask, rnh); - if ((rt != NULL) && (rt->rt_flags & RTF_PINNED)) - senderr(EADDRINUSE); - } - /* - * Remove the item from the tree and return it. - * Complain if it is not there and do no more processing. - */ - rn = rnh->rnh_deladdr(dst, netmask, rnh); - if (rn == NULL) - senderr(ESRCH); - if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) - panic ("rtrequest delete"); - rt = RNTORT(rn); - RT_LOCK(rt); - RT_ADDREF(rt); - rt->rt_flags &= ~RTF_UP; - - /* - * give the protocol a chance to keep things in sync. - */ - if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) - ifa->ifa_rtrequest(RTM_DELETE, rt, info); + rt = rt_unlinkrte(rnh, info, &error); + if (error != 0) + goto bad; - /* - * One more rtentry floating around that is not - * linked to the routing table. rttrash will be decremented - * when RTFREE(rt) is eventually called. - */ - V_rttrash++; + rt_notifydelete(rt, info); /* * If the caller wants it, then it can have it, diff --git a/sys/net/route.h b/sys/net/route.h index e62aaa6..ffbcb3c 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -128,6 +128,7 @@ struct rtentry { #define rt_endzero rt_pksent counter_u64_t rt_pksent; /* packets sent using this route */ struct mtx rt_mtx; /* mutex for routing entry */ + struct rtentry *rt_chain; /* pointer to next rtentry to delete */ }; #endif /* _KERNEL || _WANT_RTENTRY */ @@ -259,14 +260,19 @@ struct rt_msghdr { #define RTAX_BRD 7 /* for NEWADDR, broadcast or p-p dest addr */ #define RTAX_MAX 8 /* size of array to allocate */ +typedef int rt_filter_f_t(const struct rtentry *, void *); + struct rt_addrinfo { - int rti_addrs; - struct sockaddr *rti_info[RTAX_MAX]; - int rti_flags; - struct ifaddr *rti_ifa; - struct ifnet *rti_ifp; - u_long rti_mflags; - struct rt_metrics *rti_rmx; + int rti_addrs; /* Route RTF_ flags */ + int rti_flags; /* Route RTF_ flags */ + struct sockaddr *rti_info[RTAX_MAX]; /* Sockaddr data */ + struct ifaddr *rti_ifa; /* value of rt_ifa addr */ + struct ifnet *rti_ifp; /* route interface */ + rt_filter_f_t *rti_filter; /* filter function */ + void *rti_filterdata; /* filter paramenters */ + u_long rti_mflags; /* metrics RTV_ flags */ + u_long rti_spare; /* Will be used for fib */ + struct rt_metrics *rti_rmx; /* Pointer to route metrics */ }; /* @@ -383,6 +389,7 @@ void rt_updatemtu(struct ifnet *); typedef int rt_walktree_f_t(struct rtentry *, void *); typedef void rt_setwarg_t(struct radix_node_head *, uint32_t, int, void *); void rt_foreach_fib_walk(int af, rt_setwarg_t *, rt_walktree_f_t *, void *); +void rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg); void rt_flushifroutes(struct ifnet *ifp); /* XXX MRT COMPAT VERSIONS THAT SET UNIVERSE to 0 */ |