aboutsummaryrefslogtreecommitdiff
path: root/sys/net
diff options
context:
space:
mode:
authorAlexander V. Chernikov <melifaro@FreeBSD.org>2020-06-01 20:49:42 +0000
committerAlexander V. Chernikov <melifaro@FreeBSD.org>2020-06-01 20:49:42 +0000
commitda187ddb3d23836e0e079b6f7172a778a29a5732 (patch)
treef923fdedd2b62c22a4919626eca4890fd0fd5a64 /sys/net
parente7403d0230f21b9edcc3f64c877a1d619049483e (diff)
downloadsrc-da187ddb3d23836e0e079b6f7172a778a29a5732.tar.gz
src-da187ddb3d23836e0e079b6f7172a778a29a5732.zip
* Add rib_<add|del|change>_route() functions to manipulate the routing table.
The main driver for the change is the need to improve notification mechanism. Currently callers guess the operation data based on the rtentry structure returned in case of successful operation result. There are two problems with this appoach. First is that it doesn't provide enough information for the upcoming multipath changes, where rtentry refers to a new nexthop group, and there is no way of guessing which paths were added during the change. Second is that some rtentry fields can change during notification and protecting from it by requiring customers to unlock rtentry is not desired. Additionally, as the consumers such as rtsock do know which operation they request in advance, making explicit add/change/del versions of the functions makes sense, especially given the functions don't share a lot of code. With that in mind, introduce rib_cmd_info notification structure and rib_<add|del|change>_route() functions, with mandatory rib_cmd_info pointer. It will be used in upcoming generalized notifications. * Move definitions of the new functions and some other functions/structures used for the routing table manipulation to a separate header file, net/route/route_ctl.h. net/route.h is a frequently used file included in ~140 places in kernel, and 90% of the users don't need these definitions. Reviewed by: ae Differential Revision: https://reviews.freebsd.org/D25067
Notes
Notes: svn path=/head/; revision=361706
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/if_llatbl.c1
-rw-r--r--sys/net/route.c15
-rw-r--r--sys/net/route.h10
-rw-r--r--sys/net/route/nhop_ctl.c1
-rw-r--r--sys/net/route/route_ctl.c220
-rw-r--r--sys/net/route/route_ddb.c1
-rw-r--r--sys/net/route/route_helpers.c1
-rw-r--r--sys/net/route/route_temporal.c2
-rw-r--r--sys/net/route/route_var.h9
9 files changed, 223 insertions, 37 deletions
diff --git a/sys/net/if_llatbl.c b/sys/net/if_llatbl.c
index 1652e76fe955..2bfa5debc380 100644
--- a/sys/net/if_llatbl.c
+++ b/sys/net/if_llatbl.c
@@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
#include <net/if_dl.h>
#include <net/if_var.h>
#include <net/route.h>
+#include <net/route/route_ctl.h>
#include <net/vnet.h>
#include <netinet/if_ether.h>
#include <netinet6/in6_var.h>
diff --git a/sys/net/route.c b/sys/net/route.c
index 55360fb600d9..6c42e8f6eda5 100644
--- a/sys/net/route.c
+++ b/sys/net/route.c
@@ -61,6 +61,7 @@
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/route.h>
+#include <net/route/route_ctl.h>
#include <net/route/route_var.h>
#include <net/route/nhop.h>
#include <net/route/shared.h>
@@ -346,6 +347,9 @@ rt_table_init(int offset, int family, u_int fibnum)
nhops_init_rib(rh);
+ /* Init subscription system */
+ CK_STAILQ_INIT(&rh->rnh_subscribers);
+
/* Finally, set base callbacks */
rh->rnh_addaddr = rn_addroute;
rh->rnh_deladdr = rn_delete;
@@ -1148,6 +1152,7 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt,
{
const struct sockaddr *dst;
struct rib_head *rnh;
+ struct rib_cmd_info rc;
int error;
KASSERT((fibnum < rt_numfibs), ("rtrequest1_fib: bad fibnum"));
@@ -1180,10 +1185,11 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt,
if (info->rti_flags & RTF_HOST)
info->rti_info[RTAX_NETMASK] = NULL;
+ bzero(&rc, sizeof(struct rib_cmd_info));
error = 0;
switch (req) {
case RTM_DELETE:
- error = del_route(rnh, info, ret_nrt);
+ error = del_route(rnh, info, &rc);
break;
case RTM_RESOLVE:
/*
@@ -1192,15 +1198,18 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt,
*/
break;
case RTM_ADD:
- error = add_route(rnh, info, ret_nrt);
+ error = add_route(rnh, info, &rc);
break;
case RTM_CHANGE:
- error = change_route(rnh, info, ret_nrt);
+ error = change_route(rnh, info, &rc);
break;
default:
error = EOPNOTSUPP;
}
+ if (ret_nrt != NULL)
+ *ret_nrt = rc.rc_rt;
+
return (error);
}
diff --git a/sys/net/route.h b/sys/net/route.h
index 355a84407f9b..fad32d1a5ee1 100644
--- a/sys/net/route.h
+++ b/sys/net/route.h
@@ -399,12 +399,6 @@ void rtfree(struct rtentry *);
void rtfree_func(struct rtentry *);
void rt_updatemtu(struct ifnet *);
-typedef int rt_walktree_f_t(struct rtentry *, void *);
-typedef void rt_setwarg_t(struct rib_head *, uint32_t, int, void *);
-void rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f,
- void *arg, bool report);
-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_af(struct ifnet *, int);
void rt_flushifroutes(struct ifnet *ifp);
@@ -423,12 +417,8 @@ 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);
-int rib_add_redirect(u_int fibnum, struct sockaddr *dst,
- struct sockaddr *gateway, struct sockaddr *author, struct ifnet *ifp,
- int flags, int expire_sec);
/* New API */
-void rib_walk(int af, u_int fibnum, rt_walktree_f_t *wa_f, void *arg);
struct nhop_object *rib_lookup(uint32_t fibnum, const struct sockaddr *dst,
uint32_t flags, uint32_t flowid);
#endif
diff --git a/sys/net/route/nhop_ctl.c b/sys/net/route/nhop_ctl.c
index 464d4cb05eab..2d74faa212ce 100644
--- a/sys/net/route/nhop_ctl.c
+++ b/sys/net/route/nhop_ctl.c
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/route.h>
+#include <net/route/route_ctl.h>
#include <net/route/route_var.h>
#include <net/route/nhop_utils.h>
#include <net/route/nhop.h>
diff --git a/sys/net/route/route_ctl.c b/sys/net/route/route_ctl.c
index 961a7347258e..d7a2d1bea9ea 100644
--- a/sys/net/route/route_ctl.c
+++ b/sys/net/route/route_ctl.c
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <net/if_dl.h>
#include <net/vnet.h>
#include <net/route.h>
+#include <net/route/route_ctl.h>
#include <net/route/route_var.h>
#include <net/route/nhop_utils.h>
#include <net/route/nhop.h>
@@ -67,11 +68,61 @@ __FBSDID("$FreeBSD$");
* All functions assumes they are called in net epoch.
*/
+static void rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
+ struct rib_cmd_info *rc);
+
static void rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info);
+static struct rib_head *
+get_rnh(uint32_t fibnum, const struct rt_addrinfo *info)
+{
+ struct rib_head *rnh;
+ struct sockaddr *dst;
+
+ KASSERT((fibnum < rt_numfibs), ("rib_add_route: bad fibnum"));
+
+ dst = info->rti_info[RTAX_DST];
+ rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
+
+ return (rnh);
+}
+
+/*
+ * Adds route defined by @info into the kernel table specified by @fibnum and
+ * sa_family in @info->rti_info[RTAX_DST].
+ *
+ * Returns 0 on success and fills in operation metadata into @rc.
+ */
+int
+rib_add_route(uint32_t fibnum, struct rt_addrinfo *info,
+ struct rib_cmd_info *rc)
+{
+ struct rib_head *rnh;
+
+ NET_EPOCH_ASSERT();
+
+ rnh = get_rnh(fibnum, info);
+ if (rnh == NULL)
+ return (EAFNOSUPPORT);
+
+ /*
+ * Check consistency between RTF_HOST flag and netmask
+ * existence.
+ */
+ if (info->rti_flags & RTF_HOST)
+ info->rti_info[RTAX_NETMASK] = NULL;
+ else if (info->rti_info[RTAX_NETMASK] == NULL)
+ return (EINVAL);
+
+ bzero(rc, sizeof(struct rib_cmd_info));
+ rc->rc_cmd = RTM_ADD;
+
+ return (add_route(rnh, info, rc));
+}
+
int
add_route(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt)
+ struct rib_cmd_info *rc)
{
struct sockaddr *dst, *ndst, *gateway, *netmask;
struct rtentry *rt, *rt_old;
@@ -146,6 +197,7 @@ add_route(struct rib_head *rnh, struct rt_addrinfo *info,
rt->rt_weight = 1;
rt_setmetrics(info, rt);
+ rt_old = NULL;
RIB_WLOCK(rnh);
RT_LOCK(rt);
@@ -163,11 +215,19 @@ add_route(struct rib_head *rnh, struct rt_addrinfo *info,
rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes);
- if (rn != NULL && rt->rt_expire > 0)
- tmproutes_update(rnh, rt);
+ if (rn != NULL) {
+ /* Most common usecase */
+ if (rt->rt_expire > 0)
+ tmproutes_update(rnh, rt);
- rt_old = NULL;
- if (rn == NULL && (info->rti_flags & RTF_PINNED) != 0) {
+ /* Finalize notification */
+ rnh->rnh_gen++;
+
+ rc->rc_rt = RNTORT(rn);
+ rc->rc_nh_new = nh;
+
+ rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
+ } else if ((info->rti_flags & RTF_PINNED) != 0) {
/*
* Force removal and re-try addition
@@ -180,9 +240,26 @@ add_route(struct rib_head *rnh, struct rt_addrinfo *info,
rt_old = rt_unlinkrte(rnh, info, &error);
info->rti_flags |= RTF_PINNED;
info->rti_info[RTAX_DST] = info_dst;
- if (rt_old != NULL)
+ if (rt_old != NULL) {
rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head,
rt->rt_nodes);
+
+ /* Finalize notification */
+ rnh->rnh_gen++;
+
+ if (rn != NULL) {
+ rc->rc_cmd = RTM_CHANGE;
+ rc->rc_rt = RNTORT(rn);
+ rc->rc_nh_old = rt_old->rt_nhop;
+ rc->rc_nh_new = nh;
+ } else {
+ rc->rc_cmd = RTM_DELETE;
+ rc->rc_rt = RNTORT(rn);
+ rc->rc_nh_old = rt_old->rt_nhop;
+ rc->rc_nh_new = nh;
+ }
+ rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
+ }
}
RIB_WUNLOCK(rnh);
@@ -208,12 +285,6 @@ add_route(struct rib_head *rnh, struct rt_addrinfo *info,
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);
@@ -221,6 +292,29 @@ add_route(struct rib_head *rnh, struct rt_addrinfo *info,
/*
+ * Removes route defined by @info from the kernel table specified by @fibnum and
+ * sa_family in @info->rti_info[RTAX_DST].
+ *
+ * Returns 0 on success and fills in operation metadata into @rc.
+ */
+int
+rib_del_route(uint32_t fibnum, struct rt_addrinfo *info, struct rib_cmd_info *rc)
+{
+ struct rib_head *rnh;
+
+ NET_EPOCH_ASSERT();
+
+ rnh = get_rnh(fibnum, info);
+ if (rnh == NULL)
+ return (EAFNOSUPPORT);
+
+ bzero(rc, sizeof(struct rib_cmd_info));
+ rc->rc_cmd = RTM_DELETE;
+
+ return (del_route(rnh, info, rc));
+}
+
+/*
* Conditionally unlinks rtentry matching data inside @info from @rnh.
* Returns unlinked, locked and referenced @rtentry on success,
* Returns NULL and sets @perror to:
@@ -295,7 +389,7 @@ rt_unlinkrte(struct rib_head *rnh, struct rt_addrinfo *info, int *perror)
int
del_route(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt)
+ struct rib_cmd_info *rc)
{
struct sockaddr *dst, *netmask;
struct sockaddr_storage mdst;
@@ -314,6 +408,13 @@ del_route(struct rib_head *rnh, struct rt_addrinfo *info,
RIB_WLOCK(rnh);
rt = rt_unlinkrte(rnh, info, &error);
+ if (rt != NULL) {
+ /* Finalize notification */
+ rnh->rnh_gen++;
+ rc->rc_rt = rt;
+ rc->rc_nh_old = rt->rt_nhop;
+ rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
+ }
RIB_WUNLOCK(rnh);
if (error != 0)
return (error);
@@ -324,17 +425,32 @@ del_route(struct rib_head *rnh, struct rt_addrinfo *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);
}
+int
+rib_change_route(uint32_t fibnum, struct rt_addrinfo *info,
+ struct rib_cmd_info *rc)
+{
+ struct rib_head *rnh;
+
+ NET_EPOCH_ASSERT();
+
+ rnh = get_rnh(fibnum, info);
+ if (rnh == NULL)
+ return (EAFNOSUPPORT);
+
+ bzero(rc, sizeof(struct rib_cmd_info));
+ rc->rc_cmd = RTM_CHANGE;
+
+ return (change_route(rnh, info, rc));
+}
+
static int
change_route_one(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt)
+ struct rib_cmd_info *rc)
{
RIB_RLOCK_TRACKER;
struct rtentry *rt = NULL;
@@ -434,14 +550,18 @@ change_route_one(struct rib_head *rnh, struct rt_addrinfo *info,
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;
+ /* Finalize notification */
+ rc->rc_rt = rt;
+ rc->rc_nh_old = nh_orig;
+ rc->rc_nh_new = rt->rt_nhop;
RT_UNLOCK(rt);
/* Update generation id to reflect rtable change */
rnh->rnh_gen++;
+ rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
+
RIB_WUNLOCK(rnh);
nhop_free(nh_orig);
@@ -451,7 +571,7 @@ change_route_one(struct rib_head *rnh, struct rt_addrinfo *info,
int
change_route(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt)
+ struct rib_cmd_info *rc)
{
int error;
@@ -468,7 +588,7 @@ change_route(struct rib_head *rnh, struct rt_addrinfo *info,
* multiple times before failing.
*/
for (int i = 0; i < RIB_MAX_RETRIES; i++) {
- error = change_route_one(rnh, info, ret_nrt);
+ error = change_route_one(rnh, info, rc);
if (error != EAGAIN)
break;
}
@@ -581,4 +701,62 @@ rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, bool
}
}
+static void
+rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
+ struct rib_cmd_info *rc)
+{
+ struct rib_subscription *rs;
+
+ CK_STAILQ_FOREACH(rs, &rnh->rnh_subscribers, next) {
+ if (rs->type == type)
+ rs->func(rnh, rc, rs->arg);
+ }
+}
+
+struct rib_subscription *
+rib_subscribe(uint32_t fibnum, int family, rib_subscription_cb_t *f, void *arg,
+ enum rib_subscription_type type, int waitok)
+{
+ struct rib_head *rnh;
+ struct rib_subscription *rs;
+ int flags = M_ZERO | (waitok ? M_WAITOK : 0);
+
+ NET_EPOCH_ASSERT();
+ KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__));
+ rnh = rt_tables_get_rnh(fibnum, family);
+
+ rs = malloc(sizeof(struct rib_subscription), M_RTABLE, flags);
+ if (rs == NULL)
+ return (NULL);
+
+ rs->func = f;
+ rs->arg = arg;
+ rs->type = type;
+
+ RIB_WLOCK(rnh);
+ CK_STAILQ_INSERT_TAIL(&rnh->rnh_subscribers, rs, next);
+ RIB_WUNLOCK(rnh);
+
+ return (rs);
+}
+
+int
+rib_unsibscribe(uint32_t fibnum, int family, struct rib_subscription *rs)
+{
+ struct rib_head *rnh;
+
+ NET_EPOCH_ASSERT();
+ KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__));
+ rnh = rt_tables_get_rnh(fibnum, family);
+
+ if (rnh == NULL)
+ return (ENOENT);
+
+ RIB_WLOCK(rnh);
+ CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
+ RIB_WUNLOCK(rnh);
+
+ free(rs, M_RTABLE);
+ return (0);
+}
diff --git a/sys/net/route/route_ddb.c b/sys/net/route/route_ddb.c
index 64cbff700667..df50a3e725a4 100644
--- a/sys/net/route/route_ddb.c
+++ b/sys/net/route/route_ddb.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <net/if_dl.h>
#include <net/route.h>
#include <net/route/nhop.h>
+#include <net/route/route_ctl.h>
#include <net/route/route_var.h>
/*
diff --git a/sys/net/route/route_helpers.c b/sys/net/route/route_helpers.c
index 54ab18989d30..eadb62b804f3 100644
--- a/sys/net/route/route_helpers.c
+++ b/sys/net/route/route_helpers.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/route.h>
+#include <net/route/route_ctl.h>
#include <net/route/route_var.h>
#include <net/route/nhop_utils.h>
#include <net/route/nhop.h>
diff --git a/sys/net/route/route_temporal.c b/sys/net/route/route_temporal.c
index c797eefd0c18..660436a2eb6e 100644
--- a/sys/net/route/route_temporal.c
+++ b/sys/net/route/route_temporal.c
@@ -38,11 +38,13 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/lock.h>
+#include <sys/ck.h>
#include <sys/rmlock.h>
#include <sys/callout.h>
#include <net/if.h>
#include <net/route.h>
+#include <net/route/route_ctl.h>
#include <net/route/route_var.h>
#include <net/vnet.h>
diff --git a/sys/net/route/route_var.h b/sys/net/route/route_var.h
index 176084372609..4427ac046ee8 100644
--- a/sys/net/route/route_var.h
+++ b/sys/net/route/route_var.h
@@ -35,6 +35,7 @@
#ifndef RNF_NORMAL
#include <net/radix.h>
#endif
+#include <sys/ck.h>
#include <sys/epoch.h>
#include <netinet/in.h> /* struct sockaddr_in */
#include <sys/counter.h>
@@ -63,6 +64,7 @@ struct rib_head {
struct callout expire_callout; /* Callout for expiring dynamic routes */
time_t next_expire; /* Next expire run ts */
struct nh_control *nh_control; /* nexthop subsystem data */
+ CK_STAILQ_HEAD(, rib_subscription) rnh_subscribers;/* notification subscribers */
};
#define RIB_RLOCK_TRACKER struct rm_priotracker _rib_tracker
@@ -110,12 +112,13 @@ void rt_setmetrics(const struct rt_addrinfo *info, struct rtentry *rt);
struct radix_node *rt_mpath_unlink(struct rib_head *rnh,
struct rt_addrinfo *info, struct rtentry *rto, int *perror);
#endif
+struct rib_cmd_info;
int add_route(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt);
+ struct rib_cmd_info *rc);
int del_route(struct rib_head *rnh, struct rt_addrinfo *info,
- struct rtentry **ret_nrt);
+ struct rib_cmd_info *rc);
int change_route(struct rib_head *, struct rt_addrinfo *,
- struct rtentry **);
+ struct rib_cmd_info *rc);
VNET_PCPUSTAT_DECLARE(struct rtstat, rtstat);
#define RTSTAT_ADD(name, val) \