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.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;
51 adj_index_t adj_index[FIB_LINK_NUM];
52 } ethernet_arp_ip4_entry_t;
55 * @brief administrative and operational state falgs on an interface
57 typedef enum ethernet_arp_interface_flags_t_
59 ETHERNET_ARP_INTERFACE_UP = (0 << 1),
60 ETHERNET_ARP_INTERFACE_MPLS_ENABLE = (1 << 0),
61 } ethernet_arp_interface_flags_t;
64 * @brief Per-interface ARP configuration and state
66 typedef struct ethernet_arp_interface_t_
69 * Hash table of ARP entries.
70 * Since this hash table is per-interface, the key is only the IPv4 address.
75 * Flags for administrative and operational state
77 ethernet_arp_interface_flags_t flags;
78 } ethernet_arp_interface_t;
85 } ethernet_proxy_arp_t;
93 /* Used for arp event notification only */
96 } pending_resolution_t;
100 /* Hash tables mapping name to opcode. */
101 uword *opcode_by_name;
103 /* lite beer "glean" adjacency handling */
104 uword *pending_resolutions_by_address;
105 pending_resolution_t *pending_resolutions;
107 /* Mac address change notification */
108 uword *mac_changes_by_address;
109 pending_resolution_t *mac_changes;
111 ethernet_arp_ip4_entry_t *ip4_entry_pool;
113 /* ARP attack mitigation */
114 u32 arp_delete_rotor;
115 u32 limit_arp_cache_size;
117 /** Per interface state */
118 ethernet_arp_interface_t *ethernet_arp_by_sw_if_index;
120 /* Proxy arp vector */
121 ethernet_proxy_arp_t *proxy_arps;
122 } ethernet_arp_main_t;
124 static ethernet_arp_main_t ethernet_arp_main;
127 typedef enum arp_ether_type_t_
129 ARP_ETHER_TYPE_IP4 = (1 << 0),
130 ARP_ETHER_TYPE_MPLS = (1 << 1),
132 #define ARP_ETHER_TYPE_BOTH (ARP_ETHER_TYPE_MPLS | ARP_ETHER_TYPE_IP4)
137 ethernet_arp_ip4_over_ethernet_address_t a;
140 #define ETHERNET_ARP_ARGS_REMOVE (1<<0)
141 #define ETHERNET_ARP_ARGS_FLUSH (1<<1)
142 #define ETHERNET_ARP_ARGS_POPULATE (1<<2)
143 arp_ether_type_t ether_type;
144 } vnet_arp_set_ip4_over_ethernet_rpc_args_t;
147 set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t
151 format_ethernet_arp_hardware_type (u8 * s, va_list * va)
153 ethernet_arp_hardware_type_t h = va_arg (*va, ethernet_arp_hardware_type_t);
157 #define _(n,f) case n: t = #f; break;
158 foreach_ethernet_arp_hardware_type;
162 return format (s, "unknown 0x%x", h);
165 return format (s, "%s", t);
169 format_ethernet_arp_opcode (u8 * s, va_list * va)
171 ethernet_arp_opcode_t o = va_arg (*va, ethernet_arp_opcode_t);
175 #define _(f) case ETHERNET_ARP_OPCODE_##f: t = #f; break;
176 foreach_ethernet_arp_opcode;
180 return format (s, "unknown 0x%x", o);
183 return format (s, "%s", t);
187 unformat_ethernet_arp_opcode_host_byte_order (unformat_input_t * input,
190 int *result = va_arg (*args, int *);
191 ethernet_arp_main_t *am = ðernet_arp_main;
194 /* Numeric opcode. */
195 if (unformat (input, "0x%x", &x) || unformat (input, "%d", &x))
204 if (unformat_user (input, unformat_vlib_number_by_name,
205 am->opcode_by_name, &i))
215 unformat_ethernet_arp_opcode_net_byte_order (unformat_input_t * input,
218 int *result = va_arg (*args, int *);
220 (input, unformat_ethernet_arp_opcode_host_byte_order, result))
223 *result = clib_host_to_net_u16 ((u16) * result);
228 format_ethernet_arp_header (u8 * s, va_list * va)
230 ethernet_arp_header_t *a = va_arg (*va, ethernet_arp_header_t *);
231 u32 max_header_bytes = va_arg (*va, u32);
233 u16 l2_type, l3_type;
235 if (max_header_bytes != 0 && sizeof (a[0]) > max_header_bytes)
236 return format (s, "ARP header truncated");
238 l2_type = clib_net_to_host_u16 (a->l2_type);
239 l3_type = clib_net_to_host_u16 (a->l3_type);
241 indent = format_get_indent (s);
243 s = format (s, "%U, type %U/%U, address size %d/%d",
244 format_ethernet_arp_opcode, clib_net_to_host_u16 (a->opcode),
245 format_ethernet_arp_hardware_type, l2_type,
246 format_ethernet_type, l3_type,
247 a->n_l2_address_bytes, a->n_l3_address_bytes);
249 if (l2_type == ETHERNET_ARP_HARDWARE_TYPE_ethernet
250 && l3_type == ETHERNET_TYPE_IP4)
252 s = format (s, "\n%U%U/%U -> %U/%U",
253 format_white_space, indent,
254 format_ethernet_address, a->ip4_over_ethernet[0].ethernet,
255 format_ip4_address, &a->ip4_over_ethernet[0].ip4,
256 format_ethernet_address, a->ip4_over_ethernet[1].ethernet,
257 format_ip4_address, &a->ip4_over_ethernet[1].ip4);
261 uword n2 = a->n_l2_address_bytes;
262 uword n3 = a->n_l3_address_bytes;
263 s = format (s, "\n%U%U/%U -> %U/%U",
264 format_white_space, indent,
265 format_hex_bytes, a->data + 0 * n2 + 0 * n3, n2,
266 format_hex_bytes, a->data + 1 * n2 + 0 * n3, n3,
267 format_hex_bytes, a->data + 1 * n2 + 1 * n3, n2,
268 format_hex_bytes, a->data + 2 * n2 + 1 * n3, n3);
275 format_ethernet_arp_ip4_entry (u8 * s, va_list * va)
277 vnet_main_t *vnm = va_arg (*va, vnet_main_t *);
278 ethernet_arp_ip4_entry_t *e = va_arg (*va, ethernet_arp_ip4_entry_t *);
279 vnet_sw_interface_t *si;
283 return format (s, "%=12s%=16s%=6s%=20s%=24s", "Time", "IP4",
284 "Flags", "Ethernet", "Interface");
286 si = vnet_get_sw_interface (vnm, e->sw_if_index);
288 if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC)
289 flags = format (flags, "S");
291 if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC)
292 flags = format (flags, "D");
294 s = format (s, "%=12U%=16U%=6s%=20U%=24U",
295 format_vlib_cpu_time, vnm->vlib_main, e->cpu_time_last_updated,
296 format_ip4_address, &e->ip4_address,
297 flags ? (char *) flags : "",
298 format_ethernet_address, e->ethernet_address,
299 format_vnet_sw_interface_name, vnm, si);
308 } ethernet_arp_input_trace_t;
311 format_ethernet_arp_input_trace (u8 * s, va_list * va)
313 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
314 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
315 ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
318 format_ethernet_arp_header,
319 t->packet_data, sizeof (t->packet_data));
325 format_arp_term_input_trace (u8 * s, va_list * va)
327 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
328 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
329 ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
331 /* arp-term trace data saved is either arp or ip6/icmp6 packet:
332 - for arp, the 1st 16-bit field is hw type of value of 0x0001.
333 - for ip6, the first nibble has value of 6. */
334 s = format (s, "%U", t->packet_data[0] == 0 ?
335 format_ethernet_arp_header : format_ip6_header,
336 t->packet_data, sizeof (t->packet_data));
342 arp_mk_complete (ethernet_arp_interface_t * eai,
343 ethernet_arp_ip4_entry_t * e, arp_ether_type_t et)
347 .fp_proto = FIB_PROTOCOL_IP4,
349 .ip4 = e->ip4_address,
354 fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index);
356 if (et & ARP_ETHER_TYPE_IP4)
358 if (ADJ_INDEX_INVALID == e->adj_index[FIB_LINK_IP4])
360 e->adj_index[FIB_LINK_IP4] =
361 adj_nbr_add_or_lock_w_rewrite (FIB_PROTOCOL_IP4,
365 e->ethernet_address);
366 ASSERT (ADJ_INDEX_INVALID != e->adj_index[FIB_LINK_IP4]);
368 fib_table_entry_update_one_path (fib_index,
371 FIB_ENTRY_FLAG_ATTACHED,
378 FIB_ROUTE_PATH_FLAG_NONE);
382 adj_nbr_update_rewrite (e->adj_index[FIB_LINK_IP4],
383 e->ethernet_address);
386 if ((et & ARP_ETHER_TYPE_MPLS) &&
387 eai->flags & ETHERNET_ARP_INTERFACE_MPLS_ENABLE)
389 if (ADJ_INDEX_INVALID == e->adj_index[FIB_LINK_MPLS])
391 e->adj_index[FIB_LINK_MPLS] =
392 adj_nbr_add_or_lock_w_rewrite (FIB_PROTOCOL_IP4,
396 e->ethernet_address);
397 ASSERT (ADJ_INDEX_INVALID != e->adj_index[FIB_LINK_MPLS]);
401 adj_nbr_update_rewrite (e->adj_index[FIB_LINK_MPLS],
402 e->ethernet_address);
408 vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm,
409 vnet_arp_set_ip4_over_ethernet_rpc_args_t
412 ethernet_arp_ip4_entry_t *e = 0;
413 ethernet_arp_main_t *am = ðernet_arp_main;
414 ethernet_arp_ip4_over_ethernet_address_t *a = &args->a;
415 vlib_main_t *vm = vlib_get_main ();
416 int make_new_arp_cache_entry = 1;
418 pending_resolution_t *pr, *mc;
419 ethernet_arp_interface_t *arp_int;
421 int is_static = args->is_static;
422 u32 sw_if_index = args->sw_if_index;
424 vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index);
426 arp_int = &am->ethernet_arp_by_sw_if_index[sw_if_index];
428 if (NULL != arp_int->arp_entries)
430 p = hash_get (arp_int->arp_entries, a->ip4.as_u32);
433 e = pool_elt_at_index (am->ip4_entry_pool, p[0]);
435 /* Refuse to over-write static arp. */
436 if (!is_static && (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC))
438 make_new_arp_cache_entry = 0;
442 if (make_new_arp_cache_entry)
444 pool_get (am->ip4_entry_pool, e);
446 if (NULL == arp_int->arp_entries)
448 arp_int->arp_entries = hash_create (0, sizeof (u32));
449 if (mpls_sw_interface_is_enabled (sw_if_index))
450 arp_int->flags |= ETHERNET_ARP_INTERFACE_MPLS_ENABLE;
453 hash_set (arp_int->arp_entries, a->ip4.as_u32, e - am->ip4_entry_pool);
455 e->sw_if_index = sw_if_index;
456 e->ip4_address = a->ip4;
457 FOR_EACH_FIB_LINK (link)
459 e->adj_index[link] = ADJ_INDEX_INVALID;
463 /* Update time stamp and ethernet address. */
464 clib_memcpy (e->ethernet_address, a->ethernet,
465 sizeof (e->ethernet_address));
466 e->cpu_time_last_updated = clib_cpu_time_now ();
468 e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC;
470 e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC;
472 arp_mk_complete (arp_int, e, ARP_ETHER_TYPE_BOTH);
474 /* Customer(s) waiting for this address to be resolved? */
475 p = hash_get (am->pending_resolutions_by_address, a->ip4.as_u32);
481 while (next_index != (u32) ~ 0)
483 pr = pool_elt_at_index (am->pending_resolutions, next_index);
484 vlib_process_signal_event (vm, pr->node_index,
485 pr->type_opaque, pr->data);
486 next_index = pr->next_index;
487 pool_put (am->pending_resolutions, pr);
490 hash_unset (am->pending_resolutions_by_address, a->ip4.as_u32);
493 /* Customer(s) requesting ARP event for this address? */
494 p = hash_get (am->mac_changes_by_address, a->ip4.as_u32);
500 while (next_index != (u32) ~ 0)
502 int (*fp) (u32, u8 *, u32, u32);
504 mc = pool_elt_at_index (am->mac_changes, next_index);
505 fp = mc->data_callback;
507 /* Call the user's data callback, return 1 to suppress dup events */
509 rv = (*fp) (mc->data, a->ethernet, sw_if_index, 0);
512 * Signal the resolver process, as long as the user
513 * says they want to be notified
516 vlib_process_signal_event (vm, mc->node_index,
517 mc->type_opaque, mc->data);
518 next_index = mc->next_index;
526 vnet_register_ip4_arp_resolution_event (vnet_main_t * vnm,
529 uword type_opaque, uword data)
531 ethernet_arp_main_t *am = ðernet_arp_main;
532 ip4_address_t *address = address_arg;
534 pending_resolution_t *pr;
536 pool_get (am->pending_resolutions, pr);
539 pr->node_index = node_index;
540 pr->type_opaque = type_opaque;
542 pr->data_callback = 0;
544 p = hash_get (am->pending_resolutions_by_address, address->as_u32);
547 /* Insert new resolution at the head of the list */
548 pr->next_index = p[0];
549 hash_unset (am->pending_resolutions_by_address, address->as_u32);
552 hash_set (am->pending_resolutions_by_address, address->as_u32,
553 pr - am->pending_resolutions);
557 vnet_add_del_ip4_arp_change_event (vnet_main_t * vnm,
562 uword type_opaque, uword data, int is_add)
564 ethernet_arp_main_t *am = ðernet_arp_main;
565 ip4_address_t *address = address_arg;
567 pending_resolution_t *mc;
568 void (*fp) (u32, u8 *) = data_callback;
572 pool_get (am->mac_changes, mc);
575 mc->node_index = node_index;
576 mc->type_opaque = type_opaque;
578 mc->data_callback = data_callback;
581 p = hash_get (am->mac_changes_by_address, address->as_u32);
584 /* Insert new resolution at the head of the list */
585 mc->next_index = p[0];
586 hash_unset (am->mac_changes_by_address, address->as_u32);
589 hash_set (am->mac_changes_by_address, address->as_u32,
590 mc - am->mac_changes);
596 pending_resolution_t *mc_last = 0;
598 p = hash_get (am->mac_changes_by_address, address->as_u32);
600 return VNET_API_ERROR_NO_SUCH_ENTRY;
604 while (index != (u32) ~ 0)
606 mc = pool_elt_at_index (am->mac_changes, index);
607 if (mc->node_index == node_index &&
608 mc->type_opaque == type_opaque && mc->pid == pid)
610 /* Clients may need to clean up pool entries, too */
612 (*fp) (mc->data, 0 /* no new mac addrs */ );
615 hash_unset (am->mac_changes_by_address, address->as_u32);
616 if (mc->next_index != ~0)
617 hash_set (am->mac_changes_by_address, address->as_u32,
619 pool_put (am->mac_changes, mc);
625 mc_last->next_index = mc->next_index;
626 pool_put (am->mac_changes, mc);
631 index = mc->next_index;
634 return VNET_API_ERROR_NO_SUCH_ENTRY;
638 /* Either we drop the packet or we send a reply to the sender. */
642 ARP_INPUT_NEXT_REPLY_TX,
646 #define foreach_ethernet_arp_error \
647 _ (replies_sent, "ARP replies sent") \
648 _ (l2_type_not_ethernet, "L2 type not ethernet") \
649 _ (l3_type_not_ip4, "L3 type not IP4") \
650 _ (l3_src_address_not_local, "IP4 source address not local to subnet") \
651 _ (l3_dst_address_not_local, "IP4 destination address not local to subnet") \
652 _ (l3_src_address_is_local, "IP4 source address matches local interface") \
653 _ (l3_src_address_learned, "ARP request IP4 source address learned") \
654 _ (replies_received, "ARP replies received") \
655 _ (opcode_not_request, "ARP opcode not request") \
656 _ (proxy_arp_replies_sent, "Proxy ARP replies sent") \
657 _ (l2_address_mismatch, "ARP hw addr does not match L2 frame src addr") \
658 _ (missing_interface_address, "ARP missing interface address") \
659 _ (gratuitous_arp, "ARP probe or announcement dropped") \
660 _ (interface_no_table, "Interface is not mapped to an IP table") \
664 #define _(sym,string) ETHERNET_ARP_ERROR_##sym,
665 foreach_ethernet_arp_error
667 ETHERNET_ARP_N_ERROR,
668 } ethernet_arp_input_error_t;
672 unset_random_arp_entry (void)
674 ethernet_arp_main_t *am = ðernet_arp_main;
675 ethernet_arp_ip4_entry_t *e;
676 vnet_main_t *vnm = vnet_get_main ();
677 ethernet_arp_ip4_over_ethernet_address_t delme;
680 index = pool_next_index (am->ip4_entry_pool, am->arp_delete_rotor);
681 am->arp_delete_rotor = index;
683 /* Try again from elt 0, could happen if an intfc goes down */
686 index = pool_next_index (am->ip4_entry_pool, am->arp_delete_rotor);
687 am->arp_delete_rotor = index;
690 /* Nothing left in the pool */
694 e = pool_elt_at_index (am->ip4_entry_pool, index);
696 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
697 delme.ip4.as_u32 = e->ip4_address.as_u32;
699 vnet_arp_unset_ip4_over_ethernet (vnm, e->sw_if_index, &delme);
703 arp_unnumbered (vlib_buffer_t * p0,
704 u32 pi0, ethernet_header_t * eth0, u32 sw_if_index)
706 vlib_main_t *vm = vlib_get_main ();
707 vnet_main_t *vnm = vnet_get_main ();
708 vnet_interface_main_t *vim = &vnm->interface_main;
709 vnet_sw_interface_t *si;
710 vnet_hw_interface_t *hi;
711 u32 unnum_src_sw_if_index;
712 u32 *broadcast_swifs = 0;
717 u8 dst_mac_address[6];
719 ethernet_arp_header_t *arp0;
721 /* Save the dst mac address */
722 clib_memcpy (dst_mac_address, eth0->dst_address, sizeof (dst_mac_address));
724 /* Figure out which sw_if_index supplied the address */
725 unnum_src_sw_if_index = sw_if_index;
727 /* Track down all users of the unnumbered source */
729 pool_foreach (si, vim->sw_interfaces,
731 if (si->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED &&
732 (si->unnumbered_sw_if_index == unnum_src_sw_if_index))
734 vec_add1 (broadcast_swifs, si->sw_if_index);
739 ASSERT (vec_len (broadcast_swifs));
741 /* Allocate buffering if we need it */
742 if (vec_len (broadcast_swifs) > 1)
744 vec_validate (buffers, vec_len (broadcast_swifs) - 2);
745 n_alloc = vlib_buffer_alloc (vm, buffers, vec_len (buffers));
746 _vec_len (buffers) = n_alloc;
747 for (i = 0; i < n_alloc; i++)
749 b0 = vlib_get_buffer (vm, buffers[i]);
751 /* xerox (partially built) ARP pkt */
752 clib_memcpy (b0->data, p0->data,
753 p0->current_length + p0->current_data);
754 b0->current_data = p0->current_data;
755 b0->current_length = p0->current_length;
756 vnet_buffer (b0)->sw_if_index[VLIB_RX] =
757 vnet_buffer (p0)->sw_if_index[VLIB_RX];
761 vec_insert (buffers, 1, 0);
764 for (i = 0; i < vec_len (buffers); i++)
766 b0 = vlib_get_buffer (vm, buffers[i]);
767 arp0 = vlib_buffer_get_current (b0);
769 hi = vnet_get_sup_hw_interface (vnm, broadcast_swifs[i]);
770 si = vnet_get_sw_interface (vnm, broadcast_swifs[i]);
772 /* For decoration, most likely */
773 vnet_buffer (b0)->sw_if_index[VLIB_TX] = hi->sw_if_index;
775 /* Fix ARP pkt src address */
776 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, hi->hw_address, 6);
778 /* Build L2 encaps for this swif */
779 header_size = sizeof (ethernet_header_t);
780 if (si->sub.eth.flags.one_tag)
782 else if (si->sub.eth.flags.two_tags)
785 vlib_buffer_advance (b0, -header_size);
786 eth0 = vlib_buffer_get_current (b0);
788 if (si->sub.eth.flags.one_tag)
790 ethernet_vlan_header_t *outer = (void *) (eth0 + 1);
792 eth0->type = si->sub.eth.flags.dot1ad ?
793 clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
794 clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
795 outer->priority_cfi_and_id =
796 clib_host_to_net_u16 (si->sub.eth.outer_vlan_id);
797 outer->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
800 else if (si->sub.eth.flags.two_tags)
802 ethernet_vlan_header_t *outer = (void *) (eth0 + 1);
803 ethernet_vlan_header_t *inner = (void *) (outer + 1);
805 eth0->type = si->sub.eth.flags.dot1ad ?
806 clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
807 clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
808 outer->priority_cfi_and_id =
809 clib_host_to_net_u16 (si->sub.eth.outer_vlan_id);
810 outer->type = clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
811 inner->priority_cfi_and_id =
812 clib_host_to_net_u16 (si->sub.eth.inner_vlan_id);
813 inner->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
818 eth0->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
821 /* Restore the original dst address, set src address */
822 clib_memcpy (eth0->dst_address, dst_mac_address,
823 sizeof (eth0->dst_address));
824 clib_memcpy (eth0->src_address, hi->hw_address,
825 sizeof (eth0->src_address));
827 /* Transmit replicas */
831 vlib_get_frame_to_node (vm, hi->output_node_index);
832 u32 *to_next = vlib_frame_vector_args (f);
833 to_next[0] = buffers[i];
835 vlib_put_frame_to_node (vm, hi->output_node_index, f);
839 /* The regular path outputs the original pkt.. */
840 vnet_buffer (p0)->sw_if_index[VLIB_TX] = broadcast_swifs[0];
842 vec_free (broadcast_swifs);
847 arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
849 ethernet_arp_main_t *am = ðernet_arp_main;
850 vnet_main_t *vnm = vnet_get_main ();
851 ip4_main_t *im4 = &ip4_main;
852 u32 n_left_from, next_index, *from, *to_next;
853 u32 n_replies_sent = 0, n_proxy_arp_replies_sent = 0;
855 from = vlib_frame_vector_args (frame);
856 n_left_from = frame->n_vectors;
857 next_index = node->cached_next_index;
859 if (node->flags & VLIB_NODE_FLAG_TRACE)
860 vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
862 sizeof (ethernet_arp_input_trace_t));
864 while (n_left_from > 0)
868 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
870 while (n_left_from > 0 && n_left_to_next > 0)
873 vnet_hw_interface_t *hw_if0;
874 ethernet_arp_header_t *arp0;
875 ethernet_header_t *eth0;
876 ip_adjacency_t *adj0;
877 ip4_address_t *if_addr0, proxy_src;
878 u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0;
879 u8 is_request0, dst_is_local0, is_unnum0;
880 ethernet_proxy_arp_t *pa;
881 fib_node_index_t dst_fei, src_fei;
883 fib_entry_flag_t src_flags, dst_flags;
893 p0 = vlib_get_buffer (vm, pi0);
894 arp0 = vlib_buffer_get_current (p0);
896 is_request0 = arp0->opcode
897 == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
899 error0 = ETHERNET_ARP_ERROR_replies_sent;
903 clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet) ?
904 ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0);
907 clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
908 ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0);
910 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
915 /* Check that IP address is local and matches incoming interface. */
916 fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
917 if (~0 == fib_index0)
919 error0 = ETHERNET_ARP_ERROR_interface_no_table;
923 dst_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
924 &arp0->ip4_over_ethernet[1].ip4,
926 dst_flags = fib_entry_get_flags (dst_fei);
928 conn_sw_if_index0 = fib_entry_get_resolving_interface (dst_fei);
930 if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
932 error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
936 /* Honor unnumbered interface, if any */
937 is_unnum0 = sw_if_index0 != conn_sw_if_index0;
939 /* Source must also be local to subnet of matching interface address. */
940 src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
941 &arp0->ip4_over_ethernet[0].ip4,
943 src_flags = fib_entry_get_flags (src_fei);
945 if (!((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
946 (FIB_ENTRY_FLAG_CONNECTED & src_flags)) ||
947 sw_if_index0 != fib_entry_get_resolving_interface (src_fei))
949 error0 = ETHERNET_ARP_ERROR_l3_src_address_not_local;
953 /* Reject requests/replies with our local interface address. */
954 if (FIB_ENTRY_FLAG_LOCAL & src_flags)
956 error0 = ETHERNET_ARP_ERROR_l3_src_address_is_local;
960 dst_is_local0 = (FIB_ENTRY_FLAG_LOCAL & dst_flags);
961 fib_entry_get_prefix (dst_fei, &pfx0);
962 if_addr0 = &pfx0.fp_addr.ip4;
964 /* Fill in ethernet header. */
965 eth0 = ethernet_buffer_get_header (p0);
967 /* Trash ARP packets whose ARP-level source addresses do not
968 match their L2-frame-level source addresses */
969 if (memcmp (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
970 sizeof (eth0->src_address)))
972 error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
976 /* Learn or update sender's mapping only for requests or unicasts
977 that don't match local interface address. */
978 if (ethernet_address_cast (eth0->dst_address) ==
979 ETHERNET_ADDRESS_UNICAST || is_request0)
981 if (am->limit_arp_cache_size &&
982 pool_elts (am->ip4_entry_pool) >= am->limit_arp_cache_size)
983 unset_random_arp_entry ();
985 vnet_arp_set_ip4_over_ethernet (vnm, sw_if_index0,
986 &arp0->ip4_over_ethernet[0],
988 error0 = ETHERNET_ARP_ERROR_l3_src_address_learned;
991 /* Only send a reply for requests sent which match a local interface. */
992 if (!(is_request0 && dst_is_local0))
996 clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply) ?
997 ETHERNET_ARP_ERROR_replies_received : error0);
1003 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
1004 hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1006 /* Send reply back through input interface */
1007 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
1008 next0 = ARP_INPUT_NEXT_REPLY_TX;
1010 arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
1012 arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
1014 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet,
1015 hw_if0->hw_address, 6);
1016 clib_mem_unaligned (&arp0->ip4_over_ethernet[0].ip4.data_u32, u32) =
1019 /* Hardware must be ethernet-like. */
1020 ASSERT (vec_len (hw_if0->hw_address) == 6);
1022 clib_memcpy (eth0->dst_address, eth0->src_address, 6);
1023 clib_memcpy (eth0->src_address, hw_if0->hw_address, 6);
1025 /* Figure out how much to rewind current data from adjacency. */
1026 /* get the adj from the destination's covering connected */
1030 adj_get (fib_entry_get_adj_for_source
1031 (ip4_fib_table_lookup
1032 (ip4_fib_get (fib_index0),
1033 &arp0->ip4_over_ethernet[1].ip4, 31),
1034 FIB_SOURCE_INTERFACE));
1035 if (adj0->lookup_next_index != IP_LOOKUP_NEXT_GLEAN)
1037 error0 = ETHERNET_ARP_ERROR_missing_interface_address;
1041 arp_unnumbered (p0, pi0, eth0, conn_sw_if_index0);
1043 vlib_buffer_advance (p0, -adj0->rewrite_header.data_bytes);
1045 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1046 n_left_to_next, pi0, next0);
1048 n_replies_sent += 1;
1052 if (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ||
1053 (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
1054 arp0->ip4_over_ethernet[1].ip4.as_u32))
1056 error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
1059 /* See if proxy arp is configured for the address */
1062 vnet_sw_interface_t *si;
1063 u32 this_addr = clib_net_to_host_u32
1064 (arp0->ip4_over_ethernet[1].ip4.as_u32);
1067 si = vnet_get_sw_interface (vnm, sw_if_index0);
1069 if (!(si->flags & VNET_SW_INTERFACE_FLAG_PROXY_ARP))
1072 fib_index0 = vec_elt (im4->fib_index_by_sw_if_index,
1075 vec_foreach (pa, am->proxy_arps)
1077 u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr);
1078 u32 hi_addr = clib_net_to_host_u32 (pa->hi_addr);
1080 /* an ARP request hit in the proxy-arp table? */
1081 if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
1082 (fib_index0 == pa->fib_index))
1084 eth0 = ethernet_buffer_get_header (p0);
1086 arp0->ip4_over_ethernet[1].ip4.data_u32;
1089 * Rewind buffer, direct code above not to
1090 * think too hard about it.
1091 * $$$ is the answer ever anything other than
1092 * vlib_buffer_reset(..)?
1094 if_addr0 = &proxy_src;
1096 vlib_buffer_reset (p0);
1097 n_proxy_arp_replies_sent++;
1105 next0 = ARP_INPUT_NEXT_DROP;
1106 p0->error = node->errors[error0];
1108 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1109 n_left_to_next, pi0, next0);
1112 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1115 vlib_error_count (vm, node->node_index,
1116 ETHERNET_ARP_ERROR_replies_sent,
1117 n_replies_sent - n_proxy_arp_replies_sent);
1119 vlib_error_count (vm, node->node_index,
1120 ETHERNET_ARP_ERROR_proxy_arp_replies_sent,
1121 n_proxy_arp_replies_sent);
1122 return frame->n_vectors;
1125 static char *ethernet_arp_error_strings[] = {
1126 #define _(sym,string) string,
1127 foreach_ethernet_arp_error
1132 VLIB_REGISTER_NODE (arp_input_node, static) =
1134 .function = arp_input,
1135 .name = "arp-input",
1136 .vector_size = sizeof (u32),
1137 .n_errors = ETHERNET_ARP_N_ERROR,
1138 .error_strings = ethernet_arp_error_strings,
1139 .n_next_nodes = ARP_INPUT_N_NEXT,
1141 [ARP_INPUT_NEXT_DROP] = "error-drop",
1142 [ARP_INPUT_NEXT_REPLY_TX] = "interface-output",
1144 .format_buffer = format_ethernet_arp_header,
1145 .format_trace = format_ethernet_arp_input_trace,
1150 ip4_arp_entry_sort (void *a1, void *a2)
1152 ethernet_arp_ip4_entry_t *e1 = a1;
1153 ethernet_arp_ip4_entry_t *e2 = a2;
1156 vnet_main_t *vnm = vnet_get_main ();
1158 cmp = vnet_sw_interface_compare (vnm, e1->sw_if_index, e2->sw_if_index);
1160 cmp = ip4_address_compare (&e1->ip4_address, &e2->ip4_address);
1164 static clib_error_t *
1165 show_ip4_arp (vlib_main_t * vm,
1166 unformat_input_t * input, vlib_cli_command_t * cmd)
1168 vnet_main_t *vnm = vnet_get_main ();
1169 ethernet_arp_main_t *am = ðernet_arp_main;
1170 ethernet_arp_ip4_entry_t *e, *es;
1171 ethernet_proxy_arp_t *pa;
1172 clib_error_t *error = 0;
1175 /* Filter entries by interface if given. */
1177 (void) unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index);
1181 pool_foreach (e, am->ip4_entry_pool,
1183 vec_add1 (es, e[0]);
1189 vec_sort_with_function (es, ip4_arp_entry_sort);
1190 vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, 0);
1193 if (sw_if_index != ~0 && e->sw_if_index != sw_if_index)
1195 vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, e);
1200 if (vec_len (am->proxy_arps))
1202 vlib_cli_output (vm, "Proxy arps enabled for:");
1203 vec_foreach (pa, am->proxy_arps)
1205 vlib_cli_output (vm, "Fib_index %d %U - %U ",
1207 format_ip4_address, &pa->lo_addr,
1208 format_ip4_address, &pa->hi_addr);
1216 * Display all the IPv4 ARP entries.
1219 * Example of how to display the IPv4 ARP table:
1220 * @cliexstart{show ip arp}
1221 * Time FIB IP4 Flags Ethernet Interface
1222 * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
1223 * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
1224 * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
1225 * Proxy arps enabled for:
1226 * Fib_index 0 6.0.0.1 - 6.0.0.11
1230 VLIB_CLI_COMMAND (show_ip4_arp_command, static) = {
1231 .path = "show ip arp",
1232 .function = show_ip4_arp,
1233 .short_help = "show ip arp",
1239 pg_edit_t l2_type, l3_type;
1240 pg_edit_t n_l2_address_bytes, n_l3_address_bytes;
1246 } ip4_over_ethernet[2];
1247 } pg_ethernet_arp_header_t;
1250 pg_ethernet_arp_header_init (pg_ethernet_arp_header_t * p)
1252 /* Initialize fields that are not bit fields in the IP header. */
1253 #define _(f) pg_edit_init (&p->f, ethernet_arp_header_t, f);
1256 _(n_l2_address_bytes);
1257 _(n_l3_address_bytes);
1259 _(ip4_over_ethernet[0].ethernet);
1260 _(ip4_over_ethernet[0].ip4);
1261 _(ip4_over_ethernet[1].ethernet);
1262 _(ip4_over_ethernet[1].ip4);
1267 unformat_pg_arp_header (unformat_input_t * input, va_list * args)
1269 pg_stream_t *s = va_arg (*args, pg_stream_t *);
1270 pg_ethernet_arp_header_t *p;
1273 p = pg_create_edit_group (s, sizeof (p[0]), sizeof (ethernet_arp_header_t),
1275 pg_ethernet_arp_header_init (p);
1278 pg_edit_set_fixed (&p->l2_type, ETHERNET_ARP_HARDWARE_TYPE_ethernet);
1279 pg_edit_set_fixed (&p->l3_type, ETHERNET_TYPE_IP4);
1280 pg_edit_set_fixed (&p->n_l2_address_bytes, 6);
1281 pg_edit_set_fixed (&p->n_l3_address_bytes, 4);
1283 if (!unformat (input, "%U: %U/%U -> %U/%U",
1285 unformat_ethernet_arp_opcode_net_byte_order, &p->opcode,
1287 unformat_ethernet_address, &p->ip4_over_ethernet[0].ethernet,
1289 unformat_ip4_address, &p->ip4_over_ethernet[0].ip4,
1291 unformat_ethernet_address, &p->ip4_over_ethernet[1].ethernet,
1293 unformat_ip4_address, &p->ip4_over_ethernet[1].ip4))
1295 /* Free up any edits we may have added. */
1296 pg_free_edit_group (s);
1303 ip4_set_arp_limit (u32 arp_limit)
1305 ethernet_arp_main_t *am = ðernet_arp_main;
1307 am->limit_arp_cache_size = arp_limit;
1312 * @brief Control Plane hook to remove an ARP entry
1315 vnet_arp_unset_ip4_over_ethernet (vnet_main_t * vnm,
1316 u32 sw_if_index, void *a_arg)
1318 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1319 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1321 args.sw_if_index = sw_if_index;
1322 args.flags = ETHERNET_ARP_ARGS_REMOVE;
1323 args.ether_type = ARP_ETHER_TYPE_IP4;
1324 clib_memcpy (&args.a, a, sizeof (*a));
1326 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1327 (u8 *) & args, sizeof (args));
1332 * @brief Internally generated event to flush the ARP cache on an
1333 * interface state change event.
1334 * A flush will remove dynamic ARP entries, and for statics remove the MAC
1335 * address from the corresponding adjacencies.
1338 vnet_arp_flush_ip4_over_ethernet (vnet_main_t * vnm,
1340 arp_ether_type_t et, void *a_arg)
1342 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1343 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1345 args.sw_if_index = sw_if_index;
1346 args.flags = ETHERNET_ARP_ARGS_FLUSH;
1347 args.ether_type = et;
1348 clib_memcpy (&args.a, a, sizeof (*a));
1350 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1351 (u8 *) & args, sizeof (args));
1356 * @brief Internally generated event to populate the ARP cache on an
1357 * interface state change event.
1358 * For static entries this will re-source the adjacencies.
1360 * @param sw_if_index The interface on which the ARP entires are acted
1361 * @param et The ether type of those ARP entries.
1364 vnet_arp_populate_ip4_over_ethernet (vnet_main_t * vnm,
1366 arp_ether_type_t et, void *a_arg)
1368 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1369 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1371 args.sw_if_index = sw_if_index;
1372 args.flags = ETHERNET_ARP_ARGS_POPULATE;
1373 args.ether_type = et;
1374 clib_memcpy (&args.a, a, sizeof (*a));
1376 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1377 (u8 *) & args, sizeof (args));
1382 * arp_add_del_interface_address
1384 * callback when an interface address is added or deleted
1387 arp_add_del_interface_address (ip4_main_t * im,
1390 ip4_address_t * address,
1392 u32 if_address_index, u32 is_del)
1395 * Flush the ARP cache of all entries covered by the address
1396 * that is being removed.
1398 ethernet_arp_main_t *am = ðernet_arp_main;
1399 ethernet_arp_ip4_entry_t *e;
1401 if (vec_len (am->ethernet_arp_by_sw_if_index) < sw_if_index)
1406 ethernet_arp_interface_t *eai;
1407 u32 i, *to_delete = 0;
1410 eai = &am->ethernet_arp_by_sw_if_index[sw_if_index];
1412 hash_foreach_pair (pair, eai->arp_entries, (
1416 (am->ip4_entry_pool,
1419 (ip4_destination_matches_route
1420 (im, &e->ip4_address,
1421 address, address_length))
1423 vec_add1 (to_delete,
1425 am->ip4_entry_pool);}
1429 for (i = 0; i < vec_len (to_delete); i++)
1431 ethernet_arp_ip4_over_ethernet_address_t delme;
1432 e = pool_elt_at_index (am->ip4_entry_pool, to_delete[i]);
1434 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
1435 delme.ip4.as_u32 = e->ip4_address.as_u32;
1437 vnet_arp_flush_ip4_over_ethernet (vnet_get_main (),
1439 ARP_ETHER_TYPE_BOTH, &delme);
1442 vec_free (to_delete);
1447 ethernet_arp_sw_interface_mpls_state_change (u32 sw_if_index, u32 is_enable)
1449 ethernet_arp_main_t *am = ðernet_arp_main;
1450 ethernet_arp_ip4_entry_t *e;
1451 ethernet_arp_interface_t *eai;
1452 u32 i, *to_update = 0;
1455 if (vec_len (am->ethernet_arp_by_sw_if_index) < sw_if_index)
1458 eai = &am->ethernet_arp_by_sw_if_index[sw_if_index];
1461 eai->flags |= ETHERNET_ARP_INTERFACE_MPLS_ENABLE;
1463 eai->flags &= ~ETHERNET_ARP_INTERFACE_MPLS_ENABLE;
1465 hash_foreach_pair (pair, eai->arp_entries, (
1467 vec_add1 (to_update,
1472 for (i = 0; i < vec_len (to_update); i++)
1474 ethernet_arp_ip4_over_ethernet_address_t updateme;
1475 e = pool_elt_at_index (am->ip4_entry_pool, to_update[i]);
1477 clib_memcpy (&updateme.ethernet, e->ethernet_address, 6);
1478 updateme.ip4.as_u32 = e->ip4_address.as_u32;
1482 vnet_arp_populate_ip4_over_ethernet (vnet_get_main (),
1484 ARP_ETHER_TYPE_MPLS,
1491 vec_free (to_update);
1494 static clib_error_t *
1495 ethernet_arp_init (vlib_main_t * vm)
1497 ethernet_arp_main_t *am = ðernet_arp_main;
1498 ip4_main_t *im = &ip4_main;
1499 clib_error_t *error;
1502 if ((error = vlib_call_init_function (vm, ethernet_init)))
1505 ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, arp_input_node.index);
1507 pn = pg_get_node (arp_input_node.index);
1508 pn->unformat_edit = unformat_pg_arp_header;
1510 am->opcode_by_name = hash_create_string (0, sizeof (uword));
1511 #define _(o) hash_set_mem (am->opcode_by_name, #o, ETHERNET_ARP_OPCODE_##o);
1512 foreach_ethernet_arp_opcode;
1515 /* $$$ configurable */
1516 am->limit_arp_cache_size = 50000;
1518 am->pending_resolutions_by_address = hash_create (0, sizeof (uword));
1519 am->mac_changes_by_address = hash_create (0, sizeof (uword));
1521 /* don't trace ARP error packets */
1523 vlib_node_runtime_t *rt =
1524 vlib_node_get_runtime (vm, arp_input_node.index);
1527 vnet_pcap_drop_trace_filter_add_del \
1528 (rt->errors[ETHERNET_ARP_ERROR_##a], \
1530 foreach_ethernet_arp_error
1534 ip4_add_del_interface_address_callback_t cb;
1535 cb.function = arp_add_del_interface_address;
1536 cb.function_opaque = 0;
1537 vec_add1 (im->add_del_interface_address_callbacks, cb);
1539 vec_add1 (mpls_main.mpls_interface_state_change_callbacks,
1540 ethernet_arp_sw_interface_mpls_state_change);
1545 VLIB_INIT_FUNCTION (ethernet_arp_init);
1548 arp_mk_incomplete (ethernet_arp_interface_t * eai,
1549 ethernet_arp_ip4_entry_t * e, arp_ether_type_t et)
1551 fib_prefix_t pfx = {
1553 .fp_proto = FIB_PROTOCOL_IP4,
1555 .ip4 = e->ip4_address,
1560 fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index);
1562 if ((ARP_ETHER_TYPE_IP4 & et) &&
1563 (ADJ_INDEX_INVALID != e->adj_index[FIB_LINK_IP4]))
1566 * revert the adj this ARP entry sourced to incomplete
1568 adj_nbr_update_rewrite (e->adj_index[FIB_LINK_IP4], NULL);
1571 * remove the FIB erntry the ARP entry sourced
1573 fib_table_entry_delete (fib_index, &pfx, FIB_SOURCE_ADJ);
1576 * Unlock the adj now that the ARP entry is no longer a source
1578 adj_unlock (e->adj_index[FIB_LINK_IP4]);
1579 e->adj_index[FIB_LINK_IP4] = ADJ_INDEX_INVALID;
1581 if ((ARP_ETHER_TYPE_MPLS & et) &&
1582 (ADJ_INDEX_INVALID != e->adj_index[FIB_LINK_MPLS]))
1585 * revert the adj this ARP entry sourced to incomplete
1587 adj_nbr_update_rewrite (e->adj_index[FIB_LINK_MPLS], NULL);
1590 * Unlock the adj now that the ARP entry is no longer a source
1592 adj_unlock (e->adj_index[FIB_LINK_MPLS]);
1593 e->adj_index[FIB_LINK_MPLS] = ADJ_INDEX_INVALID;
1598 arp_entry_free (ethernet_arp_interface_t * eai, ethernet_arp_ip4_entry_t * e)
1600 ethernet_arp_main_t *am = ðernet_arp_main;
1602 hash_unset (eai->arp_entries, e->ip4_address.as_u32);
1603 pool_put (am->ip4_entry_pool, e);
1606 static ethernet_arp_ip4_entry_t *
1607 arp_entry_find (ethernet_arp_interface_t * eai, const ip4_address_t * addr)
1609 ethernet_arp_main_t *am = ðernet_arp_main;
1610 ethernet_arp_ip4_entry_t *e = NULL;
1613 if (NULL != eai->arp_entries)
1615 p = hash_get (eai->arp_entries, addr->as_u32);
1619 e = pool_elt_at_index (am->ip4_entry_pool, p[0]);
1626 vnet_arp_unset_ip4_over_ethernet_internal (vnet_main_t * vnm,
1627 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1630 ethernet_arp_main_t *am = ðernet_arp_main;
1631 ethernet_arp_ip4_entry_t *e;
1632 ethernet_arp_interface_t *eai;
1634 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1636 e = arp_entry_find (eai, &args->a.ip4);
1640 arp_mk_incomplete (eai, e, ARP_ETHER_TYPE_BOTH);
1641 arp_entry_free (eai, e);
1648 vnet_arp_flush_ip4_over_ethernet_internal (vnet_main_t * vnm,
1649 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1652 ethernet_arp_main_t *am = ðernet_arp_main;
1653 ethernet_arp_ip4_entry_t *e;
1654 ethernet_arp_interface_t *eai;
1656 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1658 e = arp_entry_find (eai, &args->a.ip4);
1662 arp_mk_incomplete (eai, e, args->ether_type);
1665 * The difference between flush and unset, is that an unset
1666 * means delete for static and dynamic entries. A flush
1667 * means delete only for dynamic. Flushing is what the DP
1668 * does in response to interface events. unset is only done
1669 * by the control plane.
1671 if ((e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC) &&
1672 (args->ether_type & ARP_ETHER_TYPE_IP4))
1674 arp_entry_free (eai, e);
1681 vnet_arp_populate_ip4_over_ethernet_internal (vnet_main_t * vnm,
1682 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1685 ethernet_arp_main_t *am = ðernet_arp_main;
1686 ethernet_arp_ip4_entry_t *e;
1687 ethernet_arp_interface_t *eai;
1689 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1691 e = arp_entry_find (eai, &args->a.ip4);
1695 arp_mk_complete (eai, e, args->ether_type);
1701 set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t
1704 vnet_main_t *vm = vnet_get_main ();
1705 ASSERT (os_get_cpu_number () == 0);
1707 if (a->flags & ETHERNET_ARP_ARGS_REMOVE)
1708 vnet_arp_unset_ip4_over_ethernet_internal (vm, a);
1709 else if (a->flags & ETHERNET_ARP_ARGS_FLUSH)
1710 vnet_arp_flush_ip4_over_ethernet_internal (vm, a);
1711 else if (a->flags & ETHERNET_ARP_ARGS_POPULATE)
1712 vnet_arp_populate_ip4_over_ethernet_internal (vm, a);
1714 vnet_arp_set_ip4_over_ethernet_internal (vm, a);
1718 * @brief Invoked when the interface's admin state changes
1720 static clib_error_t *
1721 ethernet_arp_sw_interface_up_down (vnet_main_t * vnm,
1722 u32 sw_if_index, u32 flags)
1724 ethernet_arp_main_t *am = ðernet_arp_main;
1725 ethernet_arp_ip4_entry_t *e;
1726 u32 i, *to_delete = 0;
1729 pool_foreach (e, am->ip4_entry_pool,
1731 if (e->sw_if_index == sw_if_index)
1733 vec_add1 (to_delete, e - am->ip4_entry_pool);
1738 for (i = 0; i < vec_len (to_delete); i++)
1740 ethernet_arp_ip4_over_ethernet_address_t delme;
1741 e = pool_elt_at_index (am->ip4_entry_pool, to_delete[i]);
1743 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
1744 delme.ip4.as_u32 = e->ip4_address.as_u32;
1746 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
1748 vnet_arp_populate_ip4_over_ethernet (vnm, e->sw_if_index,
1749 ARP_ETHER_TYPE_BOTH, &delme);
1753 vnet_arp_flush_ip4_over_ethernet (vnm, e->sw_if_index,
1754 ARP_ETHER_TYPE_BOTH, &delme);
1758 vec_free (to_delete);
1764 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ethernet_arp_sw_interface_up_down);
1768 increment_ip4_and_mac_address (ethernet_arp_ip4_over_ethernet_address_t * a)
1773 for (i = 3; i >= 0; i--)
1775 old = a->ip4.as_u8[i];
1776 a->ip4.as_u8[i] += 1;
1777 if (old < a->ip4.as_u8[i])
1781 for (i = 5; i >= 0; i--)
1783 old = a->ethernet[i];
1784 a->ethernet[i] += 1;
1785 if (old < a->ethernet[i])
1791 vnet_arp_set_ip4_over_ethernet (vnet_main_t * vnm,
1792 u32 sw_if_index, void *a_arg, int is_static)
1794 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1795 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1797 args.sw_if_index = sw_if_index;
1798 args.is_static = is_static;
1800 args.ether_type = ARP_ETHER_TYPE_IP4;
1801 clib_memcpy (&args.a, a, sizeof (*a));
1803 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1804 (u8 *) & args, sizeof (args));
1809 vnet_proxy_arp_add_del (ip4_address_t * lo_addr,
1810 ip4_address_t * hi_addr, u32 fib_index, int is_del)
1812 ethernet_arp_main_t *am = ðernet_arp_main;
1813 ethernet_proxy_arp_t *pa;
1814 u32 found_at_index = ~0;
1816 vec_foreach (pa, am->proxy_arps)
1818 if (pa->lo_addr == lo_addr->as_u32
1819 && pa->hi_addr == hi_addr->as_u32 && pa->fib_index == fib_index)
1821 found_at_index = pa - am->proxy_arps;
1826 if (found_at_index != ~0)
1828 /* Delete, otherwise it's already in the table */
1830 vec_delete (am->proxy_arps, 1, found_at_index);
1833 /* delete, no such entry */
1835 return VNET_API_ERROR_NO_SUCH_ENTRY;
1837 /* add, not in table */
1838 vec_add2 (am->proxy_arps, pa, 1);
1839 pa->lo_addr = lo_addr->as_u32;
1840 pa->hi_addr = hi_addr->as_u32;
1841 pa->fib_index = fib_index;
1846 * Remove any proxy arp entries asdociated with the
1850 vnet_proxy_arp_fib_reset (u32 fib_id)
1852 ip4_main_t *im = &ip4_main;
1853 ethernet_arp_main_t *am = ðernet_arp_main;
1854 ethernet_proxy_arp_t *pa;
1855 u32 *entries_to_delete = 0;
1860 p = hash_get (im->fib_index_by_table_id, fib_id);
1862 return VNET_API_ERROR_NO_SUCH_ENTRY;
1865 vec_foreach (pa, am->proxy_arps)
1867 if (pa->fib_index == fib_index)
1869 vec_add1 (entries_to_delete, pa - am->proxy_arps);
1873 for (i = 0; i < vec_len (entries_to_delete); i++)
1875 vec_delete (am->proxy_arps, 1, entries_to_delete[i]);
1878 vec_free (entries_to_delete);
1883 static clib_error_t *
1884 ip_arp_add_del_command_fn (vlib_main_t * vm,
1885 unformat_input_t * input, vlib_cli_command_t * cmd)
1887 vnet_main_t *vnm = vnet_get_main ();
1889 ethernet_arp_ip4_over_ethernet_address_t lo_addr, hi_addr, addr;
1898 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1900 /* set ip arp TenGigE1/1/0/1 1.2.3.4 aa:bb:... or aabb.ccdd... */
1901 if (unformat (input, "%U %U %U",
1902 unformat_vnet_sw_interface, vnm, &sw_if_index,
1903 unformat_ip4_address, &addr.ip4,
1904 unformat_ethernet_address, &addr.ethernet))
1907 else if (unformat (input, "delete") || unformat (input, "del"))
1910 else if (unformat (input, "static"))
1913 else if (unformat (input, "count %d", &count))
1916 else if (unformat (input, "fib-id %d", &fib_id))
1918 ip4_main_t *im = &ip4_main;
1919 uword *p = hash_get (im->fib_index_by_table_id, fib_id);
1921 return clib_error_return (0, "fib ID %d doesn't exist\n", fib_id);
1925 else if (unformat (input, "proxy %U - %U",
1926 unformat_ip4_address, &lo_addr.ip4,
1927 unformat_ip4_address, &hi_addr.ip4))
1935 (void) vnet_proxy_arp_add_del (&lo_addr.ip4, &hi_addr.ip4,
1944 for (i = 0; i < count; i++)
1948 uword event_type, *event_data = 0;
1950 /* Park the debug CLI until the arp entry is installed */
1951 vnet_register_ip4_arp_resolution_event
1952 (vnm, &addr.ip4, vlib_current_process (vm),
1953 1 /* type */ , 0 /* data */ );
1955 vnet_arp_set_ip4_over_ethernet
1956 (vnm, sw_if_index, &addr, is_static);
1958 vlib_process_wait_for_event (vm);
1959 event_type = vlib_process_get_events (vm, &event_data);
1960 vec_reset_length (event_data);
1961 if (event_type != 1)
1962 clib_warning ("event type %d unexpected", event_type);
1965 vnet_arp_unset_ip4_over_ethernet (vnm, sw_if_index, &addr);
1967 increment_ip4_and_mac_address (&addr);
1972 return clib_error_return (0, "unknown input `%U'",
1973 format_unformat_error, input);
1981 * Add or delete IPv4 ARP cache entries.
1983 * @note 'set ip arp' options (e.g. delete, static, 'fib-id <id>',
1984 * 'count <number>', 'interface ip4_addr mac_addr') can be added in
1985 * any order and combination.
1989 * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
1990 * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
1991 * @cliexcmd{set ip arp GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1992 * @cliexcmd{set ip arp delete GigabitEthernet2/0/0 6.0.0.3 de:ad:be:ef:ba:be}
1994 * To add or delete an IPv4 ARP cache entry to or from a specific fib
1996 * @cliexcmd{set ip arp fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1997 * @cliexcmd{set ip arp fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1999 * Add or delete IPv4 static ARP cache entries as follows:
2000 * @cliexcmd{set ip arp static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
2001 * @cliexcmd{set ip arp static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
2003 * For testing / debugging purposes, the 'set ip arp' command can add or
2004 * delete multiple entries. Supply the 'count N' parameter:
2005 * @cliexcmd{set ip arp count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
2009 VLIB_CLI_COMMAND (ip_arp_add_del_command, static) = {
2010 .path = "set ip arp",
2012 "set ip arp [del] <intfc> <ip-address> <mac-address> [static] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
2013 .function = ip_arp_add_del_command_fn,
2017 static clib_error_t *
2018 set_int_proxy_arp_command_fn (vlib_main_t * vm,
2019 unformat_input_t * input,
2020 vlib_cli_command_t * cmd)
2022 vnet_main_t *vnm = vnet_get_main ();
2024 vnet_sw_interface_t *si;
2028 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2030 if (unformat (input, "%U", unformat_vnet_sw_interface,
2033 else if (unformat (input, "enable") || unformat (input, "on"))
2035 else if (unformat (input, "disable") || unformat (input, "off"))
2042 return clib_error_return (0, "unknown input '%U'",
2043 format_unformat_error, input);
2045 si = vnet_get_sw_interface (vnm, sw_if_index);
2048 si->flags |= VNET_SW_INTERFACE_FLAG_PROXY_ARP;
2050 si->flags &= ~VNET_SW_INTERFACE_FLAG_PROXY_ARP;
2057 * Enable proxy-arp on an interface. The vpp stack will answer ARP
2058 * requests for the indicated address range. Multiple proxy-arp
2059 * ranges may be provisioned.
2061 * @note Proxy ARP as a technology is infamous for blackholing traffic.
2062 * Also, the underlying implementation has not been performance-tuned.
2063 * Avoid creating an unnecessarily large set of ranges.
2066 * To enable proxy arp on a range of addresses, use:
2067 * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11}
2068 * Append 'del' to delete a range of proxy ARP addresses:
2069 * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11 del}
2070 * You must then specifically enable proxy arp on individual interfaces:
2071 * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 enable}
2072 * To disable proxy arp on an individual interface:
2073 * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 disable}
2076 VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = {
2077 .path = "set interface proxy-arp",
2079 "set interface proxy-arp <intfc> [enable|disable]",
2080 .function = set_int_proxy_arp_command_fn,
2086 * ARP/ND Termination in a L2 Bridge Domain based on IP4/IP6 to MAC
2087 * hash tables mac_by_ip4 and mac_by_ip6 for each BD.
2091 ARP_TERM_NEXT_L2_OUTPUT,
2096 u32 arp_term_next_node_index[32];
2099 arp_term_l2bd (vlib_main_t * vm,
2100 vlib_node_runtime_t * node, vlib_frame_t * frame)
2102 l2input_main_t *l2im = &l2input_main;
2103 u32 n_left_from, next_index, *from, *to_next;
2104 u32 n_replies_sent = 0;
2105 u16 last_bd_index = ~0;
2106 l2_bridge_domain_t *last_bd_config = 0;
2107 l2_input_config_t *cfg0;
2109 from = vlib_frame_vector_args (frame);
2110 n_left_from = frame->n_vectors;
2111 next_index = node->cached_next_index;
2113 while (n_left_from > 0)
2117 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2119 while (n_left_from > 0 && n_left_to_next > 0)
2122 ethernet_header_t *eth0;
2123 ethernet_arp_header_t *arp0;
2126 u32 pi0, error0, next0, sw_if_index0;
2137 n_left_to_next -= 1;
2139 p0 = vlib_get_buffer (vm, pi0);
2140 eth0 = vlib_buffer_get_current (p0);
2141 l3h0 = (u8 *) eth0 + vnet_buffer (p0)->l2.l2_len;
2142 ethertype0 = clib_net_to_host_u16 (*(u16 *) (l3h0 - 2));
2143 arp0 = (ethernet_arp_header_t *) l3h0;
2145 if (PREDICT_FALSE ((ethertype0 != ETHERNET_TYPE_ARP) ||
2147 clib_host_to_net_u16
2148 (ETHERNET_ARP_OPCODE_request))))
2151 /* Must be ARP request packet here */
2152 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
2153 (p0->flags & VLIB_BUFFER_IS_TRACED)))
2155 u8 *t0 = vlib_add_trace (vm, node, p0,
2156 sizeof (ethernet_arp_input_trace_t));
2157 clib_memcpy (t0, l3h0, sizeof (ethernet_arp_input_trace_t));
2160 error0 = ETHERNET_ARP_ERROR_replies_sent;
2163 clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet) ?
2164 ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0);
2167 clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
2168 ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0);
2170 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
2175 /* Trash ARP packets whose ARP-level source addresses do not
2176 match their L2-frame-level source addresses */
2179 (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
2180 sizeof (eth0->src_address))))
2182 error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
2186 /* Check if anyone want ARP request events for L2 BDs */
2188 pending_resolution_t *mc;
2189 ethernet_arp_main_t *am = ðernet_arp_main;
2190 uword *p = hash_get (am->mac_changes_by_address, 0);
2191 if (p && (vnet_buffer (p0)->l2.shg == 0))
2192 { // Only SHG 0 interface which is more likely local
2193 u32 next_index = p[0];
2194 while (next_index != (u32) ~ 0)
2196 int (*fp) (u32, u8 *, u32, u32);
2198 mc = pool_elt_at_index (am->mac_changes, next_index);
2199 fp = mc->data_callback;
2200 /* Call the callback, return 1 to suppress dup events */
2202 rv = (*fp) (mc->data,
2203 arp0->ip4_over_ethernet[0].ethernet,
2205 arp0->ip4_over_ethernet[0].ip4.as_u32);
2206 /* Signal the resolver process */
2208 vlib_process_signal_event (vm, mc->node_index,
2209 mc->type_opaque, mc->data);
2210 next_index = mc->next_index;
2215 /* lookup BD mac_by_ip4 hash table for MAC entry */
2216 ip0 = arp0->ip4_over_ethernet[1].ip4.as_u32;
2217 bd_index0 = vnet_buffer (p0)->l2.bd_index;
2218 if (PREDICT_FALSE ((bd_index0 != last_bd_index)
2219 || (last_bd_index == (u16) ~ 0)))
2221 last_bd_index = bd_index0;
2222 last_bd_config = vec_elt_at_index (l2im->bd_configs, bd_index0);
2224 macp0 = (u8 *) hash_get (last_bd_config->mac_by_ip4, ip0);
2226 if (PREDICT_FALSE (!macp0))
2227 goto next_l2_feature; /* MAC not found */
2229 /* MAC found, send ARP reply -
2230 Convert ARP request packet to ARP reply */
2231 arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
2232 arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
2233 arp0->ip4_over_ethernet[0].ip4.as_u32 = ip0;
2234 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, macp0, 6);
2235 clib_memcpy (eth0->dst_address, eth0->src_address, 6);
2236 clib_memcpy (eth0->src_address, macp0, 6);
2237 n_replies_sent += 1;
2240 /* For BVI, need to use l2-fwd node to send ARP reply as
2241 l2-output node cannot output packet to BVI properly */
2242 cfg0 = vec_elt_at_index (l2im->configs, sw_if_index0);
2243 if (PREDICT_FALSE (cfg0->bvi))
2245 vnet_buffer (p0)->l2.feature_bitmap |= L2INPUT_FEAT_FWD;
2246 vnet_buffer (p0)->sw_if_index[VLIB_RX] = 0;
2247 goto next_l2_feature;
2250 /* Send ARP/ND reply back out input interface through l2-output */
2251 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
2252 next0 = ARP_TERM_NEXT_L2_OUTPUT;
2253 /* Note that output to VXLAN tunnel will fail due to SHG which
2254 is probably desireable since ARP termination is not intended
2255 for ARP requests from other hosts. If output to VXLAN tunnel is
2256 required, however, can just clear the SHG in packet as follows:
2257 vnet_buffer(p0)->l2.shg = 0; */
2258 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
2259 n_left_to_next, pi0, next0);
2263 /* IP6 ND event notification or solicitation handling to generate
2264 local response instead of flooding */
2265 iph0 = (ip6_header_t *) l3h0;
2266 if (PREDICT_FALSE (ethertype0 == ETHERNET_TYPE_IP6 &&
2267 iph0->protocol == IP_PROTOCOL_ICMP6 &&
2268 !ip6_address_is_link_local_unicast
2269 (&iph0->src_address)
2271 !ip6_address_is_unspecified
2272 (&iph0->src_address)))
2274 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
2275 if (vnet_ip6_nd_term (vm, node, p0, eth0, iph0, sw_if_index0,
2276 vnet_buffer (p0)->l2.bd_index,
2277 vnet_buffer (p0)->l2.shg))
2278 goto output_response;
2283 u32 feature_bitmap0 =
2284 vnet_buffer (p0)->l2.feature_bitmap & ~L2INPUT_FEAT_ARP_TERM;
2285 vnet_buffer (p0)->l2.feature_bitmap = feature_bitmap0;
2286 next0 = feat_bitmap_get_next_node_index (arp_term_next_node_index,
2288 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
2289 n_left_to_next, pi0, next0);
2294 if (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ||
2295 (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
2296 arp0->ip4_over_ethernet[1].ip4.as_u32))
2298 error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
2300 next0 = ARP_TERM_NEXT_DROP;
2301 p0->error = node->errors[error0];
2303 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
2304 n_left_to_next, pi0, next0);
2307 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2310 vlib_error_count (vm, node->node_index,
2311 ETHERNET_ARP_ERROR_replies_sent, n_replies_sent);
2312 return frame->n_vectors;
2316 VLIB_REGISTER_NODE (arp_term_l2bd_node, static) = {
2317 .function = arp_term_l2bd,
2318 .name = "arp-term-l2bd",
2319 .vector_size = sizeof (u32),
2320 .n_errors = ETHERNET_ARP_N_ERROR,
2321 .error_strings = ethernet_arp_error_strings,
2322 .n_next_nodes = ARP_TERM_N_NEXT,
2324 [ARP_TERM_NEXT_L2_OUTPUT] = "l2-output",
2325 [ARP_TERM_NEXT_DROP] = "error-drop",
2327 .format_buffer = format_ethernet_arp_header,
2328 .format_trace = format_arp_term_input_trace,
2333 arp_term_init (vlib_main_t * vm)
2334 { // Initialize the feature next-node indexes
2335 feat_bitmap_init_next_nodes (vm,
2336 arp_term_l2bd_node.index,
2338 l2input_get_feat_names (),
2339 arp_term_next_node_index);
2343 VLIB_INIT_FUNCTION (arp_term_init);
2346 * fd.io coding-style-patch-verification: ON
2349 * eval: (c-set-style "gnu")