diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e35132..3ecb84c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/lcpng_if_sync.c b/lcpng_if_sync.c index 2f61c06..23fd73b 100644 --- a/lcpng_if_sync.c +++ b/lcpng_if_sync.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include /* 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); +} diff --git a/lcpng_interface.c b/lcpng_interface.c index 487b193..054fef7 100644 --- a/lcpng_interface.c +++ b/lcpng_interface.c @@ -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; } diff --git a/lcpng_interface.h b/lcpng_interface.h index 28fb280..8daf2d9 100644 --- a/lcpng_interface.h +++ b/lcpng_interface.h @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include @@ -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 *