blob: e3f5b4ae67b169a07a3a720c1e004af9d116375a [file] [log] [blame]
Neale Rannscbe25aa2019-09-30 10:53:31 +00001/*
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
23typedef struct
24{
25 ip4_address_t lo_addr;
26 ip4_address_t hi_addr;
27 u32 fib_index;
28} ethernet_proxy_arp_t;
29
30typedef 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
39arp_proxy_main_t arp_proxy_main;
40
41void
42proxy_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
54int
55arp_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
71int
72arp_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
88static int
89vnet_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
126int
127arp_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
133int
134arp_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
140void
141proxy_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
153static clib_error_t *
154set_int_proxy_arp_command_fn (vlib_main_t * vm,
Neale Ranns6ca2ac32020-03-16 18:16:34 +0000155 unformat_input_t * input,
156 vlib_cli_command_t * cmd)
Neale Rannscbe25aa2019-09-30 10:53:31 +0000157{
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
Neale Ranns6ca2ac32020-03-16 18:16:34 +0000189static clib_error_t *
190set_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
Neale Rannscbe25aa2019-09-30 10:53:31 +0000226/* *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 ?*/
246VLIB_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
Neale Ranns6ca2ac32020-03-16 18:16:34 +0000254/* *INDENT-OFF* */
255VLIB_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
Neale Rannscbe25aa2019-09-30 10:53:31 +0000262typedef struct
263{
264 u8 packet_data[64];
265} ethernet_arp_input_trace_t;
266
267static u8 *
268format_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
281static uword
282arp_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
385static char *ethernet_arp_error_strings[] = {
386#define _(sym,string) string,
387 foreach_ethernet_arp_error
388#undef _
389};
390
391VLIB_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
402static clib_error_t *
403show_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* */
439VLIB_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 */