Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F171784990
D59640.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
22 KB
Referenced Files
None
Subscribers
None
D59640.diff
View Options
diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist
--- a/etc/mtree/BSD.include.dist
+++ b/etc/mtree/BSD.include.dist
@@ -406,6 +406,8 @@
route
..
..
+ netmpls
+ ..
netpfil
pf tags=package=pf-dev
..
diff --git a/include/Makefile b/include/Makefile
--- a/include/Makefile
+++ b/include/Makefile
@@ -48,7 +48,7 @@
syslog.h ucontext.h
LDIRS= geom net net80211 netgraph netinet netinet6 \
- netipsec netlink netsmb nfs nfsclient nfsserver sys vm
+ netipsec netlink netmpls netsmb nfs nfsclient nfsserver sys vm
LSUBDIRS= dev/acpica dev/agp dev/ciss dev/filemon dev/firewire \
dev/hwpmc dev/hyperv \
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -653,6 +653,7 @@
#
options INET #Internet communications protocols
options INET6 #IPv6 communications protocols
+options MPLS
#
# Note if you include INET/INET6 or both options
# You *must* define at least one of the congestion control
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4613,6 +4613,8 @@
netlink/route/neigh.c optional netlink
netlink/route/nexthop.c optional netlink
netlink/route/rt.c optional netlink
+netmpls/mpls_domain.c optional mpls
+netmpls/mpls_rtable.c optional mpls
netpfil/ipfw/dn_aqm_codel.c optional inet dummynet
netpfil/ipfw/dn_aqm_pie.c optional inet dummynet
netpfil/ipfw/dn_heap.c optional inet dummynet
diff --git a/sys/conf/options b/sys/conf/options
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -446,6 +446,7 @@
RATELIMIT_DEBUG opt_ratelimit.h
INET opt_inet.h
INET6 opt_inet6.h
+MPLS opt_mpls.h
STATS opt_global.h
IPDIVERT
IPFILTER opt_ipfilter.h
diff --git a/sys/modules/ktest/Makefile b/sys/modules/ktest/Makefile
--- a/sys/modules/ktest/Makefile
+++ b/sys/modules/ktest/Makefile
@@ -1,5 +1,6 @@
SUBDIR= ktest \
ktest_example \
+ ktest_mpls \
ktest_netlink_message_writer \
ktest_nhop \
ktest_tcphpts
diff --git a/sys/modules/ktest/ktest_mpls/Makefile b/sys/modules/ktest/ktest_mpls/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/ktest/ktest_mpls/Makefile
@@ -0,0 +1,9 @@
+PACKAGE= tests
+
+SYSDIR?=${SRCTOP}/sys
+.PATH: ${SYSDIR}/tests
+
+KMOD= ktest_mpls
+SRCS= ktest_mpls.c opt_inet.h opt_inet6.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/net/route.c b/sys/net/route.c
--- a/sys/net/route.c
+++ b/sys/net/route.c
@@ -35,6 +35,7 @@
#include "opt_inet.h"
#include "opt_inet6.h"
+#include "opt_mpls.h"
#include "opt_mrouting.h"
#include <sys/param.h>
@@ -522,6 +523,12 @@
#endif
for (u_int j = 0; j < rt_numfibs; j++) {
+#ifdef MPLS
+ struct rib_head *rh = rt_tables_get_rnh(j, AF_MPLS);
+
+ if (rh != NULL)
+ nhops_update_ifmtu(rh, ifp, ifp->if_mtu);
+#endif
#ifdef INET
nhops_update_ifmtu(rt_tables_get_rnh(j, AF_INET), ifp,
ifp->if_mtu);
diff --git a/sys/net/route/fib_algo.c b/sys/net/route/fib_algo.c
--- a/sys/net/route/fib_algo.c
+++ b/sys/net/route/fib_algo.c
@@ -1997,7 +1997,12 @@
void
fib_setup_family(int family, uint32_t num_tables)
{
- struct fib_dp_header *new_fdh = alloc_fib_dp_array(num_tables, false);
+ struct fib_dp_header *new_fdh;
+
+ if (get_family_dp_ptr(family) == NULL)
+ return;
+
+ new_fdh = alloc_fib_dp_array(num_tables, false);
if (new_fdh == NULL) {
ALGO_PRINTF(LOG_CRIT, "Unable to setup framework for %s", print_family(family));
return;
diff --git a/sys/net/route/nhop_ctl.c b/sys/net/route/nhop_ctl.c
--- a/sys/net/route/nhop_ctl.c
+++ b/sys/net/route/nhop_ctl.c
@@ -314,6 +314,10 @@
error = rnh->rnh_set_nh_pfxflags(rnh->rib_fibnum, info->rti_info[RTAX_DST],
info->rti_info[RTAX_NETMASK], nh);
+ if (error != 0) {
+ nhop_free(nh);
+ return (error);
+ }
nhop_set_redirect(nh, info->rti_flags & RTF_DYNAMIC);
nhop_set_pinned(nh, info->rti_flags & RTF_PINNED);
@@ -928,6 +932,21 @@
bool
nhop_check_gateway(int upper_family, int neigh_family)
{
+ if (upper_family == AF_MPLS) {
+ switch (neigh_family) {
+ case AF_UNSPEC:
+ case AF_LINK:
+#ifdef INET
+ case AF_INET:
+#endif
+#ifdef INET6
+ case AF_INET6:
+#endif
+ return (true);
+ default:
+ return (false);
+ }
+ }
if (upper_family == neigh_family)
return (true);
else if (neigh_family == AF_UNSPEC || neigh_family == AF_LINK)
@@ -982,10 +1001,10 @@
bool
nhop_set_upper_family(struct nhop_object *nh, int family)
{
- if (!nhop_check_gateway(nh->nh_priv->nh_upper_family, family)) {
+ if (!nhop_check_gateway(family, nhop_get_neigh_family(nh))) {
FIB_NH_LOG(LOG_DEBUG, nh,
"error: invalid upper/neigh family combination (%d, %d)",
- nh->nh_priv->nh_upper_family, family);
+ family, nhop_get_neigh_family(nh));
return (false);
}
@@ -1032,6 +1051,9 @@
bzero(&nh->gw_sa, sizeof(nh->gw_sa));
switch (nh->nh_priv->nh_upper_family) {
+ case AF_MPLS:
+ nhop_set_direct_gw(nh, V_loif);
+ break;
#ifdef INET
case AF_INET:
nh->gw4_sa.sin_family = AF_INET;
@@ -1235,6 +1257,8 @@
uint32_t fibnum = nhop_get_fibnum(nh);
int family = nhop_get_neigh_family(nh);
+ if (nhop_get_upper_family(nh) == AF_MPLS)
+ family = AF_MPLS;
return (rt_tables_get_rnh(fibnum, family));
}
diff --git a/sys/net/route/route_ctl.c b/sys/net/route/route_ctl.c
--- a/sys/net/route/route_ctl.c
+++ b/sys/net/route/route_ctl.c
@@ -55,6 +55,7 @@
#include <netinet/in.h>
#include <netinet6/scope6_var.h>
#include <netinet6/in6_var.h>
+#include <netmpls/mpls.h>
#define DEBUG_MOD_NAME route_ctl
#define DEBUG_MAX_LEVEL LOG_DEBUG
@@ -391,6 +392,14 @@
fill_pxmask_family(int family, int plen, struct sockaddr *_dst,
struct sockaddr **pmask)
{
+ if (family == AF_MPLS) {
+ if ((plen != MPLS_LABEL_BITS && plen != -1) ||
+ !mpls_valid_sockaddr((const struct sockaddr_mpls *)_dst))
+ return (false);
+ *pmask = NULL;
+ return (true);
+ }
+
if (plen == -1) {
*pmask = NULL;
return (true);
@@ -466,6 +475,7 @@
union sockaddr_union mask_storage;
struct sockaddr *netmask = &mask_storage.sa;
struct rtentry *rt = NULL;
+ int error;
NET_EPOCH_ASSERT();
@@ -473,18 +483,22 @@
rc->rc_cmd = RTM_ADD;
struct rib_head *rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
- if (rnh == NULL)
- return (EAFNOSUPPORT);
+ if (rnh == NULL) {
+ error = EAFNOSUPPORT;
+ goto out;
+ }
if (!fill_pxmask_family(dst->sa_family, plen, dst, &netmask)) {
FIB_RH_LOG(LOG_DEBUG, rnh, "error: invalid plen %d", plen);
- return (EINVAL);
+ error = EINVAL;
+ goto out;
}
if (op_flags & RTM_F_CREATE) {
if ((rt = rt_alloc(rnh, dst, netmask)) == NULL) {
FIB_RH_LOG(LOG_INFO, rnh, "rtentry allocation failed");
- return (ENOMEM);
+ error = ENOMEM;
+ goto out;
}
} else {
struct route_nhop_data rnd_tmp;
@@ -494,11 +508,16 @@
rt = lookup_prefix_bysa(rnh, dst, netmask, &rnd_tmp);
RIB_RUNLOCK(rnh);
- if (rt == NULL)
- return (ESRCH);
+ if (rt == NULL) {
+ error = ESRCH;
+ goto out;
+ }
}
return (add_route_flags(rnh, rt, rnd, op_flags, rc));
+out:
+ nhop_free_any(rnd->rnd_nhop);
+ return (error);
}
/*
@@ -1540,6 +1559,8 @@
rib_print_family(int family)
{
switch (family) {
+ case AF_MPLS:
+ return ("mpls");
case AF_INET:
return ("inet");
case AF_INET6:
diff --git a/sys/net/route/route_helpers.c b/sys/net/route/route_helpers.c
--- a/sys/net/route/route_helpers.c
+++ b/sys/net/route/route_helpers.c
@@ -61,6 +61,7 @@
#include <netinet6/in6_var.h>
#endif
#include <net/vnet.h>
+#include <netmpls/mpls.h>
#define DEBUG_MOD_NAME rt_helpers
#define DEBUG_MAX_LEVEL LOG_DEBUG2
@@ -635,6 +636,15 @@
#endif
switch (rt_get_family(rt)) {
+ case AF_MPLS:
+ {
+ const struct sockaddr_mpls *dst;
+
+ dst = (const struct sockaddr_mpls *)rt_key_const(rt);
+ snprintf(buf, bufsize, "rt/mpls/%u",
+ ntohl(dst->smpls_label) >> MPLS_LABEL_SHIFT);
+ }
+ break;
#ifdef INET
case AF_INET:
{
diff --git a/sys/net/route/route_rtentry.c b/sys/net/route/route_rtentry.c
--- a/sys/net/route/route_rtentry.c
+++ b/sys/net/route/route_rtentry.c
@@ -202,6 +202,9 @@
bool
rt_is_exportable(const struct rtentry *rt, struct ucred *cred)
{
+ if (rt_get_family(rt) == AF_MPLS)
+ return (!jailed_without_vnet(cred));
+
if (!rt_is_host(rt)) {
/*
* Performance optimisation: only host routes are allowed
diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c
--- a/sys/net/rtsock.c
+++ b/sys/net/rtsock.c
@@ -2053,6 +2053,8 @@
return (0);
int family = rt_get_family(rt);
+ if (family == AF_MPLS)
+ return (0);
init_sockaddrs_family(family, &dst.sa, &mask.sa);
export_rtaddrs(rt, &dst.sa, &mask.sa);
@@ -2582,6 +2584,9 @@
{
union sockaddr_union sa_dst, sa_mask;
+ if (family == AF_MPLS)
+ return;
+
w->family = family;
w->dst = (struct sockaddr *)&sa_dst;
w->mask = (struct sockaddr *)&sa_mask;
@@ -2625,6 +2630,8 @@
af = name[0];
if (af >= AF_MAX)
return (EINVAL);
+ if (af == AF_MPLS)
+ return (EAFNOSUPPORT);
bzero(&w, sizeof(w));
w.w_op = name[1];
w.w_arg = name[2];
diff --git a/sys/netmpls/mpls.h b/sys/netmpls/mpls.h
new file mode 100644
--- /dev/null
+++ b/sys/netmpls/mpls.h
@@ -0,0 +1,54 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _NETMPLS_MPLS_H_
+#define _NETMPLS_MPLS_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+struct mpls_shim {
+ uint32_t mpls_lse;
+};
+
+#define MPLS_LABEL_BITS 20
+#define MPLS_LABEL_MAX 0xfffffU
+#define MPLS_LABEL_SHIFT 12
+#define MPLS_LABEL_MASK 0xfffff000U
+#define MPLS_TC_SHIFT 9
+#define MPLS_TC_MASK 0x00000e00U
+#define MPLS_BOS_MASK 0x00000100U
+#define MPLS_TTL_MASK 0x000000ffU
+
+#define MPLS_LABEL_IPV4NULL 0
+#define MPLS_LABEL_RTALERT 1
+#define MPLS_LABEL_IPV6NULL 2
+#define MPLS_LABEL_IMPLNULL 3
+#define MPLS_LABEL_RESERVED_MAX 15
+
+struct sockaddr_mpls {
+ uint8_t smpls_len;
+ uint8_t smpls_family;
+ uint16_t smpls_pad;
+ uint32_t smpls_label;
+};
+
+#ifdef _KERNEL
+#include <sys/endian.h>
+
+static inline bool
+mpls_valid_sockaddr(const struct sockaddr_mpls *dst)
+{
+ uint32_t lse;
+
+ if (dst->smpls_len != sizeof(*dst) || dst->smpls_family != AF_MPLS ||
+ dst->smpls_pad != 0)
+ return (false);
+ lse = be32toh(dst->smpls_label);
+ return ((lse & ~MPLS_LABEL_MASK) == 0 &&
+ (lse >> MPLS_LABEL_SHIFT) > MPLS_LABEL_RESERVED_MAX);
+}
+#endif
+
+#endif
diff --git a/sys/netmpls/mpls_domain.c b/sys/netmpls/mpls_domain.c
new file mode 100644
--- /dev/null
+++ b/sys/netmpls/mpls_domain.c
@@ -0,0 +1,25 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/domain.h>
+#include <sys/sysctl.h>
+
+#include <netmpls/mpls.h>
+#include <netmpls/mpls_var.h>
+
+FEATURE(mpls, "MPLS label routing");
+
+static struct domain mplsdomain = {
+ .dom_family = AF_MPLS,
+ .dom_name = "mpls",
+ .dom_rtattach = mpls_inithead,
+#ifdef VIMAGE
+ .dom_rtdetach = mpls_detachhead,
+#endif
+};
+
+DOMAIN_SET(mpls);
diff --git a/sys/netmpls/mpls_rtable.c b/sys/netmpls/mpls_rtable.c
new file mode 100644
--- /dev/null
+++ b/sys/netmpls/mpls_rtable.c
@@ -0,0 +1,107 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/lock.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/rmlock.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/route.h>
+#include <net/route/nhop.h>
+#include <net/route/route_ctl.h>
+#include <net/route/route_var.h>
+#include <net/vnet.h>
+
+#include <netmpls/mpls.h>
+#include <netmpls/mpls_var.h>
+
+CTASSERT(sizeof(struct sockaddr_mpls) == 8);
+CTASSERT(sizeof(struct mpls_shim) == 4);
+
+static int
+mpls_set_nh_pfxflags(u_int fibnum __unused, const struct sockaddr *addr,
+ const struct sockaddr *mask, struct nhop_object *nh)
+{
+
+ if (mask != NULL ||
+ !mpls_valid_sockaddr((const struct sockaddr_mpls *)addr))
+ return (EINVAL);
+ nhop_set_pxtype_flag(nh, NHF_HOST);
+ return (0);
+}
+
+static int
+mpls_augment_nh(u_int fibnum __unused, struct nhop_object *nh)
+{
+
+ nhop_update_mtu(nh, if_getmtu(nh->nh_ifp));
+ return (0);
+}
+
+struct rib_head *
+mpls_inithead(uint32_t fibnum)
+{
+ struct rib_head *rh;
+
+ rh = rt_table_init(offsetof(struct sockaddr_mpls, smpls_label) * NBBY,
+ AF_MPLS, fibnum);
+ rh->rnh_set_nh_pfxflags = mpls_set_nh_pfxflags;
+ rh->rnh_augment_nh = mpls_augment_nh;
+ return (rh);
+}
+
+#ifdef VIMAGE
+void
+mpls_detachhead(struct rib_head *rh)
+{
+
+ rt_table_destroy(rh);
+}
+#endif
+
+struct nhop_object *
+mpls_rib_lookup(struct rib_head *rh, uint32_t label)
+{
+ RIB_RLOCK_TRACKER;
+ struct sockaddr_mpls dst = {
+ .smpls_len = sizeof(dst), .smpls_family = AF_MPLS,
+ .smpls_label = htonl(label << MPLS_LABEL_SHIFT),
+ };
+ struct radix_node *rn;
+ struct nhop_object *nh = NULL;
+
+ NET_EPOCH_ASSERT();
+ if (label <= MPLS_LABEL_RESERVED_MAX || label > MPLS_LABEL_MAX)
+ return (NULL);
+ RIB_RLOCK(rh);
+ rn = rh->rnh_lookup(&dst, NULL, &rh->head);
+ if (rn != NULL && (rn->rn_flags & RNF_ROOT) == 0)
+ nh = RNTORT(rn)->rt_nhop;
+ RIB_RUNLOCK(rh);
+ return (nh);
+}
+
+struct nhop_object *
+fib_mpls_lookup(uint32_t fibnum, uint32_t label, uint32_t flowid)
+{
+ struct nhop_object *nh;
+ struct rib_head *rh;
+
+ NET_EPOCH_ASSERT();
+ if (fibnum >= V_rt_numfibs || label > MPLS_LABEL_MAX ||
+ label <= MPLS_LABEL_RESERVED_MAX)
+ return (NULL);
+ rh = rt_tables_get_rnh(fibnum, AF_MPLS);
+
+ nh = rh != NULL ? mpls_rib_lookup(rh, label) : NULL;
+ if (nh != NULL)
+ nh = nhop_select_func(nh, flowid);
+ return (nh != NULL && NH_IS_VALID(nh) ? nh : NULL);
+}
diff --git a/sys/netmpls/mpls_var.h b/sys/netmpls/mpls_var.h
new file mode 100644
--- /dev/null
+++ b/sys/netmpls/mpls_var.h
@@ -0,0 +1,24 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _NETMPLS_MPLS_VAR_H_
+#define _NETMPLS_MPLS_VAR_H_
+
+#include <sys/types.h>
+
+#ifdef _KERNEL
+struct rib_head;
+struct nhop_object;
+
+struct rib_head *mpls_inithead(uint32_t fibnum);
+#ifdef VIMAGE
+void mpls_detachhead(struct rib_head *rh);
+#endif
+struct nhop_object *mpls_rib_lookup(struct rib_head *, uint32_t);
+
+struct nhop_object *fib_mpls_lookup(uint32_t fibnum, uint32_t label,
+ uint32_t flowid);
+#endif
+
+#endif
diff --git a/sys/sys/socket.h b/sys/sys/socket.h
--- a/sys/sys/socket.h
+++ b/sys/sys/socket.h
@@ -272,8 +272,9 @@
#define AF_HYPERV 43 /* HyperV sockets */
#define AF_DIVERT 44 /* divert(4) */
#define AF_IPFWLOG 46
+#define AF_MPLS 48
-#define AF_MAX 47
+#define AF_MAX 49
/*
* When allocating a new AF_ constant, please only allocate
@@ -402,6 +403,7 @@
#define PF_HYPERV AF_HYPERV
#define PF_DIVERT AF_DIVERT
#define PF_IPFWLOG AF_IPFWLOG
+#define PF_MPLS AF_MPLS
#define PF_MAX AF_MAX
diff --git a/sys/tests/ktest_mpls.c b/sys/tests/ktest_mpls.c
new file mode 100644
--- /dev/null
+++ b/sys/tests/ktest_mpls.c
@@ -0,0 +1,214 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/rmlock.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_private.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/route/nhop.h>
+#include <net/route/route_ctl.h>
+#include <net/route/route_var.h>
+#include <net/vnet.h>
+#include <netmpls/mpls.h>
+#include <netmpls/mpls_var.h>
+#include <tests/ktest.h>
+
+#define CHECK(condition) do { \
+ if (!(condition)) { \
+ KTEST_ERR(ctx, "check failed: %s", #condition); \
+ error = EINVAL; \
+ goto out; \
+ } \
+} while (0)
+
+static int
+test_mpls_keys(struct ktest_test_context *ctx)
+{
+ struct nhop_object *nh;
+ struct rib_head *rh;
+ struct epoch_tracker et;
+ struct sockaddr_mpls dst = {
+ .smpls_len = sizeof(dst), .smpls_family = AF_MPLS,
+ };
+ int error = 0;
+
+ NET_EPOCH_ENTER(et);
+ rh = rt_tables_get_rnh_safe(0, AF_MPLS);
+ nh = nhop_alloc(0, AF_MPLS);
+ CHECK(rh != NULL && nh != NULL);
+ for (uint32_t label = 0; label <= MPLS_LABEL_RESERVED_MAX + 1; label++) {
+ dst.smpls_label = htonl(label << MPLS_LABEL_SHIFT);
+ CHECK(rh->rnh_set_nh_pfxflags(0, (void *)&dst, NULL, nh) ==
+ (label <= MPLS_LABEL_RESERVED_MAX ? EINVAL : 0));
+ }
+ dst.smpls_label = htonl(MPLS_LABEL_MAX << MPLS_LABEL_SHIFT);
+ CHECK(rh->rnh_set_nh_pfxflags(0, (void *)&dst, NULL, nh) == 0);
+ CHECK((nh->nh_flags & NHF_HOST) != 0);
+ CHECK(rh->rnh_set_nh_pfxflags(0, (void *)&dst, (void *)&dst, nh) == EINVAL);
+ for (u_int bit = 0; bit < MPLS_LABEL_SHIFT; bit++) {
+ dst.smpls_label = htonl((16U << MPLS_LABEL_SHIFT) | (1U << bit));
+ CHECK(rh->rnh_set_nh_pfxflags(0, (void *)&dst, NULL, nh) == EINVAL);
+ }
+ dst.smpls_label = htonl(16U << MPLS_LABEL_SHIFT);
+ dst.smpls_pad = 1;
+ CHECK(rh->rnh_set_nh_pfxflags(0, (void *)&dst, NULL, nh) == EINVAL);
+ dst.smpls_pad = 0;
+ dst.smpls_len--;
+ CHECK(rh->rnh_set_nh_pfxflags(0, (void *)&dst, NULL, nh) == EINVAL);
+ dst.smpls_len = sizeof(dst);
+ dst.smpls_family = AF_INET;
+ CHECK(rh->rnh_set_nh_pfxflags(0, (void *)&dst, NULL, nh) == EINVAL);
+ CHECK(fib_mpls_lookup(0, MPLS_LABEL_RESERVED_MAX, 0) == NULL);
+ CHECK(fib_mpls_lookup(0, MPLS_LABEL_MAX + 1, 0) == NULL);
+ CHECK(fib_mpls_lookup(V_rt_numfibs, 16, 0) == NULL);
+out:
+ if (nh != NULL)
+ nhop_free(nh);
+ NET_EPOCH_EXIT(et);
+ return (error);
+}
+
+#if defined(INET) || defined(INET6)
+static int
+match_test_nhop(const struct rtentry *rt __unused,
+ const struct nhop_object *nh, void *arg)
+{
+ struct nhop_object **test_nh = arg;
+
+ return (nh == test_nh[0] || nh == test_nh[1]);
+}
+
+static int
+test_mpls_routes(struct ktest_test_context *ctx)
+{
+ struct nhop_object *nh[2] = {}, *candidate = NULL;
+ struct rib_head *rh;
+ struct ifnet *ifp = NULL;
+ struct ifaddr *ifa = NULL;
+ struct epoch_tracker et;
+ struct rib_cmd_info rc;
+ struct route_nhop_data rnd;
+ struct rt_addrinfo info = {};
+ struct sockaddr_storage gw = {};
+ struct sockaddr_mpls dst = {
+ .smpls_len = sizeof(dst), .smpls_family = AF_MPLS,
+ };
+ uint32_t label;
+ bool installed = false;
+ int error = 0, result;
+
+#ifdef INET
+ struct sockaddr_in *sin = (void *)&gw;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+#else
+ struct sockaddr_in6 *sin6 = (void *)&gw;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr.s6_addr[15] = 1;
+#endif
+ ifp = if_alloc(IFT_ETHER);
+ ifa = ifa_alloc(sizeof(*ifa) + sizeof(gw), M_NOWAIT);
+ NET_EPOCH_ENTER(et);
+ rh = rt_tables_get_rnh_safe(0, AF_MPLS);
+ CHECK(rh != NULL && ifp != NULL && ifa != NULL);
+ if_setmtu(ifp, 1500);
+ ifa->ifa_ifp = ifp;
+ ifa->ifa_addr = (void *)(ifa + 1);
+ memcpy(ifa->ifa_addr, &gw, sizeof(gw));
+ for (u_int i = 0; i < nitems(nh); i++) {
+ nh[i] = nhop_alloc(0, AF_MPLS);
+ CHECK(nh[i] != NULL);
+ CHECK(!nhop_set_gw(nh[i], (void *)&dst, true));
+ CHECK(nhop_set_gw(nh[i], (void *)&gw, true));
+ CHECK(nhop_get_rh(nh[i]) == rh);
+ CHECK(nhop_set_upper_family(nh[i], gw.ss_family));
+ CHECK(nhop_set_upper_family(nh[i], AF_MPLS));
+ CHECK(!nhop_set_upper_family(nh[i], AF_LOCAL));
+ nhop_set_transmit_ifp(nh[i], ifp);
+ nhop_set_src(nh[i], ifa);
+ nhop_set_pxtype_flag(nh[i], NHF_HOST);
+ nhop_set_rtflags(nh[i], RTF_STATIC);
+ nhop_set_mtu(nh[i], 1400 + i, true);
+ nh[i] = nhop_get_nhop(nh[i], &result);
+ CHECK(nh[i] != NULL && result == 0);
+ CHECK(nhop_get_rh(nh[i]) == rh && nh[i]->nh_mtu == 1400 + i);
+ }
+ dst.smpls_label = htonl((16U << MPLS_LABEL_SHIFT) | MPLS_BOS_MASK);
+ info.rti_info[RTAX_DST] = (void *)&dst;
+ info.rti_info[RTAX_GATEWAY] = (void *)&gw;
+ info.rti_flags = RTF_GATEWAY;
+ info.rti_ifp = ifp;
+ info.rti_ifa = ifa;
+ result = nhop_create_from_info(rh, &info, &candidate);
+ CHECK(result == EINVAL && candidate == NULL);
+ for (label = MPLS_LABEL_MAX - 1024; label < MPLS_LABEL_MAX; label++) {
+ dst.smpls_label = htonl(label << MPLS_LABEL_SHIFT);
+ CHECK(nhop_try_ref_object(nh[0]));
+ rnd = (struct route_nhop_data){ .rnd_nhop = nh[0], .rnd_weight = 1 };
+ result = rib_add_route_px(0, (void *)&dst, MPLS_LABEL_BITS, &rnd,
+ RTM_F_CREATE | RTM_F_EXCL, &rc);
+ if (result == 0) {
+ installed = true;
+ break;
+ }
+ CHECK(result == EEXIST);
+ }
+ CHECK(installed);
+ CHECK(mpls_rib_lookup(rh, label) == nh[0]);
+ CHECK(fib_mpls_lookup(0, label, 0) == nh[0]);
+ CHECK(fib_mpls_lookup(0, label, UINT32_MAX) == nh[0]);
+ CHECK(nhop_try_ref_object(nh[1]));
+ rnd = (struct route_nhop_data){ .rnd_nhop = nh[1], .rnd_weight = 1 };
+ CHECK(rib_add_route_px(0, (void *)&dst, MPLS_LABEL_BITS, &rnd,
+ RTM_F_REPLACE, &rc) == 0);
+ CHECK(fib_mpls_lookup(0, label, 0) == nh[1]);
+ nh[1]->nh_flags |= NHF_INVALID;
+ CHECK(fib_mpls_lookup(0, label, 0) == NULL);
+ nh[1]->nh_flags &= ~NHF_INVALID;
+ CHECK(fib_mpls_lookup(0, label, 0) == nh[1]);
+ CHECK(rib_del_route_px(0, (void *)&dst, MPLS_LABEL_BITS,
+ match_test_nhop, nh, 0, &rc) == 0);
+ installed = false;
+ CHECK(fib_mpls_lookup(0, label, 0) == NULL);
+out:
+ if (installed)
+ rib_del_route_px(0, (void *)&dst, MPLS_LABEL_BITS,
+ match_test_nhop, nh, 0, &rc);
+ if (candidate != NULL)
+ nhop_free(candidate);
+ for (u_int i = 0; i < nitems(nh); i++)
+ if (nh[i] != NULL)
+ nhop_free(nh[i]);
+ NET_EPOCH_EXIT(et);
+ NET_EPOCH_DRAIN_CALLBACKS();
+ if (ifa != NULL)
+ ifa_free(ifa);
+ if (ifp != NULL)
+ if_free(ifp);
+ return (error);
+}
+#endif
+
+static const struct ktest_test_info tests[] = {
+ { "test_mpls_keys", "Canonical labels, reserved values and lookup bounds",
+ test_mpls_keys, NULL },
+#if defined(INET) || defined(INET6)
+ { "test_mpls_routes", "Label route creation, replacement and withdrawal",
+ test_mpls_routes, NULL },
+#endif
+};
+KTEST_MODULE_DECLARE(ktest_mpls, tests);
diff --git a/tests/sys/netlink/Makefile b/tests/sys/netlink/Makefile
--- a/tests/sys/netlink/Makefile
+++ b/tests/sys/netlink/Makefile
@@ -15,6 +15,7 @@
ATF_TESTS_PYTEST += test_rtnl_route.py
ATF_TESTS_PYTEST += test_netlink_message_writer.py
ATF_TESTS_PYTEST += test_nhop.py
+ATF_TESTS_PYTEST += test_mpls_routing.py
CFLAGS+= -I${.CURDIR:H:H:H}
diff --git a/tests/sys/netlink/test_mpls_routing.py b/tests/sys/netlink/test_mpls_routing.py
new file mode 100644
--- /dev/null
+++ b/tests/sys/netlink/test_mpls_routing.py
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+import subprocess
+import sys
+
+import pytest
+
+if not sys.platform.startswith("freebsd"):
+ pytest.skip("FreeBSD kernel required", allow_module_level=True)
+
+feature = subprocess.run(
+ ["/sbin/sysctl", "-n", "kern.features.mpls"], capture_output=True, text=True
+)
+if feature.returncode != 0 or feature.stdout.strip() != "1":
+ pytest.skip("kernel requires options MPLS", allow_module_level=True)
+
+from atf_python.ktest import BaseKernelTest
+
+
+@pytest.mark.require_user("root")
+class TestMplsRouting(BaseKernelTest):
+ KTEST_MODULE_NAME = "ktest_mpls"
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Sep 14, 12:34 PM (21 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38907488
Default Alt Text
D59640.diff (22 KB)
Attached To
Mode
D59640: mpls: Add the MPLS routing family and per-FIB label tables
Attached
Detach File
Event Timeline
Log In to Comment