aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander V. Chernikov <melifaro@FreeBSD.org>2022-08-29 14:38:25 +0000
committerAlexander V. Chernikov <melifaro@FreeBSD.org>2022-08-29 14:46:03 +0000
commitfe05d1dd0fdf7c8d09b9aad4ff74c44b7c90db80 (patch)
tree747d55f8fd81da5ccd45bdab635cd3f60af30f13
parentc24a8f19c5d554b828908e7dacf1855cd4ee21f3 (diff)
downloadsrc-fe05d1dd0fdf7c8d09b9aad4ff74c44b7c90db80.tar.gz
src-fe05d1dd0fdf7c8d09b9aad4ff74c44b7c90db80.zip
routing: extend nhop(9) kpi
* add nhop_get_unlinked() used to prepare referenced but not linked nexthop, that can later be used as a clone source. * add nhop_check_gateway() to check for allowed address family combinations between the rib family and neighbor family (useful for 4o6 or direct routes) * add nhop_set_upper_family() to allow copying IPv6 nexthops to IPv4 rib. * add rt_get_rnd() wrapper, returning both nexthop/group and its weight attached to the rtentry. * Add CHT_SLIST_FOREACH_SAFE(), allowing to delete items during iteration. MFC after: 2 weeks
-rw-r--r--sys/net/route/nhop.h3
-rw-r--r--sys/net/route/nhop_ctl.c85
-rw-r--r--sys/net/route/nhop_utils.h5
-rw-r--r--sys/net/route/route_ctl.c41
-rw-r--r--sys/net/route/route_ctl.h1
-rw-r--r--sys/net/route/route_rtentry.c7
-rw-r--r--sys/net/route/route_var.h2
7 files changed, 102 insertions, 42 deletions
diff --git a/sys/net/route/nhop.h b/sys/net/route/nhop.h
index 9d0891c5b978..24ddb692efb8 100644
--- a/sys/net/route/nhop.h
+++ b/sys/net/route/nhop.h
@@ -182,6 +182,7 @@ struct rib_head;
struct nhop_object *nhop_alloc(uint32_t fibnum, int family);
void nhop_copy(struct nhop_object *nh, const struct nhop_object *nh_orig);
struct nhop_object *nhop_get_nhop(struct nhop_object *nh, int *perror);
+int nhop_get_unlinked(struct nhop_object *nh);
void nhop_set_direct_gw(struct nhop_object *nh, struct ifnet *ifp);
bool nhop_set_gw(struct nhop_object *nh, const struct sockaddr *sa, bool is_gw);
@@ -206,11 +207,13 @@ int nhop_get_rtflags(const struct nhop_object *nh);
struct vnet *nhop_get_vnet(const struct nhop_object *nh);
struct nhop_object *nhop_select_func(struct nhop_object *nh, uint32_t flowid);
int nhop_get_upper_family(const struct nhop_object *nh);
+bool nhop_set_upper_family(struct nhop_object *nh, int family);
int nhop_get_neigh_family(const struct nhop_object *nh);
uint32_t nhop_get_fibnum(const struct nhop_object *nh);
void nhop_set_fibnum(struct nhop_object *nh, uint32_t fibnum);
uint32_t nhop_get_expire(const struct nhop_object *nh);
void nhop_set_expire(struct nhop_object *nh, uint32_t expire);
+struct rib_head *nhop_get_rh(const struct nhop_object *nh);
struct nhgrp_object;
uint32_t nhgrp_get_uidx(const struct nhgrp_object *nhg);
diff --git a/sys/net/route/nhop_ctl.c b/sys/net/route/nhop_ctl.c
index 4af57d766ab7..263a71c1322e 100644
--- a/sys/net/route/nhop_ctl.c
+++ b/sys/net/route/nhop_ctl.c
@@ -85,13 +85,12 @@ _DECLARE_DEBUG(LOG_INFO);
static int dump_nhop_entry(struct rib_head *rh, struct nhop_object *nh, struct sysctl_req *w);
-static int finalize_nhop(struct nh_control *ctl, struct nhop_object *nh);
+static int finalize_nhop(struct nh_control *ctl, struct nhop_object *nh, bool link);
static struct ifnet *get_aifp(const struct nhop_object *nh);
static void fill_sdl_from_ifp(struct sockaddr_dl_short *sdl, const struct ifnet *ifp);
static void destroy_nhop_epoch(epoch_context_t ctx);
static void destroy_nhop(struct nhop_object *nh);
-static struct rib_head *nhop_get_rh(const struct nhop_object *nh);
_Static_assert(__offsetof(struct nhop_object, nh_ifp) == 32,
"nhop_object: wrong nh_ifp offset");
@@ -315,6 +314,12 @@ nhop_get_nhop(struct nhop_object *nh, int *perror)
{
struct rib_head *rnh = nhop_get_rh(nh);
+ if (__predict_false(rnh == NULL)) {
+ *perror = EAFNOSUPPORT;
+ nhop_free(nh);
+ return (NULL);
+ }
+
return (nhop_get_nhop_internal(rnh, nh, perror));
}
@@ -349,11 +354,34 @@ nhop_get_nhop_internal(struct rib_head *rnh, struct nhop_object *nh, int *perror
* relative number of such nexthops is significant, which
* is extremely unlikely.
*/
- *perror = finalize_nhop(rnh->nh_control, nh);
+ *perror = finalize_nhop(rnh->nh_control, nh, true);
return (*perror == 0 ? nh : NULL);
}
/*
+ * Gets referenced but unlinked nhop.
+ * Alocates/references the remaining bits of the nexthop data, so
+ * it can be safely linked later or used as a clone source.
+ *
+ * Returns 0 on success.
+ */
+int
+nhop_get_unlinked(struct nhop_object *nh)
+{
+ struct rib_head *rnh = nhop_get_rh(nh);
+
+ if (__predict_false(rnh == NULL)) {
+ nhop_free(nh);
+ return (EAFNOSUPPORT);
+ }
+
+ nh->nh_aifp = get_aifp(nh);
+
+ return (finalize_nhop(rnh->nh_control, nh, false));
+}
+
+
+/*
* Update @nh with data supplied in @info.
* This is a helper function to support route changes.
*
@@ -458,7 +486,7 @@ reference_nhop_deps(struct nhop_object *nh)
* errno otherwise. @nh_priv is freed in case of error.
*/
static int
-finalize_nhop(struct nh_control *ctl, struct nhop_object *nh)
+finalize_nhop(struct nh_control *ctl, struct nhop_object *nh, bool link)
{
/* Allocate per-cpu packet counter */
@@ -484,9 +512,14 @@ finalize_nhop(struct nh_control *ctl, struct nhop_object *nh)
/* Please see nhop_free() comments on the initial value */
refcount_init(&nh->nh_priv->nh_linked, 2);
- nh->nh_priv->nh_fibnum = ctl->ctl_rh->rib_fibnum;
+ MPASS(nh->nh_priv->nh_fibnum == ctl->ctl_rh->rib_fibnum);
- if (link_nhop(ctl, nh->nh_priv) == 0) {
+ if (!link) {
+ refcount_release(&nh->nh_priv->nh_linked);
+ NHOPS_WLOCK(ctl);
+ nh->nh_priv->nh_finalized = 1;
+ NHOPS_WUNLOCK(ctl);
+ } else if (link_nhop(ctl, nh->nh_priv) == 0) {
/*
* Adding nexthop to the datastructures
* failed. Call destructor w/o waiting for
@@ -693,6 +726,22 @@ nhop_set_direct_gw(struct nhop_object *nh, struct ifnet *ifp)
memset(&nh->gw_buf[nh->gw_sa.sa_len], 0, sizeof(nh->gw_buf) - nh->gw_sa.sa_len);
}
+bool
+nhop_check_gateway(int upper_family, int neigh_family)
+{
+ if (upper_family == neigh_family)
+ return (true);
+ else if (neigh_family == AF_UNSPEC || neigh_family == AF_LINK)
+ return (true);
+#if defined(INET) && defined(INET6)
+ else if (upper_family == AF_INET && neigh_family == AF_INET6 &&
+ rib_can_4o6_nhop())
+ return (true);
+#endif
+ else
+ return (false);
+}
+
/*
* Sets gateway for the nexthop.
* It can be "normal" gateway with is_gw set or a special form of
@@ -707,6 +756,14 @@ nhop_set_gw(struct nhop_object *nh, const struct sockaddr *gw, bool is_gw)
gw->sa_family, gw->sa_len);
return (false);
}
+
+ if (!nhop_check_gateway(nh->nh_priv->nh_upper_family, gw->sa_family)) {
+ FIB_NH_LOG(LOG_DEBUG, nh,
+ "error: invalid dst/gateway family combination (%d, %d)",
+ nh->nh_priv->nh_upper_family, gw->sa_family);
+ return (false);
+ }
+
memcpy(&nh->gw_sa, gw, gw->sa_len);
memset(&nh->gw_buf[gw->sa_len], 0, sizeof(nh->gw_buf) - gw->sa_len);
@@ -723,6 +780,20 @@ nhop_set_gw(struct nhop_object *nh, const struct sockaddr *gw, bool is_gw)
return (true);
}
+bool
+nhop_set_upper_family(struct nhop_object *nh, int family)
+{
+ if (!nhop_check_gateway(nh->nh_priv->nh_upper_family, family)) {
+ FIB_NH_LOG(LOG_DEBUG, nh,
+ "error: invalid upper/neigh family combination (%d, %d)",
+ nh->nh_priv->nh_upper_family, family);
+ return (false);
+ }
+
+ nh->nh_priv->nh_upper_family = family;
+ return (true);
+}
+
void
nhop_set_broadcast(struct nhop_object *nh, bool is_broadcast)
{
@@ -930,7 +1001,7 @@ nhop_set_expire(struct nhop_object *nh, uint32_t expire)
nh->nh_priv->nh_expire = expire;
}
-static struct rib_head *
+struct rib_head *
nhop_get_rh(const struct nhop_object *nh)
{
uint32_t fibnum = nhop_get_fibnum(nh);
diff --git a/sys/net/route/nhop_utils.h b/sys/net/route/nhop_utils.h
index 1f56f4cb8b0b..210b1387c5c9 100644
--- a/sys/net/route/nhop_utils.h
+++ b/sys/net/route/nhop_utils.h
@@ -139,6 +139,11 @@ struct _HNAME##_head { \
for (_x = CHT_FIRST(_head, _i); _x; _x = _PX##_next(_x))
#define CHT_SLIST_FOREACH_END }
+#define CHT_SLIST_FOREACH_SAFE(_head, _PX, _x, _tmp) \
+ for (uint32_t _i = 0; _i < (_head)->hash_size; _i++) { \
+ for (_x = CHT_FIRST(_head, _i); (_tmp = _PX##_next(_x), _x); _x = _tmp)
+#define CHT_SLIST_FOREACH_SAFE_END }
+
#define CHT_SLIST_RESIZE(_head, _PX, _new_void_ptr, _new_hsize) \
uint32_t _new_idx; \
typeof((_head)->ptr) _new_ptr = (void *)_new_void_ptr; \
diff --git a/sys/net/route/route_ctl.c b/sys/net/route/route_ctl.c
index 6881b8131d08..4b7572ce7980 100644
--- a/sys/net/route/route_ctl.c
+++ b/sys/net/route/route_ctl.c
@@ -134,7 +134,7 @@ uint8_t mpath_entropy_key[MPATH_ENTROPY_KEY_LEN] = {
#if defined(INET) && defined(INET6)
FEATURE(ipv4_rfc5549_support, "Route IPv4 packets via IPv6 nexthops");
#define V_rib_route_ipv6_nexthop VNET(rib_route_ipv6_nexthop)
-VNET_DEFINE(u_int, rib_route_ipv6_nexthop) = 1;
+VNET_DEFINE_STATIC(u_int, rib_route_ipv6_nexthop) = 1;
SYSCTL_UINT(_net_route, OID_AUTO, ipv6_nexthop, CTLFLAG_RW | CTLFLAG_VNET,
&VNET_NAME(rib_route_ipv6_nexthop), 0, "Enable IPv4 route via IPv6 Next Hop address");
#endif
@@ -157,16 +157,10 @@ get_rnh(uint32_t fibnum, const struct rt_addrinfo *info)
}
#if defined(INET) && defined(INET6)
-static bool
-rib_can_ipv6_nexthop_address(struct rib_head *rh)
+bool
+rib_can_4o6_nhop(void)
{
- int result;
-
- CURVNET_SET(rh->rib_vnet);
- result = !!V_rib_route_ipv6_nexthop;
- CURVNET_RESTORE();
-
- return (result);
+ return (!!V_rib_route_ipv6_nexthop);
}
#endif
@@ -702,30 +696,6 @@ rib_add_route(uint32_t fibnum, struct rt_addrinfo *info,
return (error);
}
-/*
- * Checks if @dst and @gateway is valid combination.
- *
- * Returns true if is valid, false otherwise.
- */
-static bool
-check_gateway(struct rib_head *rnh, struct sockaddr *dst,
- struct sockaddr *gateway)
-{
- if (dst->sa_family == gateway->sa_family)
- return (true);
- else if (gateway->sa_family == AF_UNSPEC)
- return (true);
- else if (gateway->sa_family == AF_LINK)
- return (true);
-#if defined(INET) && defined(INET6)
- else if (dst->sa_family == AF_INET && gateway->sa_family == AF_INET6 &&
- rib_can_ipv6_nexthop_address(rnh))
- return (true);
-#endif
- else
- return (false);
-}
-
static int
add_route_byinfo(struct rib_head *rnh, struct rt_addrinfo *info,
struct rib_cmd_info *rc)
@@ -744,7 +714,7 @@ add_route_byinfo(struct rib_head *rnh, struct rt_addrinfo *info,
FIB_RH_LOG(LOG_DEBUG, rnh, "error: RTF_GATEWAY set with empty gw");
return (EINVAL);
}
- if (dst && gateway && !check_gateway(rnh, dst, gateway)) {
+ if (dst && gateway && !nhop_check_gateway(dst->sa_family, gateway->sa_family)) {
FIB_RH_LOG(LOG_DEBUG, rnh,
"error: invalid dst/gateway family combination (%d, %d)",
dst->sa_family, gateway->sa_family);
@@ -1578,3 +1548,4 @@ rib_print_family(int family)
}
return ("unknown");
}
+
diff --git a/sys/net/route/route_ctl.h b/sys/net/route/route_ctl.h
index 1fc67591c2e8..e65e6be4efda 100644
--- a/sys/net/route/route_ctl.h
+++ b/sys/net/route/route_ctl.h
@@ -136,6 +136,7 @@ const struct rtentry *rib_lookup_lpm(uint32_t fibnum, int family,
bool rt_is_host(const struct rtentry *rt);
sa_family_t rt_get_family(const struct rtentry *);
struct nhop_object *rt_get_raw_nhop(const struct rtentry *rt);
+void rt_get_rnd(const struct rtentry *rt, struct route_nhop_data *rnd);
#ifdef INET
struct in_addr;
void rt_get_inet_prefix_plen(const struct rtentry *rt, struct in_addr *paddr,
diff --git a/sys/net/route/route_rtentry.c b/sys/net/route/route_rtentry.c
index 35ca304a669e..41e4ff8ac49f 100644
--- a/sys/net/route/route_rtentry.c
+++ b/sys/net/route/route_rtentry.c
@@ -192,6 +192,13 @@ rt_get_raw_nhop(const struct rtentry *rt)
return (rt->rt_nhop);
}
+void
+rt_get_rnd(const struct rtentry *rt, struct route_nhop_data *rnd)
+{
+ rnd->rnd_nhop = rt->rt_nhop;
+ rnd->rnd_weight = rt->rt_weight;
+}
+
#ifdef INET
/*
* Stores IPv4 address and prefix length of @rt inside
diff --git a/sys/net/route/route_var.h b/sys/net/route/route_var.h
index 51a4285e673e..f286e49b4526 100644
--- a/sys/net/route/route_var.h
+++ b/sys/net/route/route_var.h
@@ -228,6 +228,7 @@ bool nhop_can_multipath(const struct nhop_object *nh);
bool match_nhop_gw(const struct nhop_object *nh, const struct sockaddr *gw);
int check_info_match_nhop(const struct rt_addrinfo *info,
const struct rtentry *rt, const struct nhop_object *nh);
+bool rib_can_4o6_nhop(void);
/* route_rtentry.c */
void vnet_rtzone_init(void);
@@ -255,6 +256,7 @@ void nhop_free_any(struct nhop_object *nh);
struct nhop_object *nhop_get_nhop_internal(struct rib_head *rnh,
struct nhop_object *nh, int *perror);
+bool nhop_check_gateway(int upper_family, int neigh_family);
int nhop_create_from_info(struct rib_head *rnh, struct rt_addrinfo *info,
struct nhop_object **nh_ret);