LCOV - code coverage report
Current view: top level - vnet/arp - arp_proxy.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 91 146 62.3 %
Date: 2023-07-05 22:20:52 Functions: 15 20 75.0 %

          Line data    Source code
       1             : /*
       2             :  * ethernet/arp.c: IP v4 ARP node
       3             :  *
       4             :  * Copyright (c) 2010 Cisco and/or its affiliates.
       5             :  * Licensed under the Apache License, Version 2.0 (the "License");
       6             :  * you may not use this file except in compliance with the License.
       7             :  * You may obtain a copy of the License at:
       8             :  *
       9             :  *     http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  * Unless required by applicable law or agreed to in writing, software
      12             :  * distributed under the License is distributed on an "AS IS" BASIS,
      13             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  * See the License for the specific language governing permissions and
      15             :  * limitations under the License.
      16             :  */
      17             : 
      18             : #include <vnet/arp/arp.h>
      19             : #include <vnet/arp/arp_packet.h>
      20             : 
      21             : #include <vnet/fib/ip4_fib.h>
      22             : 
      23             : typedef struct
      24             : {
      25             :   ip4_address_t lo_addr;
      26             :   ip4_address_t hi_addr;
      27             :   u32 fib_index;
      28             : } ethernet_proxy_arp_t;
      29             : 
      30             : typedef struct arp_proxy_main_t_
      31             : {
      32             :   /** Per interface state */
      33             :   bool *enabled_by_sw_if_index;
      34             : 
      35             :   /* Proxy arp vector */
      36             :   ethernet_proxy_arp_t *proxy_arps;
      37             : } arp_proxy_main_t;
      38             : 
      39             : arp_proxy_main_t arp_proxy_main;
      40             : 
      41             : void
      42           0 : proxy_arp_walk (proxy_arp_walk_t cb, void *data)
      43             : {
      44           0 :   arp_proxy_main_t *am = &arp_proxy_main;
      45             :   ethernet_proxy_arp_t *pa;
      46             : 
      47           0 :   vec_foreach (pa, am->proxy_arps)
      48             :   {
      49           0 :     if (!cb (&pa->lo_addr, &pa->hi_addr, pa->fib_index, data))
      50           0 :       break;
      51             :   }
      52           0 : }
      53             : 
      54             : int
      55           4 : arp_proxy_disable (u32 sw_if_index)
      56             : {
      57           4 :   arp_proxy_main_t *am = &arp_proxy_main;
      58             : 
      59           4 :   vec_validate (am->enabled_by_sw_if_index, sw_if_index);
      60             : 
      61           4 :   if (am->enabled_by_sw_if_index[sw_if_index])
      62             :     {
      63           4 :       vnet_feature_enable_disable ("arp", "arp-proxy",
      64             :                                    sw_if_index, 0, NULL, 0);
      65             :     }
      66           4 :   am->enabled_by_sw_if_index[sw_if_index] = false;
      67             : 
      68           4 :   return (0);
      69             : }
      70             : 
      71             : int
      72           5 : arp_proxy_enable (u32 sw_if_index)
      73             : {
      74           5 :   arp_proxy_main_t *am = &arp_proxy_main;
      75             : 
      76           5 :   vec_validate (am->enabled_by_sw_if_index, sw_if_index);
      77             : 
      78           5 :   if (!am->enabled_by_sw_if_index[sw_if_index])
      79             :     {
      80           5 :       vnet_feature_enable_disable ("arp", "arp-proxy",
      81             :                                    sw_if_index, 1, NULL, 0);
      82             :     }
      83           5 :   am->enabled_by_sw_if_index[sw_if_index] = true;
      84             : 
      85           5 :   return (0);
      86             : }
      87             : 
      88             : static int
      89           3 : vnet_proxy_arp_add_del (const ip4_address_t * lo_addr,
      90             :                         const ip4_address_t * hi_addr,
      91             :                         u32 fib_index, int is_del)
      92             : {
      93           3 :   arp_proxy_main_t *am = &arp_proxy_main;
      94             :   ethernet_proxy_arp_t *pa;
      95           3 :   u32 found_at_index = ~0;
      96             : 
      97           5 :   vec_foreach (pa, am->proxy_arps)
      98             :   {
      99           3 :     if (pa->lo_addr.as_u32 == lo_addr->as_u32 &&
     100           1 :         pa->hi_addr.as_u32 == hi_addr->as_u32 && pa->fib_index == fib_index)
     101             :       {
     102           1 :         found_at_index = pa - am->proxy_arps;
     103           1 :         break;
     104             :       }
     105             :   }
     106             : 
     107           3 :   if (found_at_index != ~0)
     108             :     {
     109             :       /* Delete, otherwise it's already in the table */
     110           1 :       if (is_del)
     111           1 :         vec_delete (am->proxy_arps, 1, found_at_index);
     112           1 :       return 0;
     113             :     }
     114             :   /* delete, no such entry */
     115           2 :   if (is_del)
     116           0 :     return VNET_API_ERROR_NO_SUCH_ENTRY;
     117             : 
     118             :   /* add, not in table */
     119           2 :   vec_add2 (am->proxy_arps, pa, 1);
     120           2 :   pa->lo_addr.as_u32 = lo_addr->as_u32;
     121           2 :   pa->hi_addr.as_u32 = hi_addr->as_u32;
     122           2 :   pa->fib_index = fib_index;
     123           2 :   return 0;
     124             : }
     125             : 
     126             : int
     127           2 : arp_proxy_add (u32 fib_index,
     128             :                const ip4_address_t * lo, const ip4_address_t * hi)
     129             : {
     130           2 :   return (vnet_proxy_arp_add_del (lo, hi, fib_index, 0));
     131             : }
     132             : 
     133             : int
     134           1 : arp_proxy_del (u32 fib_index,
     135             :                const ip4_address_t * lo, const ip4_address_t * hi)
     136             : {
     137           1 :   return (vnet_proxy_arp_add_del (lo, hi, fib_index, 1));
     138             : }
     139             : 
     140             : void
     141           0 : proxy_arp_intfc_walk (proxy_arp_intf_walk_t cb, void *data)
     142             : {
     143           0 :   arp_proxy_main_t *am = &arp_proxy_main;
     144             :   bool *enabled;
     145             : 
     146           0 :   vec_foreach (enabled, am->enabled_by_sw_if_index)
     147             :   {
     148           0 :     if (*enabled)
     149           0 :       cb (enabled - am->enabled_by_sw_if_index, data);
     150             :   }
     151           0 : }
     152             : 
     153             : static clib_error_t *
     154           0 : set_int_proxy_arp_command_fn (vlib_main_t * vm,
     155             :                               unformat_input_t * input,
     156             :                               vlib_cli_command_t * cmd)
     157             : {
     158           0 :   vnet_main_t *vnm = vnet_get_main ();
     159             :   u32 sw_if_index;
     160           0 :   int enable = 0;
     161             : 
     162           0 :   sw_if_index = ~0;
     163             : 
     164           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     165             :     {
     166           0 :       if (unformat (input, "%U", unformat_vnet_sw_interface,
     167             :                     vnm, &sw_if_index))
     168             :         ;
     169           0 :       else if (unformat (input, "enable") || unformat (input, "on"))
     170           0 :         enable = 1;
     171           0 :       else if (unformat (input, "disable") || unformat (input, "off"))
     172           0 :         enable = 0;
     173             :       else
     174             :         break;
     175             :     }
     176             : 
     177           0 :   if (~0 == sw_if_index)
     178           0 :     return clib_error_return (0, "unknown input '%U'",
     179             :                               format_unformat_error, input);
     180             : 
     181           0 :   if (enable)
     182           0 :     arp_proxy_enable (sw_if_index);
     183             :   else
     184           0 :     arp_proxy_disable (sw_if_index);
     185             : 
     186           0 :   return 0;
     187             : }
     188             : 
     189             : static clib_error_t *
     190           0 : set_arp_proxy (vlib_main_t * vm,
     191             :                unformat_input_t * input, vlib_cli_command_t * cmd)
     192             : {
     193           0 :   ip4_address_t start = {.as_u32 = ~0 }, end =
     194             :   {
     195             :   .as_u32 = ~0};
     196           0 :   u32 fib_index, table_id = 0;
     197           0 :   int add = 1;
     198             : 
     199           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     200             :     {
     201           0 :       if (unformat (input, "table-id %d", &table_id))
     202             :         ;
     203           0 :       else if (unformat (input, "start %U", unformat_ip4_address, &start))
     204             :         ;
     205           0 :       else if (unformat (input, "end %U", unformat_ip4_address, &end))
     206             :         ;
     207           0 :       else if (unformat (input, "del") || unformat (input, "delete"))
     208           0 :         add = 0;
     209             :       else
     210             :         break;
     211             :     }
     212             : 
     213           0 :   fib_index = fib_table_find (FIB_PROTOCOL_IP4, table_id);
     214             : 
     215           0 :   if (~0 == fib_index)
     216           0 :     return (clib_error_return (0, "no such table: %d", table_id));
     217             : 
     218           0 :   if (add)
     219           0 :     arp_proxy_add (fib_index, &start, &end);
     220             :   else
     221           0 :     arp_proxy_del (fib_index, &start, &end);
     222             : 
     223           0 :   return (NULL);
     224             : }
     225             : 
     226             : /* *INDENT-OFF* */
     227             : /*?
     228             :  * Enable proxy-arp on an interface. The vpp stack will answer ARP
     229             :  * requests for the indicated address range. Multiple proxy-arp
     230             :  * ranges may be provisioned.
     231             :  *
     232             :  * @note Proxy ARP as a technology is infamous for blackholing traffic.
     233             :  * Also, the underlying implementation has not been performance-tuned.
     234             :  * Avoid creating an unnecessarily large set of ranges.
     235             :  *
     236             :  * @cliexpar
     237             :  * To enable proxy arp on a range of addresses, use:
     238             :  * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11}
     239             :  * Append 'del' to delete a range of proxy ARP addresses:
     240             :  * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11 del}
     241             :  * You must then specifically enable proxy arp on individual interfaces:
     242             :  * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 enable}
     243             :  * To disable proxy arp on an individual interface:
     244             :  * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 disable}
     245             :  ?*/
     246      272887 : VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = {
     247             :   .path = "set interface proxy-arp",
     248             :   .short_help =
     249             :   "set interface proxy-arp <intfc> [enable|disable]",
     250             :   .function = set_int_proxy_arp_command_fn,
     251             : };
     252             : /* *INDENT-ON* */
     253             : 
     254             : /* *INDENT-OFF* */
     255      272887 : VLIB_CLI_COMMAND (set_arp_proxy_command, static) = {
     256             :   .path = "set arp proxy",
     257             :   .short_help = "set arp proxy [del] table-ID <table-ID> start <start-address> end <end-addres>",
     258             :   .function = set_arp_proxy,
     259             : };
     260             : /* *INDENT-ON* */
     261             : 
     262             : typedef struct
     263             : {
     264             :   u8 packet_data[64];
     265             : } ethernet_arp_input_trace_t;
     266             : 
     267             : static u8 *
     268           4 : format_ethernet_arp_input_trace (u8 * s, va_list * va)
     269             : {
     270           4 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
     271           4 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
     272           4 :   ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
     273             : 
     274           4 :   s = format (s, "%U",
     275             :               format_ethernet_arp_header,
     276           4 :               t->packet_data, sizeof (t->packet_data));
     277             : 
     278           4 :   return s;
     279             : }
     280             : 
     281             : static uword
     282           8 : arp_proxy (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
     283             : {
     284           8 :   arp_proxy_main_t *am = &arp_proxy_main;
     285           8 :   vnet_main_t *vnm = vnet_get_main ();
     286             :   u32 n_left_from, next_index, *from, *to_next;
     287           8 :   u32 n_arp_replies_sent = 0;
     288             : 
     289           8 :   from = vlib_frame_vector_args (frame);
     290           8 :   n_left_from = frame->n_vectors;
     291           8 :   next_index = node->cached_next_index;
     292             : 
     293           8 :   if (node->flags & VLIB_NODE_FLAG_TRACE)
     294           8 :     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
     295             :                                    /* stride */ 1,
     296             :                                    sizeof (ethernet_arp_input_trace_t));
     297             : 
     298          16 :   while (n_left_from > 0)
     299             :     {
     300             :       u32 n_left_to_next;
     301             : 
     302           8 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     303             : 
     304          16 :       while (n_left_from > 0 && n_left_to_next > 0)
     305             :         {
     306             :           vlib_buffer_t *p0;
     307             :           ethernet_arp_header_t *arp0;
     308             :           ethernet_header_t *eth_rx;
     309             :           ip4_address_t proxy_src;
     310             :           u32 pi0, error0, next0, sw_if_index0, fib_index0;
     311             :           u8 is_request0;
     312             :           ethernet_proxy_arp_t *pa;
     313             : 
     314           8 :           pi0 = from[0];
     315           8 :           to_next[0] = pi0;
     316           8 :           from += 1;
     317           8 :           to_next += 1;
     318           8 :           n_left_from -= 1;
     319           8 :           n_left_to_next -= 1;
     320             : 
     321           8 :           p0 = vlib_get_buffer (vm, pi0);
     322           8 :           arp0 = vlib_buffer_get_current (p0);
     323             :           /* Fill in ethernet header. */
     324           8 :           eth_rx = ethernet_buffer_get_header (p0);
     325             : 
     326          16 :           is_request0 = arp0->opcode
     327           8 :             == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
     328             : 
     329           8 :           error0 = ARP_ERROR_REPLIES_SENT;
     330           8 :           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
     331           8 :           next0 = ARP_REPLY_NEXT_DROP;
     332             : 
     333           8 :           fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
     334           8 :           if (~0 == fib_index0)
     335             :             {
     336           0 :               error0 = ARP_ERROR_INTERFACE_NO_TABLE;
     337             :             }
     338             : 
     339           8 :           if (0 == error0 && is_request0)
     340           8 :             {
     341           8 :               u32 this_addr = clib_net_to_host_u32
     342             :                 (arp0->ip4_over_ethernet[1].ip4.as_u32);
     343             : 
     344          17 :               vec_foreach (pa, am->proxy_arps)
     345             :               {
     346           9 :                 u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr.as_u32);
     347           9 :                 u32 hi_addr = clib_net_to_host_u32 (pa->hi_addr.as_u32);
     348             : 
     349             :                 /* an ARP request hit in the proxy-arp table? */
     350           9 :                 if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
     351           6 :                     (fib_index0 == pa->fib_index))
     352             :                   {
     353           5 :                     proxy_src.as_u32 =
     354           5 :                       arp0->ip4_over_ethernet[1].ip4.data_u32;
     355             : 
     356             :                     /*
     357             :                      * change the interface address to the proxied
     358             :                      */
     359           5 :                     n_arp_replies_sent++;
     360             : 
     361             :                     next0 =
     362           5 :                       arp_mk_reply (vnm, p0, sw_if_index0, &proxy_src, arp0,
     363             :                                     eth_rx);
     364             :                   }
     365             :               }
     366             :             }
     367             :           else
     368             :             {
     369           0 :               p0->error = node->errors[error0];
     370             :             }
     371             : 
     372           8 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     373             :                                            n_left_to_next, pi0, next0);
     374             :         }
     375             : 
     376           8 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     377             :     }
     378             : 
     379           8 :   vlib_error_count (vm, node->node_index, ARP_ERROR_REPLIES_SENT,
     380             :                     n_arp_replies_sent);
     381             : 
     382           8 :   return frame->n_vectors;
     383             : }
     384             : 
     385      178120 : VLIB_REGISTER_NODE (arp_proxy_node, static) =
     386             : {
     387             :   .function = arp_proxy,
     388             :   .name = "arp-proxy",
     389             :   .vector_size = sizeof (u32),
     390             :   .n_errors = ARP_N_ERROR,
     391             :   .error_counters = arp_error_counters,
     392             :   .n_next_nodes = ARP_REPLY_N_NEXT,
     393             :   .next_nodes =
     394             :   {
     395             :     [ARP_REPLY_NEXT_DROP] = "error-drop",
     396             :     [ARP_REPLY_NEXT_REPLY_TX] = "interface-output",
     397             :   },
     398             :   .format_buffer = format_ethernet_arp_header,
     399             :   .format_trace = format_ethernet_arp_input_trace,
     400             : };
     401             : 
     402             : static clib_error_t *
     403           0 : show_ip4_arp (vlib_main_t * vm,
     404             :               unformat_input_t * input, vlib_cli_command_t * cmd)
     405             : {
     406           0 :   arp_proxy_main_t *am = &arp_proxy_main;
     407             :   ethernet_proxy_arp_t *pa;
     408             : 
     409           0 :   if (vec_len (am->proxy_arps))
     410             :     {
     411           0 :       vlib_cli_output (vm, "Proxy arps enabled for:");
     412           0 :       vec_foreach (pa, am->proxy_arps)
     413             :       {
     414           0 :         vlib_cli_output (vm, "Fib_index %d   %U - %U ",
     415             :                          pa->fib_index,
     416             :                          format_ip4_address, &pa->lo_addr,
     417             :                          format_ip4_address, &pa->hi_addr);
     418             :       }
     419             :     }
     420             : 
     421           0 :   return (NULL);
     422             : }
     423             : 
     424             : /*?
     425             :  * Display all the IPv4 ARP proxy entries.
     426             :  *
     427             :  * @cliexpar
     428             :  * Example of how to display the IPv4 ARP table:
     429             :  * @cliexstart{show ip arp}
     430             :  *    Time      FIB        IP4       Flags      Ethernet              Interface
     431             :  *    346.3028   0       6.1.1.3            de:ad:be:ef:ba:be   GigabitEthernet2/0/0
     432             :  *   3077.4271   0       6.1.1.4       S    de:ad:be:ef:ff:ff   GigabitEthernet2/0/0
     433             :  *   2998.6409   1       6.2.2.3            de:ad:be:ef:00:01   GigabitEthernet2/0/0
     434             :  * Proxy arps enabled for:
     435             :  * Fib_index 0   6.0.0.1 - 6.0.0.11
     436             :  * @cliexend
     437             :  ?*/
     438             : /* *INDENT-OFF* */
     439      272887 : VLIB_CLI_COMMAND (show_ip4_arp_command, static) = {
     440             :   .path = "show arp proxy",
     441             :   .function = show_ip4_arp,
     442             :   .short_help = "show ip arp",
     443             : };
     444             : /* *INDENT-ON* */
     445             : 
     446             : /*
     447             :  * fd.io coding-style-patch-verification: ON
     448             :  *
     449             :  * Local Variables:
     450             :  * eval: (c-set-style "gnu")
     451             :  * End:
     452             :  */

Generated by: LCOV version 1.14