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);
42 ip4_address_t ip4_address;
44 u8 ethernet_address[6];
47 #define ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC (1 << 0)
48 #define ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC (1 << 1)
50 u64 cpu_time_last_updated;
53 * The index of the adj-fib entry created
55 fib_node_index_t fib_entry_index;
56 } ethernet_arp_ip4_entry_t;
59 * @brief Per-interface ARP configuration and state
61 typedef struct ethernet_arp_interface_t_
64 * Hash table of ARP entries.
65 * Since this hash table is per-interface, the key is only the IPv4 address.
68 } ethernet_arp_interface_t;
75 } ethernet_proxy_arp_t;
83 /* Used for arp event notification only */
86 } pending_resolution_t;
90 /* Hash tables mapping name to opcode. */
91 uword *opcode_by_name;
93 /* lite beer "glean" adjacency handling */
94 uword *pending_resolutions_by_address;
95 pending_resolution_t *pending_resolutions;
97 /* Mac address change notification */
98 uword *mac_changes_by_address;
99 pending_resolution_t *mac_changes;
101 ethernet_arp_ip4_entry_t *ip4_entry_pool;
103 /* ARP attack mitigation */
104 u32 arp_delete_rotor;
105 u32 limit_arp_cache_size;
107 /** Per interface state */
108 ethernet_arp_interface_t *ethernet_arp_by_sw_if_index;
110 /* Proxy arp vector */
111 ethernet_proxy_arp_t *proxy_arps;
112 } ethernet_arp_main_t;
114 static ethernet_arp_main_t ethernet_arp_main;
119 ethernet_arp_ip4_over_ethernet_address_t a;
122 #define ETHERNET_ARP_ARGS_REMOVE (1<<0)
123 #define ETHERNET_ARP_ARGS_FLUSH (1<<1)
124 #define ETHERNET_ARP_ARGS_POPULATE (1<<2)
125 } vnet_arp_set_ip4_over_ethernet_rpc_args_t;
128 set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t
132 format_ethernet_arp_hardware_type (u8 * s, va_list * va)
134 ethernet_arp_hardware_type_t h = va_arg (*va, ethernet_arp_hardware_type_t);
138 #define _(n,f) case n: t = #f; break;
139 foreach_ethernet_arp_hardware_type;
143 return format (s, "unknown 0x%x", h);
146 return format (s, "%s", t);
150 format_ethernet_arp_opcode (u8 * s, va_list * va)
152 ethernet_arp_opcode_t o = va_arg (*va, ethernet_arp_opcode_t);
156 #define _(f) case ETHERNET_ARP_OPCODE_##f: t = #f; break;
157 foreach_ethernet_arp_opcode;
161 return format (s, "unknown 0x%x", o);
164 return format (s, "%s", t);
168 unformat_ethernet_arp_opcode_host_byte_order (unformat_input_t * input,
171 int *result = va_arg (*args, int *);
172 ethernet_arp_main_t *am = ðernet_arp_main;
175 /* Numeric opcode. */
176 if (unformat (input, "0x%x", &x) || unformat (input, "%d", &x))
185 if (unformat_user (input, unformat_vlib_number_by_name,
186 am->opcode_by_name, &i))
196 unformat_ethernet_arp_opcode_net_byte_order (unformat_input_t * input,
199 int *result = va_arg (*args, int *);
201 (input, unformat_ethernet_arp_opcode_host_byte_order, result))
204 *result = clib_host_to_net_u16 ((u16) * result);
209 format_ethernet_arp_header (u8 * s, va_list * va)
211 ethernet_arp_header_t *a = va_arg (*va, ethernet_arp_header_t *);
212 u32 max_header_bytes = va_arg (*va, u32);
214 u16 l2_type, l3_type;
216 if (max_header_bytes != 0 && sizeof (a[0]) > max_header_bytes)
217 return format (s, "ARP header truncated");
219 l2_type = clib_net_to_host_u16 (a->l2_type);
220 l3_type = clib_net_to_host_u16 (a->l3_type);
222 indent = format_get_indent (s);
224 s = format (s, "%U, type %U/%U, address size %d/%d",
225 format_ethernet_arp_opcode, clib_net_to_host_u16 (a->opcode),
226 format_ethernet_arp_hardware_type, l2_type,
227 format_ethernet_type, l3_type,
228 a->n_l2_address_bytes, a->n_l3_address_bytes);
230 if (l2_type == ETHERNET_ARP_HARDWARE_TYPE_ethernet
231 && l3_type == ETHERNET_TYPE_IP4)
233 s = format (s, "\n%U%U/%U -> %U/%U",
234 format_white_space, indent,
235 format_ethernet_address, a->ip4_over_ethernet[0].ethernet,
236 format_ip4_address, &a->ip4_over_ethernet[0].ip4,
237 format_ethernet_address, a->ip4_over_ethernet[1].ethernet,
238 format_ip4_address, &a->ip4_over_ethernet[1].ip4);
242 uword n2 = a->n_l2_address_bytes;
243 uword n3 = a->n_l3_address_bytes;
244 s = format (s, "\n%U%U/%U -> %U/%U",
245 format_white_space, indent,
246 format_hex_bytes, a->data + 0 * n2 + 0 * n3, n2,
247 format_hex_bytes, a->data + 1 * n2 + 0 * n3, n3,
248 format_hex_bytes, a->data + 1 * n2 + 1 * n3, n2,
249 format_hex_bytes, a->data + 2 * n2 + 1 * n3, n3);
256 format_ethernet_arp_ip4_entry (u8 * s, va_list * va)
258 vnet_main_t *vnm = va_arg (*va, vnet_main_t *);
259 ethernet_arp_ip4_entry_t *e = va_arg (*va, ethernet_arp_ip4_entry_t *);
260 vnet_sw_interface_t *si;
264 return format (s, "%=12s%=16s%=6s%=20s%=24s", "Time", "IP4",
265 "Flags", "Ethernet", "Interface");
267 si = vnet_get_sw_interface (vnm, e->sw_if_index);
269 if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC)
270 flags = format (flags, "S");
272 if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC)
273 flags = format (flags, "D");
275 s = format (s, "%=12U%=16U%=6s%=20U%=24U",
276 format_vlib_cpu_time, vnm->vlib_main, e->cpu_time_last_updated,
277 format_ip4_address, &e->ip4_address,
278 flags ? (char *) flags : "",
279 format_ethernet_address, e->ethernet_address,
280 format_vnet_sw_interface_name, vnm, si);
289 } ethernet_arp_input_trace_t;
292 format_ethernet_arp_input_trace (u8 * s, va_list * va)
294 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
295 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
296 ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
299 format_ethernet_arp_header,
300 t->packet_data, sizeof (t->packet_data));
306 format_arp_term_input_trace (u8 * s, va_list * va)
308 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
309 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
310 ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
312 /* arp-term trace data saved is either arp or ip6/icmp6 packet:
313 - for arp, the 1st 16-bit field is hw type of value of 0x0001.
314 - for ip6, the first nibble has value of 6. */
315 s = format (s, "%U", t->packet_data[0] == 0 ?
316 format_ethernet_arp_header : format_ip6_header,
317 t->packet_data, sizeof (t->packet_data));
323 arp_nbr_probe (ip_adjacency_t * adj)
325 vnet_main_t *vnm = vnet_get_main ();
326 ip4_main_t *im = &ip4_main;
327 ip_interface_address_t *ia;
328 ethernet_arp_header_t *h;
329 vnet_hw_interface_t *hi;
330 vnet_sw_interface_t *si;
336 vm = vlib_get_main ();
338 si = vnet_get_sw_interface (vnm, adj->rewrite_header.sw_if_index);
340 if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
346 ip4_interface_address_matching_destination (im,
347 &adj->sub_type.nbr.next_hop.
357 vlib_packet_template_get_packet (vm, &im->ip4_arp_request_packet_template,
360 hi = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index);
362 clib_memcpy (h->ip4_over_ethernet[0].ethernet,
363 hi->hw_address, sizeof (h->ip4_over_ethernet[0].ethernet));
365 h->ip4_over_ethernet[0].ip4 = src[0];
366 h->ip4_over_ethernet[1].ip4 = adj->sub_type.nbr.next_hop.ip4;
368 b = vlib_get_buffer (vm, bi);
369 vnet_buffer (b)->sw_if_index[VLIB_RX] =
370 vnet_buffer (b)->sw_if_index[VLIB_TX] = adj->rewrite_header.sw_if_index;
372 /* Add encapsulation string for software interface (e.g. ethernet header). */
373 vnet_rewrite_one_header (adj[0], h, sizeof (ethernet_header_t));
374 vlib_buffer_advance (b, -adj->rewrite_header.data_bytes);
377 vlib_frame_t *f = vlib_get_frame_to_node (vm, hi->output_node_index);
378 u32 *to_next = vlib_frame_vector_args (f);
381 vlib_put_frame_to_node (vm, hi->output_node_index, f);
386 arp_mk_complete (adj_index_t ai, ethernet_arp_ip4_entry_t * e)
388 adj_nbr_update_rewrite
389 (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE,
390 ethernet_build_rewrite (vnet_get_main (),
392 adj_get_link_type (ai), e->ethernet_address));
396 arp_mk_incomplete (adj_index_t ai, ethernet_arp_ip4_entry_t * e)
398 adj_nbr_update_rewrite
400 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
401 ethernet_build_rewrite (vnet_get_main (),
404 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
407 static ethernet_arp_ip4_entry_t *
408 arp_entry_find (ethernet_arp_interface_t * eai, const ip4_address_t * addr)
410 ethernet_arp_main_t *am = ðernet_arp_main;
411 ethernet_arp_ip4_entry_t *e = NULL;
414 if (NULL != eai->arp_entries)
416 p = hash_get (eai->arp_entries, addr->as_u32);
420 e = pool_elt_at_index (am->ip4_entry_pool, p[0]);
427 arp_mk_complete_walk (adj_index_t ai, void *ctx)
429 ethernet_arp_ip4_entry_t *e = ctx;
431 arp_mk_complete (ai, e);
433 return (ADJ_WALK_RC_CONTINUE);
437 arp_mk_incomplete_walk (adj_index_t ai, void *ctx)
439 ethernet_arp_ip4_entry_t *e = ctx;
441 arp_mk_incomplete (ai, e);
443 return (ADJ_WALK_RC_CONTINUE);
447 arp_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai)
449 ethernet_arp_main_t *am = ðernet_arp_main;
450 ethernet_arp_interface_t *arp_int;
451 ethernet_arp_ip4_entry_t *e;
456 vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index);
457 arp_int = &am->ethernet_arp_by_sw_if_index[sw_if_index];
458 e = arp_entry_find (arp_int, &adj->sub_type.nbr.next_hop.ip4);
462 adj_nbr_walk_nh4 (sw_if_index,
463 &e->ip4_address, arp_mk_complete_walk, e);
468 * no matching ARP entry.
469 * construct the rewire required to for an ARP packet, and stick
470 * that in the adj's pipe to smoke.
472 adj_nbr_update_rewrite (ai,
473 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
474 ethernet_build_rewrite (vnm,
477 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
480 * since the FIB has added this adj for a route, it makes sense it may
481 * want to forward traffic sometime soon. Let's send a speculative ARP.
482 * just one. If we were to do periodically that wouldn't be bad either,
483 * but that's more code than i'm prepared to write at this time for
484 * relatively little reward.
491 vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm,
492 vnet_arp_set_ip4_over_ethernet_rpc_args_t
495 ethernet_arp_ip4_entry_t *e = 0;
496 ethernet_arp_main_t *am = ðernet_arp_main;
497 ethernet_arp_ip4_over_ethernet_address_t *a = &args->a;
498 vlib_main_t *vm = vlib_get_main ();
499 int make_new_arp_cache_entry = 1;
501 pending_resolution_t *pr, *mc;
502 ethernet_arp_interface_t *arp_int;
503 int is_static = args->is_static;
504 u32 sw_if_index = args->sw_if_index;
506 vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index);
508 arp_int = &am->ethernet_arp_by_sw_if_index[sw_if_index];
510 if (NULL != arp_int->arp_entries)
512 p = hash_get (arp_int->arp_entries, a->ip4.as_u32);
515 e = pool_elt_at_index (am->ip4_entry_pool, p[0]);
517 /* Refuse to over-write static arp. */
518 if (!is_static && (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC))
520 make_new_arp_cache_entry = 0;
524 if (make_new_arp_cache_entry)
528 .fp_proto = FIB_PROTOCOL_IP4,
536 pool_get (am->ip4_entry_pool, e);
538 if (NULL == arp_int->arp_entries)
540 arp_int->arp_entries = hash_create (0, sizeof (u32));
543 hash_set (arp_int->arp_entries, a->ip4.as_u32, e - am->ip4_entry_pool);
545 e->sw_if_index = sw_if_index;
546 e->ip4_address = a->ip4;
547 clib_memcpy (e->ethernet_address,
548 a->ethernet, sizeof (e->ethernet_address));
550 fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index);
552 fib_table_entry_update_one_path (fib_index,
555 FIB_ENTRY_FLAG_ATTACHED,
560 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
565 * prevent a DoS attack from the data-plane that
566 * spams us with no-op updates to the MAC address
568 if (0 == memcmp (e->ethernet_address,
569 a->ethernet, sizeof (e->ethernet_address)))
572 /* Update time stamp and ethernet address. */
573 clib_memcpy (e->ethernet_address, a->ethernet,
574 sizeof (e->ethernet_address));
577 e->cpu_time_last_updated = clib_cpu_time_now ();
579 e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC;
581 e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC;
583 adj_nbr_walk_nh4 (sw_if_index, &e->ip4_address, arp_mk_complete_walk, e);
585 /* Customer(s) waiting for this address to be resolved? */
586 p = hash_get (am->pending_resolutions_by_address, a->ip4.as_u32);
592 while (next_index != (u32) ~ 0)
594 pr = pool_elt_at_index (am->pending_resolutions, next_index);
595 vlib_process_signal_event (vm, pr->node_index,
596 pr->type_opaque, pr->data);
597 next_index = pr->next_index;
598 pool_put (am->pending_resolutions, pr);
601 hash_unset (am->pending_resolutions_by_address, a->ip4.as_u32);
604 /* Customer(s) requesting ARP event for this address? */
605 p = hash_get (am->mac_changes_by_address, a->ip4.as_u32);
611 while (next_index != (u32) ~ 0)
613 int (*fp) (u32, u8 *, u32, u32);
615 mc = pool_elt_at_index (am->mac_changes, next_index);
616 fp = mc->data_callback;
618 /* Call the user's data callback, return 1 to suppress dup events */
620 rv = (*fp) (mc->data, a->ethernet, sw_if_index, 0);
623 * Signal the resolver process, as long as the user
624 * says they want to be notified
627 vlib_process_signal_event (vm, mc->node_index,
628 mc->type_opaque, mc->data);
629 next_index = mc->next_index;
637 vnet_register_ip4_arp_resolution_event (vnet_main_t * vnm,
640 uword type_opaque, uword data)
642 ethernet_arp_main_t *am = ðernet_arp_main;
643 ip4_address_t *address = address_arg;
645 pending_resolution_t *pr;
647 pool_get (am->pending_resolutions, pr);
650 pr->node_index = node_index;
651 pr->type_opaque = type_opaque;
653 pr->data_callback = 0;
655 p = hash_get (am->pending_resolutions_by_address, address->as_u32);
658 /* Insert new resolution at the head of the list */
659 pr->next_index = p[0];
660 hash_unset (am->pending_resolutions_by_address, address->as_u32);
663 hash_set (am->pending_resolutions_by_address, address->as_u32,
664 pr - am->pending_resolutions);
668 vnet_add_del_ip4_arp_change_event (vnet_main_t * vnm,
673 uword type_opaque, uword data, int is_add)
675 ethernet_arp_main_t *am = ðernet_arp_main;
676 ip4_address_t *address = address_arg;
678 pending_resolution_t *mc;
679 void (*fp) (u32, u8 *) = data_callback;
683 pool_get (am->mac_changes, mc);
686 mc->node_index = node_index;
687 mc->type_opaque = type_opaque;
689 mc->data_callback = data_callback;
692 p = hash_get (am->mac_changes_by_address, address->as_u32);
695 /* Insert new resolution at the head of the list */
696 mc->next_index = p[0];
697 hash_unset (am->mac_changes_by_address, address->as_u32);
700 hash_set (am->mac_changes_by_address, address->as_u32,
701 mc - am->mac_changes);
707 pending_resolution_t *mc_last = 0;
709 p = hash_get (am->mac_changes_by_address, address->as_u32);
711 return VNET_API_ERROR_NO_SUCH_ENTRY;
715 while (index != (u32) ~ 0)
717 mc = pool_elt_at_index (am->mac_changes, index);
718 if (mc->node_index == node_index &&
719 mc->type_opaque == type_opaque && mc->pid == pid)
721 /* Clients may need to clean up pool entries, too */
723 (*fp) (mc->data, 0 /* no new mac addrs */ );
726 hash_unset (am->mac_changes_by_address, address->as_u32);
727 if (mc->next_index != ~0)
728 hash_set (am->mac_changes_by_address, address->as_u32,
730 pool_put (am->mac_changes, mc);
736 mc_last->next_index = mc->next_index;
737 pool_put (am->mac_changes, mc);
742 index = mc->next_index;
745 return VNET_API_ERROR_NO_SUCH_ENTRY;
749 /* Either we drop the packet or we send a reply to the sender. */
753 ARP_INPUT_NEXT_REPLY_TX,
757 #define foreach_ethernet_arp_error \
758 _ (replies_sent, "ARP replies sent") \
759 _ (l2_type_not_ethernet, "L2 type not ethernet") \
760 _ (l3_type_not_ip4, "L3 type not IP4") \
761 _ (l3_src_address_not_local, "IP4 source address not local to subnet") \
762 _ (l3_dst_address_not_local, "IP4 destination address not local to subnet") \
763 _ (l3_src_address_is_local, "IP4 source address matches local interface") \
764 _ (l3_src_address_learned, "ARP request IP4 source address learned") \
765 _ (replies_received, "ARP replies received") \
766 _ (opcode_not_request, "ARP opcode not request") \
767 _ (proxy_arp_replies_sent, "Proxy ARP replies sent") \
768 _ (l2_address_mismatch, "ARP hw addr does not match L2 frame src addr") \
769 _ (missing_interface_address, "ARP missing interface address") \
770 _ (gratuitous_arp, "ARP probe or announcement dropped") \
771 _ (interface_no_table, "Interface is not mapped to an IP table") \
775 #define _(sym,string) ETHERNET_ARP_ERROR_##sym,
776 foreach_ethernet_arp_error
778 ETHERNET_ARP_N_ERROR,
779 } ethernet_arp_input_error_t;
783 unset_random_arp_entry (void)
785 ethernet_arp_main_t *am = ðernet_arp_main;
786 ethernet_arp_ip4_entry_t *e;
787 vnet_main_t *vnm = vnet_get_main ();
788 ethernet_arp_ip4_over_ethernet_address_t delme;
791 index = pool_next_index (am->ip4_entry_pool, am->arp_delete_rotor);
792 am->arp_delete_rotor = index;
794 /* Try again from elt 0, could happen if an intfc goes down */
797 index = pool_next_index (am->ip4_entry_pool, am->arp_delete_rotor);
798 am->arp_delete_rotor = index;
801 /* Nothing left in the pool */
805 e = pool_elt_at_index (am->ip4_entry_pool, index);
807 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
808 delme.ip4.as_u32 = e->ip4_address.as_u32;
810 vnet_arp_unset_ip4_over_ethernet (vnm, e->sw_if_index, &delme);
814 arp_unnumbered (vlib_buffer_t * p0,
815 u32 pi0, ethernet_header_t * eth0, u32 sw_if_index)
817 vlib_main_t *vm = vlib_get_main ();
818 vnet_main_t *vnm = vnet_get_main ();
819 vnet_interface_main_t *vim = &vnm->interface_main;
820 vnet_sw_interface_t *si;
821 vnet_hw_interface_t *hi;
822 u32 unnum_src_sw_if_index;
823 u32 *broadcast_swifs = 0;
828 u8 dst_mac_address[6];
830 ethernet_arp_header_t *arp0;
832 /* Save the dst mac address */
833 clib_memcpy (dst_mac_address, eth0->dst_address, sizeof (dst_mac_address));
835 /* Figure out which sw_if_index supplied the address */
836 unnum_src_sw_if_index = sw_if_index;
838 /* Track down all users of the unnumbered source */
840 pool_foreach (si, vim->sw_interfaces,
842 if (si->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED &&
843 (si->unnumbered_sw_if_index == unnum_src_sw_if_index))
845 vec_add1 (broadcast_swifs, si->sw_if_index);
850 /* If there are no interfaces un-unmbered to this interface,
852 if (0 == vec_len (broadcast_swifs))
855 /* Allocate buffering if we need it */
856 if (vec_len (broadcast_swifs) > 1)
858 vec_validate (buffers, vec_len (broadcast_swifs) - 2);
859 n_alloc = vlib_buffer_alloc (vm, buffers, vec_len (buffers));
860 _vec_len (buffers) = n_alloc;
861 for (i = 0; i < n_alloc; i++)
863 b0 = vlib_get_buffer (vm, buffers[i]);
865 /* xerox (partially built) ARP pkt */
866 clib_memcpy (b0->data, p0->data,
867 p0->current_length + p0->current_data);
868 b0->current_data = p0->current_data;
869 b0->current_length = p0->current_length;
870 vnet_buffer (b0)->sw_if_index[VLIB_RX] =
871 vnet_buffer (p0)->sw_if_index[VLIB_RX];
875 vec_insert (buffers, 1, 0);
878 for (i = 0; i < vec_len (buffers); i++)
880 b0 = vlib_get_buffer (vm, buffers[i]);
881 arp0 = vlib_buffer_get_current (b0);
883 hi = vnet_get_sup_hw_interface (vnm, broadcast_swifs[i]);
884 si = vnet_get_sw_interface (vnm, broadcast_swifs[i]);
886 /* For decoration, most likely */
887 vnet_buffer (b0)->sw_if_index[VLIB_TX] = hi->sw_if_index;
889 /* Fix ARP pkt src address */
890 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, hi->hw_address, 6);
892 /* Build L2 encaps for this swif */
893 header_size = sizeof (ethernet_header_t);
894 if (si->sub.eth.flags.one_tag)
896 else if (si->sub.eth.flags.two_tags)
899 vlib_buffer_advance (b0, -header_size);
900 eth0 = vlib_buffer_get_current (b0);
902 if (si->sub.eth.flags.one_tag)
904 ethernet_vlan_header_t *outer = (void *) (eth0 + 1);
906 eth0->type = si->sub.eth.flags.dot1ad ?
907 clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
908 clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
909 outer->priority_cfi_and_id =
910 clib_host_to_net_u16 (si->sub.eth.outer_vlan_id);
911 outer->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
914 else if (si->sub.eth.flags.two_tags)
916 ethernet_vlan_header_t *outer = (void *) (eth0 + 1);
917 ethernet_vlan_header_t *inner = (void *) (outer + 1);
919 eth0->type = si->sub.eth.flags.dot1ad ?
920 clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
921 clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
922 outer->priority_cfi_and_id =
923 clib_host_to_net_u16 (si->sub.eth.outer_vlan_id);
924 outer->type = clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
925 inner->priority_cfi_and_id =
926 clib_host_to_net_u16 (si->sub.eth.inner_vlan_id);
927 inner->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
932 eth0->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
935 /* Restore the original dst address, set src address */
936 clib_memcpy (eth0->dst_address, dst_mac_address,
937 sizeof (eth0->dst_address));
938 clib_memcpy (eth0->src_address, hi->hw_address,
939 sizeof (eth0->src_address));
941 /* Transmit replicas */
945 vlib_get_frame_to_node (vm, hi->output_node_index);
946 u32 *to_next = vlib_frame_vector_args (f);
947 to_next[0] = buffers[i];
949 vlib_put_frame_to_node (vm, hi->output_node_index, f);
953 /* The regular path outputs the original pkt.. */
954 vnet_buffer (p0)->sw_if_index[VLIB_TX] = broadcast_swifs[0];
956 vec_free (broadcast_swifs);
963 arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
965 ethernet_arp_main_t *am = ðernet_arp_main;
966 vnet_main_t *vnm = vnet_get_main ();
967 ip4_main_t *im4 = &ip4_main;
968 u32 n_left_from, next_index, *from, *to_next;
969 u32 n_replies_sent = 0, n_proxy_arp_replies_sent = 0;
971 from = vlib_frame_vector_args (frame);
972 n_left_from = frame->n_vectors;
973 next_index = node->cached_next_index;
975 if (node->flags & VLIB_NODE_FLAG_TRACE)
976 vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
978 sizeof (ethernet_arp_input_trace_t));
980 while (n_left_from > 0)
984 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
986 while (n_left_from > 0 && n_left_to_next > 0)
989 vnet_hw_interface_t *hw_if0;
990 ethernet_arp_header_t *arp0;
991 ethernet_header_t *eth0;
992 ip_adjacency_t *adj0;
993 ip4_address_t *if_addr0, proxy_src;
994 u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0;
995 u8 is_request0, dst_is_local0, is_unnum0;
996 ethernet_proxy_arp_t *pa;
997 fib_node_index_t dst_fei, src_fei;
999 fib_entry_flag_t src_flags, dst_flags;
1006 n_left_to_next -= 1;
1009 p0 = vlib_get_buffer (vm, pi0);
1010 arp0 = vlib_buffer_get_current (p0);
1012 is_request0 = arp0->opcode
1013 == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
1015 error0 = ETHERNET_ARP_ERROR_replies_sent;
1019 clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet) ?
1020 ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0);
1023 clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
1024 ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0);
1026 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
1031 /* Check that IP address is local and matches incoming interface. */
1032 fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
1033 if (~0 == fib_index0)
1035 error0 = ETHERNET_ARP_ERROR_interface_no_table;
1039 dst_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
1040 &arp0->ip4_over_ethernet[1].ip4,
1042 dst_flags = fib_entry_get_flags_for_source (dst_fei,
1043 FIB_SOURCE_INTERFACE);
1046 fib_entry_get_resolving_interface_for_source (dst_fei,
1047 FIB_SOURCE_INTERFACE);
1049 if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
1051 error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
1055 /* Honor unnumbered interface, if any */
1056 is_unnum0 = sw_if_index0 != conn_sw_if_index0;
1058 /* Source must also be local to subnet of matching interface address. */
1059 src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
1060 &arp0->ip4_over_ethernet[0].ip4,
1062 src_flags = fib_entry_get_flags (src_fei);
1064 if (!((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
1065 (FIB_ENTRY_FLAG_CONNECTED & src_flags)) ||
1066 sw_if_index0 != fib_entry_get_resolving_interface (src_fei))
1068 error0 = ETHERNET_ARP_ERROR_l3_src_address_not_local;
1072 /* Reject requests/replies with our local interface address. */
1073 if (FIB_ENTRY_FLAG_LOCAL & src_flags)
1075 error0 = ETHERNET_ARP_ERROR_l3_src_address_is_local;
1079 dst_is_local0 = (FIB_ENTRY_FLAG_LOCAL & dst_flags);
1080 fib_entry_get_prefix (dst_fei, &pfx0);
1081 if_addr0 = &pfx0.fp_addr.ip4;
1083 /* Fill in ethernet header. */
1084 eth0 = ethernet_buffer_get_header (p0);
1086 /* Trash ARP packets whose ARP-level source addresses do not
1087 match their L2-frame-level source addresses */
1088 if (memcmp (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
1089 sizeof (eth0->src_address)))
1091 error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
1095 /* Learn or update sender's mapping only for requests or unicasts
1096 that don't match local interface address. */
1097 if (ethernet_address_cast (eth0->dst_address) ==
1098 ETHERNET_ADDRESS_UNICAST || is_request0)
1100 if (am->limit_arp_cache_size &&
1101 pool_elts (am->ip4_entry_pool) >= am->limit_arp_cache_size)
1102 unset_random_arp_entry ();
1104 vnet_arp_set_ip4_over_ethernet (vnm, sw_if_index0,
1105 &arp0->ip4_over_ethernet[0],
1106 0 /* is_static */ );
1107 error0 = ETHERNET_ARP_ERROR_l3_src_address_learned;
1110 /* Only send a reply for requests sent which match a local interface. */
1111 if (!(is_request0 && dst_is_local0))
1115 clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply) ?
1116 ETHERNET_ARP_ERROR_replies_received : error0);
1122 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
1123 hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1125 /* Send reply back through input interface */
1126 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
1127 next0 = ARP_INPUT_NEXT_REPLY_TX;
1129 arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
1131 arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
1133 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet,
1134 hw_if0->hw_address, 6);
1135 clib_mem_unaligned (&arp0->ip4_over_ethernet[0].ip4.data_u32, u32) =
1138 /* Hardware must be ethernet-like. */
1139 ASSERT (vec_len (hw_if0->hw_address) == 6);
1141 clib_memcpy (eth0->dst_address, eth0->src_address, 6);
1142 clib_memcpy (eth0->src_address, hw_if0->hw_address, 6);
1144 /* Figure out how much to rewind current data from adjacency. */
1145 /* get the adj from the destination's covering connected */
1149 adj_get (fib_entry_get_adj_for_source
1150 (ip4_fib_table_lookup
1151 (ip4_fib_get (fib_index0),
1152 &arp0->ip4_over_ethernet[1].ip4, 31),
1153 FIB_SOURCE_INTERFACE));
1154 if (adj0->lookup_next_index != IP_LOOKUP_NEXT_GLEAN)
1156 error0 = ETHERNET_ARP_ERROR_missing_interface_address;
1161 if (!arp_unnumbered (p0, pi0, eth0, conn_sw_if_index0))
1165 vlib_buffer_advance (p0, -adj0->rewrite_header.data_bytes);
1167 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1168 n_left_to_next, pi0, next0);
1170 n_replies_sent += 1;
1174 if (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ||
1175 (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
1176 arp0->ip4_over_ethernet[1].ip4.as_u32))
1178 error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
1181 /* See if proxy arp is configured for the address */
1184 vnet_sw_interface_t *si;
1185 u32 this_addr = clib_net_to_host_u32
1186 (arp0->ip4_over_ethernet[1].ip4.as_u32);
1189 si = vnet_get_sw_interface (vnm, sw_if_index0);
1191 if (!(si->flags & VNET_SW_INTERFACE_FLAG_PROXY_ARP))
1194 fib_index0 = vec_elt (im4->fib_index_by_sw_if_index,
1197 vec_foreach (pa, am->proxy_arps)
1199 u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr);
1200 u32 hi_addr = clib_net_to_host_u32 (pa->hi_addr);
1202 /* an ARP request hit in the proxy-arp table? */
1203 if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
1204 (fib_index0 == pa->fib_index))
1206 eth0 = ethernet_buffer_get_header (p0);
1208 arp0->ip4_over_ethernet[1].ip4.data_u32;
1211 * Rewind buffer, direct code above not to
1212 * think too hard about it.
1214 if_addr0 = &proxy_src;
1216 i32 ethernet_start =
1217 vnet_buffer (p0)->ethernet.start_of_ethernet_header;
1218 i32 rewind = p0->current_data - ethernet_start;
1219 vlib_buffer_advance (p0, -rewind);
1220 n_proxy_arp_replies_sent++;
1228 next0 = ARP_INPUT_NEXT_DROP;
1229 p0->error = node->errors[error0];
1231 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1232 n_left_to_next, pi0, next0);
1235 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1238 vlib_error_count (vm, node->node_index,
1239 ETHERNET_ARP_ERROR_replies_sent,
1240 n_replies_sent - n_proxy_arp_replies_sent);
1242 vlib_error_count (vm, node->node_index,
1243 ETHERNET_ARP_ERROR_proxy_arp_replies_sent,
1244 n_proxy_arp_replies_sent);
1245 return frame->n_vectors;
1248 static char *ethernet_arp_error_strings[] = {
1249 #define _(sym,string) string,
1250 foreach_ethernet_arp_error
1255 VLIB_REGISTER_NODE (arp_input_node, static) =
1257 .function = arp_input,
1258 .name = "arp-input",
1259 .vector_size = sizeof (u32),
1260 .n_errors = ETHERNET_ARP_N_ERROR,
1261 .error_strings = ethernet_arp_error_strings,
1262 .n_next_nodes = ARP_INPUT_N_NEXT,
1264 [ARP_INPUT_NEXT_DROP] = "error-drop",
1265 [ARP_INPUT_NEXT_REPLY_TX] = "interface-output",
1267 .format_buffer = format_ethernet_arp_header,
1268 .format_trace = format_ethernet_arp_input_trace,
1273 ip4_arp_entry_sort (void *a1, void *a2)
1275 ethernet_arp_ip4_entry_t *e1 = a1;
1276 ethernet_arp_ip4_entry_t *e2 = a2;
1279 vnet_main_t *vnm = vnet_get_main ();
1281 cmp = vnet_sw_interface_compare (vnm, e1->sw_if_index, e2->sw_if_index);
1283 cmp = ip4_address_compare (&e1->ip4_address, &e2->ip4_address);
1287 static clib_error_t *
1288 show_ip4_arp (vlib_main_t * vm,
1289 unformat_input_t * input, vlib_cli_command_t * cmd)
1291 vnet_main_t *vnm = vnet_get_main ();
1292 ethernet_arp_main_t *am = ðernet_arp_main;
1293 ethernet_arp_ip4_entry_t *e, *es;
1294 ethernet_proxy_arp_t *pa;
1295 clib_error_t *error = 0;
1298 /* Filter entries by interface if given. */
1300 (void) unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index);
1304 pool_foreach (e, am->ip4_entry_pool,
1306 vec_add1 (es, e[0]);
1312 vec_sort_with_function (es, ip4_arp_entry_sort);
1313 vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, 0);
1316 if (sw_if_index != ~0 && e->sw_if_index != sw_if_index)
1318 vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, e);
1323 if (vec_len (am->proxy_arps))
1325 vlib_cli_output (vm, "Proxy arps enabled for:");
1326 vec_foreach (pa, am->proxy_arps)
1328 vlib_cli_output (vm, "Fib_index %d %U - %U ",
1330 format_ip4_address, &pa->lo_addr,
1331 format_ip4_address, &pa->hi_addr);
1339 * Display all the IPv4 ARP entries.
1342 * Example of how to display the IPv4 ARP table:
1343 * @cliexstart{show ip arp}
1344 * Time FIB IP4 Flags Ethernet Interface
1345 * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
1346 * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
1347 * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
1348 * Proxy arps enabled for:
1349 * Fib_index 0 6.0.0.1 - 6.0.0.11
1353 VLIB_CLI_COMMAND (show_ip4_arp_command, static) = {
1354 .path = "show ip arp",
1355 .function = show_ip4_arp,
1356 .short_help = "show ip arp",
1362 pg_edit_t l2_type, l3_type;
1363 pg_edit_t n_l2_address_bytes, n_l3_address_bytes;
1369 } ip4_over_ethernet[2];
1370 } pg_ethernet_arp_header_t;
1373 pg_ethernet_arp_header_init (pg_ethernet_arp_header_t * p)
1375 /* Initialize fields that are not bit fields in the IP header. */
1376 #define _(f) pg_edit_init (&p->f, ethernet_arp_header_t, f);
1379 _(n_l2_address_bytes);
1380 _(n_l3_address_bytes);
1382 _(ip4_over_ethernet[0].ethernet);
1383 _(ip4_over_ethernet[0].ip4);
1384 _(ip4_over_ethernet[1].ethernet);
1385 _(ip4_over_ethernet[1].ip4);
1390 unformat_pg_arp_header (unformat_input_t * input, va_list * args)
1392 pg_stream_t *s = va_arg (*args, pg_stream_t *);
1393 pg_ethernet_arp_header_t *p;
1396 p = pg_create_edit_group (s, sizeof (p[0]), sizeof (ethernet_arp_header_t),
1398 pg_ethernet_arp_header_init (p);
1401 pg_edit_set_fixed (&p->l2_type, ETHERNET_ARP_HARDWARE_TYPE_ethernet);
1402 pg_edit_set_fixed (&p->l3_type, ETHERNET_TYPE_IP4);
1403 pg_edit_set_fixed (&p->n_l2_address_bytes, 6);
1404 pg_edit_set_fixed (&p->n_l3_address_bytes, 4);
1406 if (!unformat (input, "%U: %U/%U -> %U/%U",
1408 unformat_ethernet_arp_opcode_net_byte_order, &p->opcode,
1410 unformat_ethernet_address, &p->ip4_over_ethernet[0].ethernet,
1412 unformat_ip4_address, &p->ip4_over_ethernet[0].ip4,
1414 unformat_ethernet_address, &p->ip4_over_ethernet[1].ethernet,
1416 unformat_ip4_address, &p->ip4_over_ethernet[1].ip4))
1418 /* Free up any edits we may have added. */
1419 pg_free_edit_group (s);
1426 ip4_set_arp_limit (u32 arp_limit)
1428 ethernet_arp_main_t *am = ðernet_arp_main;
1430 am->limit_arp_cache_size = arp_limit;
1435 * @brief Control Plane hook to remove an ARP entry
1438 vnet_arp_unset_ip4_over_ethernet (vnet_main_t * vnm,
1439 u32 sw_if_index, void *a_arg)
1441 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1442 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1444 args.sw_if_index = sw_if_index;
1445 args.flags = ETHERNET_ARP_ARGS_REMOVE;
1446 clib_memcpy (&args.a, a, sizeof (*a));
1448 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1449 (u8 *) & args, sizeof (args));
1454 * @brief Internally generated event to flush the ARP cache on an
1455 * interface state change event.
1456 * A flush will remove dynamic ARP entries, and for statics remove the MAC
1457 * address from the corresponding adjacencies.
1460 vnet_arp_flush_ip4_over_ethernet (vnet_main_t * vnm,
1461 u32 sw_if_index, void *a_arg)
1463 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1464 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1466 args.sw_if_index = sw_if_index;
1467 args.flags = ETHERNET_ARP_ARGS_FLUSH;
1468 clib_memcpy (&args.a, a, sizeof (*a));
1470 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1471 (u8 *) & args, sizeof (args));
1476 * @brief Internally generated event to populate the ARP cache on an
1477 * interface state change event.
1478 * For static entries this will re-source the adjacencies.
1480 * @param sw_if_index The interface on which the ARP entires are acted
1483 vnet_arp_populate_ip4_over_ethernet (vnet_main_t * vnm,
1484 u32 sw_if_index, void *a_arg)
1486 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1487 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1489 args.sw_if_index = sw_if_index;
1490 args.flags = ETHERNET_ARP_ARGS_POPULATE;
1491 clib_memcpy (&args.a, a, sizeof (*a));
1493 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1494 (u8 *) & args, sizeof (args));
1499 * arp_add_del_interface_address
1501 * callback when an interface address is added or deleted
1504 arp_add_del_interface_address (ip4_main_t * im,
1507 ip4_address_t * address,
1509 u32 if_address_index, u32 is_del)
1512 * Flush the ARP cache of all entries covered by the address
1513 * that is being removed.
1515 ethernet_arp_main_t *am = ðernet_arp_main;
1516 ethernet_arp_ip4_entry_t *e;
1518 if (vec_len (am->ethernet_arp_by_sw_if_index) <= sw_if_index)
1523 ethernet_arp_interface_t *eai;
1524 u32 i, *to_delete = 0;
1527 eai = &am->ethernet_arp_by_sw_if_index[sw_if_index];
1530 hash_foreach_pair (pair, eai->arp_entries,
1532 e = pool_elt_at_index(am->ip4_entry_pool,
1534 if (ip4_destination_matches_route (im, &e->ip4_address,
1535 address, address_length))
1537 vec_add1 (to_delete, e - am->ip4_entry_pool);
1542 for (i = 0; i < vec_len (to_delete); i++)
1544 ethernet_arp_ip4_over_ethernet_address_t delme;
1545 e = pool_elt_at_index (am->ip4_entry_pool, to_delete[i]);
1547 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
1548 delme.ip4.as_u32 = e->ip4_address.as_u32;
1550 vnet_arp_flush_ip4_over_ethernet (vnet_get_main (),
1551 e->sw_if_index, &delme);
1554 vec_free (to_delete);
1558 static clib_error_t *
1559 ethernet_arp_init (vlib_main_t * vm)
1561 ethernet_arp_main_t *am = ðernet_arp_main;
1562 ip4_main_t *im = &ip4_main;
1563 clib_error_t *error;
1566 if ((error = vlib_call_init_function (vm, ethernet_init)))
1569 ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, arp_input_node.index);
1571 pn = pg_get_node (arp_input_node.index);
1572 pn->unformat_edit = unformat_pg_arp_header;
1574 am->opcode_by_name = hash_create_string (0, sizeof (uword));
1575 #define _(o) hash_set_mem (am->opcode_by_name, #o, ETHERNET_ARP_OPCODE_##o);
1576 foreach_ethernet_arp_opcode;
1579 /* $$$ configurable */
1580 am->limit_arp_cache_size = 50000;
1582 am->pending_resolutions_by_address = hash_create (0, sizeof (uword));
1583 am->mac_changes_by_address = hash_create (0, sizeof (uword));
1585 /* don't trace ARP error packets */
1587 vlib_node_runtime_t *rt =
1588 vlib_node_get_runtime (vm, arp_input_node.index);
1591 vnet_pcap_drop_trace_filter_add_del \
1592 (rt->errors[ETHERNET_ARP_ERROR_##a], \
1594 foreach_ethernet_arp_error
1598 ip4_add_del_interface_address_callback_t cb;
1599 cb.function = arp_add_del_interface_address;
1600 cb.function_opaque = 0;
1601 vec_add1 (im->add_del_interface_address_callbacks, cb);
1606 VLIB_INIT_FUNCTION (ethernet_arp_init);
1609 arp_entry_free (ethernet_arp_interface_t * eai, ethernet_arp_ip4_entry_t * e)
1611 ethernet_arp_main_t *am = ðernet_arp_main;
1613 fib_table_entry_delete_index (e->fib_entry_index, FIB_SOURCE_ADJ);
1614 hash_unset (eai->arp_entries, e->ip4_address.as_u32);
1615 pool_put (am->ip4_entry_pool, e);
1619 vnet_arp_unset_ip4_over_ethernet_internal (vnet_main_t * vnm,
1620 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1623 ethernet_arp_main_t *am = ðernet_arp_main;
1624 ethernet_arp_ip4_entry_t *e;
1625 ethernet_arp_interface_t *eai;
1627 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1629 e = arp_entry_find (eai, &args->a.ip4);
1633 adj_nbr_walk_nh4 (e->sw_if_index,
1634 &e->ip4_address, arp_mk_incomplete_walk, e);
1635 arp_entry_free (eai, e);
1642 vnet_arp_flush_ip4_over_ethernet_internal (vnet_main_t * vnm,
1643 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1646 ethernet_arp_main_t *am = ðernet_arp_main;
1647 ethernet_arp_ip4_entry_t *e;
1648 ethernet_arp_interface_t *eai;
1650 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1652 e = arp_entry_find (eai, &args->a.ip4);
1656 adj_nbr_walk_nh4 (e->sw_if_index,
1657 &e->ip4_address, arp_mk_incomplete_walk, e);
1660 * The difference between flush and unset, is that an unset
1661 * means delete for static and dynamic entries. A flush
1662 * means delete only for dynamic. Flushing is what the DP
1663 * does in response to interface events. unset is only done
1664 * by the control plane.
1666 if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC)
1668 arp_entry_free (eai, e);
1675 vnet_arp_populate_ip4_over_ethernet_internal (vnet_main_t * vnm,
1676 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1679 ethernet_arp_main_t *am = ðernet_arp_main;
1680 ethernet_arp_ip4_entry_t *e;
1681 ethernet_arp_interface_t *eai;
1683 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1685 e = arp_entry_find (eai, &args->a.ip4);
1689 adj_nbr_walk_nh4 (e->sw_if_index,
1690 &e->ip4_address, arp_mk_complete_walk, e);
1696 set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t
1699 vnet_main_t *vm = vnet_get_main ();
1700 ASSERT (os_get_cpu_number () == 0);
1702 if (a->flags & ETHERNET_ARP_ARGS_REMOVE)
1703 vnet_arp_unset_ip4_over_ethernet_internal (vm, a);
1704 else if (a->flags & ETHERNET_ARP_ARGS_FLUSH)
1705 vnet_arp_flush_ip4_over_ethernet_internal (vm, a);
1706 else if (a->flags & ETHERNET_ARP_ARGS_POPULATE)
1707 vnet_arp_populate_ip4_over_ethernet_internal (vm, a);
1709 vnet_arp_set_ip4_over_ethernet_internal (vm, a);
1713 * @brief Invoked when the interface's admin state changes
1715 static clib_error_t *
1716 ethernet_arp_sw_interface_up_down (vnet_main_t * vnm,
1717 u32 sw_if_index, u32 flags)
1719 ethernet_arp_main_t *am = ðernet_arp_main;
1720 ethernet_arp_ip4_entry_t *e;
1721 u32 i, *to_delete = 0;
1724 pool_foreach (e, am->ip4_entry_pool,
1726 if (e->sw_if_index == sw_if_index)
1727 vec_add1 (to_delete,
1728 e - am->ip4_entry_pool);
1732 for (i = 0; i < vec_len (to_delete); i++)
1734 ethernet_arp_ip4_over_ethernet_address_t delme;
1735 e = pool_elt_at_index (am->ip4_entry_pool, to_delete[i]);
1737 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
1738 delme.ip4.as_u32 = e->ip4_address.as_u32;
1740 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
1742 vnet_arp_populate_ip4_over_ethernet (vnm, e->sw_if_index, &delme);
1746 vnet_arp_flush_ip4_over_ethernet (vnm, e->sw_if_index, &delme);
1750 vec_free (to_delete);
1755 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ethernet_arp_sw_interface_up_down);
1758 increment_ip4_and_mac_address (ethernet_arp_ip4_over_ethernet_address_t * a)
1763 for (i = 3; i >= 0; i--)
1765 old = a->ip4.as_u8[i];
1766 a->ip4.as_u8[i] += 1;
1767 if (old < a->ip4.as_u8[i])
1771 for (i = 5; i >= 0; i--)
1773 old = a->ethernet[i];
1774 a->ethernet[i] += 1;
1775 if (old < a->ethernet[i])
1781 vnet_arp_set_ip4_over_ethernet (vnet_main_t * vnm,
1782 u32 sw_if_index, void *a_arg, int is_static)
1784 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1785 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1787 args.sw_if_index = sw_if_index;
1788 args.is_static = is_static;
1790 clib_memcpy (&args.a, a, sizeof (*a));
1792 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1793 (u8 *) & args, sizeof (args));
1798 vnet_proxy_arp_add_del (ip4_address_t * lo_addr,
1799 ip4_address_t * hi_addr, u32 fib_index, int is_del)
1801 ethernet_arp_main_t *am = ðernet_arp_main;
1802 ethernet_proxy_arp_t *pa;
1803 u32 found_at_index = ~0;
1805 vec_foreach (pa, am->proxy_arps)
1807 if (pa->lo_addr == lo_addr->as_u32
1808 && pa->hi_addr == hi_addr->as_u32 && pa->fib_index == fib_index)
1810 found_at_index = pa - am->proxy_arps;
1815 if (found_at_index != ~0)
1817 /* Delete, otherwise it's already in the table */
1819 vec_delete (am->proxy_arps, 1, found_at_index);
1822 /* delete, no such entry */
1824 return VNET_API_ERROR_NO_SUCH_ENTRY;
1826 /* add, not in table */
1827 vec_add2 (am->proxy_arps, pa, 1);
1828 pa->lo_addr = lo_addr->as_u32;
1829 pa->hi_addr = hi_addr->as_u32;
1830 pa->fib_index = fib_index;
1835 * Remove any proxy arp entries asdociated with the
1839 vnet_proxy_arp_fib_reset (u32 fib_id)
1841 ip4_main_t *im = &ip4_main;
1842 ethernet_arp_main_t *am = ðernet_arp_main;
1843 ethernet_proxy_arp_t *pa;
1844 u32 *entries_to_delete = 0;
1849 p = hash_get (im->fib_index_by_table_id, fib_id);
1851 return VNET_API_ERROR_NO_SUCH_ENTRY;
1854 vec_foreach (pa, am->proxy_arps)
1856 if (pa->fib_index == fib_index)
1858 vec_add1 (entries_to_delete, pa - am->proxy_arps);
1862 for (i = 0; i < vec_len (entries_to_delete); i++)
1864 vec_delete (am->proxy_arps, 1, entries_to_delete[i]);
1867 vec_free (entries_to_delete);
1872 static clib_error_t *
1873 ip_arp_add_del_command_fn (vlib_main_t * vm,
1874 unformat_input_t * input, vlib_cli_command_t * cmd)
1876 vnet_main_t *vnm = vnet_get_main ();
1878 ethernet_arp_ip4_over_ethernet_address_t lo_addr, hi_addr, addr;
1887 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1889 /* set ip arp TenGigE1/1/0/1 1.2.3.4 aa:bb:... or aabb.ccdd... */
1890 if (unformat (input, "%U %U %U",
1891 unformat_vnet_sw_interface, vnm, &sw_if_index,
1892 unformat_ip4_address, &addr.ip4,
1893 unformat_ethernet_address, &addr.ethernet))
1896 else if (unformat (input, "delete") || unformat (input, "del"))
1899 else if (unformat (input, "static"))
1902 else if (unformat (input, "count %d", &count))
1905 else if (unformat (input, "fib-id %d", &fib_id))
1907 ip4_main_t *im = &ip4_main;
1908 uword *p = hash_get (im->fib_index_by_table_id, fib_id);
1910 return clib_error_return (0, "fib ID %d doesn't exist\n", fib_id);
1914 else if (unformat (input, "proxy %U - %U",
1915 unformat_ip4_address, &lo_addr.ip4,
1916 unformat_ip4_address, &hi_addr.ip4))
1924 (void) vnet_proxy_arp_add_del (&lo_addr.ip4, &hi_addr.ip4,
1933 for (i = 0; i < count; i++)
1937 uword event_type, *event_data = 0;
1939 /* Park the debug CLI until the arp entry is installed */
1940 vnet_register_ip4_arp_resolution_event
1941 (vnm, &addr.ip4, vlib_current_process (vm),
1942 1 /* type */ , 0 /* data */ );
1944 vnet_arp_set_ip4_over_ethernet
1945 (vnm, sw_if_index, &addr, is_static);
1947 vlib_process_wait_for_event (vm);
1948 event_type = vlib_process_get_events (vm, &event_data);
1949 vec_reset_length (event_data);
1950 if (event_type != 1)
1951 clib_warning ("event type %d unexpected", event_type);
1954 vnet_arp_unset_ip4_over_ethernet (vnm, sw_if_index, &addr);
1956 increment_ip4_and_mac_address (&addr);
1961 return clib_error_return (0, "unknown input `%U'",
1962 format_unformat_error, input);
1970 * Add or delete IPv4 ARP cache entries.
1972 * @note 'set ip arp' options (e.g. delete, static, 'fib-id <id>',
1973 * 'count <number>', 'interface ip4_addr mac_addr') can be added in
1974 * any order and combination.
1978 * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
1979 * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
1980 * @cliexcmd{set ip arp GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1981 * @cliexcmd{set ip arp delete GigabitEthernet2/0/0 6.0.0.3 de:ad:be:ef:ba:be}
1983 * To add or delete an IPv4 ARP cache entry to or from a specific fib
1985 * @cliexcmd{set ip arp fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1986 * @cliexcmd{set ip arp fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1988 * Add or delete IPv4 static ARP cache entries as follows:
1989 * @cliexcmd{set ip arp static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1990 * @cliexcmd{set ip arp static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1992 * For testing / debugging purposes, the 'set ip arp' command can add or
1993 * delete multiple entries. Supply the 'count N' parameter:
1994 * @cliexcmd{set ip arp count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1997 VLIB_CLI_COMMAND (ip_arp_add_del_command, static) = {
1998 .path = "set ip arp",
2000 "set ip arp [del] <intfc> <ip-address> <mac-address> [static] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
2001 .function = ip_arp_add_del_command_fn,
2005 static clib_error_t *
2006 set_int_proxy_arp_command_fn (vlib_main_t * vm,
2008 input, vlib_cli_command_t * cmd)
2010 vnet_main_t *vnm = vnet_get_main ();
2012 vnet_sw_interface_t *si;
2016 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2018 if (unformat (input, "%U", unformat_vnet_sw_interface,
2021 else if (unformat (input, "enable") || unformat (input, "on"))
2023 else if (unformat (input, "disable") || unformat (input, "off"))
2030 return clib_error_return (0, "unknown input '%U'",
2031 format_unformat_error, input);
2033 si = vnet_get_sw_interface (vnm, sw_if_index);
2036 si->flags |= VNET_SW_INTERFACE_FLAG_PROXY_ARP;
2038 si->flags &= ~VNET_SW_INTERFACE_FLAG_PROXY_ARP;
2045 * Enable proxy-arp on an interface. The vpp stack will answer ARP
2046 * requests for the indicated address range. Multiple proxy-arp
2047 * ranges may be provisioned.
2049 * @note Proxy ARP as a technology is infamous for blackholing traffic.
2050 * Also, the underlying implementation has not been performance-tuned.
2051 * Avoid creating an unnecessarily large set of ranges.
2054 * To enable proxy arp on a range of addresses, use:
2055 * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11}
2056 * Append 'del' to delete a range of proxy ARP addresses:
2057 * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11 del}
2058 * You must then specifically enable proxy arp on individual interfaces:
2059 * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 enable}
2060 * To disable proxy arp on an individual interface:
2061 * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 disable}
2063 VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = {
2064 .path = "set interface proxy-arp",
2066 "set interface proxy-arp <intfc> [enable|disable]",
2067 .function = set_int_proxy_arp_command_fn,
2073 * ARP/ND Termination in a L2 Bridge Domain based on IP4/IP6 to MAC
2074 * hash tables mac_by_ip4 and mac_by_ip6 for each BD.
2078 ARP_TERM_NEXT_L2_OUTPUT,
2083 u32 arp_term_next_node_index[32];
2086 arp_term_l2bd (vlib_main_t * vm,
2087 vlib_node_runtime_t * node, vlib_frame_t * frame)
2089 l2input_main_t *l2im = &l2input_main;
2090 u32 n_left_from, next_index, *from, *to_next;
2091 u32 n_replies_sent = 0;
2092 u16 last_bd_index = ~0;
2093 l2_bridge_domain_t *last_bd_config = 0;
2094 l2_input_config_t *cfg0;
2096 from = vlib_frame_vector_args (frame);
2097 n_left_from = frame->n_vectors;
2098 next_index = node->cached_next_index;
2100 while (n_left_from > 0)
2104 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2106 while (n_left_from > 0 && n_left_to_next > 0)
2109 ethernet_header_t *eth0;
2110 ethernet_arp_header_t *arp0;
2113 u32 pi0, error0, next0, sw_if_index0;
2124 n_left_to_next -= 1;
2126 p0 = vlib_get_buffer (vm, pi0);
2127 eth0 = vlib_buffer_get_current (p0);
2128 l3h0 = (u8 *) eth0 + vnet_buffer (p0)->l2.l2_len;
2129 ethertype0 = clib_net_to_host_u16 (*(u16 *) (l3h0 - 2));
2130 arp0 = (ethernet_arp_header_t *) l3h0;
2132 if (PREDICT_FALSE ((ethertype0 != ETHERNET_TYPE_ARP) ||
2134 clib_host_to_net_u16
2135 (ETHERNET_ARP_OPCODE_request))))
2138 /* Must be ARP request packet here */
2139 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
2140 (p0->flags & VLIB_BUFFER_IS_TRACED)))
2142 u8 *t0 = vlib_add_trace (vm, node, p0,
2143 sizeof (ethernet_arp_input_trace_t));
2144 clib_memcpy (t0, l3h0, sizeof (ethernet_arp_input_trace_t));
2147 error0 = ETHERNET_ARP_ERROR_replies_sent;
2150 clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet)
2151 ? ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0);
2154 clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
2155 ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0);
2157 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
2162 /* Trash ARP packets whose ARP-level source addresses do not
2163 match their L2-frame-level source addresses */
2166 (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
2167 sizeof (eth0->src_address))))
2169 error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
2173 /* Check if anyone want ARP request events for L2 BDs */
2175 pending_resolution_t *mc;
2176 ethernet_arp_main_t *am = ðernet_arp_main;
2177 uword *p = hash_get (am->mac_changes_by_address, 0);
2178 if (p && (vnet_buffer (p0)->l2.shg == 0))
2179 { // Only SHG 0 interface which is more likely local
2180 u32 next_index = p[0];
2181 while (next_index != (u32) ~ 0)
2183 int (*fp) (u32, u8 *, u32, u32);
2185 mc = pool_elt_at_index (am->mac_changes, next_index);
2186 fp = mc->data_callback;
2187 /* Call the callback, return 1 to suppress dup events */
2189 rv = (*fp) (mc->data,
2190 arp0->ip4_over_ethernet[0].ethernet,
2192 arp0->ip4_over_ethernet[0].ip4.as_u32);
2193 /* Signal the resolver process */
2195 vlib_process_signal_event (vm, mc->node_index,
2196 mc->type_opaque, mc->data);
2197 next_index = mc->next_index;
2202 /* lookup BD mac_by_ip4 hash table for MAC entry */
2203 ip0 = arp0->ip4_over_ethernet[1].ip4.as_u32;
2204 bd_index0 = vnet_buffer (p0)->l2.bd_index;
2205 if (PREDICT_FALSE ((bd_index0 != last_bd_index)
2206 || (last_bd_index == (u16) ~ 0)))
2208 last_bd_index = bd_index0;
2209 last_bd_config = vec_elt_at_index (l2im->bd_configs, bd_index0);
2211 macp0 = (u8 *) hash_get (last_bd_config->mac_by_ip4, ip0);
2213 if (PREDICT_FALSE (!macp0))
2214 goto next_l2_feature; /* MAC not found */
2216 /* MAC found, send ARP reply -
2217 Convert ARP request packet to ARP reply */
2218 arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
2219 arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
2220 arp0->ip4_over_ethernet[0].ip4.as_u32 = ip0;
2221 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, macp0, 6);
2222 clib_memcpy (eth0->dst_address, eth0->src_address, 6);
2223 clib_memcpy (eth0->src_address, macp0, 6);
2224 n_replies_sent += 1;
2227 /* For BVI, need to use l2-fwd node to send ARP reply as
2228 l2-output node cannot output packet to BVI properly */
2229 cfg0 = vec_elt_at_index (l2im->configs, sw_if_index0);
2230 if (PREDICT_FALSE (cfg0->bvi))
2232 vnet_buffer (p0)->l2.feature_bitmap |= L2INPUT_FEAT_FWD;
2233 vnet_buffer (p0)->sw_if_index[VLIB_RX] = 0;
2234 goto next_l2_feature;
2237 /* Send ARP/ND reply back out input interface through l2-output */
2238 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
2239 next0 = ARP_TERM_NEXT_L2_OUTPUT;
2240 /* Note that output to VXLAN tunnel will fail due to SHG which
2241 is probably desireable since ARP termination is not intended
2242 for ARP requests from other hosts. If output to VXLAN tunnel is
2243 required, however, can just clear the SHG in packet as follows:
2244 vnet_buffer(p0)->l2.shg = 0; */
2245 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2246 to_next, n_left_to_next, pi0,
2251 /* IP6 ND event notification or solicitation handling to generate
2252 local response instead of flooding */
2253 iph0 = (ip6_header_t *) l3h0;
2254 if (PREDICT_FALSE (ethertype0 == ETHERNET_TYPE_IP6 &&
2255 iph0->protocol == IP_PROTOCOL_ICMP6 &&
2256 !ip6_address_is_unspecified
2257 (&iph0->src_address)))
2259 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
2260 if (vnet_ip6_nd_term
2261 (vm, node, p0, eth0, iph0, sw_if_index0,
2262 vnet_buffer (p0)->l2.bd_index, vnet_buffer (p0)->l2.shg))
2263 goto output_response;
2268 u32 feature_bitmap0 =
2269 vnet_buffer (p0)->l2.feature_bitmap & ~L2INPUT_FEAT_ARP_TERM;
2270 vnet_buffer (p0)->l2.feature_bitmap = feature_bitmap0;
2272 feat_bitmap_get_next_node_index (arp_term_next_node_index,
2274 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2275 to_next, n_left_to_next,
2281 if (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ||
2282 (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
2283 arp0->ip4_over_ethernet[1].ip4.as_u32))
2285 error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
2287 next0 = ARP_TERM_NEXT_DROP;
2288 p0->error = node->errors[error0];
2290 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2291 to_next, n_left_to_next, pi0,
2295 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2298 vlib_error_count (vm, node->node_index,
2299 ETHERNET_ARP_ERROR_replies_sent, n_replies_sent);
2300 return frame->n_vectors;
2304 VLIB_REGISTER_NODE (arp_term_l2bd_node, static) = {
2305 .function = arp_term_l2bd,
2306 .name = "arp-term-l2bd",
2307 .vector_size = sizeof (u32),
2308 .n_errors = ETHERNET_ARP_N_ERROR,
2309 .error_strings = ethernet_arp_error_strings,
2310 .n_next_nodes = ARP_TERM_N_NEXT,
2312 [ARP_TERM_NEXT_L2_OUTPUT] = "l2-output",
2313 [ARP_TERM_NEXT_DROP] = "error-drop",
2315 .format_buffer = format_ethernet_arp_header,
2316 .format_trace = format_arp_term_input_trace,
2321 arp_term_init (vlib_main_t * vm)
2323 // Initialize the feature next-node indexes
2324 feat_bitmap_init_next_nodes (vm,
2325 arp_term_l2bd_node.index,
2327 l2input_get_feat_names (),
2328 arp_term_next_node_index);
2332 VLIB_INIT_FUNCTION (arp_term_init);
2335 change_arp_mac (u32 sw_if_index, ethernet_arp_ip4_entry_t * e)
2337 if (e->sw_if_index == sw_if_index)
2339 adj_nbr_walk_nh4 (e->sw_if_index,
2340 &e->ip4_address, arp_mk_complete_walk, e);
2345 ethernet_arp_change_mac (vnet_main_t * vnm, u32 sw_if_index)
2347 ethernet_arp_main_t *am = ðernet_arp_main;
2348 ethernet_arp_ip4_entry_t *e;
2351 pool_foreach (e, am->ip4_entry_pool,
2353 change_arp_mac (sw_if_index, e);
2359 * fd.io coding-style-patch-verification: ON
2362 * eval: (c-set-style "gnu")