From 2d00de080bd26d80ce69441b1043de37e0326e0a Mon Sep 17 00:00:00 2001 From: Pim van Pelt Date: Sun, 15 Aug 2021 16:07:53 +0200 Subject: [PATCH] Protect VPP -> Linux state propagation behind flag Introduce lcp_main.lcp_sync, which determines if state changes made to interfaces in VPP do or don't propagate into Linux. - Add a startup.conf directive 'lcp-sync' to enable at startup time. - Add CLI.short_help = "lcp lcp-sync [on|enable|off|disable]", - Show the current value in "show lcp". Gate changes in mtu, state and address on lcp_lcp_sync(). When the operator issues 'lcp lcp-sync on', it is prudent to do a one-off sync of all interface attributes from VPP into Linux. For this, add a lcp_itf_pair_sync_state_all() function. --- lcpng.c | 20 ++++++++++++++++++++ lcpng.h | 6 ++++++ lcpng_if_cli.c | 31 +++++++++++++++++++++++++++++++ lcpng_if_sync.c | 35 +++++++++++++++++++++++++++++------ lcpng_interface.c | 14 ++++++++++---- lcpng_interface.h | 9 ++++++++- 6 files changed, 104 insertions(+), 11 deletions(-) diff --git a/lcpng.c b/lcpng.c index ddc10f9..7ee4747 100644 --- a/lcpng.c +++ b/lcpng.c @@ -69,6 +69,26 @@ int lcp_set_default_ns(u8 *ns) { return 0; } +void +lcp_set_lcp_sync (u8 is_auto) +{ + lcp_main_t *lcpm = &lcp_main; + + lcpm->lcp_sync = (is_auto != 0); + + // If we set to 'on', do a one-off sync of LCP interfaces + if (is_auto) + lcp_itf_pair_sync_state_all (); +} + +int +lcp_lcp_sync (void) +{ + lcp_main_t *lcpm = &lcp_main; + + return lcpm->lcp_sync; +} + void lcp_set_lcp_auto_subint (u8 is_auto) { diff --git a/lcpng.h b/lcpng.h index 280f9f0..04ef382 100644 --- a/lcpng.h +++ b/lcpng.h @@ -25,6 +25,7 @@ typedef struct lcp_main_s u8 default_namespace[LCP_NS_LEN]; /* default namespace if set */ int default_ns_fd; u8 lcp_auto_subint; /* Automatically create/delete LCP sub-interfaces */ + u8 lcp_sync; /* Automatically sync VPP changes to LCP */ /* Set when Unit testing */ u8 test_mode; } lcp_main_t; @@ -38,6 +39,11 @@ int lcp_set_default_ns(u8 *ns); u8 *lcp_get_default_ns(void); /* Returns NULL or shared string */ int lcp_get_default_ns_fd(void); +/* + * Sync state from VPP into all LCP devices + */ +void lcp_itf_pair_sync_state_all (); + #endif /* diff --git a/lcpng_if_cli.c b/lcpng_if_cli.c index 607a2ac..696816a 100644 --- a/lcpng_if_cli.c +++ b/lcpng_if_cli.c @@ -111,6 +111,37 @@ VLIB_CLI_COMMAND(lcp_itf_pair_create_command, static) = { .function = lcp_itf_pair_create_command_fn, }; +static clib_error_t * +lcp_lcp_sync_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "on") || unformat (line_input, "enable")) + lcp_set_lcp_sync (1); + else if (unformat (line_input, "off") || + unformat (line_input, "disable")) + lcp_set_lcp_sync (0); + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + } + + unformat_free (line_input); + return 0; +} + +VLIB_CLI_COMMAND (lcp_lcp_sync_command, static) = { + .path = "lcp lcp-sync", + .short_help = "lcp lcp-sync [on|enable|off|disable]", + .function = lcp_lcp_sync_command_fn, +}; + static clib_error_t * lcp_lcp_auto_subint_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) diff --git a/lcpng_if_sync.c b/lcpng_if_sync.c index 6bb48f4..f730699 100644 --- a/lcpng_if_sync.c +++ b/lcpng_if_sync.c @@ -48,6 +48,10 @@ lcp_itf_pair_sync_state (lcp_itf_pair_t *lip) vnet_sw_interface_t *sup_sw; int curr_ns_fd = -1; int vif_ns_fd = -1; + u32 mtu; + + if (!lcp_lcp_sync ()) + return; sw = vnet_get_sw_interface_or_null (vnet_get_main (), lip->lip_phy_sw_if_index); @@ -72,16 +76,17 @@ lcp_itf_pair_sync_state (lcp_itf_pair_t *lip) /* Linux will clamp MTU of children when the parent is lower. VPP is fine * with differing MTUs. Reconcile any differences */ + mtu = sw->mtu[VNET_MTU_L3]; if (sup_sw->mtu[VNET_MTU_L3] < sw->mtu[VNET_MTU_L3]) { LCP_ITF_PAIR_WARN ("sync_state: %U flags %u mtu %u sup-mtu %u: " "clamping to sup-mtu to satisfy netlink", format_lcp_itf_pair, lip, sw->flags, sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]); - vnet_sw_interface_set_mtu (vnet_get_main (), sw->sw_if_index, - sup_sw->mtu[VNET_MTU_L3]); - vnet_netlink_set_link_mtu (lip->lip_vif_index, sup_sw->mtu[VNET_MTU_L3]); + mtu = sup_sw->mtu[VNET_MTU_L3]; } + vnet_sw_interface_set_mtu (vnet_get_main (), sw->sw_if_index, mtu); + vnet_netlink_set_link_mtu (lip->lip_vif_index, mtu); /* Linux will remove IPv6 addresses on children when the master state * goes down, so we ensure all IPv4/IPv6 addresses are synced. @@ -101,7 +106,7 @@ lcp_itf_pair_sync_state (lcp_itf_pair_t *lip) } static walk_rc_t -lcp_itf_pair_walk_sync_state_cb (index_t lipi, void *ctx) +lcp_itf_pair_walk_sync_state_all_cb (index_t lipi, void *ctx) { lcp_itf_pair_t *lip; lip = lcp_itf_pair_get (lipi); @@ -112,6 +117,12 @@ lcp_itf_pair_walk_sync_state_cb (index_t lipi, void *ctx) return WALK_CONTINUE; } +void +lcp_itf_pair_sync_state_all () +{ + lcp_itf_pair_walk (lcp_itf_pair_walk_sync_state_all_cb, 0); +} + static clib_error_t * lcp_itf_admin_state_change (vnet_main_t * vnm, u32 sw_if_index, u32 flags) { @@ -122,6 +133,9 @@ lcp_itf_admin_state_change (vnet_main_t * vnm, u32 sw_if_index, u32 flags) int curr_ns_fd = -1; int vif_ns_fd = -1; + if (!lcp_lcp_sync ()) + return 0; + LCP_ITF_PAIR_DBG ("admin_state_change: sw %U %u", format_vnet_sw_if_index_name, vnm, sw_if_index, flags); @@ -163,7 +177,7 @@ lcp_itf_admin_state_change (vnet_main_t * vnm, u32 sw_if_index, u32 flags) // change. This is not true in VPP, so we are forced to undo that change by // walking the sub-interfaces of a phy and syncing their state back into // linux. For simplicity, just walk all interfaces. - lcp_itf_pair_walk (lcp_itf_pair_walk_sync_state_cb, 0); + lcp_itf_pair_sync_state_all (); return NULL; } @@ -178,6 +192,9 @@ lcp_itf_mtu_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags) int curr_ns_fd = -1; int vif_ns_fd = -1; + if (!lcp_lcp_sync ()) + return 0; + LCP_ITF_PAIR_DBG ("mtu_change: sw %U %u", format_vnet_sw_if_index_name, vnm, sw_if_index, flags); @@ -215,7 +232,7 @@ lcp_itf_mtu_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags) // so we are forced to undo that change by walking the sub-interfaces of // a phy and syncing their state back into linux. // For simplicity, just walk all interfaces. - lcp_itf_pair_walk (lcp_itf_pair_walk_sync_state_cb, 0); + lcp_itf_pair_sync_state_all (); return NULL; } @@ -369,6 +386,9 @@ lcp_itf_ip4_add_del_interface_addr (ip4_main_t *im, uword opaque, int curr_ns_fd = -1; int vif_ns_fd = -1; + if (!lcp_lcp_sync ()) + return; + 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); @@ -415,6 +435,9 @@ lcp_itf_ip6_add_del_interface_addr (ip6_main_t *im, uword opaque, int curr_ns_fd = -1; int vif_ns_fd = -1; + if (!lcp_lcp_sync ()) + return; + 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); diff --git a/lcpng_interface.c b/lcpng_interface.c index c657b1c..4df7d7e 100644 --- a/lcpng_interface.c +++ b/lcpng_interface.c @@ -132,6 +132,7 @@ lcp_itf_pair_show (u32 phy_sw_if_index) vlib_cli_output (vm, "lcp default netns %s\n", ns ? (char *) ns : ""); vlib_cli_output (vm, "lcp lcp-auto-subint %s\n", lcp_lcp_auto_subint () ? "on" : "off"); + vlib_cli_output (vm, "lcp lcp-sync %s\n", lcp_lcp_sync () ? "on" : "off"); if (phy_sw_if_index == ~0) { @@ -568,6 +569,8 @@ lcp_itf_pair_config (vlib_main_t *vm, unformat_input_t *input) } else if (unformat (input, "lcp-auto-subint")) lcp_set_lcp_auto_subint (1 /* is_auto */); + else if (unformat (input, "lcp-sync")) + lcp_set_lcp_sync (1 /* is_auto */); else return clib_error_return (0, "unknown input `%U'", format_unformat_error, input); @@ -1033,13 +1036,16 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name, 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. + * Copy the link state from VPP into the host side, if lcp-sync is on. * This controls whether the host can RX/TX. + * The TAP is shared by many interfaces, always keep it up. */ vnet_sw_interface_admin_up (vnm, host_sw_if_index); - lip = lcp_itf_pair_get (lcp_itf_pair_find_by_vif(vif_index)); - lcp_itf_pair_sync_state (lip); + if (lcp_lcp_sync ()) + { + lip = lcp_itf_pair_get (lcp_itf_pair_find_by_vif (vif_index)); + lcp_itf_pair_sync_state (lip); + } if (host_sw_if_indexp) *host_sw_if_indexp = host_sw_if_index; diff --git a/lcpng_interface.h b/lcpng_interface.h index af19049..884c86d 100644 --- a/lcpng_interface.h +++ b/lcpng_interface.h @@ -170,6 +170,12 @@ lcp_itf_pair_find_by_host (u32 host_sw_if_index) void lcp_set_lcp_auto_subint (u8 is_auto); int lcp_lcp_auto_subint (void); +/** + * sync state changes from VPP into LCP + */ +void lcp_set_lcp_sync (u8 is_auto); +int lcp_lcp_sync (void); + typedef void (*lcp_itf_pair_add_cb_t) (lcp_itf_pair_t *); typedef void (*lcp_itf_pair_del_cb_t) (lcp_itf_pair_t *); @@ -199,13 +205,14 @@ void lcp_itf_ip6_add_del_interface_addr (ip6_main_t *im, uword opaque, u32 address_length, u32 if_address_index, u32 is_del); -/* Sync all state from VPP to Linux device +/* Sync all state from VPP to a specific Linux device, or all of them. * * Note: in some circumstances, this syncer will (have to) make changes to * the VPP interface, for example if its MTU is greater than its parent. * See the function for rationale. */ void lcp_itf_pair_sync_state (lcp_itf_pair_t *lip); +void lcp_itf_pair_sync_state_all (); /* * fd.io coding-style-patch-verification: ON