Basic MPLS support.
1) Imports ENCAP_MPLS labels from IPv4/IPv6 routes. Note that this requires libnl 3.6.0 or newer. In previous patches, the fib_path_ext_t had a path ID of -1. After a long investigation, it turned out to be caused by route weight being set to 0. There is a comment explaining more details. 2) Handles MPLS routes. MPLS routes were wrongly added as IPv4 routes before. POP and SWAP are now both supported. All the routes are installed as NON-EOS and EOS routes, as the Linux kernel does not differentiate. EOS POP used in PHP uses the next-hop address family to determine the resulting address family. This patch is sufficient for P setups. PE setups with implicit null should also function okay, as long as a seperate label gets programmed per address family. PE setups with explicit null will also forward packets, but punting is a bit odd and needs MPLS input enabled on the LCP host device. Make sure to enable MPLS in VPP first. 3) Propagate MPLS input state to LCP Pair and Linux. Since the Linux kernel uses the MPLS routes itself, the LCP pair tap needs MPLS enabled to allow host originated packets. This also syncs the Linux `net.mpls.conf.<host_if>.input` sysctl to allow punted packets to have MPLS labels, mostly explicit nulls. For that to work, load the mpls kernel modules. 4) Cross connect MPLS packets from Linux directly to interface-output This is a port of https://gerrit.fd.io/r/c/vpp/+/38702
This commit is contained in:
172
lcpng_nl_sync.c
172
lcpng_nl_sync.c
@ -17,6 +17,7 @@
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/mpls.h>
|
||||
|
||||
#include <vnet/vnet.h>
|
||||
#include <vnet/plugin/plugin.h>
|
||||
@ -146,13 +147,31 @@ vnet_sw_interface_get_available_subid (vnet_main_t *vnm, u32 sw_if_index,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static fib_protocol_t
|
||||
lcp_nl_proto_k2f (uint32_t k)
|
||||
{
|
||||
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 fib_protocol_t
|
||||
lcp_nl_mk_addr46 (const struct nl_addr *rna, ip46_address_t *ia)
|
||||
{
|
||||
fib_protocol_t fproto;
|
||||
|
||||
fproto =
|
||||
nl_addr_get_family (rna) == AF_INET6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4;
|
||||
fproto = lcp_nl_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));
|
||||
@ -166,9 +185,31 @@ static void
|
||||
lcp_nl_mk_route_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_nl_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_nl_mk_addr46 (addr, &p->fp_addr);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -208,6 +249,37 @@ lcp_nl_mk_route_entry_flags (uint8_t rtype, int table_id, uint8_t rproto)
|
||||
return (fef);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void
|
||||
lcp_nl_route_path_parse (struct rtnl_nexthop *rnh, void *arg)
|
||||
{
|
||||
@ -216,6 +288,7 @@ lcp_nl_route_path_parse (struct rtnl_nexthop *rnh, void *arg)
|
||||
lcp_itf_pair_t *lip;
|
||||
fib_protocol_t fproto;
|
||||
struct nl_addr *addr;
|
||||
int label_count = 0;
|
||||
|
||||
/* We do not log a warning/error here, because some routes (like
|
||||
* blackhole/unreach) don't have an interface associated with them.
|
||||
@ -230,9 +303,16 @@ lcp_nl_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 = lip->lip_phy_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);
|
||||
@ -244,6 +324,32 @@ lcp_nl_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_NL_DBG ("router_path_parse: 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_NL_DBG ("router_path_parse: 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_NL_DBG ("router_path_parse: has encap mpls, %d labels", label_count);
|
||||
#endif
|
||||
|
||||
if (ctx->is_mcast)
|
||||
path->frp_mitf_flags = MFIB_ITF_FLAG_FORWARD;
|
||||
|
||||
@ -439,11 +545,21 @@ lcp_nl_route_del (struct rtnl_route *rr)
|
||||
LCP_NL_DBG ("route_del: table %d prefix %U flags %U",
|
||||
rtnl_route_get_table (rr), format_fib_prefix, &pfx,
|
||||
format_fib_entry_flags, entry_flags);
|
||||
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);
|
||||
@ -451,6 +567,26 @@ lcp_nl_route_del (struct rtnl_route *rr)
|
||||
lcp_nl_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;
|
||||
}
|
||||
|
||||
void
|
||||
lcp_nl_route_add (struct rtnl_route *rr, int is_replace)
|
||||
{
|
||||
@ -536,6 +672,24 @@ lcp_nl_route_add (struct rtnl_route *rr, int is_replace)
|
||||
rtnl_route_get_table (rr), format_fib_prefix, &pfx,
|
||||
format_fib_entry_flags, entry_flags);
|
||||
|
||||
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);
|
||||
|
Reference in New Issue
Block a user