aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/linux-cp/lcp_interface.c
diff options
context:
space:
mode:
authorPim van Pelt <pim@ipng.nl>2021-08-12 21:35:06 +0200
committerMatthew Smith <mgsmith@netgate.com>2021-09-09 13:02:34 +0000
commitb89c1ddcb3b4f9138ca3ebefb2115f896ff3e1bd (patch)
tree2f828c4be47a5fe3a46d052875f8197f524ba5bd /src/plugins/linux-cp/lcp_interface.c
parent7b46e4bc574e6dfa7f5a454a696476744625c55f (diff)
linux-cp: Complete interface creation logic
Linux Control Plane interface creation logic is currently only able to create untagged interfaces, and dot1q VLAN sub-interfaces. This change makes it possible to create dot1ad VLAN sub-ints, and Q-in-AD as well as Q-in-Q sub-interfaces as well. It makes the plugin a bit more robust by catching a few common errors, such as creating an lcp on a sub-interface without its parent having one, and creating an lcp on a sub-interface that is not exact-match. This change has a bunch of smaller improvemnets as well. I documented my work in this post: https://ipng.ch/s/articles/2021/08/12/vpp-1.html It shows that after the change is merged, all VPP interface types now create and operate cleanly as LCP interfaces as well. Type: improvement Signed-off-by: Pim van Pelt <pim@ipng.nl> Change-Id: I322669f7316d44c227090b83d6a574fb9c00e76a
Diffstat (limited to 'src/plugins/linux-cp/lcp_interface.c')
-rw-r--r--src/plugins/linux-cp/lcp_interface.c287
1 files changed, 243 insertions, 44 deletions
diff --git a/src/plugins/linux-cp/lcp_interface.c b/src/plugins/linux-cp/lcp_interface.c
index da409619746..aeff9a1131e 100644
--- a/src/plugins/linux-cp/lcp_interface.c
+++ b/src/plugins/linux-cp/lcp_interface.c
@@ -21,6 +21,7 @@
#include <linux-cp/lcp_interface.h>
#include <netlink/route/link/vlan.h>
+#include <linux/if_ether.h>
#include <vnet/plugin/plugin.h>
#include <vnet/plugin/plugin.h>
@@ -43,7 +44,7 @@ static vlib_log_class_t lcp_itf_pair_logger;
/**
* Pool of LIP objects
*/
-lcp_itf_pair_t *lcp_itf_pair_pool;
+lcp_itf_pair_t *lcp_itf_pair_pool = NULL;
u32
lcp_itf_num_pairs (void)
@@ -78,6 +79,8 @@ lcp_itf_pair_register_vft (lcp_itf_pair_vft_t *lcp_itf_vft)
#define LCP_ITF_PAIR_INFO(...) \
vlib_log_notice (lcp_itf_pair_logger, __VA_ARGS__);
+#define LCP_ITF_PAIR_ERR(...) vlib_log_err (lcp_itf_pair_logger, __VA_ARGS__);
+
u8 *
format_lcp_itf_pair (u8 *s, va_list *args)
{
@@ -152,6 +155,9 @@ lcp_itf_pair_show (u32 phy_sw_if_index)
lcp_itf_pair_t *
lcp_itf_pair_get (u32 index)
{
+ if (!lcp_itf_pair_pool)
+ return NULL;
+
return pool_elt_at_index (lcp_itf_pair_pool, index);
}
@@ -175,6 +181,13 @@ lcp_itf_pair_add_sub (u32 vif, u8 *host_if_name, u32 sub_sw_if_index,
lcp_itf_pair_t *lip;
lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (phy_sw_if_index));
+ if (!lip)
+ {
+ LCP_ITF_PAIR_DBG ("lcp_itf_pair_add_sub: can't find LCP of parent %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ phy_sw_if_index);
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+ }
return lcp_itf_pair_add (lip->lip_host_sw_if_index, sub_sw_if_index,
host_if_name, vif, lip->lip_host_type, ns);
@@ -237,7 +250,7 @@ lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, u8 *host_name,
lipi = lcp_itf_pair_find_by_phy (phy_sw_if_index);
- LCP_ITF_PAIR_INFO ("add: host:%U phy:%U, host_if:%v vif:%d ns:%v",
+ LCP_ITF_PAIR_INFO ("add: host:%U phy:%U, host_if:%v vif:%d ns:%s",
format_vnet_sw_if_index_name, vnet_get_main (),
host_sw_if_index, format_vnet_sw_if_index_name,
vnet_get_main (), phy_sw_if_index, host_name, host_index,
@@ -336,7 +349,7 @@ lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, u8 *host_name,
}
static clib_error_t *
-lcp_netlink_add_link_vlan (int parent, u32 vlan, const char *name)
+lcp_netlink_add_link_vlan (int parent, u32 vlan, u16 proto, const char *name)
{
struct rtnl_link *link;
struct nl_sock *sk;
@@ -344,17 +357,25 @@ lcp_netlink_add_link_vlan (int parent, u32 vlan, const char *name)
sk = nl_socket_alloc ();
if ((err = nl_connect (sk, NETLINK_ROUTE)) < 0)
- return clib_error_return (NULL, "Unable to connect socket: %d", err);
+ {
+ LCP_ITF_PAIR_ERR ("netlink_add_link_vlan: connect error: %s",
+ nl_geterror (err));
+ return clib_error_return (NULL, "Unable to connect socket: %d", err);
+ }
link = rtnl_link_vlan_alloc ();
rtnl_link_set_link (link, parent);
rtnl_link_set_name (link, name);
-
rtnl_link_vlan_set_id (link, vlan);
+ rtnl_link_vlan_set_protocol (link, htons (proto));
if ((err = rtnl_link_add (sk, link, NLM_F_CREATE)) < 0)
- return clib_error_return (NULL, "Unable to add link %s: %d", name, err);
+ {
+ LCP_ITF_PAIR_ERR ("netlink_add_link_vlan: link add error: %s",
+ nl_geterror (err));
+ return clib_error_return (NULL, "Unable to add link %s: %d", name, err);
+ }
rtnl_link_put (link);
nl_close (sk);
@@ -531,7 +552,7 @@ lcp_itf_pair_config (vlib_main_t *vm, unformat_input_t *input)
if (vec_len (ns) > LCP_NS_LEN)
{
return clib_error_return (0,
- "linux-cp IF namespace must"
+ "linux-cp namespace must"
" be less than %d characters",
LCP_NS_LEN);
}
@@ -616,21 +637,24 @@ lcp_validate_if_name (u8 *name)
}
static void
-lcp_itf_set_vif_link_state (u32 vif_index, u8 up, u8 *ns)
+lcp_itf_set_link_state (const lcp_itf_pair_t *lip, u8 state)
{
int curr_ns_fd, vif_ns_fd;
+ if (!lip)
+ return;
+
curr_ns_fd = vif_ns_fd = -1;
- if (ns)
+ if (lip->lip_namespace)
{
curr_ns_fd = clib_netns_open (NULL /* self */);
- vif_ns_fd = clib_netns_open (ns);
+ vif_ns_fd = clib_netns_open (lip->lip_namespace);
if (vif_ns_fd != -1)
clib_setns (vif_ns_fd);
}
- vnet_netlink_set_link_state (vif_index, up);
+ vnet_netlink_set_link_state (lip->lip_vif_index, state);
if (vif_ns_fd != -1)
close (vif_ns_fd);
@@ -640,6 +664,63 @@ lcp_itf_set_vif_link_state (u32 vif_index, u8 up, u8 *ns)
clib_setns (curr_ns_fd);
close (curr_ns_fd);
}
+
+ return;
+}
+
+typedef struct
+{
+ u32 vlan;
+ bool dot1ad;
+
+ u32 matched_sw_if_index;
+} lcp_itf_match_t;
+
+static walk_rc_t
+lcp_itf_pair_find_walk (vnet_main_t *vnm, u32 sw_if_index, void *arg)
+{
+ lcp_itf_match_t *match = arg;
+ const vnet_sw_interface_t *sw;
+
+ sw = vnet_get_sw_interface (vnm, sw_if_index);
+ if (sw && (sw->sub.eth.inner_vlan_id == 0) &&
+ (sw->sub.eth.outer_vlan_id == match->vlan) &&
+ (sw->sub.eth.flags.dot1ad == match->dot1ad))
+ {
+ LCP_ITF_PAIR_DBG ("find_walk: found match outer %d dot1ad %d "
+ "inner-dot1q %d: interface %U",
+ sw->sub.eth.outer_vlan_id, sw->sub.eth.flags.dot1ad,
+ sw->sub.eth.inner_vlan_id,
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw->sw_if_index);
+ match->matched_sw_if_index = sw->sw_if_index;
+ return WALK_STOP;
+ }
+
+ return WALK_CONTINUE;
+}
+
+/* Return the index of the sub-int on the phy that has the given vlan and
+ * proto,
+ */
+static index_t
+lcp_itf_pair_find_by_outer_vlan (u32 sup_if_index, u16 vlan, bool dot1ad)
+{
+ lcp_itf_match_t match;
+ const vnet_hw_interface_t *hw;
+
+ match.vlan = vlan;
+ match.dot1ad = dot1ad;
+ match.matched_sw_if_index = INDEX_INVALID;
+ hw = vnet_get_sup_hw_interface (vnet_get_main (), sup_if_index);
+
+ vnet_hw_interface_walk_sw (vnet_get_main (), hw->hw_if_index,
+ lcp_itf_pair_find_walk, &match);
+
+ if (match.matched_sw_if_index >= vec_len (lip_db_by_phy))
+ return INDEX_INVALID;
+
+ return lip_db_by_phy[match.matched_sw_if_index];
}
int
@@ -652,21 +733,33 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
u32 vif_index = 0, host_sw_if_index;
const vnet_sw_interface_t *sw;
const vnet_hw_interface_t *hw;
+ const lcp_itf_pair_t *lip;
if (!vnet_sw_if_index_is_api_valid (phy_sw_if_index))
- return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+ {
+ LCP_ITF_PAIR_ERR ("pair_create: invalid phy index %u", phy_sw_if_index);
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+ }
if (!lcp_validate_if_name (host_if_name))
- return VNET_API_ERROR_INVALID_ARGUMENT;
+ {
+ LCP_ITF_PAIR_ERR ("pair_create: invalid host-if-name '%s'",
+ host_if_name);
+ return VNET_API_ERROR_INVALID_ARGUMENT;
+ }
vnm = vnet_get_main ();
sw = vnet_get_sw_interface (vnm, phy_sw_if_index);
hw = vnet_get_sup_hw_interface (vnm, phy_sw_if_index);
+ if (!sw || !hw)
+ {
+ LCP_ITF_PAIR_ERR ("pair_create: invalid interface");
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+ }
/*
* Use interface-specific netns if supplied.
- * Otherwise, use default netns if defined.
- * Otherwise ignore a netns and use the OS default.
+ * Otherwise, use netns if defined, otherwise use the OS default.
*/
if (ns == 0 || ns[0] == 0)
ns = lcp_get_default_ns ();
@@ -674,16 +767,51 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
/* sub interfaces do not need a tap created */
if (vnet_sw_interface_is_sub (vnm, phy_sw_if_index))
{
- const lcp_itf_pair_t *lip;
+ index_t parent_if_index;
int orig_ns_fd, ns_fd;
clib_error_t *err;
- u16 vlan;
+ u16 outer_vlan, inner_vlan;
+ u16 outer_proto, inner_proto;
+ u16 vlan, proto;
+ u32 parent_vif_index;
+
+ // TODO(pim) replace with vnet_sw_interface_supports_addressing()
+ if (sw->type == VNET_SW_INTERFACE_TYPE_SUB &&
+ sw->sub.eth.flags.exact_match == 0)
+ {
+ LCP_ITF_PAIR_ERR ("pair_create: can't create LCP for a "
+ "sub-interface without exact-match set");
+ return VNET_API_ERROR_INVALID_ARGUMENT;
+ }
- /*
- * Find the parent tap by finding the pair from the parent phy
- */
- lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw->sup_sw_if_index));
- vlan = sw->sub.eth.outer_vlan_id;
+ outer_vlan = sw->sub.eth.outer_vlan_id;
+ inner_vlan = sw->sub.eth.inner_vlan_id;
+ outer_proto = inner_proto = ETH_P_8021Q;
+ if (1 == sw->sub.eth.flags.dot1ad)
+ outer_proto = ETH_P_8021AD;
+
+ LCP_ITF_PAIR_INFO ("pair_create: subif: dot1%s outer %d inner %d on %U",
+ sw->sub.eth.flags.dot1ad ? "ad" : "q", outer_vlan,
+ inner_vlan, format_vnet_sw_if_index_name, vnm,
+ hw->sw_if_index);
+
+ parent_if_index = lcp_itf_pair_find_by_phy (sw->sup_sw_if_index);
+ if (INDEX_INVALID == parent_if_index)
+ {
+ LCP_ITF_PAIR_ERR ("pair_create: can't find LCP for %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw->sup_sw_if_index);
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+ }
+ lip = lcp_itf_pair_get (parent_if_index);
+ if (!lip)
+ {
+ LCP_ITF_PAIR_ERR ("pair_create: can't create LCP for a "
+ "sub-interface without an LCP on the parent");
+ return VNET_API_ERROR_INVALID_ARGUMENT;
+ }
+ LCP_ITF_PAIR_DBG ("pair_create: parent %U", format_lcp_itf_pair, lip);
+ parent_vif_index = lip->lip_vif_index;
/*
* see if the requested host interface has already been created
@@ -708,11 +836,56 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
/*
* no existing host interface, create it now
*/
- err = lcp_netlink_add_link_vlan (lip->lip_vif_index, vlan,
- (const char *) host_if_name);
- if (!err && -1 != ns_fd)
- err = vnet_netlink_set_link_netns (vif_index, ns_fd, NULL);
+ /*
+ * Find the parent tap:
+ * - if this is an outer VLAN, use the pair from the parent phy
+ * - if this is an inner VLAN, find the pair from the outer sub-int,
+ * which must exist.
+ */
+ if (inner_vlan)
+ {
+ index_t linux_parent_if_index;
+ const lcp_itf_pair_t *llip;
+
+ vlan = inner_vlan;
+ proto = inner_proto;
+ linux_parent_if_index = lcp_itf_pair_find_by_outer_vlan (
+ hw->sw_if_index, sw->sub.eth.outer_vlan_id,
+ sw->sub.eth.flags.dot1ad);
+ if (INDEX_INVALID == linux_parent_if_index ||
+ !(llip = lcp_itf_pair_get (linux_parent_if_index)))
+ {
+ LCP_ITF_PAIR_ERR (
+ "pair_create: can't find LCP for outer vlan %d "
+ "proto %s on %U",
+ outer_vlan,
+ outer_proto == ETH_P_8021AD ? "dot1ad" : "dot1q",
+ format_vnet_sw_if_index_name, vnm, hw->sw_if_index);
+ err = clib_error_return (0, "parent pair not found");
+ goto socket_close;
+ }
+
+ LCP_ITF_PAIR_DBG ("pair_create: linux parent %U",
+ format_lcp_itf_pair, llip);
+ parent_vif_index = llip->lip_vif_index;
+ }
+ else
+ {
+ vlan = outer_vlan;
+ proto = outer_proto;
+ }
+
+ err = lcp_netlink_add_link_vlan (parent_vif_index, vlan, proto,
+ (const char *) host_if_name);
+ if (err != 0)
+ {
+ LCP_ITF_PAIR_ERR ("pair_create: cannot create link "
+ "outer(proto:0x%04x,vlan:%u).inner(proto:0x%"
+ "04x,vlan:%u) name:'%s'",
+ outer_proto, outer_vlan, inner_proto,
+ inner_vlan, host_if_name);
+ }
if (!err)
vif_index = if_nametoindex ((char *) host_if_name);
@@ -721,13 +894,20 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
/*
* create a sub-interface on the tap
*/
- if (!err && vnet_create_sub_interface (lip->lip_host_sw_if_index,
- sw->sub.id, sw->sub.eth.raw_flags,
- sw->sub.eth.inner_vlan_id, vlan,
- &host_sw_if_index))
- LCP_ITF_PAIR_INFO ("failed create vlan: %d on %U", vlan,
- format_vnet_sw_if_index_name, vnet_get_main (),
- lip->lip_host_sw_if_index);
+ if (!err &&
+ vnet_create_sub_interface (lip->lip_host_sw_if_index, sw->sub.id,
+ sw->sub.eth.raw_flags, inner_vlan,
+ outer_vlan, &host_sw_if_index))
+ {
+ LCP_ITF_PAIR_ERR (
+ "pair_create: failed to create tap subint: %d.%d on %U",
+ outer_vlan, inner_vlan, format_vnet_sw_if_index_name, vnm,
+ lip->lip_host_sw_if_index);
+ err = clib_error_return (
+ 0, "failed to create tap subinti: %d.%d. on %U", outer_vlan,
+ inner_vlan, format_vnet_sw_if_index_name, vnm,
+ lip->lip_host_sw_if_index);
+ }
socket_close:
if (orig_ns_fd != -1)
@@ -776,24 +956,33 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
if (args.rv < 0)
{
+ LCP_ITF_PAIR_ERR ("pair_create: could not create tap, retval:%d",
+ args.rv);
return args.rv;
}
/*
- * get the hw and ethernet of the tap
+ * The TAP interface does copy forward the host MTU based on the VPP
+ * interface's L3 MTU, but it should also ensure that the VPP tap
+ * interface has an MTU that is greater-or-equal to those. Considering
+ * users can set the interfaces at runtime (set interface mtu packet ...)
+ * ensure that the tap MTU is large enough, taking the VPP interface L3
+ * if it's set, and otherwise a sensible default.
*/
- hw = vnet_get_sup_hw_interface (vnm, args.sw_if_index);
+ if (sw->mtu[VNET_MTU_L3])
+ vnet_sw_interface_set_mtu (vnm, args.sw_if_index,
+ sw->mtu[VNET_MTU_L3]);
+ else
+ vnet_sw_interface_set_mtu (vnm, args.sw_if_index,
+ ETHERNET_MAX_PACKET_BYTES);
/*
- * Set the interface down on the host side.
- * This controls whether the host can RX/TX.
+ * get the hw and ethernet of the tap
*/
+ hw = vnet_get_sup_hw_interface (vnm, args.sw_if_index);
virtio_main_t *mm = &virtio_main;
virtio_if_t *vif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
- lcp_itf_set_vif_link_state (vif->ifindex, 0 /* down */,
- args.host_namespace);
-
/*
* Leave the TAP permanently up on the VPP side.
* This TAP will be shared by many sub-interface.
@@ -819,14 +1008,24 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
return -1;
}
- vnet_sw_interface_admin_up (vnm, host_sw_if_index);
- lcp_itf_pair_add (host_sw_if_index, phy_sw_if_index, host_if_name, vif_index,
- host_if_type, ns);
-
LCP_ITF_PAIR_INFO ("pair create: {%U, %U, %s}", format_vnet_sw_if_index_name,
vnet_get_main (), phy_sw_if_index,
format_vnet_sw_if_index_name, vnet_get_main (),
host_sw_if_index, host_if_name);
+ lcp_itf_pair_add (host_sw_if_index, phy_sw_if_index, host_if_name, vif_index,
+ host_if_type, ns);
+
+ /*
+ * Copy the link state from VPP into the host side.
+ * The TAP is shared by many interfaces, always keep it up.
+ * This controls whether the host can RX/TX.
+ */
+
+ lip = lcp_itf_pair_get (lcp_itf_pair_find_by_vif (vif_index));
+ LCP_ITF_PAIR_INFO ("pair create: %U sw-flags %u hw-flags %u",
+ format_lcp_itf_pair, lip, sw->flags, hw->flags);
+ vnet_sw_interface_admin_up (vnm, host_sw_if_index);
+ lcp_itf_set_link_state (lip, sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP);
if (host_sw_if_indexp)
*host_sw_if_indexp = host_sw_if_index;