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.
This commit is contained in:
Pim van Pelt
2021-08-15 16:07:53 +02:00
parent d23aab2d95
commit 2d00de080b
6 changed files with 104 additions and 11 deletions

20
lcpng.c
View File

@ -69,6 +69,26 @@ int lcp_set_default_ns(u8 *ns) {
return 0; 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 void
lcp_set_lcp_auto_subint (u8 is_auto) lcp_set_lcp_auto_subint (u8 is_auto)
{ {

View File

@ -25,6 +25,7 @@ typedef struct lcp_main_s
u8 default_namespace[LCP_NS_LEN]; /* default namespace if set */ u8 default_namespace[LCP_NS_LEN]; /* default namespace if set */
int default_ns_fd; int default_ns_fd;
u8 lcp_auto_subint; /* Automatically create/delete LCP sub-interfaces */ u8 lcp_auto_subint; /* Automatically create/delete LCP sub-interfaces */
u8 lcp_sync; /* Automatically sync VPP changes to LCP */
/* Set when Unit testing */ /* Set when Unit testing */
u8 test_mode; u8 test_mode;
} lcp_main_t; } 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 */ u8 *lcp_get_default_ns(void); /* Returns NULL or shared string */
int lcp_get_default_ns_fd(void); int lcp_get_default_ns_fd(void);
/*
* Sync state from VPP into all LCP devices
*/
void lcp_itf_pair_sync_state_all ();
#endif #endif
/* /*

View File

@ -111,6 +111,37 @@ VLIB_CLI_COMMAND(lcp_itf_pair_create_command, static) = {
.function = lcp_itf_pair_create_command_fn, .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 * static clib_error_t *
lcp_lcp_auto_subint_command_fn (vlib_main_t *vm, unformat_input_t *input, lcp_lcp_auto_subint_command_fn (vlib_main_t *vm, unformat_input_t *input,
vlib_cli_command_t *cmd) vlib_cli_command_t *cmd)

View File

@ -48,6 +48,10 @@ lcp_itf_pair_sync_state (lcp_itf_pair_t *lip)
vnet_sw_interface_t *sup_sw; vnet_sw_interface_t *sup_sw;
int curr_ns_fd = -1; int curr_ns_fd = -1;
int vif_ns_fd = -1; int vif_ns_fd = -1;
u32 mtu;
if (!lcp_lcp_sync ())
return;
sw = sw =
vnet_get_sw_interface_or_null (vnet_get_main (), lip->lip_phy_sw_if_index); 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 /* Linux will clamp MTU of children when the parent is lower. VPP is fine
* with differing MTUs. Reconcile any differences * with differing MTUs. Reconcile any differences
*/ */
mtu = sw->mtu[VNET_MTU_L3];
if (sup_sw->mtu[VNET_MTU_L3] < 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: " LCP_ITF_PAIR_WARN ("sync_state: %U flags %u mtu %u sup-mtu %u: "
"clamping to sup-mtu to satisfy netlink", "clamping to sup-mtu to satisfy netlink",
format_lcp_itf_pair, lip, sw->flags, format_lcp_itf_pair, lip, sw->flags,
sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]); sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]);
vnet_sw_interface_set_mtu (vnet_get_main (), sw->sw_if_index, mtu = sup_sw->mtu[VNET_MTU_L3];
sup_sw->mtu[VNET_MTU_L3]);
vnet_netlink_set_link_mtu (lip->lip_vif_index, 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 /* Linux will remove IPv6 addresses on children when the master state
* goes down, so we ensure all IPv4/IPv6 addresses are synced. * 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 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; lcp_itf_pair_t *lip;
lip = lcp_itf_pair_get (lipi); 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; 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 * static clib_error_t *
lcp_itf_admin_state_change (vnet_main_t * vnm, u32 sw_if_index, u32 flags) 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 curr_ns_fd = -1;
int vif_ns_fd = -1; int vif_ns_fd = -1;
if (!lcp_lcp_sync ())
return 0;
LCP_ITF_PAIR_DBG ("admin_state_change: sw %U %u", LCP_ITF_PAIR_DBG ("admin_state_change: sw %U %u",
format_vnet_sw_if_index_name, vnm, sw_if_index, format_vnet_sw_if_index_name, vnm, sw_if_index,
flags); 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 // 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 // walking the sub-interfaces of a phy and syncing their state back into
// linux. For simplicity, just walk all interfaces. // 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; 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 curr_ns_fd = -1;
int vif_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, LCP_ITF_PAIR_DBG ("mtu_change: sw %U %u", format_vnet_sw_if_index_name, vnm,
sw_if_index, flags); 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 // so we are forced to undo that change by walking the sub-interfaces of
// a phy and syncing their state back into linux. // a phy and syncing their state back into linux.
// For simplicity, just walk all interfaces. // 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; 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 curr_ns_fd = -1;
int vif_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", LCP_ITF_PAIR_DBG ("ip4_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
format_vnet_sw_if_index_name, vnet_get_main (), format_vnet_sw_if_index_name, vnet_get_main (),
sw_if_index, format_ip4_address, address, address_length); 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 curr_ns_fd = -1;
int vif_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", LCP_ITF_PAIR_DBG ("ip6_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
format_vnet_sw_if_index_name, vnet_get_main (), format_vnet_sw_if_index_name, vnet_get_main (),
sw_if_index, format_ip6_address, address, address_length); sw_if_index, format_ip6_address, address, address_length);

View File

@ -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 : "<unset>"); vlib_cli_output (vm, "lcp default netns %s\n", ns ? (char *) ns : "<unset>");
vlib_cli_output (vm, "lcp lcp-auto-subint %s\n", vlib_cli_output (vm, "lcp lcp-auto-subint %s\n",
lcp_lcp_auto_subint () ? "on" : "off"); 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) 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")) else if (unformat (input, "lcp-auto-subint"))
lcp_set_lcp_auto_subint (1 /* is_auto */); lcp_set_lcp_auto_subint (1 /* is_auto */);
else if (unformat (input, "lcp-sync"))
lcp_set_lcp_sync (1 /* is_auto */);
else else
return clib_error_return (0, "unknown input `%U'", return clib_error_return (0, "unknown input `%U'",
format_unformat_error, input); 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); host_if_type, ns);
/* /*
* Copy the link state from VPP into the host side. * Copy the link state from VPP into the host side, if lcp-sync is on.
* The TAP is shared by many interfaces, always keep it up.
* This controls whether the host can RX/TX. * 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); vnet_sw_interface_admin_up (vnm, host_sw_if_index);
lip = lcp_itf_pair_get (lcp_itf_pair_find_by_vif(vif_index)); if (lcp_lcp_sync ())
lcp_itf_pair_sync_state (lip); {
lip = lcp_itf_pair_get (lcp_itf_pair_find_by_vif (vif_index));
lcp_itf_pair_sync_state (lip);
}
if (host_sw_if_indexp) if (host_sw_if_indexp)
*host_sw_if_indexp = host_sw_if_index; *host_sw_if_indexp = host_sw_if_index;

View File

@ -170,6 +170,12 @@ lcp_itf_pair_find_by_host (u32 host_sw_if_index)
void lcp_set_lcp_auto_subint (u8 is_auto); void lcp_set_lcp_auto_subint (u8 is_auto);
int lcp_lcp_auto_subint (void); 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_add_cb_t) (lcp_itf_pair_t *);
typedef void (*lcp_itf_pair_del_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 address_length,
u32 if_address_index, u32 is_del); 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 * 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. * the VPP interface, for example if its MTU is greater than its parent.
* See the function for rationale. * See the function for rationale.
*/ */
void lcp_itf_pair_sync_state (lcp_itf_pair_t *lip); 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 * fd.io coding-style-patch-verification: ON