Add ability to create subint's from Linux
Using the earlier placeholder hint in lcp_nl_link_add(), I know that I've gotten a NEWLINK request but the linux ifindex doesn't have a LIP. This could be because the interface is entirely foreign to VPP, for example somebody created a dummy interface or a VLAN subint on one: ip link add dum0 type dummy ip link add link dum0 name dum0.10 type vlan id 10 Or, I'm actually trying to create a VLAN subint, like these: ip link add link e0 name e0.1234 type vlan id 1234 ip link add link e0.1234 name e0.1235 type vlan id 1000 ip link add link e0 name e0.1236 type vlan id 2345 proto 802.1ad ip link add link e0.1236 name e0.1237 type vlan id 1000 None of these NEWLINK callbacks, represented by vif (linux interface id) will have a corresponding LIP. So, I try to create one by calling lcp_nl_link_add_vlan(). Here, I lookup the parent index ('dum0' or 'e0' in the first examples), the former of which also doesn't have a LIP, so I bail. If it does, I still have two choices: 1) the LIP is a phy (ie TenGigabitEthernet3/0/0) and this is a regular tagged interface; or 2) the LIP is itself a subint (ie TenGigabitEthernet3/0/0.1234) and what I'm asking for is a QinQ or QinAD sub-interface. So I look up as well the phy LIP. We now have all the ingredients I need to create the VPP sub-interfaces with the correct inner-dot1q and outer dot1q or dot1ad. Of course, I don't really know what subinterface ID to use. It's appealing to "just" use the vlan, but that's not helpful if the outer tag and the inner tag are the same. So I write a helper function vnet_sw_interface_get_available_subid() whose job it is to return an unused subid for the phy -- starting from 1. I then create the phy sub-interface and the tap sub-interface, tying them together into a new LIP. During these interface creations, I want to make sure that if lcp-auto-subint is on, we disable that. I don't want VPP racing to create LIPs for the sub-ints right now. Before I return (either in error state or upon success), I put back the lcp-auto-subint to what it was before. If I manage to create the LIP, huzzah. I return it to the caller so it can continue setting link/mac/mtu etc.
This commit is contained in:
@ -48,14 +48,13 @@ column shows changes in LInux that are copied into VPP.
|
||||
| Change MTU | ✅ | ✅ |
|
||||
| Change MAC | ❌ 1) | ✅ |
|
||||
| Add/Del IP4/IP6 Address | ✅ | ✅ |
|
||||
| MPLS | ❌ | ❌ |
|
||||
| Route | ❌ 2) | 🟠 |
|
||||
| Add/Del Tunnel | ❌ | ❌ |
|
||||
| Add/Del Phy | ✅ | 🟠 |
|
||||
| Add/Del .1q | ✅ | 🟠 |
|
||||
| Add/Del .1ad | ✅ | 🟠 |
|
||||
| Add/Del QinQ | ✅ | 🟠 |
|
||||
| Add/Del QinAD | ✅ | 🟠 |
|
||||
| Add/Del .1q | ✅ | ✅ |
|
||||
| Add/Del .1ad | ✅ | ✅ |
|
||||
| Add/Del QinQ | ✅ | ✅ |
|
||||
| Add/Del QinAD | ✅ | ✅ |
|
||||
| Add/Del BondEthernet | ✅ | 🟠 |
|
||||
|
||||
Legend: ✅=supported; 🟠=maybe; ❌=infeasible.
|
||||
|
@ -178,9 +178,10 @@ format_nl_object (u8 *s, va_list *args)
|
||||
|
||||
if (rtnl_link_is_vlan (link))
|
||||
{
|
||||
s = format (s, " vlan { id %d proto 0x%04x",
|
||||
rtnl_link_vlan_get_id (link),
|
||||
ntohs (rtnl_link_vlan_get_protocol (link)));
|
||||
s =
|
||||
format (s, " vlan { parent-idx %d id %d proto 0x%04x",
|
||||
rtnl_link_get_link (link), rtnl_link_vlan_get_id (link),
|
||||
ntohs (rtnl_link_vlan_get_protocol (link)));
|
||||
s = format (s, " flags 0x%04x", rtnl_link_vlan_get_flags (link));
|
||||
rtnl_link_vlan_flags2str (rtnl_link_vlan_get_flags (link), buf,
|
||||
sizeof (buf));
|
||||
|
223
lcpng_nl_sync.c
223
lcpng_nl_sync.c
@ -22,6 +22,7 @@
|
||||
#include <vnet/plugin/plugin.h>
|
||||
|
||||
#include <vppinfra/linux/netns.h>
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
#include <plugins/lcpng/lcpng_interface.h>
|
||||
#include <plugins/lcpng/lcpng_netlink.h>
|
||||
@ -52,21 +53,196 @@ lcp_nl_mk_mac_addr (const struct nl_addr *rna, mac_address_t *mac)
|
||||
mac_address_from_bytes (mac, nl_addr_get_binary_addr (rna));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check timestamps on netlink message and interface pair to decide whether
|
||||
* the message should be applied. See the declaration of nl_msg_info_t for
|
||||
* an explanation on why this is necessary.
|
||||
* If timestamps are good (message ts is newer than intf pair ts), return 0.
|
||||
* Else, return -1.
|
||||
*/
|
||||
static int
|
||||
lcp_nl_lip_ts_check (nl_msg_info_t *msg_info, lcp_itf_pair_t *lip)
|
||||
vnet_sw_interface_subid_exists (vnet_main_t *vnm, u32 sw_if_index, u32 id)
|
||||
{
|
||||
if (msg_info->ts > lip->lip_create_ts)
|
||||
return 0;
|
||||
u64 sup_and_sub_key = ((u64) (sw_if_index) << 32) | (u64) id;
|
||||
vnet_interface_main_t *im = &vnm->interface_main;
|
||||
uword *p;
|
||||
|
||||
NL_DBG ("lip_ts_check: Early message for %U", format_lcp_itf_pair, lip);
|
||||
return -1;
|
||||
p = hash_get_mem (im->sw_if_index_by_sup_and_sub, &sup_and_sub_key);
|
||||
if (p)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vnet_sw_interface_get_available_subid (vnet_main_t *vnm, u32 sw_if_index,
|
||||
u32 *id)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 1; i < 4096; i++)
|
||||
{
|
||||
if (!vnet_sw_interface_subid_exists (vnm, sw_if_index, i))
|
||||
{
|
||||
*id = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
*id = -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Returns the LIP for a newly created sub-int pair, or
|
||||
// NULL in case no sub-int could be created.
|
||||
static lcp_itf_pair_t *
|
||||
lcp_nl_link_add_vlan (struct rtnl_link *rl)
|
||||
{
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
int parent_idx, idx;
|
||||
lcp_itf_pair_t *parent_lip, *phy_lip;
|
||||
vnet_sw_interface_t *parent_sw;
|
||||
int vlan;
|
||||
u32 proto;
|
||||
u32 subid;
|
||||
u32 inner_vlan, outer_vlan, flags;
|
||||
u32 phy_sw_if_index, host_sw_if_index;
|
||||
lcp_main_t *lcpm = &lcp_main;
|
||||
u8 old_lcp_auto_subint;
|
||||
|
||||
if (!rtnl_link_is_vlan (rl))
|
||||
return NULL;
|
||||
|
||||
idx = rtnl_link_get_ifindex (rl);
|
||||
parent_idx = rtnl_link_get_link (rl);
|
||||
vlan = rtnl_link_vlan_get_id (rl);
|
||||
proto = rtnl_link_vlan_get_protocol (rl);
|
||||
|
||||
/* Get the LIP of the parent, can be a phy Te3/0/0 or a subint Te3/0/0.1000
|
||||
*/
|
||||
if (!(parent_lip = lcp_itf_pair_get (lcp_itf_pair_find_by_vif (parent_idx))))
|
||||
{
|
||||
NL_WARN ("link_add_vlan: no LCP for parent of %U", format_nl_object, rl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parent_sw = vnet_get_sw_interface (vnm, parent_lip->lip_phy_sw_if_index);
|
||||
if (!parent_sw)
|
||||
{
|
||||
NL_ERROR ("link_add_vlan: Cannot get parent of %U", format_lcp_itf_pair,
|
||||
parent_lip);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the LIP of the phy, ie "phy TenGigabitEthernet3/0/0 host tap1 host-if
|
||||
* e0" */
|
||||
phy_lip =
|
||||
lcp_itf_pair_get (lcp_itf_pair_find_by_phy (parent_sw->sup_sw_if_index));
|
||||
|
||||
if (vnet_sw_interface_is_sub (vnm, parent_lip->lip_phy_sw_if_index))
|
||||
{
|
||||
// QinQ or QinAD
|
||||
inner_vlan = vlan;
|
||||
outer_vlan = parent_sw->sub.eth.outer_vlan_id;
|
||||
if (ntohs (proto) == ETH_P_8021AD)
|
||||
{
|
||||
NL_ERROR ("link_add_vlan: cannot create inner dot1ad: %U",
|
||||
format_nl_object, rl);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inner_vlan = 0;
|
||||
outer_vlan = vlan;
|
||||
}
|
||||
|
||||
// Flags: no_tags(1), one_tag(2), two_tags(4), dot1ad(8), exact_match(16) see
|
||||
// vnet/interface.h
|
||||
flags = 16; // exact-match
|
||||
if ((parent_sw->sub.eth.flags.dot1ad) || (ntohs (proto) == ETH_P_8021AD))
|
||||
flags += 8; // dot1ad
|
||||
if (inner_vlan)
|
||||
flags += 4; // two_tags
|
||||
else
|
||||
flags += 2; // one_tag
|
||||
|
||||
/* Create sub on the phy and on the tap, but avoid triggering sub-int
|
||||
* autocreation if it's enabled.
|
||||
*/
|
||||
old_lcp_auto_subint = lcpm->lcp_auto_subint;
|
||||
lcpm->lcp_auto_subint = 0;
|
||||
|
||||
/* Generate a subid, take the first available one */
|
||||
if (vnet_sw_interface_get_available_subid (vnm, parent_sw->sup_sw_if_index,
|
||||
&subid))
|
||||
{
|
||||
NL_ERROR ("link_add_vlan: cannot find available subid on phy %U",
|
||||
format_vnet_sw_if_index_name, vnm, parent_sw->sup_sw_if_index);
|
||||
lcpm->lcp_auto_subint = old_lcp_auto_subint;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vlib_worker_thread_barrier_sync (vlib_get_main ());
|
||||
NL_NOTICE (
|
||||
"link_add_vlan: creating subid %u outer %u inner %u flags %u on phy %U",
|
||||
subid, outer_vlan, inner_vlan, flags, format_vnet_sw_if_index_name, vnm,
|
||||
parent_sw->sup_sw_if_index);
|
||||
if (vnet_create_sub_interface (parent_sw->sup_sw_if_index, subid, flags,
|
||||
inner_vlan, outer_vlan, &phy_sw_if_index))
|
||||
{
|
||||
NL_ERROR ("link_add_vlan: cannot create sub-int on phy %U flags %u "
|
||||
"inner-dot1q %u dot1%s %u",
|
||||
format_vnet_sw_if_index_name, vnm, parent_sw->sup_sw_if_index,
|
||||
flags, inner_vlan,
|
||||
parent_sw->sub.eth.flags.dot1ad ? "ad" : "q", outer_vlan);
|
||||
vlib_worker_thread_barrier_release (vlib_get_main ());
|
||||
lcpm->lcp_auto_subint = old_lcp_auto_subint;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Try to use the same subid on the TAP, generate a unique one otherwise. */
|
||||
if (vnet_sw_interface_subid_exists (vnm, phy_lip->lip_host_sw_if_index,
|
||||
subid) &&
|
||||
vnet_sw_interface_get_available_subid (
|
||||
vnm, phy_lip->lip_host_sw_if_index, &subid))
|
||||
{
|
||||
NL_ERROR ("link_add_vlan: cannot find available subid on host %U",
|
||||
format_vnet_sw_if_index_name, vnm,
|
||||
phy_lip->lip_host_sw_if_index);
|
||||
vlib_worker_thread_barrier_release (vlib_get_main ());
|
||||
lcpm->lcp_auto_subint = old_lcp_auto_subint;
|
||||
return NULL;
|
||||
}
|
||||
NL_NOTICE ("link_add_vlan: creating subid %u outer %u inner %u flags %u on "
|
||||
"host %U phy %U",
|
||||
subid, outer_vlan, inner_vlan, flags,
|
||||
format_vnet_sw_if_index_name, vnm,
|
||||
parent_lip->lip_host_sw_if_index, format_vnet_sw_if_index_name,
|
||||
vnm, phy_lip->lip_host_sw_if_index);
|
||||
|
||||
if (vnet_create_sub_interface (phy_lip->lip_host_sw_if_index, subid, flags,
|
||||
inner_vlan, outer_vlan, &host_sw_if_index))
|
||||
{
|
||||
NL_ERROR ("link_add_vlan: cannot create sub-int on host %U flags %u "
|
||||
"inner-dot1q %u dot1%s %u",
|
||||
format_vnet_sw_if_index_name, vnm,
|
||||
phy_lip->lip_host_sw_if_index, flags, inner_vlan,
|
||||
parent_sw->sub.eth.flags.dot1ad ? "ad" : "q", outer_vlan);
|
||||
vlib_worker_thread_barrier_release (vlib_get_main ());
|
||||
lcpm->lcp_auto_subint = old_lcp_auto_subint;
|
||||
return NULL;
|
||||
}
|
||||
NL_NOTICE ("link_add_vlan: Creating LCP for host %U phy %U name %s idx %d",
|
||||
format_vnet_sw_if_index_name, vnm, host_sw_if_index,
|
||||
format_vnet_sw_if_index_name, vnm, phy_sw_if_index,
|
||||
rtnl_link_get_name (rl), idx);
|
||||
vlib_worker_thread_barrier_release (vlib_get_main ());
|
||||
lcpm->lcp_auto_subint = old_lcp_auto_subint;
|
||||
|
||||
u8 *if_namev = 0;
|
||||
char *if_name;
|
||||
if_name = rtnl_link_get_name (rl);
|
||||
vec_validate_init_c_string (if_namev, if_name, strnlen (if_name, IFNAMSIZ));
|
||||
lcp_itf_pair_add (host_sw_if_index, phy_sw_if_index, if_namev, idx,
|
||||
phy_lip->lip_host_type, phy_lip->lip_namespace);
|
||||
vec_free (if_namev);
|
||||
|
||||
// If all went well, we just created a new LIP and added it to the index --
|
||||
// so return that new (sub-interface) LIP to the caller.
|
||||
return lcp_itf_pair_get (lcp_itf_pair_find_by_phy (phy_sw_if_index));
|
||||
}
|
||||
|
||||
void
|
||||
@ -169,28 +345,17 @@ lcp_nl_link_add (struct rtnl_link *rl, void *ctx)
|
||||
|
||||
NL_DBG ("link_add: netlink %U", format_nl_object, rl);
|
||||
|
||||
/* For NEWLINK messages, if this interface doesn't have a LIP, it
|
||||
* may be a request to create a sub-int; so we call add_vlan()
|
||||
* to create it and pass its new LIP so we can finish the request.
|
||||
*/
|
||||
if (!(lip = lcp_itf_pair_get (
|
||||
lcp_itf_pair_find_by_vif (rtnl_link_get_ifindex (rl)))))
|
||||
{
|
||||
NL_WARN ("link_add: no LCP for %U (see TODO in code)", format_nl_object,
|
||||
rl);
|
||||
// TODO(pim) -- here's where auto-creation of sub-int's comes into play
|
||||
// if this is a nelink vlan interface, its parent may have a LIP, and if
|
||||
// so, we can auto-create in VPP.
|
||||
return;
|
||||
if (!(lip = lcp_nl_link_add_vlan (rl)))
|
||||
return;
|
||||
}
|
||||
|
||||
if (lcp_nl_lip_ts_check ((nl_msg_info_t *) ctx, lip))
|
||||
return;
|
||||
|
||||
// 0 == unknown; 2 == down; 6 == up; TODO(pim) figure out operstate values
|
||||
/*
|
||||
if (2 == rtnl_link_get_operstate(rl)) {
|
||||
NL_WARN ("link_add: ignoring %U (wrong operstate)", format_nl_object,
|
||||
rl); return;
|
||||
}
|
||||
*/
|
||||
|
||||
admin_state = (IFF_UP & rtnl_link_get_flags (rl));
|
||||
vlib_worker_thread_barrier_sync (vlib_get_main ());
|
||||
if (admin_state)
|
||||
|
Reference in New Issue
Block a user