X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvnet%2Fip%2Fpunt.c;h=6fb8dd58776809e0db7fa696a0f2b5552be6be21;hb=45723b8d3;hp=4855840129208878c5ec4369fc7f7088b90de0dd;hpb=68b0fb0c620c7451ef1a6380c43c39de6614db51;p=vpp.git diff --git a/src/vnet/ip/punt.c b/src/vnet/ip/punt.c index 48558401292..6fb8dd58776 100644 --- a/src/vnet/ip/punt.c +++ b/src/vnet/ip/punt.c @@ -17,273 +17,500 @@ * @file * @brief Local TCP/IP stack punt infrastructure. * - * Provides a set of VPP nodes togather with the relevant APIs and CLI + * Provides a set of VPP nodes together with the relevant APIs and CLI * commands in order to adjust and dispatch packets from the VPP data plane * to the local TCP/IP stack */ + +#include #include -#include #include +#include #include +#include + +#include +#include +#include +#include +#include -#define foreach_punt_next \ - _ (PUNT, "error-punt") +punt_main_t punt_main; -typedef enum +char * +vnet_punt_get_server_pathname (void) { -#define _(s,n) PUNT_NEXT_##s, - foreach_punt_next -#undef _ - PUNT_N_NEXT, -} punt_next_t; + punt_main_t *pm = &punt_main; + return pm->sun_path; +} + +static void +punt_client_l4_db_add (ip_address_family_t af, u16 port, u32 index) +{ + punt_main_t *pm = &punt_main; + + pm->db.clients_by_l4_port = hash_set (pm->db.clients_by_l4_port, + punt_client_l4_mk_key (af, port), + index); +} + +static u32 +punt_client_l4_db_remove (ip_address_family_t af, u16 port) +{ + punt_main_t *pm = &punt_main; + u32 key, index = ~0; + uword *p; + + key = punt_client_l4_mk_key (af, port); + p = hash_get (pm->db.clients_by_l4_port, key); -vlib_node_registration_t udp4_punt_node; -vlib_node_registration_t udp6_punt_node; + if (p) + index = p[0]; -/** @brief IPv4/IPv6 UDP punt node main loop. + hash_unset (pm->db.clients_by_l4_port, key); - This is the main loop inline function for IPv4/IPv6 UDP punt - transition node. + return (index); +} - @param vm vlib_main_t corresponding to the current thread - @param node vlib_node_runtime_t - @param frame vlib_frame_t whose contents should be dispatched - @param is_ipv4 indicates if called for IPv4 or IPv6 node -*/ -always_inline uword -udp46_punt_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * from_frame, int is_ip4) +static void +punt_client_ip_proto_db_add (ip_address_family_t af, + ip_protocol_t proto, u32 index) { - u32 n_left_from, *from, *to_next; - word advance; + punt_main_t *pm = &punt_main; - from = vlib_frame_vector_args (from_frame); - n_left_from = from_frame->n_vectors; + pm->db.clients_by_ip_proto = hash_set (pm->db.clients_by_ip_proto, + punt_client_ip_proto_mk_key (af, + proto), + index); +} - /* udp[46]_lookup hands us the data payload, not the IP header */ - if (is_ip4) - advance = -(sizeof (ip4_header_t) + sizeof (udp_header_t)); - else - advance = -(sizeof (ip6_header_t) + sizeof (udp_header_t)); +static u32 +punt_client_ip_proto_db_remove (ip_address_family_t af, ip_protocol_t proto) +{ + punt_main_t *pm = &punt_main; + u32 key, index = ~0; + uword *p; + + key = punt_client_ip_proto_mk_key (af, proto); + p = hash_get (pm->db.clients_by_ip_proto, key); + + if (p) + index = p[0]; + + hash_unset (pm->db.clients_by_ip_proto, key); - while (n_left_from > 0) + return (index); +} + +static void +punt_client_exception_db_add (vlib_punt_reason_t reason, u32 pci) +{ + punt_main_t *pm = &punt_main; + + vec_validate_init_empty (pm->db.clients_by_exception, reason, ~0); + + pm->db.clients_by_exception[reason] = pci; +} + +static u32 +punt_client_exception_db_remove (vlib_punt_reason_t reason) +{ + punt_main_t *pm = &punt_main; + u32 pci = ~0; + + if (punt_client_exception_get (reason)) { - u32 n_left_to_next; + pci = pm->db.clients_by_exception[reason]; + pm->db.clients_by_exception[reason] = ~0; + } - vlib_get_next_frame (vm, node, PUNT_NEXT_PUNT, to_next, n_left_to_next); + return pci; +} - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - - 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); - vlib_buffer_advance (b0, advance); - b0->error = node->errors[PUNT_ERROR_UDP_PORT]; - } +static clib_error_t * +punt_socket_read_ready (clib_file_t * uf) +{ + vlib_main_t *vm = vlib_get_main (); + punt_main_t *pm = &punt_main; - vlib_put_next_frame (vm, node, PUNT_NEXT_PUNT, n_left_to_next); + /** Schedule the rx node */ + vlib_node_set_interrupt_pending (vm, punt_socket_rx_node.index); + vec_add1 (pm->ready_fds, uf->file_descriptor); + + return 0; +} + +static clib_error_t * +punt_socket_register_l4 (vlib_main_t * vm, + ip_address_family_t af, + u8 protocol, u16 port, char *client_pathname) +{ + punt_main_t *pm = &punt_main; + punt_client_t *c; + + /* For now we only support UDP punt */ + if (protocol != IP_PROTOCOL_UDP) + return clib_error_return (0, + "only UDP protocol (%d) is supported, got %d", + IP_PROTOCOL_UDP, protocol); + + if (port == (u16) ~ 0) + return clib_error_return (0, "UDP port number required"); + + c = punt_client_l4_get (af, port); + + if (NULL == c) + { + pool_get_zero (pm->punt_client_pool, c); + punt_client_l4_db_add (af, port, c - pm->punt_client_pool); } - return from_frame->n_vectors; + memcpy (c->caddr.sun_path, client_pathname, sizeof (c->caddr.sun_path)); + c->caddr.sun_family = AF_UNIX; + c->reg.type = PUNT_TYPE_L4; + c->reg.punt.l4.port = port; + c->reg.punt.l4.protocol = protocol; + c->reg.punt.l4.af = af; + + u32 node_index = (af == AF_IP4 ? + udp4_punt_socket_node.index : + udp6_punt_socket_node.index); + + udp_register_dst_port (vm, port, node_index, af == AF_IP4); + + return (NULL); } -static char *punt_error_strings[] = { -#define punt_error(n,s) s, -#include "punt_error.def" -#undef punt_error -}; +static clib_error_t * +punt_socket_register_ip_proto (vlib_main_t * vm, + ip_address_family_t af, + ip_protocol_t proto, char *client_pathname) +{ + punt_main_t *pm = &punt_main; + punt_client_t *c; -/** @brief IPv4 UDP punt node. - @node ip4-udp-punt + c = punt_client_ip_proto_get (af, proto); - This is the IPv4 UDP punt transition node. It is registered as a next - node for the "ip4-udp-lookup" handling UDP port(s) requested for punt. - The buffer's current data pointer is adjusted to the original packet - IPv4 header. All buffers are dispatched to "error-punt". + if (NULL == c) + { + pool_get_zero (pm->punt_client_pool, c); + punt_client_ip_proto_db_add (af, proto, c - pm->punt_client_pool); + } - @param vm vlib_main_t corresponding to the current thread - @param node vlib_node_runtime_t - @param frame vlib_frame_t whose contents should be dispatched + memcpy (c->caddr.sun_path, client_pathname, sizeof (c->caddr.sun_path)); + c->caddr.sun_family = AF_UNIX; + c->reg.type = PUNT_TYPE_IP_PROTO; + c->reg.punt.ip_proto.protocol = proto; + c->reg.punt.ip_proto.af = af; - @par Graph mechanics: next index usage + if (af == AF_IP4) + ip4_register_protocol (proto, ip4_proto_punt_socket_node.index); + else + ip6_register_protocol (proto, ip6_proto_punt_socket_node.index); - @em Sets: - - vnet_buffer(b)->current_data - - vnet_buffer(b)->current_len + return (NULL); +} - Next Index: - - Dispatches the packet to the "error-punt" node -*/ -static uword -udp4_punt (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * from_frame) +static clib_error_t * +punt_socket_register_exception (vlib_main_t * vm, + vlib_punt_reason_t reason, + char *client_pathname) { - return udp46_punt_inline (vm, node, from_frame, 1 /* is_ip4 */ ); + punt_main_t *pm = &punt_main; + punt_client_t *pc; + + pc = punt_client_exception_get (reason); + + if (NULL == pc) + { + pool_get_zero (pm->punt_client_pool, pc); + punt_client_exception_db_add (reason, pc - pm->punt_client_pool); + } + + memcpy (pc->caddr.sun_path, client_pathname, sizeof (pc->caddr.sun_path)); + pc->caddr.sun_family = AF_UNIX; + pc->reg.type = PUNT_TYPE_EXCEPTION; + pc->reg.punt.exception.reason = reason; + + vlib_punt_register (pm->hdl, + pc->reg.punt.exception.reason, "exception-punt-socket"); + + return (NULL); } -/** @brief IPv6 UDP punt node. - @node ip6-udp-punt +static clib_error_t * +punt_socket_unregister_l4 (ip_address_family_t af, + ip_protocol_t protocol, u16 port) +{ + u32 pci; - This is the IPv6 UDP punt transition node. It is registered as a next - node for the "ip6-udp-lookup" handling UDP port(s) requested for punt. - The buffer's current data pointer is adjusted to the original packet - IPv6 header. All buffers are dispatched to "error-punt". + udp_unregister_dst_port (vlib_get_main (), port, af == AF_IP4); - @param vm vlib_main_t corresponding to the current thread - @param node vlib_node_runtime_t - @param frame vlib_frame_t whose contents should be dispatched + pci = punt_client_l4_db_remove (af, port); - @par Graph mechanics: next index usage + if (~0 != pci) + pool_put_index (punt_main.punt_client_pool, pci); - @em Sets: - - vnet_buffer(b)->current_data - - vnet_buffer(b)->current_len + return (NULL); +} - Next Index: - - Dispatches the packet to the "error-punt" node -*/ -static uword -udp6_punt (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * from_frame) +static clib_error_t * +punt_socket_unregister_ip_proto (ip_address_family_t af, ip_protocol_t proto) { - return udp46_punt_inline (vm, node, from_frame, 0 /* is_ip4 */ ); + u32 pci; + + if (af == AF_IP4) + ip4_unregister_protocol (proto); + else + ip6_unregister_protocol (proto); + + pci = punt_client_ip_proto_db_remove (af, proto); + + if (~0 != pci) + pool_put_index (punt_main.punt_client_pool, pci); + + return (NULL); } -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (udp4_punt_node) = { - .function = udp4_punt, - .name = "ip4-udp-punt", - /* Takes a vector of packets. */ - .vector_size = sizeof (u32), - - .n_errors = PUNT_N_ERROR, - .error_strings = punt_error_strings, - - .n_next_nodes = PUNT_N_NEXT, - .next_nodes = { -#define _(s,n) [PUNT_NEXT_##s] = n, - foreach_punt_next -#undef _ - }, -}; +static clib_error_t * +punt_socket_unregister_exception (vlib_punt_reason_t reason) +{ + u32 pci; -VLIB_NODE_FUNCTION_MULTIARCH (udp4_punt_node, udp4_punt); + pci = punt_client_exception_db_remove (reason); -VLIB_REGISTER_NODE (udp6_punt_node) = { - .function = udp6_punt, - .name = "ip6-udp-punt", - /* Takes a vector of packets. */ - .vector_size = sizeof (u32), + if (~0 != pci) + pool_put_index (punt_main.punt_client_pool, pci); - .n_errors = PUNT_N_ERROR, - .error_strings = punt_error_strings, + return (NULL); +} - .n_next_nodes = PUNT_N_NEXT, - .next_nodes = { -#define _(s,n) [PUNT_NEXT_##s] = n, - foreach_punt_next -#undef _ - }, -}; -/* *INDENT-ON* */ +clib_error_t * +vnet_punt_socket_add (vlib_main_t * vm, u32 header_version, + const punt_reg_t * pr, char *client_pathname) +{ + punt_main_t *pm = &punt_main; + + if (!pm->is_configured) + return clib_error_return (0, "socket is not configured"); + + if (header_version != PUNT_PACKETDESC_VERSION) + return clib_error_return (0, "Invalid packet descriptor version"); + + if (strncmp (client_pathname, vnet_punt_get_server_pathname (), + UNIX_PATH_MAX) == 0) + return clib_error_return (0, + "Punt socket: Invalid client path: %s", + client_pathname); + + /* Register client */ + switch (pr->type) + { + case PUNT_TYPE_L4: + return (punt_socket_register_l4 (vm, + pr->punt.l4.af, + pr->punt.l4.protocol, + pr->punt.l4.port, client_pathname)); + case PUNT_TYPE_IP_PROTO: + return (punt_socket_register_ip_proto (vm, + pr->punt.ip_proto.af, + pr->punt.ip_proto.protocol, + client_pathname)); + case PUNT_TYPE_EXCEPTION: + return (punt_socket_register_exception (vm, + pr->punt.exception.reason, + client_pathname)); + } + + return 0; +} + +clib_error_t * +vnet_punt_socket_del (vlib_main_t * vm, const punt_reg_t * pr) +{ + punt_main_t *pm = &punt_main; + + if (!pm->is_configured) + return clib_error_return (0, "socket is not configured"); + + switch (pr->type) + { + case PUNT_TYPE_L4: + return (punt_socket_unregister_l4 (pr->punt.l4.af, + pr->punt.l4.protocol, + pr->punt.l4.port)); + case PUNT_TYPE_IP_PROTO: + return (punt_socket_unregister_ip_proto (pr->punt.ip_proto.af, + pr->punt.ip_proto.protocol)); + case PUNT_TYPE_EXCEPTION: + return (punt_socket_unregister_exception (pr->punt.exception.reason)); + } -VLIB_NODE_FUNCTION_MULTIARCH (udp6_punt_node, udp6_punt);; + return 0; +} /** - * @brief Request IP traffic punt to the local TCP/IP stack. + * @brief Request IP L4 traffic punt to the local TCP/IP stack. * * @em Note * - UDP is the only protocol supported in the current implementation - * - When requesting UDP punt port number(s) must be specified - * - All TCP traffic is currently punted to the host by default * * @param vm vlib_main_t corresponding to the current thread - * @param ipv IP protcol version. - * 4 - IPv4, 6 - IPv6, ~0 for both IPv6 and IPv4 + * @param af IP address family. * @param protocol 8-bits L4 protocol value - * Only value of 17 (UDP) is currently supported - * @param port 16-bits L4 (TCP/IP) port number when applicable + * UDP is 17 + * TCP is 1 + * @param port 16-bits L4 (TCP/IP) port number when applicable (UDP only) * * @returns 0 on success, non-zero value otherwise */ -clib_error_t * -vnet_punt_add_del (vlib_main_t * vm, u8 ipv, u8 protocol, u16 port, - int is_add) +static clib_error_t * +punt_l4_add_del (vlib_main_t * vm, + ip_address_family_t af, + ip_protocol_t protocol, u16 port, bool is_add) { - /* For now we only support UDP punt */ - if (protocol != IP_PROTOCOL_UDP) + /* For now we only support TCP and UDP punt */ + if (protocol != IP_PROTOCOL_UDP && protocol != IP_PROTOCOL_TCP) return clib_error_return (0, - "only UDP protocol (%d) is supported, got %d", - IP_PROTOCOL_UDP, protocol); - - if (ipv != (u8) ~ 0 && ipv != 4 && ipv != 6) - return clib_error_return (0, "IP version must be 4 or 6, got %d", ipv); + "only UDP (%d) and TCP (%d) protocols are supported, got %d", + IP_PROTOCOL_UDP, IP_PROTOCOL_TCP, protocol); if (port == (u16) ~ 0) { - if (ipv == 4 || ipv == (u8) ~ 0) - udp_punt_unknown (vm, 1, is_add); - - if (ipv == 6 || ipv == (u8) ~ 0) - udp_punt_unknown (vm, 0, is_add); + if (protocol == IP_PROTOCOL_UDP) + udp_punt_unknown (vm, af == AF_IP4, is_add); + else if (protocol == IP_PROTOCOL_TCP) + tcp_punt_unknown (vm, af == AF_IP4, is_add); return 0; } else if (is_add) { - if (ipv == 4 || ipv == (u8) ~ 0) - udp_register_dst_port (vm, port, udp4_punt_node.index, 1); + if (protocol == IP_PROTOCOL_TCP) + return clib_error_return (0, "punt TCP ports is not supported yet"); + + udp_register_dst_port (vm, port, udp4_punt_node.index, af == AF_IP4); + + return 0; + } + else + { + if (protocol == IP_PROTOCOL_TCP) + return clib_error_return (0, "punt TCP ports is not supported yet"); - if (ipv == 6 || ipv == (u8) ~ 0) - udp_register_dst_port (vm, port, udp6_punt_node.index, 0); + udp_unregister_dst_port (vm, port, af == AF_IP4); return 0; } +} +/** + * @brief Request exception traffic punt. + * + * @param reason Punting reason + * + * @returns 0 on success, non-zero value otherwise + */ +static clib_error_t * +punt_exception_add_del (vlib_punt_reason_t reason, bool is_add) +{ + punt_main_t *pm = &punt_main; + int rv = 0; + vnet_punt_reason_flag_t flag = vlib_punt_reason_get_flags (reason); + const char *node_name = + vnet_punt_reason_flag_is_IP6_PACKET (flag) ? "ip6-punt" : "ip4-punt"; + if (is_add) + rv = vlib_punt_register (pm->hdl, reason, node_name); + else + rv = vlib_punt_unregister (pm->hdl, reason, node_name); + if (!rv) + return 0; else - return clib_error_return (0, "punt delete is not supported yet"); + return clib_error_return (0, is_add ? "Existing punting registration..." : + "Punting registration not found..."); +} + +clib_error_t * +vnet_punt_add_del (vlib_main_t * vm, const punt_reg_t * pr, bool is_add) +{ + switch (pr->type) + { + case PUNT_TYPE_L4: + return (punt_l4_add_del (vm, pr->punt.l4.af, pr->punt.l4.protocol, + pr->punt.l4.port, is_add)); + case PUNT_TYPE_EXCEPTION: + return punt_exception_add_del (pr->punt.exception.reason, is_add); + case PUNT_TYPE_IP_PROTO: + break; + } + + return (clib_error_return (0, "Unsupported punt type: %d", pr->type)); } static clib_error_t * -udp_punt_cli (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) +punt_cli (vlib_main_t * vm, + unformat_input_t * input__, vlib_cli_command_t * cmd) { - u32 udp_port; - int is_add = 1; - clib_error_t *error; + unformat_input_t line_input, *input = &line_input; + clib_error_t *error = NULL; + bool is_add = true; + /* *INDENT-OFF* */ + punt_reg_t pr = { + .punt = { + .l4 = { + .af = AF_IP4, + .port = ~0, + .protocol = IP_PROTOCOL_UDP, + }, + }, + .type = PUNT_TYPE_L4, + }; + u32 port; + /* *INDENT-ON* */ + + if (!unformat_user (input__, unformat_line_input, input)) + return 0; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "del")) - is_add = 0; - if (unformat (input, "all")) - { - /* punt both IPv6 and IPv4 when used in CLI */ - error = vnet_punt_add_del (vm, ~0, IP_PROTOCOL_UDP, ~0, is_add); - if (error) - clib_error_report (error); - } - else if (unformat (input, "%d", &udp_port)) + is_add = false; + else if (unformat (input, "reason %U", unformat_punt_reason, + &pr.punt.exception.reason)) + pr.type = PUNT_TYPE_EXCEPTION; + else if (unformat (input, "ipv4")) + pr.punt.l4.af = AF_IP4; + else if (unformat (input, "ipv6")) + pr.punt.l4.af = AF_IP6; + else if (unformat (input, "ip6")) + pr.punt.l4.af = AF_IP6; + else if (unformat (input, "%d", &port)) + pr.punt.l4.port = port; + else if (unformat (input, "all")) + pr.punt.l4.port = ~0; + else if (unformat (input, "udp")) + pr.punt.l4.protocol = IP_PROTOCOL_UDP; + else if (unformat (input, "tcp")) + pr.punt.l4.protocol = IP_PROTOCOL_TCP; + else { - /* punt both IPv6 and IPv4 when used in CLI */ - error = vnet_punt_add_del (vm, ~0, IP_PROTOCOL_UDP, - udp_port, is_add); - if (error) - clib_error_report (error); + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, input); + goto done; } } - return 0; + /* punt both IPv6 and IPv4 when used in CLI */ + error = vnet_punt_add_del (vm, &pr, is_add); + if (error) + { + clib_error_report (error); + } + +done: + unformat_free (input); + return error; } /*? @@ -307,13 +534,394 @@ udp_punt_cli (vlib_main_t * vm, * @endparblock ?*/ /* *INDENT-OFF* */ -VLIB_CLI_COMMAND (punt_udp_command, static) = { - .path = "set punt udp", - .short_help = "set punt udp [del] ", - .function = udp_punt_cli, +VLIB_CLI_COMMAND (punt_command, static) = { + .path = "set punt", + .short_help = "set punt [IPV4|ip6|ipv6] [UDP|tcp] [del] [ALL|]", + .function = punt_cli, }; /* *INDENT-ON* */ +static clib_error_t * +punt_socket_register_cmd (vlib_main_t * vm, + unformat_input_t * input__, + vlib_cli_command_t * cmd) +{ + unformat_input_t line_input, *input = &line_input; + u8 *socket_name = 0; + clib_error_t *error = NULL; + /* *INDENT-OFF* */ + punt_reg_t pr = { + .punt = { + .l4 = { + .af = AF_IP4, + .port = ~0, + .protocol = IP_PROTOCOL_UDP, + }, + }, + .type = PUNT_TYPE_L4, + }; + /* *INDENT-ON* */ + + if (!unformat_user (input__, unformat_line_input, input)) + return 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "ipv4")) + pr.punt.l4.af = AF_IP4; + else if (unformat (input, "ipv6")) + pr.punt.l4.af = AF_IP6; + else if (unformat (input, "udp")) + pr.punt.l4.protocol = IP_PROTOCOL_UDP; + else if (unformat (input, "tcp")) + pr.punt.l4.protocol = IP_PROTOCOL_TCP; + else if (unformat (input, "%d", &pr.punt.l4.port)) + ; + else if (unformat (input, "all")) + pr.punt.l4.port = ~0; + else if (unformat (input, "socket %s", &socket_name)) + ; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, input); + goto done; + } + } + + if (!socket_name) + error = clib_error_return (0, "socket name not specified"); + else + error = vnet_punt_socket_add (vm, 1, &pr, (char *) socket_name); + +done: + unformat_free (input); + return error; +} + +/*? + * + * @cliexpar + * @cliexcmd{punt socket register socket punt_l4_foo.sock} + + ?*/ +/* *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] [ALL|] socket ", + .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) +{ + unformat_input_t line_input, *input = &line_input; + clib_error_t *error = NULL; + /* *INDENT-OFF* */ + punt_reg_t pr = { + .punt = { + .l4 = { + .af = AF_IP4, + .port = ~0, + .protocol = IP_PROTOCOL_UDP, + }, + }, + .type = PUNT_TYPE_L4, + }; + /* *INDENT-ON* */ + + if (!unformat_user (input__, unformat_line_input, input)) + return 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "ipv4")) + pr.punt.l4.af = AF_IP4; + else if (unformat (input, "ipv6")) + pr.punt.l4.af = AF_IP6; + else if (unformat (input, "udp")) + pr.punt.l4.protocol = IP_PROTOCOL_UDP; + else if (unformat (input, "tcp")) + pr.punt.l4.protocol = IP_PROTOCOL_TCP; + else if (unformat (input, "%d", &pr.punt.l4.port)) + ; + else if (unformat (input, "all")) + pr.punt.l4.port = ~0; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, input); + goto done; + } + } + + error = vnet_punt_socket_del (vm, &pr); +done: + unformat_free (input); + 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] [ALL|]", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +void +punt_client_walk (punt_type_t pt, punt_client_walk_cb_t cb, void *ctx) +{ + punt_main_t *pm = &punt_main; + + switch (pt) + { + case PUNT_TYPE_L4: + { + u32 pci, key; + + /* *INDENT-OFF* */ + hash_foreach(key, pci, pm->db.clients_by_l4_port, + ({ + cb (pool_elt_at_index(pm->punt_client_pool, pci), ctx); + })); + /* *INDENT-ON* */ + break; + } + case PUNT_TYPE_IP_PROTO: + { + u32 pci, key; + + /* *INDENT-OFF* */ + hash_foreach(key, pci, pm->db.clients_by_ip_proto, + ({ + cb (pool_elt_at_index(pm->punt_client_pool, pci), ctx); + })); + /* *INDENT-ON* */ + break; + } + case PUNT_TYPE_EXCEPTION: + { + u32 *pci; + + vec_foreach (pci, pm->db.clients_by_exception) + { + if (~0 != *pci) + cb (pool_elt_at_index (pm->punt_client_pool, *pci), ctx); + } + + break; + } + } +} + +static u8 * +format_punt_client (u8 * s, va_list * args) +{ + punt_client_t *pc = va_arg (*args, punt_client_t *); + + s = format (s, " punt "); + + switch (pc->reg.type) + { + case PUNT_TYPE_L4: + s = format (s, "%U %U port %d", + format_ip_address_family, pc->reg.punt.l4.af, + format_ip_protocol, pc->reg.punt.l4.protocol, + pc->reg.punt.l4.port); + break; + case PUNT_TYPE_IP_PROTO: + s = format (s, "%U %U", + format_ip_address_family, pc->reg.punt.ip_proto.af, + format_ip_protocol, pc->reg.punt.ip_proto.protocol); + break; + case PUNT_TYPE_EXCEPTION: + s = format (s, " %U", format_vlib_punt_reason, + pc->reg.punt.exception.reason); + break; + } + + s = format (s, " to socket %s \n", pc->caddr.sun_path); + + return (s); +} + +static walk_rc_t +punt_client_show_one (const punt_client_t * pc, void *ctx) +{ + vlib_cli_output (ctx, "%U", format_punt_client, pc); + + return (WALK_CONTINUE); +} + +static clib_error_t * +punt_socket_show_cmd (vlib_main_t * vm, + unformat_input_t * input__, vlib_cli_command_t * cmd) +{ + unformat_input_t line_input, *input = &line_input; + clib_error_t *error = NULL; + punt_type_t pt; + + pt = PUNT_TYPE_L4; + + if (!unformat_user (input__, unformat_line_input, input)) + return 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "exception")) + pt = PUNT_TYPE_EXCEPTION; + else if (unformat (input, "l4")) + pt = PUNT_TYPE_L4; + else if (unformat (input, "ip")) + pt = PUNT_TYPE_IP_PROTO; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, input); + goto done; + } + } + + punt_client_walk (pt, punt_client_show_one, vm); + +done: + unformat_free (input); + 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 [l4|exception]", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +clib_error_t * +ip_punt_init (vlib_main_t * vm) +{ + clib_error_t *error = NULL; + punt_main_t *pm = &punt_main; + vlib_thread_main_t *tm = vlib_get_thread_main (); + + pm->is_configured = false; + pm->interface_output_node = + vlib_get_node_by_name (vm, (u8 *) "interface-output"); + + if ((error = vlib_call_init_function (vm, punt_init))) + return error; + + pm->hdl = vlib_punt_client_register ("ip-punt"); + + vec_validate_aligned (pm->thread_data, tm->n_vlib_mains, + CLIB_CACHE_LINE_BYTES); + + return (error); +} + +u8 * +format_vnet_punt_reason_flags (u8 *s, va_list *args) +{ + vnet_punt_reason_flag_t flag = va_arg (*args, int); +#define _(pos, len, value, name, str) \ + if (vnet_punt_reason_flag_is_##name (flag)) \ + s = format (s, "%s ", str); + + foreach_vnet_punt_reason_flag +#undef _ + return (s); +} + +VLIB_INIT_FUNCTION (ip_punt_init); + +static clib_error_t * +punt_config (vlib_main_t * vm, unformat_input_t * input) +{ + punt_main_t *pm = &punt_main; + char *socket_path = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "socket %s", &socket_path)) + strncpy (pm->sun_path, socket_path, UNIX_PATH_MAX - 1); + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + } + + if (socket_path == 0) + return 0; + + /* UNIX domain socket */ + struct sockaddr_un addr; + if ((pm->socket_fd = socket (AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) + { + return clib_error_return (0, "socket error"); + } + + clib_memset (&addr, 0, sizeof (addr)); + addr.sun_family = AF_UNIX; + if (*socket_path == '\0') + { + *addr.sun_path = '\0'; + strncpy (addr.sun_path + 1, socket_path + 1, + sizeof (addr.sun_path) - 2); + } + else + { + strncpy (addr.sun_path, socket_path, sizeof (addr.sun_path) - 1); + unlink (socket_path); + } + + if (bind (pm->socket_fd, (struct sockaddr *) &addr, sizeof (addr)) == -1) + { + return clib_error_return (0, "bind error"); + } + + int n_bytes = 0x10000; + + if (setsockopt + (pm->socket_fd, SOL_SOCKET, SO_SNDBUF, &n_bytes, + sizeof (n_bytes)) == -1) + { + return clib_error_return (0, "setsockopt error"); + } + + /* Register socket */ + clib_file_main_t *fm = &file_main; + clib_file_t template = { 0 }; + template.read_function = punt_socket_read_ready; + template.file_descriptor = pm->socket_fd; + template.description = format (0, "punt socket %s", socket_path); + pm->clib_file_index = clib_file_add (fm, &template); + + pm->is_configured = true; + + return 0; +} + +VLIB_CONFIG_FUNCTION (punt_config, "punt"); + /* * fd.io coding-style-patch-verification: ON *