LCOV - code coverage report
Current view: top level - vnet/ip - punt.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 249 401 62.1 %
Date: 2023-10-26 01:39:38 Functions: 36 41 87.8 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : 
      16             : /**
      17             :  * @file
      18             :  * @brief Local TCP/IP stack punt infrastructure.
      19             :  *
      20             :  * Provides a set of VPP nodes together with the relevant APIs and CLI
      21             :  * commands in order to adjust and dispatch packets from the VPP data plane
      22             :  * to the local TCP/IP stack
      23             :  */
      24             : 
      25             : #include <vnet/ip/ip.h>
      26             : #include <vlib/vlib.h>
      27             : #include <vnet/udp/udp.h>
      28             : #include <vnet/tcp/tcp.h>
      29             : #include <vnet/ip/punt.h>
      30             : #include <vlib/unix/unix.h>
      31             : 
      32             : #include <stdio.h>
      33             : #include <unistd.h>
      34             : #include <sys/socket.h>
      35             : #include <sys/uio.h>
      36             : #include <stdlib.h>
      37             : 
      38             : punt_main_t punt_main;
      39             : 
      40             : char *
      41          70 : vnet_punt_get_server_pathname (void)
      42             : {
      43          70 :   punt_main_t *pm = &punt_main;
      44          70 :   return pm->sun_path;
      45             : }
      46             : 
      47             : static void
      48          26 : punt_client_l4_db_add (ip_address_family_t af, u16 port, u32 index)
      49             : {
      50          26 :   punt_main_t *pm = &punt_main;
      51             : 
      52          26 :   pm->db.clients_by_l4_port = hash_set (pm->db.clients_by_l4_port,
      53             :                                         punt_client_l4_mk_key (af, port),
      54             :                                         index);
      55          26 : }
      56             : 
      57             : static u32
      58          27 : punt_client_l4_db_remove (ip_address_family_t af, u16 port)
      59             : {
      60          27 :   punt_main_t *pm = &punt_main;
      61          27 :   u32 key, index = ~0;
      62             :   uword *p;
      63             : 
      64          27 :   key = punt_client_l4_mk_key (af, port);
      65          27 :   p = hash_get (pm->db.clients_by_l4_port, key);
      66             : 
      67          27 :   if (p)
      68          26 :     index = p[0];
      69             : 
      70          27 :   hash_unset (pm->db.clients_by_l4_port, key);
      71             : 
      72          27 :   return (index);
      73             : }
      74             : 
      75             : static void
      76           4 : punt_client_ip_proto_db_add (ip_address_family_t af,
      77             :                              ip_protocol_t proto, u32 index)
      78             : {
      79           4 :   punt_main_t *pm = &punt_main;
      80             : 
      81           4 :   pm->db.clients_by_ip_proto = hash_set (pm->db.clients_by_ip_proto,
      82             :                                          punt_client_ip_proto_mk_key (af,
      83             :                                                                       proto),
      84             :                                          index);
      85           4 : }
      86             : 
      87             : static u32
      88           4 : punt_client_ip_proto_db_remove (ip_address_family_t af, ip_protocol_t proto)
      89             : {
      90           4 :   punt_main_t *pm = &punt_main;
      91           4 :   u32 key, index = ~0;
      92             :   uword *p;
      93             : 
      94           4 :   key = punt_client_ip_proto_mk_key (af, proto);
      95           4 :   p = hash_get (pm->db.clients_by_ip_proto, key);
      96             : 
      97           4 :   if (p)
      98           4 :     index = p[0];
      99             : 
     100           4 :   hash_unset (pm->db.clients_by_ip_proto, key);
     101             : 
     102           4 :   return (index);
     103             : }
     104             : 
     105             : static void
     106           5 : punt_client_exception_db_add (vlib_punt_reason_t reason, u32 pci)
     107             : {
     108           5 :   punt_main_t *pm = &punt_main;
     109             : 
     110           9 :   vec_validate_init_empty (pm->db.clients_by_exception, reason, ~0);
     111             : 
     112           5 :   pm->db.clients_by_exception[reason] = pci;
     113           5 : }
     114             : 
     115             : static u32
     116           5 : punt_client_exception_db_remove (vlib_punt_reason_t reason)
     117             : {
     118           5 :   punt_main_t *pm = &punt_main;
     119           5 :   u32 pci = ~0;
     120             : 
     121           5 :   if (punt_client_exception_get (reason))
     122             :     {
     123           5 :       pci = pm->db.clients_by_exception[reason];
     124           5 :       pm->db.clients_by_exception[reason] = ~0;
     125             :     }
     126             : 
     127           5 :   return pci;
     128             : }
     129             : 
     130             : static clib_error_t *
     131           0 : punt_socket_read_ready (clib_file_t * uf)
     132             : {
     133           0 :   vlib_main_t *vm = vlib_get_main ();
     134           0 :   punt_main_t *pm = &punt_main;
     135             : 
     136             :   /** Schedule the rx node */
     137           0 :   vlib_node_set_interrupt_pending (vm, punt_socket_rx_node.index);
     138           0 :   vec_add1 (pm->ready_fds, uf->file_descriptor);
     139             : 
     140           0 :   return 0;
     141             : }
     142             : 
     143             : static clib_error_t *
     144          26 : punt_socket_register_l4 (vlib_main_t * vm,
     145             :                          ip_address_family_t af,
     146             :                          u8 protocol, u16 port, char *client_pathname)
     147             : {
     148          26 :   punt_main_t *pm = &punt_main;
     149             :   punt_client_t *c;
     150             : 
     151          26 :   if (port == (u16) ~ 0)
     152           0 :     return clib_error_return (0, "Port number required");
     153             : 
     154             :   u32 node_index;
     155          26 :   switch (protocol)
     156             :     {
     157          26 :     case IP_PROTOCOL_UDP:
     158          26 :       node_index = (af == AF_IP4 ? udp4_punt_socket_node.index :
     159             :                                          udp6_punt_socket_node.index);
     160          26 :       udp_register_dst_port (vm, port, node_index, af == AF_IP4);
     161          26 :       break;
     162           0 :     case IP_PROTOCOL_ICMP6:
     163           0 :       if (af != AF_IP6)
     164           0 :         return clib_error_return (
     165             :           0, "only UDP or ICMP6 protocol (%d, %d) is supported, got %d",
     166             :           IP_PROTOCOL_UDP, IP_PROTOCOL_ICMP6, protocol);
     167             : 
     168           0 :       node_index = icmp6_punt_socket_node.index;
     169           0 :       icmp6_register_type (vm, port, node_index);
     170           0 :       break;
     171           0 :     default:
     172           0 :       return clib_error_return (
     173             :         0, "only UDP or ICMP6 protocol (%d) is supported, got %d",
     174             :         IP_PROTOCOL_UDP, protocol);
     175             :     }
     176             : 
     177          26 :   c = punt_client_l4_get (af, port);
     178             : 
     179          26 :   if (NULL == c)
     180             :     {
     181          26 :       pool_get_zero (pm->punt_client_pool, c);
     182          26 :       punt_client_l4_db_add (af, port, c - pm->punt_client_pool);
     183             :     }
     184             : 
     185          26 :   snprintf (c->caddr.sun_path, sizeof (c->caddr.sun_path), "%s",
     186             :             client_pathname);
     187          26 :   c->caddr.sun_family = AF_UNIX;
     188          26 :   c->reg.type = PUNT_TYPE_L4;
     189          26 :   c->reg.punt.l4.port = port;
     190          26 :   c->reg.punt.l4.protocol = protocol;
     191          26 :   c->reg.punt.l4.af = af;
     192             : 
     193          26 :   return (NULL);
     194             : }
     195             : 
     196             : static clib_error_t *
     197           4 : punt_socket_register_ip_proto (vlib_main_t * vm,
     198             :                                ip_address_family_t af,
     199             :                                ip_protocol_t proto, char *client_pathname)
     200             : {
     201           4 :   punt_main_t *pm = &punt_main;
     202             :   punt_client_t *c;
     203             : 
     204           4 :   c = punt_client_ip_proto_get (af, proto);
     205             : 
     206           4 :   if (NULL == c)
     207             :     {
     208           4 :       pool_get_zero (pm->punt_client_pool, c);
     209           4 :       punt_client_ip_proto_db_add (af, proto, c - pm->punt_client_pool);
     210             :     }
     211             : 
     212           4 :   snprintf (c->caddr.sun_path, sizeof (c->caddr.sun_path), "%s",
     213             :             client_pathname);
     214           4 :   c->caddr.sun_family = AF_UNIX;
     215           4 :   c->reg.type = PUNT_TYPE_IP_PROTO;
     216           4 :   c->reg.punt.ip_proto.protocol = proto;
     217           4 :   c->reg.punt.ip_proto.af = af;
     218             : 
     219           4 :   if (af == AF_IP4)
     220           4 :     ip4_register_protocol (proto, ip4_proto_punt_socket_node.index);
     221             :   else
     222           0 :     ip6_register_protocol (proto, ip6_proto_punt_socket_node.index);
     223             : 
     224           4 :   return (NULL);
     225             : }
     226             : 
     227             : static clib_error_t *
     228           5 : punt_socket_register_exception (vlib_main_t * vm,
     229             :                                 vlib_punt_reason_t reason,
     230             :                                 char *client_pathname)
     231             : {
     232           5 :   punt_main_t *pm = &punt_main;
     233             :   punt_client_t *pc;
     234             : 
     235           5 :   pc = punt_client_exception_get (reason);
     236             : 
     237           5 :   if (NULL == pc)
     238             :     {
     239           5 :       pool_get_zero (pm->punt_client_pool, pc);
     240           5 :       punt_client_exception_db_add (reason, pc - pm->punt_client_pool);
     241             :     }
     242             : 
     243           5 :   snprintf (pc->caddr.sun_path, sizeof (pc->caddr.sun_path), "%s",
     244             :             client_pathname);
     245           5 :   pc->caddr.sun_family = AF_UNIX;
     246           5 :   pc->reg.type = PUNT_TYPE_EXCEPTION;
     247           5 :   pc->reg.punt.exception.reason = reason;
     248             : 
     249           5 :   vlib_punt_register (pm->hdl,
     250           5 :                       pc->reg.punt.exception.reason, "exception-punt-socket");
     251             : 
     252           5 :   return (NULL);
     253             : }
     254             : 
     255             : static clib_error_t *
     256          27 : punt_socket_unregister_l4 (ip_address_family_t af,
     257             :                            ip_protocol_t protocol, u16 port)
     258             : {
     259             :   u32 pci;
     260             : 
     261          27 :   udp_unregister_dst_port (vlib_get_main (), port, af == AF_IP4);
     262             : 
     263          27 :   pci = punt_client_l4_db_remove (af, port);
     264             : 
     265          27 :   if (~0 != pci)
     266          26 :     pool_put_index (punt_main.punt_client_pool, pci);
     267             : 
     268          27 :   return (NULL);
     269             : }
     270             : 
     271             : static clib_error_t *
     272           4 : punt_socket_unregister_ip_proto (ip_address_family_t af, ip_protocol_t proto)
     273             : {
     274             :   u32 pci;
     275             : 
     276           4 :   if (af == AF_IP4)
     277           4 :     ip4_unregister_protocol (proto);
     278             :   else
     279           0 :     ip6_unregister_protocol (proto);
     280             : 
     281           4 :   pci = punt_client_ip_proto_db_remove (af, proto);
     282             : 
     283           4 :   if (~0 != pci)
     284           4 :     pool_put_index (punt_main.punt_client_pool, pci);
     285             : 
     286           4 :   return (NULL);
     287             : }
     288             : 
     289             : static clib_error_t *
     290           5 : punt_socket_unregister_exception (vlib_punt_reason_t reason)
     291             : {
     292             :   u32 pci;
     293             : 
     294           5 :   pci = punt_client_exception_db_remove (reason);
     295             : 
     296           5 :   if (~0 != pci)
     297           5 :     pool_put_index (punt_main.punt_client_pool, pci);
     298             : 
     299           5 :   return (NULL);
     300             : }
     301             : 
     302             : clib_error_t *
     303          35 : vnet_punt_socket_add (vlib_main_t * vm, u32 header_version,
     304             :                       const punt_reg_t * pr, char *client_pathname)
     305             : {
     306          35 :   punt_main_t *pm = &punt_main;
     307             : 
     308          35 :   if (!pm->is_configured)
     309           0 :     return clib_error_return (0, "socket is not configured");
     310             : 
     311          35 :   if (header_version != PUNT_PACKETDESC_VERSION)
     312           0 :     return clib_error_return (0, "Invalid packet descriptor version");
     313             : 
     314          35 :   if (strncmp (client_pathname, vnet_punt_get_server_pathname (),
     315             :                UNIX_PATH_MAX) == 0)
     316           0 :     return clib_error_return (0,
     317             :                               "Punt socket: Invalid client path: %s",
     318             :                               client_pathname);
     319             : 
     320             :   /* Register client */
     321          35 :   switch (pr->type)
     322             :     {
     323          26 :     case PUNT_TYPE_L4:
     324          26 :       return (punt_socket_register_l4 (vm,
     325          26 :                                        pr->punt.l4.af,
     326          26 :                                        pr->punt.l4.protocol,
     327          26 :                                        pr->punt.l4.port, client_pathname));
     328           4 :     case PUNT_TYPE_IP_PROTO:
     329           4 :       return (punt_socket_register_ip_proto (vm,
     330           4 :                                              pr->punt.ip_proto.af,
     331           4 :                                              pr->punt.ip_proto.protocol,
     332             :                                              client_pathname));
     333           5 :     case PUNT_TYPE_EXCEPTION:
     334           5 :       return (punt_socket_register_exception (vm,
     335             :                                               pr->punt.exception.reason,
     336             :                                               client_pathname));
     337             :     }
     338             : 
     339           0 :   return 0;
     340             : }
     341             : 
     342             : clib_error_t *
     343          36 : vnet_punt_socket_del (vlib_main_t * vm, const punt_reg_t * pr)
     344             : {
     345          36 :   punt_main_t *pm = &punt_main;
     346             : 
     347          36 :   if (!pm->is_configured)
     348           0 :     return clib_error_return (0, "socket is not configured");
     349             : 
     350          36 :   switch (pr->type)
     351             :     {
     352          27 :     case PUNT_TYPE_L4:
     353          27 :       return (punt_socket_unregister_l4 (pr->punt.l4.af,
     354          27 :                                          pr->punt.l4.protocol,
     355          27 :                                          pr->punt.l4.port));
     356           4 :     case PUNT_TYPE_IP_PROTO:
     357           4 :       return (punt_socket_unregister_ip_proto (pr->punt.ip_proto.af,
     358           4 :                                                pr->punt.ip_proto.protocol));
     359           5 :     case PUNT_TYPE_EXCEPTION:
     360           5 :       return (punt_socket_unregister_exception (pr->punt.exception.reason));
     361             :     }
     362             : 
     363           0 :   return 0;
     364             : }
     365             : 
     366             : /**
     367             :  * @brief Request IP L4 traffic punt to the local TCP/IP stack.
     368             :  *
     369             :  * @em Note
     370             :  * - UDP is the only protocol supported in the current implementation
     371             :  *
     372             :  * @param vm       vlib_main_t corresponding to the current thread
     373             :  * @param af       IP address family.
     374             :  * @param protocol 8-bits L4 protocol value
     375             :  *                 UDP is 17
     376             :  *                 TCP is 1
     377             :  * @param port     16-bits L4 (TCP/IP) port number when applicable (UDP only)
     378             :  *
     379             :  * @returns 0 on success, non-zero value otherwise
     380             :  */
     381             : static clib_error_t *
     382          22 : punt_l4_add_del (vlib_main_t * vm,
     383             :                  ip_address_family_t af,
     384             :                  ip_protocol_t protocol, u16 port, bool is_add)
     385             : {
     386          22 :   int is_ip4 = af == AF_IP4;
     387             : 
     388             :   /* For now we only support TCP and UDP punt */
     389          22 :   if (protocol != IP_PROTOCOL_UDP && protocol != IP_PROTOCOL_TCP)
     390           0 :     return clib_error_return (0,
     391             :                               "only UDP (%d) and TCP (%d) protocols are supported, got %d",
     392             :                               IP_PROTOCOL_UDP, IP_PROTOCOL_TCP, protocol);
     393             : 
     394          22 :   if (port == (u16) ~ 0)
     395             :     {
     396           8 :       if (protocol == IP_PROTOCOL_UDP)
     397           4 :         udp_punt_unknown (vm, is_ip4, is_add);
     398           4 :       else if (protocol == IP_PROTOCOL_TCP)
     399           4 :         tcp_punt_unknown (vm, is_ip4, is_add);
     400             : 
     401           8 :       return 0;
     402             :     }
     403             : 
     404          14 :   else if (is_add)
     405             :     {
     406          14 :       const vlib_node_registration_t *punt_node =
     407             :         is_ip4 ? &udp4_punt_node : &udp6_punt_node;
     408             : 
     409          14 :       if (protocol == IP_PROTOCOL_TCP)
     410           0 :         return clib_error_return (0, "punt TCP ports is not supported yet");
     411             : 
     412          14 :       udp_register_dst_port (vm, port, punt_node->index, is_ip4);
     413             : 
     414          14 :       return 0;
     415             :     }
     416             :   else
     417             :     {
     418           0 :       if (protocol == IP_PROTOCOL_TCP)
     419           0 :         return clib_error_return (0, "punt TCP ports is not supported yet");
     420             : 
     421           0 :       udp_unregister_dst_port (vm, port, is_ip4);
     422             : 
     423           0 :       return 0;
     424             :     }
     425             : }
     426             : 
     427             : /**
     428             :  * @brief Request exception traffic punt.
     429             :  *
     430             :  * @param reason   Punting reason
     431             :  *
     432             :  * @returns 0 on success, non-zero value otherwise
     433             :  */
     434             : static clib_error_t *
     435           0 : punt_exception_add_del (vlib_punt_reason_t reason, bool is_add)
     436             : {
     437           0 :   punt_main_t *pm = &punt_main;
     438           0 :   int rv = 0;
     439           0 :   vnet_punt_reason_flag_t flag = vlib_punt_reason_get_flags (reason);
     440           0 :   const char *node_name =
     441           0 :     vnet_punt_reason_flag_is_IP6_PACKET (flag) ? "ip6-punt" : "ip4-punt";
     442           0 :   if (is_add)
     443           0 :     rv = vlib_punt_register (pm->hdl, reason, node_name);
     444             :   else
     445           0 :     rv = vlib_punt_unregister (pm->hdl, reason, node_name);
     446           0 :   if (!rv)
     447           0 :     return 0;
     448             :   else
     449           0 :     return clib_error_return (0, is_add ? "Existing punting registration..." :
     450             :                                           "Punting registration not found...");
     451             : }
     452             : 
     453             : clib_error_t *
     454          22 : vnet_punt_add_del (vlib_main_t * vm, const punt_reg_t * pr, bool is_add)
     455             : {
     456          22 :   switch (pr->type)
     457             :     {
     458          22 :     case PUNT_TYPE_L4:
     459          22 :       return (punt_l4_add_del (vm, pr->punt.l4.af, pr->punt.l4.protocol,
     460          22 :                                pr->punt.l4.port, is_add));
     461           0 :     case PUNT_TYPE_EXCEPTION:
     462           0 :       return punt_exception_add_del (pr->punt.exception.reason, is_add);
     463           0 :     case PUNT_TYPE_IP_PROTO:
     464           0 :       break;
     465             :     }
     466             : 
     467           0 :   return (clib_error_return (0, "Unsupported punt type: %d", pr->type));
     468             : }
     469             : 
     470             : static clib_error_t *
     471           0 : punt_cli (vlib_main_t * vm,
     472             :           unformat_input_t * input__, vlib_cli_command_t * cmd)
     473             : {
     474           0 :   unformat_input_t line_input, *input = &line_input;
     475           0 :   clib_error_t *error = NULL;
     476           0 :   bool is_add = true;
     477             :   /* *INDENT-OFF* */
     478           0 :   punt_reg_t pr = {
     479             :     .punt = {
     480             :       .l4 = {
     481             :         .af = AF_IP4,
     482             :         .port = ~0,
     483             :         .protocol = IP_PROTOCOL_UDP,
     484             :       },
     485             :     },
     486             :     .type = PUNT_TYPE_L4,
     487             :   };
     488             :   u32 port;
     489             :   /* *INDENT-ON* */
     490             : 
     491           0 :   if (!unformat_user (input__, unformat_line_input, input))
     492           0 :     return 0;
     493             : 
     494           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     495             :     {
     496           0 :       if (unformat (input, "del"))
     497           0 :         is_add = false;
     498           0 :       else if (unformat (input, "reason %U", unformat_punt_reason,
     499             :                          &pr.punt.exception.reason))
     500           0 :         pr.type = PUNT_TYPE_EXCEPTION;
     501           0 :       else if (unformat (input, "ipv4"))
     502           0 :         pr.punt.l4.af = AF_IP4;
     503           0 :       else if (unformat (input, "ipv6"))
     504           0 :         pr.punt.l4.af = AF_IP6;
     505           0 :       else if (unformat (input, "ip6"))
     506           0 :         pr.punt.l4.af = AF_IP6;
     507           0 :       else if (unformat (input, "%d", &port))
     508           0 :         pr.punt.l4.port = port;
     509           0 :       else if (unformat (input, "all"))
     510           0 :         pr.punt.l4.port = ~0;
     511           0 :       else if (unformat (input, "udp"))
     512           0 :         pr.punt.l4.protocol = IP_PROTOCOL_UDP;
     513           0 :       else if (unformat (input, "tcp"))
     514           0 :         pr.punt.l4.protocol = IP_PROTOCOL_TCP;
     515             :       else
     516             :         {
     517           0 :           error = clib_error_return (0, "parse error: '%U'",
     518             :                                      format_unformat_error, input);
     519           0 :           goto done;
     520             :         }
     521             :     }
     522             : 
     523             :   /* punt both IPv6 and IPv4 when used in CLI */
     524           0 :   error = vnet_punt_add_del (vm, &pr, is_add);
     525           0 :   if (error)
     526             :     {
     527           0 :       clib_error_report (error);
     528             :     }
     529             : 
     530           0 : done:
     531           0 :   unformat_free (input);
     532           0 :   return error;
     533             : }
     534             : 
     535             : /*?
     536             :  * The set of '<em>set punt</em>' commands allows specific IP traffic to
     537             :  * be punted to the host TCP/IP stack
     538             :  *
     539             :  * @em Note
     540             :  * - UDP is the only protocol supported in the current implementation
     541             :  * - All TCP traffic is currently punted to the host by default
     542             :  *
     543             :  * @cliexpar
     544             :  * @parblock
     545             :  * Example of how to request NTP traffic to be punted
     546             :  * @cliexcmd{set punt udp 125}
     547             :  *
     548             :  * Example of how to request all 'unknown' UDP traffic to be punted
     549             :  * @cliexcmd{set punt udp all}
     550             :  *
     551             :  * Example of how to stop all 'unknown' UDP traffic to be punted
     552             :  * @cliexcmd{set punt udp del all}
     553             :  * @endparblock
     554             : ?*/
     555             : /* *INDENT-OFF* */
     556      285289 : VLIB_CLI_COMMAND (punt_command, static) = {
     557             :   .path = "set punt",
     558             :   .short_help = "set punt [IPV4|ip6|ipv6] [UDP|tcp] [del] [ALL|<port-num>]",
     559             :   .function = punt_cli,
     560             : };
     561             : /* *INDENT-ON* */
     562             : 
     563             : static clib_error_t *
     564           0 : punt_socket_register_cmd (vlib_main_t * vm,
     565             :                           unformat_input_t * input__,
     566             :                           vlib_cli_command_t * cmd)
     567             : {
     568           0 :   unformat_input_t line_input, *input = &line_input;
     569           0 :   u8 *socket_name = 0;
     570           0 :   clib_error_t *error = NULL;
     571             :   /* *INDENT-OFF* */
     572           0 :   punt_reg_t pr = {
     573             :     .punt = {
     574             :       .l4 = {
     575             :         .af = AF_IP4,
     576             :         .port = ~0,
     577             :         .protocol = IP_PROTOCOL_UDP,
     578             :       },
     579             :     },
     580             :     .type = PUNT_TYPE_L4,
     581             :   };
     582             :   /* *INDENT-ON* */
     583             : 
     584           0 :   if (!unformat_user (input__, unformat_line_input, input))
     585           0 :     return 0;
     586             : 
     587           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     588             :     {
     589           0 :       if (unformat (input, "ipv4"))
     590           0 :         pr.punt.l4.af = AF_IP4;
     591           0 :       else if (unformat (input, "ipv6"))
     592           0 :         pr.punt.l4.af = AF_IP6;
     593           0 :       else if (unformat (input, "udp"))
     594           0 :         pr.punt.l4.protocol = IP_PROTOCOL_UDP;
     595           0 :       else if (unformat (input, "tcp"))
     596           0 :         pr.punt.l4.protocol = IP_PROTOCOL_TCP;
     597           0 :       else if (unformat (input, "%d", &pr.punt.l4.port))
     598             :         ;
     599           0 :       else if (unformat (input, "all"))
     600           0 :         pr.punt.l4.port = ~0;
     601           0 :       else if (unformat (input, "socket %s", &socket_name))
     602             :         ;
     603           0 :       else if (unformat (input, "reason %U", unformat_punt_reason,
     604             :                          &pr.punt.exception.reason))
     605           0 :         pr.type = PUNT_TYPE_EXCEPTION;
     606             :       else
     607             :         {
     608           0 :           error = clib_error_return (0, "parse error: '%U'",
     609             :                                      format_unformat_error, input);
     610           0 :           goto done;
     611             :         }
     612             :     }
     613             : 
     614           0 :   if (!socket_name)
     615           0 :     error = clib_error_return (0, "socket name not specified");
     616             :   else
     617           0 :     error = vnet_punt_socket_add (vm, 1, &pr, (char *) socket_name);
     618             : 
     619           0 : done:
     620           0 :   unformat_free (input);
     621           0 :   return error;
     622             : }
     623             : 
     624             : /*?
     625             :  *
     626             :  * @cliexpar
     627             :  * @cliexcmd{punt socket register socket punt_l4_foo.sock}
     628             : 
     629             :  ?*/
     630             : /* *INDENT-OFF* */
     631      285289 : VLIB_CLI_COMMAND (punt_socket_register_command, static) =
     632             : {
     633             :   .path = "punt socket register",
     634             :   .function = punt_socket_register_cmd,
     635             :   .short_help = "punt socket register [IPV4|ipv6] [UDP|tcp] [ALL|<port-num>] socket <socket>",
     636             :   .is_mp_safe = 1,
     637             : };
     638             : /* *INDENT-ON* */
     639             : 
     640             : static clib_error_t *
     641           0 : punt_socket_deregister_cmd (vlib_main_t * vm,
     642             :                             unformat_input_t * input__,
     643             :                             vlib_cli_command_t * cmd)
     644             : {
     645           0 :   unformat_input_t line_input, *input = &line_input;
     646           0 :   clib_error_t *error = NULL;
     647             :   /* *INDENT-OFF* */
     648           0 :   punt_reg_t pr = {
     649             :     .punt = {
     650             :       .l4 = {
     651             :         .af = AF_IP4,
     652             :         .port = ~0,
     653             :         .protocol = IP_PROTOCOL_UDP,
     654             :       },
     655             :     },
     656             :     .type = PUNT_TYPE_L4,
     657             :   };
     658             :   /* *INDENT-ON* */
     659             : 
     660           0 :   if (!unformat_user (input__, unformat_line_input, input))
     661           0 :     return 0;
     662             : 
     663           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     664             :     {
     665           0 :       if (unformat (input, "ipv4"))
     666           0 :         pr.punt.l4.af = AF_IP4;
     667           0 :       else if (unformat (input, "ipv6"))
     668           0 :         pr.punt.l4.af = AF_IP6;
     669           0 :       else if (unformat (input, "udp"))
     670           0 :         pr.punt.l4.protocol = IP_PROTOCOL_UDP;
     671           0 :       else if (unformat (input, "tcp"))
     672           0 :         pr.punt.l4.protocol = IP_PROTOCOL_TCP;
     673           0 :       else if (unformat (input, "%d", &pr.punt.l4.port))
     674             :         ;
     675           0 :       else if (unformat (input, "all"))
     676           0 :         pr.punt.l4.port = ~0;
     677           0 :       else if (unformat (input, "reason %U", unformat_punt_reason,
     678             :                          &pr.punt.exception.reason))
     679           0 :         pr.type = PUNT_TYPE_EXCEPTION;
     680             :       else
     681             :         {
     682           0 :           error = clib_error_return (0, "parse error: '%U'",
     683             :                                      format_unformat_error, input);
     684           0 :           goto done;
     685             :         }
     686             :     }
     687             : 
     688           0 :   error = vnet_punt_socket_del (vm, &pr);
     689           0 : done:
     690           0 :   unformat_free (input);
     691           0 :   return error;
     692             : }
     693             : 
     694             : /*?
     695             :  *
     696             :  * @cliexpar
     697             :  * @cliexcmd{punt socket register}
     698             :  ?*/
     699             : /* *INDENT-OFF* */
     700      285289 : VLIB_CLI_COMMAND (punt_socket_deregister_command, static) =
     701             : {
     702             :   .path = "punt socket deregister",
     703             :   .function = punt_socket_deregister_cmd,
     704             :   .short_help = "punt socket deregister [IPV4|ipv6] [UDP|tcp] [ALL|<port-num>]",
     705             :   .is_mp_safe = 1,
     706             : };
     707             : /* *INDENT-ON* */
     708             : 
     709             : void
     710          36 : punt_client_walk (punt_type_t pt, punt_client_walk_cb_t cb, void *ctx)
     711             : {
     712          36 :   punt_main_t *pm = &punt_main;
     713             : 
     714          36 :   switch (pt)
     715             :     {
     716          23 :     case PUNT_TYPE_L4:
     717             :       {
     718             :         u32 pci, key;
     719             : 
     720             :         /* *INDENT-OFF* */
     721        1389 :         hash_foreach(key, pci, pm->db.clients_by_l4_port,
     722             :         ({
     723             :           cb (pool_elt_at_index(pm->punt_client_pool, pci), ctx);
     724             :         }));
     725             :         /* *INDENT-ON* */
     726          23 :         break;
     727             :       }
     728           6 :     case PUNT_TYPE_IP_PROTO:
     729             :       {
     730             :         u32 pci, key;
     731             : 
     732             :         /* *INDENT-OFF* */
     733         333 :         hash_foreach(key, pci, pm->db.clients_by_ip_proto,
     734             :         ({
     735             :           cb (pool_elt_at_index(pm->punt_client_pool, pci), ctx);
     736             :         }));
     737             :         /* *INDENT-ON* */
     738           6 :         break;
     739             :       }
     740           7 :     case PUNT_TYPE_EXCEPTION:
     741             :       {
     742             :         u32 *pci;
     743             : 
     744          25 :         vec_foreach (pci, pm->db.clients_by_exception)
     745             :         {
     746          18 :           if (~0 != *pci)
     747           9 :             cb (pool_elt_at_index (pm->punt_client_pool, *pci), ctx);
     748             :         }
     749             : 
     750           7 :         break;
     751             :       }
     752             :     }
     753          36 : }
     754             : 
     755             : static u8 *
     756           5 : format_punt_client (u8 * s, va_list * args)
     757             : {
     758           5 :   punt_client_t *pc = va_arg (*args, punt_client_t *);
     759             : 
     760           5 :   s = format (s, " punt ");
     761             : 
     762           5 :   switch (pc->reg.type)
     763             :     {
     764           0 :     case PUNT_TYPE_L4:
     765           0 :       s = format (s, "%U %U port %d",
     766           0 :                   format_ip_address_family, pc->reg.punt.l4.af,
     767           0 :                   format_ip_protocol, pc->reg.punt.l4.protocol,
     768           0 :                   pc->reg.punt.l4.port);
     769           0 :       break;
     770           2 :     case PUNT_TYPE_IP_PROTO:
     771           2 :       s = format (s, "%U %U",
     772           2 :                   format_ip_address_family, pc->reg.punt.ip_proto.af,
     773           2 :                   format_ip_protocol, pc->reg.punt.ip_proto.protocol);
     774           2 :       break;
     775           3 :     case PUNT_TYPE_EXCEPTION:
     776           3 :       s = format (s, " %U", format_vlib_punt_reason,
     777           3 :                   pc->reg.punt.exception.reason);
     778           3 :       break;
     779             :     }
     780             : 
     781           5 :   s = format (s, " to socket %s \n", pc->caddr.sun_path);
     782             : 
     783           5 :   return (s);
     784             : }
     785             : 
     786             : static walk_rc_t
     787           5 : punt_client_show_one (const punt_client_t * pc, void *ctx)
     788             : {
     789           5 :   vlib_cli_output (ctx, "%U", format_punt_client, pc);
     790             : 
     791           5 :   return (WALK_CONTINUE);
     792             : }
     793             : 
     794             : static clib_error_t *
     795           4 : punt_socket_show_cmd (vlib_main_t * vm,
     796             :                       unformat_input_t * input__, vlib_cli_command_t * cmd)
     797             : {
     798           4 :   unformat_input_t line_input, *input = &line_input;
     799           4 :   clib_error_t *error = NULL;
     800             :   punt_type_t pt;
     801             : 
     802           4 :   pt = PUNT_TYPE_L4;
     803             : 
     804           4 :   if (!unformat_user (input__, unformat_line_input, input))
     805           1 :     return 0;
     806             : 
     807           6 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     808             :     {
     809           3 :       if (unformat (input, "exception"))
     810           2 :         pt = PUNT_TYPE_EXCEPTION;
     811           1 :       else if (unformat (input, "l4"))
     812           0 :         pt = PUNT_TYPE_L4;
     813           1 :       else if (unformat (input, "ip"))
     814           1 :         pt = PUNT_TYPE_IP_PROTO;
     815             :       else
     816             :         {
     817           0 :           error = clib_error_return (0, "parse error: '%U'",
     818             :                                      format_unformat_error, input);
     819           0 :           goto done;
     820             :         }
     821             :     }
     822             : 
     823           3 :   punt_client_walk (pt, punt_client_show_one, vm);
     824             : 
     825           3 : done:
     826           3 :   unformat_free (input);
     827           3 :   return (error);
     828             : }
     829             : 
     830             : /*?
     831             :  *
     832             :  * @cliexpar
     833             :  * @cliexcmd{show punt socket ipv4}
     834             :  ?*/
     835             : /* *INDENT-OFF* */
     836      285289 : VLIB_CLI_COMMAND (show_punt_socket_registration_command, static) =
     837             : {
     838             :   .path = "show punt socket registrations",
     839             :   .function = punt_socket_show_cmd,
     840             :   .short_help = "show punt socket registrations [l4|exception]",
     841             :   .is_mp_safe = 1,
     842             : };
     843             : /* *INDENT-ON* */
     844             : 
     845             : clib_error_t *
     846         575 : ip_punt_init (vlib_main_t * vm)
     847             : {
     848         575 :   clib_error_t *error = NULL;
     849         575 :   punt_main_t *pm = &punt_main;
     850         575 :   vlib_thread_main_t *tm = vlib_get_thread_main ();
     851             : 
     852         575 :   pm->is_configured = false;
     853         575 :   pm->interface_output_node =
     854         575 :     vlib_get_node_by_name (vm, (u8 *) "interface-output");
     855             : 
     856         575 :   if ((error = vlib_call_init_function (vm, punt_init)))
     857           0 :     return error;
     858             : 
     859         575 :   pm->hdl = vlib_punt_client_register ("ip-punt");
     860             : 
     861         575 :   vec_validate_aligned (pm->thread_data, tm->n_vlib_mains,
     862             :                         CLIB_CACHE_LINE_BYTES);
     863             : 
     864         575 :   return (error);
     865             : }
     866             : 
     867             : u8 *
     868        1721 : format_vnet_punt_reason_flags (u8 *s, va_list *args)
     869             : {
     870        1721 :   vnet_punt_reason_flag_t flag = va_arg (*args, int);
     871             : #define _(pos, len, value, name, str)                                         \
     872             :   if (vnet_punt_reason_flag_is_##name (flag))                                 \
     873             :     s = format (s, "%s ", str);
     874             : 
     875        1721 :   foreach_vnet_punt_reason_flag
     876             : #undef _
     877        1721 :     return (s);
     878             : }
     879             : 
     880       47231 : VLIB_INIT_FUNCTION (ip_punt_init);
     881             : 
     882             : static clib_error_t *
     883         575 : punt_config (vlib_main_t * vm, unformat_input_t * input)
     884             : {
     885         575 :   punt_main_t *pm = &punt_main;
     886         575 :   char *socket_path = 0;
     887             : 
     888         580 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     889             :     {
     890           5 :       if (unformat (input, "socket %s", &socket_path))
     891           5 :         strncpy (pm->sun_path, socket_path, UNIX_PATH_MAX - 1);
     892             :       else
     893           0 :         return clib_error_return (0, "unknown input `%U'",
     894             :                                   format_unformat_error, input);
     895             :     }
     896             : 
     897         575 :   if (socket_path == 0)
     898         570 :     return 0;
     899             : 
     900             :   /* UNIX domain socket */
     901             :   struct sockaddr_un addr;
     902           5 :   if ((pm->socket_fd = socket (AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1)
     903             :     {
     904           0 :       return clib_error_return (0, "socket error");
     905             :     }
     906             : 
     907           5 :   clib_memset (&addr, 0, sizeof (addr));
     908           5 :   addr.sun_family = AF_UNIX;
     909           5 :   if (*socket_path == '\0')
     910             :     {
     911           0 :       *addr.sun_path = '\0';
     912           0 :       strncpy (addr.sun_path + 1, socket_path + 1,
     913             :                sizeof (addr.sun_path) - 2);
     914             :     }
     915             :   else
     916             :     {
     917           5 :       strncpy (addr.sun_path, socket_path, sizeof (addr.sun_path) - 1);
     918           5 :       unlink (socket_path);
     919             :     }
     920             : 
     921           5 :   if (bind (pm->socket_fd, (struct sockaddr *) &addr, sizeof (addr)) == -1)
     922             :     {
     923           0 :       return clib_error_return (0, "bind error");
     924             :     }
     925             : 
     926           5 :   int n_bytes = 0x10000;
     927             : 
     928           5 :   if (setsockopt
     929             :       (pm->socket_fd, SOL_SOCKET, SO_SNDBUF, &n_bytes,
     930             :        sizeof (n_bytes)) == -1)
     931             :     {
     932           0 :       return clib_error_return (0, "setsockopt error");
     933             :     }
     934             : 
     935             :   /* Register socket */
     936           5 :   clib_file_main_t *fm = &file_main;
     937           5 :   clib_file_t template = { 0 };
     938           5 :   template.read_function = punt_socket_read_ready;
     939           5 :   template.file_descriptor = pm->socket_fd;
     940           5 :   template.description = format (0, "punt socket %s", socket_path);
     941           5 :   pm->clib_file_index = clib_file_add (fm, &template);
     942             : 
     943           5 :   pm->is_configured = true;
     944             : 
     945           5 :   return 0;
     946             : }
     947             : 
     948        7514 : VLIB_CONFIG_FUNCTION (punt_config, "punt");
     949             : 
     950             : /*
     951             :  * fd.io coding-style-patch-verification: ON
     952             :  *
     953             :  * Local Variables:
     954             :  * eval: (c-set-style "gnu")
     955             :  * End:
     956             :  */

Generated by: LCOV version 1.14