aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/configure.ac1
-rw-r--r--src/plugins/Makefile.am4
-rw-r--r--src/plugins/lacp.am47
-rw-r--r--src/plugins/lacp/cli.c393
-rw-r--r--src/plugins/lacp/input.c359
-rw-r--r--src/plugins/lacp/lacp.api80
-rw-r--r--src/plugins/lacp/lacp.c428
-rw-r--r--src/plugins/lacp/lacp_all_api_h.h18
-rw-r--r--src/plugins/lacp/lacp_api.c217
-rw-r--r--src/plugins/lacp/lacp_doc.md92
-rw-r--r--src/plugins/lacp/lacp_msg_enum.h31
-rw-r--r--src/plugins/lacp/lacp_test.c231
-rw-r--r--src/plugins/lacp/machine.h57
-rw-r--r--src/plugins/lacp/mux_machine.c236
-rw-r--r--src/plugins/lacp/mux_machine.h80
-rw-r--r--src/plugins/lacp/node.c209
-rw-r--r--src/plugins/lacp/node.h276
-rw-r--r--src/plugins/lacp/protocol.h178
-rw-r--r--src/plugins/lacp/ptx_machine.c202
-rw-r--r--src/plugins/lacp/ptx_machine.h80
-rw-r--r--src/plugins/lacp/rx_machine.c425
-rw-r--r--src/plugins/lacp/rx_machine.h91
-rw-r--r--src/plugins/lacp/selection.c92
-rw-r--r--src/plugins/lacp/tx_machine.c109
-rw-r--r--src/plugins/lacp/tx_machine.h59
-rw-r--r--src/vat/api_format.c477
-rw-r--r--src/vnet.am15
-rw-r--r--src/vnet/bonding/bond.api163
-rw-r--r--src/vnet/bonding/bond_api.c328
-rw-r--r--src/vnet/bonding/cli.c706
-rw-r--r--src/vnet/bonding/device.c610
-rw-r--r--src/vnet/bonding/node.c509
-rw-r--r--src/vnet/bonding/node.h451
-rw-r--r--src/vnet/vnet_all_api_h.h1
-rw-r--r--src/vpp/api/custom_dump.c84
-rw-r--r--test/test_bond.py282
-rw-r--r--test/vpp_bond_interface.py48
-rw-r--r--test/vpp_papi_provider.py75
38 files changed, 7743 insertions, 1 deletions
diff --git a/src/configure.ac b/src/configure.ac
index c4554231308..d0067c079eb 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -222,6 +222,7 @@ PLUGIN_ENABLED(ioam)
PLUGIN_ENABLED(ixge)
PLUGIN_ENABLED(kubeproxy)
PLUGIN_ENABLED(l2e)
+PLUGIN_ENABLED(lacp)
PLUGIN_ENABLED(lb)
PLUGIN_ENABLED(marvell)
PLUGIN_ENABLED(memif)
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index 37b2e259056..03a39dfcd7a 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -71,6 +71,10 @@ if ENABLE_KUBEPROXY_PLUGIN
include kubeproxy.am
endif
+if ENABLE_LACP_PLUGIN
+include lacp.am
+endif
+
if ENABLE_LB_PLUGIN
include lb.am
endif
diff --git a/src/plugins/lacp.am b/src/plugins/lacp.am
new file mode 100644
index 00000000000..c7e571d95fb
--- /dev/null
+++ b/src/plugins/lacp.am
@@ -0,0 +1,47 @@
+# Copyright (c) 2017 Cisco Systems, Inc.
+# 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.
+
+vppplugins_LTLIBRARIES += lacp_plugin.la
+vppapitestplugins_LTLIBRARIES += lacp_test_plugin.la
+
+lacp_plugin_la_LIBADD =
+lacp_plugin_la_SOURCES = lacp/lacp.c \
+ lacp/lacp_api.c \
+ lacp/selection.c \
+ lacp/rx_machine.c \
+ lacp/tx_machine.c \
+ lacp/mux_machine.c \
+ lacp/ptx_machine.c \
+ lacp/cli.c \
+ lacp/input.c \
+ lacp/node.c
+
+lacp_test_plugin_la_SOURCES = \
+ lacp/lacp_test.c
+
+noinst_HEADERS += lacp/protocol.h \
+ lacp/machine.h \
+ lacp/rx_machine.h \
+ lacp/tx_machine.h \
+ lacp/mux_machine.h \
+ lacp/ptx_machine.h \
+ lacp/node.h
+
+nobase_apiinclude_HEADERS += \
+ lacp/lacp_all_api_h.h \
+ lacp/lacp_msg_enum.h \
+ lacp/lacp.api.h
+
+API_FILES += lacp/lacp.api
+
+# vi:syntax=automake
diff --git a/src/plugins/lacp/cli.c b/src/plugins/lacp/cli.c
new file mode 100644
index 00000000000..10627774f56
--- /dev/null
+++ b/src/plugins/lacp/cli.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#define _GNU_SOURCE
+#include <vnet/bonding/node.h>
+#include <lacp/node.h>
+
+int
+lacp_dump_ifs (lacp_interface_details_t ** out_lacpifs)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ bond_main_t *bm = &bond_main;
+ slave_if_t *sif;
+ bond_if_t *bif;
+ vnet_hw_interface_t *hi;
+ lacp_interface_details_t *r_lacpifs = NULL;
+ lacp_interface_details_t *lacpif = NULL;
+
+ /* *INDENT-OFF* */
+ pool_foreach (sif, bm->neighbors,
+ if ((sif->port_enabled == 0) || (sif->lacp_enabled == 0))
+ continue;
+ vec_add2(r_lacpifs, lacpif, 1);
+ memset (lacpif, 0, sizeof (*lacpif));
+ lacpif->sw_if_index = sif->sw_if_index;
+ hi = vnet_get_hw_interface (vnm, sif->hw_if_index);
+ clib_memcpy(lacpif->interface_name, hi->name,
+ MIN (ARRAY_LEN (lacpif->interface_name) - 1,
+ strlen ((const char *) hi->name)));
+ bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
+ hi = vnet_get_hw_interface (vnm, bif->hw_if_index);
+ clib_memcpy(lacpif->bond_interface_name, hi->name,
+ MIN (ARRAY_LEN (lacpif->bond_interface_name) - 1,
+ strlen ((const char *) hi->name)));
+ clib_memcpy (lacpif->actor_system, sif->actor.system, 6);
+ lacpif->actor_system_priority = sif->actor.system_priority;
+ lacpif->actor_key = sif->actor.key;
+ lacpif->actor_port_priority = sif->actor.port_priority;
+ lacpif->actor_port_number = sif->actor.port_number;
+ lacpif->actor_state = sif->actor.state;
+ clib_memcpy (lacpif->partner_system, sif->partner.system, 6);
+ lacpif->partner_system_priority = sif->partner.system_priority;
+ lacpif->partner_key = sif->partner.key;
+ lacpif->partner_port_priority = sif->partner.port_priority;
+ lacpif->partner_port_number = sif->partner.port_number;
+ lacpif->partner_state = sif->partner.state;
+ lacpif->rx_state = sif->rx_state;
+ lacpif->tx_state = sif->tx_state;
+ lacpif->ptx_state = sif->ptx_state;
+ lacpif->mux_state = sif->mux_state;
+ );
+ /* *INDENT-ON* */
+
+ *out_lacpifs = r_lacpifs;
+
+ return 0;
+}
+
+static void
+show_lacp (vlib_main_t * vm, u32 * sw_if_indices)
+{
+ int i;
+ slave_if_t *sif;
+ bond_if_t *bif;
+
+ if (!sw_if_indices)
+ return;
+
+ vlib_cli_output (vm, "%-55s %-32s %-32s", " ", "actor state",
+ "partner state");
+ vlib_cli_output (vm, "%-25s %-12s %-16s %-31s %-31s", "interface name",
+ "sw_if_index", "bond interface",
+ "exp/def/dis/col/syn/agg/tim/act",
+ "exp/def/dis/col/syn/agg/tim/act");
+
+ for (i = 0; i < vec_len (sw_if_indices); i++)
+ {
+ sif = bond_get_slave_by_sw_if_index (sw_if_indices[i]);
+ if (!sif || (sif->port_enabled == 0) || (sif->lacp_enabled == 0))
+ continue;
+ bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
+ vlib_cli_output (vm,
+ "%-25U %-12d %-16U %3x %3x %3x %3x %3x %3x %3x %3x "
+ "%4x %3x %3x %3x %3x %3x %3x %3x",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sif->sw_if_index, sif->sw_if_index,
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ bif->sw_if_index, lacp_bit_test (sif->actor.state, 7),
+ lacp_bit_test (sif->actor.state, 6),
+ lacp_bit_test (sif->actor.state, 5),
+ lacp_bit_test (sif->actor.state, 4),
+ lacp_bit_test (sif->actor.state, 3),
+ lacp_bit_test (sif->actor.state, 2),
+ lacp_bit_test (sif->actor.state, 1),
+ lacp_bit_test (sif->actor.state, 0),
+ lacp_bit_test (sif->partner.state, 7),
+ lacp_bit_test (sif->partner.state, 6),
+ lacp_bit_test (sif->partner.state, 5),
+ lacp_bit_test (sif->partner.state, 4),
+ lacp_bit_test (sif->partner.state, 3),
+ lacp_bit_test (sif->partner.state, 2),
+ lacp_bit_test (sif->partner.state, 1),
+ lacp_bit_test (sif->partner.state, 0));
+ vlib_cli_output (vm,
+ " LAG ID: "
+ "[(%04x,%02x-%02x-%02x-%02x-%02x-%02x,%04x,%04x,%04x), "
+ "(%04x,%02x-%02x-%02x-%02x-%02x-%02x,%04x,%04x,%04x)]",
+ ntohs (sif->actor.system_priority),
+ sif->actor.system[0], sif->actor.system[1],
+ sif->actor.system[2], sif->actor.system[3],
+ sif->actor.system[4], sif->actor.system[5],
+ ntohs (sif->actor.key),
+ ntohs (sif->actor.port_priority),
+ ntohs (sif->actor.port_number),
+ ntohs (sif->partner.system_priority),
+ sif->partner.system[0], sif->partner.system[1],
+ sif->partner.system[2], sif->partner.system[3],
+ sif->partner.system[4], sif->partner.system[5],
+ ntohs (sif->partner.key),
+ ntohs (sif->partner.port_priority),
+ ntohs (sif->partner.port_number));
+ vlib_cli_output (vm,
+ " RX-state: %U, TX-state: %U, "
+ "MUX-state: %U, PTX-state: %U",
+ format_rx_sm_state, sif->rx_state, format_tx_sm_state,
+ sif->tx_state, format_mux_sm_state, sif->mux_state,
+ format_ptx_sm_state, sif->ptx_state);
+ }
+}
+
+static void
+show_lacp_details (vlib_main_t * vm, u32 * sw_if_indices)
+{
+ slave_if_t *sif;
+ lacp_state_struct *state_entry;
+ int i;
+ f64 now;
+
+ if (!sw_if_indices)
+ return;
+
+ now = vlib_time_now (vm);
+ for (i = 0; i < vec_len (sw_if_indices); i++)
+ {
+ sif = bond_get_slave_by_sw_if_index (sw_if_indices[i]);
+ if (!sif || (sif->port_enabled == 0) || (sif->lacp_enabled == 0))
+ continue;
+ vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name,
+ vnet_get_main (), sif->sw_if_index);
+ vlib_cli_output (vm, " debug: %d", sif->debug);
+ vlib_cli_output (vm, " loopback port: %d", sif->loopback_port);
+ vlib_cli_output (vm, " port moved: %d", sif->port_moved);
+ vlib_cli_output (vm, " ready_n: %d", sif->ready_n);
+ vlib_cli_output (vm, " ready: %d", sif->ready);
+ vlib_cli_output (vm, " Actor");
+ vlib_cli_output (vm, " system: %U",
+ format_ethernet_address, sif->actor.system);
+ vlib_cli_output (vm, " system priority: %u",
+ ntohs (sif->actor.system_priority));
+ vlib_cli_output (vm, " key: %u", ntohs (sif->actor.key));
+ vlib_cli_output (vm, " port priority: %u",
+ ntohs (sif->actor.port_priority));
+ vlib_cli_output (vm, " port number: %u",
+ ntohs (sif->actor.port_number));
+ vlib_cli_output (vm, " state: 0x%x", sif->actor.state);
+
+ state_entry = (lacp_state_struct *) & lacp_state_array;
+ while (state_entry->str)
+ {
+ if (sif->actor.state & (1 << state_entry->bit))
+ vlib_cli_output (vm, " %s (%d)", state_entry->str,
+ state_entry->bit);
+ state_entry++;
+ }
+
+ vlib_cli_output (vm, " Partner");
+ vlib_cli_output (vm, " system: %U",
+ format_ethernet_address, sif->partner.system);
+ vlib_cli_output (vm, " system priority: %u",
+ ntohs (sif->partner.system_priority));
+ vlib_cli_output (vm, " key: %u", ntohs (sif->partner.key));
+ vlib_cli_output (vm, " port priority: %u",
+ ntohs (sif->partner.port_priority));
+ vlib_cli_output (vm, " port number: %u",
+ ntohs (sif->partner.port_number));
+ vlib_cli_output (vm, " state: 0x%x", sif->partner.state);
+
+ state_entry = (lacp_state_struct *) & lacp_state_array;
+ while (state_entry->str)
+ {
+ if (sif->partner.state & (1 << state_entry->bit))
+ vlib_cli_output (vm, " %s (%d)", state_entry->str,
+ state_entry->bit);
+ state_entry++;
+ }
+
+ if (!lacp_timer_is_running (sif->wait_while_timer))
+ vlib_cli_output (vm, " wait while timer: not running");
+ else
+ vlib_cli_output (vm, " wait while timer: %=10.2f seconds",
+ sif->wait_while_timer - now);
+ if (!lacp_timer_is_running (sif->current_while_timer))
+ vlib_cli_output (vm, " current while timer: not running");
+ else
+ vlib_cli_output (vm, " current while timer: %=10.2f seconds",
+ sif->current_while_timer - now);
+ if (!lacp_timer_is_running (sif->periodic_timer))
+ vlib_cli_output (vm, " periodic timer: not running");
+ else
+ vlib_cli_output (vm, " periodic timer: %=10.2f seconds",
+ sif->periodic_timer - now);
+ vlib_cli_output (vm, " RX-state: %U", format_rx_sm_state,
+ sif->rx_state);
+ vlib_cli_output (vm, " TX-state: %U", format_tx_sm_state,
+ sif->tx_state);
+ vlib_cli_output (vm, " MUX-state: %U", format_mux_sm_state,
+ sif->mux_state);
+ vlib_cli_output (vm, " PTX-state: %U", format_ptx_sm_state,
+ sif->ptx_state);
+ vlib_cli_output (vm, "\n");
+ }
+}
+
+static clib_error_t *
+show_lacp_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ bond_main_t *bm = &bond_main;
+ vnet_main_t *vnm = &vnet_main;
+ slave_if_t *sif;
+ clib_error_t *error = 0;
+ u8 details = 0;
+ u32 hw_if_index, *sw_if_indices = 0;
+ vnet_interface_main_t *im = &vnm->interface_main;
+ vnet_sw_interface_t *sw;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat
+ (input, "%U", unformat_vnet_hw_interface, vnm, &hw_if_index))
+ {
+ sw = pool_elt_at_index (im->sw_interfaces, hw_if_index);
+ sif = bond_get_slave_by_sw_if_index (sw->sw_if_index);
+ if (!sif)
+ {
+ error = clib_error_return (0, "interface is not enslaved");
+ goto done;
+ }
+ vec_add1 (sw_if_indices, sif->sw_if_index);
+ }
+ else if (unformat (input, "details"))
+ details = 1;
+ else
+ {
+ error = clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ goto done;
+ }
+ }
+
+ if (vec_len (sw_if_indices) == 0)
+ {
+ pool_foreach (sif, bm->neighbors,
+ vec_add1 (sw_if_indices, sif->sw_if_index);
+ );
+ }
+
+ if (details)
+ show_lacp_details (vm, sw_if_indices);
+ else
+ show_lacp (vm, sw_if_indices);
+
+done:
+ vec_free (sw_if_indices);
+ return error;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_lacp_command, static) = {
+ .path = "show lacp",
+ .short_help = "show lacp [<interface>] [details]",
+ .function = show_lacp_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+debug_lacp_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ clib_error_t *error = NULL;
+ lacp_main_t *lm = &lacp_main;
+ u8 onoff = 0;
+ u8 input_found = 0;
+ u32 hw_if_index = ~0;
+ slave_if_t *sif;
+ vnet_main_t *vnm = vnet_get_main ();
+ vnet_interface_main_t *im = &vnm->interface_main;
+ vnet_sw_interface_t *sw;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return clib_error_return (0, "missing argument");
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "%U",
+ unformat_vnet_hw_interface, vnm, &hw_if_index))
+ ;
+ if (input_found)
+ {
+ error = clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ else if (unformat (line_input, "on"))
+ {
+ input_found = 1;
+ onoff = 1;
+ }
+ else if (unformat (line_input, "off"))
+ {
+ input_found = 1;
+ onoff = 0;
+ }
+ else
+ {
+ error = clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+ if (!input_found)
+ return clib_error_return (0, "must specify on or off");
+
+ if (hw_if_index != ~0)
+ {
+ sw = pool_elt_at_index (im->sw_interfaces, hw_if_index);
+ sif = bond_get_slave_by_sw_if_index (sw->sw_if_index);
+ if (!sif)
+ return (clib_error_return (0, "Please enslave the interface first"));
+ sif->debug = onoff;
+ }
+ else
+ lm->debug = onoff;
+
+done:
+ unformat_free (line_input);
+
+ return error;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (debug_lacp_command, static) = {
+ .path = "debug lacp",
+ .short_help = "debug lacp <interface> <on | off>",
+ .function = debug_lacp_command_fn,
+};
+/* *INDENT-ON* */
+
+clib_error_t *
+lacp_cli_init (vlib_main_t * vm)
+{
+ lacp_main_t *lm = &lacp_main;
+
+ lm->vlib_main = vm;
+ lm->vnet_main = vnet_get_main ();
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (lacp_cli_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/input.c b/src/plugins/lacp/input.c
new file mode 100644
index 00000000000..45db3b8455b
--- /dev/null
+++ b/src/plugins/lacp/input.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#define _GNU_SOURCE
+#include <vnet/bonding/node.h>
+#include <lacp/node.h>
+
+static int
+lacp_packet_scan (vlib_main_t * vm, slave_if_t * sif)
+{
+ lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
+
+ if (lacpdu->subtype != LACP_SUBTYPE)
+ return LACP_ERROR_UNSUPPORTED;
+
+ /*
+ * According to the spec, no checking on the version number and tlv types.
+ * But we may check the tlv lengths.
+ */
+ if ((lacpdu->actor.tlv_length != sizeof (lacp_actor_partner_t)) ||
+ (lacpdu->partner.tlv_length != sizeof (lacp_actor_partner_t)) ||
+ (lacpdu->collector.tlv_length != sizeof (lacp_collector_t)) ||
+ (lacpdu->terminator.tlv_length != 0))
+ return (LACP_ERROR_BAD_TLV);
+
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_PDU_RECEIVED, &sif->rx_state);
+
+ return LACP_ERROR_NONE;
+}
+
+static void
+marker_fill_pdu (marker_pdu_t * marker, slave_if_t * sif)
+{
+ marker_pdu_t *pkt = (marker_pdu_t *) sif->last_marker_pkt;
+
+ marker->marker_info = pkt->marker_info;
+ marker->marker_info.tlv_type = MARKER_RESPONSE_INFORMATION;
+}
+
+void
+marker_fill_request_pdu (marker_pdu_t * marker, slave_if_t * sif)
+{
+ marker->marker_info.tlv_type = MARKER_INFORMATION;
+ marker->marker_info.requester_port = sif->actor.port_number;
+ clib_memcpy (marker->marker_info.requester_system, sif->actor.system, 6);
+ marker->marker_info.requester_transaction_id = sif->marker_tx_id;
+ sif->marker_tx_id++;
+}
+
+static void
+send_ethernet_marker_response_pdu (slave_if_t * sif)
+{
+ lacp_main_t *lm = &lacp_main;
+ u32 *to_next;
+ ethernet_marker_pdu_t *h0;
+ vnet_hw_interface_t *hw;
+ u32 bi0;
+ vlib_buffer_t *b0;
+ vlib_frame_t *f;
+ vlib_main_t *vm = lm->vlib_main;
+ vnet_main_t *vnm = lm->vnet_main;
+
+ /*
+ * see lacp_periodic_init() to understand what's already painted
+ * into the buffer by the packet template mechanism
+ */
+ h0 = vlib_packet_template_get_packet
+ (vm, &lm->marker_packet_templates[sif->packet_template_index], &bi0);
+
+ if (!h0)
+ return;
+
+ /* Add the interface's ethernet source address */
+ hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
+
+ clib_memcpy (h0->ethernet.src_address, hw->hw_address,
+ vec_len (hw->hw_address));
+
+ marker_fill_pdu (&h0->marker, sif);
+
+ /* Set the outbound packet length */
+ b0 = vlib_get_buffer (vm, bi0);
+ b0->current_length = sizeof (ethernet_marker_pdu_t);
+ b0->current_data = 0;
+ b0->total_length_not_including_first_buffer = 0;
+
+ /* And the outbound interface */
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = hw->sw_if_index;
+
+ /* And output the packet on the correct interface */
+ f = vlib_get_frame_to_node (vm, hw->output_node_index);
+
+ to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+
+ vlib_put_frame_to_node (vm, hw->output_node_index, f);
+}
+
+static int
+handle_marker_protocol (vlib_main_t * vm, slave_if_t * sif)
+{
+ marker_pdu_t *marker = (marker_pdu_t *) sif->last_marker_pkt;
+
+ /*
+ * According to the spec, no checking on the version number and tlv types.
+ * But we may check the tlv lengths.
+ */
+ if ((marker->marker_info.tlv_length != sizeof (marker_information_t)) ||
+ (marker->terminator.tlv_length != 0))
+ return (LACP_ERROR_BAD_TLV);
+
+ send_ethernet_marker_response_pdu (sif);
+
+ return LACP_ERROR_NONE;
+}
+
+/*
+ * lacp input routine
+ */
+lacp_error_t
+lacp_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0)
+{
+ lacp_main_t *lm = &lacp_main;
+ slave_if_t *sif;
+ uword nbytes;
+ lacp_error_t e;
+ marker_pdu_t *marker;
+ uword last_packet_signature;
+ bond_if_t *bif;
+
+ sif =
+ bond_get_slave_by_sw_if_index (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
+ if ((sif == 0) || (sif->mode != BOND_MODE_LACP))
+ {
+ return LACP_ERROR_DISABLED;
+ }
+
+ /* Handle marker protocol */
+ marker = (marker_pdu_t *) (b0->data + b0->current_data);
+ if (marker->subtype == MARKER_SUBTYPE)
+ {
+ if (sif->last_marker_pkt)
+ _vec_len (sif->last_marker_pkt) = 0;
+ vec_validate (sif->last_marker_pkt,
+ vlib_buffer_length_in_chain (vm, b0) - 1);
+ nbytes = vlib_buffer_contents (vm, bi0, sif->last_marker_pkt);
+ ASSERT (nbytes <= vec_len (sif->last_marker_pkt));
+ if (nbytes < sizeof (lacp_pdu_t))
+ return LACP_ERROR_TOO_SMALL;
+ return (handle_marker_protocol (vm, sif));
+ }
+
+ /*
+ * typical clib idiom. Don't repeatedly allocate and free
+ * the per-neighbor rx buffer. Reset its apparent length to zero
+ * and reuse it.
+ */
+ if (sif->last_rx_pkt)
+ _vec_len (sif->last_rx_pkt) = 0;
+
+ /*
+ * Make sure the per-neighbor rx buffer is big enough to hold
+ * the data we're about to copy
+ */
+ vec_validate (sif->last_rx_pkt, vlib_buffer_length_in_chain (vm, b0) - 1);
+
+ /*
+ * Coalesce / copy the buffer chain into the per-neighbor
+ * rx buffer
+ */
+ nbytes = vlib_buffer_contents (vm, bi0, sif->last_rx_pkt);
+ ASSERT (nbytes <= vec_len (sif->last_rx_pkt));
+
+ if (nbytes < sizeof (lacp_pdu_t))
+ {
+ return LACP_ERROR_TOO_SMALL;
+ }
+
+ last_packet_signature =
+ hash_memory (sif->last_rx_pkt, vec_len (sif->last_rx_pkt), 0xd00b);
+
+ bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
+ if (sif->last_packet_signature_valid &&
+ (sif->last_packet_signature == last_packet_signature) &&
+ hash_get (bif->active_slave_by_sw_if_index, sif->sw_if_index))
+ {
+ lacp_start_current_while_timer (lm->vlib_main, sif,
+ sif->ttl_in_seconds);
+ e = LACP_ERROR_CACHE_HIT;
+ }
+ else
+ {
+ /* Actually scan the packet */
+ e = lacp_packet_scan (vm, sif);
+ sif->last_packet_signature_valid = 1;
+ sif->last_packet_signature = last_packet_signature;
+ }
+
+ if (sif->last_rx_pkt)
+ _vec_len (sif->last_rx_pkt) = 0;
+
+ return e;
+}
+
+/*
+ * setup neighbor hash table
+ */
+static clib_error_t *
+lacp_init (vlib_main_t * vm)
+{
+ clib_error_t *error;
+
+ if ((error = vlib_call_init_function (vm, lacp_periodic_init)))
+ return error;
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (lacp_init);
+
+/*
+ * packet trace format function, very similar to
+ * lacp_packet_scan except that we call the per TLV format
+ * functions instead of the per TLV processing functions
+ */
+u8 *
+lacp_input_format_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ lacp_input_trace_t *t = va_arg (*args, lacp_input_trace_t *);
+ lacp_pdu_t *lacpdu = &t->pkt.lacpdu;
+ marker_pdu_t *marker = &t->pkt.marker;
+ int i, len;
+ u8 *p;
+ lacp_state_struct *state_entry;
+
+ s = format (s, "Length: %d\n", t->len);
+ if (t->len >= sizeof (lacp_pdu_t))
+ {
+ switch (lacpdu->subtype)
+ {
+ case MARKER_SUBTYPE:
+ if (marker->version_number == MARKER_PROTOCOL_VERSION)
+ s = format (s, " Markerv1\n");
+ else
+ s = format (s, " Subtype %u, Version %u\n", marker->subtype,
+ marker->version_number);
+ s = format (s, " Marker Information TLV: type %u\n",
+ marker->marker_info.tlv_type);
+ s = format (s, " Marker Information TLV: length %u\n",
+ marker->marker_info.tlv_length);
+ s = format (s, " Requester port: %u\n",
+ marker->marker_info.requester_port);
+ s = format (s, " Requester system: %U\n", format_ethernet_address,
+ marker->marker_info.requester_system);
+ s = format (s, " Requester transaction ID: %u\n",
+ marker->marker_info.requester_transaction_id);
+ break;
+
+ case LACP_SUBTYPE:
+ if (lacpdu->version_number == LACP_ACTOR_LACP_VERSION)
+ s = format (s, " LACPv1\n");
+ else
+ s = format (s, " Subtype %u, Version %u\n", lacpdu->subtype,
+ lacpdu->version_number);
+ s = format (s, " Actor Information TLV: length %u\n",
+ lacpdu->actor.tlv_length);
+ s = format (s, " System %U\n", format_ethernet_address,
+ lacpdu->actor.port_info.system);
+ s = format (s, " System priority %u\n",
+ ntohs (lacpdu->actor.port_info.system_priority));
+ s = format (s, " Key %u\n", ntohs (lacpdu->actor.port_info.key));
+ s = format (s, " Port priority %u\n",
+ ntohs (lacpdu->actor.port_info.port_priority));
+ s = format (s, " Port number %u\n",
+ ntohs (lacpdu->actor.port_info.port_number));
+ s = format (s, " State 0x%x\n", lacpdu->actor.port_info.state);
+ state_entry = (lacp_state_struct *) & lacp_state_array;
+ while (state_entry->str)
+ {
+ if (lacpdu->actor.port_info.state & (1 << state_entry->bit))
+ s = format (s, " %s (%d)\n", state_entry->str,
+ state_entry->bit);
+ state_entry++;
+ }
+
+ s = format (s, " Partner Information TLV: length %u\n",
+ lacpdu->partner.tlv_length);
+ s = format (s, " System %U\n", format_ethernet_address,
+ lacpdu->partner.port_info.system);
+ s = format (s, " System priority %u\n",
+ ntohs (lacpdu->partner.port_info.system_priority));
+ s =
+ format (s, " Key %u\n", ntohs (lacpdu->partner.port_info.key));
+ s =
+ format (s, " Port priority %u\n",
+ ntohs (lacpdu->partner.port_info.port_priority));
+ s =
+ format (s, " Port number %u\n",
+ ntohs (lacpdu->partner.port_info.port_number));
+ s = format (s, " State 0x%x\n", lacpdu->partner.port_info.state);
+ state_entry = (lacp_state_struct *) & lacp_state_array;
+ while (state_entry->str)
+ {
+ if (lacpdu->partner.port_info.state & (1 << state_entry->bit))
+ s = format (s, " %s (%d)\n", state_entry->str,
+ state_entry->bit);
+ state_entry++;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (t->len > sizeof (lacp_pdu_t))
+ len = sizeof (lacp_pdu_t);
+ else
+ len = t->len;
+ p = (u8 *) lacpdu;
+ for (i = 0; i < len; i++)
+ {
+ if ((i % 16) == 0)
+ {
+ if (i)
+ s = format (s, "\n");
+ s = format (s, " 0x%04x: ", i);
+ }
+ if ((i % 2) == 0)
+ s = format (s, " ");
+ s = format (s, "%02x", p[i]);
+ }
+
+ return s;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/lacp.api b/src/plugins/lacp/lacp.api
new file mode 100644
index 00000000000..9eb5c7eed07
--- /dev/null
+++ b/src/plugins/lacp/lacp.api
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+/** \file
+
+ This file defines vpe control-plane API messages for
+ the bonding device driver
+*/
+
+option version = "1.0.0";
+
+/** \brief Dump lacp interfaces request */
+define sw_interface_lacp_dump
+{
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Reply for lacp dump request
+ @param sw_if_index - software index of slave interface
+ @param interface_name - name of slave interface
+ @param rx_state - rx machine state
+ @param tx_state - tx machine state
+ @param mux_state - mux machine state
+ @param ptx_state - ptx machine state
+ @param bond_interface_name - name of bond interface
+ @param actor_system_priority - actor system priority
+ @param actor_system - actor system
+ @param actor_key - actor key
+ @param actor_port_priority - actor port priority
+ @param actor_port_number - actor port number
+ @param actor_state - actor state
+ @param partner_system_priority - partner system priority
+ @param partner_system - partner system
+ @param partner_key - partner key
+ @param partner_port_priority - partner port priority
+ @param partner_port_number - partner port number
+ @param partner_state - partner state
+*/
+define sw_interface_lacp_details
+{
+ u32 context;
+ u32 sw_if_index;
+ u8 interface_name[64];
+ u32 rx_state;
+ u32 tx_state;
+ u32 mux_state;
+ u32 ptx_state;
+ u8 bond_interface_name[64];
+ u16 actor_system_priority;
+ u8 actor_system[6];
+ u16 actor_key;
+ u16 actor_port_priority;
+ u16 actor_port_number;
+ u8 actor_state;
+ u16 partner_system_priority;
+ u8 partner_system[6];
+ u16 partner_key;
+ u16 partner_port_priority;
+ u16 partner_port_number;
+ u8 partner_state;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/lacp.c b/src/plugins/lacp/lacp.c
new file mode 100644
index 00000000000..5fe505a4f0f
--- /dev/null
+++ b/src/plugins/lacp/lacp.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2017 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 <stdint.h>
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+#include <vppinfra/hash.h>
+#include <vnet/bonding/node.h>
+#include <lacp/node.h>
+
+lacp_main_t lacp_main;
+
+/*
+ * Generate lacp pdu
+ */
+static void
+lacp_fill_pdu (lacp_pdu_t * lacpdu, slave_if_t * sif)
+{
+ /* Actor TLV */
+ lacpdu->actor.port_info = sif->actor;
+
+ /* Partner TLV */
+ lacpdu->partner.port_info = sif->partner;
+}
+
+/*
+ * send a lacp pkt on an ethernet interface
+ */
+static void
+lacp_send_ethernet_lacp_pdu (slave_if_t * sif)
+{
+ lacp_main_t *lm = &lacp_main;
+ u32 *to_next;
+ ethernet_lacp_pdu_t *h0;
+ vnet_hw_interface_t *hw;
+ u32 bi0;
+ vlib_buffer_t *b0;
+ vlib_frame_t *f;
+ vlib_main_t *vm = lm->vlib_main;
+ vnet_main_t *vnm = lm->vnet_main;
+
+ /*
+ * see lacp_periodic_init() to understand what's already painted
+ * into the buffer by the packet template mechanism
+ */
+ h0 = vlib_packet_template_get_packet
+ (vm, &lm->packet_templates[sif->packet_template_index], &bi0);
+
+ if (!h0)
+ return;
+
+ /* Add the interface's ethernet source address */
+ hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
+
+ clib_memcpy (h0->ethernet.src_address, hw->hw_address,
+ vec_len (hw->hw_address));
+
+ lacp_fill_pdu (&h0->lacp, sif);
+
+ /* Set the outbound packet length */
+ b0 = vlib_get_buffer (vm, bi0);
+ b0->current_length = sizeof (ethernet_lacp_pdu_t);
+ b0->current_data = 0;
+ b0->total_length_not_including_first_buffer = 0;
+
+ /* And the outbound interface */
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = hw->sw_if_index;
+
+ /* And output the packet on the correct interface */
+ f = vlib_get_frame_to_node (vm, hw->output_node_index);
+
+ to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+
+ vlib_put_frame_to_node (vm, hw->output_node_index, f);
+
+ sif->last_lacpdu_time = vlib_time_now (vm);
+}
+
+/*
+ * Decide which lacp packet template to use
+ */
+static int
+lacp_pick_packet_template (slave_if_t * sif)
+{
+ sif->packet_template_index = LACP_PACKET_TEMPLATE_ETHERNET;
+
+ return 0;
+}
+
+void
+lacp_send_lacp_pdu (vlib_main_t * vm, slave_if_t * sif)
+{
+ lacp_main_t *lm = &lacp_main;
+
+ if (sif->mode != BOND_MODE_LACP)
+ {
+ lacp_stop_timer (&sif->periodic_timer);
+ return;
+ }
+
+ if (sif->packet_template_index == (u8) ~ 0)
+ {
+ /* If we don't know how to talk to this peer, don't try again */
+ if (lacp_pick_packet_template (sif))
+ {
+ lacp_stop_timer (&sif->periodic_timer);
+ return;
+ }
+ }
+
+ switch (sif->packet_template_index)
+ {
+ case LACP_PACKET_TEMPLATE_ETHERNET:
+ lacp_send_ethernet_lacp_pdu (sif);
+ break;
+
+ default:
+ ASSERT (0);
+ }
+
+ lacp_start_periodic_timer (lm->vlib_main, sif, sif->is_long_timeout ?
+ LACP_SLOW_PERIODIC_TIMER :
+ LACP_FAST_PERIODIC_TIMER);
+}
+
+void
+lacp_periodic (vlib_main_t * vm)
+{
+ bond_main_t *bm = &bond_main;
+ lacp_main_t *lm = &lacp_main;
+ slave_if_t *sif;
+
+ /* *INDENT-OFF* */
+ pool_foreach (sif, bm->neighbors,
+ ({
+ if (sif->port_enabled == 0)
+ continue;
+
+ if (lacp_timer_is_running (sif->current_while_timer) &&
+ lacp_timer_is_expired (lm->vlib_main, sif->current_while_timer))
+ {
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_TIMER_EXPIRED, &sif->rx_state);
+ }
+
+ if (lacp_timer_is_running (sif->periodic_timer) &&
+ lacp_timer_is_expired (lm->vlib_main, sif->periodic_timer))
+ {
+ lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
+ LACP_PTX_EVENT_TIMER_EXPIRED, &sif->ptx_state);
+ }
+ if (lacp_timer_is_running (sif->wait_while_timer) &&
+ lacp_timer_is_expired (lm->vlib_main, sif->wait_while_timer))
+ {
+ sif->ready_n = 1;
+ lacp_stop_timer (&sif->wait_while_timer);
+ lacp_selection_logic (vm, sif);
+ }
+ }));
+ /* *INDENT-ON* */
+}
+
+static void
+lacp_interface_enable_disable (vlib_main_t * vm, bond_if_t * bif,
+ slave_if_t * sif, u8 enable)
+{
+ lacp_main_t *lm = &lacp_main;
+ uword port_number;
+
+ if (enable)
+ {
+ port_number = clib_bitmap_first_clear (bif->port_number_bitmap);
+ bif->port_number_bitmap = clib_bitmap_set (bif->port_number_bitmap,
+ port_number, 1);
+ // bitmap starts at 0. Our port number starts at 1.
+ lacp_init_neighbor (sif, bif->hw_address, port_number + 1, sif->group);
+ lacp_init_state_machines (vm, sif);
+ lm->lacp_int++;
+ if (lm->lacp_int == 1)
+ {
+ vlib_process_signal_event (vm, lm->lacp_process_node_index,
+ LACP_PROCESS_EVENT_START, 0);
+ }
+ }
+ else
+ {
+ lm->lacp_int--;
+ if (lm->lacp_int == 0)
+ {
+ vlib_process_signal_event (vm, lm->lacp_process_node_index,
+ LACP_PROCESS_EVENT_STOP, 0);
+ }
+ }
+}
+
+static clib_error_t *
+lacp_periodic_init (vlib_main_t * vm)
+{
+ lacp_main_t *lm = &lacp_main;
+ ethernet_lacp_pdu_t h;
+ ethernet_marker_pdu_t m;
+ u8 dst[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 };
+
+ /* initialize binary API */
+ lacp_plugin_api_hookup (vm);
+
+ /* Create the ethernet lacp packet template */
+
+ memset (&h, 0, sizeof (h));
+
+ memcpy (h.ethernet.dst_address, dst, sizeof (h.ethernet.dst_address));
+
+ /* leave src address blank (fill in at send time) */
+
+ h.ethernet.type = htons (ETHERNET_TYPE_SLOW_PROTOCOLS);
+
+ h.lacp.subtype = LACP_SUBTYPE;
+ h.lacp.version_number = LACP_ACTOR_LACP_VERSION;
+
+ /* Actor TLV */
+ h.lacp.actor.tlv_type = LACP_ACTOR_INFORMATION;
+ h.lacp.actor.tlv_length = sizeof (lacp_actor_partner_t);
+
+ /* Partner TLV */
+ h.lacp.partner.tlv_type = LACP_PARTNER_INFORMATION;
+ h.lacp.partner.tlv_length = sizeof (lacp_actor_partner_t);
+
+ /* Collector TLV */
+ h.lacp.collector.tlv_type = LACP_COLLECTOR_INFORMATION;
+ h.lacp.collector.tlv_length = sizeof (lacp_collector_t);
+ h.lacp.collector.max_delay = 0;
+
+ /* Terminator TLV */
+ h.lacp.terminator.tlv_type = LACP_TERMINATOR_INFORMATION;
+ h.lacp.terminator.tlv_length = 0;
+
+ vlib_packet_template_init
+ (vm, &lm->packet_templates[LACP_PACKET_TEMPLATE_ETHERNET],
+ /* data */ &h,
+ sizeof (h),
+ /* alloc chunk size */ 8,
+ "lacp-ethernet");
+
+ /* Create the ethernet marker protocol packet template */
+
+ memset (&m, 0, sizeof (m));
+
+ memcpy (m.ethernet.dst_address, dst, sizeof (m.ethernet.dst_address));
+
+ /* leave src address blank (fill in at send time) */
+
+ m.ethernet.type = htons (ETHERNET_TYPE_SLOW_PROTOCOLS);
+
+ m.marker.subtype = MARKER_SUBTYPE;
+ m.marker.version_number = MARKER_PROTOCOL_VERSION;
+
+ m.marker.marker_info.tlv_length = sizeof (marker_information_t);
+
+ /* Terminator TLV */
+ m.marker.terminator.tlv_type = MARKER_TERMINATOR_INFORMATION;
+ m.marker.terminator.tlv_length = 0;
+
+ vlib_packet_template_init
+ (vm, &lm->marker_packet_templates[MARKER_PACKET_TEMPLATE_ETHERNET],
+ /* data */ &m,
+ sizeof (m),
+ /* alloc chunk size */ 8,
+ "marker-ethernet");
+
+ bond_register_callback (lacp_interface_enable_disable);
+
+ return 0;
+}
+
+int
+lacp_machine_dispatch (lacp_machine_t * machine, vlib_main_t * vm,
+ slave_if_t * sif, int event, int *state)
+{
+ lacp_fsm_state_t *transition;
+ int rc = 0;
+
+ transition = &machine->tables[*state].state_table[event];
+ LACP_DBG2 (sif, event, *state, machine, transition);
+ *state = transition->next_state;
+ if (transition->action)
+ rc = (*transition->action) ((void *) vm, (void *) sif);
+
+ return rc;
+}
+
+void
+lacp_init_neighbor (slave_if_t * sif, u8 * hw_address, u16 port_number,
+ u32 group)
+{
+ lacp_stop_timer (&sif->wait_while_timer);
+ lacp_stop_timer (&sif->current_while_timer);
+ lacp_stop_timer (&sif->actor_churn_timer);
+ lacp_stop_timer (&sif->partner_churn_timer);
+ lacp_stop_timer (&sif->periodic_timer);
+ lacp_stop_timer (&sif->last_lacpdu_time);
+ sif->lacp_enabled = 1;
+ sif->loopback_port = 0;
+ sif->ready = 0;
+ sif->ready_n = 0;
+ sif->port_moved = 0;
+ sif->ntt = 0;
+ sif->selected = LACP_PORT_UNSELECTED;
+ sif->actor.state = LACP_STATE_AGGREGATION;
+ if (sif->ttl_in_seconds == LACP_SHORT_TIMOUT_TIME)
+ sif->actor.state |= LACP_STATE_LACP_TIMEOUT;
+ if (sif->is_passive == 0)
+ sif->actor.state |= LACP_STATE_LACP_ACTIVITY;
+ clib_memcpy (sif->actor.system, hw_address, 6);
+ sif->actor.system_priority = htons (LACP_DEFAULT_SYSTEM_PRIORITY);
+ sif->actor.key = htons (group);
+ sif->actor.port_number = htons (port_number);
+ sif->actor.port_priority = htons (LACP_DEFAULT_PORT_PRIORITY);
+
+ sif->partner.system_priority = htons (LACP_DEFAULT_SYSTEM_PRIORITY);
+ sif->partner.key = htons (group);
+ sif->partner.port_number = htons (port_number);
+ sif->partner.port_priority = htons (LACP_DEFAULT_PORT_PRIORITY);
+ sif->partner.key = htons (group);
+ sif->partner.state = LACP_STATE_LACP_ACTIVITY;
+
+ sif->actor_admin = sif->actor;
+ sif->partner_admin = sif->partner;
+}
+
+void
+lacp_init_state_machines (vlib_main_t * vm, slave_if_t * sif)
+{
+ lacp_init_tx_machine (vm, sif);
+ lacp_init_mux_machine (vm, sif);
+ lacp_init_ptx_machine (vm, sif);
+ lacp_init_rx_machine (vm, sif);
+}
+
+VLIB_INIT_FUNCTION (lacp_periodic_init);
+
+static clib_error_t *
+lacp_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
+{
+ lacp_main_t *lm = &lacp_main;
+ slave_if_t *sif;
+ vlib_main_t *vm = lm->vlib_main;
+
+ sif = bond_get_slave_by_sw_if_index (sw_if_index);
+ if (sif)
+ {
+ sif->port_enabled = flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
+ if (sif->port_enabled == 0)
+ {
+ if (sif->lacp_enabled)
+ {
+ lacp_init_state_machines (vm, sif);
+ lacp_init_neighbor (sif, sif->actor_admin.system,
+ ntohs (sif->actor_admin.port_number),
+ ntohs (sif->actor_admin.key));
+ }
+ }
+ }
+
+ return 0;
+}
+
+VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (lacp_sw_interface_up_down);
+
+static clib_error_t *
+lacp_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
+{
+ lacp_main_t *lm = &lacp_main;
+ slave_if_t *sif;
+ vnet_sw_interface_t *sw;
+ vlib_main_t *vm = lm->vlib_main;
+ vnet_interface_main_t *im = &vnm->interface_main;
+
+ sw = pool_elt_at_index (im->sw_interfaces, hw_if_index);
+ sif = bond_get_slave_by_sw_if_index (sw->sw_if_index);
+ if (sif)
+ {
+ if (!(flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
+ {
+ if (sif->lacp_enabled)
+ {
+ lacp_init_state_machines (vm, sif);
+ lacp_init_neighbor (sif, sif->actor_admin.system,
+ ntohs (sif->actor_admin.port_number),
+ ntohs (sif->actor_admin.key));
+ }
+ }
+ }
+
+ return 0;
+}
+
+VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (lacp_hw_interface_up_down);
+
+/* *INDENT-OFF* */
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "Link Aggregation Control Protocol",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/lacp_all_api_h.h b/src/plugins/lacp/lacp_all_api_h.h
new file mode 100644
index 00000000000..188c8fd3fb6
--- /dev/null
+++ b/src/plugins/lacp/lacp_all_api_h.h
@@ -0,0 +1,18 @@
+/*
+ * lacp_all_api_h.h - plug-in api #include file
+ *
+ * Copyright (c) <current-year> <your-organization>
+ * 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 the generated file, see BUILT_SOURCES in Makefile.am */
+#include <lacp/lacp.api.h>
diff --git a/src/plugins/lacp/lacp_api.c b/src/plugins/lacp/lacp_api.c
new file mode 100644
index 00000000000..129c3605527
--- /dev/null
+++ b/src/plugins/lacp/lacp_api.c
@@ -0,0 +1,217 @@
+/*
+ *------------------------------------------------------------------
+ * lacp_api.c - lacp api
+ *
+ * Copyright (c) 2017 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 <vlib/vlib.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vlib/unix/unix.h>
+#include <lacp/node.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+
+/* define message IDs */
+#include <lacp/lacp_msg_enum.h>
+
+/* define message structures */
+#define vl_typedefs
+#include <lacp/lacp_all_api_h.h>
+#undef vl_typedefs
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include <lacp/lacp_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <lacp/lacp_all_api_h.h>
+#undef vl_printfun
+
+/* Get the API version number */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <lacp/lacp_all_api_h.h>
+#undef vl_api_version
+
+/*
+ * A handy macro to set up a message reply.
+ * Assumes that the following variables are available:
+ * mp - pointer to request message
+ * rmp - pointer to reply message type
+ * rv - return value
+ */
+#define REPLY_MACRO(t) \
+do { \
+ svm_queue_t * q = \
+ vl_api_client_index_to_input_queue (mp->client_index); \
+ if (!q) \
+ return; \
+ \
+ rmp = vl_msg_api_alloc (sizeof (*rmp)); \
+ rmp->_vl_msg_id = htons ((t)+lm->msg_id_base); \
+ rmp->context = mp->context; \
+ rmp->retval = htonl (rv); \
+ \
+ vl_msg_api_send_shmem (q, (u8 *)&rmp); \
+} while(0);
+
+#define REPLY_MACRO2(t, body) \
+do { \
+ svm_queue_t * q = \
+ vl_api_client_index_to_input_queue (mp->client_index); \
+ if (!q) \
+ return; \
+ \
+ rmp = vl_msg_api_alloc (sizeof (*rmp)); \
+ rmp->_vl_msg_id = htons ((t)+lm->msg_id_base); \
+ rmp->context = mp->context; \
+ rmp->retval = htonl (rv); \
+ do {body;} while (0); \
+ vl_msg_api_send_shmem (q, (u8 *)&rmp); \
+} while(0);
+
+#define foreach_lacp_plugin_api_msg \
+_(SW_INTERFACE_LACP_DUMP, sw_interface_lacp_dump)
+
+static void
+lacp_send_sw_interface_details (vl_api_registration_t * reg,
+ lacp_interface_details_t * lacp_if,
+ u32 context)
+{
+ lacp_main_t *lm = &lacp_main;
+ vl_api_sw_interface_lacp_details_t *mp;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = htons (VL_API_SW_INTERFACE_LACP_DETAILS + lm->msg_id_base);
+ mp->sw_if_index = htonl (lacp_if->sw_if_index);
+
+ /* These fields in network order already */
+ mp->actor_system_priority = lacp_if->actor_system_priority;
+ mp->actor_key = lacp_if->actor_key;
+ mp->actor_port_priority = lacp_if->actor_port_priority;
+ mp->actor_port_number = lacp_if->actor_port_number;
+ mp->actor_state = lacp_if->actor_state;
+ clib_memcpy (mp->actor_system, lacp_if->actor_system, 6);
+ mp->partner_system_priority = lacp_if->partner_system_priority;
+ mp->partner_key = lacp_if->partner_key;
+ mp->partner_port_priority = lacp_if->partner_port_priority;
+ mp->partner_port_number = lacp_if->partner_port_number;
+ mp->partner_state = lacp_if->partner_state;
+
+ clib_memcpy (mp->partner_system, lacp_if->partner_system, 6);
+ clib_memcpy (mp->interface_name, lacp_if->interface_name,
+ MIN (ARRAY_LEN (mp->interface_name) - 1,
+ strlen ((const char *) lacp_if->interface_name)));
+ clib_memcpy (mp->bond_interface_name, lacp_if->bond_interface_name,
+ MIN (ARRAY_LEN (mp->bond_interface_name) - 1,
+ strlen ((const char *) lacp_if->bond_interface_name)));
+ mp->rx_state = htonl (lacp_if->rx_state);
+ mp->tx_state = htonl (lacp_if->tx_state);
+ mp->mux_state = htonl (lacp_if->mux_state);
+ mp->ptx_state = htonl (lacp_if->ptx_state);
+
+ mp->context = context;
+ vl_api_send_msg (reg, (u8 *) mp);
+}
+
+/**
+ * @brief Message handler for lacp_dump API.
+ * @param mp vl_api_lacp_dump_t * mp the api message
+ */
+void
+vl_api_sw_interface_lacp_dump_t_handler (vl_api_sw_interface_lacp_dump_t * mp)
+{
+ int rv;
+ vl_api_registration_t *reg;
+ lacp_interface_details_t *lacpifs = NULL;
+ lacp_interface_details_t *lacp_if = NULL;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ rv = lacp_dump_ifs (&lacpifs);
+ if (rv)
+ return;
+
+ vec_foreach (lacp_if, lacpifs)
+ {
+ lacp_send_sw_interface_details (reg, lacp_if, mp->context);
+ }
+
+ vec_free (lacpifs);
+}
+
+#define vl_msg_name_crc_list
+#include <lacp/lacp_all_api_h.h>
+#undef vl_msg_name_crc_list
+
+static void
+setup_message_id_table (lacp_main_t * lm, api_main_t * am)
+{
+#define _(id,n,crc) \
+ vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + lm->msg_id_base);
+ foreach_vl_msg_name_crc_lacp;
+#undef _
+}
+
+/* Set up the API message handling tables */
+clib_error_t *
+lacp_plugin_api_hookup (vlib_main_t * vm)
+{
+ lacp_main_t *lm = &lacp_main;
+ api_main_t *am = &api_main;
+ u8 *name;
+
+ /* Construct the API name */
+ name = format (0, "lacp_%08x%c", api_version, 0);
+
+ /* Ask for a correctly-sized block of API message decode slots */
+ lm->msg_id_base = vl_msg_api_get_msg_ids
+ ((char *) name, VL_MSG_FIRST_AVAILABLE);
+
+#define _(N,n) \
+ vl_msg_api_set_handlers((VL_API_##N + lm->msg_id_base), \
+ #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_lacp_plugin_api_msg;
+#undef _
+
+ /*
+ * Set up the (msg_name, crc, message-id) table
+ */
+ setup_message_id_table (lm, am);
+
+ vec_free (name);
+ return 0;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/lacp_doc.md b/src/plugins/lacp/lacp_doc.md
new file mode 100644
index 00000000000..f196e0aa60b
--- /dev/null
+++ b/src/plugins/lacp/lacp_doc.md
@@ -0,0 +1,92 @@
+# VPP Link Aggregation Control Protocol (LACP) implementation {#lacp_doc}
+
+This document is to describe the usage of VPP LACP implementation.
+
+
+## LACP
+
+The Link Aggregation Control Protocol (LACP) is an 802.3ad standard which
+provides a protocol for exchanging information between Partner Systems on a
+link to allow their protocol instances to reach agreement on the Link Aggregation
+Group to which the link belongs and enable transmission and reception for the
+higher layer. Multiple links may be bundled to the same Aggregation Group to form
+a high bandwidth transmission medium and create a fault-tolerant link.
+
+
+### Configuration
+
+1. Create the bond interface
+create bond mode lacp [hw-addr <mac-address>] [load-balance { l2 | l23 | l34 }]
+
+2. Enslave the physical interface to the bond
+enslave interface <interface> to <bond-interface-name> [passive] [long-timeout]"
+
+3. Delete the bond interface
+delete bond {<interface> | sw_if_index <sw_idx>}
+
+4. Detach the slave interface from the bond
+detach interface <interface>
+
+### Configuration example
+
+create bond mode lacp
+set interface state BondEthernet0 up
+enslave interface TenGigabitEthernet7/0/0 to BondEthernet1
+enslave interface TenGigabitEthernet7/0/1 to BondEthernet1
+enslave interface TenGigabitEthernet5/0/0 to BondEthernet1
+enslave interface TenGigabitEthernet5/0/1 to BondEthernet1
+
+detach interface TenGigabitEthernet5/0/1
+
+delete bond BondEthernet0
+
+### Operational data
+
+show lacp [<interface>] [details]
+
+Example:
+
+show lacp
+
+
+DBGvpp# sh lacp
+sh lacp
+ actor state partner state
+interface name sw_if_index bond interface exp/def/dis/col/syn/agg/tim/act exp/def/dis/col/syn/agg/tim/act
+GigabitEthernet2/0/1 1 BondEthernet0 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1
+ LAG ID: [(ffff,e4-c7-22-f3-26-71,0000,00ff,0001), (ffff,fc-99-47-4a-0c-8b,0009,00ff,0001)]
+ RX-state: CURRENT, TX-state: TRANSMIT, MUX-state: COLLECTING_DISTRIBUTING, PTX-state: PERIODIC_TX
+TenGigabitEthernet4/0/0 2 BondEthernet1 0 0 1 1 1 1 1 1 0 0 1 1 1 1 0 1
+ LAG ID: [(ffff,90-e2-ba-76-cf-2d,0001,00ff,0001), (8000,00-2a-6a-e5-50-c1,0140,8000,011d)]
+ RX-state: CURRENT, TX-state: TRANSMIT, MUX-state: COLLECTING_DISTRIBUTING, PTX-state: PERIODIC_TX
+TenGigabitEthernet4/0/1 3 BondEthernet1 0 0 1 1 1 1 1 1 0 0 1 1 1 1 0 1
+ LAG ID: [(ffff,90-e2-ba-76-cf-2d,0001,00ff,0002), (8000,00-2a-6a-e5-50-c1,0140,8000,011e)]
+ RX-state: CURRENT, TX-state: TRANSMIT, MUX-state: COLLECTING_DISTRIBUTING, PTX-state: PERIODIC_TX
+TenGigabitEthernet8/0/1 7 BondEthernet1 0 0 1 1 1 1 1 1 0 0 1 1 1 1 0 1
+ LAG ID: [(ffff,90-e2-ba-76-cf-2d,0001,00ff,0003), (8000,00-2a-6a-e5-50-01,007a,8000,0114)]
+ RX-state: CURRENT, TX-state: TRANSMIT, MUX-state: COLLECTING_DISTRIBUTING, PTX-state: PERIODIC_TX
+TenGigabitEthernet8/0/0 6 BondEthernet1 0 0 1 1 1 1 1 1 0 0 1 1 1 1 0 1
+ LAG ID: [(ffff,90-e2-ba-76-cf-2d,0001,00ff,0004), (8000,00-2a-6a-e5-50-01,007a,8000,0115)]
+ RX-state: CURRENT, TX-state: TRANSMIT, MUX-state: COLLECTING_DISTRIBUTING, PTX-state: PERIODIC_TX
+TenGigabitEthernet6/0/1 5 BondEthernet2 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1
+ LAG ID: [(ffff,90-e2-ba-36-31-21,0002,00ff,0001), (ffff,90-e2-ba-29-f5-31,000f,00ff,0002)]
+ RX-state: CURRENT, TX-state: TRANSMIT, MUX-state: COLLECTING_DISTRIBUTING, PTX-state: PERIODIC_TX
+TenGigabitEthernet6/0/0 4 BondEthernet2 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1
+ LAG ID: [(ffff,90-e2-ba-36-31-21,0002,00ff,0002), (ffff,90-e2-ba-29-f5-31,000f,00ff,0001)]
+ RX-state: CURRENT, TX-state: TRANSMIT, MUX-state: COLLECTING_DISTRIBUTING, PTX-state: PERIODIC_TX
+DBGvpp#
+
+show bond [details]
+
+
+DBGvpp# sh bond
+sh bond
+interface name sw_if_index mode load balance active slaves slaves
+BondEthernet0 10 lacp l2 1 1
+BondEthernet1 11 lacp l34 4 4
+BondEthernet2 12 lacp l23 2 2
+DBGvpp#
+
+### Debugging
+
+debug lacp [<interface>] <on | off> \ No newline at end of file
diff --git a/src/plugins/lacp/lacp_msg_enum.h b/src/plugins/lacp/lacp_msg_enum.h
new file mode 100644
index 00000000000..138683fe3e5
--- /dev/null
+++ b/src/plugins/lacp/lacp_msg_enum.h
@@ -0,0 +1,31 @@
+/*
+ * lacp_msg_enum.h - vpp engine plug-in message enumeration
+ *
+ * Copyright (c) <current-year> <your-organization>
+ * 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.
+ */
+#ifndef included_lacp_msg_enum_h
+#define included_lacp_msg_enum_h
+
+#include <vppinfra/byte_order.h>
+
+#define vl_msg_id(n,h) n,
+typedef enum
+{
+#include <lacp/lacp_all_api_h.h>
+ /* We'll want to know how many messages IDs we need... */
+ VL_MSG_FIRST_AVAILABLE,
+} vl_msg_id_t;
+#undef vl_msg_id
+
+#endif /* included_lacp_msg_enum_h */
diff --git a/src/plugins/lacp/lacp_test.c b/src/plugins/lacp/lacp_test.c
new file mode 100644
index 00000000000..0a8631d4df1
--- /dev/null
+++ b/src/plugins/lacp/lacp_test.c
@@ -0,0 +1,231 @@
+/*
+ * lacp VAT support
+ *
+ * Copyright (c) 2017 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 <inttypes.h>
+
+#include <vat/vat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#include <vppinfra/error.h>
+#include <lacp/node.h>
+
+#define __plugin_msg_base lacp_test_main.msg_id_base
+#include <vlibapi/vat_helper_macros.h>
+
+/* declare message IDs */
+#include <lacp/lacp_msg_enum.h>
+
+/* Get CRC codes of the messages defined outside of this plugin */
+#define vl_msg_name_crc_list
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_msg_name_crc_list
+
+/* define message structures */
+#define vl_typedefs
+#include <vpp/api/vpe_all_api_h.h>
+#include <lacp/lacp_all_api_h.h>
+#undef vl_typedefs
+
+/* declare message handlers for each api */
+
+#define vl_endianfun /* define message structures */
+#include <lacp/lacp_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...)
+#define vl_printfun
+#include <lacp/lacp_all_api_h.h>
+#undef vl_printfun
+
+/* Get the API version number. */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <lacp/lacp_all_api_h.h>
+#undef vl_api_version
+
+typedef struct
+{
+ /* API message ID base */
+ u16 msg_id_base;
+ u32 ping_id;
+ vat_main_t *vat_main;
+} lacp_test_main_t;
+
+lacp_test_main_t lacp_test_main;
+
+/*
+ * Table of message reply handlers, must include boilerplate handlers
+ * we just generated
+ */
+#define foreach_vpe_api_reply_msg \
+_(SW_INTERFACE_LACP_DETAILS, sw_interface_lacp_details)
+
+/* lacp-dump API */
+static void vl_api_sw_interface_lacp_details_t_handler
+ (vl_api_sw_interface_lacp_details_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+
+ fformat (vam->ofp,
+ "%-25s %-12d %-16s %3x %3x %3x %3x %3x %3x %3x %3x "
+ "%4x %3x %3x %3x %3x %3x %3x %3x\n",
+ mp->interface_name, ntohl (mp->sw_if_index),
+ mp->bond_interface_name,
+ lacp_bit_test (mp->actor_state, 7),
+ lacp_bit_test (mp->actor_state, 6),
+ lacp_bit_test (mp->actor_state, 5),
+ lacp_bit_test (mp->actor_state, 4),
+ lacp_bit_test (mp->actor_state, 3),
+ lacp_bit_test (mp->actor_state, 2),
+ lacp_bit_test (mp->actor_state, 1),
+ lacp_bit_test (mp->actor_state, 0),
+ lacp_bit_test (mp->partner_state, 7),
+ lacp_bit_test (mp->partner_state, 6),
+ lacp_bit_test (mp->partner_state, 5),
+ lacp_bit_test (mp->partner_state, 4),
+ lacp_bit_test (mp->partner_state, 3),
+ lacp_bit_test (mp->partner_state, 2),
+ lacp_bit_test (mp->partner_state, 1),
+ lacp_bit_test (mp->partner_state, 0));
+ fformat (vam->ofp,
+ " LAG ID: [(%04x,%02x-%02x-%02x-%02x-%02x-%02x,%04x,%04x,%04x), "
+ "(%04x,%02x-%02x-%02x-%02x-%02x-%02x,%04x,%04x,%04x)]\n",
+ ntohs (mp->actor_system_priority), mp->actor_system[0],
+ mp->actor_system[1], mp->actor_system[2], mp->actor_system[3],
+ mp->actor_system[4], mp->actor_system[5], ntohs (mp->actor_key),
+ ntohs (mp->actor_port_priority), ntohs (mp->actor_port_number),
+ ntohs (mp->partner_system_priority), mp->partner_system[0],
+ mp->partner_system[1], mp->partner_system[2],
+ mp->partner_system[3], mp->partner_system[4],
+ mp->partner_system[5], ntohs (mp->partner_key),
+ ntohs (mp->partner_port_priority),
+ ntohs (mp->partner_port_number));
+ fformat (vam->ofp,
+ " RX-state: %U, TX-state: %U, MUX-state: %U, PTX-state: %U\n",
+ format_rx_sm_state, ntohl (mp->rx_state), format_tx_sm_state,
+ ntohl (mp->tx_state), format_mux_sm_state, ntohl (mp->mux_state),
+ format_ptx_sm_state, ntohl (mp->ptx_state));
+}
+
+static int
+api_sw_interface_lacp_dump (vat_main_t * vam)
+{
+ lacp_test_main_t *lm = &lacp_test_main;
+ vl_api_sw_interface_lacp_dump_t *mp;
+ vl_api_control_ping_t *mp_ping;
+ int ret;
+
+ if (vam->json_output)
+ {
+ clib_warning ("JSON output not supported for sw_interface_lacp_dump");
+ return -99;
+ }
+
+ fformat (vam->ofp, "%-55s %-32s %-32s\n", " ", "actor state",
+ "partner state");
+ fformat (vam->ofp, "%-25s %-12s %-16s %-31s %-31s\n", "interface name",
+ "sw_if_index", "bond interface", "exp/def/dis/col/syn/agg/tim/act",
+ "exp/def/dis/col/syn/agg/tim/act");
+
+ /* Get list of lacp interfaces */
+ M (SW_INTERFACE_LACP_DUMP, mp);
+ S (mp);
+
+ /* Use a control ping for synchronization */
+ mp_ping = vl_msg_api_alloc_as_if_client (sizeof (*mp_ping));
+ mp_ping->_vl_msg_id = htons (lm->ping_id);
+ mp_ping->client_index = vam->my_client_index;
+
+ fformat (vam->ofp, "Sending ping id=%d\n", lm->ping_id);
+
+ vam->result_ready = 0;
+ S (mp_ping);
+
+ W (ret);
+ return ret;
+}
+
+/*
+ * List of messages that the api test plugin sends,
+ * and that the data plane plugin processes
+ */
+#define foreach_vpe_api_msg \
+_(sw_interface_lacp_dump, "")
+
+static void
+lacp_vat_api_hookup (vat_main_t * vam)
+{
+ lacp_test_main_t *lm __attribute__ ((unused)) = &lacp_test_main;
+ /* Hook up handlers for replies from the data plane plug-in */
+#define _(N,n) \
+ vl_msg_api_set_handlers((VL_API_##N + lm->msg_id_base), \
+ #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_vpe_api_reply_msg;
+#undef _
+
+ /* API messages we can send */
+#define _(n,h) \
+ hash_set_mem (vam->function_by_name, #n, api_##n);
+ foreach_vpe_api_msg;
+#undef _
+
+ /* Help strings */
+#define _(n,h) hash_set_mem (vam->help_by_name, #n, h);
+ foreach_vpe_api_msg;
+#undef _
+}
+
+clib_error_t *
+vat_plugin_register (vat_main_t * vam)
+{
+ lacp_test_main_t *lm = &lacp_test_main;
+ u8 *name;
+
+ lm->vat_main = vam;
+
+ /* Ask the vpp engine for the first assigned message-id */
+ name = format (0, "lacp_%08x%c", api_version, 0);
+ lm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name);
+
+ /* Get the control ping ID */
+#define _(id,n,crc) \
+ const char *id ## _CRC __attribute__ ((unused)) = #n "_" #crc;
+ foreach_vl_msg_name_crc_vpe;
+#undef _
+ lm->ping_id = vl_msg_api_get_msg_index ((u8 *) (VL_API_CONTROL_PING_CRC));
+
+ if (lm->msg_id_base != (u16) ~ 0)
+ lacp_vat_api_hookup (vam);
+
+ vec_free (name);
+
+ return 0;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/machine.h b/src/plugins/lacp/machine.h
new file mode 100644
index 00000000000..0590b6cf101
--- /dev/null
+++ b/src/plugins/lacp/machine.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#ifndef __LACP_MACHINE_H__
+#define __LACP_MACHINE_H__
+
+#include <stdint.h>
+
+#define LACP_NOACTION ((int (*)(void *, void *))0)
+#define LACP_ACTION_ROUTINE(rtn) ((int(*)(void *, void *))rtn)
+
+typedef int (*action_func) (void *, void *);
+
+typedef struct
+{
+ action_func action;
+ int next_state;
+} lacp_fsm_state_t;
+
+typedef void (*debug_func) (slave_if_t * sif, int event, int state,
+ lacp_fsm_state_t * transition);
+
+typedef struct
+{
+ lacp_fsm_state_t *state_table;
+} lacp_fsm_machine_t;
+
+typedef struct
+{
+ lacp_fsm_machine_t *tables;
+ debug_func debug;
+} lacp_machine_t;
+
+extern int lacp_machine_dispatch (lacp_machine_t * machine, vlib_main_t * vm,
+ slave_if_t * sif, int event, int *state);
+
+#endif /* __LACP_MACHINE_H__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/mux_machine.c b/src/plugins/lacp/mux_machine.c
new file mode 100644
index 00000000000..f33c2642f20
--- /dev/null
+++ b/src/plugins/lacp/mux_machine.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <vlib/vlib.h>
+#include <vnet/bonding/node.h>
+#include <lacp/node.h>
+
+/*
+ * LACP State = DETACHED
+ */
+static lacp_fsm_state_t lacp_mux_state_detached[] = {
+ {LACP_ACTION_DETACHED, LACP_MUX_STATE_DETACHED}, // event 0 BEGIN
+ {LACP_ACTION_WAITING, LACP_MUX_STATE_WAITING}, // event 1 SELECTED
+ {LACP_ACTION_WAITING, LACP_MUX_STATE_WAITING}, // event 2 STANDBY
+ {LACP_ACTION_DETACHED, LACP_MUX_STATE_DETACHED}, // event 3 UNSELECTED
+ {LACP_ACTION_DETACHED, LACP_MUX_STATE_DETACHED}, // event 4 READY
+ {LACP_ACTION_DETACHED, LACP_MUX_STATE_DETACHED}, // event 5 SYNC
+};
+
+/*
+ * LACP State = WAITING
+ */
+static lacp_fsm_state_t lacp_mux_state_waiting[] = {
+ {LACP_ACTION_DETACHED, LACP_MUX_STATE_DETACHED}, // event 0 BEGIN
+ {LACP_ACTION_WAITING, LACP_MUX_STATE_WAITING}, // event 1 SELECTED
+ {LACP_ACTION_WAITING, LACP_MUX_STATE_WAITING}, // event 2 STANDBY
+ {LACP_ACTION_DETACHED, LACP_MUX_STATE_DETACHED}, // event 3 UNSELECTED
+ {LACP_ACTION_ATTACHED, LACP_MUX_STATE_ATTACHED}, // event 4 READY
+ {LACP_ACTION_WAITING, LACP_MUX_STATE_WAITING}, // event 5 SYNC
+};
+
+/*
+ * LACP State = ATTACHED
+ */
+static lacp_fsm_state_t lacp_mux_state_attached[] = {
+ {LACP_ACTION_DETACHED, LACP_MUX_STATE_DETACHED}, // event 0 BEGIN
+ {LACP_ACTION_ATTACHED, LACP_MUX_STATE_ATTACHED}, // event 1 SELECTED
+ {LACP_ACTION_DETACHED, LACP_MUX_STATE_DETACHED}, // event 2 STANDBY
+ {LACP_ACTION_DETACHED, LACP_MUX_STATE_DETACHED}, // event 3 UNSELECTED
+ {LACP_ACTION_ATTACHED, LACP_MUX_STATE_ATTACHED}, // event 4 READY
+ {LACP_ACTION_COLLECTING_DISTRIBUTING, LACP_MUX_STATE_COLLECTING_DISTRIBUTING}, // event 5_SYNC
+};
+
+/*
+ * LACP State = COLLECTING_DISTRIBUTING
+ */
+static lacp_fsm_state_t lacp_mux_state_collecting_distributing[] = {
+ {LACP_ACTION_DETACHED, LACP_MUX_STATE_DETACHED}, // event 0 BEGIN
+ {LACP_ACTION_COLLECTING_DISTRIBUTING, LACP_MUX_STATE_COLLECTING_DISTRIBUTING}, // event 1 SELECTED
+ {LACP_ACTION_COLLECTING_DISTRIBUTING, LACP_MUX_STATE_COLLECTING_DISTRIBUTING}, // event 2 STANDBY
+ {LACP_ACTION_ATTACHED, LACP_MUX_STATE_ATTACHED}, // event 3 UNSELECTED
+ {LACP_ACTION_COLLECTING_DISTRIBUTING, LACP_MUX_STATE_COLLECTING_DISTRIBUTING}, // event 4 READY
+ {LACP_ACTION_COLLECTING_DISTRIBUTING, LACP_MUX_STATE_COLLECTING_DISTRIBUTING}, // event 5 SYNC
+};
+
+static lacp_fsm_machine_t lacp_mux_fsm_table[] = {
+ {lacp_mux_state_detached},
+ {lacp_mux_state_waiting},
+ {lacp_mux_state_attached},
+ {lacp_mux_state_collecting_distributing},
+};
+
+lacp_machine_t lacp_mux_machine = {
+ lacp_mux_fsm_table,
+ lacp_mux_debug_func,
+};
+
+static void
+lacp_detach_mux_from_aggregator (vlib_main_t * vm, slave_if_t * sif)
+{
+ sif->actor.state &= ~LACP_STATE_SYNCHRONIZATION;
+ sif->ready = 0;
+ sif->ready_n = 0;
+}
+
+static void
+lacp_attach_mux_to_aggregator (vlib_main_t * vm, slave_if_t * sif)
+{
+ sif->actor.state |= LACP_STATE_SYNCHRONIZATION;
+}
+
+int
+lacp_mux_action_detached (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+
+ lacp_detach_mux_from_aggregator (vm, sif);
+ sif->actor.state &= ~LACP_STATE_COLLECTING;
+ bond_disable_collecting_distributing (vm, sif);
+ sif->actor.state &= ~LACP_STATE_DISTRIBUTING;
+ sif->ntt = 1;
+ lacp_machine_dispatch (&lacp_tx_machine, vm, sif, LACP_TX_EVENT_NTT,
+ &sif->tx_state);
+
+ if (sif->selected == LACP_PORT_SELECTED)
+ lacp_machine_dispatch (&lacp_mux_machine, vm, sif,
+ LACP_MUX_EVENT_SELECTED, &sif->mux_state);
+
+ if (sif->selected == LACP_PORT_STANDBY)
+ lacp_machine_dispatch (&lacp_mux_machine, vm, sif, LACP_MUX_EVENT_STANDBY,
+ &sif->mux_state);
+
+ return 0;
+}
+
+int
+lacp_mux_action_attached (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+
+ lacp_attach_mux_to_aggregator (vm, sif);
+ sif->actor.state &= ~LACP_STATE_COLLECTING;
+ bond_disable_collecting_distributing (vm, sif);
+ sif->actor.state &= ~LACP_STATE_DISTRIBUTING;
+ sif->ntt = 1;
+ lacp_machine_dispatch (&lacp_tx_machine, vm, sif, LACP_TX_EVENT_NTT,
+ &sif->tx_state);
+
+ if ((sif->selected == LACP_PORT_UNSELECTED) ||
+ (sif->selected == LACP_PORT_STANDBY))
+ lacp_machine_dispatch (&lacp_mux_machine, vm, sif,
+ LACP_MUX_EVENT_UNSELECTED, &sif->mux_state);
+
+ if ((sif->selected == LACP_PORT_SELECTED) &&
+ (sif->partner.state & LACP_STATE_SYNCHRONIZATION))
+ lacp_machine_dispatch (&lacp_mux_machine, vm, sif, LACP_MUX_EVENT_SYNC,
+ &sif->mux_state);
+ return 0;
+}
+
+int
+lacp_mux_action_waiting (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+ lacp_main_t *lm = &lacp_main;
+
+ if (!lacp_timer_is_running (sif->wait_while_timer))
+ lacp_start_wait_while_timer (lm->vlib_main, sif,
+ LACP_AGGREGATE_WAIT_TIME);
+
+ if ((sif->selected == LACP_PORT_SELECTED) && sif->ready)
+ lacp_machine_dispatch (&lacp_mux_machine, vm, sif,
+ LACP_MUX_EVENT_READY, &sif->mux_state);
+
+ if (sif->selected == LACP_PORT_UNSELECTED)
+ lacp_machine_dispatch (&lacp_mux_machine, vm, sif,
+ LACP_MUX_EVENT_UNSELECTED, &sif->mux_state);
+
+ return 0;
+}
+
+int
+lacp_mux_action_collecting_distributing (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+
+ sif->actor.state |= LACP_STATE_SYNCHRONIZATION | LACP_STATE_COLLECTING |
+ LACP_STATE_DISTRIBUTING;
+ bond_enable_collecting_distributing (vm, sif);
+ sif->ntt = 1;
+ lacp_machine_dispatch (&lacp_tx_machine, vm, sif, LACP_TX_EVENT_NTT,
+ &sif->tx_state);
+ if ((sif->selected == LACP_PORT_UNSELECTED) ||
+ (sif->selected == LACP_PORT_STANDBY) ||
+ !(sif->partner.state & LACP_STATE_SYNCHRONIZATION))
+ lacp_machine_dispatch (&lacp_mux_machine, vm, sif,
+ LACP_MUX_EVENT_UNSELECTED, &sif->mux_state);
+
+
+ return 0;
+}
+
+static u8 *
+format_mux_event (u8 * s, va_list * args)
+{
+ static lacp_event_struct lacp_mux_event_array[] = {
+#define _(b, s, n) {.bit = b, .str = #s, },
+ foreach_lacp_mux_event
+#undef _
+ {.str = NULL}
+ };
+ int e = va_arg (*args, int);
+ lacp_event_struct *event_entry =
+ (lacp_event_struct *) & lacp_mux_event_array;
+
+ if (e >= (sizeof (lacp_mux_event_array) / sizeof (*event_entry)))
+ s = format (s, "Bad event %d", e);
+ else
+ s = format (s, "%s", event_entry[e].str);
+
+ return s;
+}
+
+void
+lacp_mux_debug_func (slave_if_t * sif, int event, int state,
+ lacp_fsm_state_t * transition)
+{
+ clib_warning ("%U-MUX: event %U, old state %U, new state %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sif->sw_if_index, format_mux_event,
+ event, format_mux_sm_state, state, format_mux_sm_state,
+ transition->next_state);
+}
+
+void
+lacp_init_mux_machine (vlib_main_t * vm, slave_if_t * sif)
+{
+ lacp_machine_dispatch (&lacp_mux_machine, vm, sif, LACP_MUX_EVENT_BEGIN,
+ &sif->mux_state);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/mux_machine.h b/src/plugins/lacp/mux_machine.h
new file mode 100644
index 00000000000..48e9a0bed4e
--- /dev/null
+++ b/src/plugins/lacp/mux_machine.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#ifndef __LACP_MUX_MACHINE_H__
+#define __LACP_MUX_MACHINE_H__
+
+#include <stdint.h>
+#include <lacp/machine.h>
+
+#define foreach_lacp_mux_event \
+ _(0, BEGIN, "begin") \
+ _(1, SELECTED, "selected") \
+ _(2, STANDBY, "standby") \
+ _(3, UNSELECTED, "unselected") \
+ _(4, READY, "ready") \
+ _(5, SYNC, "sync")
+
+typedef enum
+{
+#define _(a, b, c) LACP_MUX_EVENT_##b = (a),
+ foreach_lacp_mux_event
+#undef _
+} lacp_mux_event_t;
+
+#define foreach_lacp_mux_sm_state \
+ _(0, DETACHED, "detached") \
+ _(1, WAITING, "waiting") \
+ _(2, ATTACHED, "attached") \
+ _(3, COLLECTING_DISTRIBUTING, "collecting distributing")
+
+typedef enum
+{
+#define _(a, b, c) LACP_MUX_STATE_##b = (a),
+ foreach_lacp_mux_sm_state
+#undef _
+} lacp_mux_sm_state_t;
+
+extern lacp_machine_t lacp_mux_machine;
+
+int lacp_mux_action_detached (void *p1, void *p2);
+int lacp_mux_action_attached (void *p1, void *p2);
+int lacp_mux_action_waiting (void *p1, void *p2);
+int lacp_mux_action_collecting_distributing (void *p1, void *p2);
+void lacp_mux_debug_func (slave_if_t * sif, int event, int state,
+ lacp_fsm_state_t * transition);
+
+#define LACP_ACTION_DETACHED LACP_ACTION_ROUTINE(lacp_mux_action_detached)
+#define LACP_ACTION_ATTACHED LACP_ACTION_ROUTINE(lacp_mux_action_attached)
+#define LACP_ACTION_WAITING LACP_ACTION_ROUTINE(lacp_mux_action_waiting)
+#define LACP_ACTION_COLLECTING_DISTRIBUTING \
+ LACP_ACTION_ROUTINE(lacp_mux_action_collecting_distributing)
+
+static inline void
+lacp_start_wait_while_timer (vlib_main_t * vm, slave_if_t * sif,
+ u8 expiration)
+{
+ sif->wait_while_timer = vlib_time_now (vm) + expiration;
+}
+
+#endif /* __LACP_MUX_MACHINE_H__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/node.c b/src/plugins/lacp/node.c
new file mode 100644
index 00000000000..8eb78876461
--- /dev/null
+++ b/src/plugins/lacp/node.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#define _GNU_SOURCE
+#include <vnet/bonding/node.h>
+#include <vnet/ethernet/packet.h>
+#include <lacp/node.h>
+
+lacp_state_struct lacp_state_array[] = {
+#define _(b, s, n) {.bit = b, .str = #s, },
+ foreach_lacp_state_flag
+#undef _
+ {.str = NULL}
+};
+
+static vlib_node_registration_t lacp_process_node;
+
+/** \file
+
+ 2 x LACP graph nodes: an "interior" node to process
+ incoming announcements, and a "process" node to periodically
+ send announcements.
+
+ The interior node is neither pipelined nor dual-looped, because
+ it would be very unusual to see more than one LACP packet in
+ a given input frame. So, it's a very simple / straighforward
+ example.
+*/
+
+/*
+ * packet counter strings
+ * Dump these counters via the "show error" CLI command
+ */
+static char *lacp_error_strings[] = {
+#define _(sym,string) string,
+ foreach_lacp_error
+#undef _
+};
+
+/*
+ * We actually send all lacp pkts to the "error" node after scanning
+ * them, so the graph node has only one next-index. The "error-drop"
+ * node automatically bumps our per-node packet counters for us.
+ */
+typedef enum
+{
+ LACP_INPUT_NEXT_NORMAL,
+ LACP_INPUT_N_NEXT,
+} lacp_next_t;
+
+/*
+ * Process a frame of lacp packets
+ * Expect 1 packet / frame
+ */
+static uword
+lacp_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ u32 n_left_from, *from;
+ lacp_input_trace_t *t0;
+ uword n_trace = vlib_get_trace_count (vm, node);
+
+ from = vlib_frame_vector_args (frame); /* array of buffer indices */
+ n_left_from = frame->n_vectors; /* number of buffer indices */
+
+ while (n_left_from > 0)
+ {
+ u32 bi0;
+ vlib_buffer_t *b0;
+ u32 next0, error0;
+
+ bi0 = from[0];
+ b0 = vlib_get_buffer (vm, bi0);
+
+ next0 = LACP_INPUT_NEXT_NORMAL;
+
+ /* scan this lacp pkt. error0 is the counter index to bump */
+ error0 = lacp_input (vm, b0, bi0);
+ b0->error = node->errors[error0];
+
+ /* If this pkt is traced, snapshoot the data */
+ if (PREDICT_FALSE (n_trace > 0))
+ {
+ int len;
+ vlib_trace_buffer (vm, node, next0, b0,
+ /* follow_chain */ 0);
+ vlib_set_trace_count (vm, node, --n_trace);
+ t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
+ len = (b0->current_length < sizeof (t0->pkt))
+ ? b0->current_length : sizeof (t0->pkt);
+ t0->len = len;
+ clib_memcpy (&t0->pkt, vlib_buffer_get_current (b0), len);
+ }
+ /* push this pkt to the next graph node, always error-drop */
+ vlib_set_next_frame_buffer (vm, node, next0, bi0);
+
+ from += 1;
+ n_left_from -= 1;
+ }
+
+ return frame->n_vectors;
+}
+
+/*
+ * lacp input graph node declaration
+ */
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (lacp_input_node, static) = {
+ .function = lacp_node_fn,
+ .name = "lacp-input",
+ .vector_size = sizeof (u32),
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = LACP_N_ERROR,
+ .error_strings = lacp_error_strings,
+
+ .format_trace = lacp_input_format_trace,
+
+ .n_next_nodes = LACP_INPUT_N_NEXT,
+ .next_nodes = {
+ [LACP_INPUT_NEXT_NORMAL] = "error-drop",
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * lacp periodic function
+ */
+static uword
+lacp_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+ lacp_main_t *lm = &lacp_main;
+ f64 poll_time_remaining;
+ uword event_type, *event_data = 0;
+ u8 enabled = 0;
+
+ /* So we can send events to the lacp process */
+ lm->lacp_process_node_index = lacp_process_node.index;
+
+ ethernet_register_input_type (vm, ETHERNET_TYPE_SLOW_PROTOCOLS /* LACP */ ,
+ lacp_input_node.index);
+
+ poll_time_remaining = 0.2;
+ while (1)
+ {
+ if (enabled)
+ poll_time_remaining =
+ vlib_process_wait_for_event_or_clock (vm, poll_time_remaining);
+ else
+ vlib_process_wait_for_event (vm);
+
+ event_type = vlib_process_get_events (vm, &event_data);
+ switch (event_type)
+ {
+ case ~0: /* no events => timeout */
+ break;
+ case LACP_PROCESS_EVENT_START:
+ enabled = 1;
+ break;
+ case LACP_PROCESS_EVENT_STOP:
+ enabled = 0;
+ continue;
+ default:
+ clib_warning ("BUG: event type 0x%wx", event_type);
+ break;
+ }
+ if (event_data)
+ _vec_len (event_data) = 0;
+
+ if (vlib_process_suspend_time_is_zero (poll_time_remaining))
+ {
+ lacp_periodic (vm);
+ poll_time_remaining = 0.2;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * lacp periodic node declaration
+ */
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (lacp_process_node, static) = {
+ .function = lacp_process,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "lacp-process",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/node.h b/src/plugins/lacp/node.h
new file mode 100644
index 00000000000..26cf7a36ccc
--- /dev/null
+++ b/src/plugins/lacp/node.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+#ifndef __included_lacp_node_h__
+#define __included_lacp_node_h__
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vppinfra/format.h>
+#include <vppinfra/hash.h>
+#include <lacp/protocol.h>
+#include <lacp/rx_machine.h>
+#include <lacp/tx_machine.h>
+#include <lacp/mux_machine.h>
+#include <lacp/ptx_machine.h>
+
+typedef enum
+{
+ LACP_PACKET_TEMPLATE_ETHERNET,
+ LACP_N_PACKET_TEMPLATES,
+} lacp_packet_template_id_t;
+
+typedef enum
+{
+ MARKER_PACKET_TEMPLATE_ETHERNET,
+ MARKER_N_PACKET_TEMPLATES,
+} marker_packet_template_id_t;
+
+enum
+{
+ LACP_PROCESS_EVENT_START = 1,
+ LACP_PROCESS_EVENT_STOP = 2,
+} lacp_process_event_t;
+
+#define LACP_DBG(n, args...) \
+ { \
+ lacp_main_t *_lm = &lacp_main; \
+ if (_lm->debug || n->debug) \
+ clib_warning (args); \
+ }
+
+#define LACP_DBG2(n, e, s, m, t) \
+ { \
+ lacp_main_t *_lm = &lacp_main; \
+ if ((m)->debug && (_lm->debug || (n)->debug)) \
+ (*m->debug)(n, e, s, t); \
+ }
+
+/* Packet counters */
+#define foreach_lacp_error \
+_ (NONE, "good lacp packets -- consumed") \
+_ (CACHE_HIT, "good lacp packets -- cache hit") \
+_ (UNSUPPORTED, "unsupported slow protocol packets") \
+_ (TOO_SMALL, "bad lacp packets -- packet too small") \
+_ (BAD_TLV, "bad lacp packets -- bad TLV length") \
+_ (DISABLED, "lacp packets received on disabled interfaces")
+
+typedef enum
+{
+#define _(sym,str) LACP_ERROR_##sym,
+ foreach_lacp_error
+#undef _
+ LACP_N_ERROR,
+} lacp_error_t;
+
+/* lacp packet trace capture */
+typedef struct
+{
+ u32 len;
+ union
+ {
+ marker_pdu_t marker;
+ lacp_pdu_t lacpdu;
+ } pkt;
+} lacp_input_trace_t;
+
+/** LACP interface details struct */
+typedef struct
+{
+ u32 sw_if_index;
+ u8 interface_name[64];
+ u32 rx_state;
+ u32 tx_state;
+ u32 mux_state;
+ u32 ptx_state;
+ u8 bond_interface_name[64];
+ u16 actor_system_priority;
+ u8 actor_system[6];
+ u16 actor_key;
+ u16 actor_port_priority;
+ u16 actor_port_number;
+ u8 actor_state;
+ u16 partner_system_priority;
+ u8 partner_system[6];
+ u16 partner_key;
+ u16 partner_port_priority;
+ u16 partner_port_number;
+ u8 partner_state;
+} lacp_interface_details_t;
+
+typedef struct
+{
+ /** API message ID base */
+ u16 msg_id_base;
+
+ /* convenience variables */
+ vlib_main_t *vlib_main;
+ vnet_main_t *vnet_main;
+
+ /* Background process node index */
+ u32 lacp_process_node_index;
+
+ /* Packet templates for different encap types */
+ vlib_packet_template_t packet_templates[LACP_N_PACKET_TEMPLATES];
+
+ /* Packet templates for different encap types */
+ vlib_packet_template_t marker_packet_templates[MARKER_N_PACKET_TEMPLATES];
+
+ /* LACP interface count */
+ u32 lacp_int;
+
+ /* debug is on or off */
+ u8 debug;
+} lacp_main_t;
+
+extern lacp_state_struct lacp_state_array[];
+extern lacp_main_t lacp_main;
+
+clib_error_t *lacp_plugin_api_hookup (vlib_main_t * vm);
+int lacp_dump_ifs (lacp_interface_details_t ** out_bondids);
+lacp_error_t lacp_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0);
+void lacp_periodic (vlib_main_t * vm);
+u8 *lacp_input_format_trace (u8 * s, va_list * args);
+void lacp_init_neighbor (slave_if_t * sif, u8 * hw_address,
+ u16 port_number, u32 group);
+void lacp_init_state_machines (vlib_main_t * vm, slave_if_t * sif);
+void lacp_init_rx_machine (vlib_main_t * vm, slave_if_t * sif);
+void lacp_init_tx_machine (vlib_main_t * vm, slave_if_t * sif);
+void lacp_init_ptx_machine (vlib_main_t * vm, slave_if_t * sif);
+void lacp_init_mux_machine (vlib_main_t * vm, slave_if_t * sif);
+void lacp_selection_logic (vlib_main_t * vm, slave_if_t * sif);
+void lacp_send_lacp_pdu (vlib_main_t * vm, slave_if_t * sif);
+
+static inline void
+lacp_stop_timer (f64 * timer)
+{
+ *timer = 0.0;
+}
+
+static inline u8
+lacp_timer_is_running (f64 timer)
+{
+ return (timer != 0.0);
+}
+
+static inline u8
+lacp_timer_is_expired (vlib_main_t * vm, f64 timer)
+{
+ f64 now = vlib_time_now (vm);
+
+ return (now >= timer);
+}
+
+static inline u8 *
+format_rx_sm_state (u8 * s, va_list * args)
+{
+ lacp_state_struct lacp_rx_sm_state_array[] = {
+#define _(b, s, n) {.bit = b, .str = #s, },
+ foreach_lacp_rx_sm_state
+#undef _
+ {.str = NULL}
+ };
+ int state = va_arg (*args, int);
+ lacp_state_struct *state_entry =
+ (lacp_state_struct *) & lacp_rx_sm_state_array;
+
+ if (state >= (sizeof (lacp_rx_sm_state_array) / sizeof (*state_entry)))
+ s = format (s, "Bad state %d", state);
+ else
+ s = format (s, "%s", state_entry[state].str);
+
+ return s;
+}
+
+static inline u8 *
+format_tx_sm_state (u8 * s, va_list * args)
+{
+ lacp_state_struct lacp_tx_sm_state_array[] = {
+#define _(b, s, n) {.bit = b, .str = #s, },
+ foreach_lacp_tx_sm_state
+#undef _
+ {.str = NULL}
+ };
+ int state = va_arg (*args, int);
+ lacp_state_struct *state_entry =
+ (lacp_state_struct *) & lacp_tx_sm_state_array;
+
+ if (state >= (sizeof (lacp_tx_sm_state_array) / sizeof (*state_entry)))
+ s = format (s, "Bad state %d", state);
+ else
+ s = format (s, "%s", state_entry[state].str);
+
+ return s;
+}
+
+static inline u8 *
+format_mux_sm_state (u8 * s, va_list * args)
+{
+ lacp_state_struct lacp_mux_sm_state_array[] = {
+#define _(b, s, n) {.bit = b, .str = #s, },
+ foreach_lacp_mux_sm_state
+#undef _
+ {.str = NULL}
+ };
+ int state = va_arg (*args, int);
+ lacp_state_struct *state_entry =
+ (lacp_state_struct *) & lacp_mux_sm_state_array;
+
+ if (state >= (sizeof (lacp_mux_sm_state_array) / sizeof (*state_entry)))
+ s = format (s, "Bad state %d", state);
+ else
+ s = format (s, "%s", state_entry[state].str);
+
+ return s;
+}
+
+static inline u8 *
+format_ptx_sm_state (u8 * s, va_list * args)
+{
+ lacp_state_struct lacp_ptx_sm_state_array[] = {
+#define _(b, s, n) {.bit = b, .str = #s, },
+ foreach_lacp_ptx_sm_state
+#undef _
+ {.str = NULL}
+ };
+ int state = va_arg (*args, int);
+ lacp_state_struct *state_entry =
+ (lacp_state_struct *) & lacp_ptx_sm_state_array;
+
+ if (state >= (sizeof (lacp_ptx_sm_state_array) / sizeof (*state_entry)))
+ s = format (s, "Bad state %d", state);
+ else
+ s = format (s, "%s", state_entry[state].str);
+
+ return s;
+}
+
+static inline int
+lacp_bit_test (u8 val, u8 bit)
+{
+ if (val & (1 << bit))
+ return 1;
+ else
+ return 0;
+}
+
+#endif /* __included_lacp_node_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/protocol.h b/src/plugins/lacp/protocol.h
new file mode 100644
index 00000000000..05a3f04a227
--- /dev/null
+++ b/src/plugins/lacp/protocol.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#ifndef __included_lacp_protocol_h__
+#define __included_lacp_protocol_h__
+
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/bonding/node.h>
+
+#define LACP_CHURN_DETECTION_TIME 60
+#define LACP_AGGREGATE_WAIT_TIME 2.0
+
+#define LACP_SUBTYPE 1
+#define LACP_ACTOR_LACP_VERSION 1
+
+#define foreach_lacp_tlv \
+ _ (TERMINATOR_INFORMATION, 0) \
+ _ (ACTOR_INFORMATION, 1) \
+ _ (PARTNER_INFORMATION , 2) \
+ _ (COLLECTOR_INFORMATION, 3)
+
+typedef enum
+{
+#define _(f,n) LACP_##f = (n),
+ foreach_lacp_tlv
+#undef _
+} lacp_tlv_t;
+
+#define foreach_lacp_port \
+ _ (UNSELECTED, 0) \
+ _ (SELECTED, 1) \
+ _ (STANDBY, 2)
+
+typedef enum
+{
+#define _(f,n) LACP_PORT_##f = (n),
+ foreach_lacp_port
+#undef _
+} lacp_port_t;
+
+/* Port state */
+#define foreach_lacp_state \
+ _(0, LACP_ACTIVITY, "activity") \
+ _(1, LACP_TIMEOUT, "lacp timeout") \
+ _(2, AGGREGATION, "aggregation") \
+ _(3, SYNCHRONIZATION, "synchronization") \
+ _(4, COLLECTING, "collecting") \
+ _(5, DISTRIBUTING, "distributing") \
+ _(6, DEFAULTED, "defaulted") \
+ _(7, EXPIRED, "expired")
+
+typedef enum
+{
+#define _(a, b, c) LACP_STATE_##b = (1 << a),
+ foreach_lacp_state
+#undef _
+} lacp_state_t;
+
+#define foreach_lacp_state_flag \
+ _(0, LACP_STATE_LACP_ACTIViTY, "activity") \
+ _(1, LACP_STATE_LACP_TIMEOUT, "lacp timeout") \
+ _(2, LACP_STATE_AGGREGATION, "aggregation") \
+ _(3, LACP_STATE_SYNCHRONIZATION, "synchronization") \
+ _(4, LACP_STATE_COLLECTIING, "collecting") \
+ _(5, LACP_STATE_DISTRIBUTING, "distributing") \
+ _(6, LACP_STATE_DEFAULTED, "defaulted") \
+ _(7, LACP_STATE_EXPIRED, "expired")
+
+typedef struct
+{
+ u8 bit;
+ char *str;
+} lacp_state_struct;
+
+typedef struct
+{
+ u8 bit;
+ char *str;
+} lacp_event_struct;
+
+#define LACP_MAX_TX_IN_SECOND 3
+#define LACP_DEFAULT_PORT_PRIORITY 0x00ff
+#define LACP_DEFAULT_SYSTEM_PRIORITY 0xffff
+
+typedef CLIB_PACKED (struct
+ {
+ u8 tlv_type;
+ u8 tlv_length;
+ lacp_port_info_t port_info; u8 reserved[3];
+ }) lacp_actor_partner_t;
+
+typedef CLIB_PACKED (struct
+ {
+ u8 tlv_type; u8 tlv_length; u16 max_delay;
+ u8 reserved[12];
+ }) lacp_collector_t;
+
+typedef CLIB_PACKED (struct
+ {
+ u8 tlv_type; u8 tlv_length;
+ u8 pad[50];
+ }) lacp_terminator_t;
+
+typedef CLIB_PACKED (struct
+ {
+ u8 subtype; u8 version_number;
+ lacp_actor_partner_t actor; lacp_actor_partner_t partner;
+ lacp_collector_t collector; lacp_terminator_t terminator;
+ }) lacp_pdu_t;
+
+typedef CLIB_PACKED (struct
+ {
+ ethernet_header_t ethernet; lacp_pdu_t lacp;
+ }) ethernet_lacp_pdu_t;
+
+#define MARKER_SUBTYPE 2
+#define MARKER_PROTOCOL_VERSION 1
+
+#define foreach_marker_tlv \
+ _ (TERMINATOR_INFORMATION, 0) \
+ _ (INFORMATION, 1) \
+ _ (RESPONSE_INFORMATION , 2)
+
+typedef enum
+{
+#define _(f,n) MARKER_##f = (n),
+ foreach_marker_tlv
+#undef _
+} marker_tlv_t;
+
+typedef CLIB_PACKED (struct
+ {
+ u8 tlv_type; u8 tlv_length;
+ u8 reserved[90];
+ }) marker_terminator_t;
+
+typedef CLIB_PACKED (struct
+ {
+ u8 tlv_type;
+ u8 tlv_length;
+ u16 requester_port; u8 requester_system[6];
+ u32 requester_transaction_id; u8 pad[2];
+ }) marker_information_t;
+
+typedef CLIB_PACKED (struct
+ {
+ u8 subtype;
+ u8 version_number;
+ marker_information_t marker_info;
+ marker_terminator_t terminator;
+ }) marker_pdu_t;
+
+typedef CLIB_PACKED (struct
+ {
+ ethernet_header_t ethernet; marker_pdu_t marker;
+ }) ethernet_marker_pdu_t;
+
+#endif /* __included_lacp_protocol_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/ptx_machine.c b/src/plugins/lacp/ptx_machine.c
new file mode 100644
index 00000000000..ac83444b08a
--- /dev/null
+++ b/src/plugins/lacp/ptx_machine.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <vnet/bonding/node.h>
+#include <lacp/node.h>
+
+/*
+ * LACP State = NO_PERIODIC
+ */
+static lacp_fsm_state_t lacp_ptx_state_no_periodic[] = {
+ {LACP_ACTION_NO_PERIODIC, LACP_PTX_STATE_FAST_PERIODIC}, // event 0 BEGIN
+ {LACP_NOACTION, LACP_PTX_STATE_NO_PERIODIC}, // event 1 LONG_TIMEOUT
+ {LACP_NOACTION, LACP_PTX_STATE_NO_PERIODIC}, // event 2 TIMER_EXPIRED
+ {LACP_NOACTION, LACP_PTX_STATE_NO_PERIODIC}, // event 3 SHORT_TIMEOUT
+};
+
+/*
+ * LACP State = FAST_PERIODIC
+ */
+static lacp_fsm_state_t lacp_ptx_state_fast_periodic[] = {
+ {LACP_ACTION_FAST_PERIODIC, LACP_PTX_STATE_FAST_PERIODIC}, // event 0 BEGIN
+ {LACP_ACTION_SLOW_PERIODIC, LACP_PTX_STATE_SLOW_PERIODIC}, // event 1 LONG_TIMEOUT
+ {LACP_ACTION_TIMER_EXPIRED, LACP_PTX_STATE_PERIODIC_TX}, // event 2 TIMER_EXPIRED
+ {LACP_ACTION_FAST_PERIODIC, LACP_PTX_STATE_FAST_PERIODIC}, // event 3 SHORT_TIMEOUT
+};
+
+/*
+ * LACP State = SLOW_PERIODIC
+ */
+static lacp_fsm_state_t lacp_ptx_state_slow_periodic[] = {
+ {LACP_ACTION_NO_PERIODIC, LACP_PTX_STATE_NO_PERIODIC}, // event 0 BEGIN
+ {LACP_ACTION_SLOW_PERIODIC, LACP_PTX_STATE_SLOW_PERIODIC}, // event 1 LONG_TIMEOUT
+ {LACP_ACTION_TIMER_EXPIRED, LACP_PTX_STATE_PERIODIC_TX}, // event 2 TIMER_EXPIRED
+ {LACP_ACTION_FAST_PERIODIC, LACP_PTX_STATE_FAST_PERIODIC}, // event 3 SHORT_TIMEOUT
+};
+
+/*
+ * LACP State = PERIODIC_TX
+ */
+static lacp_fsm_state_t lacp_ptx_state_periodic_tx[] = {
+ {LACP_ACTION_NO_PERIODIC, LACP_PTX_STATE_NO_PERIODIC}, // event 0 BEGIN
+ {LACP_NOACTION, LACP_PTX_STATE_PERIODIC_TX}, // event 1 LONG_TIMEOUT
+ {LACP_ACTION_TIMER_EXPIRED, LACP_PTX_STATE_PERIODIC_TX}, // event 2 TIMER_EXPIRED
+ {LACP_NOACTION, LACP_PTX_STATE_PERIODIC_TX}, // event 3 SHORT_TIMEOUT
+};
+
+
+static lacp_fsm_machine_t lacp_ptx_fsm_table[] = {
+ {lacp_ptx_state_no_periodic},
+ {lacp_ptx_state_fast_periodic},
+ {lacp_ptx_state_slow_periodic},
+ {lacp_ptx_state_periodic_tx},
+};
+
+lacp_machine_t lacp_ptx_machine = {
+ lacp_ptx_fsm_table,
+ lacp_ptx_debug_func,
+};
+
+int
+lacp_ptx_action_no_periodic (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+
+ lacp_stop_timer (&sif->periodic_timer);
+
+ lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
+ LACP_PTX_EVENT_BEGIN, &sif->ptx_state);
+
+ return 0;
+}
+
+int
+lacp_ptx_action_slow_periodic (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+ u8 timer_expired;
+ lacp_main_t *lm = &lacp_main;
+
+ if (lacp_timer_is_running (sif->periodic_timer) &&
+ lacp_timer_is_expired (lm->vlib_main, sif->periodic_timer))
+ timer_expired = 1;
+ else
+ timer_expired = 0;
+
+ lacp_start_periodic_timer (lm->vlib_main, sif, LACP_SLOW_PERIODIC_TIMER);
+
+ if (timer_expired || (sif->partner.state & LACP_STATE_LACP_TIMEOUT))
+ lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
+ LACP_PTX_EVENT_TIMER_EXPIRED, &sif->ptx_state);
+
+ return 0;
+}
+
+int
+lacp_ptx_action_fast_periodic (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+ u8 timer_expired;
+ lacp_main_t *lm = &lacp_main;
+
+ if (lacp_timer_is_running (sif->periodic_timer) &&
+ lacp_timer_is_expired (lm->vlib_main, sif->periodic_timer))
+ timer_expired = 1;
+ else
+ timer_expired = 0;
+
+ lacp_start_periodic_timer (lm->vlib_main, sif, LACP_FAST_PERIODIC_TIMER);
+
+ if (timer_expired)
+ lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
+ LACP_PTX_EVENT_TIMER_EXPIRED, &sif->ptx_state);
+
+ if (!(sif->partner.state & LACP_STATE_LACP_TIMEOUT))
+ lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
+ LACP_PTX_EVENT_LONG_TIMEOUT, &sif->ptx_state);
+
+ return 0;
+}
+
+int
+lacp_ptx_action_timer_expired (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+
+ sif->ntt = 1;
+ lacp_machine_dispatch (&lacp_tx_machine, vm, sif, LACP_TX_EVENT_NTT,
+ &sif->tx_state);
+ if (sif->partner.state & LACP_STATE_LACP_TIMEOUT)
+ lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
+ LACP_PTX_EVENT_SHORT_TIMEOUT, &sif->ptx_state);
+ else
+ lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
+ LACP_PTX_EVENT_LONG_TIMEOUT, &sif->ptx_state);
+
+ return 0;
+}
+
+static u8 *
+format_ptx_event (u8 * s, va_list * args)
+{
+ static lacp_event_struct lacp_ptx_event_array[] = {
+#define _(b, s, n) {.bit = b, .str = #s, },
+ foreach_lacp_ptx_event
+#undef _
+ {.str = NULL}
+ };
+ int e = va_arg (*args, int);
+ lacp_event_struct *event_entry =
+ (lacp_event_struct *) & lacp_ptx_event_array;
+
+ if (e >= (sizeof (lacp_ptx_event_array) / sizeof (*event_entry)))
+ s = format (s, "Bad event %d", e);
+ else
+ s = format (s, "%s", event_entry[e].str);
+
+ return s;
+}
+
+void
+lacp_ptx_debug_func (slave_if_t * sif, int event, int state,
+ lacp_fsm_state_t * transition)
+{
+ clib_warning ("%U-PTX: event %U, old state %U, new state %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sif->sw_if_index, format_ptx_event,
+ event, format_ptx_sm_state, state, format_ptx_sm_state,
+ transition->next_state);
+}
+
+void
+lacp_init_ptx_machine (vlib_main_t * vm, slave_if_t * sif)
+{
+ lacp_machine_dispatch (&lacp_ptx_machine, vm, sif, LACP_PTX_EVENT_BEGIN,
+ &sif->ptx_state);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/ptx_machine.h b/src/plugins/lacp/ptx_machine.h
new file mode 100644
index 00000000000..a9af4bb89d3
--- /dev/null
+++ b/src/plugins/lacp/ptx_machine.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#ifndef ___LACP_PTX_MACHINE_H__
+#define ___LACP_PTX_MACHINE_H__
+
+#include <stdint.h>
+#include <lacp/machine.h>
+
+#define foreach_lacp_ptx_event \
+ _(0, BEGIN, "begin") \
+ _(1, LONG_TIMEOUT, "long tiemout") \
+ _(2, TIMER_EXPIRED, "timer expired") \
+ _(3, SHORT_TIMEOUT, "short timeout")
+
+typedef enum
+{
+#define _(a, b, c) LACP_PTX_EVENT_##b = (a),
+ foreach_lacp_ptx_event
+#undef _
+} lacp_ptx_event_t;
+
+#define foreach_lacp_ptx_sm_state \
+ _(0, NO_PERIODIC, "no periodic") \
+ _(1, FAST_PERIODIC, "fast periodic") \
+ _(2, SLOW_PERIODIC, "slow periodic") \
+ _(3, PERIODIC_TX, "periodic transmission")
+
+typedef enum
+{
+#define _(a, b, c) LACP_PTX_STATE_##b = (a),
+ foreach_lacp_ptx_sm_state
+#undef _
+} lacp_ptx_sm_state_t;
+
+extern lacp_machine_t lacp_ptx_machine;
+
+int lacp_ptx_action_no_periodic (void *p1, void *p2);
+int lacp_ptx_action_slow_periodic (void *p1, void *p2);
+int lacp_ptx_action_fast_periodic (void *p1, void *p2);
+int lacp_ptx_action_timer_expired (void *p1, void *p2);
+void lacp_ptx_debug_func (slave_if_t * sif, int event, int state,
+ lacp_fsm_state_t * transition);
+
+#define LACP_ACTION_NO_PERIODIC \
+ LACP_ACTION_ROUTINE(lacp_ptx_action_no_periodic)
+#define LACP_ACTION_SLOW_PERIODIC \
+ LACP_ACTION_ROUTINE(lacp_ptx_action_slow_periodic)
+#define LACP_ACTION_FAST_PERIODIC \
+ LACP_ACTION_ROUTINE(lacp_ptx_action_fast_periodic)
+#define LACP_ACTION_TIMER_EXPIRED \
+ LACP_ACTION_ROUTINE(lacp_ptx_action_timer_expired)
+
+static inline void
+lacp_start_periodic_timer (vlib_main_t * vm, slave_if_t * sif, u8 expiration)
+{
+ sif->periodic_timer = vlib_time_now (vm) + expiration;
+}
+
+#endif /* __LACP_PTX_MACHINE_H__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/rx_machine.c b/src/plugins/lacp/rx_machine.c
new file mode 100644
index 00000000000..374e3f84939
--- /dev/null
+++ b/src/plugins/lacp/rx_machine.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <vnet/bonding/node.h>
+#include <lacp/node.h>
+
+/*
+ * LACP State = INITIALIZE
+ */
+static lacp_fsm_state_t lacp_rx_state_initialize[] = {
+ {LACP_ACTION_INITIALIZE, LACP_RX_STATE_PORT_DISABLED}, // event 0 BEGIN
+ {LACP_ACTION_INITIALIZE, LACP_RX_STATE_PORT_DISABLED}, // event 1 PORT_DISABLED
+ {LACP_ACTION_INITIALIZE, LACP_RX_STATE_PORT_DISABLED}, // event 2 PORT_MOVED
+ {LACP_NOACTION, LACP_RX_STATE_INITIALIZE}, // event 3 LACP_ENABLED
+ {LACP_NOACTION, LACP_RX_STATE_INITIALIZE}, // event 4 LACP_DISABLED
+ {LACP_NOACTION, LACP_RX_STATE_INITIALIZE}, // event 5 PDU_RECEIVED
+ {LACP_NOACTION, LACP_RX_STATE_INITIALIZE}, // event 6 TIMER_EXPIRED
+};
+
+/*
+ * LACP State = PORT_DISABLED
+ */
+static lacp_fsm_state_t lacp_rx_state_port_disabled[] = {
+ {LACP_ACTION_PORT_DISABLED, LACP_RX_STATE_PORT_DISABLED}, // event 0 BEGIN
+ {LACP_ACTION_PORT_DISABLED, LACP_RX_STATE_PORT_DISABLED}, // event 1 PORT_DISABLED
+ {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE}, // event 2 PORT_MOVED
+ {LACP_ACTION_EXPIRED, LACP_RX_STATE_EXPIRED}, // event 3 LACP_ENABLED
+ {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED}, // event 4 LACP_DISABLED
+ {LACP_NOACTION, LACP_RX_STATE_PORT_DISABLED}, // event 5 PDU_RECEIVED
+ {LACP_NOACTION, LACP_RX_STATE_PORT_DISABLED}, // event 6 TIMER_EXPIRED
+};
+
+/*
+ * LACP State = EXPIRED
+ */
+static lacp_fsm_state_t lacp_rx_state_expired[] = {
+ {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE}, // event 0 BEGIN
+ {LACP_NOACTION, LACP_RX_STATE_EXPIRED}, // event 1 PORT_DISABLED
+ {LACP_NOACTION, LACP_RX_STATE_EXPIRED}, // event 2 PORT_MOVED
+ {LACP_NOACTION, LACP_RX_STATE_EXPIRED}, // event 3 LACP_ENABLED
+ {LACP_NOACTION, LACP_RX_STATE_EXPIRED}, // event 4 LACP_DISABLED
+ {LACP_ACTION_CURRENT, LACP_RX_STATE_CURRENT}, // event 5 PDU_RECEIVED
+ {LACP_ACTION_DEFAULTED, LACP_RX_STATE_DEFAULTED}, // event 6 TIMER_EXPIRED
+};
+
+/*
+ * LACP State = LACP_DISABLED
+ */
+static lacp_fsm_state_t lacp_rx_state_lacp_disabled[] = {
+ {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE}, // event 0 BEGIN
+ {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED}, // event 1 PORT_DISABLED
+ {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED}, // event 2 PORT_MOVED
+ {LACP_ACTION_EXPIRED, LACP_RX_STATE_EXPIRED}, // event 3 LACP_ENABLED XXX
+ {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED}, // event 4 LACP_DISABLED
+ {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED}, // event 5 PDU_RECEIVED
+ {LACP_NOACTION, LACP_RX_STATE_LACP_DISABLED}, // event 6 TIMER_EXPIRED
+};
+
+/*
+ * LACP State = DEFAULTED
+ */
+static lacp_fsm_state_t lacp_rx_state_defaulted[] = {
+ {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE}, // event 0 BEGIN
+ {LACP_NOACTION, LACP_RX_STATE_DEFAULTED}, // event 1 PORT_DISABLED
+ {LACP_NOACTION, LACP_RX_STATE_DEFAULTED}, // event 2 PORT_MOVED
+ {LACP_NOACTION, LACP_RX_STATE_DEFAULTED}, // event 3 LACP_ENABLED
+ {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED}, // event 4 LACP_DISABLED
+ {LACP_ACTION_CURRENT, LACP_RX_STATE_CURRENT}, // event 5 PDU_RECEIVED
+ {LACP_ACTION_DEFAULTED, LACP_RX_STATE_DEFAULTED}, // event 6 TIMER_EXPIRED
+};
+
+/*
+ * LACP State = CURRENT
+ */
+static lacp_fsm_state_t lacp_rx_state_current[] = {
+ {LACP_ACTION_INITIALIZE, LACP_RX_STATE_INITIALIZE}, // event 0 BEGIN
+ {LACP_NOACTION, LACP_RX_STATE_CURRENT}, // event 1 PORT_DISABLED
+ {LACP_NOACTION, LACP_RX_STATE_CURRENT}, // event 1 PORT_MOVED
+ {LACP_NOACTION, LACP_RX_STATE_CURRENT}, // event 2 LACP_ENABLED
+ {LACP_ACTION_LACP_DISABLED, LACP_RX_STATE_LACP_DISABLED}, // event 3 LACP_DISABLED
+ {LACP_ACTION_CURRENT, LACP_RX_STATE_CURRENT}, // event 4 PDU_RECEIVED
+ {LACP_ACTION_EXPIRED, LACP_RX_STATE_EXPIRED}, // event 5 TIMER_EXPIRED
+};
+
+static lacp_fsm_machine_t lacp_rx_fsm_table[] = {
+ {lacp_rx_state_initialize},
+ {lacp_rx_state_port_disabled},
+ {lacp_rx_state_expired},
+ {lacp_rx_state_lacp_disabled},
+ {lacp_rx_state_defaulted},
+ {lacp_rx_state_current},
+};
+
+lacp_machine_t lacp_rx_machine = {
+ lacp_rx_fsm_table,
+ lacp_rx_debug_func,
+};
+
+static void
+lacp_set_port_unselected (vlib_main_t * vm, slave_if_t * sif)
+{
+ sif->selected = LACP_PORT_UNSELECTED;
+
+ switch (sif->mux_state)
+ {
+ case LACP_MUX_STATE_DETACHED:
+ break;
+ case LACP_MUX_STATE_WAITING:
+ break;
+ case LACP_MUX_STATE_ATTACHED:
+ return;
+ break;
+ case LACP_MUX_STATE_COLLECTING_DISTRIBUTING:
+ if (sif->partner.state & LACP_STATE_SYNCHRONIZATION)
+ return;
+ break;
+ default:
+ break;
+ }
+ lacp_machine_dispatch (&lacp_mux_machine, vm, sif,
+ LACP_MUX_EVENT_UNSELECTED, &sif->mux_state);
+}
+
+static void
+lacp_update_default_selected (vlib_main_t * vm, slave_if_t * sif)
+{
+ if ((sif->partner_admin.state & LACP_STATE_AGGREGATION) !=
+ (sif->partner.state & LACP_STATE_AGGREGATION) ||
+ memcmp (&sif->partner, &sif->partner_admin,
+ sizeof (sif->partner) - sizeof (sif->partner.state)))
+ {
+ lacp_set_port_unselected (vm, sif);
+ }
+}
+
+static void
+lacp_record_default (slave_if_t * sif)
+{
+ sif->partner = sif->partner_admin;
+ sif->actor.state |= LACP_STATE_DEFAULTED;
+}
+
+static void
+lacp_update_selected (vlib_main_t * vm, slave_if_t * sif)
+{
+ lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
+
+ if ((lacpdu->actor.port_info.state & LACP_STATE_AGGREGATION) !=
+ (sif->partner.state & LACP_STATE_AGGREGATION) ||
+ memcmp (&sif->partner, &lacpdu->actor.port_info,
+ sizeof (sif->partner) - sizeof (sif->partner.state)))
+ {
+ lacp_set_port_unselected (vm, sif);
+ }
+}
+
+static void
+lacp_update_ntt (vlib_main_t * vm, slave_if_t * sif)
+{
+ lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
+ u8 states = LACP_STATE_LACP_ACTIVITY | LACP_STATE_LACP_TIMEOUT |
+ LACP_STATE_SYNCHRONIZATION | LACP_STATE_AGGREGATION;
+
+ if ((states & lacpdu->partner.port_info.state) !=
+ (states & sif->actor.state)
+ || memcmp (&sif->actor, &lacpdu->partner.port_info,
+ sizeof (sif->actor) - sizeof (sif->actor.state)))
+ {
+ sif->ntt = 1;
+ lacp_machine_dispatch (&lacp_tx_machine, vm, sif, LACP_TX_EVENT_NTT,
+ &sif->tx_state);
+ }
+}
+
+/*
+ * compare lacpdu partner info against sif->partner. Return 1 if they match, 0
+ * otherwise.
+ */
+static u8
+lacp_compare_partner (slave_if_t * sif)
+{
+ lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
+
+ if ((!memcmp (&sif->partner, &lacpdu->actor.port_info,
+ sizeof (sif->partner) - sizeof (sif->partner.state)) &&
+ ((sif->actor.state & LACP_STATE_AGGREGATION) ==
+ (lacpdu->partner.port_info.state & LACP_STATE_AGGREGATION))) ||
+ ((lacpdu->actor.port_info.state & LACP_STATE_AGGREGATION) == 0))
+ return 1;
+
+ return 0;
+}
+
+static void
+lacp_record_pdu (slave_if_t * sif)
+{
+ lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
+ u8 match;
+
+ match = lacp_compare_partner (sif);
+ sif->partner = lacpdu->actor.port_info;
+ sif->actor.state &= ~LACP_STATE_DEFAULTED;
+ if (match && (lacpdu->actor.port_info.state & LACP_STATE_SYNCHRONIZATION))
+ sif->partner.state |= LACP_STATE_SYNCHRONIZATION;
+ else
+ sif->partner.state &= ~LACP_STATE_SYNCHRONIZATION;
+}
+
+static void
+lacp_set_port_moved (vlib_main_t * vm, slave_if_t * sif, u8 val)
+{
+ sif->port_moved = val;
+
+ if (sif->port_moved)
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_PORT_MOVED, &sif->rx_state);
+ else if (!sif->port_enabled)
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_PORT_DISABLED, &sif->rx_state);
+}
+
+int
+lacp_rx_action_initialize (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+
+ lacp_set_port_unselected (vm, sif);
+ lacp_record_default (sif);
+ sif->actor.state &= ~LACP_STATE_EXPIRED;
+ lacp_set_port_moved (vm, sif, 0);
+ /* UCT */
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_BEGIN, &sif->rx_state);
+
+ return 0;
+}
+
+int
+lacp_rx_action_port_disabled (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+
+ sif->partner.state &= ~LACP_STATE_SYNCHRONIZATION;
+ if (sif->port_moved)
+ {
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_PORT_MOVED, &sif->rx_state);
+ }
+ if (sif->port_enabled)
+ {
+ if (sif->lacp_enabled)
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_LACP_ENABLED, &sif->rx_state);
+ else
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_LACP_DISABLED, &sif->rx_state);
+ }
+
+ return 0;
+}
+
+int
+lacp_rx_action_expired (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+ u8 timer_expired;
+ lacp_main_t *lm = &lacp_main;
+
+ sif->partner.state &= ~LACP_STATE_SYNCHRONIZATION;
+ sif->partner.state |= LACP_STATE_LACP_TIMEOUT;
+ lacp_machine_dispatch (&lacp_ptx_machine, vm, sif,
+ LACP_PTX_EVENT_SHORT_TIMEOUT, &sif->ptx_state);
+ if (lacp_timer_is_running (sif->current_while_timer) &&
+ lacp_timer_is_expired (lm->vlib_main, sif->current_while_timer))
+ timer_expired = 1;
+ else
+ timer_expired = 0;
+ lacp_start_current_while_timer (lm->vlib_main, sif, LACP_SHORT_TIMOUT_TIME);
+ sif->actor.state |= LACP_STATE_EXPIRED;
+ if (timer_expired)
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_TIMER_EXPIRED, &sif->rx_state);
+ if (sif->last_rx_pkt && vec_len (sif->last_rx_pkt))
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_PDU_RECEIVED, &sif->rx_state);
+
+ return 0;
+}
+
+int
+lacp_rx_action_lacp_disabled (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+
+ lacp_set_port_unselected (vm, sif);
+ lacp_record_default (sif);
+ sif->partner.state &= ~LACP_STATE_AGGREGATION;
+ sif->actor.state &= ~LACP_STATE_EXPIRED;
+
+ return 0;
+}
+
+int
+lacp_rx_action_defaulted (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+
+ lacp_update_default_selected (vm, sif);
+ lacp_record_default (sif);
+ sif->actor.state &= ~LACP_STATE_EXPIRED;
+ if (sif->last_rx_pkt && vec_len (sif->last_rx_pkt))
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_PDU_RECEIVED, &sif->rx_state);
+
+ return 0;
+}
+
+static int
+lacp_port_is_moved (vlib_main_t * vm, slave_if_t * sif)
+{
+ bond_main_t *bm = &bond_main;
+ slave_if_t *sif2;
+ lacp_pdu_t *lacpdu = (lacp_pdu_t *) sif->last_rx_pkt;
+
+ /* *INDENT-OFF* */
+ pool_foreach (sif2, bm->neighbors, {
+ {
+ if ((sif != sif2) && (sif2->rx_state == LACP_RX_STATE_PORT_DISABLED) &&
+ !memcmp (sif2->partner.system,
+ lacpdu->partner.port_info.system, 6) &&
+ (sif2->partner.port_number == lacpdu->partner.port_info.port_number))
+ return 1;
+ }
+ });
+ /* *INDENT-ON* */
+
+ return 0;
+}
+
+int
+lacp_rx_action_current (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+ lacp_main_t *lm = &lacp_main;
+
+ lacp_update_selected (vm, sif);
+ lacp_update_ntt (vm, sif);
+ lacp_record_pdu (sif);
+ lacp_start_current_while_timer (lm->vlib_main, sif, sif->ttl_in_seconds);
+ sif->actor.state &= ~LACP_STATE_EXPIRED;
+ if (lacp_port_is_moved (vm, sif))
+ lacp_set_port_moved (vm, sif, 1);
+ lacp_selection_logic (vm, sif);
+
+ return 0;
+}
+
+static u8 *
+format_rx_event (u8 * s, va_list * args)
+{
+ static lacp_event_struct lacp_rx_event_array[] = {
+#define _(b, s, n) {.bit = b, .str = #s, },
+ foreach_lacp_rx_event
+#undef _
+ {.str = NULL}
+ };
+ int e = va_arg (*args, int);
+ lacp_event_struct *event_entry =
+ (lacp_event_struct *) & lacp_rx_event_array;
+
+ if (e >= (sizeof (lacp_rx_event_array) / sizeof (*event_entry)))
+ s = format (s, "Bad event %d", e);
+ else
+ s = format (s, "%s", event_entry[e].str);
+
+ return s;
+}
+
+void
+lacp_rx_debug_func (slave_if_t * sif, int event, int state,
+ lacp_fsm_state_t * transition)
+{
+ clib_warning ("%U-RX: event %U, old state %U, new state %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sif->sw_if_index, format_rx_event,
+ event, format_rx_sm_state, state, format_rx_sm_state,
+ transition->next_state);
+}
+
+void
+lacp_init_rx_machine (vlib_main_t * vm, slave_if_t * sif)
+{
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif, LACP_RX_EVENT_BEGIN,
+ &sif->rx_state);
+ lacp_machine_dispatch (&lacp_rx_machine, vm, sif,
+ LACP_RX_EVENT_LACP_ENABLED, &sif->rx_state);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/rx_machine.h b/src/plugins/lacp/rx_machine.h
new file mode 100644
index 00000000000..706dbd08c67
--- /dev/null
+++ b/src/plugins/lacp/rx_machine.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#ifndef __LACP_RX_MACHINE_H__
+#define __LACP_RX_MACHINE_H__
+
+#include <stdint.h>
+#include <lacp/machine.h>
+
+#define foreach_lacp_rx_event \
+ _(0, BEGIN, "begin") \
+ _(1, PORT_DISABLED, "port disabled") \
+ _(2, PORT_MOVED, "port moved") \
+ _(3, LACP_ENABLED, "lacp enabled") \
+ _(4, LACP_DISABLED, "lacp disabled") \
+ _(5, PDU_RECEIVED, "pdu received") \
+ _(6, TIMER_EXPIRED, "timer expired")
+
+typedef enum
+{
+#define _(a, b, c) LACP_RX_EVENT_##b = (a),
+ foreach_lacp_rx_event
+#undef _
+} lacp_rx_event_t;
+
+#define foreach_lacp_rx_sm_state \
+ _(0, INITIALIZE, "initialize") \
+ _(1, PORT_DISABLED, "port disabled") \
+ _(2, EXPIRED, "expired") \
+ _(3, LACP_DISABLED, "lacp disabled") \
+ _(4, DEFAULTED, "defaulted") \
+ _(5, CURRENT, "current")
+
+typedef enum
+{
+#define _(a, b, c) LACP_RX_STATE_##b = (a),
+ foreach_lacp_rx_sm_state
+#undef _
+} lacp_rx_sm_state_t;
+
+extern lacp_machine_t lacp_rx_machine;
+
+int lacp_rx_action_initialize (void *, void *);
+int lacp_rx_action_port_disabled (void *, void *);
+int lacp_rx_action_pdu_received (void *, void *);
+int lacp_rx_action_expired (void *, void *);
+int lacp_rx_action_lacp_disabled (void *, void *);
+int lacp_rx_action_defaulted (void *, void *);
+int lacp_rx_action_current (void *, void *);
+void lacp_rx_debug_func (slave_if_t * sif, int event, int state,
+ lacp_fsm_state_t * transition);
+
+#define LACP_ACTION_INITIALIZE \
+ LACP_ACTION_ROUTINE(lacp_rx_action_initialize)
+#define LACP_ACTION_PORT_DISABLED \
+ LACP_ACTION_ROUTINE(lacp_rx_action_port_disabled)
+#define LACP_ACTION_EXPIRED \
+ LACP_ACTION_ROUTINE(lacp_rx_action_expired)
+#define LACP_ACTION_LACP_DISABLED \
+ LACP_ACTION_ROUTINE(lacp_rx_action_lacp_disabled)
+#define LACP_ACTION_DEFAULTED LACP_ACTION_ROUTINE(lacp_rx_action_defaulted)
+#define LACP_ACTION_CURRENT LACP_ACTION_ROUTINE(lacp_rx_action_current)
+
+static inline void
+lacp_start_current_while_timer (vlib_main_t * vm, slave_if_t * sif,
+ u8 expiration)
+{
+ sif->current_while_timer = vlib_time_now (vm) + expiration;
+}
+
+#endif /* __LACP_RX_MACHINE_H__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/selection.c b/src/plugins/lacp/selection.c
new file mode 100644
index 00000000000..898b6a95023
--- /dev/null
+++ b/src/plugins/lacp/selection.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2017 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 <stdint.h>
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vnet/bonding/node.h>
+#include <lacp/node.h>
+
+static void
+lacp_set_port_selected (vlib_main_t * vm, slave_if_t * sif)
+{
+ /* Handle loopback port */
+ if (!memcmp (sif->partner.system, sif->actor.system, 6) &&
+ (sif->partner.key == sif->actor.key))
+ {
+ sif->loopback_port = 1;
+ sif->actor.state &= ~LACP_STATE_AGGREGATION;
+ }
+ sif->selected = LACP_PORT_SELECTED;
+
+ switch (sif->mux_state)
+ {
+ case LACP_MUX_STATE_DETACHED:
+ break;
+ case LACP_MUX_STATE_WAITING:
+ if (!sif->ready)
+ return;
+ break;
+ case LACP_MUX_STATE_ATTACHED:
+ if (!(sif->partner.state & LACP_STATE_SYNCHRONIZATION))
+ return;
+ break;
+ case LACP_MUX_STATE_COLLECTING_DISTRIBUTING:
+ break;
+ default:
+ break;
+ }
+ lacp_machine_dispatch (&lacp_mux_machine, vm, sif, LACP_MUX_EVENT_SELECTED,
+ &sif->mux_state);
+}
+
+void
+lacp_selection_logic (vlib_main_t * vm, slave_if_t * sif)
+{
+ slave_if_t *sif2;
+ bond_if_t *bif;
+ u32 *sw_if_index;
+
+ bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
+ vec_foreach (sw_if_index, bif->slaves)
+ {
+ sif2 = bond_get_slave_by_sw_if_index (*sw_if_index);
+ if (sif2 && (sif2->actor.state & LACP_STATE_SYNCHRONIZATION) &&
+ (sif2->ready_n == 0))
+ goto out;
+ }
+
+ vec_foreach (sw_if_index, bif->slaves)
+ {
+ sif2 = bond_get_slave_by_sw_if_index (*sw_if_index);
+ if (sif2)
+ {
+ sif2->ready = 1;
+ if (sif2->selected == LACP_PORT_SELECTED)
+ lacp_machine_dispatch (&lacp_mux_machine, vm, sif2,
+ LACP_MUX_EVENT_READY, &sif2->mux_state);
+ }
+ }
+out:
+ lacp_set_port_selected (vm, sif);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/tx_machine.c b/src/plugins/lacp/tx_machine.c
new file mode 100644
index 00000000000..794b4f10d95
--- /dev/null
+++ b/src/plugins/lacp/tx_machine.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <vnet/bonding/node.h>
+#include <lacp/node.h>
+
+/*
+ * LACP State = TRANSMIT
+ */
+static lacp_fsm_state_t lacp_tx_state_transmit[] = {
+ {LACP_ACTION_TRANSMIT, LACP_TX_STATE_TRANSMIT}, // event 0 BEGIN
+ {LACP_ACTION_TRANSMIT, LACP_TX_STATE_TRANSMIT}, // event 1 NTT
+};
+
+static lacp_fsm_machine_t lacp_tx_fsm_table[] = {
+ {lacp_tx_state_transmit},
+};
+
+lacp_machine_t lacp_tx_machine = {
+ lacp_tx_fsm_table,
+ lacp_tx_debug_func,
+};
+
+int
+lacp_tx_action_transmit (void *p1, void *p2)
+{
+ vlib_main_t *vm = (vlib_main_t *) p1;
+ slave_if_t *sif = (slave_if_t *) p2;
+ f64 now = vlib_time_now (vm);
+
+ if (!lacp_timer_is_running (sif->periodic_timer))
+ return 0;
+
+ // No more than 3 LACPDUs per fast interval
+ if (now <= (sif->last_lacpdu_time + 0.333))
+ return 0;
+
+ if (sif->ntt)
+ {
+ lacp_send_lacp_pdu (vm, sif);
+ }
+ sif->ntt = 0;
+
+ return 0;
+}
+
+static u8 *
+format_tx_event (u8 * s, va_list * args)
+{
+ static lacp_event_struct lacp_tx_event_array[] = {
+#define _(b, s, n) {.bit = b, .str = #s, },
+ foreach_lacp_tx_event
+#undef _
+ {.str = NULL}
+ };
+ int e = va_arg (*args, int);
+ lacp_event_struct *event_entry =
+ (lacp_event_struct *) & lacp_tx_event_array;
+
+ if (e >= (sizeof (lacp_tx_event_array) / sizeof (*event_entry)))
+ s = format (s, "Bad event %d", e);
+ else
+ s = format (s, "%s", event_entry[e].str);
+
+ return s;
+}
+
+void
+lacp_tx_debug_func (slave_if_t * sif, int event, int state,
+ lacp_fsm_state_t * transition)
+{
+ clib_warning ("%U-TX: event %U, old state %U, new state %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sif->sw_if_index, format_tx_event,
+ event, format_tx_sm_state, state, format_tx_sm_state,
+ transition->next_state);
+}
+
+void
+lacp_init_tx_machine (vlib_main_t * vm, slave_if_t * sif)
+{
+ lacp_machine_dispatch (&lacp_tx_machine, vm, sif, LACP_TX_EVENT_BEGIN,
+ &sif->tx_state);
+ if (sif->is_passive == 0)
+ lacp_machine_dispatch (&lacp_tx_machine, vm, sif, LACP_TX_EVENT_NTT,
+ &sif->tx_state);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/lacp/tx_machine.h b/src/plugins/lacp/tx_machine.h
new file mode 100644
index 00000000000..428c19b5edd
--- /dev/null
+++ b/src/plugins/lacp/tx_machine.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#ifndef __LACP_TX_MACHINE_H__
+#define __LACP_TX_MACHINE_H__
+
+#include <stdint.h>
+#include <lacp/machine.h>
+
+#define foreach_lacp_tx_event \
+ _(0, BEGIN, "begin") \
+ _(1, NTT, "Need To Transmit")
+
+typedef enum
+{
+#define _(a, b, c) LACP_TX_EVENT_##b = (a),
+ foreach_lacp_tx_event
+#undef _
+} lacp_tx_event_t;
+
+#define foreach_lacp_tx_sm_state \
+ _(0, TRANSMIT, "transmit PDU")
+
+typedef enum
+{
+#define _(a, b, c) LACP_TX_STATE_##b = (a),
+ foreach_lacp_tx_sm_state
+#undef _
+} lacp_tx_sm_state_t;
+
+extern lacp_machine_t lacp_tx_machine;
+
+int lacp_tx_action_transmit (void *p1, void *p2);
+void lacp_tx_debug_func (slave_if_t * sif, int event, int state,
+ lacp_fsm_state_t * transition);
+
+#define LACP_ACTION_TRANSMIT LACP_ACTION_ROUTINE(lacp_tx_action_transmit)
+
+#endif /* __LACP_TX_MACHINE_H__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index 97e67f9d80b..019d095c40a 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -50,7 +50,7 @@
#include <vnet/policer/police.h>
#include <vnet/mfib/mfib_types.h>
#include <vnet/dhcp/dhcp_proxy.h>
-
+#include <vnet/bonding/node.h>
#include "vat/json_format.h"
#include <inttypes.h>
@@ -1775,6 +1775,275 @@ static void vl_api_tap_delete_v2_reply_t_handler_json
vam->result_ready = 1;
}
+static void
+vl_api_bond_create_reply_t_handler (vl_api_bond_create_reply_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ i32 retval = ntohl (mp->retval);
+
+ if (vam->async_mode)
+ {
+ vam->async_errors += (retval < 0);
+ }
+ else
+ {
+ vam->retval = retval;
+ vam->sw_if_index = ntohl (mp->sw_if_index);
+ vam->result_ready = 1;
+ }
+}
+
+static void vl_api_bond_create_reply_t_handler_json
+ (vl_api_bond_create_reply_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ vat_json_node_t node;
+
+ vat_json_init_object (&node);
+ vat_json_object_add_int (&node, "retval", ntohl (mp->retval));
+ vat_json_object_add_uint (&node, "sw_if_index", ntohl (mp->sw_if_index));
+
+ vat_json_print (vam->ofp, &node);
+ vat_json_free (&node);
+
+ vam->retval = ntohl (mp->retval);
+ vam->result_ready = 1;
+}
+
+static void
+vl_api_bond_delete_reply_t_handler (vl_api_bond_delete_reply_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ i32 retval = ntohl (mp->retval);
+
+ if (vam->async_mode)
+ {
+ vam->async_errors += (retval < 0);
+ }
+ else
+ {
+ vam->retval = retval;
+ vam->result_ready = 1;
+ }
+}
+
+static void vl_api_bond_delete_reply_t_handler_json
+ (vl_api_bond_delete_reply_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ vat_json_node_t node;
+
+ vat_json_init_object (&node);
+ vat_json_object_add_int (&node, "retval", ntohl (mp->retval));
+
+ vat_json_print (vam->ofp, &node);
+ vat_json_free (&node);
+
+ vam->retval = ntohl (mp->retval);
+ vam->result_ready = 1;
+}
+
+static void
+vl_api_bond_enslave_reply_t_handler (vl_api_bond_enslave_reply_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ i32 retval = ntohl (mp->retval);
+
+ if (vam->async_mode)
+ {
+ vam->async_errors += (retval < 0);
+ }
+ else
+ {
+ vam->retval = retval;
+ vam->result_ready = 1;
+ }
+}
+
+static void vl_api_bond_enslave_reply_t_handler_json
+ (vl_api_bond_enslave_reply_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ vat_json_node_t node;
+
+ vat_json_init_object (&node);
+ vat_json_object_add_int (&node, "retval", ntohl (mp->retval));
+
+ vat_json_print (vam->ofp, &node);
+ vat_json_free (&node);
+
+ vam->retval = ntohl (mp->retval);
+ vam->result_ready = 1;
+}
+
+static void
+vl_api_bond_detach_slave_reply_t_handler (vl_api_bond_detach_slave_reply_t *
+ mp)
+{
+ vat_main_t *vam = &vat_main;
+ i32 retval = ntohl (mp->retval);
+
+ if (vam->async_mode)
+ {
+ vam->async_errors += (retval < 0);
+ }
+ else
+ {
+ vam->retval = retval;
+ vam->result_ready = 1;
+ }
+}
+
+static void vl_api_bond_detach_slave_reply_t_handler_json
+ (vl_api_bond_detach_slave_reply_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ vat_json_node_t node;
+
+ vat_json_init_object (&node);
+ vat_json_object_add_int (&node, "retval", ntohl (mp->retval));
+
+ vat_json_print (vam->ofp, &node);
+ vat_json_free (&node);
+
+ vam->retval = ntohl (mp->retval);
+ vam->result_ready = 1;
+}
+
+static void vl_api_sw_interface_bond_details_t_handler
+ (vl_api_sw_interface_bond_details_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+
+ print (vam->ofp,
+ "%-16s %-12d %-12U %-13U %-14u %-14u",
+ mp->interface_name, ntohl (mp->sw_if_index),
+ format_bond_mode, mp->mode, format_bond_load_balance, mp->lb,
+ ntohl (mp->active_slaves), ntohl (mp->slaves));
+}
+
+static void vl_api_sw_interface_bond_details_t_handler_json
+ (vl_api_sw_interface_bond_details_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ vat_json_node_t *node = NULL;
+
+ if (VAT_JSON_ARRAY != vam->json_tree.type)
+ {
+ ASSERT (VAT_JSON_NONE == vam->json_tree.type);
+ vat_json_init_array (&vam->json_tree);
+ }
+ node = vat_json_array_add (&vam->json_tree);
+
+ vat_json_init_object (node);
+ vat_json_object_add_uint (node, "sw_if_index", ntohl (mp->sw_if_index));
+ vat_json_object_add_string_copy (node, "interface_name",
+ mp->interface_name);
+ vat_json_object_add_uint (node, "mode", mp->mode);
+ vat_json_object_add_uint (node, "load_balance", mp->lb);
+ vat_json_object_add_uint (node, "active_slaves", ntohl (mp->active_slaves));
+ vat_json_object_add_uint (node, "slaves", ntohl (mp->slaves));
+}
+
+static int
+api_sw_interface_bond_dump (vat_main_t * vam)
+{
+ vl_api_sw_interface_bond_dump_t *mp;
+ vl_api_control_ping_t *mp_ping;
+ int ret;
+
+ print (vam->ofp,
+ "\n%-16s %-12s %-12s %-13s %-14s %-14s",
+ "interface name", "sw_if_index", "mode", "load balance",
+ "active slaves", "slaves");
+
+ /* Get list of bond interfaces */
+ M (SW_INTERFACE_BOND_DUMP, mp);
+ S (mp);
+
+ /* Use a control ping for synchronization */
+ MPING (CONTROL_PING, mp_ping);
+ S (mp_ping);
+
+ W (ret);
+ return ret;
+}
+
+static void vl_api_sw_interface_slave_details_t_handler
+ (vl_api_sw_interface_slave_details_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+
+ print (vam->ofp,
+ "%-25s %-12d %-12d %d", mp->interface_name,
+ ntohl (mp->sw_if_index), mp->is_passive, mp->is_long_timeout);
+}
+
+static void vl_api_sw_interface_slave_details_t_handler_json
+ (vl_api_sw_interface_slave_details_t * mp)
+{
+ vat_main_t *vam = &vat_main;
+ vat_json_node_t *node = NULL;
+
+ if (VAT_JSON_ARRAY != vam->json_tree.type)
+ {
+ ASSERT (VAT_JSON_NONE == vam->json_tree.type);
+ vat_json_init_array (&vam->json_tree);
+ }
+ node = vat_json_array_add (&vam->json_tree);
+
+ vat_json_init_object (node);
+ vat_json_object_add_uint (node, "sw_if_index", ntohl (mp->sw_if_index));
+ vat_json_object_add_string_copy (node, "interface_name",
+ mp->interface_name);
+ vat_json_object_add_uint (node, "passive", mp->is_passive);
+ vat_json_object_add_uint (node, "long_timeout", mp->is_long_timeout);
+}
+
+static int
+api_sw_interface_slave_dump (vat_main_t * vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_sw_interface_slave_dump_t *mp;
+ vl_api_control_ping_t *mp_ping;
+ u32 sw_if_index = ~0;
+ u8 sw_if_index_set = 0;
+ int ret;
+
+ /* Parse args required to build the message */
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "%U", api_unformat_sw_if_index, vam, &sw_if_index))
+ sw_if_index_set = 1;
+ else if (unformat (i, "sw_if_index %d", &sw_if_index))
+ sw_if_index_set = 1;
+ else
+ break;
+ }
+
+ if (sw_if_index_set == 0)
+ {
+ errmsg ("missing vpp interface name. ");
+ return -99;
+ }
+
+ print (vam->ofp,
+ "\n%-25s %-12s %-12s %s",
+ "slave interface name", "sw_if_index", "passive", "long_timeout");
+
+ /* Get list of bond interfaces */
+ M (SW_INTERFACE_SLAVE_DUMP, mp);
+ mp->sw_if_index = ntohl (sw_if_index);
+ S (mp);
+
+ /* Use a control ping for synchronization */
+ MPING (CONTROL_PING, mp_ping);
+ S (mp_ping);
+
+ W (ret);
+ return ret;
+}
+
static void vl_api_mpls_tunnel_add_del_reply_t_handler
(vl_api_mpls_tunnel_add_del_reply_t * mp)
{
@@ -5466,6 +5735,12 @@ _(SW_INTERFACE_TAP_DETAILS, sw_interface_tap_details) \
_(TAP_CREATE_V2_REPLY, tap_create_v2_reply) \
_(TAP_DELETE_V2_REPLY, tap_delete_v2_reply) \
_(SW_INTERFACE_TAP_V2_DETAILS, sw_interface_tap_v2_details) \
+_(BOND_CREATE_REPLY, bond_create_reply) \
+_(BOND_DELETE_REPLY, bond_delete_reply) \
+_(BOND_ENSLAVE_REPLY, bond_enslave_reply) \
+_(BOND_DETACH_SLAVE_REPLY, bond_detach_slave_reply) \
+_(SW_INTERFACE_BOND_DETAILS, sw_interface_bond_details) \
+_(SW_INTERFACE_SLAVE_DETAILS, sw_interface_slave_details) \
_(IP_ADD_DEL_ROUTE_REPLY, ip_add_del_route_reply) \
_(IP_TABLE_ADD_DEL_REPLY, ip_table_add_del_reply) \
_(IP_MROUTE_ADD_DEL_REPLY, ip_mroute_add_del_reply) \
@@ -7954,6 +8229,194 @@ api_tap_delete_v2 (vat_main_t * vam)
}
static int
+api_bond_create (vat_main_t * vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_bond_create_t *mp;
+ u8 mac_address[6];
+ u8 custom_mac = 0;
+ int ret;
+ u8 mode;
+ u8 lb;
+ u8 mode_is_set = 0;
+
+ memset (mac_address, 0, sizeof (mac_address));
+ lb = BOND_LB_L2;
+
+ /* Parse args required to build the message */
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "mode %U", unformat_bond_mode, &mode))
+ mode_is_set = 1;
+ else if (((mode == BOND_MODE_LACP) || (mode == BOND_MODE_XOR))
+ && unformat (i, "lb %U", unformat_bond_load_balance, &lb))
+ ;
+ else if (unformat (i, "hw-addr %U", unformat_ethernet_address,
+ mac_address))
+ custom_mac = 1;
+ else
+ break;
+ }
+
+ if (mode_is_set == 0)
+ {
+ errmsg ("Missing bond mode. ");
+ return -99;
+ }
+
+ /* Construct the API message */
+ M (BOND_CREATE, mp);
+
+ mp->use_custom_mac = custom_mac;
+
+ mp->mode = mode;
+ mp->lb = lb;
+
+ if (custom_mac)
+ clib_memcpy (mp->mac_address, mac_address, 6);
+
+ /* send it... */
+ S (mp);
+
+ /* Wait for a reply... */
+ W (ret);
+ return ret;
+}
+
+static int
+api_bond_delete (vat_main_t * vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_bond_delete_t *mp;
+ u32 sw_if_index = ~0;
+ u8 sw_if_index_set = 0;
+ int ret;
+
+ /* Parse args required to build the message */
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "%U", api_unformat_sw_if_index, vam, &sw_if_index))
+ sw_if_index_set = 1;
+ else if (unformat (i, "sw_if_index %d", &sw_if_index))
+ sw_if_index_set = 1;
+ else
+ break;
+ }
+
+ if (sw_if_index_set == 0)
+ {
+ errmsg ("missing vpp interface name. ");
+ return -99;
+ }
+
+ /* Construct the API message */
+ M (BOND_DELETE, mp);
+
+ mp->sw_if_index = ntohl (sw_if_index);
+
+ /* send it... */
+ S (mp);
+
+ /* Wait for a reply... */
+ W (ret);
+ return ret;
+}
+
+static int
+api_bond_enslave (vat_main_t * vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_bond_enslave_t *mp;
+ u32 bond_sw_if_index;
+ int ret;
+ u8 is_passive;
+ u8 is_long_timeout;
+ u32 bond_sw_if_index_is_set = 0;
+ u32 sw_if_index;
+ u8 sw_if_index_is_set = 0;
+
+ /* Parse args required to build the message */
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "sw_if_index %d", &sw_if_index))
+ sw_if_index_is_set = 1;
+ else if (unformat (i, "bond %u", &bond_sw_if_index))
+ bond_sw_if_index_is_set = 1;
+ else if (unformat (i, "passive %d", &is_passive))
+ ;
+ else if (unformat (i, "long-timeout %d", &is_long_timeout))
+ ;
+ else
+ break;
+ }
+
+ if (bond_sw_if_index_is_set == 0)
+ {
+ errmsg ("Missing bond sw_if_index. ");
+ return -99;
+ }
+ if (sw_if_index_is_set == 0)
+ {
+ errmsg ("Missing slave sw_if_index. ");
+ return -99;
+ }
+
+ /* Construct the API message */
+ M (BOND_ENSLAVE, mp);
+
+ mp->bond_sw_if_index = ntohl (bond_sw_if_index);
+ mp->sw_if_index = ntohl (sw_if_index);
+ mp->is_long_timeout = is_long_timeout;
+ mp->is_passive = is_passive;
+
+ /* send it... */
+ S (mp);
+
+ /* Wait for a reply... */
+ W (ret);
+ return ret;
+}
+
+static int
+api_bond_detach_slave (vat_main_t * vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_bond_detach_slave_t *mp;
+ u32 sw_if_index = ~0;
+ u8 sw_if_index_set = 0;
+ int ret;
+
+ /* Parse args required to build the message */
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "%U", api_unformat_sw_if_index, vam, &sw_if_index))
+ sw_if_index_set = 1;
+ else if (unformat (i, "sw_if_index %d", &sw_if_index))
+ sw_if_index_set = 1;
+ else
+ break;
+ }
+
+ if (sw_if_index_set == 0)
+ {
+ errmsg ("missing vpp interface name. ");
+ return -99;
+ }
+
+ /* Construct the API message */
+ M (BOND_DETACH_SLAVE, mp);
+
+ mp->sw_if_index = ntohl (sw_if_index);
+
+ /* send it... */
+ S (mp);
+
+ /* Wait for a reply... */
+ W (ret);
+ return ret;
+}
+
+static int
api_ip_table_add_del (vat_main_t * vam)
{
unformat_input_t *i = vam->input;
@@ -22782,6 +23245,18 @@ _(tap_create_v2, \
_(tap_delete_v2, \
"<vpp-if-name> | sw_if_index <id>") \
_(sw_interface_tap_v2_dump, "") \
+_(bond_create, \
+ "[hw-addr <mac-addr>] {round-robin | active-backup | " \
+ "broadcast | {lacp | xor} [load-balance { l2 | l23 | l34 }]}") \
+_(bond_delete, \
+ "<vpp-if-name> | sw_if_index <id>") \
+_(bond_enslave, \
+ "sw_if_index <n> bond <sw_if_index> [is_passive] [is_long_timeout]") \
+_(bond_detach_slave, \
+ "sw_if_index <n>") \
+_(sw_interface_bond_dump, "") \
+_(sw_interface_slave_dump, \
+ "<vpp-if-name> | sw_if_index <id>") \
_(ip_table_add_del, \
"table-id <n> [ipv6]\n") \
_(ip_add_del_route, \
diff --git a/src/vnet.am b/src/vnet.am
index d20124191b7..a58bdcae6f0 100644
--- a/src/vnet.am
+++ b/src/vnet.am
@@ -273,6 +273,21 @@ nobase_include_HEADERS += \
API_FILES += vnet/geneve/geneve.api
########################################
+# Layer 2 / Bonding
+########################################
+libvnet_la_SOURCES += \
+ vnet/bonding/cli.c \
+ vnet/bonding/node.c \
+ vnet/bonding/device.c \
+ vnet/bonding/bond_api.c
+
+nobase_include_HEADERS += \
+ vnet/bonding/node.h \
+ vnet/bonding/bond.api.h
+
+API_FILES += vnet/bonding/bond.api
+
+########################################
# Layer 2 / LLDP
########################################
libvnet_la_SOURCES += \
diff --git a/src/vnet/bonding/bond.api b/src/vnet/bonding/bond.api
new file mode 100644
index 00000000000..e8919e14904
--- /dev/null
+++ b/src/vnet/bonding/bond.api
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+/** \file
+
+ This file defines vpe control-plane API messages for
+ the bonding device driver
+*/
+
+option version = "1.0.0";
+
+/** \brief Initialize a new bond interface with the given paramters
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param use_custom_mac - if set, mac_address is valid
+ @param mac_address - mac addr to assign to the interface if use_custom_mac is set
+ @param mode - mode, required (1=round-robin, 2=active-backup, 3=xor, 4=broadcastcast, 5=lacp)
+ @param lb - load balance, optional (0=l2, 1=l34, 2=l23) valid for xor and lacp modes. Otherwise ignored
+*/
+define bond_create
+{
+ u32 client_index;
+ u32 context;
+ u8 use_custom_mac;
+ u8 mac_address[6];
+ u8 mode;
+ u8 lb;
+};
+
+/** \brief Reply for bond create reply
+ @param context - returned sender context, to match reply w/ request
+ @param retval - return code
+ @param sw_if_index - software index allocated for the new tap interface
+*/
+define bond_create_reply
+{
+ u32 context;
+ i32 retval;
+ u32 sw_if_index;
+};
+
+/** \brief Delete bond interface
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param sw_if_index - interface index of slave interface
+*/
+autoreply define bond_delete
+{
+ u32 client_index;
+ u32 context;
+ u32 sw_if_index;
+};
+
+/** \brief Initialize a new bond interface with the given paramters
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param sw_if_index - slave sw_if_index
+ @param bond_sw_if_index - bond sw_if_index
+ @param is_passive - interface does not initiate the lacp protocol, remote must be active speaker
+ @param is_long_timeout - 90 seconds vs default 3 seconds neighbor timeout
+*/
+define bond_enslave
+{
+ u32 client_index;
+ u32 context;
+ u32 sw_if_index;
+ u32 bond_sw_if_index;
+ u8 is_passive;
+ u8 is_long_timeout;
+};
+
+/** \brief Reply for bond enslave reply
+ @param context - returned sender context, to match reply w/ request
+ @param retval - return code
+*/
+define bond_enslave_reply
+{
+ u32 context;
+ i32 retval;
+};
+
+/** \brief bond detach slave
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param sw_if_index - interface index of slave interface
+*/
+autoreply define bond_detach_slave
+{
+ u32 client_index;
+ u32 context;
+ u32 sw_if_index;
+};
+
+/** \brief Dump bond interfaces request */
+define sw_interface_bond_dump
+{
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Reply for bond dump request
+ @param sw_if_index - software index of bond interface
+ @param interface_name - name of interface
+ @param mode - bonding mode
+ @param lb - load balance algo
+ @param active_slaves - active slaves count
+ @param slaves - config slave count
+*/
+define sw_interface_bond_details
+{
+ u32 context;
+ u32 sw_if_index;
+ u8 interface_name[64];
+ u8 mode;
+ u8 lb;
+ u32 active_slaves;
+ u32 slaves;
+};
+
+/** \brief bond slave dump
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param sw_if_index - interface index of bond interface
+*/
+define sw_interface_slave_dump
+{
+ u32 client_index;
+ u32 context;
+ u32 sw_if_index;
+};
+
+/** \brief Reply for slave dump request
+ @param sw_if_index - software index of slave interface
+ @param interface_name - name of interface
+ @param is_passve - interface does not initiate the lacp protocol, remote must be active speaker
+ @param is_long_timeout - 90 seconds vs default 3 seconds neighbor timeout
+*/
+define sw_interface_slave_details
+{
+ u32 context;
+ u32 sw_if_index;
+ u8 interface_name[64];
+ u8 is_passive;
+ u8 is_long_timeout;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/bonding/bond_api.c b/src/vnet/bonding/bond_api.c
new file mode 100644
index 00000000000..02536e966a1
--- /dev/null
+++ b/src/vnet/bonding/bond_api.c
@@ -0,0 +1,328 @@
+/*
+ *------------------------------------------------------------------
+ * bond_api.c - vnet bonding device driver API support
+ *
+ * Copyright (c) 2017 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 <vnet/vnet.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/interface.h>
+#include <vnet/api_errno.h>
+#include <vnet/ethernet/ethernet.h>
+
+#include <vnet/vnet_msg_enum.h>
+
+#define vl_typedefs /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <vnet/vnet_all_api_h.h>
+#undef vl_printfun
+
+#include <vlibapi/api_helper_macros.h>
+#include <vnet/bonding/node.h>
+
+#define foreach_bond_api_msg \
+_(BOND_CREATE, bond_create) \
+_(BOND_DELETE, bond_delete) \
+_(BOND_ENSLAVE, bond_enslave) \
+_(BOND_DETACH_SLAVE, bond_detach_slave) \
+_(SW_INTERFACE_BOND_DUMP, sw_interface_bond_dump)\
+_(SW_INTERFACE_SLAVE_DUMP, sw_interface_slave_dump)
+
+static void
+bond_send_sw_interface_event_deleted (vpe_api_main_t * am,
+ unix_shared_memory_queue_t * q,
+ u32 sw_if_index)
+{
+ vl_api_sw_interface_event_t *mp;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_SW_INTERFACE_EVENT);
+ mp->sw_if_index = ntohl (sw_if_index);
+
+ mp->admin_up_down = 0;
+ mp->link_up_down = 0;
+ mp->deleted = 1;
+ vl_msg_api_send_shmem (q, (u8 *) & mp);
+}
+
+static void
+vl_api_bond_delete_t_handler (vl_api_bond_delete_t * mp)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ int rv;
+ vpe_api_main_t *vam = &vpe_api_main;
+ vl_api_bond_delete_reply_t *rmp;
+ unix_shared_memory_queue_t *q;
+ u32 sw_if_index = ntohl (mp->sw_if_index);
+
+ rv = bond_delete_if (vm, sw_if_index);
+
+ q = vl_api_client_index_to_input_queue (mp->client_index);
+ if (!q)
+ return;
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ rmp->_vl_msg_id = ntohs (VL_API_BOND_DELETE_REPLY);
+ rmp->context = mp->context;
+ rmp->retval = ntohl (rv);
+
+ vl_msg_api_send_shmem (q, (u8 *) & rmp);
+
+ if (!rv)
+ bond_send_sw_interface_event_deleted (vam, q, sw_if_index);
+}
+
+static void
+vl_api_bond_create_t_handler (vl_api_bond_create_t * mp)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ vl_api_bond_create_reply_t *rmp;
+ unix_shared_memory_queue_t *q;
+ bond_create_if_args_t _a, *ap = &_a;
+
+ memset (ap, 0, sizeof (*ap));
+
+ if (mp->use_custom_mac)
+ {
+ clib_memcpy (ap->hw_addr, mp->mac_address, 6);
+ ap->hw_addr_set = 1;
+ }
+
+ ap->mode = mp->mode;
+ ap->lb = mp->lb;
+ bond_create_if (vm, ap);
+
+ q = vl_api_client_index_to_input_queue (mp->client_index);
+ if (!q)
+ return;
+
+ if (ap->rv != 0)
+ return;
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ rmp->_vl_msg_id = ntohs (VL_API_BOND_CREATE_REPLY);
+ rmp->context = mp->context;
+ rmp->retval = ntohl (ap->rv);
+ rmp->sw_if_index = ntohl (ap->sw_if_index);
+
+ vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+vl_api_bond_enslave_t_handler (vl_api_bond_enslave_t * mp)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ vl_api_bond_enslave_reply_t *rmp;
+ unix_shared_memory_queue_t *q;
+ bond_enslave_args_t _a, *ap = &_a;
+
+ memset (ap, 0, sizeof (*ap));
+
+ ap->group = ntohl (mp->bond_sw_if_index);
+ ap->slave = ntohl (mp->sw_if_index);
+ ap->is_passive = mp->is_passive;
+ ap->is_long_timeout = mp->is_long_timeout;
+
+ bond_enslave (vm, ap);
+
+ q = vl_api_client_index_to_input_queue (mp->client_index);
+ if (!q)
+ return;
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ rmp->_vl_msg_id = ntohs (VL_API_BOND_ENSLAVE_REPLY);
+ rmp->context = mp->context;
+ rmp->retval = ntohl (ap->rv);
+
+ vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+vl_api_bond_detach_slave_t_handler (vl_api_bond_detach_slave_t * mp)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ vl_api_bond_detach_slave_reply_t *rmp;
+ unix_shared_memory_queue_t *q;
+ bond_detach_slave_args_t _a, *ap = &_a;
+
+ memset (ap, 0, sizeof (*ap));
+
+ ap->slave = ntohl (mp->sw_if_index);
+ bond_detach_slave (vm, ap);
+
+ q = vl_api_client_index_to_input_queue (mp->client_index);
+ if (!q)
+ return;
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ rmp->_vl_msg_id = ntohs (VL_API_BOND_DETACH_SLAVE_REPLY);
+ rmp->context = mp->context;
+ rmp->retval = htonl (ap->rv);
+
+ vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+bond_send_sw_interface_details (vpe_api_main_t * am,
+ vl_api_registration_t * reg,
+ bond_interface_details_t * bond_if,
+ u32 context)
+{
+ vl_api_sw_interface_bond_details_t *mp;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = htons (VL_API_SW_INTERFACE_BOND_DETAILS);
+ mp->sw_if_index = htonl (bond_if->sw_if_index);
+ clib_memcpy (mp->interface_name, bond_if->interface_name,
+ MIN (ARRAY_LEN (mp->interface_name) - 1,
+ strlen ((const char *) bond_if->interface_name)));
+ mp->mode = bond_if->mode;
+ mp->lb = bond_if->lb;
+ mp->active_slaves = htonl (bond_if->active_slaves);
+ mp->slaves = htonl (bond_if->slaves);
+
+ mp->context = context;
+ vl_api_send_msg (reg, (u8 *) mp);
+}
+
+static void
+vl_api_sw_interface_bond_dump_t_handler (vl_api_sw_interface_bond_dump_t * mp)
+{
+ int rv;
+ vpe_api_main_t *am = &vpe_api_main;
+ vl_api_registration_t *reg;
+ bond_interface_details_t *bondifs = NULL;
+ bond_interface_details_t *bond_if = NULL;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ rv = bond_dump_ifs (&bondifs);
+ if (rv)
+ return;
+
+ vec_foreach (bond_if, bondifs)
+ {
+ bond_send_sw_interface_details (am, reg, bond_if, mp->context);
+ }
+
+ vec_free (bondifs);
+}
+
+static void
+bond_send_sw_interface_slave_details (vpe_api_main_t * am,
+ vl_api_registration_t * reg,
+ slave_interface_details_t * slave_if,
+ u32 context)
+{
+ vl_api_sw_interface_slave_details_t *mp;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = htons (VL_API_SW_INTERFACE_SLAVE_DETAILS);
+ mp->sw_if_index = htonl (slave_if->sw_if_index);
+ clib_memcpy (mp->interface_name, slave_if->interface_name,
+ MIN (ARRAY_LEN (mp->interface_name) - 1,
+ strlen ((const char *) slave_if->interface_name)));
+ mp->is_passive = slave_if->is_passive;
+ mp->is_long_timeout = slave_if->is_long_timeout;
+
+ mp->context = context;
+ vl_api_send_msg (reg, (u8 *) mp);
+}
+
+static void
+vl_api_sw_interface_slave_dump_t_handler (vl_api_sw_interface_slave_dump_t *
+ mp)
+{
+ int rv;
+ vpe_api_main_t *am = &vpe_api_main;
+ vl_api_registration_t *reg;
+ slave_interface_details_t *slaveifs = NULL;
+ slave_interface_details_t *slave_if = NULL;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ rv = bond_dump_slave_ifs (&slaveifs, ntohl (mp->sw_if_index));
+ if (rv)
+ return;
+
+ vec_foreach (slave_if, slaveifs)
+ {
+ bond_send_sw_interface_slave_details (am, reg, slave_if, mp->context);
+ }
+
+ vec_free (slaveifs);
+}
+
+#define vl_msg_name_crc_list
+#include <vnet/vnet_all_api_h.h>
+#undef vl_msg_name_crc_list
+
+static void
+bond_setup_message_id_table (api_main_t * am)
+{
+#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
+ foreach_vl_msg_name_crc_bond;
+#undef _
+}
+
+static clib_error_t *
+bond_api_hookup (vlib_main_t * vm)
+{
+ api_main_t *am = &api_main;
+
+#define _(N,n) \
+ vl_msg_api_set_handlers(VL_API_##N, #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_bond_api_msg;
+#undef _
+
+ /*
+ * Set up the (msg_name, crc, message-id) table
+ */
+ bond_setup_message_id_table (am);
+
+ return 0;
+}
+
+VLIB_API_INIT_FUNCTION (bond_api_hookup);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/bonding/cli.c b/src/vnet/bonding/cli.c
new file mode 100644
index 00000000000..b2d66f9f1c8
--- /dev/null
+++ b/src/vnet/bonding/cli.c
@@ -0,0 +1,706 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2017 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 <stdint.h>
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/bonding/node.h>
+
+void
+bond_disable_collecting_distributing (vlib_main_t * vm, slave_if_t * sif)
+{
+ bond_if_t *bif;
+ int i;
+ uword p;
+
+ bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
+ vec_foreach_index (i, bif->active_slaves)
+ {
+ p = *vec_elt_at_index (bif->active_slaves, i);
+ if (p == sif->sw_if_index)
+ {
+ vec_del1 (bif->active_slaves, i);
+ hash_unset (bif->active_slave_by_sw_if_index, sif->sw_if_index);
+ break;
+ }
+ }
+}
+
+void
+bond_enable_collecting_distributing (vlib_main_t * vm, slave_if_t * sif)
+{
+ bond_if_t *bif;
+
+ bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
+ if (!hash_get (bif->active_slave_by_sw_if_index, sif->sw_if_index))
+ {
+ hash_set (bif->active_slave_by_sw_if_index, sif->sw_if_index,
+ sif->sw_if_index);
+ vec_add1 (bif->active_slaves, sif->sw_if_index);
+ }
+}
+
+int
+bond_dump_ifs (bond_interface_details_t ** out_bondifs)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ bond_main_t *bm = &bond_main;
+ bond_if_t *bif;
+ vnet_hw_interface_t *hi;
+ bond_interface_details_t *r_bondifs = NULL;
+ bond_interface_details_t *bondif = NULL;
+
+ /* *INDENT-OFF* */
+ pool_foreach (bif, bm->interfaces,
+ vec_add2(r_bondifs, bondif, 1);
+ memset (bondif, 0, sizeof (*bondif));
+ bondif->sw_if_index = bif->sw_if_index;
+ hi = vnet_get_hw_interface (vnm, bif->hw_if_index);
+ clib_memcpy(bondif->interface_name, hi->name,
+ MIN (ARRAY_LEN (bondif->interface_name) - 1,
+ strlen ((const char *) hi->name)));
+ bondif->mode = bif->mode;
+ bondif->lb = bif->lb;
+ bondif->active_slaves = vec_len (bif->active_slaves);
+ bondif->slaves = vec_len (bif->slaves);
+ );
+ /* *INDENT-ON* */
+
+ *out_bondifs = r_bondifs;
+
+ return 0;
+}
+
+int
+bond_dump_slave_ifs (slave_interface_details_t ** out_slaveifs,
+ u32 bond_sw_if_index)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ bond_if_t *bif;
+ vnet_hw_interface_t *hi;
+ vnet_sw_interface_t *sw;
+ slave_interface_details_t *r_slaveifs = NULL;
+ slave_interface_details_t *slaveif = NULL;
+ u32 *sw_if_index = NULL;
+ slave_if_t *sif;
+
+ bif = bond_get_master_by_sw_if_index (bond_sw_if_index);
+ if (!bif)
+ return 1;
+
+ vec_foreach (sw_if_index, bif->slaves)
+ {
+ vec_add2 (r_slaveifs, slaveif, 1);
+ memset (slaveif, 0, sizeof (*slaveif));
+ sif = bond_get_slave_by_sw_if_index (*sw_if_index);
+ if (sif)
+ {
+ sw = vnet_get_sw_interface (vnm, sif->sw_if_index);
+ hi = vnet_get_hw_interface (vnm, sw->hw_if_index);
+ clib_memcpy (slaveif->interface_name, hi->name,
+ MIN (ARRAY_LEN (slaveif->interface_name) - 1,
+ strlen ((const char *) hi->name)));
+ slaveif->sw_if_index = sif->sw_if_index;
+ slaveif->is_passive = sif->is_passive;
+ slaveif->is_long_timeout = sif->is_long_timeout;
+ }
+ }
+ *out_slaveifs = r_slaveifs;
+
+ return 0;
+}
+
+static void
+bond_delete_neighbor (vlib_main_t * vm, bond_if_t * bif, slave_if_t * sif)
+{
+ bond_main_t *bm = &bond_main;
+ vnet_main_t *vnm = vnet_get_main ();
+ int i;
+ vnet_hw_interface_t *hw;
+
+ bif->port_number_bitmap =
+ clib_bitmap_set (bif->port_number_bitmap,
+ ntohs (sif->actor_admin.port_number) - 1, 0);
+ hash_unset (bm->neighbor_by_sw_if_index, sif->sw_if_index);
+ vec_free (sif->last_marker_pkt);
+ vec_free (sif->last_rx_pkt);
+ vec_foreach_index (i, bif->slaves)
+ {
+ uword p = *vec_elt_at_index (bif->slaves, i);
+ if (p == sif->sw_if_index)
+ {
+ vec_del1 (bif->slaves, i);
+ break;
+ }
+ }
+
+ bond_disable_collecting_distributing (vm, sif);
+
+ /* Put back the old mac */
+ hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
+ vnet_hw_interface_change_mac_address (vnm, hw->hw_if_index,
+ sif->persistent_hw_address);
+
+ pool_put (bm->neighbors, sif);
+
+ if ((bif->mode == BOND_MODE_LACP) && bm->lacp_enable_disable)
+ (*bm->lacp_enable_disable) (vm, bif, sif, 0);
+}
+
+int
+bond_delete_if (vlib_main_t * vm, u32 sw_if_index)
+{
+ bond_main_t *bm = &bond_main;
+ vnet_main_t *vnm = vnet_get_main ();
+ bond_if_t *bif;
+ slave_if_t *sif;
+ vnet_hw_interface_t *hw;
+ u32 *sif_sw_if_index;
+
+ hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
+ if (hw == NULL || bond_dev_class.index != hw->dev_class_index)
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ bif = bond_get_master_by_dev_instance (hw->dev_instance);
+
+ vec_foreach (sif_sw_if_index, bif->slaves)
+ {
+ sif = bond_get_slave_by_sw_if_index (*sif_sw_if_index);
+ if (sif)
+ bond_delete_neighbor (vm, bif, sif);
+ }
+
+ /* bring down the interface */
+ vnet_hw_interface_set_flags (vnm, bif->hw_if_index, 0);
+ vnet_sw_interface_set_flags (vnm, bif->sw_if_index, 0);
+
+ ethernet_delete_interface (vnm, bif->hw_if_index);
+
+ clib_bitmap_free (bif->port_number_bitmap);
+ hash_unset (bm->bond_by_sw_if_index, bif->sw_if_index);
+ memset (bif, 0, sizeof (*bif));
+ pool_put (bm->interfaces, bif);
+
+ return 0;
+}
+
+void
+bond_create_if (vlib_main_t * vm, bond_create_if_args_t * args)
+{
+ bond_main_t *bm = &bond_main;
+ vnet_main_t *vnm = vnet_get_main ();
+ vnet_sw_interface_t *sw;
+ bond_if_t *bif;
+
+ if ((args->mode == BOND_MODE_LACP) && bm->lacp_plugin_loaded == 0)
+ {
+ args->rv = VNET_API_ERROR_FEATURE_DISABLED;
+ args->error = clib_error_return (0, "LACP plugin is not loaded");
+ return;
+ }
+ if (args->mode > BOND_MODE_LACP || args->mode < BOND_MODE_ROUND_ROBIN)
+ {
+ args->rv = VNET_API_ERROR_INVALID_ARGUMENT;
+ args->error = clib_error_return (0, "Invalid mode");
+ return;
+ }
+ if (args->lb > BOND_LB_L23)
+ {
+ args->rv = VNET_API_ERROR_INVALID_ARGUMENT;
+ args->error = clib_error_return (0, "Invalid load-balance");
+ return;
+ }
+ pool_get (bm->interfaces, bif);
+ memset (bif, 0, sizeof (*bif));
+ bif->dev_instance = bif - bm->interfaces;
+ bif->lb = args->lb;
+ bif->mode = args->mode;
+
+ // Special load-balance mode used for rr and bc
+ if (bif->mode == BOND_MODE_ROUND_ROBIN)
+ bif->lb = BOND_LB_RR;
+ else if (bif->mode == BOND_MODE_BROADCAST)
+ bif->lb = BOND_LB_BC;
+
+ bif->use_custom_mac = args->hw_addr_set;
+ if (!args->hw_addr_set)
+ {
+ f64 now = vlib_time_now (vm);
+ u32 rnd;
+ rnd = (u32) (now * 1e6);
+ rnd = random_u32 (&rnd);
+
+ memcpy (args->hw_addr + 2, &rnd, sizeof (rnd));
+ args->hw_addr[0] = 2;
+ args->hw_addr[1] = 0xfe;
+ }
+ memcpy (bif->hw_address, args->hw_addr, 6);
+ args->error = ethernet_register_interface
+ (vnm, bond_dev_class.index, bif - bm->interfaces /* device instance */ ,
+ bif->hw_address /* ethernet address */ ,
+ &bif->hw_if_index, 0 /* flag change */ );
+
+ if (args->error)
+ {
+ args->rv = VNET_API_ERROR_INVALID_REGISTRATION;
+ pool_put (bm->interfaces, bif);
+ return;
+ }
+
+ sw = vnet_get_hw_sw_interface (vnm, bif->hw_if_index);
+ bif->sw_if_index = sw->sw_if_index;
+ bif->group = bif->sw_if_index;
+
+ vnet_hw_interface_set_flags (vnm, bif->hw_if_index,
+ VNET_HW_INTERFACE_FLAG_LINK_UP);
+
+ hash_set (bm->bond_by_sw_if_index, bif->sw_if_index, bif->dev_instance);
+
+ // for return
+ args->sw_if_index = bif->sw_if_index;
+}
+
+static clib_error_t *
+bond_create_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ bond_create_if_args_t args = { 0 };
+ u8 mode_is_set = 0;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return clib_error_return (0, "Missing required arguments.");
+
+ args.mode = -1;
+ args.lb = BOND_LB_L2;
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "mode %U", unformat_bond_mode, &args.mode))
+ mode_is_set = 1;
+ else if (((args.mode == BOND_MODE_LACP) || (args.mode == BOND_MODE_XOR))
+ && unformat (line_input, "load-balance %U",
+ unformat_bond_load_balance, &args.lb))
+ ;
+ else if (unformat (line_input, "hw-addr %U",
+ unformat_ethernet_address, args.hw_addr))
+ args.hw_addr_set = 1;
+ else
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ }
+ unformat_free (line_input);
+
+ if (mode_is_set == 0)
+ return clib_error_return (0, "Missing bond mode");
+
+ bond_create_if (vm, &args);
+
+ return args.error;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (bond_create_command, static) = {
+ .path = "create bond",
+ .short_help = "create bond mode {round-robin | active-backup | broadcast | "
+ "{lacp | xor} [load-balance { l2 | l23 | l34 }]} [hw-addr <mac-address>]",
+ .function = bond_create_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+bond_delete_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u32 sw_if_index = ~0;
+ vnet_main_t *vnm = vnet_get_main ();
+ int rv;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return clib_error_return (0, "Missing <interface>");
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "sw_if_index %d", &sw_if_index))
+ ;
+ else if (unformat (line_input, "%U", unformat_vnet_sw_interface,
+ vnm, &sw_if_index))
+ ;
+ else
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ }
+ unformat_free (line_input);
+
+ if (sw_if_index == ~0)
+ return clib_error_return (0,
+ "please specify interface name or sw_if_index");
+
+ rv = bond_delete_if (vm, sw_if_index);
+ if (rv == VNET_API_ERROR_INVALID_SW_IF_INDEX)
+ return clib_error_return (0, "not a bond interface");
+ else if (rv != 0)
+ return clib_error_return (0, "error on deleting bond interface");
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (bond_delete__command, static) =
+{
+ .path = "delete bond",
+ .short_help = "delete bond {<interface> | sw_if_index <sw_idx>}",
+ .function = bond_delete_command_fn,
+};
+/* *INDENT-ON* */
+
+void
+bond_enslave (vlib_main_t * vm, bond_enslave_args_t * args)
+{
+ bond_main_t *bm = &bond_main;
+ vnet_main_t *vnm = vnet_get_main ();
+ bond_if_t *bif;
+ slave_if_t *sif;
+ vnet_interface_main_t *im = &vnm->interface_main;
+ vnet_hw_interface_t *hw, *hw2;
+ vnet_sw_interface_t *sw;
+
+ bif = bond_get_master_by_sw_if_index (args->group);
+ if (!bif)
+ {
+ args->rv = VNET_API_ERROR_INVALID_INTERFACE;
+ args->error = clib_error_return (0, "bond interface not found");
+ return;
+ }
+ // make sure the interface is not already enslaved
+ if (bond_get_slave_by_sw_if_index (args->slave))
+ {
+ args->rv = VNET_API_ERROR_VALUE_EXIST;
+ args->error = clib_error_return (0, "interface was already enslaved");
+ return;
+ }
+ hw = vnet_get_sup_hw_interface (vnm, args->slave);
+ if (hw->dev_class_index == bond_dev_class.index)
+ {
+ args->rv = VNET_API_ERROR_INVALID_INTERFACE;
+ args->error =
+ clib_error_return (0, "bond interface cannot be enslaved");
+ return;
+ }
+ pool_get (bm->neighbors, sif);
+ memset (sif, 0, sizeof (*sif));
+ clib_spinlock_init (&sif->lockp);
+ sw = pool_elt_at_index (im->sw_interfaces, args->slave);
+ sif->port_enabled = sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
+ sif->sw_if_index = sw->sw_if_index;
+ sif->hw_if_index = sw->hw_if_index;
+ sif->packet_template_index = (u8) ~ 0;
+ sif->is_passive = args->is_passive;
+ sif->group = args->group;
+ sif->bif_dev_instance = bif->dev_instance;
+ sif->mode = bif->mode;
+
+ sif->is_long_timeout = args->is_long_timeout;
+ if (args->is_long_timeout)
+ sif->ttl_in_seconds = LACP_LONG_TIMOUT_TIME;
+ else
+ sif->ttl_in_seconds = LACP_SHORT_TIMOUT_TIME;
+
+ hash_set (bm->neighbor_by_sw_if_index, sif->sw_if_index,
+ sif - bm->neighbors);
+ vec_add1 (bif->slaves, sif->sw_if_index);
+
+ hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
+ /* Save the old mac */
+ memcpy (sif->persistent_hw_address, hw->hw_address, 6);
+ if (bif->use_custom_mac)
+ {
+ vnet_hw_interface_change_mac_address (vnm, hw->hw_if_index,
+ bif->hw_address);
+ }
+ else
+ {
+ // bond interface gets the mac address from the first slave
+ if (vec_len (bif->slaves) == 1)
+ {
+ memcpy (bif->hw_address, hw->hw_address, 6);
+ hw2 = vnet_get_sup_hw_interface (vnm, bif->sw_if_index);
+ vnet_hw_interface_change_mac_address (vnm, hw2->hw_if_index,
+ hw->hw_address);
+ }
+ else
+ {
+ // subsequent slaves gets the mac address of the bond interface
+ vnet_hw_interface_change_mac_address (vnm, hw->hw_if_index,
+ bif->hw_address);
+ }
+ }
+
+ if ((bif->mode == BOND_MODE_LACP) && bm->lacp_enable_disable)
+ {
+ (*bm->lacp_enable_disable) (vm, bif, sif, 1);
+ }
+ else
+ {
+ bond_enable_collecting_distributing (vm, sif);
+ }
+
+ args->rv = vnet_feature_enable_disable ("device-input", "bond-input",
+ hw->hw_if_index, 1, 0, 0);
+
+ if (args->rv)
+ {
+ args->error =
+ clib_error_return (0,
+ "Error encountered on input feature arc enable");
+ }
+}
+
+static clib_error_t *
+enslave_interface_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ bond_enslave_args_t args = { 0 };
+ unformat_input_t _line_input, *line_input = &_line_input;
+ vnet_main_t *vnm = vnet_get_main ();
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return clib_error_return (0, "Missing required arguments.");
+
+ args.slave = ~0;
+ args.group = ~0;
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "interface %U",
+ unformat_vnet_sw_interface, vnm, &args.slave))
+ ;
+ else if (unformat (line_input, "to %U", unformat_vnet_sw_interface, vnm,
+ &args.group))
+ ;
+ else if (unformat (line_input, "passive"))
+ args.is_passive = 1;
+ else if (unformat (line_input, "long-timeout"))
+ args.is_long_timeout = 1;
+ else
+ {
+ args.error = clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ break;
+ }
+ }
+ unformat_free (line_input);
+
+ if (args.error)
+ return args.error;
+ if (args.group == ~0)
+ return clib_error_return (0, "Missing bond interface");
+ if (args.slave == ~0)
+ return clib_error_return (0, "please specify valid interface name");
+
+ bond_enslave (vm, &args);
+
+ return args.error;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (enslave_interface_command, static) = {
+ .path = "enslave",
+ .short_help = "enslave interface <interface> to <BondEthernetx> [passive] [long-timeout]",
+ .function = enslave_interface_command_fn,
+};
+/* *INDENT-ON* */
+
+void
+bond_detach_slave (vlib_main_t * vm, bond_detach_slave_args_t * args)
+{
+ bond_if_t *bif;
+ slave_if_t *sif;
+
+ sif = bond_get_slave_by_sw_if_index (args->slave);
+ if (!sif)
+ {
+ args->rv = VNET_API_ERROR_INVALID_INTERFACE;
+ args->error = clib_error_return (0, "interface was not enslaved");
+ return;
+ }
+ bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
+ bond_delete_neighbor (vm, bif, sif);
+}
+
+static clib_error_t *
+detach_interface_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ bond_detach_slave_args_t args = { 0 };
+ unformat_input_t _line_input, *line_input = &_line_input;
+ vnet_main_t *vnm = vnet_get_main ();
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return clib_error_return (0, "Missing required arguments.");
+
+ args.slave = ~0;
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "interface %U",
+ unformat_vnet_sw_interface, vnm, &args.slave))
+ ;
+ else
+ {
+ args.error = clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ break;
+ }
+ }
+ unformat_free (line_input);
+
+ if (args.error)
+ return args.error;
+ if (args.slave == ~0)
+ return clib_error_return (0, "please specify valid interface name");
+
+ bond_detach_slave (vm, &args);
+
+ return args.error;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (detach_interface_command, static) = {
+ .path = "detach",
+ .short_help = "detach interface <interface>",
+ .function = detach_interface_command_fn,
+};
+/* *INDENT-ON* */
+
+static void
+show_bond (vlib_main_t * vm)
+{
+ bond_main_t *bm = &bond_main;
+ bond_if_t *bif;
+
+ vlib_cli_output (vm, "%-16s %-12s %-12s %-13s %-14s %s",
+ "interface name", "sw_if_index", "mode",
+ "load balance", "active slaves", "slaves");
+
+ /* *INDENT-OFF* */
+ pool_foreach (bif, bm->interfaces,
+ ({
+ vlib_cli_output (vm, "%-16U %-12d %-12U %-13U %-14u %u",
+ format_bond_interface_name, bif->dev_instance,
+ bif->sw_if_index, format_bond_mode, bif->mode,
+ format_bond_load_balance, bif->lb,
+ vec_len (bif->active_slaves), vec_len (bif->slaves));
+ }));
+ /* *INDENT-ON* */
+}
+
+static void
+show_bond_details (vlib_main_t * vm)
+{
+ bond_main_t *bm = &bond_main;
+ bond_if_t *bif;
+ u32 *sw_if_index;
+
+ /* *INDENT-OFF* */
+ pool_foreach (bif, bm->interfaces,
+ ({
+ vlib_cli_output (vm, "%U", format_bond_interface_name, bif->dev_instance);
+ vlib_cli_output (vm, " mode: %U",
+ format_bond_mode, bif->mode);
+ vlib_cli_output (vm, " load balance: %U",
+ format_bond_load_balance, bif->lb);
+ if (bif->mode == BOND_MODE_ROUND_ROBIN)
+ vlib_cli_output (vm, " last xmit slave index: %u",
+ bif->lb_rr_last_index);
+ vlib_cli_output (vm, " number of active slaves: %d",
+ vec_len (bif->active_slaves));
+ vec_foreach (sw_if_index, bif->active_slaves)
+ {
+ vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name,
+ vnet_get_main (), *sw_if_index);
+ }
+ vlib_cli_output (vm, " number of slaves: %d", vec_len (bif->slaves));
+ vec_foreach (sw_if_index, bif->slaves)
+ {
+ vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name,
+ vnet_get_main (), *sw_if_index);
+ }
+ vlib_cli_output (vm, " device instance: %d", bif->dev_instance);
+ vlib_cli_output (vm, " sw_if_index: %d", bif->sw_if_index);
+ vlib_cli_output (vm, " hw_if_index: %d", bif->hw_if_index);
+ }));
+ /* *INDENT-ON* */
+}
+
+static clib_error_t *
+show_bond_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ u8 details = 0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "details"))
+ details = 1;
+ else
+ {
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
+ }
+ }
+
+ if (details)
+ show_bond_details (vm);
+ else
+ show_bond (vm);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_bond_command, static) = {
+ .path = "show bond",
+ .short_help = "show bond [details]",
+ .function = show_bond_fn,
+};
+/* *INDENT-ON* */
+
+clib_error_t *
+bond_cli_init (vlib_main_t * vm)
+{
+ bond_main_t *bm = &bond_main;
+
+ bm->vlib_main = vm;
+ bm->vnet_main = vnet_get_main ();
+ bm->neighbor_by_sw_if_index = hash_create (0, sizeof (uword));
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (bond_cli_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/bonding/device.c b/src/vnet/bonding/device.c
new file mode 100644
index 00000000000..8f9b3a95591
--- /dev/null
+++ b/src/vnet/bonding/device.c
@@ -0,0 +1,610 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2017 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.
+ *------------------------------------------------------------------
+ */
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ip/ip6_hop_by_hop_packet.h>
+#include <vnet/bonding/node.h>
+
+#define foreach_bond_tx_error \
+ _(NONE, "no error") \
+ _(IF_DOWN, "interface down") \
+ _(NO_SLAVE, "no slave")
+
+typedef enum
+{
+#define _(f,s) BOND_TX_ERROR_##f,
+ foreach_bond_tx_error
+#undef _
+ BOND_TX_N_ERROR,
+} bond_tx_error_t;
+
+static char *bond_tx_error_strings[] = {
+#define _(n,s) s,
+ foreach_bond_tx_error
+#undef _
+};
+
+static u8 *
+format_bond_tx_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ bond_packet_trace_t *t = va_arg (*args, bond_packet_trace_t *);
+ vnet_hw_interface_t *hw, *hw1;
+ vnet_main_t *vnm = vnet_get_main ();
+
+ hw = vnet_get_sup_hw_interface (vnm, t->sw_if_index);
+ hw1 = vnet_get_sup_hw_interface (vnm, t->bond_sw_if_index);
+ s = format (s, "src %U, dst %U, %s -> %s",
+ format_ethernet_address, t->ethernet.src_address,
+ format_ethernet_address, t->ethernet.dst_address,
+ hw->name, hw1->name);
+
+ return s;
+}
+
+u8 *
+format_bond_interface_name (u8 * s, va_list * args)
+{
+ u32 dev_instance = va_arg (*args, u32);
+ bond_main_t *bm = &bond_main;
+ bond_if_t *bif = pool_elt_at_index (bm->interfaces, dev_instance);
+
+ s = format (s, "BondEthernet%lu", bif->dev_instance);
+
+ return s;
+}
+
+static __clib_unused clib_error_t *
+bond_subif_add_del_function (vnet_main_t * vnm, u32 hw_if_index,
+ struct vnet_sw_interface_t *st, int is_add)
+{
+ /* Nothing for now */
+ return 0;
+}
+
+static clib_error_t *
+bond_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
+{
+ vnet_hw_interface_t *hif = vnet_get_hw_interface (vnm, hw_if_index);
+ uword is_up = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0;
+ bond_main_t *bm = &bond_main;
+ bond_if_t *bif = pool_elt_at_index (bm->interfaces, hif->dev_instance);
+
+ bif->admin_up = is_up;
+ if (is_up && vec_len (bif->active_slaves))
+ vnet_hw_interface_set_flags (vnm, bif->hw_if_index,
+ VNET_HW_INTERFACE_FLAG_LINK_UP);
+ return 0;
+}
+
+static inline u32
+bond_load_balance_broadcast (vlib_main_t * vm, vlib_node_runtime_t * node,
+ bond_if_t * bif, vlib_buffer_t * b0)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ vlib_buffer_t *c0;
+ int i;
+ u32 *to_next = 0;
+ u32 sw_if_index;
+ vlib_frame_t *f;
+
+
+ for (i = 1; i < vec_len (bif->active_slaves); i++)
+ {
+ sw_if_index = *vec_elt_at_index (bif->active_slaves, i);
+ f = vnet_get_frame_to_sw_interface (vnm, sw_if_index);
+ to_next = vlib_frame_vector_args (f);
+ to_next += f->n_vectors;
+ c0 = vlib_buffer_copy (vm, b0);
+ if (PREDICT_TRUE (c0 != 0))
+ {
+ vnet_buffer (c0)->sw_if_index[VLIB_TX] = sw_if_index;
+ to_next[0] = vlib_get_buffer_index (vm, c0);
+ f->n_vectors++;
+ vnet_put_frame_to_sw_interface (vnm, sw_if_index, f);
+ }
+ }
+
+ return 0;
+}
+
+static inline u32
+bond_load_balance_l2 (vlib_main_t * vm, vlib_node_runtime_t * node,
+ bond_if_t * bif, vlib_buffer_t * b0)
+{
+ ethernet_header_t *eth = (ethernet_header_t *) vlib_buffer_get_current (b0);
+ u32 a = 0, b = 0, c = 0, t1, t2;
+ u16 t11, t22;
+
+ memcpy (&t1, eth->src_address, sizeof (t1));
+ memcpy (&t11, &eth->src_address[4], sizeof (t11));
+ a = t1 ^ t11;
+
+ memcpy (&t2, eth->dst_address, sizeof (t2));
+ memcpy (&t22, &eth->dst_address[4], sizeof (t22));
+ b = t2 ^ t22;
+
+ hash_v3_mix32 (a, b, c);
+ hash_v3_finalize32 (a, b, c);
+
+ return c % vec_len (bif->active_slaves);
+}
+
+static inline u16 *
+bond_locate_ethertype (ethernet_header_t * eth)
+{
+ u16 *ethertype_p;
+ ethernet_vlan_header_t *vlan;
+
+ if (!ethernet_frame_is_tagged (clib_net_to_host_u16 (eth->type)))
+ {
+ ethertype_p = &eth->type;
+ }
+ else
+ {
+ vlan = (void *) (eth + 1);
+ ethertype_p = &vlan->type;
+ if (*ethertype_p == ntohs (ETHERNET_TYPE_VLAN))
+ {
+ vlan++;
+ ethertype_p = &vlan->type;
+ }
+ }
+ return ethertype_p;
+}
+
+static inline u32
+bond_load_balance_l23 (vlib_main_t * vm, vlib_node_runtime_t * node,
+ bond_if_t * bif, vlib_buffer_t * b0)
+{
+ ethernet_header_t *eth = (ethernet_header_t *) vlib_buffer_get_current (b0);
+ u8 ip_version;
+ ip4_header_t *ip4;
+ u16 ethertype, *ethertype_p;
+
+ ethertype_p = bond_locate_ethertype (eth);
+ ethertype = *ethertype_p;
+
+ if ((ethertype != htons (ETHERNET_TYPE_IP4)) &&
+ (ethertype != htons (ETHERNET_TYPE_IP6)))
+ return (bond_load_balance_l2 (vm, node, bif, b0));
+
+ ip4 = (ip4_header_t *) (ethertype_p + 1);
+ ip_version = (ip4->ip_version_and_header_length >> 4);
+
+ if (ip_version == 0x4)
+ {
+ u16 t11, t22;
+ u32 a = 0, b = 0, c = 0, t1, t2;
+
+ memcpy (&t1, eth->src_address, sizeof (t1));
+ memcpy (&t11, &eth->src_address[4], sizeof (t11));
+ a = t1 ^ t11;
+
+ memcpy (&t2, eth->dst_address, sizeof (t2));
+ memcpy (&t22, &eth->dst_address[4], sizeof (t22));
+ b = t2 ^ t22;
+
+ c = ip4->src_address.data_u32 ^ ip4->dst_address.data_u32;
+
+ hash_v3_mix32 (a, b, c);
+ hash_v3_finalize32 (a, b, c);
+
+ return c % vec_len (bif->active_slaves);
+ }
+ else if (ip_version == 0x6)
+ {
+ u64 a, b, c;
+ u64 t1 = 0, t2 = 0;
+ ip6_header_t *ip6 = (ip6_header_t *) (eth + 1);
+
+ memcpy (&t1, eth->src_address, sizeof (eth->src_address));
+ memcpy (&t2, eth->dst_address, sizeof (eth->dst_address));
+ a = t1 ^ t2;
+
+ b = (ip6->src_address.as_u64[0] ^ ip6->src_address.as_u64[1]);
+ c = (ip6->dst_address.as_u64[0] ^ ip6->dst_address.as_u64[1]);
+
+ hash_mix64 (a, b, c);
+ return c % vec_len (bif->active_slaves);
+ }
+ return (bond_load_balance_l2 (vm, node, bif, b0));
+}
+
+static inline u32
+bond_load_balance_l34 (vlib_main_t * vm, vlib_node_runtime_t * node,
+ bond_if_t * bif, vlib_buffer_t * b0)
+{
+ ethernet_header_t *eth = (ethernet_header_t *) vlib_buffer_get_current (b0);
+ u8 ip_version;
+ uword is_tcp_udp = 0;
+ ip4_header_t *ip4;
+ u16 ethertype, *ethertype_p;
+
+ ethertype_p = bond_locate_ethertype (eth);
+ ethertype = *ethertype_p;
+
+ if ((ethertype != htons (ETHERNET_TYPE_IP4)) &&
+ (ethertype != htons (ETHERNET_TYPE_IP6)))
+ return (bond_load_balance_l2 (vm, node, bif, b0));
+
+ ip4 = (ip4_header_t *) (ethertype_p + 1);
+ ip_version = (ip4->ip_version_and_header_length >> 4);
+
+ if (ip_version == 0x4)
+ {
+ u32 a = 0, b = 0, c = 0, t1, t2;
+ tcp_header_t *tcp = (void *) (ip4 + 1);
+ is_tcp_udp = (ip4->protocol == IP_PROTOCOL_TCP) ||
+ (ip4->protocol == IP_PROTOCOL_UDP);
+
+ a = ip4->src_address.data_u32 ^ ip4->dst_address.data_u32;
+
+ t1 = is_tcp_udp ? tcp->src : 0;
+ t2 = is_tcp_udp ? tcp->dst : 0;
+ b = t1 + (t2 << 16);
+
+ hash_v3_mix32 (a, b, c);
+ hash_v3_finalize32 (a, b, c);
+
+ return c % vec_len (bif->active_slaves);
+ }
+ else if (ip_version == 0x6)
+ {
+ u64 a, b, c;
+ u64 t1, t2;
+ ip6_header_t *ip6 = (ip6_header_t *) (eth + 1);
+ tcp_header_t *tcp = (void *) (ip6 + 1);
+
+ if (PREDICT_TRUE ((ip6->protocol == IP_PROTOCOL_TCP) ||
+ (ip6->protocol == IP_PROTOCOL_UDP)))
+ {
+ is_tcp_udp = 1;
+ tcp = (void *) (ip6 + 1);
+ }
+ else if (ip6->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)
+ {
+ ip6_hop_by_hop_header_t *hbh =
+ (ip6_hop_by_hop_header_t *) (ip6 + 1);
+ if ((hbh->protocol == IP_PROTOCOL_TCP)
+ || (hbh->protocol == IP_PROTOCOL_UDP))
+ {
+ is_tcp_udp = 1;
+ tcp = (tcp_header_t *) ((u8 *) hbh + ((hbh->length + 1) << 3));
+ }
+ }
+ a = (ip6->src_address.as_u64[0] ^ ip6->src_address.as_u64[1]);
+ b = (ip6->dst_address.as_u64[0] ^ ip6->dst_address.as_u64[1]);
+
+ t1 = is_tcp_udp ? tcp->src : 0;
+ t2 = is_tcp_udp ? tcp->dst : 0;
+ c = (t2 << 16) | t1;
+ hash_mix64 (a, b, c);
+
+ return c % vec_len (bif->active_slaves);
+ }
+
+ return (bond_load_balance_l2 (vm, node, bif, b0));
+}
+
+static inline u32
+bond_load_balance_round_robin (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ bond_if_t * bif, vlib_buffer_t * b0)
+{
+ bif->lb_rr_last_index++;
+ bif->lb_rr_last_index %= vec_len (bif->active_slaves);
+
+ return bif->lb_rr_last_index;
+}
+
+static inline u32
+bond_load_balance_active_backup (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ bond_if_t * bif, vlib_buffer_t * b0)
+{
+ /* First interface is the active, the rest is backup */
+ return 0;
+}
+
+static bond_load_balance_func_t bond_load_balance_table[] = {
+#define _(v,f,s, p) { bond_load_balance_##p },
+ foreach_bond_lb_algo
+#undef _
+};
+
+static uword
+bond_tx_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ vnet_interface_output_runtime_t *rund = (void *) node->runtime_data;
+ bond_main_t *bm = &bond_main;
+ bond_if_t *bif = pool_elt_at_index (bm->interfaces, rund->dev_instance);
+ u32 bi0, bi1, bi2, bi3;
+ vlib_buffer_t *b0, *b1, *b2, *b3;
+ u32 *from = vlib_frame_vector_args (frame);
+ u32 n_left_from;
+ ethernet_header_t *eth;
+ u32 next0 = 0, next1 = 0, next2 = 0, next3 = 0;
+ u32 port, port1, port2, port3;
+ u32 sw_if_index, sw_if_index1, sw_if_index2, sw_if_index3;
+ bond_packet_trace_t *t0;
+ uword n_trace = vlib_get_trace_count (vm, node);
+ u16 thread_index = vlib_get_thread_index ();
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 *to_next, *to_next1, *to_next2, *to_next3;
+ u32 sif_if_index, sif_if_index1, sif_if_index2, sif_if_index3;
+ vlib_frame_t *f, *f1, *f2, *f3;
+
+ if (PREDICT_FALSE (bif->admin_up == 0))
+ {
+ vlib_buffer_free (vm, vlib_frame_args (frame), frame->n_vectors);
+ vlib_increment_simple_counter (vnet_main.interface_main.sw_if_counters +
+ VNET_INTERFACE_COUNTER_DROP,
+ thread_index, bif->sw_if_index,
+ frame->n_vectors);
+ vlib_error_count (vm, node->node_index, BOND_TX_ERROR_IF_DOWN,
+ frame->n_vectors);
+ return frame->n_vectors;
+ }
+
+ if (PREDICT_FALSE (vec_len (bif->active_slaves) == 0))
+ {
+ bi0 = from[0];
+ b0 = vlib_get_buffer (vm, bi0);
+ vlib_increment_combined_counter
+ (vnet_main.interface_main.combined_sw_if_counters
+ + VNET_INTERFACE_COUNTER_TX, thread_index, bif->sw_if_index,
+ frame->n_vectors, b0->current_length);
+
+ vlib_buffer_free (vm, vlib_frame_args (frame), frame->n_vectors);
+ vlib_increment_simple_counter (vnet_main.interface_main.sw_if_counters +
+ VNET_INTERFACE_COUNTER_DROP,
+ thread_index, bif->sw_if_index,
+ frame->n_vectors);
+ vlib_error_count (vm, node->node_index, BOND_TX_ERROR_NO_SLAVE,
+ frame->n_vectors);
+ return frame->n_vectors;
+ }
+
+ /* Number of buffers / pkts */
+ n_left_from = frame->n_vectors;
+
+ while (n_left_from >= 8)
+ {
+ // Prefetch next iteration
+ {
+ vlib_buffer_t *p4, *p5, *p6, *p7;
+
+ p4 = vlib_get_buffer (vm, from[4]);
+ p5 = vlib_get_buffer (vm, from[5]);
+ p6 = vlib_get_buffer (vm, from[6]);
+ p7 = vlib_get_buffer (vm, from[7]);
+
+ vlib_prefetch_buffer_header (p4, STORE);
+ vlib_prefetch_buffer_header (p5, STORE);
+ vlib_prefetch_buffer_header (p6, STORE);
+ vlib_prefetch_buffer_header (p7, STORE);
+
+ CLIB_PREFETCH (p4->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ CLIB_PREFETCH (p5->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ CLIB_PREFETCH (p6->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ CLIB_PREFETCH (p7->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ }
+
+ bi0 = from[0];
+ bi1 = from[1];
+ bi2 = from[2];
+ bi3 = from[3];
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+ b2 = vlib_get_buffer (vm, bi2);
+ b3 = vlib_get_buffer (vm, bi3);
+
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b1);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b2);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b3);
+
+ sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+ sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_TX];
+ sw_if_index2 = vnet_buffer (b2)->sw_if_index[VLIB_TX];
+ sw_if_index3 = vnet_buffer (b3)->sw_if_index[VLIB_TX];
+
+ port =
+ (bond_load_balance_table[bif->lb]).load_balance (vm, node, bif, b0);
+ port1 =
+ (bond_load_balance_table[bif->lb]).load_balance (vm, node, bif, b1);
+ port2 =
+ (bond_load_balance_table[bif->lb]).load_balance (vm, node, bif, b2);
+ port3 =
+ (bond_load_balance_table[bif->lb]).load_balance (vm, node, bif, b3);
+
+ sif_if_index = *vec_elt_at_index (bif->active_slaves, port);
+ sif_if_index1 = *vec_elt_at_index (bif->active_slaves, port1);
+ sif_if_index2 = *vec_elt_at_index (bif->active_slaves, port2);
+ sif_if_index3 = *vec_elt_at_index (bif->active_slaves, port3);
+
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = sif_if_index;
+ vnet_buffer (b1)->sw_if_index[VLIB_TX] = sif_if_index1;
+ vnet_buffer (b2)->sw_if_index[VLIB_TX] = sif_if_index2;
+ vnet_buffer (b3)->sw_if_index[VLIB_TX] = sif_if_index3;
+
+ f = vnet_get_frame_to_sw_interface (vnm, sif_if_index);
+ f1 = vnet_get_frame_to_sw_interface (vnm, sif_if_index1);
+ f2 = vnet_get_frame_to_sw_interface (vnm, sif_if_index2);
+ f3 = vnet_get_frame_to_sw_interface (vnm, sif_if_index3);
+
+ to_next = vlib_frame_vector_args (f);
+ to_next1 = vlib_frame_vector_args (f1);
+ to_next2 = vlib_frame_vector_args (f2);
+ to_next3 = vlib_frame_vector_args (f3);
+
+ to_next += f->n_vectors;
+ to_next1 += f1->n_vectors;
+ to_next2 += f2->n_vectors;
+ to_next3 += f3->n_vectors;
+
+ to_next[0] = vlib_get_buffer_index (vm, b0);
+ to_next1[0] = vlib_get_buffer_index (vm, b1);
+ to_next2[0] = vlib_get_buffer_index (vm, b2);
+ to_next3[0] = vlib_get_buffer_index (vm, b3);
+
+ f->n_vectors++;
+ f1->n_vectors++;
+ f2->n_vectors++;
+ f3->n_vectors++;
+
+ vnet_put_frame_to_sw_interface (vnm, sif_if_index, f);
+ vnet_put_frame_to_sw_interface (vnm, sif_if_index1, f1);
+ vnet_put_frame_to_sw_interface (vnm, sif_if_index2, f2);
+ vnet_put_frame_to_sw_interface (vnm, sif_if_index3, f3);
+
+ if (PREDICT_FALSE (n_trace > 0))
+ {
+ vlib_trace_buffer (vm, node, next0, b0, 0 /* follow_chain */ );
+ vlib_set_trace_count (vm, node, --n_trace);
+ t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
+ eth = (ethernet_header_t *) vlib_buffer_get_current (b0);
+ t0->ethernet = *eth;
+ t0->sw_if_index = sw_if_index;
+ t0->bond_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+
+ if (PREDICT_TRUE (n_trace > 0))
+ {
+ vlib_trace_buffer (vm, node, next1, b1, 0 /* follow_chain */ );
+ vlib_set_trace_count (vm, node, --n_trace);
+ t0 = vlib_add_trace (vm, node, b1, sizeof (*t0));
+ eth = (ethernet_header_t *) vlib_buffer_get_current (b1);
+ t0->ethernet = *eth;
+ t0->sw_if_index = sw_if_index1;
+ t0->bond_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX];
+
+ if (PREDICT_TRUE (n_trace > 0))
+ {
+ vlib_trace_buffer (vm, node, next2, b2,
+ 0 /* follow_chain */ );
+ vlib_set_trace_count (vm, node, --n_trace);
+ t0 = vlib_add_trace (vm, node, b2, sizeof (*t0));
+ eth = (ethernet_header_t *) vlib_buffer_get_current (b2);
+ t0->ethernet = *eth;
+ t0->sw_if_index = sw_if_index2;
+ t0->bond_sw_if_index =
+ vnet_buffer (b2)->sw_if_index[VLIB_TX];
+
+ if (PREDICT_TRUE (n_trace > 0))
+ {
+ vlib_trace_buffer (vm, node, next3, b3,
+ 0 /* follow_chain */ );
+ vlib_set_trace_count (vm, node, --n_trace);
+ t0 = vlib_add_trace (vm, node, b3, sizeof (*t0));
+ eth =
+ (ethernet_header_t *) vlib_buffer_get_current (b3);
+ t0->ethernet = *eth;
+ t0->sw_if_index = sw_if_index3;
+ t0->bond_sw_if_index =
+ vnet_buffer (b3)->sw_if_index[VLIB_TX];
+ }
+ }
+ }
+ }
+
+ from += 4;
+ n_left_from -= 4;
+ }
+
+ while (n_left_from > 0)
+ {
+ // Prefetch next iteration
+ if (n_left_from > 1)
+ {
+ vlib_buffer_t *p2;
+
+ p2 = vlib_get_buffer (vm, from[1]);
+ vlib_prefetch_buffer_header (p2, STORE);
+ CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ }
+
+ bi0 = from[0];
+ b0 = vlib_get_buffer (vm, bi0);
+
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+
+ sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+
+ port =
+ (bond_load_balance_table[bif->lb]).load_balance (vm, node, bif, b0);
+ sif_if_index = *vec_elt_at_index (bif->active_slaves, port);
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = sif_if_index;
+ f = vnet_get_frame_to_sw_interface (vnm, sif_if_index);
+ to_next = vlib_frame_vector_args (f);
+ to_next += f->n_vectors;
+
+ to_next[0] = vlib_get_buffer_index (vm, b0);
+ f->n_vectors++;
+ vnet_put_frame_to_sw_interface (vnm, sif_if_index, f);
+
+ if (PREDICT_FALSE (n_trace > 0))
+ {
+ vlib_trace_buffer (vm, node, next0, b0, 0 /* follow_chain */ );
+ vlib_set_trace_count (vm, node, --n_trace);
+ t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
+ eth = (ethernet_header_t *) vlib_buffer_get_current (b0);
+ t0->ethernet = *eth;
+ t0->sw_if_index = sw_if_index;
+ t0->bond_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+ }
+
+ from += 1;
+ n_left_from -= 1;
+ }
+
+ vlib_increment_simple_counter (vnet_main.interface_main.sw_if_counters
+ + VNET_INTERFACE_COUNTER_TX, thread_index,
+ bif->sw_if_index, frame->n_vectors);
+
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VNET_DEVICE_CLASS (bond_dev_class) = {
+ .name = "bond",
+ .tx_function = bond_tx_fn,
+ .tx_function_n_errors = BOND_TX_N_ERROR,
+ .tx_function_error_strings = bond_tx_error_strings,
+ .format_device_name = format_bond_interface_name,
+ .admin_up_down_function = bond_interface_admin_up_down,
+ .subif_add_del_function = bond_subif_add_del_function,
+ .format_tx_trace = format_bond_tx_trace,
+};
+
+VLIB_DEVICE_TX_FUNCTION_MULTIARCH (bond_dev_class, bond_tx_fn)
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/bonding/node.c b/src/vnet/bonding/node.c
new file mode 100644
index 00000000000..4deec829195
--- /dev/null
+++ b/src/vnet/bonding/node.c
@@ -0,0 +1,509 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2017 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.
+ *------------------------------------------------------------------
+ */
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <vnet/llc/llc.h>
+#include <vnet/snap/snap.h>
+#include <vnet/bonding/node.h>
+
+bond_main_t bond_main;
+
+#define foreach_bond_input_error \
+ _(NONE, "no error") \
+ _(IF_DOWN, "interface down") \
+ _(NO_SLAVE, "no slave") \
+ _(NO_BOND, "no bond interface")\
+ _(PASS_THRU, "pass through")
+
+typedef enum
+{
+#define _(f,s) BOND_INPUT_ERROR_##f,
+ foreach_bond_input_error
+#undef _
+ BOND_INPUT_N_ERROR,
+} bond_input_error_t;
+
+static char *bond_input_error_strings[] = {
+#define _(n,s) s,
+ foreach_bond_input_error
+#undef _
+};
+
+static u8 *
+format_bond_input_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ bond_packet_trace_t *t = va_arg (*args, bond_packet_trace_t *);
+ vnet_hw_interface_t *hw, *hw1;
+ vnet_main_t *vnm = vnet_get_main ();
+
+ hw = vnet_get_sup_hw_interface (vnm, t->sw_if_index);
+ hw1 = vnet_get_sup_hw_interface (vnm, t->bond_sw_if_index);
+ s = format (s, "src %U, dst %U, %s -> %s",
+ format_ethernet_address, t->ethernet.src_address,
+ format_ethernet_address, t->ethernet.dst_address,
+ hw->name, hw1->name);
+
+ return s;
+}
+
+static_always_inline u8
+packet_is_cdp (ethernet_header_t * eth)
+{
+ llc_header_t *llc;
+ snap_header_t *snap;
+
+ llc = (llc_header_t *) (eth + 1);
+ snap = (snap_header_t *) (llc + 1);
+
+ return ((eth->type == htons (ETHERNET_TYPE_CDP)) ||
+ ((llc->src_sap == 0xAA) && (llc->control == 0x03) &&
+ (snap->protocol == htons (0x2000)) &&
+ (snap->oui[0] == 0) && (snap->oui[1] == 0) &&
+ (snap->oui[2] == 0x0C)));
+}
+
+static inline void
+bond_sw_if_index_rewrite (vlib_main_t * vm, vlib_node_runtime_t * node,
+ slave_if_t * sif, ethernet_header_t * eth,
+ vlib_buffer_t * b0)
+{
+ bond_if_t *bif;
+ u16 thread_index = vlib_get_thread_index ();
+ u16 *ethertype_p, ethertype;
+ ethernet_vlan_header_t *vlan;
+
+ if (PREDICT_TRUE (sif != 0))
+ {
+ bif = bond_get_master_by_sw_if_index (sif->group);
+ if (PREDICT_TRUE (bif != 0))
+ {
+ if (PREDICT_TRUE (vec_len (bif->slaves) >= 1))
+ {
+ if (PREDICT_TRUE (bif->admin_up == 1))
+ {
+ if (!ethernet_frame_is_tagged (ntohs (eth->type)))
+ {
+ // Let some layer2 packets pass through.
+ if (PREDICT_TRUE ((eth->type !=
+ htons (ETHERNET_TYPE_SLOW_PROTOCOLS))
+ && !packet_is_cdp (eth)
+ && (eth->type !=
+ htons
+ (ETHERNET_TYPE_802_1_LLDP))))
+ {
+ // Change the physical interface to
+ // bond interface
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] =
+ bif->sw_if_index;
+
+ /* increase rx counters */
+ vlib_increment_simple_counter
+ (vnet_main.interface_main.sw_if_counters +
+ VNET_INTERFACE_COUNTER_RX, thread_index,
+ bif->sw_if_index, 1);
+ }
+ else
+ {
+ vlib_error_count (vm, node->node_index,
+ BOND_INPUT_ERROR_PASS_THRU, 1);
+ }
+ }
+ else
+ {
+ vlan = (void *) (eth + 1);
+ ethertype_p = &vlan->type;
+ if (*ethertype_p == ntohs (ETHERNET_TYPE_VLAN))
+ {
+ vlan++;
+ ethertype_p = &vlan->type;
+ }
+ ethertype = *ethertype_p;
+ if (PREDICT_TRUE ((ethertype !=
+ htons (ETHERNET_TYPE_SLOW_PROTOCOLS))
+ && (ethertype !=
+ htons (ETHERNET_TYPE_CDP))
+ && (ethertype !=
+ htons
+ (ETHERNET_TYPE_802_1_LLDP))))
+ {
+ // Change the physical interface to
+ // bond interface
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] =
+ bif->sw_if_index;
+
+ /* increase rx counters */
+ vlib_increment_simple_counter
+ (vnet_main.interface_main.sw_if_counters +
+ VNET_INTERFACE_COUNTER_RX, thread_index,
+ bif->sw_if_index, 1);
+ }
+ else
+ {
+ vlib_error_count (vm, node->node_index,
+ BOND_INPUT_ERROR_PASS_THRU, 1);
+ }
+ }
+ }
+ else
+ {
+ vlib_error_count (vm, node->node_index,
+ BOND_INPUT_ERROR_IF_DOWN, 1);
+ }
+ }
+ else
+ {
+ vlib_error_count (vm, node->node_index,
+ BOND_INPUT_ERROR_NO_SLAVE, 1);
+ }
+ }
+ else
+ {
+ vlib_error_count (vm, node->node_index,
+ BOND_INPUT_ERROR_NO_BOND, 1);
+ }
+ }
+ else
+ {
+ vlib_error_count (vm, node->node_index, BOND_INPUT_ERROR_NO_SLAVE, 1);
+ }
+
+}
+
+static uword
+bond_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 bi0, bi1, bi2, bi3;
+ vlib_buffer_t *b0, *b1, *b2, *b3;
+ u32 next_index;
+ u32 *from, *to_next, n_left_from, n_left_to_next;
+ ethernet_header_t *eth, *eth1, *eth2, *eth3;
+ u32 next0, next1, next2, next3;
+ bond_packet_trace_t *t0;
+ uword n_trace = vlib_get_trace_count (vm, node);
+ u32 sw_if_index, sw_if_index1, sw_if_index2, sw_if_index3;
+ slave_if_t *sif, *sif1, *sif2, *sif3;
+ u16 thread_index = vlib_get_thread_index ();
+
+ /* Vector of buffer / pkt indices we're supposed to process */
+ from = vlib_frame_vector_args (frame);
+
+ /* Number of buffers / pkts */
+ n_left_from = frame->n_vectors;
+
+ /* Speculatively send the first buffer to the last disposition we used */
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ /* set up to enqueue to our disposition with index = next_index */
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from >= 12 && n_left_to_next >= 4)
+ {
+ // Prefetch next iteration
+ {
+ vlib_buffer_t *b4, *b5, *b6, *b7;
+
+ b4 = vlib_get_buffer (vm, from[4]);
+ b5 = vlib_get_buffer (vm, from[5]);
+ b6 = vlib_get_buffer (vm, from[6]);
+ b7 = vlib_get_buffer (vm, from[7]);
+
+ vlib_prefetch_buffer_header (b4, STORE);
+ vlib_prefetch_buffer_header (b5, STORE);
+ vlib_prefetch_buffer_header (b6, STORE);
+ vlib_prefetch_buffer_header (b7, STORE);
+
+ CLIB_PREFETCH (b4->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ CLIB_PREFETCH (b5->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ CLIB_PREFETCH (b6->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ CLIB_PREFETCH (b7->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ }
+
+ next0 = 0;
+ next1 = 0;
+ next2 = 0;
+ next3 = 0;
+
+ bi0 = from[0];
+ bi1 = from[1];
+ bi2 = from[2];
+ bi3 = from[3];
+
+ to_next[0] = bi0;
+ to_next[1] = bi1;
+ to_next[2] = bi2;
+ to_next[3] = bi3;
+
+ from += 4;
+ to_next += 4;
+ n_left_from -= 4;
+ n_left_to_next -= 4;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+ b2 = vlib_get_buffer (vm, bi2);
+ b3 = vlib_get_buffer (vm, bi3);
+
+ vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_RX], &next0,
+ b0);
+ vnet_feature_next (vnet_buffer (b1)->sw_if_index[VLIB_RX], &next1,
+ b1);
+ vnet_feature_next (vnet_buffer (b2)->sw_if_index[VLIB_RX], &next2,
+ b2);
+ vnet_feature_next (vnet_buffer (b3)->sw_if_index[VLIB_RX], &next3,
+ b3);
+
+ eth = (ethernet_header_t *) vlib_buffer_get_current (b0);
+ eth1 = (ethernet_header_t *) vlib_buffer_get_current (b1);
+ eth2 = (ethernet_header_t *) vlib_buffer_get_current (b2);
+ eth3 = (ethernet_header_t *) vlib_buffer_get_current (b3);
+
+ sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+ sw_if_index2 = vnet_buffer (b2)->sw_if_index[VLIB_RX];
+ sw_if_index3 = vnet_buffer (b3)->sw_if_index[VLIB_RX];
+
+ // sw_if_index points to the physical interface
+ sif = bond_get_slave_by_sw_if_index (sw_if_index);
+ sif1 = bond_get_slave_by_sw_if_index (sw_if_index1);
+ sif2 = bond_get_slave_by_sw_if_index (sw_if_index2);
+ sif3 = bond_get_slave_by_sw_if_index (sw_if_index3);
+
+ bond_sw_if_index_rewrite (vm, node, sif, eth, b0);
+ bond_sw_if_index_rewrite (vm, node, sif1, eth1, b1);
+ bond_sw_if_index_rewrite (vm, node, sif2, eth2, b2);
+ bond_sw_if_index_rewrite (vm, node, sif3, eth3, b3);
+
+ if (PREDICT_FALSE (n_trace > 0))
+ {
+ vlib_trace_buffer (vm, node, next0, b0, 0 /* follow_chain */ );
+ vlib_set_trace_count (vm, node, --n_trace);
+ t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
+ t0->ethernet = *eth;
+ t0->sw_if_index = sw_if_index;
+ t0->bond_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+
+ if (PREDICT_TRUE (n_trace > 0))
+ {
+ vlib_trace_buffer (vm, node, next1, b1,
+ 0 /* follow_chain */ );
+ vlib_set_trace_count (vm, node, --n_trace);
+ t0 = vlib_add_trace (vm, node, b1, sizeof (*t0));
+ t0->ethernet = *eth1;
+ t0->sw_if_index = sw_if_index1;
+ t0->bond_sw_if_index =
+ vnet_buffer (b1)->sw_if_index[VLIB_RX];
+
+ if (PREDICT_TRUE (n_trace > 0))
+ {
+ vlib_trace_buffer (vm, node, next1, b2,
+ 0 /* follow_chain */ );
+ vlib_set_trace_count (vm, node, --n_trace);
+ t0 = vlib_add_trace (vm, node, b2, sizeof (*t0));
+ t0->ethernet = *eth2;
+ t0->sw_if_index = sw_if_index2;
+ t0->bond_sw_if_index =
+ vnet_buffer (b2)->sw_if_index[VLIB_RX];
+
+ if (PREDICT_TRUE (n_trace > 0))
+ {
+ vlib_trace_buffer (vm, node, next1, b2,
+ 0 /* follow_chain */ );
+ vlib_set_trace_count (vm, node, --n_trace);
+ t0 = vlib_add_trace (vm, node, b3, sizeof (*t0));
+ t0->ethernet = *eth3;
+ t0->sw_if_index = sw_if_index3;
+ t0->bond_sw_if_index =
+ vnet_buffer (b3)->sw_if_index[VLIB_RX];
+ }
+ }
+ }
+ }
+
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b1);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b2);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b3);
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x4 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, bi2, bi3, next0, next1,
+ next2, next3);
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ // Prefetch next iteration
+ if (n_left_from > 1)
+ {
+ vlib_buffer_t *p2;
+
+ p2 = vlib_get_buffer (vm, from[1]);
+ vlib_prefetch_buffer_header (p2, STORE);
+ CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ }
+
+ next0 = 0;
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_RX], &next0,
+ b0);
+
+ eth = (ethernet_header_t *) vlib_buffer_get_current (b0);
+
+ sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ // sw_if_index points to the physical interface
+ sif = bond_get_slave_by_sw_if_index (sw_if_index);
+ bond_sw_if_index_rewrite (vm, node, sif, eth, b0);
+
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, next0);
+ }
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ vlib_node_increment_counter (vm, bond_input_node.index,
+ BOND_INPUT_ERROR_NONE, frame->n_vectors);
+
+ vnet_device_increment_rx_packets (thread_index, frame->n_vectors);
+
+ return frame->n_vectors;
+}
+
+static clib_error_t *
+bond_input_init (vlib_main_t * vm)
+{
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (bond_input_node) = {
+ .function = bond_input_fn,
+ .name = "bond-input",
+ .vector_size = sizeof (u32),
+ .format_buffer = format_ethernet_header_with_length,
+ .format_trace = format_bond_input_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = BOND_INPUT_N_ERROR,
+ .error_strings = bond_input_error_strings,
+ .n_next_nodes = 0,
+ .next_nodes =
+ {
+ [0] = "error-drop"
+ }
+};
+
+VLIB_INIT_FUNCTION (bond_input_init);
+
+VNET_FEATURE_INIT (bond_input, static) =
+{
+ .arc_name = "device-input",
+ .node_name = "bond-input",
+ .runs_before = VNET_FEATURES ("ethernet-input"),
+};
+VLIB_NODE_FUNCTION_MULTIARCH (bond_input_node, bond_input_fn)
+/* *INDENT-ON* */
+
+static clib_error_t *
+bond_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
+{
+ bond_main_t *bm = &bond_main;
+ slave_if_t *sif;
+ vlib_main_t *vm = bm->vlib_main;
+
+ sif = bond_get_slave_by_sw_if_index (sw_if_index);
+ if (sif)
+ {
+ sif->port_enabled = flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
+ if (sif->port_enabled == 0)
+ {
+ if (sif->lacp_enabled == 0)
+ {
+ bond_disable_collecting_distributing (vm, sif);
+ }
+ }
+ else
+ {
+ if (sif->lacp_enabled == 0)
+ {
+ bond_enable_collecting_distributing (vm, sif);
+ }
+ }
+ }
+
+ return 0;
+}
+
+VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (bond_sw_interface_up_down);
+
+static clib_error_t *
+bond_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
+{
+ bond_main_t *bm = &bond_main;
+ slave_if_t *sif;
+ vnet_sw_interface_t *sw;
+ vlib_main_t *vm = bm->vlib_main;
+ vnet_interface_main_t *im = &vnm->interface_main;
+
+ sw = pool_elt_at_index (im->sw_interfaces, hw_if_index);
+ sif = bond_get_slave_by_sw_if_index (sw->sw_if_index);
+ if (sif)
+ {
+ if (!(flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
+ {
+ if (sif->lacp_enabled == 0)
+ {
+ bond_disable_collecting_distributing (vm, sif);
+ }
+ }
+ else
+ {
+ if (sif->lacp_enabled == 0)
+ {
+ bond_enable_collecting_distributing (vm, sif);
+ }
+ }
+ }
+
+ return 0;
+}
+
+VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bond_hw_interface_up_down);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/bonding/node.h b/src/vnet/bonding/node.h
new file mode 100644
index 00000000000..74f3b1a356a
--- /dev/null
+++ b/src/vnet/bonding/node.h
@@ -0,0 +1,451 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+#ifndef __included_vnet_bonding_node_h__
+#define __included_vnet_bonding_node_h__
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vppinfra/format.h>
+#include <vppinfra/hash.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/interface.h>
+
+#define LACP_FAST_PERIODIC_TIMER 1.0
+#define LACP_SHORT_TIMOUT_TIME (LACP_FAST_PERIODIC_TIMER * 3)
+#define LACP_SLOW_PERIODIC_TIMER 30.0
+#define LACP_LONG_TIMOUT_TIME (LACP_SLOW_PERIODIC_TIMER * 3)
+
+#ifndef MIN
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#endif
+
+#define foreach_bond_mode \
+ _ (1, ROUND_ROBIN, "round-robin") \
+ _ (2, ACTIVE_BACKUP, "active-backup") \
+ _ (3, XOR, "xor") \
+ _ (4, BROADCAST, "broadcast") \
+ _ (5, LACP, "lacp")
+
+typedef enum
+{
+#define _(v, f, s) BOND_MODE_##f = v,
+ foreach_bond_mode
+#undef _
+} bond_mode_t;
+
+/* configurable load-balances */
+#define foreach_bond_lb \
+ _ (2, L23, "l23", l23) \
+ _ (1, l34 , "l34", l34) \
+ _ (0, L2, "l2", l2)
+
+/* load-balance functions implemented in bond-output */
+#define foreach_bond_lb_algo \
+ _ (0, L2, "l2", l2) \
+ _ (1, l34 , "l34", l34) \
+ _ (2, L23, "l23", l23) \
+ _ (3, RR, "round-robin", round_robin) \
+ _ (4, BC, "broadcast", broadcast) \
+ _ (5, AB, "active-backup", active_backup)
+
+typedef enum
+{
+#define _(v, f, s, p) BOND_LB_##f = v,
+ foreach_bond_lb_algo
+#undef _
+} bond_load_balance_t;
+
+typedef struct
+{
+ u8 hw_addr_set;
+ u8 hw_addr[6];
+ u8 mode;
+ u8 lb;
+ /* return */
+ u32 sw_if_index;
+ int rv;
+ clib_error_t *error;
+} bond_create_if_args_t;
+
+typedef struct
+{
+ /* slave's sw_if_index */
+ u32 slave;
+ /* bond's sw_if_index */
+ u32 group;
+ u8 is_passive;
+ u8 is_long_timeout;
+ /* return */
+ int rv;
+ clib_error_t *error;
+} bond_enslave_args_t;
+
+typedef struct
+{
+ u32 slave;
+ /* return */
+ int rv;
+ clib_error_t *error;
+} bond_detach_slave_args_t;
+
+/** BOND interface details struct */
+typedef struct
+{
+ u32 sw_if_index;
+ u8 interface_name[64];
+ u8 mode;
+ u8 lb;
+ u32 active_slaves;
+ u32 slaves;
+} bond_interface_details_t;
+
+/** slave interface details struct */
+typedef struct
+{
+ u32 sw_if_index;
+ u8 interface_name[64];
+ u8 is_passive;
+ u8 is_long_timeout;
+ u32 active_slaves;
+} slave_interface_details_t;
+
+typedef CLIB_PACKED (struct
+ {
+ u16 system_priority;
+ u8 system[6];
+ u16 key; u16 port_priority; u16 port_number;
+ u8 state;
+ }) lacp_port_info_t;
+
+typedef struct
+{
+ u8 admin_up;
+ u8 mode;
+ u8 lb;
+
+ /* the last slave index for the rr lb */
+ u32 lb_rr_last_index;
+
+ u32 dev_instance;
+ u32 hw_if_index;
+ u32 sw_if_index;
+
+ /* Configured slaves */
+ u32 *slaves;
+
+ /* Slaves that are in DISTRIBUTING state */
+ u32 *active_slaves;
+
+ /* rapidly find an active slave */
+ uword *active_slave_by_sw_if_index;
+
+ lacp_port_info_t partner;
+ lacp_port_info_t actor;
+ u8 individual_aggregator;
+
+ u32 group;
+ uword *port_number_bitmap;
+ u8 use_custom_mac;
+ u8 hw_address[6];
+} bond_if_t;
+
+typedef struct
+{
+ u8 persistent_hw_address[6];
+
+ /* neighbor's vlib software interface index */
+ u32 sw_if_index;
+
+ /* Neighbor time-to-live (usually 3s) */
+ f32 ttl_in_seconds;
+
+ /* 1 = interface is configured with long timeout (60s) */
+ u8 is_long_timeout;
+
+ /* 1 = debug is on; 0 = debug is off */
+ u8 debug;
+
+ /* tx packet template id for this neighbor */
+ u8 packet_template_index;
+
+ /* Info we actually keep about each neighbor */
+
+ /* Jenkins hash optimization: avoid tlv scan, send short keepalive msg */
+ u8 last_packet_signature_valid;
+ uword last_packet_signature;
+
+ /* last received lacp packet, for the J-hash optimization */
+ u8 *last_rx_pkt;
+
+ /* last marker packet */
+ u8 *last_marker_pkt;
+
+ /* neighbor vlib hw_if_index */
+ u32 hw_if_index;
+
+ /* actor does not initiate the protocol exchange */
+ u8 is_passive;
+
+ /* Partner port information */
+ lacp_port_info_t partner;
+ lacp_port_info_t partner_admin;;
+
+ /* Partner port information */
+ lacp_port_info_t actor;
+ lacp_port_info_t actor_admin;
+
+ /* Need To Transmit flag */
+ u8 ntt;
+
+ /* Link has been established and Aggregate Port is operable */
+ u8 port_enabled;
+
+ /* Initialization or reinitialization of the lacp protocol entity */
+ u8 begin;
+
+ /* Aggregation Port is operating the lacp */
+ u8 lacp_enabled;
+
+ /* MUX to indicate to the Selection Logic wait_while_timer expired */
+ u8 ready_n;
+
+ /* Selection Logic indicates al Aggregation Ports attached */
+ u8 ready;
+
+ /* Selection Logic selected an Aggregator */
+ int selected;
+
+ /* RX machine indicates an Aggregation Port in PORT_DISABLED state */
+ u8 port_moved;
+
+ /* timer used to detect whether received protocol information has expired */
+ f64 current_while_timer;
+
+ /* timer used to detect actor churn states */
+ f64 actor_churn_timer;
+
+ /* time last lacpdu was sent */
+ f64 last_lacpdu_time;
+
+ /* timer used to generate periodic transmission */
+ f64 periodic_timer;
+
+ /* timer used to detect partner churn states */
+ f64 partner_churn_timer;
+
+ /* provides hysteresis before performing an aggregation change */
+ f64 wait_while_timer;
+
+ /* Implemention variables, not in the spec */
+ int rx_state;
+ int tx_state;
+ int mux_state;
+ int ptx_state;
+
+ /* actor admin key */
+ u32 group;
+
+ u32 marker_tx_id;
+
+ u32 bif_dev_instance;
+
+ u8 loopback_port;
+
+ /* bond mode */
+ u8 mode;
+
+ clib_spinlock_t lockp;
+} slave_if_t;
+
+typedef void (*lacp_enable_disable_func) (vlib_main_t * vm, bond_if_t * bif,
+ slave_if_t * sif, u8 enable);
+
+typedef struct
+{
+ /* pool of bonding interfaces */
+ bond_if_t *interfaces;
+
+ /* pool of lacp neighbors */
+ slave_if_t *neighbors;
+
+ /* rapidly find a neighbor by vlib software interface index */
+ uword *neighbor_by_sw_if_index;
+
+ /* rapidly find a bond by vlib software interface index */
+ uword *bond_by_sw_if_index;
+
+ /* convenience variables */
+ vlib_main_t *vlib_main;
+ vnet_main_t *vnet_main;
+
+ /* lacp plugin is loaded */
+ u8 lacp_plugin_loaded;
+
+ lacp_enable_disable_func lacp_enable_disable;
+} bond_main_t;
+
+/* bond packet trace capture */
+typedef struct
+{
+ ethernet_header_t ethernet;
+ u32 sw_if_index;
+ u32 bond_sw_if_index;
+} bond_packet_trace_t;
+
+typedef u32 (*load_balance_func) (vlib_main_t * vm,
+ vlib_node_runtime_t * node, bond_if_t * bif,
+ vlib_buffer_t * b0);
+
+typedef struct
+{
+ load_balance_func load_balance;
+} bond_load_balance_func_t;
+
+extern vlib_node_registration_t bond_input_node;
+extern vnet_device_class_t bond_dev_class;
+extern bond_main_t bond_main;
+
+void bond_disable_collecting_distributing (vlib_main_t * vm,
+ slave_if_t * sif);
+void bond_enable_collecting_distributing (vlib_main_t * vm, slave_if_t * sif);
+u8 *format_bond_interface_name (u8 * s, va_list * args);
+
+void bond_create_if (vlib_main_t * vm, bond_create_if_args_t * args);
+int bond_delete_if (vlib_main_t * vm, u32 sw_if_index);
+void bond_enslave (vlib_main_t * vm, bond_enslave_args_t * args);
+void bond_detach_slave (vlib_main_t * vm, bond_detach_slave_args_t * args);
+int bond_dump_ifs (bond_interface_details_t ** out_bondids);
+int bond_dump_slave_ifs (slave_interface_details_t ** out_slaveids,
+ u32 bond_sw_if_index);
+
+static inline uword
+unformat_bond_mode (unformat_input_t * input, va_list * args)
+{
+ u8 *r = va_arg (*args, u8 *);
+
+ if (0);
+#define _(v, f, s) else if (unformat (input, s)) *r = BOND_MODE_##f;
+ foreach_bond_mode
+#undef _
+ else
+ return 0;
+
+ return 1;
+}
+
+static inline u8 *
+format_bond_mode (u8 * s, va_list * args)
+{
+ u32 i = va_arg (*args, u32);
+ u8 *t = 0;
+
+ switch (i)
+ {
+#define _(v, f, s) case BOND_MODE_##f: t = (u8 *) s; break;
+ foreach_bond_mode
+#undef _
+ default:
+ return format (s, "unknown");
+ }
+ return format (s, "%s", t);
+}
+
+static inline uword
+unformat_bond_load_balance (unformat_input_t * input, va_list * args)
+{
+ u8 *r = va_arg (*args, u8 *);
+
+ if (0);
+#define _(v, f, s, p) else if (unformat (input, s)) *r = BOND_LB_##f;
+ foreach_bond_lb
+#undef _
+ else
+ return 0;
+
+ return 1;
+}
+
+static inline u8 *
+format_bond_load_balance (u8 * s, va_list * args)
+{
+ u32 i = va_arg (*args, u32);
+ u8 *t = 0;
+
+ switch (i)
+ {
+#define _(v, f, s, p) case BOND_LB_##f: t = (u8 *) s; break;
+ foreach_bond_lb_algo
+#undef _
+ default:
+ return format (s, "unknown");
+ }
+ return format (s, "%s", t);
+}
+
+static inline void
+bond_register_callback (lacp_enable_disable_func func)
+{
+ bond_main_t *bm = &bond_main;
+
+ bm->lacp_plugin_loaded = 1;
+ bm->lacp_enable_disable = func;
+}
+
+static inline bond_if_t *
+bond_get_master_by_sw_if_index (u32 sw_if_index)
+{
+ bond_main_t *bm = &bond_main;
+ uword *p;
+
+ p = hash_get (bm->bond_by_sw_if_index, sw_if_index);
+ if (!p)
+ {
+ return 0;
+ }
+ return pool_elt_at_index (bm->interfaces, p[0]);
+}
+
+static inline bond_if_t *
+bond_get_master_by_dev_instance (u32 dev_instance)
+{
+ bond_main_t *bm = &bond_main;
+
+ return pool_elt_at_index (bm->interfaces, dev_instance);
+}
+
+static inline slave_if_t *
+bond_get_slave_by_sw_if_index (u32 sw_if_index)
+{
+ bond_main_t *bm = &bond_main;
+ slave_if_t *sif = 0;
+ uword *p;
+
+ p = hash_get (bm->neighbor_by_sw_if_index, sw_if_index);
+ if (p)
+ {
+ sif = pool_elt_at_index (bm->neighbors, p[0]);
+ }
+ return sif;
+}
+
+#endif /* __included_vnet_bonding_node_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/vnet_all_api_h.h b/src/vnet/vnet_all_api_h.h
index fea92e7fd27..5c7c4869c91 100644
--- a/src/vnet/vnet_all_api_h.h
+++ b/src/vnet/vnet_all_api_h.h
@@ -29,6 +29,7 @@
#include <vlibmemory/vl_memory_api_h.h>
#endif /* included_from_layer_3 */
+#include <vnet/bonding/bond.api.h>
#include <vnet/devices/af_packet/af_packet.api.h>
#include <vnet/devices/netmap/netmap.api.h>
#include <vnet/devices/virtio/vhost_user.api.h>
diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c
index 28f16d2fe67..ded6e7de337 100644
--- a/src/vpp/api/custom_dump.c
+++ b/src/vpp/api/custom_dump.c
@@ -47,6 +47,8 @@
#include <vpp/api/vpe_msg_enum.h>
+#include <vnet/bonding/node.h>
+
#define vl_typedefs /* define message structures */
#include <vpp/api/vpe_all_api_h.h>
#undef vl_typedefs
@@ -609,6 +611,84 @@ static void *vl_api_sw_interface_tap_v2_dump_t_print
FINISH;
}
+static void *vl_api_bond_create_t_print
+ (vl_api_bond_create_t * mp, void *handle)
+{
+ u8 *s;
+ u8 null_mac[6];
+
+ memset (null_mac, 0, sizeof (null_mac));
+
+ s = format (0, "SCRIPT: bond_create ");
+ if (memcmp (mp->mac_address, null_mac, 6))
+ s = format (s, "mac-address %U ",
+ format_ethernet_address, mp->mac_address);
+ if (mp->mode)
+ s = format (s, "mode %U", format_bond_mode, mp->mode);
+ if (mp->lb)
+ s = format (s, "lb %U", format_bond_load_balance, mp->lb);
+ FINISH;
+}
+
+static void *vl_api_bond_delete_t_print
+ (vl_api_bond_delete_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: bond_delete ");
+ s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index));
+
+ FINISH;
+}
+
+static void *vl_api_bond_enslave_t_print
+ (vl_api_bond_enslave_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: bond_enslave ");
+ s = format (s, "bond_sw_if_index %u ", mp->bond_sw_if_index);
+ s = format (s, "sw_if_index %u ", mp->sw_if_index);
+ if (mp->is_passive)
+ s = format (s, "passive ");
+ if (mp->is_long_timeout)
+ s = format (s, "long-timeout ");
+
+ FINISH;
+}
+
+static void *vl_api_bond_detach_slave_t_print
+ (vl_api_bond_detach_slave_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: bond_detach_slave ");
+ s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index));
+
+ FINISH;
+}
+
+static void *vl_api_sw_interface_bond_dump_t_print
+ (vl_api_sw_interface_bond_dump_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: sw_interface_bond_dump ");
+
+ FINISH;
+}
+
+static void *vl_api_sw_interface_slave_dump_t_print
+ (vl_api_sw_interface_slave_dump_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: sw_interface_slave_dump ");
+ s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index));
+
+ FINISH;
+}
+
static void *vl_api_ip_add_del_route_t_print
(vl_api_ip_add_del_route_t * mp, void *handle)
{
@@ -3357,6 +3437,10 @@ _(TAP_CONNECT, tap_connect) \
_(TAP_MODIFY, tap_modify) \
_(TAP_DELETE, tap_delete) \
_(SW_INTERFACE_TAP_DUMP, sw_interface_tap_dump) \
+_(BOND_CREATE, bond_create) \
+_(BOND_DELETE, bond_delete) \
+_(BOND_ENSLAVE, bond_enslave) \
+_(BOND_DETACH_SLAVE, bond_detach_slave) \
_(TAP_CREATE_V2, tap_create_v2) \
_(TAP_DELETE_V2, tap_delete_v2) \
_(SW_INTERFACE_TAP_V2_DUMP, sw_interface_tap_v2_dump) \
diff --git a/test/test_bond.py b/test/test_bond.py
new file mode 100644
index 00000000000..b54a1f1deb5
--- /dev/null
+++ b/test/test_bond.py
@@ -0,0 +1,282 @@
+#!/usr/bin/env python
+
+import socket
+import unittest
+
+from framework import VppTestCase, VppTestRunner
+from scapy.packet import Raw
+from scapy.layers.l2 import Ether
+from scapy.layers.inet import IP, UDP
+from util import mactobinary
+from vpp_bond_interface import VppBondInterface
+
+
+class TestBondInterface(VppTestCase):
+ """Bond Test Case
+
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestBondInterface, cls).setUpClass()
+ # Test variables
+ cls.pkts_per_burst = 257 # Number of packets per burst
+ # create 3 pg interfaces
+ cls.create_pg_interfaces(range(4))
+
+ # packet sizes
+ cls.pg_if_packet_sizes = [64, 512, 1518] # , 9018]
+
+ # setup all interfaces
+ for i in cls.pg_interfaces:
+ i.admin_up()
+
+ def setUp(self):
+ super(TestBondInterface, self).setUp()
+
+ def tearDown(self):
+ super(TestBondInterface, self).tearDown()
+ if not self.vpp_dead:
+ self.logger.info(self.vapi.ppcli("show interface"))
+
+ def test_bond_traffic(self):
+ """ Bond traffic test """
+
+ # topology
+ #
+ # RX-> TX->
+ #
+ # pg2 ------+ +------pg0 (slave)
+ # | |
+ # BondEthernet0 (10.10.10.1)
+ # | |
+ # pg3 ------+ +------pg1 (slave)
+ #
+
+ # create interface (BondEthernet0)
+ # self.logger.info("create bond")
+ bond0_mac = "02:fe:38:30:59:3c"
+ mac = mactobinary(bond0_mac)
+ bond0 = VppBondInterface(self,
+ mode=3,
+ lb=1,
+ use_custom_mac=1,
+ mac_address=mac)
+ bond0.add_vpp_config()
+ bond0.admin_up()
+ bond0_addr = socket.inet_pton(socket.AF_INET, "10.10.10.1")
+ self.vapi.sw_interface_add_del_address(bond0.sw_if_index,
+ bond0_addr,
+ 24)
+
+ self.pg2.config_ip4()
+ self.pg2.resolve_arp()
+ self.pg3.config_ip4()
+ self.pg3.resolve_arp()
+
+ self.logger.info(self.vapi.cli("show interface"))
+ self.logger.info(self.vapi.cli("show interface address"))
+ self.logger.info(self.vapi.cli("show ip arp"))
+
+ # enslave pg0 and pg1 to BondEthernet0
+ self.logger.info("bond enslave interface pg0 to BondEthernet0")
+ bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index,
+ is_passive=0,
+ is_long_timeout=0)
+ self.logger.info("bond enslave interface pg1 to BondEthernet0")
+ bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index,
+ is_passive=0,
+ is_long_timeout=0)
+
+ # verify both slaves in BondEthernet0
+ if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
+ self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
+ self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
+
+ # generate a packet from pg2 -> BondEthernet0 -> pg1
+ # BondEthernet0 TX hashes this packet to pg1
+ p2 = (Ether(src=bond0_mac, dst=self.pg2.local_mac) /
+ IP(src=self.pg2.local_ip4, dst="10.10.10.12") /
+ UDP(sport=1235, dport=1235) /
+ Raw('\xa5' * 100))
+ self.pg2.add_stream(p2)
+
+ # generate a packet from pg3 -> BondEthernet0 -> pg0
+ # BondEthernet0 TX hashes this packet to pg0
+ # notice the ip address and ports are different than p2 packet
+ p3 = (Ether(src=bond0_mac, dst=self.pg3.local_mac) /
+ IP(src=self.pg3.local_ip4, dst="10.10.10.11") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ self.pg3.add_stream(p3)
+
+ self.pg_enable_capture(self.pg_interfaces)
+
+ # set up the static arp entries pointing to the BondEthernet0 interface
+ # so that it does not try to resolve the ip address
+ self.logger.info(self.vapi.cli(
+ "set ip arp static BondEthernet0 10.10.10.12 abcd.abcd.0002"))
+ self.logger.info(self.vapi.cli(
+ "set ip arp static BondEthernet0 10.10.10.11 abcd.abcd.0004"))
+
+ # clear the interface counters
+ self.logger.info(self.vapi.cli("clear interfaces"))
+
+ self.pg_start()
+
+ self.logger.info("check the interface counters")
+
+ # verify counters
+
+ # BondEthernet0 tx bytes = 284
+ intfs = self.vapi.cli("show interface BondEthernet0").split("\n")
+ found = 0
+ for intf in intfs:
+ if "tx bytes" in intf and "284" in intf:
+ found = 1
+ self.assertEqual(found, 1)
+
+ # pg0 tx bytes = 142
+ intfs = self.vapi.cli("show interface pg0").split("\n")
+ found = 0
+ for intf in intfs:
+ if "tx bytes" in intf and "142" in intf:
+ found = 1
+ self.assertEqual(found, 1)
+
+ # pg0 tx bytes = 142
+ intfs = self.vapi.cli("show interface pg1").split("\n")
+ found = 0
+ for intf in intfs:
+ if "tx bytes" in intf and "142" in intf:
+ found = 1
+ self.assertEqual(found, 1)
+
+ # pg2 rx bytes = 142
+ intfs = self.vapi.cli("show interface pg2").split("\n")
+ found = 0
+ for intf in intfs:
+ if "rx bytes" in intf and "142" in intf:
+ found = 1
+ self.assertEqual(found, 1)
+
+ # pg3 rx bytes = 142
+ intfs = self.vapi.cli("show interface pg3").split("\n")
+ found = 0
+ for intf in intfs:
+ if "rx bytes" in intf and "142" in intf:
+ found = 1
+ self.assertEqual(found, 1)
+
+ bond0.remove_vpp_config()
+
+ def test_bond_enslave(self):
+ """ Bond enslave/detach slave test """
+
+ # create interface (BondEthernet0)
+ self.logger.info("create bond")
+ bond0 = VppBondInterface(self, mode=3)
+ bond0.add_vpp_config()
+ bond0.admin_up()
+
+ # verify pg0 and pg1 not in BondEthernet0
+ if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
+ self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
+ self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
+
+ # enslave pg0 and pg1 to BondEthernet0
+ self.logger.info("bond enslave interface pg0 to BondEthernet0")
+ bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index,
+ is_passive=0,
+ is_long_timeout=0)
+
+ self.logger.info("bond enslave interface pg1 to BondEthernet0")
+ bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index,
+ is_passive=0,
+ is_long_timeout=0)
+
+ # verify both slaves in BondEthernet0
+ if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
+ self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
+ self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
+
+ # detach interface pg0
+ self.logger.info("detach interface pg0")
+ bond0.detach_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index)
+
+ # verify pg0 is not in BondEthernet0, but pg1 is
+ if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
+ self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
+ self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
+
+ # detach interface pg1
+ self.logger.info("detach interface pg1")
+ bond0.detach_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index)
+
+ # verify pg0 and pg1 not in BondEthernet0
+ if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
+ self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
+ self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
+
+ bond0.remove_vpp_config()
+
+ def test_bond(self):
+ """ Bond add/delete interface test """
+ self.logger.info("Bond add interfaces")
+
+ # create interface 1 (BondEthernet0)
+ bond0 = VppBondInterface(self, mode=5)
+ bond0.add_vpp_config()
+ bond0.admin_up()
+
+ # create interface 2 (BondEthernet1)
+ bond1 = VppBondInterface(self, mode=3)
+ bond1.add_vpp_config()
+ bond1.admin_up()
+
+ # verify both interfaces in the show
+ ifs = self.vapi.cli("show interface")
+ self.assertNotEqual(ifs.find('BondEthernet0'), -1)
+ self.assertNotEqual(ifs.find('BondEthernet1'), -1)
+
+ # verify they are in the dump also
+ if_dump = self.vapi.sw_interface_bond_dump()
+ self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
+ self.assertTrue(bond1.is_interface_config_in_dump(if_dump))
+
+ # delete BondEthernet1
+ self.logger.info("Deleting BondEthernet1")
+ bond1.remove_vpp_config()
+
+ self.logger.info("Verifying BondEthernet1 is deleted")
+
+ ifs = self.vapi.cli("show interface")
+ # verify BondEthernet0 still in the show
+ self.assertNotEqual(ifs.find('BondEthernet0'), -1)
+
+ # verify BondEthernet1 not in the show
+ self.assertEqual(ifs.find('BondEthernet1'), -1)
+
+ # verify BondEthernet1 is not in the dump
+ if_dump = self.vapi.sw_interface_bond_dump()
+ self.assertFalse(bond1.is_interface_config_in_dump(if_dump))
+
+ # verify BondEthernet0 is still in the dump
+ self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
+
+ # delete BondEthernet0
+ self.logger.info("Deleting BondEthernet0")
+ bond0.remove_vpp_config()
+
+ self.logger.info("Verifying BondEthernet0 is deleted")
+
+ # verify BondEthernet0 not in the show
+ ifs = self.vapi.cli("show interface")
+ self.assertEqual(ifs.find('BondEthernet0'), -1)
+
+ # verify BondEthernet0 is not in the dump
+ if_dump = self.vapi.sw_interface_bond_dump()
+ self.assertFalse(bond0.is_interface_config_in_dump(if_dump))
+
+if __name__ == '__main__':
+ unittest.main(testRunner=VppTestRunner)
diff --git a/test/vpp_bond_interface.py b/test/vpp_bond_interface.py
new file mode 100644
index 00000000000..1c33e1cecd6
--- /dev/null
+++ b/test/vpp_bond_interface.py
@@ -0,0 +1,48 @@
+from vpp_object import VppObject
+from vpp_interface import VppInterface
+
+
+class VppBondInterface(VppInterface):
+ """VPP bond interface."""
+
+ def __init__(self, test, mode, lb=0,
+ use_custom_mac=0, mac_address=''):
+
+ """ Create VPP Bond interface """
+ self._test = test
+ self.mode = mode
+ self.lb = lb
+ self.use_custom_mac = use_custom_mac
+ self.mac_address = mac_address
+ self._sw_if_index = 0
+ super(VppBondInterface, self).__init__(test)
+
+ def add_vpp_config(self):
+ r = self.test.vapi.bond_create(self.mode,
+ self.lb,
+ self.use_custom_mac,
+ self.mac_address)
+ self._sw_if_index = r.sw_if_index
+
+ def remove_vpp_config(self):
+ self.test.vapi.bond_delete(self.sw_if_index)
+
+ def enslave_vpp_bond_interface(self,
+ sw_if_index,
+ is_passive,
+ is_long_timeout):
+ self.test.vapi.bond_enslave(sw_if_index,
+ self.sw_if_index,
+ is_passive,
+ is_long_timeout)
+
+ def detach_vpp_bond_interface(self,
+ sw_if_index):
+ self.test.vapi.bond_detach_slave(sw_if_index)
+
+ def is_interface_config_in_dump(self, dump):
+ for i in dump:
+ if i.sw_if_index == self.sw_if_index:
+ return True
+ else:
+ return False
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 65cf766ffff..5517174590d 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -3341,3 +3341,78 @@ class VppPapiProvider(object):
def want_igmp_events(self, enable=1):
return self.api(self.papi.want_igmp_events, {'enable': enable,
'pid': os.getpid()})
+
+ def bond_create(
+ self,
+ mode,
+ lb,
+ use_custom_mac,
+ mac_address=''):
+ """
+ :param mode: mode
+ :param lb: load balance
+ :param use_custom_mac: use custom mac
+ :param mac_address: mac address
+ """
+ return self.api(
+ self.papi.bond_create,
+ {'mode': mode,
+ 'lb': lb,
+ 'use_custom_mac': use_custom_mac,
+ 'mac_address': mac_address
+ })
+
+ def bond_delete(
+ self,
+ sw_if_index):
+ """
+ :param sw_if_index: interface the operation is applied to
+ """
+ return self.api(self.papi.bond_delete,
+ {'sw_if_index': sw_if_index})
+
+ def bond_enslave(
+ self,
+ sw_if_index,
+ bond_sw_if_index,
+ is_passive,
+ is_long_timeout):
+ """
+ :param sw_if_index: slave sw_if_index
+ :param bond_sw_if_index: bond sw_if_index
+ :param is_passive: is passive lacp speaker
+ :param is_long_time: 90 seconds timeout instead of 3 seconds timeout
+ """
+ return self.api(
+ self.papi.bond_enslave,
+ {'sw_if_index': sw_if_index,
+ 'bond_sw_if_index': bond_sw_if_index,
+ 'is_passive': is_passive,
+ 'is_long_timeout': is_long_timeout
+ })
+
+ def bond_detach_slave(
+ self,
+ sw_if_index):
+ """
+ :param sw_if_index: slave interface the operation is applied to
+ """
+ return self.api(self.papi.bond_detach_slave,
+ {'sw_if_index': sw_if_index})
+
+ def sw_interface_slave_dump(
+ self,
+ sw_if_index):
+ """
+ :param sw_if_index: bond sw_if_index
+ """
+ return self.api(self.papi.sw_interface_slave_dump,
+ {'sw_if_index': sw_if_index})
+
+ def sw_interface_bond_dump(
+ self):
+ """
+
+ """
+ return self.api(self.papi.sw_interface_bond_dump,
+ {})