blob: 346a21775f8dcc951328983ee7c4b7941bcf4428 [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,
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 ?*/
209VLIB_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
217typedef struct
218{
219 u8 packet_data[64];
220} ethernet_arp_input_trace_t;
221
222static u8 *
223format_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
236static uword
237arp_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
340static char *ethernet_arp_error_strings[] = {
341#define _(sym,string) string,
342 foreach_ethernet_arp_error
343#undef _
344};
345
346VLIB_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
357static clib_error_t *
358show_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* */
394VLIB_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 */