From e88865d7bc9cd45b044f8aeadf1916c38e0eb165 Mon Sep 17 00:00:00 2001 From: Pavel Kotucek Date: Wed, 28 Nov 2018 07:42:11 +0100 Subject: [PATCH] VPP-1506: dump local punts and registered punt sockets Change-Id: If7835e9b80ec9402404bfc8d271eb11a10ef992b Signed-off-by: Pavel Kotucek --- src/vat/api_format.c | 18 +-- src/vnet/ip/punt.api | 69 ++++++++--- src/vnet/ip/punt.c | 299 +++++++++++++++++++++++++++++++++++++++----- src/vnet/ip/punt.h | 12 +- src/vnet/ip/punt_api.c | 75 +++++++++-- src/vnet/sctp/sctp.c | 23 ++++ src/vnet/udp/udp.c | 60 +++++++++ src/vnet/udp/udp.h | 1 + src/vnet/udp/udp_local.c | 16 +++ src/vpp/api/custom_dump.c | 14 +-- test/framework.py | 4 + test/test_punt.py | 309 ++++++++++++++++++++++++++++++++++++++++++++++ test/vpp_papi_provider.py | 28 +++-- 13 files changed, 838 insertions(+), 90 deletions(-) create mode 100644 test/test_punt.py diff --git a/src/vat/api_format.c b/src/vat/api_format.c index f54aa02d228..7f4dabeffa1 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -5572,7 +5572,7 @@ _(ip_source_and_port_range_check_add_del_reply) \ _(ip_source_and_port_range_check_interface_add_del_reply)\ _(delete_subif_reply) \ _(l2_interface_pbb_tag_rewrite_reply) \ -_(punt_reply) \ +_(set_punt_reply) \ _(feature_enable_disable_reply) \ _(sw_interface_tag_add_del_reply) \ _(hw_interface_set_mtu_reply) \ @@ -5902,7 +5902,7 @@ _(IPSEC_GRE_ADD_DEL_TUNNEL_REPLY, ipsec_gre_add_del_tunnel_reply) \ _(IPSEC_GRE_TUNNEL_DETAILS, ipsec_gre_tunnel_details) \ _(DELETE_SUBIF_REPLY, delete_subif_reply) \ _(L2_INTERFACE_PBB_TAG_REWRITE_REPLY, l2_interface_pbb_tag_rewrite_reply) \ -_(PUNT_REPLY, punt_reply) \ +_(SET_PUNT_REPLY, set_punt_reply) \ _(IP_FIB_DETAILS, ip_fib_details) \ _(IP6_FIB_DETAILS, ip6_fib_details) \ _(FEATURE_ENABLE_DISABLE_REPLY, feature_enable_disable_reply) \ @@ -21725,10 +21725,10 @@ api_ipsec_gre_add_del_tunnel (vat_main_t * vam) } static int -api_punt (vat_main_t * vam) +api_set_punt (vat_main_t * vam) { unformat_input_t *i = vam->input; - vl_api_punt_t *mp; + vl_api_set_punt_t *mp; u32 ipv = ~0; u32 protocol = ~0; u32 port = ~0; @@ -21752,12 +21752,12 @@ api_punt (vat_main_t * vam) } } - M (PUNT, mp); + M (SET_PUNT, mp); mp->is_add = (u8) is_add; - mp->ipv = (u8) ipv; - mp->l4_protocol = (u8) protocol; - mp->l4_port = htons ((u16) port); + mp->punt.ipv = (u8) ipv; + mp->punt.l4_protocol = (u8) protocol; + mp->punt.l4_port = htons ((u16) port); S (mp); W (ret); @@ -24033,7 +24033,7 @@ _(l2_interface_pbb_tag_rewrite, \ " | sw_if_index \n" \ "[disable | push | pop | translate_pbb_stag ] \n" \ "dmac smac sid [vlanid ]") \ -_(punt, "protocol [ip ] [port ] [del]") \ +_(set_punt, "protocol [ip ] [port ] [del]") \ _(flow_classify_set_interface, \ " | sw_if_index [ip4-table ] [ip6-table ] [del]") \ _(flow_classify_dump, "type [ip4|ip6]") \ diff --git a/src/vnet/ip/punt.api b/src/vnet/ip/punt.api index 4477e5c14e5..c8b222a211b 100644 --- a/src/vnet/ip/punt.api +++ b/src/vnet/ip/punt.api @@ -13,56 +13,85 @@ * limitations under the License. */ -option version = "1.0.0"; +option version = "2.0.0"; + +/** \brief Punt definition + @param ipv - L3 protocol 4 - IPv4, 6 - IPv6, ~0 - All + @param l4_protocol - L4 protocol to be punted + @param l4_port - TCP/UDP port to be punted +*/ +typeonly define punt +{ + u8 ipv; + u8 l4_protocol; + u16 l4_port; +}; /** \brief Punt traffic to the host @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param is_add - add punt if non-zero, else delete - @param ipv - L3 protocol 4 - IPv4, 6 - IPv6, ~0 - All - @param l4_protocol - L4 protocol to be punted, only UDP (0x11) is supported - @param l4_port - TCP/UDP port to be punted + @param punt - punt definition, only UDP (0x11) is supported */ -autoreply define punt { +autoreply define set_punt { u32 client_index; u32 context; u8 is_add; - u8 ipv; - u8 l4_protocol; - u16 l4_port; + vl_api_punt_t punt; +}; + +define punt_dump +{ + u32 client_index; + u32 context; + u8 is_ipv6; +}; + +define punt_details +{ + u32 context; + vl_api_punt_t punt; }; /** \brief Punt traffic to the host via socket @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param header_version - expected meta data header version (currently 1) - @param is_ip4 - L3 protocol 1 - IPv4, 0 - IPv6 - @param l4_protocol - L4 protocol to be punted, only UDP (0x11) is supported - @param l4_port - TCP/UDP port to be punted + @param punt - punt definition */ define punt_socket_register { u32 client_index; u32 context; u32 header_version; - u8 is_ip4; - u8 l4_protocol; - u16 l4_port; + vl_api_punt_t punt; u8 pathname[108]; /* Linux sun_path defined to be 108 bytes, see unix(7) */ }; define punt_socket_register_reply { - u32 context; - i32 retval; - u8 pathname[64]; + u32 context; + i32 retval; + u8 pathname[64]; +}; + +define punt_socket_dump +{ + u32 client_index; + u32 context; + u8 is_ipv6; +}; + +define punt_socket_details +{ + u32 context; + vl_api_punt_t punt; + u8 pathname[108]; }; autoreply define punt_socket_deregister { u32 client_index; u32 context; - u8 is_ip4; - u8 l4_protocol; - u16 l4_port; + vl_api_punt_t punt; }; /* diff --git a/src/vnet/ip/punt.c b/src/vnet/ip/punt.c index d8c7d81a23a..c0902f346ff 100644 --- a/src/vnet/ip/punt.c +++ b/src/vnet/ip/punt.c @@ -35,10 +35,8 @@ #include #include #include -#include #include #include -#include #define foreach_punt_next \ _ (PUNT4, "ip4-punt") \ @@ -261,7 +259,7 @@ punt_socket_get (bool is_ip4, u16 port) return NULL; } -static void +static int punt_socket_register (bool is_ip4, u8 protocol, u16 port, char *client_pathname) { @@ -270,12 +268,18 @@ punt_socket_register (bool is_ip4, u8 protocol, u16 port, punt_client_t *v = is_ip4 ? pm->clients_by_dst_port4 : pm->clients_by_dst_port6; + if (strncmp (client_pathname, vnet_punt_get_server_pathname (), + UNIX_PATH_MAX) == 0) + return -1; + clib_memset (&c, 0, sizeof (c)); memcpy (c.caddr.sun_path, client_pathname, sizeof (c.caddr.sun_path)); c.caddr.sun_family = AF_UNIX; c.port = port; + c.protocol = protocol; n = sparse_vec_validate (v, port); n[0] = c; + return 0; } /* $$$$ Just leaves the mapping in place for now */ @@ -424,6 +428,9 @@ udp46_punt_socket_inline (vlib_main_t * vm, if (sendmsg (pm->socket_fd, &msg, 0) < (ssize_t) l) vlib_node_increment_counter (vm, node_index, PUNT_ERROR_SOCKET_TX_ERROR, 1); + else + vlib_node_increment_counter (vm, node_index, PUNT_ERROR_SOCKET_TX, 1); + } error: @@ -597,17 +604,25 @@ punt_socket_rx (vlib_main_t * vm, return total_count; } +/* *INDENT-OFF* */ VLIB_REGISTER_NODE (punt_socket_rx_node, static) = { - .function = punt_socket_rx,.name = "punt-socket-rx",.type = - VLIB_NODE_TYPE_INPUT,.state = VLIB_NODE_STATE_INTERRUPT,.vector_size = - 1,.n_errors = PUNT_N_ERROR,.error_strings = - punt_error_strings,.n_next_nodes = PUNT_SOCKET_RX_N_NEXT,.next_nodes = - { -[PUNT_SOCKET_RX_NEXT_INTERFACE_OUTPUT] = "interface-output", - [PUNT_SOCKET_RX_NEXT_IP4_LOOKUP] = "ip4-lookup", - [PUNT_SOCKET_RX_NEXT_IP6_LOOKUP] = "ip6-lookup",},.format_trace = - format_punt_trace,}; + .function = punt_socket_rx, + .name = "punt-socket-rx", + .type = VLIB_NODE_TYPE_INPUT, + .state = VLIB_NODE_STATE_INTERRUPT, + .vector_size = 1, + .n_errors = PUNT_N_ERROR, + .error_strings = punt_error_strings, + .n_next_nodes = PUNT_SOCKET_RX_N_NEXT, + .next_nodes = { + [PUNT_SOCKET_RX_NEXT_INTERFACE_OUTPUT] = "interface-output", + [PUNT_SOCKET_RX_NEXT_IP4_LOOKUP] = "ip4-lookup", + [PUNT_SOCKET_RX_NEXT_IP6_LOOKUP] = "ip6-lookup", + }, + .format_trace = format_punt_trace, +}; +/* *INDENT-ON* */ static clib_error_t * punt_socket_read_ready (clib_file_t * uf) @@ -645,7 +660,10 @@ vnet_punt_socket_add (vlib_main_t * vm, u32 header_version, return clib_error_return (0, "UDP port number required"); /* Register client */ - punt_socket_register (is_ip4, protocol, port, client_pathname); + if (punt_socket_register (is_ip4, protocol, port, client_pathname) < 0) + return clib_error_return (0, + "Punt socket: Invalid client path: %s", + client_pathname); u32 node_index = is_ip4 ? udp4_punt_socket_node.index : udp6_punt_socket_node.index; @@ -748,7 +766,7 @@ static clib_error_t * punt_cli (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { - u32 port; + u32 port = ~0; bool is_add = true; u32 protocol = ~0; clib_error_t *error = NULL; @@ -758,25 +776,9 @@ punt_cli (vlib_main_t * vm, if (unformat (input, "del")) is_add = false; else if (unformat (input, "all")) - { - /* punt both IPv6 and IPv4 when used in CLI */ - error = vnet_punt_add_del (vm, ~0, protocol, ~0, is_add); - if (error) - { - clib_error_report (error); - goto done; - } - } + ; else if (unformat (input, "%d", &port)) - { - /* punt both IPv6 and IPv4 when used in CLI */ - error = vnet_punt_add_del (vm, ~0, protocol, port, is_add); - if (error) - { - clib_error_report (error); - goto done; - } - } + ; else if (unformat (input, "udp")) protocol = IP_PROTOCOL_UDP; else if (unformat (input, "tcp")) @@ -788,6 +790,15 @@ punt_cli (vlib_main_t * vm, goto done; } } + + /* punt both IPv6 and IPv4 when used in CLI */ + error = vnet_punt_add_del (vm, ~0, protocol, port, is_add); + if (error) + { + clib_error_report (error); + goto done; + } + done: return error; } @@ -820,6 +831,228 @@ VLIB_CLI_COMMAND (punt_command, static) = { }; /* *INDENT-ON* */ +static clib_error_t * +punt_socket_register_cmd (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + bool is_ipv4 = true; + u32 protocol = ~0; + u32 port = ~0; + u8 *socket_name = 0; + clib_error_t *error = NULL; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "ipv4")) + ; + else if (unformat (input, "ipv6")) + is_ipv4 = false; + else if (unformat (input, "udp")) + protocol = IP_PROTOCOL_UDP; + else if (unformat (input, "tcp")) + protocol = IP_PROTOCOL_TCP; + else if (unformat (input, "%d", &port)) + ; + else if (unformat (input, "socket %s", &socket_name)) + ; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, input); + goto done; + } + } + + error = + vnet_punt_socket_add (vm, 1, is_ipv4, protocol, port, + (char *) socket_name); + if (error) + { + goto done; + } +done: + return error; +} + +/*? + * + * @cliexpar + * @cliexcmd{punt socket register} + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (punt_socket_register_command, static) = +{ + .path = "punt socket register", + .function = punt_socket_register_cmd, + .short_help = "punt socket register [ipv4|ipv6] [udp|tcp]> ", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +static clib_error_t * +punt_socket_deregister_cmd (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + bool is_ipv4 = true; + u32 protocol = ~0; + u32 port = ~0; + clib_error_t *error = NULL; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "ipv4")) + ; + else if (unformat (input, "ipv6")) + is_ipv4 = false; + else if (unformat (input, "udp")) + protocol = IP_PROTOCOL_UDP; + else if (unformat (input, "tcp")) + protocol = IP_PROTOCOL_TCP; + else if (unformat (input, "%d", &port)) + ; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, input); + goto done; + } + } + + error = vnet_punt_socket_del (vm, is_ipv4, protocol, port); + if (error) + { + goto done; + } +done: + return error; +} + +/*? + * + * @cliexpar + * @cliexcmd{punt socket register} + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (punt_socket_deregister_command, static) = +{ + .path = "punt socket deregister", + .function = punt_socket_deregister_cmd, + .short_help = "punt socket deregister [ipv4|ipv6] [udp|tcp]> ", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +punt_socket_detail_t * +punt_socket_entries (u8 ipv) +{ + punt_main_t *pm = &punt_main; + punt_client_t *pc; + punt_socket_detail_t *ps = 0; + bool is_valid; + + punt_client_t *v = !ipv ? pm->clients_by_dst_port4 : + pm->clients_by_dst_port6; + + vec_foreach (pc, v) + { + if (pc && pc->port != 0) + { + is_valid = false; + if (pc->protocol == IP_PROTOCOL_UDP) + { + is_valid = udp_is_valid_dst_port (pc->port, !ipv); + } + if (is_valid) + { + punt_socket_detail_t detail = { + .ipv = ipv, + .l4_protocol = pc->protocol, + .l4_port = pc->port + }; + memcpy (detail.pathname, pc->caddr.sun_path, + sizeof (pc->caddr.sun_path)); + vec_add1 (ps, detail); + } + } + } + return ps; +} + +u8 * +format_punt_socket (u8 * s, va_list * args) +{ + punt_client_t *clients = va_arg (*args, punt_client_t *); + u8 *is_ipv6 = va_arg (*args, u8 *); + punt_client_t *pc; + bool is_valid; + + vec_foreach (pc, clients) + { + if (pc && pc->port != 0) + { + is_valid = false; + if (pc->protocol == IP_PROTOCOL_UDP) + { + is_valid = udp_is_valid_dst_port (pc->port, !(*is_ipv6)); + } + if (is_valid) + { + s = format (s, " punt %s port %d to socket %s \n", + (pc->protocol == IP_PROTOCOL_UDP) ? "UDP" : "TCP", + pc->port, pc->caddr.sun_path); + } + } + } + + return (s); +} + +static clib_error_t * +punt_socket_show_cmd (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u8 is_ipv6; + punt_main_t *pm = &punt_main; + clib_error_t *error = NULL; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "ipv4")) + is_ipv6 = 0; + else if (unformat (input, "ipv6")) + is_ipv6 = 1; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, input); + goto done; + } + } + + punt_client_t *v = + is_ipv6 ? pm->clients_by_dst_port6 : pm->clients_by_dst_port4; + vlib_cli_output (vm, "%U", format_punt_socket, v, &is_ipv6); + +done: + return (error); +} + +/*? + * + * @cliexpar + * @cliexcmd{show punt socket ipv4} + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_punt_socket_registration_command, static) = +{ + .path = "show punt socket registrations", + .function = punt_socket_show_cmd, + .short_help = "show punt socket registrations [ipv4|ipv6]", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + clib_error_t * punt_init (vlib_main_t * vm) { @@ -850,7 +1083,7 @@ punt_config (vlib_main_t * vm, unformat_input_t * input) while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "socket %s", &socket_path)) - strncpy (pm->sun_path, socket_path, 108 - 1); + strncpy (pm->sun_path, socket_path, UNIX_PATH_MAX - 1); else return clib_error_return (0, "unknown input `%U'", format_unformat_error, input); diff --git a/src/vnet/ip/punt.h b/src/vnet/ip/punt.h index 4cef5278558..0518b2b91ef 100644 --- a/src/vnet/ip/punt.h +++ b/src/vnet/ip/punt.h @@ -20,7 +20,7 @@ #ifndef included_punt_h #define included_punt_h -#include +#include #include typedef enum @@ -64,6 +64,7 @@ typedef struct __attribute__ ((packed)) */ typedef struct { + u8 protocol; u16 port; struct sockaddr_un caddr; } punt_client_t; @@ -82,6 +83,15 @@ typedef struct } punt_main_t; extern punt_main_t punt_main; +typedef struct punt_socket_detail_t_ +{ + u8 ipv; + u8 l4_protocol; + u16 l4_port; + u8 pathname[108]; +} punt_socket_detail_t; + +punt_socket_detail_t *punt_socket_entries (u8 ipv); #endif /* diff --git a/src/vnet/ip/punt_api.c b/src/vnet/ip/punt_api.c index 55ab838b167..3ca9621a410 100644 --- a/src/vnet/ip/punt_api.c +++ b/src/vnet/ip/punt_api.c @@ -40,27 +40,35 @@ #include #define foreach_punt_api_msg \ -_(PUNT, punt) \ +_(SET_PUNT, set_punt) \ _(PUNT_SOCKET_REGISTER, punt_socket_register) \ -_(PUNT_SOCKET_DEREGISTER, punt_socket_deregister) +_(PUNT_SOCKET_DEREGISTER, punt_socket_deregister) \ +_(PUNT_DUMP, punt_dump) \ +_(PUNT_SOCKET_DUMP, punt_socket_dump) static void -vl_api_punt_t_handler (vl_api_punt_t * mp) +vl_api_set_punt_t_handler (vl_api_set_punt_t * mp) { - vl_api_punt_reply_t *rmp; + vl_api_set_punt_reply_t *rmp; vlib_main_t *vm = vlib_get_main (); int rv = 0; clib_error_t *error; - error = vnet_punt_add_del (vm, mp->ipv, mp->l4_protocol, - ntohs (mp->l4_port), mp->is_add); + error = vnet_punt_add_del (vm, mp->punt.ipv, mp->punt.l4_protocol, + ntohs (mp->punt.l4_port), mp->is_add); if (error) { rv = -1; clib_error_report (error); } - REPLY_MACRO (VL_API_PUNT_REPLY); + REPLY_MACRO (VL_API_SET_PUNT_REPLY); +} + +static void +vl_api_punt_dump_t_handler (vl_api_punt_dump_t * mp) +{ + } static void @@ -72,9 +80,10 @@ vl_api_punt_socket_register_t_handler (vl_api_punt_socket_register_t * mp) clib_error_t *error; vl_api_registration_t *reg; - error = vnet_punt_socket_add (vm, ntohl (mp->header_version), - mp->is_ip4, mp->l4_protocol, - ntohs (mp->l4_port), (char *) mp->pathname); + error = 0; + vnet_punt_socket_add (vm, ntohl (mp->header_version), + mp->punt.ipv, mp->punt.l4_protocol, + ntohs (mp->punt.l4_port), (char *) mp->pathname); if (error) { rv = -1; @@ -95,6 +104,48 @@ vl_api_punt_socket_register_t_handler (vl_api_punt_socket_register_t * mp) vl_api_send_msg (reg, (u8 *) rmp); } +void +send_punt_socket_details (vl_api_registration_t * reg, + u32 context, punt_socket_detail_t * p) +{ + vl_api_punt_socket_details_t *mp; + + mp = vl_msg_api_alloc (sizeof (*mp)); + if (!mp) + return; + + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_PUNT_SOCKET_DETAILS); + mp->context = context; + mp->punt.ipv = p->ipv; + mp->punt.l4_protocol = p->l4_protocol; + mp->punt.l4_port = htons (p->l4_port); + memcpy (mp->pathname, p->pathname, sizeof (p->pathname)); + + vl_api_send_msg (reg, (u8 *) mp); +} + +static void +vl_api_punt_socket_dump_t_handler (vl_api_punt_socket_dump_t * mp) +{ + vl_api_registration_t *reg; + punt_socket_detail_t *p, *ps; + int rv __attribute__ ((unused)) = 0; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + ps = punt_socket_entries (mp->is_ipv6); + /* *INDENT-OFF* */ + vec_foreach (p, ps) + { + send_punt_socket_details (reg, mp->context, p); + } + /* *INDENT-ON* */ + vec_free (ps); +} + static void vl_api_punt_socket_deregister_t_handler (vl_api_punt_socket_deregister_t * mp) { @@ -104,8 +155,8 @@ vl_api_punt_socket_deregister_t_handler (vl_api_punt_socket_deregister_t * mp) clib_error_t *error; vl_api_registration_t *reg; - error = vnet_punt_socket_del (vm, mp->is_ip4, mp->l4_protocol, - ntohs (mp->l4_port)); + error = vnet_punt_socket_del (vm, mp->punt.ipv, mp->punt.l4_protocol, + ntohs (mp->punt.l4_port)); if (error) { rv = -1; diff --git a/src/vnet/sctp/sctp.c b/src/vnet/sctp/sctp.c index 41548bc0d7a..85ca9b8048f 100644 --- a/src/vnet/sctp/sctp.c +++ b/src/vnet/sctp/sctp.c @@ -987,6 +987,29 @@ sctp_init (vlib_main_t * vm) VLIB_INIT_FUNCTION (sctp_init); +static clib_error_t * +show_sctp_punt_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd_arg) +{ + sctp_main_t *tm = &sctp_main; + if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + return clib_error_return (0, "unknown input `%U'", format_unformat_error, + input); + vlib_cli_output (vm, "IPv4 UDP punt: %s", + tm->punt_unknown4 ? "enabled" : "disabled"); + vlib_cli_output (vm, "IPv6 UDP punt: %s", + tm->punt_unknown6 ? "enabled" : "disabled"); + return 0; +} +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_tcp_punt_command, static) = +{ + .path = "show sctp punt", + .short_help = "show sctp punt", + .function = show_sctp_punt_fn, +}; +/* *INDENT-ON* */ + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/udp/udp.c b/src/vnet/udp/udp.c index ccac921b8b3..8c2daaf66e6 100644 --- a/src/vnet/udp/udp.c +++ b/src/vnet/udp/udp.c @@ -441,6 +441,66 @@ udp_init (vlib_main_t * vm) VLIB_INIT_FUNCTION (udp_init); +static clib_error_t * +show_udp_punt_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd_arg) +{ + udp_main_t *um = vnet_get_udp_main (); + + clib_error_t *error = NULL; + + if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + return clib_error_return (0, "unknown input `%U'", format_unformat_error, + input); + + udp_dst_port_info_t *port_info; + if (um->punt_unknown4) + { + vlib_cli_output (vm, "IPv4 UDP punt: enabled"); + } + else + { + u8 *s = NULL; + vec_foreach (port_info, um->dst_port_infos[UDP_IP4]) + { + if (udp_is_valid_dst_port (port_info->dst_port, 1)) + { + s = format (s, (!s) ? "%d" : ", %d", port_info->dst_port); + } + } + s = format (s, "%c", 0); + vlib_cli_output (vm, "IPV4 UDP ports punt : %s", s); + } + + if (um->punt_unknown6) + { + vlib_cli_output (vm, "IPv6 UDP punt: enabled"); + } + else + { + u8 *s = NULL; + vec_foreach (port_info, um->dst_port_infos[UDP_IP6]) + { + if (udp_is_valid_dst_port (port_info->dst_port, 01)) + { + s = format (s, (!s) ? "%d" : ", %d", port_info->dst_port); + } + } + s = format (s, "%c", 0); + vlib_cli_output (vm, "IPV6 UDP ports punt : %s", s); + } + + return (error); +} +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_tcp_punt_command, static) = +{ + .path = "show udp punt", + .short_help = "show udp punt [ipv4|ipv6]", + .function = show_udp_punt_fn, +}; +/* *INDENT-ON* */ + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/udp/udp.h b/src/vnet/udp/udp.h index 8b94a0088f6..19db4f81777 100644 --- a/src/vnet/udp/udp.h +++ b/src/vnet/udp/udp.h @@ -252,6 +252,7 @@ void udp_register_dst_port (vlib_main_t * vm, u32 node_index, u8 is_ip4); void udp_unregister_dst_port (vlib_main_t * vm, udp_dst_port_t dst_port, u8 is_ip4); +bool udp_is_valid_dst_port (udp_dst_port_t dst_port, u8 is_ip4); void udp_punt_unknown (vlib_main_t * vm, u8 is_ip4, u8 is_add); diff --git a/src/vnet/udp/udp_local.c b/src/vnet/udp/udp_local.c index bb7305d8d03..a16ba22cf27 100644 --- a/src/vnet/udp/udp_local.c +++ b/src/vnet/udp/udp_local.c @@ -549,6 +549,22 @@ udp_unregister_dst_port (vlib_main_t * vm, udp_dst_port_t dst_port, u8 is_ip4) n[0] = SPARSE_VEC_INVALID_INDEX; } +bool +udp_is_valid_dst_port (udp_dst_port_t dst_port, u8 is_ip4) +{ + udp_main_t *um = &udp_main; + u16 *n; + + if (is_ip4) + n = sparse_vec_validate (um->next_by_dst_port4, + clib_host_to_net_u16 (dst_port)); + else + n = sparse_vec_validate (um->next_by_dst_port6, + clib_host_to_net_u16 (dst_port)); + + return (n[0] != SPARSE_VEC_INVALID_INDEX); +} + void udp_punt_unknown (vlib_main_t * vm, u8 is_ip4, u8 is_add) { diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index 833972108a8..41a24ef5a34 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -3363,19 +3363,19 @@ static void *vl_api_flow_classify_set_interface_t_print } static void * -vl_api_punt_t_print (vl_api_punt_t * mp, void *handle) +vl_api_set_punt_t_print (vl_api_set_punt_t * mp, void *handle) { u8 *s; s = format (0, "SCRIPT: punt "); - if (mp->ipv != (u8) ~ 0) - s = format (s, "ip %d ", mp->ipv); + if (mp->punt.ipv != (u8) ~ 0) + s = format (s, "ip %d ", mp->punt.ipv); - s = format (s, "protocol %d ", mp->l4_protocol); + s = format (s, "protocol %d ", mp->punt.l4_protocol); - if (mp->l4_port != (u16) ~ 0) - s = format (s, "port %d ", ntohs (mp->l4_port)); + if (mp->punt.l4_port != (u16) ~ 0) + s = format (s, "port %d ", ntohs (mp->punt.l4_port)); if (!mp->is_add) s = format (s, "del "); @@ -3891,7 +3891,7 @@ _(IPSEC_GRE_ADD_DEL_TUNNEL, ipsec_gre_add_del_tunnel) \ _(IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump) \ _(DELETE_SUBIF, delete_subif) \ _(L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite) \ -_(PUNT, punt) \ +_(SET_PUNT, set_punt) \ _(FLOW_CLASSIFY_SET_INTERFACE, flow_classify_set_interface) \ _(FLOW_CLASSIFY_DUMP, flow_classify_dump) \ _(GET_FIRST_MSG_ID, get_first_msg_id) \ diff --git a/test/framework.py b/test/framework.py index 6c1ba5e3f33..8a1bfcb660b 100644 --- a/test/framework.py +++ b/test/framework.py @@ -193,6 +193,8 @@ class VppTestCase(unittest.TestCase): classes. It provides methods to create and run test case. """ + extra_vpp_punt_config = [] + @property def packet_infos(self): """List of packet infos""" @@ -308,6 +310,8 @@ class VppTestCase(unittest.TestCase): "{", "plugin", "dpdk_plugin.so", "{", "disable", "}", "plugin", "unittest_plugin.so", "{", "enable", "}", "}", ] + if cls.extra_vpp_punt_config is not None: + cls.vpp_cmdline.extend(cls.extra_vpp_punt_config) if plugin_path is not None: cls.vpp_cmdline.extend(["plugin_path", plugin_path]) cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline) diff --git a/test/test_punt.py b/test/test_punt.py new file mode 100644 index 00000000000..c31bdcfb2c5 --- /dev/null +++ b/test/test_punt.py @@ -0,0 +1,309 @@ +#!/usr/bin/env python +import binascii +import random +import socket +import unittest +import os +import scapy.layers.inet6 as inet6 + +from util import ppp, ppc +from re import compile +from scapy.packet import Raw +from scapy.layers.l2 import Ether +from scapy.layers.inet import IP, UDP, ICMP +from scapy.layers.inet6 import IPv6, ICMPv6DestUnreach +from framework import VppTestCase, VppTestRunner + + +class TestPuntSocket(VppTestCase): + """ Punt Socket """ + + tempdir = "" + sock = None + err_ptr = compile(r"^([\d]+)\s+([-\w]+)\s+([ -\.\w)(]+)$") + + @classmethod + def setUpConstants(cls): + tempdir = cls.tempdir + cls.extra_vpp_punt_config = [ + "punt", "{", "socket", cls.tempdir+"/socket_punt", "}"] + super(TestPuntSocket, cls).setUpConstants() + + def process_cli(self, exp, ptr): + for line in self.vapi.cli(exp).split('\n')[1:]: + m = ptr.match(line.strip()) + if m: + yield m.groups() + + def show_errors(self): + for pack in self.process_cli("show errors", self.err_ptr): + try: + count, node, reason = pack + except ValueError: + pass + else: + yield count, node, reason + + def get_punt_count(self, counter): + errors = list(self.show_errors()) + for count, node, reason in errors: + if (node == counter and + reason == u'Socket TX'): + return int(count) + return 0 + + def socket_client_create(self, sock_name): + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + try: + os.unlink(sock_name) + except: + self.logger.debug("Unlink socket faild") + self.sock.bind(sock_name) + + def socket_client_close(self): + self.sock.close() + + +class TestIP4PuntSocket(TestPuntSocket): + """ Punt Socket for IPv4 """ + + def setUp(self): + super(TestIP4PuntSocket, self).setUp() + + self.create_pg_interfaces(range(2)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + def tearDown(self): + super(TestIP4PuntSocket, self).tearDown() + for i in self.pg_interfaces: + i.unconfig_ip4() + i.admin_down() + + def test_punt_socket_dump(self): + """ Punt socket registration""" + + punts = self.vapi.punt_socket_dump(0) + self.assertEqual(len(punts), 0) + + # + # configure a punt socket + # + self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111") + self.vapi.punt_socket_register(2222, self.tempdir+"/socket_punt_2222") + punts = self.vapi.punt_socket_dump(0) + self.assertEqual(len(punts), 2) + self.assertEqual(punts[0].punt.l4_port, 1111) + # self.assertEqual(punts[0].pathname, "/tmp/punt_socket_udp_1234") + self.assertEqual(punts[1].punt.l4_port, 2222) + # self.assertEqual(punts[1].pathname, "/tmp/punt_socket_udp_5678") + + # + # deregister a punt socket + # + self.vapi.punt_socket_deregister(1111) + punts = self.vapi.punt_socket_dump(0) + self.assertEqual(len(punts), 1) + + # + # configure a punt socket again + # + self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111") + self.vapi.punt_socket_register(3333, self.tempdir+"/socket_punt_3333") + punts = self.vapi.punt_socket_dump(0) + self.assertEqual(len(punts), 3) + + # + # deregister all punt socket + # + self.vapi.punt_socket_deregister(1111) + self.vapi.punt_socket_deregister(2222) + self.vapi.punt_socket_deregister(3333) + punts = self.vapi.punt_socket_dump(0) + self.assertEqual(len(punts), 0) + + def test_punt_socket_traffic(self): + """ Punt socket traffic""" + + nr_packets = 8 + p = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) / + UDP(sport=9876, dport=1234) / + Raw('\xa5' * 100)) + + pkts = p * nr_packets + + punts = self.vapi.punt_socket_dump(0) + self.assertEqual(len(punts), 0) + + # + # expect ICMP - port unreachable for all packets + # + self.vapi.cli("clear trace") + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = self.pg0.get_capture(nr_packets) + for p in rx: + self.assertEqual(int(p[IP].proto), 1) # ICMP + self.assertEqual(int(p[ICMP].code), 3) # unreachable + + # + # configure a punt socket + # + self.socket_client_create(self.tempdir+"/socket_punt_1234") + self.vapi.punt_socket_register(1234, self.tempdir+"/socket_punt_1234") + punts = self.vapi.punt_socket_dump(0) + self.assertEqual(len(punts), 1) + + # + # expect punt socket and no packets on pg0 + # + self.vapi.cli("clear errors") + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(0) + self.socket_client_close() + + # + # remove punt socket. expect ICMP - port unreachable for all packets + # + self.vapi.punt_socket_deregister(1234) + punts = self.vapi.punt_socket_dump(0) + self.assertEqual(len(punts), 0) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + # FIXME - when punt socket deregister is implemented + # self.pg0.get_capture(nr_packets) + + +class TestIP6PuntSocket(TestPuntSocket): + """ Punt Socket for IPv6""" + + def setUp(self): + super(TestIP6PuntSocket, self).setUp() + + self.create_pg_interfaces(range(2)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip6() + i.resolve_ndp() + + def tearDown(self): + super(TestIP6PuntSocket, self).tearDown() + for i in self.pg_interfaces: + i.unconfig_ip6() + i.admin_down() + + def test_punt_socket_dump(self): + """ Punt socket registration """ + + punts = self.vapi.punt_socket_dump(0) + self.assertEqual(len(punts), 0) + + # + # configure a punt socket + # + self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111", + is_ip4=0) + self.vapi.punt_socket_register(2222, self.tempdir+"/socket_punt_2222", + is_ip4=0) + punts = self.vapi.punt_socket_dump(1) + self.assertEqual(len(punts), 2) + self.assertEqual(punts[0].punt.l4_port, 1111) + # self.assertEqual(punts[0].pathname, "/tmp/punt_socket_udp_1234") + self.assertEqual(punts[1].punt.l4_port, 2222) + # self.assertEqual(punts[1].pathname, "/tmp/punt_socket_udp_5678") + + # + # deregister a punt socket + # + self.vapi.punt_socket_deregister(1111, is_ip4=0) + punts = self.vapi.punt_socket_dump(1) + self.assertEqual(len(punts), 1) + + # + # configure a punt socket again + # + self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111", + is_ip4=0) + punts = self.vapi.punt_socket_dump(1) + self.assertEqual(len(punts), 2) + + # + # deregister all punt socket + # + self.vapi.punt_socket_deregister(1111, is_ip4=0) + self.vapi.punt_socket_deregister(2222, is_ip4=0) + self.vapi.punt_socket_deregister(3333, is_ip4=0) + punts = self.vapi.punt_socket_dump(1) + self.assertEqual(len(punts), 0) + + def test_punt_socket_traffic(self): + """ Punt socket traffic""" + + nr_packets = 2 + p = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) / + inet6.UDP(sport=9876, dport=1234) / + Raw('\xa5' * 100)) + + pkts = p * nr_packets + + punts = self.vapi.punt_socket_dump(1) + self.assertEqual(len(punts), 0) + + # + # expect ICMPv6 - destination unreachable for all packets + # + self.vapi.cli("clear trace") + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = self.pg0.get_capture(nr_packets) + for p in rx: + self.assertEqual(int(p[IPv6].nh), 58) # ICMPv6 + self.assertEqual(int(p[ICMPv6DestUnreach].code), 4) # unreachable + + # + # configure a punt socket + # + self.socket_client_create(self.tempdir+"/socket_punt_1234") + self.vapi.punt_socket_register(1234, self.tempdir+"/socket_punt_1234", + is_ip4=0) + punts = self.vapi.punt_socket_dump(1) + self.assertEqual(len(punts), 1) + + # + # expect punt socket and no packets on pg0 + # + self.vapi.cli("clear errors") + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(0) + self.socket_client_close() + + # + # remove punt socket. expect ICMP - dest. unreachable for all packets + # + self.vapi.punt_socket_deregister(1234, is_ip4=0) + punts = self.vapi.punt_socket_dump(1) + self.assertEqual(len(punts), 0) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + # FIXME - when punt socket deregister is implemented +# self.pg0.get_capture(nr_packets) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 8153e031607..612a678ae9d 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -3486,15 +3486,27 @@ class VppPapiProvider(object): 'namespace_id': namespace_id, 'namespace_id_len': len(namespace_id)}) - def punt_socket_register(self, l4_port, pathname, header_version=1, - is_ip4=1, l4_protocol=0x11): - """ Punt to socket """ + def punt_socket_register(self, port, pathname, protocol=0x11, + header_version=1, is_ip4=1): + """ Register punt socket """ return self.api(self.papi.punt_socket_register, - {'is_ip4': is_ip4, - 'l4_protocol': l4_protocol, - 'l4_port': l4_port, - 'pathname': pathname, - 'header_version': header_version}) + {'header_version': header_version, + 'punt': {'ipv': is_ip4, + 'l4_protocol': protocol, + 'l4_port': port}, + 'pathname': pathname}) + + def punt_socket_deregister(self, port, protocol=0x11, is_ip4=1): + """ Unregister punt socket """ + return self.api(self.papi.punt_socket_deregister, + {'punt': {'ipv': is_ip4, + 'l4_protocol': protocol, + 'l4_port': port}}) + + def punt_socket_dump(self, is_ip6=1): + """ Dump punt socket""" + return self.api(self.papi.punt_socket_dump, + {'is_ipv6': is_ip6}) def ip_reassembly_set(self, timeout_ms, max_reassemblies, expire_walk_interval_ms, is_ip6=0): -- 2.16.6