Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F107895914
D24870.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
32 KB
Referenced Files
None
Subscribers
None
D24870.diff
View Options
Index: head/sys/net/if_var.h
===================================================================
--- head/sys/net/if_var.h
+++ head/sys/net/if_var.h
@@ -686,8 +686,8 @@
struct ifaddr *ifa_ifwithbroadaddr(const struct sockaddr *, int);
struct ifaddr *ifa_ifwithdstaddr(const struct sockaddr *, int);
struct ifaddr *ifa_ifwithnet(const struct sockaddr *, int, int);
-struct ifaddr *ifa_ifwithroute(int, const struct sockaddr *, struct sockaddr *,
- u_int);
+struct ifaddr *ifa_ifwithroute(int, const struct sockaddr *,
+ const struct sockaddr *, u_int);
struct ifaddr *ifaof_ifpforaddr(const struct sockaddr *, struct ifnet *);
int ifa_preferred(struct ifaddr *, struct ifaddr *);
Index: head/sys/net/route.c
===================================================================
--- head/sys/net/route.c
+++ head/sys/net/route.c
@@ -121,45 +121,17 @@
#define V_rt_tables VNET(rt_tables)
-/*
- * Convert a 'struct radix_node *' to a 'struct rtentry *'.
- * The operation can be done safely (in this code) because a
- * 'struct rtentry' starts with two 'struct radix_node''s, the first
- * one representing leaf nodes in the routing tree, which is
- * what the code in radix.c passes us as a 'struct radix_node'.
- *
- * But because there are a lot of assumptions in this conversion,
- * do not cast explicitly, but always use the macro below.
- */
-#define RNTORT(p) ((struct rtentry *)(p))
-
-VNET_DEFINE_STATIC(uma_zone_t, rtzone); /* Routing table UMA zone. */
+VNET_DEFINE(uma_zone_t, rtzone); /* Routing table UMA zone. */
#define V_rtzone VNET(rtzone)
EVENTHANDLER_LIST_DEFINE(rt_addrmsg);
-static int rt_getifa_fib(struct rt_addrinfo *, u_int);
-static void rt_setmetrics(const struct rt_addrinfo *, struct rtentry *);
static int rt_ifdelroute(const struct rtentry *rt, const struct nhop_object *,
void *arg);
-static struct rtentry *rt_unlinkrte(struct rib_head *rnh,
- struct rt_addrinfo *info, int *perror);
-static void rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info);
static void destroy_rtentry_epoch(epoch_context_t ctx);
-#ifdef RADIX_MPATH
-static struct radix_node *rt_mpath_unlink(struct rib_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);
-static int add_route(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt);
-static int del_route(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt);
-static int change_route(struct rib_head *, struct rt_addrinfo *,
- struct rtentry **);
-
/*
* handler for net.my_fibnum
*/
@@ -572,8 +544,8 @@
}
struct ifaddr *
-ifa_ifwithroute(int flags, const struct sockaddr *dst, struct sockaddr *gateway,
- u_int fibnum)
+ifa_ifwithroute(int flags, const struct sockaddr *dst,
+ const struct sockaddr *gateway, u_int fibnum)
{
struct ifaddr *ifa;
@@ -839,99 +811,7 @@
}
}
-struct rt_delinfo
-{
- struct rt_addrinfo info;
- struct rib_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_nhop->gw_sa;
-
- 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 a routing table specified by @fibnum and @family and
- * deletes elements marked by @filter_f.
- * @fibnum: rtable id
- * @family: AF_ address family
- * @filter_f: function returning non-zero value for items to delete
- * @arg: data to pass to the @filter_f function
- * @report: true if rtsock notification is needed.
- */
-void
-rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, bool report)
-{
- struct rib_head *rnh;
- struct rt_delinfo di;
- struct rtentry *rt;
-
- rnh = rt_tables_get_rnh(fibnum, family);
- if (rnh == NULL)
- return;
-
- bzero(&di, sizeof(di));
- di.info.rti_filter = filter_f;
- di.info.rti_filterdata = arg;
- di.rnh = rnh;
-
- RIB_WLOCK(rnh);
- rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di);
- RIB_WUNLOCK(rnh);
-
- if (di.head == NULL)
- return;
-
- /* 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);
-
- if (report)
- rt_routemsg(RTM_DELETE, rt, rt->rt_nhop->nh_ifp, 0,
- fibnum);
- rtfree(rt);
- }
-}
-
-/*
* Iterates over all existing fibs in system and deletes each element
* for which @filter_f function returns non-zero value.
* If @family is not AF_UNSPEC, iterates over fibs in particular
@@ -1018,104 +898,6 @@
}
/*
- * 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 rib_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->head);
- 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, rt->rt_nhop, 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_nhop->gw_sa;
- }
-
- /*
- * 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 (rt_mpath_capable(rnh))
- rn = rt_mpath_unlink(rnh, info, rt, perror);
- else
-#endif
- rn = rnh->rnh_deladdr(dst, netmask, &rnh->head);
- if (rn == NULL)
- return (NULL);
-
- if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
- panic ("rtrequest delete");
-
- rt = RNTORT(rn);
- RT_LOCK(rt);
- rt->rt_flags &= ~RTF_UP;
-
- *perror = 0;
-
- return (rt);
-}
-
-static void
-rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info)
-{
- struct ifaddr *ifa;
-
- /*
- * give the protocol a chance to keep things in sync.
- */
- ifa = rt->rt_nhop->nh_ifa;
- if (ifa != NULL && ifa->ifa_rtrequest != NULL)
- ifa->ifa_rtrequest(RTM_DELETE, rt, rt->rt_nhop, info);
-}
-
-
-/*
- * These (questionable) definitions of apparent local variables apply
- * to the next two functions. XXXXXX!!!
- */
-#define dst info->rti_info[RTAX_DST]
-#define gateway info->rti_info[RTAX_GATEWAY]
-#define netmask info->rti_info[RTAX_NETMASK]
-#define ifaaddr info->rti_info[RTAX_IFA]
-#define ifpaddr info->rti_info[RTAX_IFP]
-#define flags info->rti_flags
-
-/*
* Look up rt_addrinfo for a specific fib. Note that if rti_ifa is defined,
* it will be referenced so the caller must free it.
*
@@ -1125,9 +907,16 @@
int
rt_getifa_fib(struct rt_addrinfo *info, u_int fibnum)
{
+ const struct sockaddr *dst, *gateway, *ifpaddr, *ifaaddr;
struct epoch_tracker et;
- int needref, error;
+ int needref, error, flags;
+ dst = info->rti_info[RTAX_DST];
+ gateway = info->rti_info[RTAX_GATEWAY];
+ ifpaddr = info->rti_info[RTAX_IFP];
+ ifaaddr = info->rti_info[RTAX_IFA];
+ flags = info->rti_flags;
+
/*
* ifp may be specified by sockaddr_dl
* when protocol address is ambiguous.
@@ -1150,7 +939,7 @@
if (info->rti_ifa == NULL && ifaaddr != NULL)
info->rti_ifa = ifa_ifwithaddr(ifaaddr);
if (info->rti_ifa == NULL) {
- struct sockaddr *sa;
+ const struct sockaddr *sa;
/*
* Most common use case for the userland-supplied routes.
@@ -1282,7 +1071,7 @@
* Returnes unlinked entry. In case of failure, returns NULL
* and sets @perror to ESRCH.
*/
-static struct radix_node *
+struct radix_node *
rt_mpath_unlink(struct rib_head *rnh, struct rt_addrinfo *info,
struct rtentry *rto, int *perror)
{
@@ -1335,7 +1124,9 @@
* use the normal delete code to remove
* the first entry
*/
- rn = rnh->rnh_deladdr(dst, netmask, &rnh->head);
+ rn = rnh->rnh_deladdr(info->rti_info[RTAX_DST],
+ info->rti_info[RTAX_NETMASK],
+ &rnh->head);
*perror = 0;
return (rn);
}
@@ -1351,13 +1142,6 @@
}
#endif
-#undef dst
-#undef gateway
-#undef netmask
-#undef ifaaddr
-#undef ifpaddr
-#undef flags
-
int
rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt,
u_int fibnum)
@@ -1369,7 +1153,7 @@
KASSERT((fibnum < rt_numfibs), ("rtrequest1_fib: bad fibnum"));
KASSERT((info->rti_flags & RTF_RNH_LOCKED) == 0, ("rtrequest1_fib: locked"));
NET_EPOCH_ASSERT();
-
+
dst = info->rti_info[RTAX_DST];
switch (dst->sa_family) {
@@ -1420,341 +1204,7 @@
return (error);
}
-static int
-add_route(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt)
-{
- struct sockaddr *dst, *ndst, *gateway, *netmask;
- struct rtentry *rt, *rt_old;
- struct nhop_object *nh;
- struct radix_node *rn;
- struct ifaddr *ifa;
- int error, flags;
- struct epoch_tracker et;
-
- dst = info->rti_info[RTAX_DST];
- gateway = info->rti_info[RTAX_GATEWAY];
- netmask = info->rti_info[RTAX_NETMASK];
- flags = info->rti_flags;
-
- if ((flags & RTF_GATEWAY) && !gateway)
- return (EINVAL);
- if (dst && gateway && (dst->sa_family != gateway->sa_family) &&
- (gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != AF_LINK))
- return (EINVAL);
-
- if (dst->sa_len > sizeof(((struct rtentry *)NULL)->rt_dstb))
- return (EINVAL);
-
- if (info->rti_ifa == NULL) {
- error = rt_getifa_fib(info, rnh->rib_fibnum);
- if (error)
- return (error);
- } else {
- ifa_ref(info->rti_ifa);
- }
-
- NET_EPOCH_ENTER(et);
- error = nhop_create_from_info(rnh, info, &nh);
- NET_EPOCH_EXIT(et);
- if (error != 0) {
- ifa_free(info->rti_ifa);
- return (error);
- }
-
- rt = uma_zalloc(V_rtzone, M_NOWAIT);
- if (rt == NULL) {
- ifa_free(info->rti_ifa);
- nhop_free(nh);
- return (ENOBUFS);
- }
- rt->rt_flags = RTF_UP | flags;
- rt->rt_nhop = nh;
-
- /* Fill in dst */
- memcpy(&rt->rt_dst, dst, dst->sa_len);
- rt_key(rt) = &rt->rt_dst;
-
- /*
- * point to the (possibly newly malloc'd) dest address.
- */
- ndst = (struct sockaddr *)rt_key(rt);
-
- /*
- * make sure it contains the value we want (masked if needed).
- */
- if (netmask) {
- rt_maskedcopy(dst, ndst, netmask);
- } else
- bcopy(dst, ndst, dst->sa_len);
-
- /*
- * We use the ifa reference returned by rt_getifa_fib().
- * This moved from below so that rnh->rnh_addaddr() can
- * examine the ifa and ifa->ifa_ifp if it so desires.
- */
- ifa = info->rti_ifa;
- rt->rt_weight = 1;
-
- rt_setmetrics(info, rt);
-
- RIB_WLOCK(rnh);
- RT_LOCK(rt);
-#ifdef RADIX_MPATH
- /* do not permit exactly the same dst/mask/gw pair */
- if (rt_mpath_capable(rnh) &&
- rt_mpath_conflict(rnh, rt, netmask)) {
- RIB_WUNLOCK(rnh);
-
- nhop_free(nh);
- uma_zfree(V_rtzone, rt);
- return (EEXIST);
- }
-#endif
-
- rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes);
-
- if (rn != NULL && rt->rt_expire > 0)
- tmproutes_update(rnh, rt);
-
- rt_old = NULL;
- if (rn == NULL && (info->rti_flags & RTF_PINNED) != 0) {
-
- /*
- * Force removal and re-try addition
- * TODO: better multipath&pinned support
- */
- struct sockaddr *info_dst = info->rti_info[RTAX_DST];
- info->rti_info[RTAX_DST] = ndst;
- /* Do not delete existing PINNED(interface) routes */
- info->rti_flags &= ~RTF_PINNED;
- rt_old = rt_unlinkrte(rnh, info, &error);
- info->rti_flags |= RTF_PINNED;
- info->rti_info[RTAX_DST] = info_dst;
- if (rt_old != NULL)
- rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head,
- rt->rt_nodes);
- }
- RIB_WUNLOCK(rnh);
-
- if (rt_old != NULL) {
- rt_notifydelete(rt_old, info);
- rtfree(rt_old);
- }
-
- /*
- * If it still failed to go into the tree,
- * then un-make it (this should be a function)
- */
- if (rn == NULL) {
- nhop_free(nh);
- uma_zfree(V_rtzone, rt);
- return (EEXIST);
- }
-
- /*
- * If this protocol has something to add to this then
- * allow it to do that as well.
- */
- if (ifa->ifa_rtrequest)
- ifa->ifa_rtrequest(RTM_ADD, rt, rt->rt_nhop, info);
-
- /*
- * actually return a resultant rtentry
- */
- if (ret_nrt)
- *ret_nrt = rt;
- rnh->rnh_gen++; /* Routing table updated */
- RT_UNLOCK(rt);
-
- return (0);
-}
-
-static int
-del_route(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt)
-{
- struct sockaddr *dst, *netmask;
- struct sockaddr_storage mdst;
- struct rtentry *rt;
- int error;
-
- dst = info->rti_info[RTAX_DST];
- netmask = info->rti_info[RTAX_NETMASK];
-
- if (netmask) {
- if (dst->sa_len > sizeof(mdst))
- return (EINVAL);
- rt_maskedcopy(dst, (struct sockaddr *)&mdst, netmask);
- dst = (struct sockaddr *)&mdst;
- }
-
- RIB_WLOCK(rnh);
- rt = rt_unlinkrte(rnh, info, &error);
- RIB_WUNLOCK(rnh);
- if (error != 0)
- return (error);
-
- rt_notifydelete(rt, info);
-
- /*
- * If the caller wants it, then it can have it,
- * the entry will be deleted after the end of the current epoch.
- */
- if (ret_nrt)
- *ret_nrt = rt;
-
- rtfree(rt);
-
- return (0);
-}
-
-static int
-change_route_one(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt)
-{
- RIB_RLOCK_TRACKER;
- struct rtentry *rt = NULL;
- int error = 0;
- int free_ifa = 0;
- struct nhop_object *nh, *nh_orig;
-
- RIB_RLOCK(rnh);
- rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST],
- info->rti_info[RTAX_NETMASK], &rnh->head);
-
- if (rt == NULL) {
- RIB_RUNLOCK(rnh);
- return (ESRCH);
- }
-
-#ifdef RADIX_MPATH
- /*
- * If we got multipath routes,
- * we require users to specify a matching RTAX_GATEWAY.
- */
- if (rt_mpath_capable(rnh)) {
- rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]);
- if (rt == NULL) {
- RIB_RUNLOCK(rnh);
- return (ESRCH);
- }
- }
-#endif
- nh_orig = rt->rt_nhop;
-
- RIB_RUNLOCK(rnh);
-
- rt = NULL;
- nh = NULL;
-
- /*
- * New gateway could require new ifaddr, ifp;
- * flags may also be different; ifp may be specified
- * by ll sockaddr when protocol address is ambiguous
- */
- if (((nh_orig->nh_flags & NHF_GATEWAY) &&
- info->rti_info[RTAX_GATEWAY] != NULL) ||
- info->rti_info[RTAX_IFP] != NULL ||
- (info->rti_info[RTAX_IFA] != NULL &&
- !sa_equal(info->rti_info[RTAX_IFA], nh_orig->nh_ifa->ifa_addr))) {
- error = rt_getifa_fib(info, rnh->rib_fibnum);
- if (info->rti_ifa != NULL)
- free_ifa = 1;
-
- if (error != 0) {
- if (free_ifa) {
- ifa_free(info->rti_ifa);
- info->rti_ifa = NULL;
- }
-
- return (error);
- }
- }
-
- error = nhop_create_from_nhop(rnh, nh_orig, info, &nh);
- if (free_ifa) {
- ifa_free(info->rti_ifa);
- info->rti_ifa = NULL;
- }
- if (error != 0)
- return (error);
-
- RIB_WLOCK(rnh);
-
- /* Lookup rtentry once again and check if nexthop is still the same */
- rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST],
- info->rti_info[RTAX_NETMASK], &rnh->head);
-
- if (rt == NULL) {
- RIB_WUNLOCK(rnh);
- nhop_free(nh);
- return (ESRCH);
- }
-
- if (rt->rt_nhop != nh_orig) {
- RIB_WUNLOCK(rnh);
- nhop_free(nh);
- return (EAGAIN);
- }
-
- /* Proceed with the update */
- RT_LOCK(rt);
-
- /* Provide notification to the protocols.*/
- if ((nh_orig->nh_ifa != nh->nh_ifa) && nh_orig->nh_ifa->ifa_rtrequest)
- nh_orig->nh_ifa->ifa_rtrequest(RTM_DELETE, rt, nh_orig, info);
-
- rt->rt_nhop = nh;
- rt_setmetrics(info, rt);
-
- if ((nh_orig->nh_ifa != nh->nh_ifa) && nh_orig->nh_ifa->ifa_rtrequest)
- nh_orig->nh_ifa->ifa_rtrequest(RTM_DELETE, rt, nh_orig, info);
-
- if (ret_nrt != NULL)
- *ret_nrt = rt;
-
- RT_UNLOCK(rt);
-
- /* Update generation id to reflect rtable change */
- rnh->rnh_gen++;
-
- RIB_WUNLOCK(rnh);
-
- nhop_free(nh_orig);
-
- return (0);
-}
-
-static int
-change_route(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt)
-{
- int error;
-
- /* Check if updated gateway exists */
- if ((info->rti_flags & RTF_GATEWAY) &&
- (info->rti_info[RTAX_GATEWAY] == NULL))
- return (EINVAL);
-
- /*
- * route change is done in multiple steps, with dropping and
- * reacquiring lock. In the situations with multiple processes
- * changes the same route in can lead to the case when route
- * is changed between the steps. Address it by retrying the operation
- * multiple times before failing.
- */
- for (int i = 0; i < RIB_MAX_RETRIES; i++) {
- error = change_route_one(rnh, info, ret_nrt);
- if (error != EAGAIN)
- break;
- }
-
- return (error);
-}
-
-
-static void
+void
rt_setmetrics(const struct rt_addrinfo *info, struct rtentry *rt)
{
Index: head/sys/net/route/route_ctl.c
===================================================================
--- head/sys/net/route/route_ctl.c
+++ head/sys/net/route/route_ctl.c
@@ -29,6 +29,7 @@
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
+#include "opt_mpath.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -53,6 +54,10 @@
#include <net/route/shared.h>
#include <netinet/in.h>
+#ifdef RADIX_MPATH
+#include <net/radix_mpath.h>
+#endif
+
#include <vm/uma.h>
@@ -61,5 +66,519 @@
*
* All functions assumes they are called in net epoch.
*/
+
+static void rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info);
+
+int
+add_route(struct rib_head *rnh, struct rt_addrinfo *info,
+ struct rtentry **ret_nrt)
+{
+ struct sockaddr *dst, *ndst, *gateway, *netmask;
+ struct rtentry *rt, *rt_old;
+ struct nhop_object *nh;
+ struct radix_node *rn;
+ struct ifaddr *ifa;
+ int error, flags;
+ struct epoch_tracker et;
+
+ dst = info->rti_info[RTAX_DST];
+ gateway = info->rti_info[RTAX_GATEWAY];
+ netmask = info->rti_info[RTAX_NETMASK];
+ flags = info->rti_flags;
+
+ if ((flags & RTF_GATEWAY) && !gateway)
+ return (EINVAL);
+ if (dst && gateway && (dst->sa_family != gateway->sa_family) &&
+ (gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != AF_LINK))
+ return (EINVAL);
+
+ if (dst->sa_len > sizeof(((struct rtentry *)NULL)->rt_dstb))
+ return (EINVAL);
+
+ if (info->rti_ifa == NULL) {
+ error = rt_getifa_fib(info, rnh->rib_fibnum);
+ if (error)
+ return (error);
+ } else {
+ ifa_ref(info->rti_ifa);
+ }
+
+ NET_EPOCH_ENTER(et);
+ error = nhop_create_from_info(rnh, info, &nh);
+ NET_EPOCH_EXIT(et);
+ if (error != 0) {
+ ifa_free(info->rti_ifa);
+ return (error);
+ }
+
+ rt = uma_zalloc(V_rtzone, M_NOWAIT);
+ if (rt == NULL) {
+ ifa_free(info->rti_ifa);
+ nhop_free(nh);
+ return (ENOBUFS);
+ }
+ rt->rt_flags = RTF_UP | flags;
+ rt->rt_nhop = nh;
+
+ /* Fill in dst */
+ memcpy(&rt->rt_dst, dst, dst->sa_len);
+ rt_key(rt) = &rt->rt_dst;
+
+ /*
+ * point to the (possibly newly malloc'd) dest address.
+ */
+ ndst = (struct sockaddr *)rt_key(rt);
+
+ /*
+ * make sure it contains the value we want (masked if needed).
+ */
+ if (netmask) {
+ rt_maskedcopy(dst, ndst, netmask);
+ } else
+ bcopy(dst, ndst, dst->sa_len);
+
+ /*
+ * We use the ifa reference returned by rt_getifa_fib().
+ * This moved from below so that rnh->rnh_addaddr() can
+ * examine the ifa and ifa->ifa_ifp if it so desires.
+ */
+ ifa = info->rti_ifa;
+ rt->rt_weight = 1;
+
+ rt_setmetrics(info, rt);
+
+ RIB_WLOCK(rnh);
+ RT_LOCK(rt);
+#ifdef RADIX_MPATH
+ /* do not permit exactly the same dst/mask/gw pair */
+ if (rt_mpath_capable(rnh) &&
+ rt_mpath_conflict(rnh, rt, netmask)) {
+ RIB_WUNLOCK(rnh);
+
+ nhop_free(nh);
+ uma_zfree(V_rtzone, rt);
+ return (EEXIST);
+ }
+#endif
+
+ rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes);
+
+ if (rn != NULL && rt->rt_expire > 0)
+ tmproutes_update(rnh, rt);
+
+ rt_old = NULL;
+ if (rn == NULL && (info->rti_flags & RTF_PINNED) != 0) {
+
+ /*
+ * Force removal and re-try addition
+ * TODO: better multipath&pinned support
+ */
+ struct sockaddr *info_dst = info->rti_info[RTAX_DST];
+ info->rti_info[RTAX_DST] = ndst;
+ /* Do not delete existing PINNED(interface) routes */
+ info->rti_flags &= ~RTF_PINNED;
+ rt_old = rt_unlinkrte(rnh, info, &error);
+ info->rti_flags |= RTF_PINNED;
+ info->rti_info[RTAX_DST] = info_dst;
+ if (rt_old != NULL)
+ rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head,
+ rt->rt_nodes);
+ }
+ RIB_WUNLOCK(rnh);
+
+ if (rt_old != NULL) {
+ rt_notifydelete(rt_old, info);
+ rtfree(rt_old);
+ }
+
+ /*
+ * If it still failed to go into the tree,
+ * then un-make it (this should be a function)
+ */
+ if (rn == NULL) {
+ nhop_free(nh);
+ uma_zfree(V_rtzone, rt);
+ return (EEXIST);
+ }
+
+ /*
+ * If this protocol has something to add to this then
+ * allow it to do that as well.
+ */
+ if (ifa->ifa_rtrequest)
+ ifa->ifa_rtrequest(RTM_ADD, rt, rt->rt_nhop, info);
+
+ /*
+ * actually return a resultant rtentry
+ */
+ if (ret_nrt)
+ *ret_nrt = rt;
+ rnh->rnh_gen++; /* Routing table updated */
+ RT_UNLOCK(rt);
+
+ return (0);
+}
+
+
+/*
+ * 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).
+ */
+struct rtentry *
+rt_unlinkrte(struct rib_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->head);
+ 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, rt->rt_nhop, 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_nhop->gw_sa;
+ }
+
+ /*
+ * 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 (rt_mpath_capable(rnh))
+ rn = rt_mpath_unlink(rnh, info, rt, perror);
+ else
+#endif
+ rn = rnh->rnh_deladdr(dst, netmask, &rnh->head);
+ if (rn == NULL)
+ return (NULL);
+
+ if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
+ panic ("rtrequest delete");
+
+ rt = RNTORT(rn);
+ RT_LOCK(rt);
+ rt->rt_flags &= ~RTF_UP;
+
+ *perror = 0;
+
+ return (rt);
+}
+
+int
+del_route(struct rib_head *rnh, struct rt_addrinfo *info,
+ struct rtentry **ret_nrt)
+{
+ struct sockaddr *dst, *netmask;
+ struct sockaddr_storage mdst;
+ struct rtentry *rt;
+ int error;
+
+ dst = info->rti_info[RTAX_DST];
+ netmask = info->rti_info[RTAX_NETMASK];
+
+ if (netmask) {
+ if (dst->sa_len > sizeof(mdst))
+ return (EINVAL);
+ rt_maskedcopy(dst, (struct sockaddr *)&mdst, netmask);
+ dst = (struct sockaddr *)&mdst;
+ }
+
+ RIB_WLOCK(rnh);
+ rt = rt_unlinkrte(rnh, info, &error);
+ RIB_WUNLOCK(rnh);
+ if (error != 0)
+ return (error);
+
+ rt_notifydelete(rt, info);
+
+ /*
+ * If the caller wants it, then it can have it,
+ * the entry will be deleted after the end of the current epoch.
+ */
+ if (ret_nrt)
+ *ret_nrt = rt;
+
+ rtfree(rt);
+
+ return (0);
+}
+
+static int
+change_route_one(struct rib_head *rnh, struct rt_addrinfo *info,
+ struct rtentry **ret_nrt)
+{
+ RIB_RLOCK_TRACKER;
+ struct rtentry *rt = NULL;
+ int error = 0;
+ int free_ifa = 0;
+ struct nhop_object *nh, *nh_orig;
+
+ RIB_RLOCK(rnh);
+ rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST],
+ info->rti_info[RTAX_NETMASK], &rnh->head);
+
+ if (rt == NULL) {
+ RIB_RUNLOCK(rnh);
+ return (ESRCH);
+ }
+
+#ifdef RADIX_MPATH
+ /*
+ * If we got multipath routes,
+ * we require users to specify a matching RTAX_GATEWAY.
+ */
+ if (rt_mpath_capable(rnh)) {
+ rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]);
+ if (rt == NULL) {
+ RIB_RUNLOCK(rnh);
+ return (ESRCH);
+ }
+ }
+#endif
+ nh_orig = rt->rt_nhop;
+
+ RIB_RUNLOCK(rnh);
+
+ rt = NULL;
+ nh = NULL;
+
+ /*
+ * New gateway could require new ifaddr, ifp;
+ * flags may also be different; ifp may be specified
+ * by ll sockaddr when protocol address is ambiguous
+ */
+ if (((nh_orig->nh_flags & NHF_GATEWAY) &&
+ info->rti_info[RTAX_GATEWAY] != NULL) ||
+ info->rti_info[RTAX_IFP] != NULL ||
+ (info->rti_info[RTAX_IFA] != NULL &&
+ !sa_equal(info->rti_info[RTAX_IFA], nh_orig->nh_ifa->ifa_addr))) {
+ error = rt_getifa_fib(info, rnh->rib_fibnum);
+ if (info->rti_ifa != NULL)
+ free_ifa = 1;
+
+ if (error != 0) {
+ if (free_ifa) {
+ ifa_free(info->rti_ifa);
+ info->rti_ifa = NULL;
+ }
+
+ return (error);
+ }
+ }
+
+ error = nhop_create_from_nhop(rnh, nh_orig, info, &nh);
+ if (free_ifa) {
+ ifa_free(info->rti_ifa);
+ info->rti_ifa = NULL;
+ }
+ if (error != 0)
+ return (error);
+
+ RIB_WLOCK(rnh);
+
+ /* Lookup rtentry once again and check if nexthop is still the same */
+ rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST],
+ info->rti_info[RTAX_NETMASK], &rnh->head);
+
+ if (rt == NULL) {
+ RIB_WUNLOCK(rnh);
+ nhop_free(nh);
+ return (ESRCH);
+ }
+
+ if (rt->rt_nhop != nh_orig) {
+ RIB_WUNLOCK(rnh);
+ nhop_free(nh);
+ return (EAGAIN);
+ }
+
+ /* Proceed with the update */
+ RT_LOCK(rt);
+
+ /* Provide notification to the protocols.*/
+ if ((nh_orig->nh_ifa != nh->nh_ifa) && nh_orig->nh_ifa->ifa_rtrequest)
+ nh_orig->nh_ifa->ifa_rtrequest(RTM_DELETE, rt, nh_orig, info);
+
+ rt->rt_nhop = nh;
+ rt_setmetrics(info, rt);
+
+ if ((nh_orig->nh_ifa != nh->nh_ifa) && nh_orig->nh_ifa->ifa_rtrequest)
+ nh_orig->nh_ifa->ifa_rtrequest(RTM_DELETE, rt, nh_orig, info);
+
+ if (ret_nrt != NULL)
+ *ret_nrt = rt;
+
+ RT_UNLOCK(rt);
+
+ /* Update generation id to reflect rtable change */
+ rnh->rnh_gen++;
+
+ RIB_WUNLOCK(rnh);
+
+ nhop_free(nh_orig);
+
+ return (0);
+}
+
+int
+change_route(struct rib_head *rnh, struct rt_addrinfo *info,
+ struct rtentry **ret_nrt)
+{
+ int error;
+
+ /* Check if updated gateway exists */
+ if ((info->rti_flags & RTF_GATEWAY) &&
+ (info->rti_info[RTAX_GATEWAY] == NULL))
+ return (EINVAL);
+
+ /*
+ * route change is done in multiple steps, with dropping and
+ * reacquiring lock. In the situations with multiple processes
+ * changes the same route in can lead to the case when route
+ * is changed between the steps. Address it by retrying the operation
+ * multiple times before failing.
+ */
+ for (int i = 0; i < RIB_MAX_RETRIES; i++) {
+ error = change_route_one(rnh, info, ret_nrt);
+ if (error != EAGAIN)
+ break;
+ }
+
+ return (error);
+}
+
+static void
+rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info)
+{
+ struct ifaddr *ifa;
+
+ /*
+ * give the protocol a chance to keep things in sync.
+ */
+ ifa = rt->rt_nhop->nh_ifa;
+ if (ifa != NULL && ifa->ifa_rtrequest != NULL)
+ ifa->ifa_rtrequest(RTM_DELETE, rt, rt->rt_nhop, info);
+}
+
+struct rt_delinfo
+{
+ struct rt_addrinfo info;
+ struct rib_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_nhop->gw_sa;
+
+ 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 a routing table specified by @fibnum and @family and
+ * deletes elements marked by @filter_f.
+ * @fibnum: rtable id
+ * @family: AF_ address family
+ * @filter_f: function returning non-zero value for items to delete
+ * @arg: data to pass to the @filter_f function
+ * @report: true if rtsock notification is needed.
+ */
+void
+rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, bool report)
+{
+ struct rib_head *rnh;
+ struct rt_delinfo di;
+ struct rtentry *rt;
+
+ rnh = rt_tables_get_rnh(fibnum, family);
+ if (rnh == NULL)
+ return;
+
+ bzero(&di, sizeof(di));
+ di.info.rti_filter = filter_f;
+ di.info.rti_filterdata = arg;
+ di.rnh = rnh;
+
+ RIB_WLOCK(rnh);
+ rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di);
+ RIB_WUNLOCK(rnh);
+
+ if (di.head == NULL)
+ return;
+
+ /* 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);
+
+ if (report)
+ rt_routemsg(RTM_DELETE, rt, rt->rt_nhop->nh_ifp, 0,
+ fibnum);
+ rtfree(rt);
+ }
+}
Index: head/sys/net/route/route_var.h
===================================================================
--- head/sys/net/route/route_var.h
+++ head/sys/net/route/route_var.h
@@ -104,11 +104,36 @@
struct rib_head *rt_tables_get_rnh(int fib, int family);
void rt_mpath_init_rnh(struct rib_head *rnh);
+int rt_getifa_fib(struct rt_addrinfo *info, u_int fibnum);
+void rt_setmetrics(const struct rt_addrinfo *info, struct rtentry *rt);
+#ifdef RADIX_MPATH
+struct radix_node *rt_mpath_unlink(struct rib_head *rnh,
+ struct rt_addrinfo *info, struct rtentry *rto, int *perror);
+#endif
+int add_route(struct rib_head *rnh, struct rt_addrinfo *info,
+ struct rtentry **ret_nrt);
+int del_route(struct rib_head *rnh, struct rt_addrinfo *info,
+ struct rtentry **ret_nrt);
+int change_route(struct rib_head *, struct rt_addrinfo *,
+ struct rtentry **);
VNET_PCPUSTAT_DECLARE(struct rtstat, rtstat);
#define RTSTAT_ADD(name, val) \
VNET_PCPUSTAT_ADD(struct rtstat, rtstat, name, (val))
#define RTSTAT_INC(name) RTSTAT_ADD(name, 1)
+
+
+/*
+ * Convert a 'struct radix_node *' to a 'struct rtentry *'.
+ * The operation can be done safely (in this code) because a
+ * 'struct rtentry' starts with two 'struct radix_node''s, the first
+ * one representing leaf nodes in the routing tree, which is
+ * what the code in radix.c passes us as a 'struct radix_node'.
+ *
+ * But because there are a lot of assumptions in this conversion,
+ * do not cast explicitly, but always use the macro below.
+ */
+#define RNTORT(p) ((struct rtentry *)(p))
struct rtentry {
struct radix_node rt_nodes[2]; /* tree glue, and other values */
Index: head/sys/net/route/shared.h
===================================================================
--- head/sys/net/route/shared.h
+++ head/sys/net/route/shared.h
@@ -37,6 +37,8 @@
#ifndef _NET_ROUTE_SHARED_H_
#define _NET_ROUTE_SHARED_H_
+#include <vm/uma.h>
+
#ifdef RTDEBUG
#define DPRINTF(_fmt, ...) printf("%s: " _fmt "\n", __func__ , ## __VA_ARGS__)
#else
@@ -63,6 +65,13 @@
void nhops_update_ifmtu(struct rib_head *rh, struct ifnet *ifp, uint32_t mtu);
int nhops_dump_sysctl(struct rib_head *rh, struct sysctl_req *w);
+
+/* route */
+VNET_DECLARE(uma_zone_t, rtzone); /* Routing table UMA zone. */
+#define V_rtzone VNET(rtzone)
+
+struct rtentry *rt_unlinkrte(struct rib_head *rnh, struct rt_addrinfo *info,
+ int *perror);
#endif
Index: head/sys/netinet/in_fib.c
===================================================================
--- head/sys/netinet/in_fib.c
+++ head/sys/netinet/in_fib.c
@@ -71,7 +71,6 @@
static void fib4_rte_to_nh_extended(struct nhop_object *nh, struct in_addr dst,
uint32_t flags, struct nhop4_extended *pnh4);
-#define RNTORT(p) ((struct rtentry *)(p))
static void
fib4_rte_to_nh_basic(struct nhop_object *nh, struct in_addr dst,
Index: head/sys/netinet6/in6_fib.c
===================================================================
--- head/sys/netinet6/in6_fib.c
+++ head/sys/netinet6/in6_fib.c
@@ -74,7 +74,6 @@
const struct in6_addr *dst, uint32_t flags, struct nhop6_extended *pnh6);
static void fib6_rte_to_nh_basic(const struct nhop_object *nh, const struct in6_addr *dst,
uint32_t flags, struct nhop6_basic *pnh6);
-#define RNTORT(p) ((struct rtentry *)(p))
#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa))
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Jan 20, 3:14 AM (20 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15972323
Default Alt Text
D24870.diff (32 KB)
Attached To
Mode
D24870: Move <add|del|change>_route to route_ctl.c
Attached
Detach File
Event Timeline
Log In to Comment