blob: 39f624d5a1d5b8cbd5147b3265e860ba48dba553 [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/*?
227 * Enable proxy-arp on an interface. The vpp stack will answer ARP
228 * requests for the indicated address range. Multiple proxy-arp
229 * ranges may be provisioned.
230 *
231 * @note Proxy ARP as a technology is infamous for blackholing traffic.
232 * Also, the underlying implementation has not been performance-tuned.
233 * Avoid creating an unnecessarily large set of ranges.
234 *
235 * @cliexpar
236 * To enable proxy arp on a range of addresses, use:
237 * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11}
238 * Append 'del' to delete a range of proxy ARP addresses:
239 * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11 del}
240 * You must then specifically enable proxy arp on individual interfaces:
241 * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 enable}
242 * To disable proxy arp on an individual interface:
243 * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 disable}
244 ?*/
245VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = {
246 .path = "set interface proxy-arp",
247 .short_help =
248 "set interface proxy-arp <intfc> [enable|disable]",
249 .function = set_int_proxy_arp_command_fn,
250};
Neale Rannscbe25aa2019-09-30 10:53:31 +0000251
Neale Ranns6ca2ac32020-03-16 18:16:34 +0000252VLIB_CLI_COMMAND (set_arp_proxy_command, static) = {
253 .path = "set arp proxy",
254 .short_help = "set arp proxy [del] table-ID <table-ID> start <start-address> end <end-addres>",
255 .function = set_arp_proxy,
256};
Neale Ranns6ca2ac32020-03-16 18:16:34 +0000257
Neale Rannscbe25aa2019-09-30 10:53:31 +0000258typedef struct
259{
260 u8 packet_data[64];
261} ethernet_arp_input_trace_t;
262
263static u8 *
264format_ethernet_arp_input_trace (u8 * s, va_list * va)
265{
266 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
267 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
268 ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
269
270 s = format (s, "%U",
271 format_ethernet_arp_header,
272 t->packet_data, sizeof (t->packet_data));
273
274 return s;
275}
276
277static uword
278arp_proxy (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
279{
280 arp_proxy_main_t *am = &arp_proxy_main;
281 vnet_main_t *vnm = vnet_get_main ();
282 u32 n_left_from, next_index, *from, *to_next;
283 u32 n_arp_replies_sent = 0;
284
285 from = vlib_frame_vector_args (frame);
286 n_left_from = frame->n_vectors;
287 next_index = node->cached_next_index;
288
289 if (node->flags & VLIB_NODE_FLAG_TRACE)
290 vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
291 /* stride */ 1,
292 sizeof (ethernet_arp_input_trace_t));
293
294 while (n_left_from > 0)
295 {
296 u32 n_left_to_next;
297
298 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
299
300 while (n_left_from > 0 && n_left_to_next > 0)
301 {
302 vlib_buffer_t *p0;
303 ethernet_arp_header_t *arp0;
304 ethernet_header_t *eth_rx;
305 ip4_address_t proxy_src;
306 u32 pi0, error0, next0, sw_if_index0, fib_index0;
307 u8 is_request0;
308 ethernet_proxy_arp_t *pa;
309
310 pi0 = from[0];
311 to_next[0] = pi0;
312 from += 1;
313 to_next += 1;
314 n_left_from -= 1;
315 n_left_to_next -= 1;
316
317 p0 = vlib_get_buffer (vm, pi0);
318 arp0 = vlib_buffer_get_current (p0);
319 /* Fill in ethernet header. */
320 eth_rx = ethernet_buffer_get_header (p0);
321
322 is_request0 = arp0->opcode
323 == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
324
Neale Ranns13a74ae2022-08-09 00:59:37 +0000325 error0 = ARP_ERROR_REPLIES_SENT;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000326 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
327 next0 = ARP_REPLY_NEXT_DROP;
328
329 fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
330 if (~0 == fib_index0)
331 {
Neale Ranns13a74ae2022-08-09 00:59:37 +0000332 error0 = ARP_ERROR_INTERFACE_NO_TABLE;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000333 }
334
335 if (0 == error0 && is_request0)
336 {
337 u32 this_addr = clib_net_to_host_u32
338 (arp0->ip4_over_ethernet[1].ip4.as_u32);
339
340 vec_foreach (pa, am->proxy_arps)
341 {
342 u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr.as_u32);
343 u32 hi_addr = clib_net_to_host_u32 (pa->hi_addr.as_u32);
344
345 /* an ARP request hit in the proxy-arp table? */
346 if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
347 (fib_index0 == pa->fib_index))
348 {
349 proxy_src.as_u32 =
350 arp0->ip4_over_ethernet[1].ip4.data_u32;
351
352 /*
353 * change the interface address to the proxied
354 */
355 n_arp_replies_sent++;
356
357 next0 =
358 arp_mk_reply (vnm, p0, sw_if_index0, &proxy_src, arp0,
359 eth_rx);
360 }
361 }
362 }
363 else
364 {
365 p0->error = node->errors[error0];
366 }
367
368 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
369 n_left_to_next, pi0, next0);
370 }
371
372 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
373 }
374
Neale Ranns13a74ae2022-08-09 00:59:37 +0000375 vlib_error_count (vm, node->node_index, ARP_ERROR_REPLIES_SENT,
376 n_arp_replies_sent);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000377
378 return frame->n_vectors;
379}
380
Neale Rannscbe25aa2019-09-30 10:53:31 +0000381VLIB_REGISTER_NODE (arp_proxy_node, static) =
382{
Neale Ranns13a74ae2022-08-09 00:59:37 +0000383 .function = arp_proxy,
384 .name = "arp-proxy",
385 .vector_size = sizeof (u32),
386 .n_errors = ARP_N_ERROR,
387 .error_counters = arp_error_counters,
388 .n_next_nodes = ARP_REPLY_N_NEXT,
389 .next_nodes =
Neale Rannscbe25aa2019-09-30 10:53:31 +0000390 {
Neale Ranns13a74ae2022-08-09 00:59:37 +0000391 [ARP_REPLY_NEXT_DROP] = "error-drop",
392 [ARP_REPLY_NEXT_REPLY_TX] = "interface-output",
393 },
394 .format_buffer = format_ethernet_arp_header,
395 .format_trace = format_ethernet_arp_input_trace,
396};
Neale Rannscbe25aa2019-09-30 10:53:31 +0000397
398static clib_error_t *
399show_ip4_arp (vlib_main_t * vm,
400 unformat_input_t * input, vlib_cli_command_t * cmd)
401{
402 arp_proxy_main_t *am = &arp_proxy_main;
403 ethernet_proxy_arp_t *pa;
404
405 if (vec_len (am->proxy_arps))
406 {
407 vlib_cli_output (vm, "Proxy arps enabled for:");
408 vec_foreach (pa, am->proxy_arps)
409 {
410 vlib_cli_output (vm, "Fib_index %d %U - %U ",
411 pa->fib_index,
412 format_ip4_address, &pa->lo_addr,
413 format_ip4_address, &pa->hi_addr);
414 }
415 }
416
417 return (NULL);
418}
419
420/*?
421 * Display all the IPv4 ARP proxy entries.
422 *
423 * @cliexpar
424 * Example of how to display the IPv4 ARP table:
425 * @cliexstart{show ip arp}
426 * Time FIB IP4 Flags Ethernet Interface
427 * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
428 * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
429 * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
430 * Proxy arps enabled for:
431 * Fib_index 0 6.0.0.1 - 6.0.0.11
432 * @cliexend
433 ?*/
Neale Rannscbe25aa2019-09-30 10:53:31 +0000434VLIB_CLI_COMMAND (show_ip4_arp_command, static) = {
435 .path = "show arp proxy",
436 .function = show_ip4_arp,
437 .short_help = "show ip arp",
438};
Neale Rannscbe25aa2019-09-30 10:53:31 +0000439
440/*
441 * fd.io coding-style-patch-verification: ON
442 *
443 * Local Variables:
444 * eval: (c-set-style "gnu")
445 * End:
446 */