arp: Restore CLI for setting ARP proxy entries
[vpp.git] / src / vnet / arp / arp_proxy.c
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 proxy_arp_walk (proxy_arp_walk_t cb, void *data)
43 {
44   arp_proxy_main_t *am = &arp_proxy_main;
45   ethernet_proxy_arp_t *pa;
46
47   vec_foreach (pa, am->proxy_arps)
48   {
49     if (!cb (&pa->lo_addr, &pa->hi_addr, pa->fib_index, data))
50       break;
51   }
52 }
53
54 int
55 arp_proxy_disable (u32 sw_if_index)
56 {
57   arp_proxy_main_t *am = &arp_proxy_main;
58
59   vec_validate (am->enabled_by_sw_if_index, sw_if_index);
60
61   if (am->enabled_by_sw_if_index[sw_if_index])
62     {
63       vnet_feature_enable_disable ("arp", "arp-proxy",
64                                    sw_if_index, 0, NULL, 0);
65     }
66   am->enabled_by_sw_if_index[sw_if_index] = false;
67
68   return (0);
69 }
70
71 int
72 arp_proxy_enable (u32 sw_if_index)
73 {
74   arp_proxy_main_t *am = &arp_proxy_main;
75
76   vec_validate (am->enabled_by_sw_if_index, sw_if_index);
77
78   if (!am->enabled_by_sw_if_index[sw_if_index])
79     {
80       vnet_feature_enable_disable ("arp", "arp-proxy",
81                                    sw_if_index, 1, NULL, 0);
82     }
83   am->enabled_by_sw_if_index[sw_if_index] = true;
84
85   return (0);
86 }
87
88 static int
89 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   arp_proxy_main_t *am = &arp_proxy_main;
94   ethernet_proxy_arp_t *pa;
95   u32 found_at_index = ~0;
96
97   vec_foreach (pa, am->proxy_arps)
98   {
99     if (pa->lo_addr.as_u32 == lo_addr->as_u32 &&
100         pa->hi_addr.as_u32 == hi_addr->as_u32 && pa->fib_index == fib_index)
101       {
102         found_at_index = pa - am->proxy_arps;
103         break;
104       }
105   }
106
107   if (found_at_index != ~0)
108     {
109       /* Delete, otherwise it's already in the table */
110       if (is_del)
111         vec_delete (am->proxy_arps, 1, found_at_index);
112       return 0;
113     }
114   /* delete, no such entry */
115   if (is_del)
116     return VNET_API_ERROR_NO_SUCH_ENTRY;
117
118   /* add, not in table */
119   vec_add2 (am->proxy_arps, pa, 1);
120   pa->lo_addr.as_u32 = lo_addr->as_u32;
121   pa->hi_addr.as_u32 = hi_addr->as_u32;
122   pa->fib_index = fib_index;
123   return 0;
124 }
125
126 int
127 arp_proxy_add (u32 fib_index,
128                const ip4_address_t * lo, const ip4_address_t * hi)
129 {
130   return (vnet_proxy_arp_add_del (lo, hi, fib_index, 0));
131 }
132
133 int
134 arp_proxy_del (u32 fib_index,
135                const ip4_address_t * lo, const ip4_address_t * hi)
136 {
137   return (vnet_proxy_arp_add_del (lo, hi, fib_index, 1));
138 }
139
140 void
141 proxy_arp_intfc_walk (proxy_arp_intf_walk_t cb, void *data)
142 {
143   arp_proxy_main_t *am = &arp_proxy_main;
144   bool *enabled;
145
146   vec_foreach (enabled, am->enabled_by_sw_if_index)
147   {
148     if (*enabled)
149       cb (enabled - am->enabled_by_sw_if_index, data);
150   }
151 }
152
153 static clib_error_t *
154 set_int_proxy_arp_command_fn (vlib_main_t * vm,
155                               unformat_input_t * input,
156                               vlib_cli_command_t * cmd)
157 {
158   vnet_main_t *vnm = vnet_get_main ();
159   u32 sw_if_index;
160   int enable = 0;
161
162   sw_if_index = ~0;
163
164   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
165     {
166       if (unformat (input, "%U", unformat_vnet_sw_interface,
167                     vnm, &sw_if_index))
168         ;
169       else if (unformat (input, "enable") || unformat (input, "on"))
170         enable = 1;
171       else if (unformat (input, "disable") || unformat (input, "off"))
172         enable = 0;
173       else
174         break;
175     }
176
177   if (~0 == sw_if_index)
178     return clib_error_return (0, "unknown input '%U'",
179                               format_unformat_error, input);
180
181   if (enable)
182     arp_proxy_enable (sw_if_index);
183   else
184     arp_proxy_disable (sw_if_index);
185
186   return 0;
187 }
188
189 static clib_error_t *
190 set_arp_proxy (vlib_main_t * vm,
191                unformat_input_t * input, vlib_cli_command_t * cmd)
192 {
193   ip4_address_t start = {.as_u32 = ~0 }, end =
194   {
195   .as_u32 = ~0};
196   u32 fib_index, table_id = 0;
197   int add = 1;
198
199   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
200     {
201       if (unformat (input, "table-id %d", &table_id))
202         ;
203       else if (unformat (input, "start %U", unformat_ip4_address, &start))
204         ;
205       else if (unformat (input, "end %U", unformat_ip4_address, &end))
206         ;
207       else if (unformat (input, "del") || unformat (input, "delete"))
208         add = 0;
209       else
210         break;
211     }
212
213   fib_index = fib_table_find (FIB_PROTOCOL_IP4, table_id);
214
215   if (~0 == fib_index)
216     return (clib_error_return (0, "no such table: %d", table_id));
217
218   if (add)
219     arp_proxy_add (fib_index, &start, &end);
220   else
221     arp_proxy_del (fib_index, &start, &end);
222
223   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 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 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 format_ethernet_arp_input_trace (u8 * s, va_list * va)
269 {
270   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
271   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
272   ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
273
274   s = format (s, "%U",
275               format_ethernet_arp_header,
276               t->packet_data, sizeof (t->packet_data));
277
278   return s;
279 }
280
281 static uword
282 arp_proxy (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
283 {
284   arp_proxy_main_t *am = &arp_proxy_main;
285   vnet_main_t *vnm = vnet_get_main ();
286   u32 n_left_from, next_index, *from, *to_next;
287   u32 n_arp_replies_sent = 0;
288
289   from = vlib_frame_vector_args (frame);
290   n_left_from = frame->n_vectors;
291   next_index = node->cached_next_index;
292
293   if (node->flags & VLIB_NODE_FLAG_TRACE)
294     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
295                                    /* stride */ 1,
296                                    sizeof (ethernet_arp_input_trace_t));
297
298   while (n_left_from > 0)
299     {
300       u32 n_left_to_next;
301
302       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
303
304       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           pi0 = from[0];
315           to_next[0] = pi0;
316           from += 1;
317           to_next += 1;
318           n_left_from -= 1;
319           n_left_to_next -= 1;
320
321           p0 = vlib_get_buffer (vm, pi0);
322           arp0 = vlib_buffer_get_current (p0);
323           /* Fill in ethernet header. */
324           eth_rx = ethernet_buffer_get_header (p0);
325
326           is_request0 = arp0->opcode
327             == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
328
329           error0 = ETHERNET_ARP_ERROR_replies_sent;
330           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
331           next0 = ARP_REPLY_NEXT_DROP;
332
333           fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
334           if (~0 == fib_index0)
335             {
336               error0 = ETHERNET_ARP_ERROR_interface_no_table;
337             }
338
339           if (0 == error0 && is_request0)
340             {
341               u32 this_addr = clib_net_to_host_u32
342                 (arp0->ip4_over_ethernet[1].ip4.as_u32);
343
344               vec_foreach (pa, am->proxy_arps)
345               {
346                 u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr.as_u32);
347                 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                 if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
351                     (fib_index0 == pa->fib_index))
352                   {
353                     proxy_src.as_u32 =
354                       arp0->ip4_over_ethernet[1].ip4.data_u32;
355
356                     /*
357                      * change the interface address to the proxied
358                      */
359                     n_arp_replies_sent++;
360
361                     next0 =
362                       arp_mk_reply (vnm, p0, sw_if_index0, &proxy_src, arp0,
363                                     eth_rx);
364                   }
365               }
366             }
367           else
368             {
369               p0->error = node->errors[error0];
370             }
371
372           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
373                                            n_left_to_next, pi0, next0);
374         }
375
376       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
377     }
378
379   vlib_error_count (vm, node->node_index,
380                     ETHERNET_ARP_ERROR_replies_sent, n_arp_replies_sent);
381
382   return frame->n_vectors;
383 }
384
385 static char *ethernet_arp_error_strings[] = {
386 #define _(sym,string) string,
387   foreach_ethernet_arp_error
388 #undef _
389 };
390
391 VLIB_REGISTER_NODE (arp_proxy_node, static) =
392 {
393   .function = arp_proxy,.name = "arp-proxy",.vector_size =
394     sizeof (u32),.n_errors = ETHERNET_ARP_N_ERROR,.error_strings =
395     ethernet_arp_error_strings,.n_next_nodes = ARP_REPLY_N_NEXT,.next_nodes =
396   {
397   [ARP_REPLY_NEXT_DROP] = "error-drop",
398       [ARP_REPLY_NEXT_REPLY_TX] = "interface-output",}
399 ,.format_buffer = format_ethernet_arp_header,.format_trace =
400     format_ethernet_arp_input_trace,};
401
402 static clib_error_t *
403 show_ip4_arp (vlib_main_t * vm,
404               unformat_input_t * input, vlib_cli_command_t * cmd)
405 {
406   arp_proxy_main_t *am = &arp_proxy_main;
407   ethernet_proxy_arp_t *pa;
408
409   if (vec_len (am->proxy_arps))
410     {
411       vlib_cli_output (vm, "Proxy arps enabled for:");
412       vec_foreach (pa, am->proxy_arps)
413       {
414         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   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 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  */