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.
621 lines
18 KiB
C
621 lines
18 KiB
C
/* Hey Emacs use -*- mode: C -*- */
|
|
/*
|
|
* Copyright 2021 Cisco and/or its affiliates.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <sys/socket.h>
|
|
#include <linux/if.h>
|
|
|
|
#include <vnet/vnet.h>
|
|
#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>
|
|
|
|
#include <vnet/fib/fib_table.h>
|
|
#include <vnet/mfib/mfib_table.h>
|
|
#include <vnet/ip/ip6_ll_table.h>
|
|
#include <vnet/ip-neighbor/ip_neighbor.h>
|
|
#include <vnet/ip/ip6_link.h>
|
|
|
|
#ifndef NUD_VALID
|
|
#define NUD_VALID \
|
|
(NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE | NUD_PROBE | NUD_STALE | \
|
|
NUD_DELAY)
|
|
#endif
|
|
|
|
static void
|
|
lcp_nl_mk_ip_addr (const struct nl_addr *rna, ip_address_t *ia)
|
|
{
|
|
ip_address_reset (ia);
|
|
ip_address_set (ia, nl_addr_get_binary_addr (rna),
|
|
nl_addr_get_family (rna) == AF_INET6 ? AF_IP6 : AF_IP4);
|
|
}
|
|
|
|
static void
|
|
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));
|
|
}
|
|
|
|
static int
|
|
vnet_sw_interface_subid_exists (vnet_main_t *vnm, u32 sw_if_index, u32 id)
|
|
{
|
|
u64 sup_and_sub_key = ((u64) (sw_if_index) << 32) | (u64) id;
|
|
vnet_interface_main_t *im = &vnm->interface_main;
|
|
uword *p;
|
|
|
|
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
|
|
lcp_nl_link_del (struct rtnl_link *rl)
|
|
{
|
|
lcp_itf_pair_t *lip;
|
|
|
|
NL_DBG ("link_del: netlink %U", format_nl_object, rl);
|
|
|
|
if (!(lip = lcp_itf_pair_get (
|
|
lcp_itf_pair_find_by_vif (rtnl_link_get_ifindex (rl)))))
|
|
{
|
|
NL_WARN ("link_del: no LCP for %U ", format_nl_object, rl);
|
|
return;
|
|
}
|
|
|
|
NL_NOTICE ("link_del: Removing %U", format_lcp_itf_pair, lip);
|
|
vlib_worker_thread_barrier_sync (vlib_get_main ());
|
|
lcp_itf_pair_delete (lip->lip_phy_sw_if_index);
|
|
vlib_worker_thread_barrier_release (vlib_get_main ());
|
|
|
|
if (rtnl_link_is_vlan (rl))
|
|
{
|
|
NL_NOTICE ("link_del: Removing subint %U", format_vnet_sw_if_index_name,
|
|
vnet_get_main (), lip->lip_phy_sw_if_index);
|
|
vlib_worker_thread_barrier_sync (vlib_get_main ());
|
|
vnet_delete_sub_interface (lip->lip_phy_sw_if_index);
|
|
vnet_delete_sub_interface (lip->lip_host_sw_if_index);
|
|
vlib_worker_thread_barrier_release (vlib_get_main ());
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
lcp_nl_link_set_mtu (struct rtnl_link *rl, lcp_itf_pair_t *lip)
|
|
{
|
|
vnet_main_t *vnm = vnet_get_main ();
|
|
u32 mtu;
|
|
vnet_sw_interface_t *sw;
|
|
vnet_hw_interface_t *hw;
|
|
|
|
mtu = rtnl_link_get_mtu (rl);
|
|
if (!mtu)
|
|
return;
|
|
|
|
sw = vnet_get_sw_interface (vnm, lip->lip_phy_sw_if_index);
|
|
hw = vnet_get_sup_hw_interface (vnm, lip->lip_phy_sw_if_index);
|
|
if (!sw || !hw)
|
|
return;
|
|
|
|
/* Set the MTU on the TAP and sw */
|
|
vnet_sw_interface_set_mtu (vnm, lip->lip_host_sw_if_index, mtu);
|
|
vnet_sw_interface_set_mtu (vnm, lip->lip_phy_sw_if_index, mtu);
|
|
}
|
|
|
|
static void
|
|
lcp_nl_link_set_lladdr (struct rtnl_link *rl, lcp_itf_pair_t *lip)
|
|
{
|
|
vnet_main_t *vnm = vnet_get_main ();
|
|
struct nl_addr *mac_addr;
|
|
vnet_sw_interface_t *sw;
|
|
vnet_hw_interface_t *hw;
|
|
void *mac_addr_bytes;
|
|
|
|
mac_addr = rtnl_link_get_addr (rl);
|
|
if (!mac_addr || (nl_addr_get_family (mac_addr) != AF_LLC))
|
|
return;
|
|
|
|
sw = vnet_get_sw_interface (vnm, lip->lip_phy_sw_if_index);
|
|
hw = vnet_get_sup_hw_interface (vnm, lip->lip_phy_sw_if_index);
|
|
if (!sw || !hw)
|
|
return;
|
|
|
|
/* can only change address on hw interface */
|
|
if (sw->sw_if_index != sw->sup_sw_if_index)
|
|
return;
|
|
/* can only change if there's an address present */
|
|
if (!vec_len (hw->hw_address))
|
|
return;
|
|
|
|
mac_addr_bytes = nl_addr_get_binary_addr (mac_addr);
|
|
if (clib_memcmp (mac_addr_bytes, hw->hw_address, nl_addr_get_len (mac_addr)))
|
|
vnet_hw_interface_change_mac_address (vnm, hw->hw_if_index,
|
|
mac_addr_bytes);
|
|
|
|
/* mcast adjacencies need to be updated */
|
|
vnet_update_adjacency_for_sw_interface (vnm, lip->lip_phy_sw_if_index,
|
|
lip->lip_phy_adjs.adj_index[AF_IP4]);
|
|
vnet_update_adjacency_for_sw_interface (vnm, lip->lip_phy_sw_if_index,
|
|
lip->lip_phy_adjs.adj_index[AF_IP6]);
|
|
}
|
|
|
|
void
|
|
lcp_nl_link_add (struct rtnl_link *rl, void *ctx)
|
|
{
|
|
vnet_main_t *vnm = vnet_get_main ();
|
|
lcp_itf_pair_t *lip;
|
|
int admin_state;
|
|
|
|
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)))))
|
|
{
|
|
if (!(lip = lcp_nl_link_add_vlan (rl)))
|
|
return;
|
|
}
|
|
|
|
admin_state = (IFF_UP & rtnl_link_get_flags (rl));
|
|
vlib_worker_thread_barrier_sync (vlib_get_main ());
|
|
if (admin_state)
|
|
{
|
|
vnet_sw_interface_admin_up (vnm, lip->lip_host_sw_if_index);
|
|
vnet_sw_interface_admin_up (vnm, lip->lip_phy_sw_if_index);
|
|
}
|
|
else
|
|
{
|
|
vnet_sw_interface_admin_down (vnm, lip->lip_phy_sw_if_index);
|
|
vnet_sw_interface_admin_down (vnm, lip->lip_host_sw_if_index);
|
|
}
|
|
|
|
lcp_nl_link_set_mtu (rl, lip);
|
|
lcp_nl_link_set_lladdr (rl, lip);
|
|
vlib_worker_thread_barrier_release (vlib_get_main ());
|
|
|
|
NL_NOTICE ("link_add: %U admin %s", format_lcp_itf_pair, lip,
|
|
admin_state ? "up" : "down");
|
|
|
|
return;
|
|
}
|
|
|
|
static const mfib_prefix_t ip4_specials[] = {
|
|
/* ALL prefixes are in network order */
|
|
{
|
|
/* (*,224.0.0.0)/24 - all local subnet */
|
|
.fp_grp_addr = {
|
|
.ip4.data_u32 = 0x000000e0,
|
|
},
|
|
.fp_len = 24,
|
|
.fp_proto = FIB_PROTOCOL_IP4,
|
|
},
|
|
};
|
|
|
|
static void
|
|
lcp_nl_ip4_mroutes_add_del (u32 sw_if_index, u8 is_add)
|
|
{
|
|
const fib_route_path_t path = {
|
|
.frp_proto = DPO_PROTO_IP4,
|
|
.frp_addr = zero_addr,
|
|
.frp_sw_if_index = sw_if_index,
|
|
.frp_fib_index = ~0,
|
|
.frp_weight = 1,
|
|
.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT,
|
|
};
|
|
u32 mfib_index;
|
|
int ii;
|
|
|
|
mfib_index =
|
|
mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
|
|
|
|
for (ii = 0; ii < ARRAY_LEN (ip4_specials); ii++)
|
|
{
|
|
if (is_add)
|
|
{
|
|
mfib_table_entry_path_update (mfib_index, &ip4_specials[ii],
|
|
MFIB_SOURCE_PLUGIN_LOW, &path);
|
|
}
|
|
else
|
|
{
|
|
mfib_table_entry_path_remove (mfib_index, &ip4_specials[ii],
|
|
MFIB_SOURCE_PLUGIN_LOW, &path);
|
|
}
|
|
}
|
|
}
|
|
|
|
static const mfib_prefix_t ip6_specials[] = {
|
|
/* ALL prefixes are in network order */
|
|
{
|
|
/* (*,ff00::)/8 - all local subnet */
|
|
.fp_grp_addr = {
|
|
.ip6.as_u64[0] = 0x00000000000000ff,
|
|
},
|
|
.fp_len = 8,
|
|
.fp_proto = FIB_PROTOCOL_IP6,
|
|
},
|
|
};
|
|
|
|
static void
|
|
lcp_nl_ip6_mroutes_add_del (u32 sw_if_index, u8 is_add)
|
|
{
|
|
const fib_route_path_t path = {
|
|
.frp_proto = DPO_PROTO_IP6,
|
|
.frp_addr = zero_addr,
|
|
.frp_sw_if_index = sw_if_index,
|
|
.frp_fib_index = ~0,
|
|
.frp_weight = 1,
|
|
.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT,
|
|
};
|
|
u32 mfib_index;
|
|
int ii;
|
|
|
|
mfib_index =
|
|
mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
|
|
|
|
for (ii = 0; ii < ARRAY_LEN (ip6_specials); ii++)
|
|
{
|
|
if (is_add)
|
|
{
|
|
mfib_table_entry_path_update (mfib_index, &ip6_specials[ii],
|
|
MFIB_SOURCE_PLUGIN_LOW, &path);
|
|
}
|
|
else
|
|
{
|
|
mfib_table_entry_path_remove (mfib_index, &ip6_specials[ii],
|
|
MFIB_SOURCE_PLUGIN_LOW, &path);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
lcp_nl_addr_add_del (struct rtnl_addr *ra, int is_del)
|
|
{
|
|
lcp_itf_pair_t *lip;
|
|
ip_address_t nh;
|
|
|
|
NL_DBG ("addr_%s: netlink %U", is_del ? "del" : "add", format_nl_object, ra);
|
|
|
|
if (!(lip = lcp_itf_pair_get (
|
|
lcp_itf_pair_find_by_vif (rtnl_addr_get_ifindex (ra)))))
|
|
{
|
|
NL_WARN ("addr_%s: no LCP for %U ", is_del ? "del" : "add",
|
|
format_nl_object, ra);
|
|
return;
|
|
}
|
|
|
|
lcp_nl_mk_ip_addr (rtnl_addr_get_local (ra), &nh);
|
|
|
|
vlib_worker_thread_barrier_sync (vlib_get_main ());
|
|
if (AF_IP4 == ip_addr_version (&nh))
|
|
{
|
|
ip4_add_del_interface_address (
|
|
vlib_get_main (), lip->lip_phy_sw_if_index, &ip_addr_v4 (&nh),
|
|
rtnl_addr_get_prefixlen (ra), is_del);
|
|
lcp_nl_ip4_mroutes_add_del (lip->lip_phy_sw_if_index, !is_del);
|
|
}
|
|
else if (AF_IP6 == ip_addr_version (&nh))
|
|
{
|
|
if (ip6_address_is_link_local_unicast (&ip_addr_v6 (&nh)))
|
|
if (is_del)
|
|
ip6_link_disable (lip->lip_phy_sw_if_index);
|
|
else
|
|
{
|
|
ip6_link_enable (lip->lip_phy_sw_if_index, NULL);
|
|
ip6_link_set_local_address (lip->lip_phy_sw_if_index,
|
|
&ip_addr_v6 (&nh));
|
|
}
|
|
else
|
|
ip6_add_del_interface_address (
|
|
vlib_get_main (), lip->lip_phy_sw_if_index, &ip_addr_v6 (&nh),
|
|
rtnl_addr_get_prefixlen (ra), is_del);
|
|
lcp_nl_ip6_mroutes_add_del (lip->lip_phy_sw_if_index, !is_del);
|
|
}
|
|
vlib_worker_thread_barrier_release (vlib_get_main ());
|
|
|
|
NL_NOTICE ("addr_%s %U/%d iface %U", is_del ? "del: Deleted" : "add: Added",
|
|
format_ip_address, &nh, rtnl_addr_get_prefixlen (ra),
|
|
format_vnet_sw_if_index_name, vnet_get_main (),
|
|
lip->lip_phy_sw_if_index);
|
|
}
|
|
|
|
void
|
|
lcp_nl_addr_add (struct rtnl_addr *ra)
|
|
{
|
|
lcp_nl_addr_add_del (ra, 0);
|
|
}
|
|
|
|
void
|
|
lcp_nl_addr_del (struct rtnl_addr *ra)
|
|
{
|
|
lcp_nl_addr_add_del (ra, 1);
|
|
}
|
|
|
|
void
|
|
lcp_nl_neigh_add (struct rtnl_neigh *rn)
|
|
{
|
|
lcp_itf_pair_t *lip;
|
|
struct nl_addr *ll;
|
|
ip_address_t nh;
|
|
int state;
|
|
|
|
NL_DBG ("neigh_add: netlink %U", format_nl_object, rn);
|
|
|
|
if (!(lip = lcp_itf_pair_get (
|
|
lcp_itf_pair_find_by_vif (rtnl_neigh_get_ifindex (rn)))))
|
|
{
|
|
NL_WARN ("neigh_add: no LCP for %U ", format_nl_object, rn);
|
|
return;
|
|
}
|
|
|
|
lcp_nl_mk_ip_addr (rtnl_neigh_get_dst (rn), &nh);
|
|
ll = rtnl_neigh_get_lladdr (rn);
|
|
state = rtnl_neigh_get_state (rn);
|
|
|
|
if (ll && (state & NUD_VALID))
|
|
{
|
|
mac_address_t mac;
|
|
ip_neighbor_flags_t flags;
|
|
int rv;
|
|
|
|
lcp_nl_mk_mac_addr (ll, &mac);
|
|
|
|
if (state & (NUD_NOARP | NUD_PERMANENT))
|
|
flags = IP_NEIGHBOR_FLAG_STATIC;
|
|
else
|
|
flags = IP_NEIGHBOR_FLAG_DYNAMIC;
|
|
|
|
vlib_worker_thread_barrier_sync (vlib_get_main ());
|
|
rv = ip_neighbor_add (&nh, &mac, lip->lip_phy_sw_if_index, flags, NULL);
|
|
vlib_worker_thread_barrier_release (vlib_get_main ());
|
|
|
|
if (rv)
|
|
{
|
|
NL_ERROR ("neigh_add: Failed %U lladdr %U iface %U",
|
|
format_ip_address, &nh, format_mac_address, &mac,
|
|
format_vnet_sw_if_index_name, vnet_get_main (),
|
|
lip->lip_phy_sw_if_index);
|
|
}
|
|
else
|
|
{
|
|
NL_NOTICE ("neigh_add: Added %U lladdr %U iface %U",
|
|
format_ip_address, &nh, format_mac_address, &mac,
|
|
format_vnet_sw_if_index_name, vnet_get_main (),
|
|
lip->lip_phy_sw_if_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
lcp_nl_neigh_del (struct rtnl_neigh *rn)
|
|
{
|
|
ip_address_t nh;
|
|
int rv;
|
|
NL_DBG ("neigh_del: netlink %U", format_nl_object, rn);
|
|
|
|
lcp_itf_pair_t *lip;
|
|
if (!(lip = lcp_itf_pair_get (
|
|
lcp_itf_pair_find_by_vif (rtnl_neigh_get_ifindex (rn)))))
|
|
{
|
|
NL_WARN ("neigh_del: no LCP for %U ", format_nl_object, rn);
|
|
return;
|
|
}
|
|
|
|
lcp_nl_mk_ip_addr (rtnl_neigh_get_dst (rn), &nh);
|
|
vlib_worker_thread_barrier_sync (vlib_get_main ());
|
|
rv = ip_neighbor_del (&nh, lip->lip_phy_sw_if_index);
|
|
vlib_worker_thread_barrier_release (vlib_get_main ());
|
|
|
|
if (rv == 0 || rv == VNET_API_ERROR_NO_SUCH_ENTRY)
|
|
{
|
|
NL_NOTICE ("neigh_del: Deleted %U iface %U", format_ip_address, &nh,
|
|
format_vnet_sw_if_index_name, vnet_get_main (),
|
|
lip->lip_phy_sw_if_index);
|
|
}
|
|
else
|
|
{
|
|
NL_ERROR ("neigh_del: Failed %U iface %U", format_ip_address, &nh,
|
|
format_vnet_sw_if_index_name, vnet_get_main (),
|
|
lip->lip_phy_sw_if_index);
|
|
}
|
|
}
|