Line data Source code
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 0 : proxy_arp_walk (proxy_arp_walk_t cb, void *data)
43 : {
44 0 : arp_proxy_main_t *am = &arp_proxy_main;
45 : ethernet_proxy_arp_t *pa;
46 :
47 0 : vec_foreach (pa, am->proxy_arps)
48 : {
49 0 : if (!cb (&pa->lo_addr, &pa->hi_addr, pa->fib_index, data))
50 0 : break;
51 : }
52 0 : }
53 :
54 : int
55 4 : arp_proxy_disable (u32 sw_if_index)
56 : {
57 4 : arp_proxy_main_t *am = &arp_proxy_main;
58 :
59 4 : vec_validate (am->enabled_by_sw_if_index, sw_if_index);
60 :
61 4 : if (am->enabled_by_sw_if_index[sw_if_index])
62 : {
63 4 : vnet_feature_enable_disable ("arp", "arp-proxy",
64 : sw_if_index, 0, NULL, 0);
65 : }
66 4 : am->enabled_by_sw_if_index[sw_if_index] = false;
67 :
68 4 : return (0);
69 : }
70 :
71 : int
72 5 : arp_proxy_enable (u32 sw_if_index)
73 : {
74 5 : arp_proxy_main_t *am = &arp_proxy_main;
75 :
76 5 : vec_validate (am->enabled_by_sw_if_index, sw_if_index);
77 :
78 5 : if (!am->enabled_by_sw_if_index[sw_if_index])
79 : {
80 5 : vnet_feature_enable_disable ("arp", "arp-proxy",
81 : sw_if_index, 1, NULL, 0);
82 : }
83 5 : am->enabled_by_sw_if_index[sw_if_index] = true;
84 :
85 5 : return (0);
86 : }
87 :
88 : static int
89 3 : 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 3 : arp_proxy_main_t *am = &arp_proxy_main;
94 : ethernet_proxy_arp_t *pa;
95 3 : u32 found_at_index = ~0;
96 :
97 5 : vec_foreach (pa, am->proxy_arps)
98 : {
99 3 : if (pa->lo_addr.as_u32 == lo_addr->as_u32 &&
100 1 : pa->hi_addr.as_u32 == hi_addr->as_u32 && pa->fib_index == fib_index)
101 : {
102 1 : found_at_index = pa - am->proxy_arps;
103 1 : break;
104 : }
105 : }
106 :
107 3 : if (found_at_index != ~0)
108 : {
109 : /* Delete, otherwise it's already in the table */
110 1 : if (is_del)
111 1 : vec_delete (am->proxy_arps, 1, found_at_index);
112 1 : return 0;
113 : }
114 : /* delete, no such entry */
115 2 : if (is_del)
116 0 : return VNET_API_ERROR_NO_SUCH_ENTRY;
117 :
118 : /* add, not in table */
119 2 : vec_add2 (am->proxy_arps, pa, 1);
120 2 : pa->lo_addr.as_u32 = lo_addr->as_u32;
121 2 : pa->hi_addr.as_u32 = hi_addr->as_u32;
122 2 : pa->fib_index = fib_index;
123 2 : return 0;
124 : }
125 :
126 : int
127 2 : arp_proxy_add (u32 fib_index,
128 : const ip4_address_t * lo, const ip4_address_t * hi)
129 : {
130 2 : return (vnet_proxy_arp_add_del (lo, hi, fib_index, 0));
131 : }
132 :
133 : int
134 1 : arp_proxy_del (u32 fib_index,
135 : const ip4_address_t * lo, const ip4_address_t * hi)
136 : {
137 1 : return (vnet_proxy_arp_add_del (lo, hi, fib_index, 1));
138 : }
139 :
140 : void
141 0 : proxy_arp_intfc_walk (proxy_arp_intf_walk_t cb, void *data)
142 : {
143 0 : arp_proxy_main_t *am = &arp_proxy_main;
144 : bool *enabled;
145 :
146 0 : vec_foreach (enabled, am->enabled_by_sw_if_index)
147 : {
148 0 : if (*enabled)
149 0 : cb (enabled - am->enabled_by_sw_if_index, data);
150 : }
151 0 : }
152 :
153 : static clib_error_t *
154 0 : set_int_proxy_arp_command_fn (vlib_main_t * vm,
155 : unformat_input_t * input,
156 : vlib_cli_command_t * cmd)
157 : {
158 0 : vnet_main_t *vnm = vnet_get_main ();
159 : u32 sw_if_index;
160 0 : int enable = 0;
161 :
162 0 : sw_if_index = ~0;
163 :
164 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
165 : {
166 0 : if (unformat (input, "%U", unformat_vnet_sw_interface,
167 : vnm, &sw_if_index))
168 : ;
169 0 : else if (unformat (input, "enable") || unformat (input, "on"))
170 0 : enable = 1;
171 0 : else if (unformat (input, "disable") || unformat (input, "off"))
172 0 : enable = 0;
173 : else
174 : break;
175 : }
176 :
177 0 : if (~0 == sw_if_index)
178 0 : return clib_error_return (0, "unknown input '%U'",
179 : format_unformat_error, input);
180 :
181 0 : if (enable)
182 0 : arp_proxy_enable (sw_if_index);
183 : else
184 0 : arp_proxy_disable (sw_if_index);
185 :
186 0 : return 0;
187 : }
188 :
189 : static clib_error_t *
190 0 : set_arp_proxy (vlib_main_t * vm,
191 : unformat_input_t * input, vlib_cli_command_t * cmd)
192 : {
193 0 : ip4_address_t start = {.as_u32 = ~0 }, end =
194 : {
195 : .as_u32 = ~0};
196 0 : u32 fib_index, table_id = 0;
197 0 : int add = 1;
198 :
199 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
200 : {
201 0 : if (unformat (input, "table-id %d", &table_id))
202 : ;
203 0 : else if (unformat (input, "start %U", unformat_ip4_address, &start))
204 : ;
205 0 : else if (unformat (input, "end %U", unformat_ip4_address, &end))
206 : ;
207 0 : else if (unformat (input, "del") || unformat (input, "delete"))
208 0 : add = 0;
209 : else
210 : break;
211 : }
212 :
213 0 : fib_index = fib_table_find (FIB_PROTOCOL_IP4, table_id);
214 :
215 0 : if (~0 == fib_index)
216 0 : return (clib_error_return (0, "no such table: %d", table_id));
217 :
218 0 : if (add)
219 0 : arp_proxy_add (fib_index, &start, &end);
220 : else
221 0 : arp_proxy_del (fib_index, &start, &end);
222 :
223 0 : 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 272887 : 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 272887 : 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];
265 : } ethernet_arp_input_trace_t;
266 :
267 : static u8 *
268 4 : format_ethernet_arp_input_trace (u8 * s, va_list * va)
269 : {
270 4 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
271 4 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
272 4 : ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
273 :
274 4 : s = format (s, "%U",
275 : format_ethernet_arp_header,
276 4 : t->packet_data, sizeof (t->packet_data));
277 :
278 4 : return s;
279 : }
280 :
281 : static uword
282 8 : arp_proxy (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
283 : {
284 8 : arp_proxy_main_t *am = &arp_proxy_main;
285 8 : vnet_main_t *vnm = vnet_get_main ();
286 : u32 n_left_from, next_index, *from, *to_next;
287 8 : u32 n_arp_replies_sent = 0;
288 :
289 8 : from = vlib_frame_vector_args (frame);
290 8 : n_left_from = frame->n_vectors;
291 8 : next_index = node->cached_next_index;
292 :
293 8 : if (node->flags & VLIB_NODE_FLAG_TRACE)
294 8 : vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
295 : /* stride */ 1,
296 : sizeof (ethernet_arp_input_trace_t));
297 :
298 16 : while (n_left_from > 0)
299 : {
300 : u32 n_left_to_next;
301 :
302 8 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
303 :
304 16 : 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 8 : pi0 = from[0];
315 8 : to_next[0] = pi0;
316 8 : from += 1;
317 8 : to_next += 1;
318 8 : n_left_from -= 1;
319 8 : n_left_to_next -= 1;
320 :
321 8 : p0 = vlib_get_buffer (vm, pi0);
322 8 : arp0 = vlib_buffer_get_current (p0);
323 : /* Fill in ethernet header. */
324 8 : eth_rx = ethernet_buffer_get_header (p0);
325 :
326 16 : is_request0 = arp0->opcode
327 8 : == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
328 :
329 8 : error0 = ARP_ERROR_REPLIES_SENT;
330 8 : sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
331 8 : next0 = ARP_REPLY_NEXT_DROP;
332 :
333 8 : fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
334 8 : if (~0 == fib_index0)
335 : {
336 0 : error0 = ARP_ERROR_INTERFACE_NO_TABLE;
337 : }
338 :
339 8 : if (0 == error0 && is_request0)
340 8 : {
341 8 : u32 this_addr = clib_net_to_host_u32
342 : (arp0->ip4_over_ethernet[1].ip4.as_u32);
343 :
344 17 : vec_foreach (pa, am->proxy_arps)
345 : {
346 9 : u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr.as_u32);
347 9 : 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 9 : if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
351 6 : (fib_index0 == pa->fib_index))
352 : {
353 5 : proxy_src.as_u32 =
354 5 : arp0->ip4_over_ethernet[1].ip4.data_u32;
355 :
356 : /*
357 : * change the interface address to the proxied
358 : */
359 5 : n_arp_replies_sent++;
360 :
361 : next0 =
362 5 : arp_mk_reply (vnm, p0, sw_if_index0, &proxy_src, arp0,
363 : eth_rx);
364 : }
365 : }
366 : }
367 : else
368 : {
369 0 : p0->error = node->errors[error0];
370 : }
371 :
372 8 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
373 : n_left_to_next, pi0, next0);
374 : }
375 :
376 8 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
377 : }
378 :
379 8 : vlib_error_count (vm, node->node_index, ARP_ERROR_REPLIES_SENT,
380 : n_arp_replies_sent);
381 :
382 8 : return frame->n_vectors;
383 : }
384 :
385 178120 : VLIB_REGISTER_NODE (arp_proxy_node, static) =
386 : {
387 : .function = arp_proxy,
388 : .name = "arp-proxy",
389 : .vector_size = sizeof (u32),
390 : .n_errors = ARP_N_ERROR,
391 : .error_counters = arp_error_counters,
392 : .n_next_nodes = ARP_REPLY_N_NEXT,
393 : .next_nodes =
394 : {
395 : [ARP_REPLY_NEXT_DROP] = "error-drop",
396 : [ARP_REPLY_NEXT_REPLY_TX] = "interface-output",
397 : },
398 : .format_buffer = format_ethernet_arp_header,
399 : .format_trace = format_ethernet_arp_input_trace,
400 : };
401 :
402 : static clib_error_t *
403 0 : show_ip4_arp (vlib_main_t * vm,
404 : unformat_input_t * input, vlib_cli_command_t * cmd)
405 : {
406 0 : arp_proxy_main_t *am = &arp_proxy_main;
407 : ethernet_proxy_arp_t *pa;
408 :
409 0 : if (vec_len (am->proxy_arps))
410 : {
411 0 : vlib_cli_output (vm, "Proxy arps enabled for:");
412 0 : vec_foreach (pa, am->proxy_arps)
413 : {
414 0 : 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 0 : 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 272887 : 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 : */
|