Page MenuHomeFreeBSD

D59640.diff
No OneTemporary

D59640.diff

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

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)

Event Timeline