summaryrefslogtreecommitdiffstats
path: root/src/plugins/linux-cp/lcp_router.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/linux-cp/lcp_router.c')
-rw-r--r--src/plugins/linux-cp/lcp_router.c168
1 files changed, 158 insertions, 10 deletions
diff --git a/src/plugins/linux-cp/lcp_router.c b/src/plugins/linux-cp/lcp_router.c
index 88b7c537afc..f53ec4fd8c6 100644
--- a/src/plugins/linux-cp/lcp_router.c
+++ b/src/plugins/linux-cp/lcp_router.c
@@ -15,6 +15,7 @@
#include <sys/socket.h>
#include <linux/if.h>
+#include <linux/mpls.h>
//#include <vlib/vlib.h>
#include <vlib/unix/plugin.h>
@@ -27,6 +28,7 @@
#include <netlink/route/link.h>
#include <netlink/route/route.h>
#include <netlink/route/neighbour.h>
+#include <netlink/route/nexthop.h>
#include <netlink/route/addr.h>
#include <netlink/route/link/vlan.h>
@@ -596,9 +598,18 @@ VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (lcp_router_link_up_down);
static fib_protocol_t
lcp_router_proto_k2f (uint32_t k)
{
- if (AF_INET6 == k)
- return (FIB_PROTOCOL_IP6);
- return (FIB_PROTOCOL_IP4);
+ switch (k)
+ {
+ case AF_INET6:
+ return FIB_PROTOCOL_IP6;
+ case AF_INET:
+ return FIB_PROTOCOL_IP4;
+ case AF_MPLS:
+ return FIB_PROTOCOL_MPLS;
+ default:
+ ASSERT (0);
+ return FIB_PROTOCOL_NONE;
+ }
}
static void
@@ -608,6 +619,7 @@ lcp_router_mk_addr (const struct nl_addr *rna, ip_address_t *ia)
ip_address_reset (ia);
fproto = lcp_router_proto_k2f (nl_addr_get_family (rna));
+ ASSERT (FIB_PROTOCOL_MPLS != fproto);
ip_address_set (ia, nl_addr_get_binary_addr (rna),
FIB_PROTOCOL_IP4 == fproto ? AF_IP4 : AF_IP6);
@@ -619,6 +631,8 @@ lcp_router_mk_addr46 (const struct nl_addr *rna, ip46_address_t *ia)
fib_protocol_t fproto;
fproto = lcp_router_proto_k2f (nl_addr_get_family (rna));
+ ASSERT (FIB_PROTOCOL_MPLS != fproto);
+
ip46_address_reset (ia);
if (FIB_PROTOCOL_IP4 == fproto)
memcpy (&ia->ip4, nl_addr_get_binary_addr (rna), nl_addr_get_len (rna));
@@ -988,9 +1002,31 @@ static void
lcp_router_route_mk_prefix (struct rtnl_route *r, fib_prefix_t *p)
{
const struct nl_addr *addr = rtnl_route_get_dst (r);
+ u32 *baddr = nl_addr_get_binary_addr (addr);
+ u32 blen = nl_addr_get_len (addr);
+ ip46_address_t *paddr = &p->fp_addr;
+ u32 entry;
+
+ p->fp_proto = lcp_router_proto_k2f (nl_addr_get_family (addr));
+
+ switch (p->fp_proto)
+ {
+ case FIB_PROTOCOL_MPLS:
+ entry = ntohl (*baddr);
+ p->fp_label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT;
+ p->fp_len = 21;
+ p->fp_eos = MPLS_NON_EOS;
+ return;
+ case FIB_PROTOCOL_IP4:
+ ip46_address_reset (paddr);
+ memcpy (&paddr->ip4, baddr, blen);
+ break;
+ case FIB_PROTOCOL_IP6:
+ memcpy (&paddr->ip6, baddr, blen);
+ break;
+ }
p->fp_len = nl_addr_get_prefixlen (addr);
- p->fp_proto = lcp_router_mk_addr46 (addr, &p->fp_addr);
}
static void
@@ -1008,6 +1044,37 @@ lcp_router_route_mk_mprefix (struct rtnl_route *r, mfib_prefix_t *p)
p->fp_proto = lcp_router_mk_addr46 (addr, &p->fp_src_addr);
}
+static int
+lcp_router_mpls_nladdr_to_path (fib_route_path_t *path, struct nl_addr *addr)
+{
+ if (!addr)
+ return 0;
+
+ struct mpls_label *stack = nl_addr_get_binary_addr (addr);
+ u32 entry, label;
+ u8 exp, ttl;
+ int label_count = 0;
+
+ while (1)
+ {
+ entry = ntohl (stack[label_count++].entry);
+ label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT;
+ exp = (entry & MPLS_LS_TC_MASK) >> MPLS_LS_TC_SHIFT;
+ ttl = (entry & MPLS_LS_TTL_MASK) >> MPLS_LS_TTL_SHIFT;
+
+ fib_mpls_label_t fml = {
+ .fml_value = label,
+ .fml_exp = exp,
+ .fml_ttl = ttl,
+ };
+ vec_add1 (path->frp_label_stack, fml);
+
+ if (entry & MPLS_LS_S_MASK)
+ break;
+ }
+ return label_count;
+}
+
typedef struct lcp_router_route_path_parse_t_
{
fib_route_path_t *paths;
@@ -1023,6 +1090,7 @@ lcp_router_route_path_parse (struct rtnl_nexthop *rnh, void *arg)
lcp_router_route_path_parse_t *ctx = arg;
fib_route_path_t *path;
u32 sw_if_index;
+ int label_count = 0;
sw_if_index = lcp_router_intf_h2p (rtnl_route_nh_get_ifindex (rnh));
@@ -1035,9 +1103,16 @@ lcp_router_route_path_parse (struct rtnl_nexthop *rnh, void *arg)
path->frp_flags = FIB_ROUTE_PATH_FLAG_NONE | ctx->type_flags;
path->frp_sw_if_index = sw_if_index;
- path->frp_weight = rtnl_route_nh_get_weight (rnh);
path->frp_preference = ctx->preference;
+ /*
+ * FIB Path Weight of 0 is meaningless and replaced with 1 further along.
+ * See fib_path_create. fib_path_cmp_w_route_path would fail to match
+ * such a fib_route_path_t with any fib_path_t, because a fib_path_t's
+ * fp_weight can never be 0.
+ */
+ path->frp_weight = clib_max (1, rtnl_route_nh_get_weight (rnh));
+
addr = rtnl_route_nh_get_gateway (rnh);
if (!addr)
addr = rtnl_route_nh_get_via (rnh);
@@ -1049,6 +1124,32 @@ lcp_router_route_path_parse (struct rtnl_nexthop *rnh, void *arg)
path->frp_proto = fib_proto_to_dpo (fproto);
+ if (ctx->route_proto == FIB_PROTOCOL_MPLS)
+ {
+ addr = rtnl_route_nh_get_newdst (rnh);
+ label_count = lcp_router_mpls_nladdr_to_path (path, addr);
+ if (label_count)
+ {
+ LCP_ROUTER_DBG (" is label swap to %u",
+ path->frp_label_stack[0].fml_value);
+ }
+ else
+ {
+ fib_mpls_label_t fml = {
+ .fml_value = MPLS_LABEL_POP,
+ };
+ vec_add1 (path->frp_label_stack, fml);
+ LCP_ROUTER_DBG (" is label pop");
+ }
+ }
+
+#ifdef NL_CAPABILITY_VERSION_3_6_0
+ addr = rtnl_route_nh_get_encap_mpls_dst (rnh);
+ label_count = lcp_router_mpls_nladdr_to_path (path, addr);
+ if (label_count)
+ LCP_ROUTER_DBG (" has encap mpls, %d labels", label_count);
+#endif
+
if (ctx->is_mcast)
path->frp_mitf_flags = MFIB_ITF_FLAG_FORWARD;
@@ -1171,11 +1272,20 @@ lcp_router_route_del (struct rtnl_route *rr)
fib_src = lcp_router_proto_fib_source (rproto);
- if (pfx.fp_proto == FIB_PROTOCOL_IP6)
- fib_table_entry_delete (nlt->nlt_fib_index, &pfx, fib_src);
- else
- fib_table_entry_path_remove2 (nlt->nlt_fib_index, &pfx, fib_src,
- np.paths);
+ switch (pfx.fp_proto)
+ {
+ case FIB_PROTOCOL_IP6:
+ fib_table_entry_delete (nlt->nlt_fib_index, &pfx, fib_src);
+ break;
+ case FIB_PROTOCOL_MPLS:
+ fib_table_entry_path_remove2 (nlt->nlt_fib_index, &pfx, fib_src,
+ np.paths);
+ /* delete the EOS route in addition to NEOS - fallthrough */
+ pfx.fp_eos = MPLS_EOS;
+ default:
+ fib_table_entry_path_remove2 (nlt->nlt_fib_index, &pfx, fib_src,
+ np.paths);
+ }
}
vec_free (np.paths);
@@ -1183,6 +1293,26 @@ lcp_router_route_del (struct rtnl_route *rr)
lcp_router_table_unlock (nlt);
}
+static fib_route_path_t *
+lcp_router_fib_route_path_dup (fib_route_path_t *old)
+{
+ int idx;
+ fib_route_path_t *p;
+
+ fib_route_path_t *new = vec_dup (old);
+ if (!new)
+ return NULL;
+
+ for (idx = 0; idx < vec_len (new); idx++)
+ {
+ p = &new[idx];
+ if (p->frp_label_stack)
+ p->frp_label_stack = vec_dup (p->frp_label_stack);
+ }
+
+ return new;
+}
+
static void
lcp_router_route_add (struct rtnl_route *rr, int is_replace)
{
@@ -1263,6 +1393,24 @@ lcp_router_route_add (struct rtnl_route *rr, int is_replace)
fib_src = lcp_router_proto_fib_source (rproto);
+ if (pfx.fp_proto == FIB_PROTOCOL_MPLS)
+ {
+ /* in order to avoid double-frees, we duplicate the paths. */
+ fib_route_path_t *pathdup =
+ lcp_router_fib_route_path_dup (np.paths);
+ if (is_replace)
+ fib_table_entry_update (nlt->nlt_fib_index, &pfx, fib_src,
+ entry_flags, pathdup);
+ else
+ fib_table_entry_path_add2 (nlt->nlt_fib_index, &pfx, fib_src,
+ entry_flags, pathdup);
+ vec_free (pathdup);
+
+ /* install EOS route in addition to NEOS */
+ pfx.fp_eos = MPLS_EOS;
+ pfx.fp_payload_proto = np.paths[0].frp_proto;
+ }
+
if (is_replace)
fib_table_entry_update (nlt->nlt_fib_index, &pfx, fib_src,
entry_flags, np.paths);