Sync IPv4 and IPv6 addresses from VPP to LCP
There are three ways in which IP addresses will want to be copied from VPP into the companion Linux devices: 1) set interface ip address ... adds an IPv4 or IPv6 address - this is handled by lcp_itf_ip[46]_add_del_interface_addr() which is a callback installed in lcp_itf_pair_init() 2) set interface ip address del ... removes them - also handled by lcp_itf_ip[46]_add_del_interface_addr() but curiously there is no upstream vnet_netlink_del_ip[46]_addr() so I wrote them inline here - I will try to get them upstreamed, as they appear to be obvious companions in vnet/device/netlink.h 3) Upon LIP creation, it could be that there are L3 addresses already on the VPP interface. If so, set them with lcp_itf_set_interface_addr() This means that now, at any time a new LIP is created, its state from VPP is fully copied over (MTU, Link state, IPv4/IPv6 addresses)! At runtime, new addresses can be set/removed as well.
This commit is contained in:
@ -28,6 +28,7 @@ add_vpp_library(lcpng
|
||||
SOURCES
|
||||
lcpng_interface.c
|
||||
lcpng_adj.c
|
||||
lcpng_if_sync.c
|
||||
lcpng.c
|
||||
|
||||
LINK_LIBRARIES
|
||||
@ -44,7 +45,6 @@ add_vpp_plugin(lcpng_if
|
||||
lcpng_if_api.c
|
||||
lcpng_if_cli.c
|
||||
lcpng_if_node.c
|
||||
lcpng_if_sync.c
|
||||
|
||||
API_FILES
|
||||
lcpng_if.api
|
||||
|
201
lcpng_if_sync.c
201
lcpng_if_sync.c
@ -31,6 +31,8 @@
|
||||
#include <vnet/tcp/tcp.h>
|
||||
#include <vnet/devices/tap/tap.h>
|
||||
#include <vnet/devices/netlink.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
/* walk function to copy forward all sw interface link state flags into
|
||||
* their counterpart LIP link state.
|
||||
@ -54,6 +56,15 @@ lcp_itf_pair_walk_sync_state_cb (index_t lipi, void *ctx)
|
||||
lip, phy->flags);
|
||||
lcp_itf_set_link_state (lip, (phy->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP));
|
||||
|
||||
/* Linux will remove IPv6 addresses on children when the master state
|
||||
* goes down, so we ensure all IPv4/IPv6 addresses are set when the phy
|
||||
* comes back up.
|
||||
*/
|
||||
if (phy->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
|
||||
{
|
||||
lcp_itf_set_interface_addr (lip);
|
||||
}
|
||||
|
||||
return WALK_CONTINUE;
|
||||
}
|
||||
|
||||
@ -83,7 +94,7 @@ lcp_itf_admin_state_change (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
|
||||
format_vnet_sw_if_index_name, vnm, hi->hw_if_index,
|
||||
format_vnet_sw_if_index_name, vnm, si->sw_if_index, flags);
|
||||
tap_set_carrier (si->hw_if_index,
|
||||
(hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP));
|
||||
(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP));
|
||||
|
||||
// When Linux changes link on a master interface, all of its children also
|
||||
// change. This is not true in VPP, so we are forced to undo that change by
|
||||
@ -122,3 +133,191 @@ lcp_itf_mtu_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags)
|
||||
}
|
||||
|
||||
VNET_SW_INTERFACE_MTU_CHANGE_FUNCTION (lcp_itf_mtu_change);
|
||||
|
||||
// TODO(pim): submit upstream to vnet/devices/netlink.[ch]
|
||||
typedef struct
|
||||
{
|
||||
u8 *data;
|
||||
} vnet_netlink_msg_t;
|
||||
|
||||
static void
|
||||
vnet_netlink_msg_init (vnet_netlink_msg_t *m, u16 type, u16 flags,
|
||||
void *msg_data, int msg_len)
|
||||
{
|
||||
struct nlmsghdr *nh;
|
||||
u8 *p;
|
||||
clib_memset (m, 0, sizeof (vnet_netlink_msg_t));
|
||||
vec_add2 (m->data, p, NLMSG_SPACE (msg_len));
|
||||
ASSERT (m->data == p);
|
||||
|
||||
nh = (struct nlmsghdr *) p;
|
||||
nh->nlmsg_flags = flags | NLM_F_ACK;
|
||||
nh->nlmsg_type = type;
|
||||
clib_memcpy (m->data + sizeof (struct nlmsghdr), msg_data, msg_len);
|
||||
}
|
||||
|
||||
static void
|
||||
vnet_netlink_msg_add_rtattr (vnet_netlink_msg_t *m, u16 rta_type,
|
||||
void *rta_data, int rta_data_len)
|
||||
{
|
||||
struct rtattr *rta;
|
||||
u8 *p;
|
||||
|
||||
vec_add2 (m->data, p, RTA_SPACE (rta_data_len));
|
||||
rta = (struct rtattr *) p;
|
||||
rta->rta_type = rta_type;
|
||||
rta->rta_len = RTA_LENGTH (rta_data_len);
|
||||
clib_memcpy (RTA_DATA (rta), rta_data, rta_data_len);
|
||||
}
|
||||
static clib_error_t *
|
||||
vnet_netlink_msg_send (vnet_netlink_msg_t *m, vnet_netlink_msg_t **replies)
|
||||
{
|
||||
clib_error_t *err = 0;
|
||||
struct sockaddr_nl ra = { 0 };
|
||||
int len, sock;
|
||||
struct nlmsghdr *nh = (struct nlmsghdr *) m->data;
|
||||
nh->nlmsg_len = vec_len (m->data);
|
||||
char buf[4096];
|
||||
|
||||
if ((sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
|
||||
return clib_error_return_unix (0, "socket(AF_NETLINK)");
|
||||
|
||||
ra.nl_family = AF_NETLINK;
|
||||
ra.nl_pid = 0;
|
||||
|
||||
if ((bind (sock, (struct sockaddr *) &ra, sizeof (ra))) == -1)
|
||||
{
|
||||
err = clib_error_return_unix (0, "bind");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((send (sock, m->data, vec_len (m->data), 0)) == -1)
|
||||
err = clib_error_return_unix (0, "send");
|
||||
|
||||
if ((len = recv (sock, buf, sizeof (buf), 0)) == -1)
|
||||
err = clib_error_return_unix (0, "recv");
|
||||
for (nh = (struct nlmsghdr *) buf; NLMSG_OK (nh, len);
|
||||
nh = NLMSG_NEXT (nh, len))
|
||||
{
|
||||
if (nh->nlmsg_type == NLMSG_DONE)
|
||||
goto done;
|
||||
|
||||
if (nh->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
struct nlmsgerr *e = (struct nlmsgerr *) NLMSG_DATA (nh);
|
||||
if (e->error)
|
||||
err = clib_error_return (0, "netlink error %d", e->error);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (replies)
|
||||
{
|
||||
vnet_netlink_msg_t msg = { NULL };
|
||||
u8 *p;
|
||||
vec_add2 (msg.data, p, nh->nlmsg_len);
|
||||
clib_memcpy (p, nh, nh->nlmsg_len);
|
||||
vec_add1 (*replies, msg);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
close (sock);
|
||||
vec_free (m->data);
|
||||
return err;
|
||||
}
|
||||
|
||||
clib_error_t *
|
||||
vnet_netlink_del_ip4_addr (int ifindex, void *addr, int pfx_len)
|
||||
{
|
||||
vnet_netlink_msg_t m;
|
||||
struct ifaddrmsg ifa = { 0 };
|
||||
clib_error_t *err = 0;
|
||||
|
||||
ifa.ifa_family = AF_INET;
|
||||
ifa.ifa_prefixlen = pfx_len;
|
||||
ifa.ifa_index = ifindex;
|
||||
|
||||
vnet_netlink_msg_init (&m, RTM_DELADDR, NLM_F_REQUEST, &ifa,
|
||||
sizeof (struct ifaddrmsg));
|
||||
|
||||
vnet_netlink_msg_add_rtattr (&m, IFA_LOCAL, addr, 4);
|
||||
vnet_netlink_msg_add_rtattr (&m, IFA_ADDRESS, addr, 4);
|
||||
err = vnet_netlink_msg_send (&m, NULL);
|
||||
if (err)
|
||||
err = clib_error_return (0, "del ip4 addr %U", format_clib_error, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
clib_error_t *
|
||||
vnet_netlink_del_ip6_addr (int ifindex, void *addr, int pfx_len)
|
||||
{
|
||||
vnet_netlink_msg_t m;
|
||||
struct ifaddrmsg ifa = { 0 };
|
||||
clib_error_t *err = 0;
|
||||
|
||||
ifa.ifa_family = AF_INET6;
|
||||
ifa.ifa_prefixlen = pfx_len;
|
||||
ifa.ifa_index = ifindex;
|
||||
|
||||
vnet_netlink_msg_init (&m, RTM_DELADDR, NLM_F_REQUEST, &ifa,
|
||||
sizeof (struct ifaddrmsg));
|
||||
|
||||
vnet_netlink_msg_add_rtattr (&m, IFA_LOCAL, addr, 16);
|
||||
vnet_netlink_msg_add_rtattr (&m, IFA_ADDRESS, addr, 16);
|
||||
err = vnet_netlink_msg_send (&m, NULL);
|
||||
if (err)
|
||||
err = clib_error_return (0, "del ip6 addr %U", format_clib_error, err);
|
||||
return err;
|
||||
}
|
||||
// TODO(pim) move previous block upstream
|
||||
|
||||
void
|
||||
lcp_itf_ip4_add_del_interface_addr (ip4_main_t *im, uword opaque,
|
||||
u32 sw_if_index, ip4_address_t *address,
|
||||
u32 address_length, u32 if_address_index,
|
||||
u32 is_del)
|
||||
{
|
||||
const lcp_itf_pair_t *lip;
|
||||
|
||||
LCP_ITF_PAIR_DBG ("ip4_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
|
||||
format_vnet_sw_if_index_name, vnet_get_main (),
|
||||
sw_if_index, format_ip4_address, address, address_length);
|
||||
|
||||
lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
|
||||
if (!lip)
|
||||
return;
|
||||
LCP_ITF_PAIR_ERR ("ip4_addr_%s: %U ip4 %U/%u", is_del ? "del" : "add",
|
||||
format_lcp_itf_pair, lip, format_ip4_address, address,
|
||||
address_length);
|
||||
|
||||
if (is_del)
|
||||
vnet_netlink_del_ip4_addr (lip->lip_vif_index, address, address_length);
|
||||
else
|
||||
vnet_netlink_add_ip4_addr (lip->lip_vif_index, address, address_length);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
lcp_itf_ip6_add_del_interface_addr (ip6_main_t *im, uword opaque,
|
||||
u32 sw_if_index, ip6_address_t *address,
|
||||
u32 address_length, u32 if_address_index,
|
||||
u32 is_del)
|
||||
{
|
||||
const lcp_itf_pair_t *lip;
|
||||
|
||||
LCP_ITF_PAIR_DBG ("ip6_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
|
||||
format_vnet_sw_if_index_name, vnet_get_main (),
|
||||
sw_if_index, format_ip6_address, address, address_length);
|
||||
|
||||
lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
|
||||
if (!lip)
|
||||
return;
|
||||
LCP_ITF_PAIR_ERR ("ip6_addr_%s: %U ip4 %U/%u", is_del ? "del" : "add",
|
||||
format_lcp_itf_pair, lip, format_ip6_address, address,
|
||||
address_length);
|
||||
if (is_del)
|
||||
vnet_netlink_del_ip6_addr (lip->lip_vif_index, address, address_length);
|
||||
else
|
||||
vnet_netlink_add_ip6_addr (lip->lip_vif_index, address, address_length);
|
||||
}
|
||||
|
@ -618,6 +618,36 @@ lcp_itf_set_link_state (const lcp_itf_pair_t *lip, u8 state)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
lcp_itf_set_interface_addr (const lcp_itf_pair_t *lip)
|
||||
{
|
||||
ip4_main_t *im4 = &ip4_main;
|
||||
ip6_main_t *im6 = &ip6_main;
|
||||
ip_lookup_main_t *lm4 = &im4->lookup_main;
|
||||
ip_lookup_main_t *lm6 = &im6->lookup_main;
|
||||
ip_interface_address_t *ia = 0;
|
||||
|
||||
/* Display any IP4 addressing info */
|
||||
foreach_ip_interface_address (
|
||||
lm4, ia, lip->lip_phy_sw_if_index, 1 /* honor unnumbered */, ({
|
||||
ip4_address_t *r4 = ip_interface_address_get_address (lm4, ia);
|
||||
LCP_ITF_PAIR_ERR ("set_interface_addr: %U add ip4 %U/%d",
|
||||
format_lcp_itf_pair, lip, format_ip4_address, r4,
|
||||
ia->address_length);
|
||||
vnet_netlink_add_ip4_addr (lip->lip_vif_index, r4, ia->address_length);
|
||||
}));
|
||||
|
||||
/* Display any IP6 addressing info */
|
||||
foreach_ip_interface_address (
|
||||
lm6, ia, lip->lip_phy_sw_if_index, 1 /* honor unnumbered */, ({
|
||||
ip6_address_t *r6 = ip_interface_address_get_address (lm6, ia);
|
||||
LCP_ITF_PAIR_ERR ("set_interface_addr: %U add ip6 %U/%d",
|
||||
format_lcp_itf_pair, lip, format_ip6_address, r6,
|
||||
ia->address_length);
|
||||
vnet_netlink_add_ip6_addr (lip->lip_vif_index, r6, ia->address_length);
|
||||
}));
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 hw_if_index;
|
||||
@ -927,7 +957,7 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
|
||||
host_if_type, ns);
|
||||
|
||||
/*
|
||||
* Copy the link state from VPP inon the host side.
|
||||
* 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.
|
||||
*/
|
||||
@ -939,6 +969,9 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
|
||||
format_lcp_itf_pair, lip);
|
||||
lcp_itf_set_link_state (lip, sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP);
|
||||
|
||||
/* Copy L3 addresses from VPP into the host side, if any.
|
||||
*/
|
||||
lcp_itf_set_interface_addr (lip);
|
||||
|
||||
if (host_sw_if_indexp)
|
||||
*host_sw_if_indexp = host_sw_if_index;
|
||||
@ -1051,7 +1084,13 @@ VNET_SW_INTERFACE_ADD_DEL_FUNCTION (lcp_itf_phy_add);
|
||||
static clib_error_t *
|
||||
lcp_itf_pair_init (vlib_main_t *vm)
|
||||
{
|
||||
ip4_main_t *im4 = &ip4_main;
|
||||
ip6_main_t *im6 = &ip6_main;
|
||||
ip4_add_del_interface_address_callback_t cb4;
|
||||
ip6_add_del_interface_address_callback_t cb6;
|
||||
|
||||
vlib_punt_hdl_t punt_hdl = vlib_punt_client_register("linux-cp");
|
||||
lcp_itf_pair_logger = vlib_log_register_class ("linux-cp", "if");
|
||||
|
||||
/* punt IKE */
|
||||
vlib_punt_register(punt_hdl, ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0],
|
||||
@ -1063,7 +1102,13 @@ lcp_itf_pair_init (vlib_main_t *vm)
|
||||
tcp_punt_unknown (vm, 0, 1);
|
||||
tcp_punt_unknown (vm, 1, 1);
|
||||
|
||||
lcp_itf_pair_logger = vlib_log_register_class("linux-cp", "if");
|
||||
cb4.function = lcp_itf_ip4_add_del_interface_addr;
|
||||
cb4.function_opaque = 0;
|
||||
vec_add1 (im4->add_del_interface_address_callbacks, cb4);
|
||||
|
||||
cb6.function = lcp_itf_ip6_add_del_interface_addr;
|
||||
cb6.function_opaque = 0;
|
||||
vec_add1 (im6->add_del_interface_address_callbacks, cb6);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include <vnet/dpo/dpo.h>
|
||||
#include <vnet/adj/adj.h>
|
||||
#include <vnet/ip/ip_types.h>
|
||||
#include <vnet/udp/udp.h>
|
||||
#include <vnet/tcp/tcp.h>
|
||||
|
||||
#include <plugins/lcpng/lcpng.h>
|
||||
|
||||
@ -170,6 +172,21 @@ void lcp_itf_pair_register_vft (lcp_itf_pair_vft_t *lcp_itf_vft);
|
||||
/* Set TAP and Linux host link state */
|
||||
void lcp_itf_set_link_state (const lcp_itf_pair_t *lip, u8 state);
|
||||
|
||||
/* Set any VPP L3 addresses on Linux host device */
|
||||
void lcp_itf_set_interface_addr (const lcp_itf_pair_t *lip);
|
||||
|
||||
/* Sync IPv4 and IPv6 address from VPP to Linux device */
|
||||
void lcp_itf_ip4_add_del_interface_addr (ip4_main_t *im, uword opaque,
|
||||
u32 sw_if_index,
|
||||
ip4_address_t *address,
|
||||
u32 address_length,
|
||||
u32 if_address_index, u32 is_del);
|
||||
void lcp_itf_ip6_add_del_interface_addr (ip6_main_t *im, uword opaque,
|
||||
u32 sw_if_index,
|
||||
ip6_address_t *address,
|
||||
u32 address_length,
|
||||
u32 if_address_index, u32 is_del);
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
|
Reference in New Issue
Block a user