346a21775f8dcc951328983ee7c4b7941bcf4428
[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 *
156                               input, 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 /* *INDENT-OFF* */
190 /*?
191  * Enable proxy-arp on an interface. The vpp stack will answer ARP
192  * requests for the indicated address range. Multiple proxy-arp
193  * ranges may be provisioned.
194  *
195  * @note Proxy ARP as a technology is infamous for blackholing traffic.
196  * Also, the underlying implementation has not been performance-tuned.
197  * Avoid creating an unnecessarily large set of ranges.
198  *
199  * @cliexpar
200  * To enable proxy arp on a range of addresses, use:
201  * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11}
202  * Append 'del' to delete a range of proxy ARP addresses:
203  * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11 del}
204  * You must then specifically enable proxy arp on individual interfaces:
205  * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 enable}
206  * To disable proxy arp on an individual interface:
207  * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 disable}
208  ?*/
209 VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = {
210   .path = "set interface proxy-arp",
211   .short_help =
212   "set interface proxy-arp <intfc> [enable|disable]",
213   .function = set_int_proxy_arp_command_fn,
214 };
215 /* *INDENT-ON* */
216
217 typedef struct
218 {
219   u8 packet_data[64];
220 } ethernet_arp_input_trace_t;
221
222 static u8 *
223 format_ethernet_arp_input_trace (u8 * s, va_list * va)
224 {
225   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
226   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
227   ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
228
229   s = format (s, "%U",
230               format_ethernet_arp_header,
231               t->packet_data, sizeof (t->packet_data));
232
233   return s;
234 }
235
236 static uword
237 arp_proxy (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
238 {
239   arp_proxy_main_t *am = &arp_proxy_main;
240   vnet_main_t *vnm = vnet_get_main ();
241   u32 n_left_from, next_index, *from, *to_next;
242   u32 n_arp_replies_sent = 0;
243
244   from = vlib_frame_vector_args (frame);
245   n_left_from = frame->n_vectors;
246   next_index = node->cached_next_index;
247
248   if (node->flags & VLIB_NODE_FLAG_TRACE)
249     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
250                                    /* stride */ 1,
251                                    sizeof (ethernet_arp_input_trace_t));
252
253   while (n_left_from > 0)
254     {
255       u32 n_left_to_next;
256
257       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
258
259       while (n_left_from > 0 && n_left_to_next > 0)
260         {
261           vlib_buffer_t *p0;
262           ethernet_arp_header_t *arp0;
263           ethernet_header_t *eth_rx;
264           ip4_address_t proxy_src;
265           u32 pi0, error0, next0, sw_if_index0, fib_index0;
266           u8 is_request0;
267           ethernet_proxy_arp_t *pa;
268
269           pi0 = from[0];
270           to_next[0] = pi0;
271           from += 1;
272           to_next += 1;
273           n_left_from -= 1;
274           n_left_to_next -= 1;
275
276           p0 = vlib_get_buffer (vm, pi0);
277           arp0 = vlib_buffer_get_current (p0);
278           /* Fill in ethernet header. */
279           eth_rx = ethernet_buffer_get_header (p0);
280
281           is_request0 = arp0->opcode
282             == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
283
284           error0 = ETHERNET_ARP_ERROR_replies_sent;
285           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
286           next0 = ARP_REPLY_NEXT_DROP;
287
288           fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
289           if (~0 == fib_index0)
290             {
291               error0 = ETHERNET_ARP_ERROR_interface_no_table;
292             }
293
294           if (0 == error0 && is_request0)
295             {
296               u32 this_addr = clib_net_to_host_u32
297                 (arp0->ip4_over_ethernet[1].ip4.as_u32);
298
299               vec_foreach (pa, am->proxy_arps)
300               {
301                 u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr.as_u32);
302                 u32 hi_addr = clib_net_to_host_u32 (pa->hi_addr.as_u32);
303
304                 /* an ARP request hit in the proxy-arp table? */
305                 if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
306                     (fib_index0 == pa->fib_index))
307                   {
308                     proxy_src.as_u32 =
309                       arp0->ip4_over_ethernet[1].ip4.data_u32;
310
311                     /*
312                      * change the interface address to the proxied
313                      */
314                     n_arp_replies_sent++;
315
316                     next0 =
317                       arp_mk_reply (vnm, p0, sw_if_index0, &proxy_src, arp0,
318                                     eth_rx);
319                   }
320               }
321             }
322           else
323             {
324               p0->error = node->errors[error0];
325             }
326
327           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
328                                            n_left_to_next, pi0, next0);
329         }
330
331       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
332     }
333
334   vlib_error_count (vm, node->node_index,
335                     ETHERNET_ARP_ERROR_replies_sent, n_arp_replies_sent);
336
337   return frame->n_vectors;
338 }
339
340 static char *ethernet_arp_error_strings[] = {
341 #define _(sym,string) string,
342   foreach_ethernet_arp_error
343 #undef _
344 };
345
346 VLIB_REGISTER_NODE (arp_proxy_node, static) =
347 {
348   .function = arp_proxy,.name = "arp-proxy",.vector_size =
349     sizeof (u32),.n_errors = ETHERNET_ARP_N_ERROR,.error_strings =
350     ethernet_arp_error_strings,.n_next_nodes = ARP_REPLY_N_NEXT,.next_nodes =
351   {
352   [ARP_REPLY_NEXT_DROP] = "error-drop",
353       [ARP_REPLY_NEXT_REPLY_TX] = "interface-output",}
354 ,.format_buffer = format_ethernet_arp_header,.format_trace =
355     format_ethernet_arp_input_trace,};
356
357 static clib_error_t *
358 show_ip4_arp (vlib_main_t * vm,
359               unformat_input_t * input, vlib_cli_command_t * cmd)
360 {
361   arp_proxy_main_t *am = &arp_proxy_main;
362   ethernet_proxy_arp_t *pa;
363
364   if (vec_len (am->proxy_arps))
365     {
366       vlib_cli_output (vm, "Proxy arps enabled for:");
367       vec_foreach (pa, am->proxy_arps)
368       {
369         vlib_cli_output (vm, "Fib_index %d   %U - %U ",
370                          pa->fib_index,
371                          format_ip4_address, &pa->lo_addr,
372                          format_ip4_address, &pa->hi_addr);
373       }
374     }
375
376   return (NULL);
377 }
378
379 /*?
380  * Display all the IPv4 ARP proxy entries.
381  *
382  * @cliexpar
383  * Example of how to display the IPv4 ARP table:
384  * @cliexstart{show ip arp}
385  *    Time      FIB        IP4       Flags      Ethernet              Interface
386  *    346.3028   0       6.1.1.3            de:ad:be:ef:ba:be   GigabitEthernet2/0/0
387  *   3077.4271   0       6.1.1.4       S    de:ad:be:ef:ff:ff   GigabitEthernet2/0/0
388  *   2998.6409   1       6.2.2.3            de:ad:be:ef:00:01   GigabitEthernet2/0/0
389  * Proxy arps enabled for:
390  * Fib_index 0   6.0.0.1 - 6.0.0.11
391  * @cliexend
392  ?*/
393 /* *INDENT-OFF* */
394 VLIB_CLI_COMMAND (show_ip4_arp_command, static) = {
395   .path = "show arp proxy",
396   .function = show_ip4_arp,
397   .short_help = "show ip arp",
398 };
399 /* *INDENT-ON* */
400
401 /*
402  * fd.io coding-style-patch-verification: ON
403  *
404  * Local Variables:
405  * eval: (c-set-style "gnu")
406  * End:
407  */