FD.io VPP  v21.01.1
Vector Packet Processing
arp_proxy.c
Go to the documentation of this file.
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 {
29 
30 typedef struct arp_proxy_main_t_
31 {
32  /** Per interface state */
34 
35  /* Proxy arp vector */
38 
40 
41 void
43 {
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
56 {
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  }
67 
68  return (0);
69 }
70 
71 int
73 {
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  }
84 
85  return (0);
86 }
87 
88 static int
90  const ip4_address_t * hi_addr,
91  u32 fib_index, int is_del)
92 {
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
142 {
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 *
155  unformat_input_t * input,
156  vlib_cli_command_t * cmd)
157 {
158  vnet_main_t *vnm = vnet_get_main ();
160  int enable = 0;
161 
162  sw_if_index = ~0;
163 
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 *
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 
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];
266 
267 static u8 *
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 *);
273 
274  s = format (s, "%U",
276  t->packet_data, sizeof (t->packet_data));
277 
278  return s;
279 }
280 
281 static uword
283 {
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;
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,
388 #undef _
389 };
390 
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 =
401 
402 static clib_error_t *
404  unformat_input_t * input, vlib_cli_command_t * cmd)
405 {
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,
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  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:509
#define foreach_ethernet_arp_error
Definition: arp.h:23
arp_proxy_main_t arp_proxy_main
Definition: arp_proxy.c:39
#define CLIB_UNUSED(x)
Definition: clib.h:87
vnet_main_t * vnet_get_main(void)
Definition: misc.c:46
int arp_proxy_add(u32 fib_index, const ip4_address_t *lo, const ip4_address_t *hi)
Definition: arp_proxy.c:127
static void vlib_error_count(vlib_main_t *vm, uword node_index, uword counter, uword increment)
Definition: error_funcs.h:57
bool * enabled_by_sw_if_index
Per interface state.
Definition: arp_proxy.c:33
static u8 * format_ethernet_arp_input_trace(u8 *s, va_list *va)
Definition: arp_proxy.c:268
static clib_error_t * set_arp_proxy(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: arp_proxy.c:190
static clib_error_t * show_ip4_arp(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: arp_proxy.c:403
#define vec_add2(V, P, N)
Add N elements to end of vector V, return pointer to new elements in P.
Definition: vec.h:630
ethernet_proxy_arp_t * proxy_arps
Definition: arp_proxy.c:36
void proxy_arp_intfc_walk(proxy_arp_intf_walk_t cb, void *data)
Definition: arp_proxy.c:141
vlib_main_t * vm
Definition: in2out_ed.c:1580
unformat_function_t unformat_vnet_sw_interface
static vlib_node_registration_t arp_proxy_node
(constructor) VLIB_REGISTER_NODE (arp_proxy_node)
Definition: arp_proxy.c:391
vlib_error_t * errors
Vector of errors for this node.
Definition: node.h:470
static char * ethernet_arp_error_strings[]
Definition: arp_proxy.c:385
unsigned char u8
Definition: types.h:56
u8 data[128]
Definition: ipsec_types.api:90
u32 ip4_fib_table_get_index_for_sw_if_index(u32 sw_if_index)
Definition: ip4_fib.c:227
format_function_t format_ip4_address
Definition: format.h:73
unformat_function_t unformat_ip4_address
Definition: format.h:68
ethernet_arp_ip4_over_ethernet_address_t ip4_over_ethernet[2]
Definition: arp_packet.h:142
description fragment has unexpected format
Definition: map.api:433
#define clib_error_return(e, args...)
Definition: error.h:99
unsigned int u32
Definition: types.h:88
u32 fib_table_find(fib_protocol_t proto, u32 table_id)
Get the index of the FIB for a Table-ID.
Definition: fib_table.c:1106
void proxy_arp_walk(proxy_arp_walk_t cb, void *data)
Definition: arp_proxy.c:42
vlib_error_t error
Error code for buffers to be enqueued to error handler.
Definition: buffer.h:136
static ethernet_header_t * ethernet_buffer_get_header(vlib_buffer_t *b)
Definition: ethernet.h:419
lo
static clib_error_t * set_int_proxy_arp_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: arp_proxy.c:154
struct _unformat_input_t unformat_input_t
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:233
int arp_proxy_enable(u32 sw_if_index)
Definition: arp_proxy.c:72
u32 node_index
Node index.
Definition: node.h:488
#define vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, bi0, next0)
Finish enqueueing one buffer forward in the graph.
Definition: buffer_node.h:224
static_always_inline u32 arp_mk_reply(vnet_main_t *vnm, vlib_buffer_t *p0, u32 sw_if_index0, const ip4_address_t *if_addr0, ethernet_arp_header_t *arp0, ethernet_header_t *eth_rx)
Definition: arp_packet.h:32
#define vlib_get_next_frame(vm, node, next_index, vectors, n_vectors_left)
Get pointer to next frame vector data by (vlib_node_runtime_t, next_index).
Definition: node_funcs.h:391
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:170
#define UNFORMAT_END_OF_INPUT
Definition: format.h:144
int arp_proxy_disable(u32 sw_if_index)
Definition: arp_proxy.c:55
u16 n_vectors
Definition: node.h:397
void vlib_put_next_frame(vlib_main_t *vm, vlib_node_runtime_t *r, u32 next_index, u32 n_vectors_left)
Release pointer to next frame vector data.
Definition: main.c:483
vlib_main_t vlib_node_runtime_t * node
Definition: in2out_ed.c:1580
walk_rc_t() proxy_arp_intf_walk_t(u32 sw_if_index, void *data)
call back function when walking the DB of proxy ARP interface
Definition: arp.h:73
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:158
int arp_proxy_del(u32 fib_index, const ip4_address_t *lo, const ip4_address_t *hi)
Definition: arp_proxy.c:134
u16 cached_next_index
Next frame index that vector arguments were last enqueued to last time this node ran.
Definition: node.h:511
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:696
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:854
walk_rc_t() proxy_arp_walk_t(const ip4_address_t *lo_addr, const ip4_address_t *hi_addr, u32 fib_index, void *dat)
call back function when walking the DB of proxy ARPs
Definition: arp.h:63
static int vnet_proxy_arp_add_del(const ip4_address_t *lo_addr, const ip4_address_t *hi_addr, u32 fib_index, int is_del)
Definition: arp_proxy.c:89
vl_api_ip4_address_t hi
Definition: arp.api:37
void vlib_trace_frame_buffers_only(vlib_main_t *vm, vlib_node_runtime_t *node, u32 *buffers, uword n_buffers, uword next_buffer_stride, uword n_buffer_data_bytes_in_trace)
Definition: trace.c:48
ip4_address_t hi_addr
Definition: arp_proxy.c:26
struct arp_proxy_main_t_ arp_proxy_main_t
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
vlib_main_t vlib_node_runtime_t vlib_frame_t * frame
Definition: in2out_ed.c:1581
VLIB buffer representation.
Definition: buffer.h:102
u64 uword
Definition: types.h:112
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:297
static uword arp_proxy(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
Definition: arp_proxy.c:282
u32 table_id
Definition: wireguard.api:102
u8 * format_ethernet_arp_header(u8 *s, va_list *va)
Definition: arp_packet.c:59
ip4_address_t lo_addr
Definition: arp_proxy.c:25
#define vnet_buffer(b)
Definition: buffer.h:417
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
#define vec_foreach(var, vec)
Vector iterator.
f64 end
end of the time range
Definition: mactime.api:44
u16 flags
Copy of main node flags.
Definition: node.h:501
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:302
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:85
vl_api_interface_index_t sw_if_index
Definition: wireguard.api:34
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:978
Definition: defs.h:46
int vnet_feature_enable_disable(const char *arc_name, const char *node_name, u32 sw_if_index, int enable_disable, void *feature_config, u32 n_feature_config_bytes)
Definition: feature.c:303
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:170