diff options
-rw-r--r-- | src/plugins/linux-cp/lcp.api | 36 | ||||
-rw-r--r-- | src/plugins/linux-cp/lcp_api.c | 34 | ||||
-rw-r--r-- | src/plugins/linux-cp/lcp_cli.c | 56 | ||||
-rw-r--r-- | src/plugins/linux-cp/lcp_interface.c | 47 | ||||
-rw-r--r-- | src/plugins/linux-cp/lcp_interface.h | 13 | ||||
-rw-r--r-- | src/plugins/linux-cp/lcp_node.c | 116 | ||||
-rw-r--r-- | test/test_linux_cp.py | 134 |
7 files changed, 410 insertions, 26 deletions
diff --git a/src/plugins/linux-cp/lcp.api b/src/plugins/linux-cp/lcp.api index e7eaa5a3669..8b0fdb5eb53 100644 --- a/src/plugins/linux-cp/lcp.api +++ b/src/plugins/linux-cp/lcp.api @@ -177,6 +177,42 @@ autoendian define lcp_itf_pair_details option in_progress; }; +/** \brief Enable linux-cp-punt-xc for a given ethertype + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ethertype - the ethertype to enable +*/ +autoreply define lcp_ethertype_enable +{ + u32 client_index; + u32 context; + u16 ethertype; +}; + +/** \brief Get the enabled ethertypes for linux-cp-punt-xc + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define lcp_ethertype_get +{ + u32 client_index; + u32 context; +}; + +/** \brief Reply to get the enabled ethertypes for linux-cp-punt-xc + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param count - number of enabled ethertypes + @param ethertypes - array of enabled ethertypes +*/ +define lcp_ethertype_get_reply +{ + u32 context; + i32 retval; + u16 count; + u16 ethertypes[count]; +}; + service { rpc lcp_itf_pair_get returns lcp_itf_pair_get_reply stream lcp_itf_pair_details; diff --git a/src/plugins/linux-cp/lcp_api.c b/src/plugins/linux-cp/lcp_api.c index 74421230e9d..0db502988d7 100644 --- a/src/plugins/linux-cp/lcp_api.c +++ b/src/plugins/linux-cp/lcp_api.c @@ -280,6 +280,40 @@ vl_api_lcp_itf_pair_replace_end_t_handler ( REPLY_MACRO (VL_API_LCP_ITF_PAIR_REPLACE_END_REPLY); } +static void +vl_api_lcp_ethertype_enable_t_handler (vl_api_lcp_ethertype_enable_t *mp) +{ + vl_api_lcp_ethertype_enable_reply_t *rmp; + int rv; + + rv = lcp_ethertype_enable (mp->ethertype); + + REPLY_MACRO (VL_API_LCP_ETHERTYPE_ENABLE_REPLY); +} + +static void +vl_api_lcp_ethertype_get_t_handler (vl_api_lcp_ethertype_get_t *mp) +{ + vl_api_lcp_ethertype_get_reply_t *rmp; + ethernet_type_t *ethertypes = vec_new (ethernet_type_t, 0); + u16 count = 0; + int rv = 0; + + rv = lcp_ethertype_get_enabled (ðertypes); + if (!rv) + count = vec_len (ethertypes); + + REPLY_MACRO3 (VL_API_LCP_ETHERTYPE_GET_REPLY, sizeof (u16) * count, ({ + rmp->count = htons (count); + for (int i = 0; i < count; i++) + { + rmp->ethertypes[i] = htons (ethertypes[i]); + } + })); + + vec_free (ethertypes); +} + /* * Set up the API message handling tables */ diff --git a/src/plugins/linux-cp/lcp_cli.c b/src/plugins/linux-cp/lcp_cli.c index 0dcf600b301..e89afd2a753 100644 --- a/src/plugins/linux-cp/lcp_cli.c +++ b/src/plugins/linux-cp/lcp_cli.c @@ -337,6 +337,62 @@ VLIB_CLI_COMMAND (lcp_itf_pair_show_cmd_node, static) = { .is_mp_safe = 1, }; +static clib_error_t * +lcp_ethertype_enable_cmd (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + ethernet_type_t ethertype; + int rv; + + if (!unformat (input, "%U", unformat_ethernet_type_host_byte_order, + ðertype)) + return clib_error_return (0, "Invalid ethertype"); + + rv = lcp_ethertype_enable (ethertype); + if (rv) + return clib_error_return (0, "Failed to enable ethertype (%d)", rv); + + return 0; +} + +VLIB_CLI_COMMAND (lcp_ethertype_enable_command, static) = { + .path = "lcp ethertype enable", + .short_help = + "lcp ethertype enable (<hex_ethertype_num>|<uc_ethertype_name>)", + .function = lcp_ethertype_enable_cmd, +}; + +static clib_error_t * +lcp_ethertype_show_cmd (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + ethernet_type_t *ethertypes = vec_new (ethernet_type_t, 0); + ethernet_type_t *etype; + int rv; + + rv = lcp_ethertype_get_enabled (ðertypes); + if (rv) + { + vec_free (ethertypes); + return clib_error_return (0, "Failed to get enabled ethertypes (%d)", + rv); + } + + vec_foreach (etype, ethertypes) + { + vlib_cli_output (vm, "0x%04x", *etype); + } + + vec_free (ethertypes); + return 0; +} + +VLIB_CLI_COMMAND (lcp_ethertype_show_command, static) = { + .path = "show lcp ethertype", + .short_help = "show lcp ethertype", + .function = lcp_ethertype_show_cmd, +}; + clib_error_t * lcp_cli_init (vlib_main_t *vm) { diff --git a/src/plugins/linux-cp/lcp_interface.c b/src/plugins/linux-cp/lcp_interface.c index 9a6b9b11be5..31864f791af 100644 --- a/src/plugins/linux-cp/lcp_interface.c +++ b/src/plugins/linux-cp/lcp_interface.c @@ -1230,6 +1230,53 @@ lcp_itf_pair_link_up_down (vnet_main_t *vnm, u32 hw_if_index, u32 flags) return 0; } +int +lcp_ethertype_enable (ethernet_type_t ethertype) +{ + ethernet_main_t *em = ðernet_main; + ethernet_type_info_t *eti; + vlib_main_t *vm = vlib_get_main (); + vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "linux-cp-punt-xc"); + + if (!node) + return VNET_API_ERROR_UNIMPLEMENTED; + + eti = ethernet_get_type_info (em, ethertype); + if (!eti) + return VNET_API_ERROR_INVALID_VALUE; + + if (eti->node_index != ~0 && eti->node_index != node->index) + return VNET_API_ERROR_INVALID_REGISTRATION; + + ethernet_register_input_type (vm, ethertype, node->index); + return 0; +} + +int +lcp_ethertype_get_enabled (ethernet_type_t **ethertypes_vec) +{ + ethernet_main_t *em = ðernet_main; + ethernet_type_info_t *eti; + vlib_main_t *vm = vlib_get_main (); + vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "linux-cp-punt-xc"); + + if (!ethertypes_vec) + return VNET_API_ERROR_INVALID_ARGUMENT; + + if (!node) + return VNET_API_ERROR_UNIMPLEMENTED; + + vec_foreach (eti, em->type_infos) + { + if (eti->node_index == node->index) + { + vec_add1 (*ethertypes_vec, eti->type); + } + } + + return 0; +} + VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (lcp_itf_pair_link_up_down); static clib_error_t * diff --git a/src/plugins/linux-cp/lcp_interface.h b/src/plugins/linux-cp/lcp_interface.h index cfcd3925a15..8cf6d3f4da1 100644 --- a/src/plugins/linux-cp/lcp_interface.h +++ b/src/plugins/linux-cp/lcp_interface.h @@ -18,6 +18,7 @@ #include <vnet/dpo/dpo.h> #include <vnet/adj/adj.h> #include <vnet/ip/ip_types.h> +#include <vnet/ethernet/ethernet.h> #include <plugins/linux-cp/lcp.h> @@ -198,6 +199,18 @@ void lcp_itf_pair_sync_state (lcp_itf_pair_t *lip); void lcp_itf_pair_sync_state_hw (vnet_hw_interface_t *hi); void lcp_itf_pair_sync_state_all (); +/** + * Enable linux-cp-punt-xc for a given ethertype. + * @param ethertype - ethertype to enable + */ +int lcp_ethertype_enable (ethernet_type_t ethertype); + +/** + * Get the list of ethertypes enabled for linux-cp-punt-xc. + * @param ethertypes_vec - pointer to a vector to store the list of ethertypes + */ +int lcp_ethertype_get_enabled (ethernet_type_t **ethertypes_vec); + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/linux-cp/lcp_node.c b/src/plugins/linux-cp/lcp_node.c index 241cc5e4bff..9fa1aa5bd66 100644 --- a/src/plugins/linux-cp/lcp_node.c +++ b/src/plugins/linux-cp/lcp_node.c @@ -39,40 +39,51 @@ typedef enum { -#define _(sym, str) LIP_PUNT_NEXT_##sym, +#define _(sym, str) LIP_PUNT_XC_NEXT_##sym, foreach_lip_punt #undef _ - LIP_PUNT_N_NEXT, -} lip_punt_next_t; + LIP_PUNT_XC_N_NEXT, +} lip_punt_xc_next_t; -typedef struct lip_punt_trace_t_ +typedef struct lip_punt_xc_trace_t_ { + bool is_xc; u32 phy_sw_if_index; u32 host_sw_if_index; -} lip_punt_trace_t; +} lip_punt_xc_trace_t; /* packet trace format function */ static u8 * -format_lip_punt_trace (u8 *s, va_list *args) +format_lip_punt_xc_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 *); - lip_punt_trace_t *t = va_arg (*args, lip_punt_trace_t *); + lip_punt_xc_trace_t *t = va_arg (*args, lip_punt_xc_trace_t *); - s = - format (s, "lip-punt: %u -> %u", t->phy_sw_if_index, t->host_sw_if_index); + if (t->is_xc) + { + s = format (s, "lip-xc: %u -> %u", t->host_sw_if_index, + t->phy_sw_if_index); + } + else + { + s = format (s, "lip-punt: %u -> %u", t->phy_sw_if_index, + t->host_sw_if_index); + } return s; } /** * Pass punted packets from the PHY to the HOST. + * Conditionally x-connect packets from the HOST to the PHY. */ -VLIB_NODE_FN (lip_punt_node) -(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +static_always_inline u32 +lip_punt_xc_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, bool check_xc) { u32 n_left_from, *from, *to_next, n_left_to_next; - lip_punt_next_t next_index; + lip_punt_xc_next_t next_index; next_index = node->cached_next_index; n_left_from = frame->n_vectors; @@ -89,6 +100,7 @@ VLIB_NODE_FN (lip_punt_node) u32 next0 = ~0; u32 bi0, lipi0; u32 sw_if_index0; + bool is_xc0 = 0; u8 len0; bi0 = to_next[0] = from[0]; @@ -97,18 +109,33 @@ VLIB_NODE_FN (lip_punt_node) to_next += 1; n_left_from -= 1; n_left_to_next -= 1; - next0 = LIP_PUNT_NEXT_DROP; + next0 = LIP_PUNT_XC_NEXT_DROP; b0 = vlib_get_buffer (vm, bi0); sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; lipi0 = lcp_itf_pair_find_by_phy (sw_if_index0); - if (PREDICT_FALSE (lipi0 == INDEX_INVALID)) - goto trace0; + + /* + * lip_punt_node: expect sw_if_index0 is phy in an itf pair + * lip_punt_xc_node: if sw_if_index0 is not phy, expect it is host + */ + if (!check_xc && (PREDICT_FALSE (lipi0 == INDEX_INVALID))) + { + goto trace0; + } + else if (check_xc && (lipi0 == INDEX_INVALID)) + { + is_xc0 = 1; + lipi0 = lcp_itf_pair_find_by_host (sw_if_index0); + if (PREDICT_FALSE (lipi0 == INDEX_INVALID)) + goto trace0; + } lip0 = lcp_itf_pair_get (lipi0); - next0 = LIP_PUNT_NEXT_IO; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip0->lip_host_sw_if_index; + next0 = LIP_PUNT_XC_NEXT_IO; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = + is_xc0 ? lip0->lip_phy_sw_if_index : lip0->lip_host_sw_if_index; if (PREDICT_TRUE (lip0->lip_host_type == LCP_ITF_HOST_TAP)) { @@ -129,10 +156,22 @@ VLIB_NODE_FN (lip_punt_node) trace0: if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) { - lip_punt_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); - t->phy_sw_if_index = sw_if_index0; - t->host_sw_if_index = - (lipi0 == INDEX_INVALID) ? ~0 : lip0->lip_host_sw_if_index; + lip_punt_xc_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + + t->is_xc = is_xc0; + if (is_xc0) + { + t->phy_sw_if_index = + (lipi0 == INDEX_INVALID) ? ~0 : lip0->lip_phy_sw_if_index; + t->host_sw_if_index = sw_if_index0; + } + else + { + t->phy_sw_if_index = sw_if_index0; + t->host_sw_if_index = + (lipi0 == INDEX_INVALID) ? ~0 : lip0->lip_host_sw_if_index; + } } vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, @@ -145,16 +184,41 @@ VLIB_NODE_FN (lip_punt_node) return frame->n_vectors; } +VLIB_NODE_FN (lip_punt_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return (lip_punt_xc_inline (vm, node, frame, false /* xc */)); +} + +VLIB_NODE_FN (lip_punt_xc_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return (lip_punt_xc_inline (vm, node, frame, true /* xc */)); +} + VLIB_REGISTER_NODE (lip_punt_node) = { .name = "linux-cp-punt", .vector_size = sizeof (u32), - .format_trace = format_lip_punt_trace, + .format_trace = format_lip_punt_xc_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_next_nodes = LIP_PUNT_XC_N_NEXT, + .next_nodes = { + [LIP_PUNT_XC_NEXT_DROP] = "error-drop", + [LIP_PUNT_XC_NEXT_IO] = "interface-output", + }, +}; + +VLIB_REGISTER_NODE (lip_punt_xc_node) = { + .name = "linux-cp-punt-xc", + .vector_size = sizeof (u32), + .format_trace = format_lip_punt_xc_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_next_nodes = LIP_PUNT_N_NEXT, + .n_next_nodes = LIP_PUNT_XC_N_NEXT, .next_nodes = { - [LIP_PUNT_NEXT_DROP] = "error-drop", - [LIP_PUNT_NEXT_IO] = "interface-output", + [LIP_PUNT_XC_NEXT_DROP] = "error-drop", + [LIP_PUNT_XC_NEXT_IO] = "interface-output", }, }; @@ -190,7 +254,7 @@ VLIB_NODE_FN (lcp_punt_l3_node) (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { u32 n_left_from, *from, *to_next, n_left_to_next; - lip_punt_next_t next_index; + lip_punt_xc_next_t next_index; next_index = node->cached_next_index; n_left_from = frame->n_vectors; diff --git a/test/test_linux_cp.py b/test/test_linux_cp.py index ff6023cea26..d7116233236 100644 --- a/test/test_linux_cp.py +++ b/test/test_linux_cp.py @@ -6,6 +6,14 @@ import socket from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6, Raw from scapy.layers.l2 import Ether, ARP +from scapy.contrib.lacp import LACP +from scapy.contrib.lldp import ( + LLDPDUChassisID, + LLDPDUPortID, + LLDPDUTimeToLive, + LLDPDUEndOfLLDPDU, + LLDPDU, +) from util import reassemble4 from vpp_object import VppObject @@ -427,5 +435,131 @@ class TestLinuxCPIpsec(TemplateIpsec, TemplateIpsecItf4, IpsecTun4): self.unconfig_network(p) +@unittest.skipIf("linux-cp" in config.excluded_plugins, "Exclude linux-cp plugin tests") +class TestLinuxCPEthertype(VppTestCase): + """Linux CP Ethertype""" + + extra_vpp_plugin_config = [ + "plugin", + "linux_cp_plugin.so", + "{", + "enable", + "}", + "plugin", + "linux_cp_unittest_plugin.so", + "{", + "enable", + "}", + "plugin", + "lldp_plugin.so", + "{", + "disable", + "}", + ] + + LACP_ETHERTYPE = 0x8809 + LLDP_ETHERTYPE = 0x88CC + + @classmethod + def setUpClass(cls): + super(TestLinuxCPEthertype, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestLinuxCPEthertype, cls).tearDownClass() + + def setUp(self): + super(TestLinuxCPEthertype, self).setUp() + self.create_pg_interfaces(range(2)) + for i in self.pg_interfaces: + i.admin_up() + + self.host = self.pg0 + self.phy = self.pg1 + + self.pair = VppLcpPair(self, self.phy, self.host).add_vpp_config() + self.logger.info(self.vapi.cli("sh lcp")) + + def tearDown(self): + self.pair.remove_vpp_config() + + for i in self.pg_interfaces: + i.admin_down() + super(TestLinuxCPEthertype, self).tearDown() + + def send_packet(self, sender, receiver, ethertype, dst, data, expect_copy=True): + packet = Ether(src=sender.remote_mac, dst=dst, type=ethertype) / data + if expect_copy: + rxs = self.send_and_expect(sender, [packet], receiver) + for rx in rxs: + self.assertEqual(packet.show2(True), rx.show2(True)) + else: + self.send_and_assert_no_replies(sender, [packet]) + + def send_lacp_packet(self, sender, receiver, expect_copy=True): + data = LACP( + actor_system="00:00:00:00:00:01", partner_system="00:00:00:00:00:02" + ) + self.send_packet( + sender, + receiver, + self.LACP_ETHERTYPE, + "01:80:c2:00:00:02", + data, + expect_copy, + ) + + def send_lldp_packet(self, sender, receiver, expect_copy=True): + data = ( + LLDPDUChassisID(subtype=4, id="01:02:03:04:05:06") + / LLDPDUPortID(subtype=3, id="07:08:09:0a:0b:0c") + / LLDPDUTimeToLive(ttl=120) + / LLDPDUEndOfLLDPDU() + ) + self.send_packet( + sender, + receiver, + self.LLDP_ETHERTYPE, + "01:80:c2:00:00:0e", + data, + expect_copy, + ) + + def check_ethertype_enabled(self, ethertype, enabled=True): + reply = self.vapi.lcp_ethertype_get() + output = self.vapi.cli("show lcp ethertype") + + if enabled: + self.assertIn(ethertype, reply.ethertypes) + self.assertIn(hex(ethertype), output) + else: + self.assertNotIn(ethertype, reply.ethertypes) + self.assertNotIn(hex(ethertype), output) + + def test_linux_cp_lacp(self): + """Linux CP LACP Test""" + self.check_ethertype_enabled(self.LACP_ETHERTYPE, enabled=False) + self.send_lacp_packet(self.phy, self.host, expect_copy=False) + self.send_lacp_packet(self.host, self.phy, expect_copy=False) + + self.vapi.cli("lcp ethertype enable " + str(self.LACP_ETHERTYPE)) + + self.check_ethertype_enabled(self.LACP_ETHERTYPE, enabled=True) + self.send_lacp_packet(self.phy, self.host, expect_copy=True) + self.send_lacp_packet(self.host, self.phy, expect_copy=True) + + def test_linux_cp_lldp(self): + """Linux CP LLDP Test""" + self.check_ethertype_enabled(self.LLDP_ETHERTYPE, enabled=False) + self.send_lldp_packet(self.phy, self.host, expect_copy=False) + self.send_lldp_packet(self.host, self.phy, expect_copy=False) + + self.vapi.cli("lcp ethertype enable " + str(self.LLDP_ETHERTYPE)) + + self.check_ethertype_enabled(self.LLDP_ETHERTYPE, enabled=True) + self.send_lldp_packet(self.phy, self.host, expect_copy=True) + self.send_lldp_packet(self.host, self.phy, expect_copy=True) + + if __name__ == "__main__": unittest.main(testRunner=VppTestRunner) |