2 * ethernet/arp.c: IP v4 ARP node
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:
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <vnet/ip/ip.h>
19 #include <vnet/ip/ip6.h>
20 #include <vnet/ethernet/ethernet.h>
21 #include <vnet/ethernet/arp_packet.h>
22 #include <vnet/l2/l2_input.h>
23 #include <vppinfra/mhash.h>
24 #include <vnet/fib/ip4_fib.h>
25 #include <vnet/adj/adj_nbr.h>
26 #include <vnet/mpls/mpls.h>
32 * This file contains code to manage the IPv4 ARP tables (IP Address
33 * to MAC Address lookup).
37 void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
40 * @brief Per-interface ARP configuration and state
42 typedef struct ethernet_arp_interface_t_
45 * Hash table of ARP entries.
46 * Since this hash table is per-interface, the key is only the IPv4 address.
49 } ethernet_arp_interface_t;
56 } ethernet_proxy_arp_t;
64 /* Used for arp event notification only */
67 } pending_resolution_t;
71 /* Hash tables mapping name to opcode. */
72 uword *opcode_by_name;
74 /* lite beer "glean" adjacency handling */
75 uword *pending_resolutions_by_address;
76 pending_resolution_t *pending_resolutions;
78 /* Mac address change notification */
79 uword *mac_changes_by_address;
80 pending_resolution_t *mac_changes;
82 ethernet_arp_ip4_entry_t *ip4_entry_pool;
84 /* ARP attack mitigation */
86 u32 limit_arp_cache_size;
88 /** Per interface state */
89 ethernet_arp_interface_t *ethernet_arp_by_sw_if_index;
91 /* Proxy arp vector */
92 ethernet_proxy_arp_t *proxy_arps;
93 } ethernet_arp_main_t;
95 static ethernet_arp_main_t ethernet_arp_main;
100 ethernet_arp_ip4_over_ethernet_address_t a;
103 #define ETHERNET_ARP_ARGS_REMOVE (1<<0)
104 #define ETHERNET_ARP_ARGS_FLUSH (1<<1)
105 #define ETHERNET_ARP_ARGS_POPULATE (1<<2)
106 } vnet_arp_set_ip4_over_ethernet_rpc_args_t;
109 set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t
113 format_ethernet_arp_hardware_type (u8 * s, va_list * va)
115 ethernet_arp_hardware_type_t h = va_arg (*va, ethernet_arp_hardware_type_t);
119 #define _(n,f) case n: t = #f; break;
120 foreach_ethernet_arp_hardware_type;
124 return format (s, "unknown 0x%x", h);
127 return format (s, "%s", t);
131 format_ethernet_arp_opcode (u8 * s, va_list * va)
133 ethernet_arp_opcode_t o = va_arg (*va, ethernet_arp_opcode_t);
137 #define _(f) case ETHERNET_ARP_OPCODE_##f: t = #f; break;
138 foreach_ethernet_arp_opcode;
142 return format (s, "unknown 0x%x", o);
145 return format (s, "%s", t);
149 unformat_ethernet_arp_opcode_host_byte_order (unformat_input_t * input,
152 int *result = va_arg (*args, int *);
153 ethernet_arp_main_t *am = ðernet_arp_main;
156 /* Numeric opcode. */
157 if (unformat (input, "0x%x", &x) || unformat (input, "%d", &x))
166 if (unformat_user (input, unformat_vlib_number_by_name,
167 am->opcode_by_name, &i))
177 unformat_ethernet_arp_opcode_net_byte_order (unformat_input_t * input,
180 int *result = va_arg (*args, int *);
182 (input, unformat_ethernet_arp_opcode_host_byte_order, result))
185 *result = clib_host_to_net_u16 ((u16) * result);
190 format_ethernet_arp_header (u8 * s, va_list * va)
192 ethernet_arp_header_t *a = va_arg (*va, ethernet_arp_header_t *);
193 u32 max_header_bytes = va_arg (*va, u32);
195 u16 l2_type, l3_type;
197 if (max_header_bytes != 0 && sizeof (a[0]) > max_header_bytes)
198 return format (s, "ARP header truncated");
200 l2_type = clib_net_to_host_u16 (a->l2_type);
201 l3_type = clib_net_to_host_u16 (a->l3_type);
203 indent = format_get_indent (s);
205 s = format (s, "%U, type %U/%U, address size %d/%d",
206 format_ethernet_arp_opcode, clib_net_to_host_u16 (a->opcode),
207 format_ethernet_arp_hardware_type, l2_type,
208 format_ethernet_type, l3_type,
209 a->n_l2_address_bytes, a->n_l3_address_bytes);
211 if (l2_type == ETHERNET_ARP_HARDWARE_TYPE_ethernet
212 && l3_type == ETHERNET_TYPE_IP4)
214 s = format (s, "\n%U%U/%U -> %U/%U",
215 format_white_space, indent,
216 format_ethernet_address, a->ip4_over_ethernet[0].ethernet,
217 format_ip4_address, &a->ip4_over_ethernet[0].ip4,
218 format_ethernet_address, a->ip4_over_ethernet[1].ethernet,
219 format_ip4_address, &a->ip4_over_ethernet[1].ip4);
223 uword n2 = a->n_l2_address_bytes;
224 uword n3 = a->n_l3_address_bytes;
225 s = format (s, "\n%U%U/%U -> %U/%U",
226 format_white_space, indent,
227 format_hex_bytes, a->data + 0 * n2 + 0 * n3, n2,
228 format_hex_bytes, a->data + 1 * n2 + 0 * n3, n3,
229 format_hex_bytes, a->data + 1 * n2 + 1 * n3, n2,
230 format_hex_bytes, a->data + 2 * n2 + 1 * n3, n3);
237 format_ethernet_arp_ip4_entry (u8 * s, va_list * va)
239 vnet_main_t *vnm = va_arg (*va, vnet_main_t *);
240 ethernet_arp_ip4_entry_t *e = va_arg (*va, ethernet_arp_ip4_entry_t *);
241 vnet_sw_interface_t *si;
245 return format (s, "%=12s%=16s%=6s%=20s%=24s", "Time", "IP4",
246 "Flags", "Ethernet", "Interface");
248 si = vnet_get_sw_interface (vnm, e->sw_if_index);
250 if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC)
251 flags = format (flags, "S");
253 if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC)
254 flags = format (flags, "D");
256 s = format (s, "%=12U%=16U%=6s%=20U%=24U",
257 format_vlib_cpu_time, vnm->vlib_main, e->cpu_time_last_updated,
258 format_ip4_address, &e->ip4_address,
259 flags ? (char *) flags : "",
260 format_ethernet_address, e->ethernet_address,
261 format_vnet_sw_interface_name, vnm, si);
270 } ethernet_arp_input_trace_t;
273 format_ethernet_arp_input_trace (u8 * s, va_list * va)
275 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
276 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
277 ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
280 format_ethernet_arp_header,
281 t->packet_data, sizeof (t->packet_data));
287 format_arp_term_input_trace (u8 * s, va_list * va)
289 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
290 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
291 ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
293 /* arp-term trace data saved is either arp or ip6/icmp6 packet:
294 - for arp, the 1st 16-bit field is hw type of value of 0x0001.
295 - for ip6, the first nibble has value of 6. */
296 s = format (s, "%U", t->packet_data[0] == 0 ?
297 format_ethernet_arp_header : format_ip6_header,
298 t->packet_data, sizeof (t->packet_data));
304 arp_nbr_probe (ip_adjacency_t * adj)
306 vnet_main_t *vnm = vnet_get_main ();
307 ip4_main_t *im = &ip4_main;
308 ip_interface_address_t *ia;
309 ethernet_arp_header_t *h;
310 vnet_hw_interface_t *hi;
311 vnet_sw_interface_t *si;
317 vm = vlib_get_main ();
319 si = vnet_get_sw_interface (vnm, adj->rewrite_header.sw_if_index);
321 if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
327 ip4_interface_address_matching_destination (im,
328 &adj->sub_type.nbr.next_hop.
338 vlib_packet_template_get_packet (vm, &im->ip4_arp_request_packet_template,
341 hi = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index);
343 clib_memcpy (h->ip4_over_ethernet[0].ethernet,
344 hi->hw_address, sizeof (h->ip4_over_ethernet[0].ethernet));
346 h->ip4_over_ethernet[0].ip4 = src[0];
347 h->ip4_over_ethernet[1].ip4 = adj->sub_type.nbr.next_hop.ip4;
349 b = vlib_get_buffer (vm, bi);
350 vnet_buffer (b)->sw_if_index[VLIB_RX] =
351 vnet_buffer (b)->sw_if_index[VLIB_TX] = adj->rewrite_header.sw_if_index;
353 /* Add encapsulation string for software interface (e.g. ethernet header). */
354 vnet_rewrite_one_header (adj[0], h, sizeof (ethernet_header_t));
355 vlib_buffer_advance (b, -adj->rewrite_header.data_bytes);
358 vlib_frame_t *f = vlib_get_frame_to_node (vm, hi->output_node_index);
359 u32 *to_next = vlib_frame_vector_args (f);
362 vlib_put_frame_to_node (vm, hi->output_node_index, f);
367 arp_mk_complete (adj_index_t ai, ethernet_arp_ip4_entry_t * e)
369 adj_nbr_update_rewrite
370 (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE,
371 ethernet_build_rewrite (vnet_get_main (),
373 adj_get_link_type (ai), e->ethernet_address));
377 arp_mk_incomplete (adj_index_t ai)
379 ip_adjacency_t *adj = adj_get (ai);
381 adj_nbr_update_rewrite
383 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
384 ethernet_build_rewrite (vnet_get_main (),
385 adj->rewrite_header.sw_if_index,
387 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
390 static ethernet_arp_ip4_entry_t *
391 arp_entry_find (ethernet_arp_interface_t * eai, const ip4_address_t * addr)
393 ethernet_arp_main_t *am = ðernet_arp_main;
394 ethernet_arp_ip4_entry_t *e = NULL;
397 if (NULL != eai->arp_entries)
399 p = hash_get (eai->arp_entries, addr->as_u32);
403 e = pool_elt_at_index (am->ip4_entry_pool, p[0]);
410 arp_mk_complete_walk (adj_index_t ai, void *ctx)
412 ethernet_arp_ip4_entry_t *e = ctx;
414 arp_mk_complete (ai, e);
416 return (ADJ_WALK_RC_CONTINUE);
420 arp_mk_incomplete_walk (adj_index_t ai, void *ctx)
422 arp_mk_incomplete (ai);
424 return (ADJ_WALK_RC_CONTINUE);
428 arp_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai)
430 ethernet_arp_main_t *am = ðernet_arp_main;
431 ethernet_arp_interface_t *arp_int;
432 ethernet_arp_ip4_entry_t *e;
437 vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index);
438 arp_int = &am->ethernet_arp_by_sw_if_index[sw_if_index];
439 e = arp_entry_find (arp_int, &adj->sub_type.nbr.next_hop.ip4);
443 adj_nbr_walk_nh4 (sw_if_index,
444 &e->ip4_address, arp_mk_complete_walk, e);
449 * no matching ARP entry.
450 * construct the rewire required to for an ARP packet, and stick
451 * that in the adj's pipe to smoke.
453 adj_nbr_update_rewrite (ai,
454 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
455 ethernet_build_rewrite (vnm,
458 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
461 * since the FIB has added this adj for a route, it makes sense it may
462 * want to forward traffic sometime soon. Let's send a speculative ARP.
463 * just one. If we were to do periodically that wouldn't be bad either,
464 * but that's more code than i'm prepared to write at this time for
465 * relatively little reward.
472 vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm,
473 vnet_arp_set_ip4_over_ethernet_rpc_args_t
476 ethernet_arp_ip4_entry_t *e = 0;
477 ethernet_arp_main_t *am = ðernet_arp_main;
478 ethernet_arp_ip4_over_ethernet_address_t *a = &args->a;
479 vlib_main_t *vm = vlib_get_main ();
480 int make_new_arp_cache_entry = 1;
482 pending_resolution_t *pr, *mc;
483 ethernet_arp_interface_t *arp_int;
484 int is_static = args->is_static;
485 u32 sw_if_index = args->sw_if_index;
487 vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index);
489 arp_int = &am->ethernet_arp_by_sw_if_index[sw_if_index];
491 if (NULL != arp_int->arp_entries)
493 p = hash_get (arp_int->arp_entries, a->ip4.as_u32);
496 e = pool_elt_at_index (am->ip4_entry_pool, p[0]);
498 /* Refuse to over-write static arp. */
499 if (!is_static && (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC))
501 make_new_arp_cache_entry = 0;
505 if (make_new_arp_cache_entry)
509 .fp_proto = FIB_PROTOCOL_IP4,
517 pool_get (am->ip4_entry_pool, e);
519 if (NULL == arp_int->arp_entries)
521 arp_int->arp_entries = hash_create (0, sizeof (u32));
524 hash_set (arp_int->arp_entries, a->ip4.as_u32, e - am->ip4_entry_pool);
526 e->sw_if_index = sw_if_index;
527 e->ip4_address = a->ip4;
528 clib_memcpy (e->ethernet_address,
529 a->ethernet, sizeof (e->ethernet_address));
531 fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index);
533 fib_table_entry_update_one_path (fib_index,
536 FIB_ENTRY_FLAG_ATTACHED,
541 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
546 * prevent a DoS attack from the data-plane that
547 * spams us with no-op updates to the MAC address
549 if (0 == memcmp (e->ethernet_address,
550 a->ethernet, sizeof (e->ethernet_address)))
553 /* Update time stamp and ethernet address. */
554 clib_memcpy (e->ethernet_address, a->ethernet,
555 sizeof (e->ethernet_address));
558 e->cpu_time_last_updated = clib_cpu_time_now ();
560 e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC;
562 e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC;
564 adj_nbr_walk_nh4 (sw_if_index, &e->ip4_address, arp_mk_complete_walk, e);
566 /* Customer(s) waiting for this address to be resolved? */
567 p = hash_get (am->pending_resolutions_by_address, a->ip4.as_u32);
573 while (next_index != (u32) ~ 0)
575 pr = pool_elt_at_index (am->pending_resolutions, next_index);
576 vlib_process_signal_event (vm, pr->node_index,
577 pr->type_opaque, pr->data);
578 next_index = pr->next_index;
579 pool_put (am->pending_resolutions, pr);
582 hash_unset (am->pending_resolutions_by_address, a->ip4.as_u32);
585 /* Customer(s) requesting ARP event for this address? */
586 p = hash_get (am->mac_changes_by_address, a->ip4.as_u32);
592 while (next_index != (u32) ~ 0)
594 int (*fp) (u32, u8 *, u32, u32);
596 mc = pool_elt_at_index (am->mac_changes, next_index);
597 fp = mc->data_callback;
599 /* Call the user's data callback, return 1 to suppress dup events */
601 rv = (*fp) (mc->data, a->ethernet, sw_if_index, 0);
604 * Signal the resolver process, as long as the user
605 * says they want to be notified
608 vlib_process_signal_event (vm, mc->node_index,
609 mc->type_opaque, mc->data);
610 next_index = mc->next_index;
618 vnet_register_ip4_arp_resolution_event (vnet_main_t * vnm,
621 uword type_opaque, uword data)
623 ethernet_arp_main_t *am = ðernet_arp_main;
624 ip4_address_t *address = address_arg;
626 pending_resolution_t *pr;
628 pool_get (am->pending_resolutions, pr);
631 pr->node_index = node_index;
632 pr->type_opaque = type_opaque;
634 pr->data_callback = 0;
636 p = hash_get (am->pending_resolutions_by_address, address->as_u32);
639 /* Insert new resolution at the head of the list */
640 pr->next_index = p[0];
641 hash_unset (am->pending_resolutions_by_address, address->as_u32);
644 hash_set (am->pending_resolutions_by_address, address->as_u32,
645 pr - am->pending_resolutions);
649 vnet_add_del_ip4_arp_change_event (vnet_main_t * vnm,
654 uword type_opaque, uword data, int is_add)
656 ethernet_arp_main_t *am = ðernet_arp_main;
657 ip4_address_t *address = address_arg;
659 pending_resolution_t *mc;
660 void (*fp) (u32, u8 *) = data_callback;
664 pool_get (am->mac_changes, mc);
667 mc->node_index = node_index;
668 mc->type_opaque = type_opaque;
670 mc->data_callback = data_callback;
673 p = hash_get (am->mac_changes_by_address, address->as_u32);
676 /* Insert new resolution at the head of the list */
677 mc->next_index = p[0];
678 hash_unset (am->mac_changes_by_address, address->as_u32);
681 hash_set (am->mac_changes_by_address, address->as_u32,
682 mc - am->mac_changes);
688 pending_resolution_t *mc_last = 0;
690 p = hash_get (am->mac_changes_by_address, address->as_u32);
692 return VNET_API_ERROR_NO_SUCH_ENTRY;
696 while (index != (u32) ~ 0)
698 mc = pool_elt_at_index (am->mac_changes, index);
699 if (mc->node_index == node_index &&
700 mc->type_opaque == type_opaque && mc->pid == pid)
702 /* Clients may need to clean up pool entries, too */
704 (*fp) (mc->data, 0 /* no new mac addrs */ );
707 hash_unset (am->mac_changes_by_address, address->as_u32);
708 if (mc->next_index != ~0)
709 hash_set (am->mac_changes_by_address, address->as_u32,
711 pool_put (am->mac_changes, mc);
717 mc_last->next_index = mc->next_index;
718 pool_put (am->mac_changes, mc);
723 index = mc->next_index;
726 return VNET_API_ERROR_NO_SUCH_ENTRY;
730 /* Either we drop the packet or we send a reply to the sender. */
734 ARP_INPUT_NEXT_REPLY_TX,
738 #define foreach_ethernet_arp_error \
739 _ (replies_sent, "ARP replies sent") \
740 _ (l2_type_not_ethernet, "L2 type not ethernet") \
741 _ (l3_type_not_ip4, "L3 type not IP4") \
742 _ (l3_src_address_not_local, "IP4 source address not local to subnet") \
743 _ (l3_dst_address_not_local, "IP4 destination address not local to subnet") \
744 _ (l3_src_address_is_local, "IP4 source address matches local interface") \
745 _ (l3_src_address_learned, "ARP request IP4 source address learned") \
746 _ (replies_received, "ARP replies received") \
747 _ (opcode_not_request, "ARP opcode not request") \
748 _ (proxy_arp_replies_sent, "Proxy ARP replies sent") \
749 _ (l2_address_mismatch, "ARP hw addr does not match L2 frame src addr") \
750 _ (missing_interface_address, "ARP missing interface address") \
751 _ (gratuitous_arp, "ARP probe or announcement dropped") \
752 _ (interface_no_table, "Interface is not mapped to an IP table") \
756 #define _(sym,string) ETHERNET_ARP_ERROR_##sym,
757 foreach_ethernet_arp_error
759 ETHERNET_ARP_N_ERROR,
760 } ethernet_arp_input_error_t;
764 unset_random_arp_entry (void)
766 ethernet_arp_main_t *am = ðernet_arp_main;
767 ethernet_arp_ip4_entry_t *e;
768 vnet_main_t *vnm = vnet_get_main ();
769 ethernet_arp_ip4_over_ethernet_address_t delme;
772 index = pool_next_index (am->ip4_entry_pool, am->arp_delete_rotor);
773 am->arp_delete_rotor = index;
775 /* Try again from elt 0, could happen if an intfc goes down */
778 index = pool_next_index (am->ip4_entry_pool, am->arp_delete_rotor);
779 am->arp_delete_rotor = index;
782 /* Nothing left in the pool */
786 e = pool_elt_at_index (am->ip4_entry_pool, index);
788 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
789 delme.ip4.as_u32 = e->ip4_address.as_u32;
791 vnet_arp_unset_ip4_over_ethernet (vnm, e->sw_if_index, &delme);
795 arp_unnumbered (vlib_buffer_t * p0,
796 u32 pi0, ethernet_header_t * eth0, u32 sw_if_index)
798 vlib_main_t *vm = vlib_get_main ();
799 vnet_main_t *vnm = vnet_get_main ();
800 vnet_interface_main_t *vim = &vnm->interface_main;
801 vnet_sw_interface_t *si;
802 vnet_hw_interface_t *hi;
803 u32 unnum_src_sw_if_index;
804 u32 *broadcast_swifs = 0;
809 u8 dst_mac_address[6];
811 ethernet_arp_header_t *arp0;
813 /* Save the dst mac address */
814 clib_memcpy (dst_mac_address, eth0->dst_address, sizeof (dst_mac_address));
816 /* Figure out which sw_if_index supplied the address */
817 unnum_src_sw_if_index = sw_if_index;
819 /* Track down all users of the unnumbered source */
821 pool_foreach (si, vim->sw_interfaces,
823 if (si->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED &&
824 (si->unnumbered_sw_if_index == unnum_src_sw_if_index))
826 vec_add1 (broadcast_swifs, si->sw_if_index);
831 /* If there are no interfaces un-unmbered to this interface,
833 if (0 == vec_len (broadcast_swifs))
836 /* Allocate buffering if we need it */
837 if (vec_len (broadcast_swifs) > 1)
839 vec_validate (buffers, vec_len (broadcast_swifs) - 2);
840 n_alloc = vlib_buffer_alloc (vm, buffers, vec_len (buffers));
841 _vec_len (buffers) = n_alloc;
842 for (i = 0; i < n_alloc; i++)
844 b0 = vlib_get_buffer (vm, buffers[i]);
846 /* xerox (partially built) ARP pkt */
847 clib_memcpy (b0->data, p0->data,
848 p0->current_length + p0->current_data);
849 b0->current_data = p0->current_data;
850 b0->current_length = p0->current_length;
851 vnet_buffer (b0)->sw_if_index[VLIB_RX] =
852 vnet_buffer (p0)->sw_if_index[VLIB_RX];
856 vec_insert (buffers, 1, 0);
859 for (i = 0; i < vec_len (buffers); i++)
861 b0 = vlib_get_buffer (vm, buffers[i]);
862 arp0 = vlib_buffer_get_current (b0);
864 hi = vnet_get_sup_hw_interface (vnm, broadcast_swifs[i]);
865 si = vnet_get_sw_interface (vnm, broadcast_swifs[i]);
867 /* For decoration, most likely */
868 vnet_buffer (b0)->sw_if_index[VLIB_TX] = hi->sw_if_index;
870 /* Fix ARP pkt src address */
871 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, hi->hw_address, 6);
873 /* Build L2 encaps for this swif */
874 header_size = sizeof (ethernet_header_t);
875 if (si->sub.eth.flags.one_tag)
877 else if (si->sub.eth.flags.two_tags)
880 vlib_buffer_advance (b0, -header_size);
881 eth0 = vlib_buffer_get_current (b0);
883 if (si->sub.eth.flags.one_tag)
885 ethernet_vlan_header_t *outer = (void *) (eth0 + 1);
887 eth0->type = si->sub.eth.flags.dot1ad ?
888 clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
889 clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
890 outer->priority_cfi_and_id =
891 clib_host_to_net_u16 (si->sub.eth.outer_vlan_id);
892 outer->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
895 else if (si->sub.eth.flags.two_tags)
897 ethernet_vlan_header_t *outer = (void *) (eth0 + 1);
898 ethernet_vlan_header_t *inner = (void *) (outer + 1);
900 eth0->type = si->sub.eth.flags.dot1ad ?
901 clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
902 clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
903 outer->priority_cfi_and_id =
904 clib_host_to_net_u16 (si->sub.eth.outer_vlan_id);
905 outer->type = clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
906 inner->priority_cfi_and_id =
907 clib_host_to_net_u16 (si->sub.eth.inner_vlan_id);
908 inner->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
913 eth0->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
916 /* Restore the original dst address, set src address */
917 clib_memcpy (eth0->dst_address, dst_mac_address,
918 sizeof (eth0->dst_address));
919 clib_memcpy (eth0->src_address, hi->hw_address,
920 sizeof (eth0->src_address));
922 /* Transmit replicas */
926 vlib_get_frame_to_node (vm, hi->output_node_index);
927 u32 *to_next = vlib_frame_vector_args (f);
928 to_next[0] = buffers[i];
930 vlib_put_frame_to_node (vm, hi->output_node_index, f);
934 /* The regular path outputs the original pkt.. */
935 vnet_buffer (p0)->sw_if_index[VLIB_TX] = broadcast_swifs[0];
937 vec_free (broadcast_swifs);
944 arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
946 ethernet_arp_main_t *am = ðernet_arp_main;
947 vnet_main_t *vnm = vnet_get_main ();
948 ip4_main_t *im4 = &ip4_main;
949 u32 n_left_from, next_index, *from, *to_next;
950 u32 n_replies_sent = 0, n_proxy_arp_replies_sent = 0;
952 from = vlib_frame_vector_args (frame);
953 n_left_from = frame->n_vectors;
954 next_index = node->cached_next_index;
956 if (node->flags & VLIB_NODE_FLAG_TRACE)
957 vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
959 sizeof (ethernet_arp_input_trace_t));
961 while (n_left_from > 0)
965 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
967 while (n_left_from > 0 && n_left_to_next > 0)
970 vnet_hw_interface_t *hw_if0;
971 ethernet_arp_header_t *arp0;
972 ethernet_header_t *eth0;
973 ip_adjacency_t *adj0;
974 ip4_address_t *if_addr0, proxy_src;
975 u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0;
976 u8 is_request0, dst_is_local0, is_unnum0;
977 ethernet_proxy_arp_t *pa;
978 fib_node_index_t dst_fei, src_fei;
980 fib_entry_flag_t src_flags, dst_flags;
990 p0 = vlib_get_buffer (vm, pi0);
991 arp0 = vlib_buffer_get_current (p0);
993 is_request0 = arp0->opcode
994 == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
996 error0 = ETHERNET_ARP_ERROR_replies_sent;
1000 clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet) ?
1001 ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0);
1004 clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
1005 ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0);
1007 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
1012 /* Check that IP address is local and matches incoming interface. */
1013 fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
1014 if (~0 == fib_index0)
1016 error0 = ETHERNET_ARP_ERROR_interface_no_table;
1020 dst_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
1021 &arp0->ip4_over_ethernet[1].ip4,
1023 dst_flags = fib_entry_get_flags_for_source (dst_fei,
1024 FIB_SOURCE_INTERFACE);
1027 fib_entry_get_resolving_interface_for_source (dst_fei,
1028 FIB_SOURCE_INTERFACE);
1030 if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
1032 error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
1036 /* Honor unnumbered interface, if any */
1037 is_unnum0 = sw_if_index0 != conn_sw_if_index0;
1039 /* Source must also be local to subnet of matching interface address. */
1040 src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
1041 &arp0->ip4_over_ethernet[0].ip4,
1043 src_flags = fib_entry_get_flags (src_fei);
1045 if (!((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
1046 (FIB_ENTRY_FLAG_CONNECTED & src_flags)))
1049 * The packet was sent from an address that is not connected nor attached
1050 * i.e. it is not from an address that is covered by a link's sub-net,
1051 * nor is it a already learned host resp.
1053 error0 = ETHERNET_ARP_ERROR_l3_src_address_not_local;
1056 if (sw_if_index0 != fib_entry_get_resolving_interface (src_fei))
1059 * The interface the ARP was received on is not the interface
1060 * on which the covering prefix is configured. Maybe this is a case
1066 /* Reject requests/replies with our local interface address. */
1067 if (FIB_ENTRY_FLAG_LOCAL & src_flags)
1069 error0 = ETHERNET_ARP_ERROR_l3_src_address_is_local;
1073 dst_is_local0 = (FIB_ENTRY_FLAG_LOCAL & dst_flags);
1074 fib_entry_get_prefix (dst_fei, &pfx0);
1075 if_addr0 = &pfx0.fp_addr.ip4;
1077 /* Fill in ethernet header. */
1078 eth0 = ethernet_buffer_get_header (p0);
1080 /* Trash ARP packets whose ARP-level source addresses do not
1081 match their L2-frame-level source addresses */
1082 if (memcmp (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
1083 sizeof (eth0->src_address)))
1085 error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
1089 /* Learn or update sender's mapping only for requests or unicasts
1090 that don't match local interface address. */
1091 if (ethernet_address_cast (eth0->dst_address) ==
1092 ETHERNET_ADDRESS_UNICAST || is_request0)
1094 if (am->limit_arp_cache_size &&
1095 pool_elts (am->ip4_entry_pool) >= am->limit_arp_cache_size)
1096 unset_random_arp_entry ();
1098 vnet_arp_set_ip4_over_ethernet (vnm, sw_if_index0,
1099 &arp0->ip4_over_ethernet[0],
1100 0 /* is_static */ );
1101 error0 = ETHERNET_ARP_ERROR_l3_src_address_learned;
1104 /* Only send a reply for requests sent which match a local interface. */
1105 if (!(is_request0 && dst_is_local0))
1109 clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply) ?
1110 ETHERNET_ARP_ERROR_replies_received : error0);
1116 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
1117 hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1119 /* Send reply back through input interface */
1120 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
1121 next0 = ARP_INPUT_NEXT_REPLY_TX;
1123 arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
1125 arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
1127 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet,
1128 hw_if0->hw_address, 6);
1129 clib_mem_unaligned (&arp0->ip4_over_ethernet[0].ip4.data_u32, u32) =
1132 /* Hardware must be ethernet-like. */
1133 ASSERT (vec_len (hw_if0->hw_address) == 6);
1135 clib_memcpy (eth0->dst_address, eth0->src_address, 6);
1136 clib_memcpy (eth0->src_address, hw_if0->hw_address, 6);
1138 /* Figure out how much to rewind current data from adjacency. */
1139 /* get the adj from the destination's covering connected */
1143 adj_get (fib_entry_get_adj_for_source
1144 (ip4_fib_table_lookup
1145 (ip4_fib_get (fib_index0),
1146 &arp0->ip4_over_ethernet[1].ip4, 31),
1147 FIB_SOURCE_INTERFACE));
1148 if (adj0->lookup_next_index != IP_LOOKUP_NEXT_GLEAN)
1150 error0 = ETHERNET_ARP_ERROR_missing_interface_address;
1155 if (!arp_unnumbered (p0, pi0, eth0, conn_sw_if_index0))
1159 vlib_buffer_advance (p0, -adj0->rewrite_header.data_bytes);
1161 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1162 n_left_to_next, pi0, next0);
1164 n_replies_sent += 1;
1168 if (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ||
1169 (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
1170 arp0->ip4_over_ethernet[1].ip4.as_u32))
1172 error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
1175 /* See if proxy arp is configured for the address */
1178 vnet_sw_interface_t *si;
1179 u32 this_addr = clib_net_to_host_u32
1180 (arp0->ip4_over_ethernet[1].ip4.as_u32);
1183 si = vnet_get_sw_interface (vnm, sw_if_index0);
1185 if (!(si->flags & VNET_SW_INTERFACE_FLAG_PROXY_ARP))
1188 fib_index0 = vec_elt (im4->fib_index_by_sw_if_index,
1191 vec_foreach (pa, am->proxy_arps)
1193 u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr);
1194 u32 hi_addr = clib_net_to_host_u32 (pa->hi_addr);
1196 /* an ARP request hit in the proxy-arp table? */
1197 if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
1198 (fib_index0 == pa->fib_index))
1200 eth0 = ethernet_buffer_get_header (p0);
1202 arp0->ip4_over_ethernet[1].ip4.data_u32;
1205 * Rewind buffer, direct code above not to
1206 * think too hard about it.
1208 if_addr0 = &proxy_src;
1210 i32 ethernet_start =
1211 vnet_buffer (p0)->ethernet.start_of_ethernet_header;
1212 i32 rewind = p0->current_data - ethernet_start;
1213 vlib_buffer_advance (p0, -rewind);
1214 n_proxy_arp_replies_sent++;
1222 next0 = ARP_INPUT_NEXT_DROP;
1223 p0->error = node->errors[error0];
1225 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1226 n_left_to_next, pi0, next0);
1229 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1232 vlib_error_count (vm, node->node_index,
1233 ETHERNET_ARP_ERROR_replies_sent,
1234 n_replies_sent - n_proxy_arp_replies_sent);
1236 vlib_error_count (vm, node->node_index,
1237 ETHERNET_ARP_ERROR_proxy_arp_replies_sent,
1238 n_proxy_arp_replies_sent);
1239 return frame->n_vectors;
1242 static char *ethernet_arp_error_strings[] = {
1243 #define _(sym,string) string,
1244 foreach_ethernet_arp_error
1249 VLIB_REGISTER_NODE (arp_input_node, static) =
1251 .function = arp_input,
1252 .name = "arp-input",
1253 .vector_size = sizeof (u32),
1254 .n_errors = ETHERNET_ARP_N_ERROR,
1255 .error_strings = ethernet_arp_error_strings,
1256 .n_next_nodes = ARP_INPUT_N_NEXT,
1258 [ARP_INPUT_NEXT_DROP] = "error-drop",
1259 [ARP_INPUT_NEXT_REPLY_TX] = "interface-output",
1261 .format_buffer = format_ethernet_arp_header,
1262 .format_trace = format_ethernet_arp_input_trace,
1267 ip4_arp_entry_sort (void *a1, void *a2)
1269 ethernet_arp_ip4_entry_t *e1 = a1;
1270 ethernet_arp_ip4_entry_t *e2 = a2;
1273 vnet_main_t *vnm = vnet_get_main ();
1275 cmp = vnet_sw_interface_compare (vnm, e1->sw_if_index, e2->sw_if_index);
1277 cmp = ip4_address_compare (&e1->ip4_address, &e2->ip4_address);
1281 ethernet_arp_ip4_entry_t *
1282 ip4_neighbor_entries (u32 sw_if_index)
1284 ethernet_arp_main_t *am = ðernet_arp_main;
1285 ethernet_arp_ip4_entry_t *n, *ns = 0;
1288 pool_foreach (n, am->ip4_entry_pool, ({
1289 if (sw_if_index != ~0 && n->sw_if_index != sw_if_index)
1291 vec_add1 (ns, n[0]);
1296 vec_sort_with_function (ns, ip4_arp_entry_sort);
1300 static clib_error_t *
1301 show_ip4_arp (vlib_main_t * vm,
1302 unformat_input_t * input, vlib_cli_command_t * cmd)
1304 vnet_main_t *vnm = vnet_get_main ();
1305 ethernet_arp_main_t *am = ðernet_arp_main;
1306 ethernet_arp_ip4_entry_t *e, *es;
1307 ethernet_proxy_arp_t *pa;
1308 clib_error_t *error = 0;
1311 /* Filter entries by interface if given. */
1313 (void) unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index);
1315 es = ip4_neighbor_entries (sw_if_index);
1318 vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, 0);
1321 vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, e);
1326 if (vec_len (am->proxy_arps))
1328 vlib_cli_output (vm, "Proxy arps enabled for:");
1329 vec_foreach (pa, am->proxy_arps)
1331 vlib_cli_output (vm, "Fib_index %d %U - %U ",
1333 format_ip4_address, &pa->lo_addr,
1334 format_ip4_address, &pa->hi_addr);
1342 * Display all the IPv4 ARP entries.
1345 * Example of how to display the IPv4 ARP table:
1346 * @cliexstart{show ip arp}
1347 * Time FIB IP4 Flags Ethernet Interface
1348 * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
1349 * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
1350 * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
1351 * Proxy arps enabled for:
1352 * Fib_index 0 6.0.0.1 - 6.0.0.11
1356 VLIB_CLI_COMMAND (show_ip4_arp_command, static) = {
1357 .path = "show ip arp",
1358 .function = show_ip4_arp,
1359 .short_help = "show ip arp",
1365 pg_edit_t l2_type, l3_type;
1366 pg_edit_t n_l2_address_bytes, n_l3_address_bytes;
1372 } ip4_over_ethernet[2];
1373 } pg_ethernet_arp_header_t;
1376 pg_ethernet_arp_header_init (pg_ethernet_arp_header_t * p)
1378 /* Initialize fields that are not bit fields in the IP header. */
1379 #define _(f) pg_edit_init (&p->f, ethernet_arp_header_t, f);
1382 _(n_l2_address_bytes);
1383 _(n_l3_address_bytes);
1385 _(ip4_over_ethernet[0].ethernet);
1386 _(ip4_over_ethernet[0].ip4);
1387 _(ip4_over_ethernet[1].ethernet);
1388 _(ip4_over_ethernet[1].ip4);
1393 unformat_pg_arp_header (unformat_input_t * input, va_list * args)
1395 pg_stream_t *s = va_arg (*args, pg_stream_t *);
1396 pg_ethernet_arp_header_t *p;
1399 p = pg_create_edit_group (s, sizeof (p[0]), sizeof (ethernet_arp_header_t),
1401 pg_ethernet_arp_header_init (p);
1404 pg_edit_set_fixed (&p->l2_type, ETHERNET_ARP_HARDWARE_TYPE_ethernet);
1405 pg_edit_set_fixed (&p->l3_type, ETHERNET_TYPE_IP4);
1406 pg_edit_set_fixed (&p->n_l2_address_bytes, 6);
1407 pg_edit_set_fixed (&p->n_l3_address_bytes, 4);
1409 if (!unformat (input, "%U: %U/%U -> %U/%U",
1411 unformat_ethernet_arp_opcode_net_byte_order, &p->opcode,
1413 unformat_ethernet_address, &p->ip4_over_ethernet[0].ethernet,
1415 unformat_ip4_address, &p->ip4_over_ethernet[0].ip4,
1417 unformat_ethernet_address, &p->ip4_over_ethernet[1].ethernet,
1419 unformat_ip4_address, &p->ip4_over_ethernet[1].ip4))
1421 /* Free up any edits we may have added. */
1422 pg_free_edit_group (s);
1429 ip4_set_arp_limit (u32 arp_limit)
1431 ethernet_arp_main_t *am = ðernet_arp_main;
1433 am->limit_arp_cache_size = arp_limit;
1438 * @brief Control Plane hook to remove an ARP entry
1441 vnet_arp_unset_ip4_over_ethernet (vnet_main_t * vnm,
1442 u32 sw_if_index, void *a_arg)
1444 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1445 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1447 args.sw_if_index = sw_if_index;
1448 args.flags = ETHERNET_ARP_ARGS_REMOVE;
1449 clib_memcpy (&args.a, a, sizeof (*a));
1451 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1452 (u8 *) & args, sizeof (args));
1457 * @brief Internally generated event to flush the ARP cache on an
1458 * interface state change event.
1459 * A flush will remove dynamic ARP entries, and for statics remove the MAC
1460 * address from the corresponding adjacencies.
1463 vnet_arp_flush_ip4_over_ethernet (vnet_main_t * vnm,
1464 u32 sw_if_index, void *a_arg)
1466 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1467 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1469 args.sw_if_index = sw_if_index;
1470 args.flags = ETHERNET_ARP_ARGS_FLUSH;
1471 clib_memcpy (&args.a, a, sizeof (*a));
1473 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1474 (u8 *) & args, sizeof (args));
1479 * @brief Internally generated event to populate the ARP cache on an
1480 * interface state change event.
1481 * For static entries this will re-source the adjacencies.
1483 * @param sw_if_index The interface on which the ARP entires are acted
1486 vnet_arp_populate_ip4_over_ethernet (vnet_main_t * vnm,
1487 u32 sw_if_index, void *a_arg)
1489 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1490 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1492 args.sw_if_index = sw_if_index;
1493 args.flags = ETHERNET_ARP_ARGS_POPULATE;
1494 clib_memcpy (&args.a, a, sizeof (*a));
1496 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1497 (u8 *) & args, sizeof (args));
1502 * arp_add_del_interface_address
1504 * callback when an interface address is added or deleted
1507 arp_add_del_interface_address (ip4_main_t * im,
1510 ip4_address_t * address,
1512 u32 if_address_index, u32 is_del)
1515 * Flush the ARP cache of all entries covered by the address
1516 * that is being removed.
1518 ethernet_arp_main_t *am = ðernet_arp_main;
1519 ethernet_arp_ip4_entry_t *e;
1521 if (vec_len (am->ethernet_arp_by_sw_if_index) <= sw_if_index)
1526 ethernet_arp_interface_t *eai;
1527 u32 i, *to_delete = 0;
1530 eai = &am->ethernet_arp_by_sw_if_index[sw_if_index];
1533 hash_foreach_pair (pair, eai->arp_entries,
1535 e = pool_elt_at_index(am->ip4_entry_pool,
1537 if (ip4_destination_matches_route (im, &e->ip4_address,
1538 address, address_length))
1540 vec_add1 (to_delete, e - am->ip4_entry_pool);
1545 for (i = 0; i < vec_len (to_delete); i++)
1547 ethernet_arp_ip4_over_ethernet_address_t delme;
1548 e = pool_elt_at_index (am->ip4_entry_pool, to_delete[i]);
1550 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
1551 delme.ip4.as_u32 = e->ip4_address.as_u32;
1553 vnet_arp_flush_ip4_over_ethernet (vnet_get_main (),
1554 e->sw_if_index, &delme);
1557 vec_free (to_delete);
1561 static clib_error_t *
1562 ethernet_arp_init (vlib_main_t * vm)
1564 ethernet_arp_main_t *am = ðernet_arp_main;
1565 ip4_main_t *im = &ip4_main;
1566 clib_error_t *error;
1569 if ((error = vlib_call_init_function (vm, ethernet_init)))
1572 ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, arp_input_node.index);
1574 pn = pg_get_node (arp_input_node.index);
1575 pn->unformat_edit = unformat_pg_arp_header;
1577 am->opcode_by_name = hash_create_string (0, sizeof (uword));
1578 #define _(o) hash_set_mem (am->opcode_by_name, #o, ETHERNET_ARP_OPCODE_##o);
1579 foreach_ethernet_arp_opcode;
1582 /* $$$ configurable */
1583 am->limit_arp_cache_size = 50000;
1585 am->pending_resolutions_by_address = hash_create (0, sizeof (uword));
1586 am->mac_changes_by_address = hash_create (0, sizeof (uword));
1588 /* don't trace ARP error packets */
1590 vlib_node_runtime_t *rt =
1591 vlib_node_get_runtime (vm, arp_input_node.index);
1594 vnet_pcap_drop_trace_filter_add_del \
1595 (rt->errors[ETHERNET_ARP_ERROR_##a], \
1597 foreach_ethernet_arp_error
1601 ip4_add_del_interface_address_callback_t cb;
1602 cb.function = arp_add_del_interface_address;
1603 cb.function_opaque = 0;
1604 vec_add1 (im->add_del_interface_address_callbacks, cb);
1609 VLIB_INIT_FUNCTION (ethernet_arp_init);
1612 arp_entry_free (ethernet_arp_interface_t * eai, ethernet_arp_ip4_entry_t * e)
1614 ethernet_arp_main_t *am = ðernet_arp_main;
1616 fib_table_entry_delete_index (e->fib_entry_index, FIB_SOURCE_ADJ);
1617 hash_unset (eai->arp_entries, e->ip4_address.as_u32);
1618 pool_put (am->ip4_entry_pool, e);
1622 vnet_arp_unset_ip4_over_ethernet_internal (vnet_main_t * vnm,
1623 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1626 ethernet_arp_main_t *am = ðernet_arp_main;
1627 ethernet_arp_ip4_entry_t *e;
1628 ethernet_arp_interface_t *eai;
1630 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1632 e = arp_entry_find (eai, &args->a.ip4);
1636 arp_entry_free (eai, e);
1638 adj_nbr_walk_nh4 (e->sw_if_index,
1639 &e->ip4_address, arp_mk_incomplete_walk, NULL);
1646 vnet_arp_flush_ip4_over_ethernet_internal (vnet_main_t * vnm,
1647 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1650 ethernet_arp_main_t *am = ðernet_arp_main;
1651 ethernet_arp_ip4_entry_t *e;
1652 ethernet_arp_interface_t *eai;
1654 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1656 e = arp_entry_find (eai, &args->a.ip4);
1660 adj_nbr_walk_nh4 (e->sw_if_index,
1661 &e->ip4_address, arp_mk_incomplete_walk, e);
1664 * The difference between flush and unset, is that an unset
1665 * means delete for static and dynamic entries. A flush
1666 * means delete only for dynamic. Flushing is what the DP
1667 * does in response to interface events. unset is only done
1668 * by the control plane.
1670 if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC)
1672 arp_entry_free (eai, e);
1679 vnet_arp_populate_ip4_over_ethernet_internal (vnet_main_t * vnm,
1680 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1683 ethernet_arp_main_t *am = ðernet_arp_main;
1684 ethernet_arp_ip4_entry_t *e;
1685 ethernet_arp_interface_t *eai;
1687 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1689 e = arp_entry_find (eai, &args->a.ip4);
1693 adj_nbr_walk_nh4 (e->sw_if_index,
1694 &e->ip4_address, arp_mk_complete_walk, e);
1700 set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t
1703 vnet_main_t *vm = vnet_get_main ();
1704 ASSERT (os_get_cpu_number () == 0);
1706 if (a->flags & ETHERNET_ARP_ARGS_REMOVE)
1707 vnet_arp_unset_ip4_over_ethernet_internal (vm, a);
1708 else if (a->flags & ETHERNET_ARP_ARGS_FLUSH)
1709 vnet_arp_flush_ip4_over_ethernet_internal (vm, a);
1710 else if (a->flags & ETHERNET_ARP_ARGS_POPULATE)
1711 vnet_arp_populate_ip4_over_ethernet_internal (vm, a);
1713 vnet_arp_set_ip4_over_ethernet_internal (vm, a);
1717 * @brief Invoked when the interface's admin state changes
1719 static clib_error_t *
1720 ethernet_arp_sw_interface_up_down (vnet_main_t * vnm,
1721 u32 sw_if_index, u32 flags)
1723 ethernet_arp_main_t *am = ðernet_arp_main;
1724 ethernet_arp_ip4_entry_t *e;
1725 u32 i, *to_delete = 0;
1728 pool_foreach (e, am->ip4_entry_pool,
1730 if (e->sw_if_index == sw_if_index)
1731 vec_add1 (to_delete,
1732 e - am->ip4_entry_pool);
1736 for (i = 0; i < vec_len (to_delete); i++)
1738 ethernet_arp_ip4_over_ethernet_address_t delme;
1739 e = pool_elt_at_index (am->ip4_entry_pool, to_delete[i]);
1741 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
1742 delme.ip4.as_u32 = e->ip4_address.as_u32;
1744 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
1746 vnet_arp_populate_ip4_over_ethernet (vnm, e->sw_if_index, &delme);
1750 vnet_arp_flush_ip4_over_ethernet (vnm, e->sw_if_index, &delme);
1754 vec_free (to_delete);
1759 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ethernet_arp_sw_interface_up_down);
1762 increment_ip4_and_mac_address (ethernet_arp_ip4_over_ethernet_address_t * a)
1767 for (i = 3; i >= 0; i--)
1769 old = a->ip4.as_u8[i];
1770 a->ip4.as_u8[i] += 1;
1771 if (old < a->ip4.as_u8[i])
1775 for (i = 5; i >= 0; i--)
1777 old = a->ethernet[i];
1778 a->ethernet[i] += 1;
1779 if (old < a->ethernet[i])
1785 vnet_arp_set_ip4_over_ethernet (vnet_main_t * vnm,
1786 u32 sw_if_index, void *a_arg, int is_static)
1788 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1789 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1791 args.sw_if_index = sw_if_index;
1792 args.is_static = is_static;
1794 clib_memcpy (&args.a, a, sizeof (*a));
1796 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1797 (u8 *) & args, sizeof (args));
1802 vnet_proxy_arp_add_del (ip4_address_t * lo_addr,
1803 ip4_address_t * hi_addr, u32 fib_index, int is_del)
1805 ethernet_arp_main_t *am = ðernet_arp_main;
1806 ethernet_proxy_arp_t *pa;
1807 u32 found_at_index = ~0;
1809 vec_foreach (pa, am->proxy_arps)
1811 if (pa->lo_addr == lo_addr->as_u32
1812 && pa->hi_addr == hi_addr->as_u32 && pa->fib_index == fib_index)
1814 found_at_index = pa - am->proxy_arps;
1819 if (found_at_index != ~0)
1821 /* Delete, otherwise it's already in the table */
1823 vec_delete (am->proxy_arps, 1, found_at_index);
1826 /* delete, no such entry */
1828 return VNET_API_ERROR_NO_SUCH_ENTRY;
1830 /* add, not in table */
1831 vec_add2 (am->proxy_arps, pa, 1);
1832 pa->lo_addr = lo_addr->as_u32;
1833 pa->hi_addr = hi_addr->as_u32;
1834 pa->fib_index = fib_index;
1839 * Remove any proxy arp entries asdociated with the
1843 vnet_proxy_arp_fib_reset (u32 fib_id)
1845 ip4_main_t *im = &ip4_main;
1846 ethernet_arp_main_t *am = ðernet_arp_main;
1847 ethernet_proxy_arp_t *pa;
1848 u32 *entries_to_delete = 0;
1853 p = hash_get (im->fib_index_by_table_id, fib_id);
1855 return VNET_API_ERROR_NO_SUCH_ENTRY;
1858 vec_foreach (pa, am->proxy_arps)
1860 if (pa->fib_index == fib_index)
1862 vec_add1 (entries_to_delete, pa - am->proxy_arps);
1866 for (i = 0; i < vec_len (entries_to_delete); i++)
1868 vec_delete (am->proxy_arps, 1, entries_to_delete[i]);
1871 vec_free (entries_to_delete);
1876 static clib_error_t *
1877 ip_arp_add_del_command_fn (vlib_main_t * vm,
1878 unformat_input_t * input, vlib_cli_command_t * cmd)
1880 vnet_main_t *vnm = vnet_get_main ();
1882 ethernet_arp_ip4_over_ethernet_address_t lo_addr, hi_addr, addr;
1891 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1893 /* set ip arp TenGigE1/1/0/1 1.2.3.4 aa:bb:... or aabb.ccdd... */
1894 if (unformat (input, "%U %U %U",
1895 unformat_vnet_sw_interface, vnm, &sw_if_index,
1896 unformat_ip4_address, &addr.ip4,
1897 unformat_ethernet_address, &addr.ethernet))
1900 else if (unformat (input, "delete") || unformat (input, "del"))
1903 else if (unformat (input, "static"))
1906 else if (unformat (input, "count %d", &count))
1909 else if (unformat (input, "fib-id %d", &fib_id))
1911 ip4_main_t *im = &ip4_main;
1912 uword *p = hash_get (im->fib_index_by_table_id, fib_id);
1914 return clib_error_return (0, "fib ID %d doesn't exist\n", fib_id);
1918 else if (unformat (input, "proxy %U - %U",
1919 unformat_ip4_address, &lo_addr.ip4,
1920 unformat_ip4_address, &hi_addr.ip4))
1928 (void) vnet_proxy_arp_add_del (&lo_addr.ip4, &hi_addr.ip4,
1937 for (i = 0; i < count; i++)
1941 uword event_type, *event_data = 0;
1943 /* Park the debug CLI until the arp entry is installed */
1944 vnet_register_ip4_arp_resolution_event
1945 (vnm, &addr.ip4, vlib_current_process (vm),
1946 1 /* type */ , 0 /* data */ );
1948 vnet_arp_set_ip4_over_ethernet
1949 (vnm, sw_if_index, &addr, is_static);
1951 vlib_process_wait_for_event (vm);
1952 event_type = vlib_process_get_events (vm, &event_data);
1953 vec_reset_length (event_data);
1954 if (event_type != 1)
1955 clib_warning ("event type %d unexpected", event_type);
1958 vnet_arp_unset_ip4_over_ethernet (vnm, sw_if_index, &addr);
1960 increment_ip4_and_mac_address (&addr);
1965 return clib_error_return (0, "unknown input `%U'",
1966 format_unformat_error, input);
1974 * Add or delete IPv4 ARP cache entries.
1976 * @note 'set ip arp' options (e.g. delete, static, 'fib-id <id>',
1977 * 'count <number>', 'interface ip4_addr mac_addr') can be added in
1978 * any order and combination.
1982 * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
1983 * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
1984 * @cliexcmd{set ip arp GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1985 * @cliexcmd{set ip arp delete GigabitEthernet2/0/0 6.0.0.3 de:ad:be:ef:ba:be}
1987 * To add or delete an IPv4 ARP cache entry to or from a specific fib
1989 * @cliexcmd{set ip arp fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1990 * @cliexcmd{set ip arp fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1992 * Add or delete IPv4 static ARP cache entries as follows:
1993 * @cliexcmd{set ip arp static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1994 * @cliexcmd{set ip arp static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1996 * For testing / debugging purposes, the 'set ip arp' command can add or
1997 * delete multiple entries. Supply the 'count N' parameter:
1998 * @cliexcmd{set ip arp count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
2001 VLIB_CLI_COMMAND (ip_arp_add_del_command, static) = {
2002 .path = "set ip arp",
2004 "set ip arp [del] <intfc> <ip-address> <mac-address> [static] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
2005 .function = ip_arp_add_del_command_fn,
2009 static clib_error_t *
2010 set_int_proxy_arp_command_fn (vlib_main_t * vm,
2012 input, vlib_cli_command_t * cmd)
2014 vnet_main_t *vnm = vnet_get_main ();
2016 vnet_sw_interface_t *si;
2020 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2022 if (unformat (input, "%U", unformat_vnet_sw_interface,
2025 else if (unformat (input, "enable") || unformat (input, "on"))
2027 else if (unformat (input, "disable") || unformat (input, "off"))
2034 return clib_error_return (0, "unknown input '%U'",
2035 format_unformat_error, input);
2037 si = vnet_get_sw_interface (vnm, sw_if_index);
2040 si->flags |= VNET_SW_INTERFACE_FLAG_PROXY_ARP;
2042 si->flags &= ~VNET_SW_INTERFACE_FLAG_PROXY_ARP;
2049 * Enable proxy-arp on an interface. The vpp stack will answer ARP
2050 * requests for the indicated address range. Multiple proxy-arp
2051 * ranges may be provisioned.
2053 * @note Proxy ARP as a technology is infamous for blackholing traffic.
2054 * Also, the underlying implementation has not been performance-tuned.
2055 * Avoid creating an unnecessarily large set of ranges.
2058 * To enable proxy arp on a range of addresses, use:
2059 * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11}
2060 * Append 'del' to delete a range of proxy ARP addresses:
2061 * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11 del}
2062 * You must then specifically enable proxy arp on individual interfaces:
2063 * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 enable}
2064 * To disable proxy arp on an individual interface:
2065 * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 disable}
2067 VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = {
2068 .path = "set interface proxy-arp",
2070 "set interface proxy-arp <intfc> [enable|disable]",
2071 .function = set_int_proxy_arp_command_fn,
2077 * ARP/ND Termination in a L2 Bridge Domain based on IP4/IP6 to MAC
2078 * hash tables mac_by_ip4 and mac_by_ip6 for each BD.
2082 ARP_TERM_NEXT_L2_OUTPUT,
2087 u32 arp_term_next_node_index[32];
2090 arp_term_l2bd (vlib_main_t * vm,
2091 vlib_node_runtime_t * node, vlib_frame_t * frame)
2093 l2input_main_t *l2im = &l2input_main;
2094 u32 n_left_from, next_index, *from, *to_next;
2095 u32 n_replies_sent = 0;
2096 u16 last_bd_index = ~0;
2097 l2_bridge_domain_t *last_bd_config = 0;
2098 l2_input_config_t *cfg0;
2100 from = vlib_frame_vector_args (frame);
2101 n_left_from = frame->n_vectors;
2102 next_index = node->cached_next_index;
2104 while (n_left_from > 0)
2108 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2110 while (n_left_from > 0 && n_left_to_next > 0)
2113 ethernet_header_t *eth0;
2114 ethernet_arp_header_t *arp0;
2117 u32 pi0, error0, next0, sw_if_index0;
2128 n_left_to_next -= 1;
2130 p0 = vlib_get_buffer (vm, pi0);
2131 eth0 = vlib_buffer_get_current (p0);
2132 l3h0 = (u8 *) eth0 + vnet_buffer (p0)->l2.l2_len;
2133 ethertype0 = clib_net_to_host_u16 (*(u16 *) (l3h0 - 2));
2134 arp0 = (ethernet_arp_header_t *) l3h0;
2136 if (PREDICT_FALSE ((ethertype0 != ETHERNET_TYPE_ARP) ||
2138 clib_host_to_net_u16
2139 (ETHERNET_ARP_OPCODE_request))))
2142 /* Must be ARP request packet here */
2143 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
2144 (p0->flags & VLIB_BUFFER_IS_TRACED)))
2146 u8 *t0 = vlib_add_trace (vm, node, p0,
2147 sizeof (ethernet_arp_input_trace_t));
2148 clib_memcpy (t0, l3h0, sizeof (ethernet_arp_input_trace_t));
2151 error0 = ETHERNET_ARP_ERROR_replies_sent;
2154 clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet)
2155 ? ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0);
2158 clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
2159 ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0);
2161 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
2166 /* Trash ARP packets whose ARP-level source addresses do not
2167 match their L2-frame-level source addresses */
2170 (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
2171 sizeof (eth0->src_address))))
2173 error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
2177 /* Check if anyone want ARP request events for L2 BDs */
2179 pending_resolution_t *mc;
2180 ethernet_arp_main_t *am = ðernet_arp_main;
2181 uword *p = hash_get (am->mac_changes_by_address, 0);
2182 if (p && (vnet_buffer (p0)->l2.shg == 0))
2183 { // Only SHG 0 interface which is more likely local
2184 u32 next_index = p[0];
2185 while (next_index != (u32) ~ 0)
2187 int (*fp) (u32, u8 *, u32, u32);
2189 mc = pool_elt_at_index (am->mac_changes, next_index);
2190 fp = mc->data_callback;
2191 /* Call the callback, return 1 to suppress dup events */
2193 rv = (*fp) (mc->data,
2194 arp0->ip4_over_ethernet[0].ethernet,
2196 arp0->ip4_over_ethernet[0].ip4.as_u32);
2197 /* Signal the resolver process */
2199 vlib_process_signal_event (vm, mc->node_index,
2200 mc->type_opaque, mc->data);
2201 next_index = mc->next_index;
2206 /* lookup BD mac_by_ip4 hash table for MAC entry */
2207 ip0 = arp0->ip4_over_ethernet[1].ip4.as_u32;
2208 bd_index0 = vnet_buffer (p0)->l2.bd_index;
2209 if (PREDICT_FALSE ((bd_index0 != last_bd_index)
2210 || (last_bd_index == (u16) ~ 0)))
2212 last_bd_index = bd_index0;
2213 last_bd_config = vec_elt_at_index (l2im->bd_configs, bd_index0);
2215 macp0 = (u8 *) hash_get (last_bd_config->mac_by_ip4, ip0);
2217 if (PREDICT_FALSE (!macp0))
2218 goto next_l2_feature; /* MAC not found */
2220 /* MAC found, send ARP reply -
2221 Convert ARP request packet to ARP reply */
2222 arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
2223 arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
2224 arp0->ip4_over_ethernet[0].ip4.as_u32 = ip0;
2225 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, macp0, 6);
2226 clib_memcpy (eth0->dst_address, eth0->src_address, 6);
2227 clib_memcpy (eth0->src_address, macp0, 6);
2228 n_replies_sent += 1;
2231 /* For BVI, need to use l2-fwd node to send ARP reply as
2232 l2-output node cannot output packet to BVI properly */
2233 cfg0 = vec_elt_at_index (l2im->configs, sw_if_index0);
2234 if (PREDICT_FALSE (cfg0->bvi))
2236 vnet_buffer (p0)->l2.feature_bitmap |= L2INPUT_FEAT_FWD;
2237 vnet_buffer (p0)->sw_if_index[VLIB_RX] = 0;
2238 goto next_l2_feature;
2241 /* Send ARP/ND reply back out input interface through l2-output */
2242 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
2243 next0 = ARP_TERM_NEXT_L2_OUTPUT;
2244 /* Note that output to VXLAN tunnel will fail due to SHG which
2245 is probably desireable since ARP termination is not intended
2246 for ARP requests from other hosts. If output to VXLAN tunnel is
2247 required, however, can just clear the SHG in packet as follows:
2248 vnet_buffer(p0)->l2.shg = 0; */
2249 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2250 to_next, n_left_to_next, pi0,
2255 /* IP6 ND event notification or solicitation handling to generate
2256 local response instead of flooding */
2257 iph0 = (ip6_header_t *) l3h0;
2258 if (PREDICT_FALSE (ethertype0 == ETHERNET_TYPE_IP6 &&
2259 iph0->protocol == IP_PROTOCOL_ICMP6 &&
2260 !ip6_address_is_unspecified
2261 (&iph0->src_address)))
2263 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
2264 if (vnet_ip6_nd_term
2265 (vm, node, p0, eth0, iph0, sw_if_index0,
2266 vnet_buffer (p0)->l2.bd_index, vnet_buffer (p0)->l2.shg))
2267 goto output_response;
2272 u32 feature_bitmap0 =
2273 vnet_buffer (p0)->l2.feature_bitmap & ~L2INPUT_FEAT_ARP_TERM;
2274 vnet_buffer (p0)->l2.feature_bitmap = feature_bitmap0;
2276 feat_bitmap_get_next_node_index (arp_term_next_node_index,
2278 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2279 to_next, n_left_to_next,
2285 if (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ||
2286 (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
2287 arp0->ip4_over_ethernet[1].ip4.as_u32))
2289 error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
2291 next0 = ARP_TERM_NEXT_DROP;
2292 p0->error = node->errors[error0];
2294 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2295 to_next, n_left_to_next, pi0,
2299 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2302 vlib_error_count (vm, node->node_index,
2303 ETHERNET_ARP_ERROR_replies_sent, n_replies_sent);
2304 return frame->n_vectors;
2308 VLIB_REGISTER_NODE (arp_term_l2bd_node, static) = {
2309 .function = arp_term_l2bd,
2310 .name = "arp-term-l2bd",
2311 .vector_size = sizeof (u32),
2312 .n_errors = ETHERNET_ARP_N_ERROR,
2313 .error_strings = ethernet_arp_error_strings,
2314 .n_next_nodes = ARP_TERM_N_NEXT,
2316 [ARP_TERM_NEXT_L2_OUTPUT] = "l2-output",
2317 [ARP_TERM_NEXT_DROP] = "error-drop",
2319 .format_buffer = format_ethernet_arp_header,
2320 .format_trace = format_arp_term_input_trace,
2325 arp_term_init (vlib_main_t * vm)
2327 // Initialize the feature next-node indexes
2328 feat_bitmap_init_next_nodes (vm,
2329 arp_term_l2bd_node.index,
2331 l2input_get_feat_names (),
2332 arp_term_next_node_index);
2336 VLIB_INIT_FUNCTION (arp_term_init);
2339 change_arp_mac (u32 sw_if_index, ethernet_arp_ip4_entry_t * e)
2341 if (e->sw_if_index == sw_if_index)
2343 adj_nbr_walk_nh4 (e->sw_if_index,
2344 &e->ip4_address, arp_mk_complete_walk, e);
2349 ethernet_arp_change_mac (u32 sw_if_index)
2351 ethernet_arp_main_t *am = ðernet_arp_main;
2352 ethernet_arp_ip4_entry_t *e;
2355 pool_foreach (e, am->ip4_entry_pool,
2357 change_arp_mac (sw_if_index, e);
2363 * fd.io coding-style-patch-verification: ON
2366 * eval: (c-set-style "gnu")