2 * ethernet/arp.c: IP v4 ARP node
4 * Copyright (c) 2010 Cisco and/or its affiliates.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include <vnet/ip/ip.h>
19 #include <vnet/ip/ip6.h>
20 #include <vnet/ethernet/ethernet.h>
21 #include <vnet/ethernet/arp_packet.h>
22 #include <vnet/l2/l2_input.h>
23 #include <vppinfra/mhash.h>
24 #include <vnet/fib/ip4_fib.h>
25 #include <vnet/adj/adj_nbr.h>
26 #include <vnet/mpls/mpls.h>
32 * This file contains code to manage the IPv4 ARP tables (IP Address
33 * to MAC Address lookup).
37 void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
40 * @brief Per-interface ARP configuration and state
42 typedef struct ethernet_arp_interface_t_
45 * Hash table of ARP entries.
46 * Since this hash table is per-interface, the key is only the IPv4 address.
49 } ethernet_arp_interface_t;
56 } ethernet_proxy_arp_t;
64 /* Used for arp event notification only */
67 } pending_resolution_t;
71 /* Hash tables mapping name to opcode. */
72 uword *opcode_by_name;
74 /* lite beer "glean" adjacency handling */
75 uword *pending_resolutions_by_address;
76 pending_resolution_t *pending_resolutions;
78 /* Mac address change notification */
79 uword *mac_changes_by_address;
80 pending_resolution_t *mac_changes;
82 ethernet_arp_ip4_entry_t *ip4_entry_pool;
84 /* ARP attack mitigation */
86 u32 limit_arp_cache_size;
88 /** Per interface state */
89 ethernet_arp_interface_t *ethernet_arp_by_sw_if_index;
91 /* Proxy arp vector */
92 ethernet_proxy_arp_t *proxy_arps;
93 } ethernet_arp_main_t;
95 static ethernet_arp_main_t ethernet_arp_main;
100 ethernet_arp_ip4_over_ethernet_address_t a;
103 #define ETHERNET_ARP_ARGS_REMOVE (1<<0)
104 #define ETHERNET_ARP_ARGS_FLUSH (1<<1)
105 #define ETHERNET_ARP_ARGS_POPULATE (1<<2)
106 } vnet_arp_set_ip4_over_ethernet_rpc_args_t;
109 set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t
113 format_ethernet_arp_hardware_type (u8 * s, va_list * va)
115 ethernet_arp_hardware_type_t h = va_arg (*va, ethernet_arp_hardware_type_t);
119 #define _(n,f) case n: t = #f; break;
120 foreach_ethernet_arp_hardware_type;
124 return format (s, "unknown 0x%x", h);
127 return format (s, "%s", t);
131 format_ethernet_arp_opcode (u8 * s, va_list * va)
133 ethernet_arp_opcode_t o = va_arg (*va, ethernet_arp_opcode_t);
137 #define _(f) case ETHERNET_ARP_OPCODE_##f: t = #f; break;
138 foreach_ethernet_arp_opcode;
142 return format (s, "unknown 0x%x", o);
145 return format (s, "%s", t);
149 unformat_ethernet_arp_opcode_host_byte_order (unformat_input_t * input,
152 int *result = va_arg (*args, int *);
153 ethernet_arp_main_t *am = ðernet_arp_main;
156 /* Numeric opcode. */
157 if (unformat (input, "0x%x", &x) || unformat (input, "%d", &x))
166 if (unformat_user (input, unformat_vlib_number_by_name,
167 am->opcode_by_name, &i))
177 unformat_ethernet_arp_opcode_net_byte_order (unformat_input_t * input,
180 int *result = va_arg (*args, int *);
182 (input, unformat_ethernet_arp_opcode_host_byte_order, result))
185 *result = clib_host_to_net_u16 ((u16) * result);
190 format_ethernet_arp_header (u8 * s, va_list * va)
192 ethernet_arp_header_t *a = va_arg (*va, ethernet_arp_header_t *);
193 u32 max_header_bytes = va_arg (*va, u32);
195 u16 l2_type, l3_type;
197 if (max_header_bytes != 0 && sizeof (a[0]) > max_header_bytes)
198 return format (s, "ARP header truncated");
200 l2_type = clib_net_to_host_u16 (a->l2_type);
201 l3_type = clib_net_to_host_u16 (a->l3_type);
203 indent = format_get_indent (s);
205 s = format (s, "%U, type %U/%U, address size %d/%d",
206 format_ethernet_arp_opcode, clib_net_to_host_u16 (a->opcode),
207 format_ethernet_arp_hardware_type, l2_type,
208 format_ethernet_type, l3_type,
209 a->n_l2_address_bytes, a->n_l3_address_bytes);
211 if (l2_type == ETHERNET_ARP_HARDWARE_TYPE_ethernet
212 && l3_type == ETHERNET_TYPE_IP4)
214 s = format (s, "\n%U%U/%U -> %U/%U",
215 format_white_space, indent,
216 format_ethernet_address, a->ip4_over_ethernet[0].ethernet,
217 format_ip4_address, &a->ip4_over_ethernet[0].ip4,
218 format_ethernet_address, a->ip4_over_ethernet[1].ethernet,
219 format_ip4_address, &a->ip4_over_ethernet[1].ip4);
223 uword n2 = a->n_l2_address_bytes;
224 uword n3 = a->n_l3_address_bytes;
225 s = format (s, "\n%U%U/%U -> %U/%U",
226 format_white_space, indent,
227 format_hex_bytes, a->data + 0 * n2 + 0 * n3, n2,
228 format_hex_bytes, a->data + 1 * n2 + 0 * n3, n3,
229 format_hex_bytes, a->data + 1 * n2 + 1 * n3, n2,
230 format_hex_bytes, a->data + 2 * n2 + 1 * n3, n3);
237 format_ethernet_arp_ip4_entry (u8 * s, va_list * va)
239 vnet_main_t *vnm = va_arg (*va, vnet_main_t *);
240 ethernet_arp_ip4_entry_t *e = va_arg (*va, ethernet_arp_ip4_entry_t *);
241 vnet_sw_interface_t *si;
245 return format (s, "%=12s%=16s%=6s%=20s%=24s", "Time", "IP4",
246 "Flags", "Ethernet", "Interface");
248 si = vnet_get_sw_interface (vnm, e->sw_if_index);
250 if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC)
251 flags = format (flags, "S");
253 if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC)
254 flags = format (flags, "D");
256 s = format (s, "%=12U%=16U%=6s%=20U%=24U",
257 format_vlib_cpu_time, vnm->vlib_main, e->cpu_time_last_updated,
258 format_ip4_address, &e->ip4_address,
259 flags ? (char *) flags : "",
260 format_ethernet_address, e->ethernet_address,
261 format_vnet_sw_interface_name, vnm, si);
270 } ethernet_arp_input_trace_t;
273 format_ethernet_arp_input_trace (u8 * s, va_list * va)
275 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
276 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
277 ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
280 format_ethernet_arp_header,
281 t->packet_data, sizeof (t->packet_data));
287 format_arp_term_input_trace (u8 * s, va_list * va)
289 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
290 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
291 ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
293 /* arp-term trace data saved is either arp or ip6/icmp6 packet:
294 - for arp, the 1st 16-bit field is hw type of value of 0x0001.
295 - for ip6, the first nibble has value of 6. */
296 s = format (s, "%U", t->packet_data[0] == 0 ?
297 format_ethernet_arp_header : format_ip6_header,
298 t->packet_data, sizeof (t->packet_data));
304 arp_nbr_probe (ip_adjacency_t * adj)
306 vnet_main_t *vnm = vnet_get_main ();
307 ip4_main_t *im = &ip4_main;
308 ip_interface_address_t *ia;
309 ethernet_arp_header_t *h;
310 vnet_hw_interface_t *hi;
311 vnet_sw_interface_t *si;
317 vm = vlib_get_main ();
319 si = vnet_get_sw_interface (vnm, adj->rewrite_header.sw_if_index);
321 if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
327 ip4_interface_address_matching_destination (im,
328 &adj->sub_type.nbr.next_hop.
338 vlib_packet_template_get_packet (vm, &im->ip4_arp_request_packet_template,
341 hi = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index);
343 clib_memcpy (h->ip4_over_ethernet[0].ethernet,
344 hi->hw_address, sizeof (h->ip4_over_ethernet[0].ethernet));
346 h->ip4_over_ethernet[0].ip4 = src[0];
347 h->ip4_over_ethernet[1].ip4 = adj->sub_type.nbr.next_hop.ip4;
349 b = vlib_get_buffer (vm, bi);
350 vnet_buffer (b)->sw_if_index[VLIB_RX] =
351 vnet_buffer (b)->sw_if_index[VLIB_TX] = adj->rewrite_header.sw_if_index;
353 /* Add encapsulation string for software interface (e.g. ethernet header). */
354 vnet_rewrite_one_header (adj[0], h, sizeof (ethernet_header_t));
355 vlib_buffer_advance (b, -adj->rewrite_header.data_bytes);
358 vlib_frame_t *f = vlib_get_frame_to_node (vm, hi->output_node_index);
359 u32 *to_next = vlib_frame_vector_args (f);
362 vlib_put_frame_to_node (vm, hi->output_node_index, f);
367 arp_mk_complete (adj_index_t ai, ethernet_arp_ip4_entry_t * e)
369 adj_nbr_update_rewrite
370 (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE,
371 ethernet_build_rewrite (vnet_get_main (),
373 adj_get_link_type (ai), e->ethernet_address));
377 arp_mk_incomplete (adj_index_t ai, ethernet_arp_ip4_entry_t * e)
379 adj_nbr_update_rewrite
381 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
382 ethernet_build_rewrite (vnet_get_main (),
385 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
388 static ethernet_arp_ip4_entry_t *
389 arp_entry_find (ethernet_arp_interface_t * eai, const ip4_address_t * addr)
391 ethernet_arp_main_t *am = ðernet_arp_main;
392 ethernet_arp_ip4_entry_t *e = NULL;
395 if (NULL != eai->arp_entries)
397 p = hash_get (eai->arp_entries, addr->as_u32);
401 e = pool_elt_at_index (am->ip4_entry_pool, p[0]);
408 arp_mk_complete_walk (adj_index_t ai, void *ctx)
410 ethernet_arp_ip4_entry_t *e = ctx;
412 arp_mk_complete (ai, e);
414 return (ADJ_WALK_RC_CONTINUE);
418 arp_mk_incomplete_walk (adj_index_t ai, void *ctx)
420 ethernet_arp_ip4_entry_t *e = ctx;
422 arp_mk_incomplete (ai, e);
424 return (ADJ_WALK_RC_CONTINUE);
428 arp_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai)
430 ethernet_arp_main_t *am = ðernet_arp_main;
431 ethernet_arp_interface_t *arp_int;
432 ethernet_arp_ip4_entry_t *e;
437 vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index);
438 arp_int = &am->ethernet_arp_by_sw_if_index[sw_if_index];
439 e = arp_entry_find (arp_int, &adj->sub_type.nbr.next_hop.ip4);
443 adj_nbr_walk_nh4 (sw_if_index,
444 &e->ip4_address, arp_mk_complete_walk, e);
449 * no matching ARP entry.
450 * construct the rewire required to for an ARP packet, and stick
451 * that in the adj's pipe to smoke.
453 adj_nbr_update_rewrite (ai,
454 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
455 ethernet_build_rewrite (vnm,
458 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
461 * since the FIB has added this adj for a route, it makes sense it may
462 * want to forward traffic sometime soon. Let's send a speculative ARP.
463 * just one. If we were to do periodically that wouldn't be bad either,
464 * but that's more code than i'm prepared to write at this time for
465 * relatively little reward.
472 vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm,
473 vnet_arp_set_ip4_over_ethernet_rpc_args_t
476 ethernet_arp_ip4_entry_t *e = 0;
477 ethernet_arp_main_t *am = ðernet_arp_main;
478 ethernet_arp_ip4_over_ethernet_address_t *a = &args->a;
479 vlib_main_t *vm = vlib_get_main ();
480 int make_new_arp_cache_entry = 1;
482 pending_resolution_t *pr, *mc;
483 ethernet_arp_interface_t *arp_int;
484 int is_static = args->is_static;
485 u32 sw_if_index = args->sw_if_index;
487 vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index);
489 arp_int = &am->ethernet_arp_by_sw_if_index[sw_if_index];
491 if (NULL != arp_int->arp_entries)
493 p = hash_get (arp_int->arp_entries, a->ip4.as_u32);
496 e = pool_elt_at_index (am->ip4_entry_pool, p[0]);
498 /* Refuse to over-write static arp. */
499 if (!is_static && (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC))
501 make_new_arp_cache_entry = 0;
505 if (make_new_arp_cache_entry)
509 .fp_proto = FIB_PROTOCOL_IP4,
517 pool_get (am->ip4_entry_pool, e);
519 if (NULL == arp_int->arp_entries)
521 arp_int->arp_entries = hash_create (0, sizeof (u32));
524 hash_set (arp_int->arp_entries, a->ip4.as_u32, e - am->ip4_entry_pool);
526 e->sw_if_index = sw_if_index;
527 e->ip4_address = a->ip4;
528 clib_memcpy (e->ethernet_address,
529 a->ethernet, sizeof (e->ethernet_address));
531 fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index);
533 fib_table_entry_update_one_path (fib_index,
536 FIB_ENTRY_FLAG_ATTACHED,
541 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
546 * prevent a DoS attack from the data-plane that
547 * spams us with no-op updates to the MAC address
549 if (0 == memcmp (e->ethernet_address,
550 a->ethernet, sizeof (e->ethernet_address)))
553 /* Update time stamp and ethernet address. */
554 clib_memcpy (e->ethernet_address, a->ethernet,
555 sizeof (e->ethernet_address));
558 e->cpu_time_last_updated = clib_cpu_time_now ();
560 e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC;
562 e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC;
564 adj_nbr_walk_nh4 (sw_if_index, &e->ip4_address, arp_mk_complete_walk, e);
566 /* Customer(s) waiting for this address to be resolved? */
567 p = hash_get (am->pending_resolutions_by_address, a->ip4.as_u32);
573 while (next_index != (u32) ~ 0)
575 pr = pool_elt_at_index (am->pending_resolutions, next_index);
576 vlib_process_signal_event (vm, pr->node_index,
577 pr->type_opaque, pr->data);
578 next_index = pr->next_index;
579 pool_put (am->pending_resolutions, pr);
582 hash_unset (am->pending_resolutions_by_address, a->ip4.as_u32);
585 /* Customer(s) requesting ARP event for this address? */
586 p = hash_get (am->mac_changes_by_address, a->ip4.as_u32);
592 while (next_index != (u32) ~ 0)
594 int (*fp) (u32, u8 *, u32, u32);
596 mc = pool_elt_at_index (am->mac_changes, next_index);
597 fp = mc->data_callback;
599 /* Call the user's data callback, return 1 to suppress dup events */
601 rv = (*fp) (mc->data, a->ethernet, sw_if_index, 0);
604 * Signal the resolver process, as long as the user
605 * says they want to be notified
608 vlib_process_signal_event (vm, mc->node_index,
609 mc->type_opaque, mc->data);
610 next_index = mc->next_index;
618 vnet_register_ip4_arp_resolution_event (vnet_main_t * vnm,
621 uword type_opaque, uword data)
623 ethernet_arp_main_t *am = ðernet_arp_main;
624 ip4_address_t *address = address_arg;
626 pending_resolution_t *pr;
628 pool_get (am->pending_resolutions, pr);
631 pr->node_index = node_index;
632 pr->type_opaque = type_opaque;
634 pr->data_callback = 0;
636 p = hash_get (am->pending_resolutions_by_address, address->as_u32);
639 /* Insert new resolution at the head of the list */
640 pr->next_index = p[0];
641 hash_unset (am->pending_resolutions_by_address, address->as_u32);
644 hash_set (am->pending_resolutions_by_address, address->as_u32,
645 pr - am->pending_resolutions);
649 vnet_add_del_ip4_arp_change_event (vnet_main_t * vnm,
654 uword type_opaque, uword data, int is_add)
656 ethernet_arp_main_t *am = ðernet_arp_main;
657 ip4_address_t *address = address_arg;
659 pending_resolution_t *mc;
660 void (*fp) (u32, u8 *) = data_callback;
664 pool_get (am->mac_changes, mc);
667 mc->node_index = node_index;
668 mc->type_opaque = type_opaque;
670 mc->data_callback = data_callback;
673 p = hash_get (am->mac_changes_by_address, address->as_u32);
676 /* Insert new resolution at the head of the list */
677 mc->next_index = p[0];
678 hash_unset (am->mac_changes_by_address, address->as_u32);
681 hash_set (am->mac_changes_by_address, address->as_u32,
682 mc - am->mac_changes);
688 pending_resolution_t *mc_last = 0;
690 p = hash_get (am->mac_changes_by_address, address->as_u32);
692 return VNET_API_ERROR_NO_SUCH_ENTRY;
696 while (index != (u32) ~ 0)
698 mc = pool_elt_at_index (am->mac_changes, index);
699 if (mc->node_index == node_index &&
700 mc->type_opaque == type_opaque && mc->pid == pid)
702 /* Clients may need to clean up pool entries, too */
704 (*fp) (mc->data, 0 /* no new mac addrs */ );
707 hash_unset (am->mac_changes_by_address, address->as_u32);
708 if (mc->next_index != ~0)
709 hash_set (am->mac_changes_by_address, address->as_u32,
711 pool_put (am->mac_changes, mc);
717 mc_last->next_index = mc->next_index;
718 pool_put (am->mac_changes, mc);
723 index = mc->next_index;
726 return VNET_API_ERROR_NO_SUCH_ENTRY;
730 /* Either we drop the packet or we send a reply to the sender. */
734 ARP_INPUT_NEXT_REPLY_TX,
738 #define foreach_ethernet_arp_error \
739 _ (replies_sent, "ARP replies sent") \
740 _ (l2_type_not_ethernet, "L2 type not ethernet") \
741 _ (l3_type_not_ip4, "L3 type not IP4") \
742 _ (l3_src_address_not_local, "IP4 source address not local to subnet") \
743 _ (l3_dst_address_not_local, "IP4 destination address not local to subnet") \
744 _ (l3_src_address_is_local, "IP4 source address matches local interface") \
745 _ (l3_src_address_learned, "ARP request IP4 source address learned") \
746 _ (replies_received, "ARP replies received") \
747 _ (opcode_not_request, "ARP opcode not request") \
748 _ (proxy_arp_replies_sent, "Proxy ARP replies sent") \
749 _ (l2_address_mismatch, "ARP hw addr does not match L2 frame src addr") \
750 _ (missing_interface_address, "ARP missing interface address") \
751 _ (gratuitous_arp, "ARP probe or announcement dropped") \
752 _ (interface_no_table, "Interface is not mapped to an IP table") \
756 #define _(sym,string) ETHERNET_ARP_ERROR_##sym,
757 foreach_ethernet_arp_error
759 ETHERNET_ARP_N_ERROR,
760 } ethernet_arp_input_error_t;
764 unset_random_arp_entry (void)
766 ethernet_arp_main_t *am = ðernet_arp_main;
767 ethernet_arp_ip4_entry_t *e;
768 vnet_main_t *vnm = vnet_get_main ();
769 ethernet_arp_ip4_over_ethernet_address_t delme;
772 index = pool_next_index (am->ip4_entry_pool, am->arp_delete_rotor);
773 am->arp_delete_rotor = index;
775 /* Try again from elt 0, could happen if an intfc goes down */
778 index = pool_next_index (am->ip4_entry_pool, am->arp_delete_rotor);
779 am->arp_delete_rotor = index;
782 /* Nothing left in the pool */
786 e = pool_elt_at_index (am->ip4_entry_pool, index);
788 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
789 delme.ip4.as_u32 = e->ip4_address.as_u32;
791 vnet_arp_unset_ip4_over_ethernet (vnm, e->sw_if_index, &delme);
795 arp_unnumbered (vlib_buffer_t * p0,
796 u32 pi0, ethernet_header_t * eth0, u32 sw_if_index)
798 vlib_main_t *vm = vlib_get_main ();
799 vnet_main_t *vnm = vnet_get_main ();
800 vnet_interface_main_t *vim = &vnm->interface_main;
801 vnet_sw_interface_t *si;
802 vnet_hw_interface_t *hi;
803 u32 unnum_src_sw_if_index;
804 u32 *broadcast_swifs = 0;
809 u8 dst_mac_address[6];
811 ethernet_arp_header_t *arp0;
813 /* Save the dst mac address */
814 clib_memcpy (dst_mac_address, eth0->dst_address, sizeof (dst_mac_address));
816 /* Figure out which sw_if_index supplied the address */
817 unnum_src_sw_if_index = sw_if_index;
819 /* Track down all users of the unnumbered source */
821 pool_foreach (si, vim->sw_interfaces,
823 if (si->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED &&
824 (si->unnumbered_sw_if_index == unnum_src_sw_if_index))
826 vec_add1 (broadcast_swifs, si->sw_if_index);
831 /* If there are no interfaces un-unmbered to this interface,
833 if (0 == vec_len (broadcast_swifs))
836 /* Allocate buffering if we need it */
837 if (vec_len (broadcast_swifs) > 1)
839 vec_validate (buffers, vec_len (broadcast_swifs) - 2);
840 n_alloc = vlib_buffer_alloc (vm, buffers, vec_len (buffers));
841 _vec_len (buffers) = n_alloc;
842 for (i = 0; i < n_alloc; i++)
844 b0 = vlib_get_buffer (vm, buffers[i]);
846 /* xerox (partially built) ARP pkt */
847 clib_memcpy (b0->data, p0->data,
848 p0->current_length + p0->current_data);
849 b0->current_data = p0->current_data;
850 b0->current_length = p0->current_length;
851 vnet_buffer (b0)->sw_if_index[VLIB_RX] =
852 vnet_buffer (p0)->sw_if_index[VLIB_RX];
856 vec_insert (buffers, 1, 0);
859 for (i = 0; i < vec_len (buffers); i++)
861 b0 = vlib_get_buffer (vm, buffers[i]);
862 arp0 = vlib_buffer_get_current (b0);
864 hi = vnet_get_sup_hw_interface (vnm, broadcast_swifs[i]);
865 si = vnet_get_sw_interface (vnm, broadcast_swifs[i]);
867 /* For decoration, most likely */
868 vnet_buffer (b0)->sw_if_index[VLIB_TX] = hi->sw_if_index;
870 /* Fix ARP pkt src address */
871 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, hi->hw_address, 6);
873 /* Build L2 encaps for this swif */
874 header_size = sizeof (ethernet_header_t);
875 if (si->sub.eth.flags.one_tag)
877 else if (si->sub.eth.flags.two_tags)
880 vlib_buffer_advance (b0, -header_size);
881 eth0 = vlib_buffer_get_current (b0);
883 if (si->sub.eth.flags.one_tag)
885 ethernet_vlan_header_t *outer = (void *) (eth0 + 1);
887 eth0->type = si->sub.eth.flags.dot1ad ?
888 clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
889 clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
890 outer->priority_cfi_and_id =
891 clib_host_to_net_u16 (si->sub.eth.outer_vlan_id);
892 outer->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
895 else if (si->sub.eth.flags.two_tags)
897 ethernet_vlan_header_t *outer = (void *) (eth0 + 1);
898 ethernet_vlan_header_t *inner = (void *) (outer + 1);
900 eth0->type = si->sub.eth.flags.dot1ad ?
901 clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
902 clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
903 outer->priority_cfi_and_id =
904 clib_host_to_net_u16 (si->sub.eth.outer_vlan_id);
905 outer->type = clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
906 inner->priority_cfi_and_id =
907 clib_host_to_net_u16 (si->sub.eth.inner_vlan_id);
908 inner->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
913 eth0->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
916 /* Restore the original dst address, set src address */
917 clib_memcpy (eth0->dst_address, dst_mac_address,
918 sizeof (eth0->dst_address));
919 clib_memcpy (eth0->src_address, hi->hw_address,
920 sizeof (eth0->src_address));
922 /* Transmit replicas */
926 vlib_get_frame_to_node (vm, hi->output_node_index);
927 u32 *to_next = vlib_frame_vector_args (f);
928 to_next[0] = buffers[i];
930 vlib_put_frame_to_node (vm, hi->output_node_index, f);
934 /* The regular path outputs the original pkt.. */
935 vnet_buffer (p0)->sw_if_index[VLIB_TX] = broadcast_swifs[0];
937 vec_free (broadcast_swifs);
944 arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
946 ethernet_arp_main_t *am = ðernet_arp_main;
947 vnet_main_t *vnm = vnet_get_main ();
948 ip4_main_t *im4 = &ip4_main;
949 u32 n_left_from, next_index, *from, *to_next;
950 u32 n_replies_sent = 0, n_proxy_arp_replies_sent = 0;
952 from = vlib_frame_vector_args (frame);
953 n_left_from = frame->n_vectors;
954 next_index = node->cached_next_index;
956 if (node->flags & VLIB_NODE_FLAG_TRACE)
957 vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
959 sizeof (ethernet_arp_input_trace_t));
961 while (n_left_from > 0)
965 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
967 while (n_left_from > 0 && n_left_to_next > 0)
970 vnet_hw_interface_t *hw_if0;
971 ethernet_arp_header_t *arp0;
972 ethernet_header_t *eth0;
973 ip_adjacency_t *adj0;
974 ip4_address_t *if_addr0, proxy_src;
975 u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0;
976 u8 is_request0, dst_is_local0, is_unnum0;
977 ethernet_proxy_arp_t *pa;
978 fib_node_index_t dst_fei, src_fei;
980 fib_entry_flag_t src_flags, dst_flags;
990 p0 = vlib_get_buffer (vm, pi0);
991 arp0 = vlib_buffer_get_current (p0);
993 is_request0 = arp0->opcode
994 == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
996 error0 = ETHERNET_ARP_ERROR_replies_sent;
1000 clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet) ?
1001 ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0);
1004 clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
1005 ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0);
1007 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
1012 /* Check that IP address is local and matches incoming interface. */
1013 fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
1014 if (~0 == fib_index0)
1016 error0 = ETHERNET_ARP_ERROR_interface_no_table;
1020 dst_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
1021 &arp0->ip4_over_ethernet[1].ip4,
1023 dst_flags = fib_entry_get_flags_for_source (dst_fei,
1024 FIB_SOURCE_INTERFACE);
1027 fib_entry_get_resolving_interface_for_source (dst_fei,
1028 FIB_SOURCE_INTERFACE);
1030 if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
1032 error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
1036 /* Honor unnumbered interface, if any */
1037 is_unnum0 = sw_if_index0 != conn_sw_if_index0;
1039 /* Source must also be local to subnet of matching interface address. */
1040 src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
1041 &arp0->ip4_over_ethernet[0].ip4,
1043 src_flags = fib_entry_get_flags (src_fei);
1045 if (!((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
1046 (FIB_ENTRY_FLAG_CONNECTED & src_flags)) ||
1047 sw_if_index0 != fib_entry_get_resolving_interface (src_fei))
1049 error0 = ETHERNET_ARP_ERROR_l3_src_address_not_local;
1053 /* Reject requests/replies with our local interface address. */
1054 if (FIB_ENTRY_FLAG_LOCAL & src_flags)
1056 error0 = ETHERNET_ARP_ERROR_l3_src_address_is_local;
1060 dst_is_local0 = (FIB_ENTRY_FLAG_LOCAL & dst_flags);
1061 fib_entry_get_prefix (dst_fei, &pfx0);
1062 if_addr0 = &pfx0.fp_addr.ip4;
1064 /* Fill in ethernet header. */
1065 eth0 = ethernet_buffer_get_header (p0);
1067 /* Trash ARP packets whose ARP-level source addresses do not
1068 match their L2-frame-level source addresses */
1069 if (memcmp (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
1070 sizeof (eth0->src_address)))
1072 error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
1076 /* Learn or update sender's mapping only for requests or unicasts
1077 that don't match local interface address. */
1078 if (ethernet_address_cast (eth0->dst_address) ==
1079 ETHERNET_ADDRESS_UNICAST || is_request0)
1081 if (am->limit_arp_cache_size &&
1082 pool_elts (am->ip4_entry_pool) >= am->limit_arp_cache_size)
1083 unset_random_arp_entry ();
1085 vnet_arp_set_ip4_over_ethernet (vnm, sw_if_index0,
1086 &arp0->ip4_over_ethernet[0],
1087 0 /* is_static */ );
1088 error0 = ETHERNET_ARP_ERROR_l3_src_address_learned;
1091 /* Only send a reply for requests sent which match a local interface. */
1092 if (!(is_request0 && dst_is_local0))
1096 clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply) ?
1097 ETHERNET_ARP_ERROR_replies_received : error0);
1103 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
1104 hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1106 /* Send reply back through input interface */
1107 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
1108 next0 = ARP_INPUT_NEXT_REPLY_TX;
1110 arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
1112 arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
1114 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet,
1115 hw_if0->hw_address, 6);
1116 clib_mem_unaligned (&arp0->ip4_over_ethernet[0].ip4.data_u32, u32) =
1119 /* Hardware must be ethernet-like. */
1120 ASSERT (vec_len (hw_if0->hw_address) == 6);
1122 clib_memcpy (eth0->dst_address, eth0->src_address, 6);
1123 clib_memcpy (eth0->src_address, hw_if0->hw_address, 6);
1125 /* Figure out how much to rewind current data from adjacency. */
1126 /* get the adj from the destination's covering connected */
1130 adj_get (fib_entry_get_adj_for_source
1131 (ip4_fib_table_lookup
1132 (ip4_fib_get (fib_index0),
1133 &arp0->ip4_over_ethernet[1].ip4, 31),
1134 FIB_SOURCE_INTERFACE));
1135 if (adj0->lookup_next_index != IP_LOOKUP_NEXT_GLEAN)
1137 error0 = ETHERNET_ARP_ERROR_missing_interface_address;
1142 if (!arp_unnumbered (p0, pi0, eth0, conn_sw_if_index0))
1146 vlib_buffer_advance (p0, -adj0->rewrite_header.data_bytes);
1148 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1149 n_left_to_next, pi0, next0);
1151 n_replies_sent += 1;
1155 if (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ||
1156 (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
1157 arp0->ip4_over_ethernet[1].ip4.as_u32))
1159 error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
1162 /* See if proxy arp is configured for the address */
1165 vnet_sw_interface_t *si;
1166 u32 this_addr = clib_net_to_host_u32
1167 (arp0->ip4_over_ethernet[1].ip4.as_u32);
1170 si = vnet_get_sw_interface (vnm, sw_if_index0);
1172 if (!(si->flags & VNET_SW_INTERFACE_FLAG_PROXY_ARP))
1175 fib_index0 = vec_elt (im4->fib_index_by_sw_if_index,
1178 vec_foreach (pa, am->proxy_arps)
1180 u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr);
1181 u32 hi_addr = clib_net_to_host_u32 (pa->hi_addr);
1183 /* an ARP request hit in the proxy-arp table? */
1184 if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
1185 (fib_index0 == pa->fib_index))
1187 eth0 = ethernet_buffer_get_header (p0);
1189 arp0->ip4_over_ethernet[1].ip4.data_u32;
1192 * Rewind buffer, direct code above not to
1193 * think too hard about it.
1195 if_addr0 = &proxy_src;
1197 i32 ethernet_start =
1198 vnet_buffer (p0)->ethernet.start_of_ethernet_header;
1199 i32 rewind = p0->current_data - ethernet_start;
1200 vlib_buffer_advance (p0, -rewind);
1201 n_proxy_arp_replies_sent++;
1209 next0 = ARP_INPUT_NEXT_DROP;
1210 p0->error = node->errors[error0];
1212 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
1213 n_left_to_next, pi0, next0);
1216 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1219 vlib_error_count (vm, node->node_index,
1220 ETHERNET_ARP_ERROR_replies_sent,
1221 n_replies_sent - n_proxy_arp_replies_sent);
1223 vlib_error_count (vm, node->node_index,
1224 ETHERNET_ARP_ERROR_proxy_arp_replies_sent,
1225 n_proxy_arp_replies_sent);
1226 return frame->n_vectors;
1229 static char *ethernet_arp_error_strings[] = {
1230 #define _(sym,string) string,
1231 foreach_ethernet_arp_error
1236 VLIB_REGISTER_NODE (arp_input_node, static) =
1238 .function = arp_input,
1239 .name = "arp-input",
1240 .vector_size = sizeof (u32),
1241 .n_errors = ETHERNET_ARP_N_ERROR,
1242 .error_strings = ethernet_arp_error_strings,
1243 .n_next_nodes = ARP_INPUT_N_NEXT,
1245 [ARP_INPUT_NEXT_DROP] = "error-drop",
1246 [ARP_INPUT_NEXT_REPLY_TX] = "interface-output",
1248 .format_buffer = format_ethernet_arp_header,
1249 .format_trace = format_ethernet_arp_input_trace,
1254 ip4_arp_entry_sort (void *a1, void *a2)
1256 ethernet_arp_ip4_entry_t *e1 = a1;
1257 ethernet_arp_ip4_entry_t *e2 = a2;
1260 vnet_main_t *vnm = vnet_get_main ();
1262 cmp = vnet_sw_interface_compare (vnm, e1->sw_if_index, e2->sw_if_index);
1264 cmp = ip4_address_compare (&e1->ip4_address, &e2->ip4_address);
1268 ethernet_arp_ip4_entry_t *
1269 ip4_neighbor_entries (u32 sw_if_index)
1271 ethernet_arp_main_t *am = ðernet_arp_main;
1272 ethernet_arp_ip4_entry_t *n, *ns = 0;
1275 pool_foreach (n, am->ip4_entry_pool, ({
1276 if (sw_if_index != ~0 && n->sw_if_index != sw_if_index)
1278 vec_add1 (ns, n[0]);
1283 vec_sort_with_function (ns, ip4_arp_entry_sort);
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);
1302 es = ip4_neighbor_entries (sw_if_index);
1305 vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, 0);
1308 vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, e);
1313 if (vec_len (am->proxy_arps))
1315 vlib_cli_output (vm, "Proxy arps enabled for:");
1316 vec_foreach (pa, am->proxy_arps)
1318 vlib_cli_output (vm, "Fib_index %d %U - %U ",
1320 format_ip4_address, &pa->lo_addr,
1321 format_ip4_address, &pa->hi_addr);
1329 * Display all the IPv4 ARP entries.
1332 * Example of how to display the IPv4 ARP table:
1333 * @cliexstart{show ip arp}
1334 * Time FIB IP4 Flags Ethernet Interface
1335 * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
1336 * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
1337 * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
1338 * Proxy arps enabled for:
1339 * Fib_index 0 6.0.0.1 - 6.0.0.11
1343 VLIB_CLI_COMMAND (show_ip4_arp_command, static) = {
1344 .path = "show ip arp",
1345 .function = show_ip4_arp,
1346 .short_help = "show ip arp",
1352 pg_edit_t l2_type, l3_type;
1353 pg_edit_t n_l2_address_bytes, n_l3_address_bytes;
1359 } ip4_over_ethernet[2];
1360 } pg_ethernet_arp_header_t;
1363 pg_ethernet_arp_header_init (pg_ethernet_arp_header_t * p)
1365 /* Initialize fields that are not bit fields in the IP header. */
1366 #define _(f) pg_edit_init (&p->f, ethernet_arp_header_t, f);
1369 _(n_l2_address_bytes);
1370 _(n_l3_address_bytes);
1372 _(ip4_over_ethernet[0].ethernet);
1373 _(ip4_over_ethernet[0].ip4);
1374 _(ip4_over_ethernet[1].ethernet);
1375 _(ip4_over_ethernet[1].ip4);
1380 unformat_pg_arp_header (unformat_input_t * input, va_list * args)
1382 pg_stream_t *s = va_arg (*args, pg_stream_t *);
1383 pg_ethernet_arp_header_t *p;
1386 p = pg_create_edit_group (s, sizeof (p[0]), sizeof (ethernet_arp_header_t),
1388 pg_ethernet_arp_header_init (p);
1391 pg_edit_set_fixed (&p->l2_type, ETHERNET_ARP_HARDWARE_TYPE_ethernet);
1392 pg_edit_set_fixed (&p->l3_type, ETHERNET_TYPE_IP4);
1393 pg_edit_set_fixed (&p->n_l2_address_bytes, 6);
1394 pg_edit_set_fixed (&p->n_l3_address_bytes, 4);
1396 if (!unformat (input, "%U: %U/%U -> %U/%U",
1398 unformat_ethernet_arp_opcode_net_byte_order, &p->opcode,
1400 unformat_ethernet_address, &p->ip4_over_ethernet[0].ethernet,
1402 unformat_ip4_address, &p->ip4_over_ethernet[0].ip4,
1404 unformat_ethernet_address, &p->ip4_over_ethernet[1].ethernet,
1406 unformat_ip4_address, &p->ip4_over_ethernet[1].ip4))
1408 /* Free up any edits we may have added. */
1409 pg_free_edit_group (s);
1416 ip4_set_arp_limit (u32 arp_limit)
1418 ethernet_arp_main_t *am = ðernet_arp_main;
1420 am->limit_arp_cache_size = arp_limit;
1425 * @brief Control Plane hook to remove an ARP entry
1428 vnet_arp_unset_ip4_over_ethernet (vnet_main_t * vnm,
1429 u32 sw_if_index, void *a_arg)
1431 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1432 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1434 args.sw_if_index = sw_if_index;
1435 args.flags = ETHERNET_ARP_ARGS_REMOVE;
1436 clib_memcpy (&args.a, a, sizeof (*a));
1438 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1439 (u8 *) & args, sizeof (args));
1444 * @brief Internally generated event to flush the ARP cache on an
1445 * interface state change event.
1446 * A flush will remove dynamic ARP entries, and for statics remove the MAC
1447 * address from the corresponding adjacencies.
1450 vnet_arp_flush_ip4_over_ethernet (vnet_main_t * vnm,
1451 u32 sw_if_index, void *a_arg)
1453 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1454 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1456 args.sw_if_index = sw_if_index;
1457 args.flags = ETHERNET_ARP_ARGS_FLUSH;
1458 clib_memcpy (&args.a, a, sizeof (*a));
1460 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1461 (u8 *) & args, sizeof (args));
1466 * @brief Internally generated event to populate the ARP cache on an
1467 * interface state change event.
1468 * For static entries this will re-source the adjacencies.
1470 * @param sw_if_index The interface on which the ARP entires are acted
1473 vnet_arp_populate_ip4_over_ethernet (vnet_main_t * vnm,
1474 u32 sw_if_index, void *a_arg)
1476 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1477 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1479 args.sw_if_index = sw_if_index;
1480 args.flags = ETHERNET_ARP_ARGS_POPULATE;
1481 clib_memcpy (&args.a, a, sizeof (*a));
1483 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1484 (u8 *) & args, sizeof (args));
1489 * arp_add_del_interface_address
1491 * callback when an interface address is added or deleted
1494 arp_add_del_interface_address (ip4_main_t * im,
1497 ip4_address_t * address,
1499 u32 if_address_index, u32 is_del)
1502 * Flush the ARP cache of all entries covered by the address
1503 * that is being removed.
1505 ethernet_arp_main_t *am = ðernet_arp_main;
1506 ethernet_arp_ip4_entry_t *e;
1508 if (vec_len (am->ethernet_arp_by_sw_if_index) <= sw_if_index)
1513 ethernet_arp_interface_t *eai;
1514 u32 i, *to_delete = 0;
1517 eai = &am->ethernet_arp_by_sw_if_index[sw_if_index];
1520 hash_foreach_pair (pair, eai->arp_entries,
1522 e = pool_elt_at_index(am->ip4_entry_pool,
1524 if (ip4_destination_matches_route (im, &e->ip4_address,
1525 address, address_length))
1527 vec_add1 (to_delete, e - am->ip4_entry_pool);
1532 for (i = 0; i < vec_len (to_delete); i++)
1534 ethernet_arp_ip4_over_ethernet_address_t delme;
1535 e = pool_elt_at_index (am->ip4_entry_pool, to_delete[i]);
1537 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
1538 delme.ip4.as_u32 = e->ip4_address.as_u32;
1540 vnet_arp_flush_ip4_over_ethernet (vnet_get_main (),
1541 e->sw_if_index, &delme);
1544 vec_free (to_delete);
1548 static clib_error_t *
1549 ethernet_arp_init (vlib_main_t * vm)
1551 ethernet_arp_main_t *am = ðernet_arp_main;
1552 ip4_main_t *im = &ip4_main;
1553 clib_error_t *error;
1556 if ((error = vlib_call_init_function (vm, ethernet_init)))
1559 ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, arp_input_node.index);
1561 pn = pg_get_node (arp_input_node.index);
1562 pn->unformat_edit = unformat_pg_arp_header;
1564 am->opcode_by_name = hash_create_string (0, sizeof (uword));
1565 #define _(o) hash_set_mem (am->opcode_by_name, #o, ETHERNET_ARP_OPCODE_##o);
1566 foreach_ethernet_arp_opcode;
1569 /* $$$ configurable */
1570 am->limit_arp_cache_size = 50000;
1572 am->pending_resolutions_by_address = hash_create (0, sizeof (uword));
1573 am->mac_changes_by_address = hash_create (0, sizeof (uword));
1575 /* don't trace ARP error packets */
1577 vlib_node_runtime_t *rt =
1578 vlib_node_get_runtime (vm, arp_input_node.index);
1581 vnet_pcap_drop_trace_filter_add_del \
1582 (rt->errors[ETHERNET_ARP_ERROR_##a], \
1584 foreach_ethernet_arp_error
1588 ip4_add_del_interface_address_callback_t cb;
1589 cb.function = arp_add_del_interface_address;
1590 cb.function_opaque = 0;
1591 vec_add1 (im->add_del_interface_address_callbacks, cb);
1596 VLIB_INIT_FUNCTION (ethernet_arp_init);
1599 arp_entry_free (ethernet_arp_interface_t * eai, ethernet_arp_ip4_entry_t * e)
1601 ethernet_arp_main_t *am = ðernet_arp_main;
1603 fib_table_entry_delete_index (e->fib_entry_index, FIB_SOURCE_ADJ);
1604 hash_unset (eai->arp_entries, e->ip4_address.as_u32);
1605 pool_put (am->ip4_entry_pool, e);
1609 vnet_arp_unset_ip4_over_ethernet_internal (vnet_main_t * vnm,
1610 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1613 ethernet_arp_main_t *am = ðernet_arp_main;
1614 ethernet_arp_ip4_entry_t *e;
1615 ethernet_arp_interface_t *eai;
1617 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1619 e = arp_entry_find (eai, &args->a.ip4);
1623 adj_nbr_walk_nh4 (e->sw_if_index,
1624 &e->ip4_address, arp_mk_incomplete_walk, e);
1625 arp_entry_free (eai, e);
1632 vnet_arp_flush_ip4_over_ethernet_internal (vnet_main_t * vnm,
1633 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1636 ethernet_arp_main_t *am = ðernet_arp_main;
1637 ethernet_arp_ip4_entry_t *e;
1638 ethernet_arp_interface_t *eai;
1640 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1642 e = arp_entry_find (eai, &args->a.ip4);
1646 adj_nbr_walk_nh4 (e->sw_if_index,
1647 &e->ip4_address, arp_mk_incomplete_walk, e);
1650 * The difference between flush and unset, is that an unset
1651 * means delete for static and dynamic entries. A flush
1652 * means delete only for dynamic. Flushing is what the DP
1653 * does in response to interface events. unset is only done
1654 * by the control plane.
1656 if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC)
1658 arp_entry_free (eai, e);
1665 vnet_arp_populate_ip4_over_ethernet_internal (vnet_main_t * vnm,
1666 vnet_arp_set_ip4_over_ethernet_rpc_args_t
1669 ethernet_arp_main_t *am = ðernet_arp_main;
1670 ethernet_arp_ip4_entry_t *e;
1671 ethernet_arp_interface_t *eai;
1673 eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
1675 e = arp_entry_find (eai, &args->a.ip4);
1679 adj_nbr_walk_nh4 (e->sw_if_index,
1680 &e->ip4_address, arp_mk_complete_walk, e);
1686 set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t
1689 vnet_main_t *vm = vnet_get_main ();
1690 ASSERT (os_get_cpu_number () == 0);
1692 if (a->flags & ETHERNET_ARP_ARGS_REMOVE)
1693 vnet_arp_unset_ip4_over_ethernet_internal (vm, a);
1694 else if (a->flags & ETHERNET_ARP_ARGS_FLUSH)
1695 vnet_arp_flush_ip4_over_ethernet_internal (vm, a);
1696 else if (a->flags & ETHERNET_ARP_ARGS_POPULATE)
1697 vnet_arp_populate_ip4_over_ethernet_internal (vm, a);
1699 vnet_arp_set_ip4_over_ethernet_internal (vm, a);
1703 * @brief Invoked when the interface's admin state changes
1705 static clib_error_t *
1706 ethernet_arp_sw_interface_up_down (vnet_main_t * vnm,
1707 u32 sw_if_index, u32 flags)
1709 ethernet_arp_main_t *am = ðernet_arp_main;
1710 ethernet_arp_ip4_entry_t *e;
1711 u32 i, *to_delete = 0;
1714 pool_foreach (e, am->ip4_entry_pool,
1716 if (e->sw_if_index == sw_if_index)
1717 vec_add1 (to_delete,
1718 e - am->ip4_entry_pool);
1722 for (i = 0; i < vec_len (to_delete); i++)
1724 ethernet_arp_ip4_over_ethernet_address_t delme;
1725 e = pool_elt_at_index (am->ip4_entry_pool, to_delete[i]);
1727 clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
1728 delme.ip4.as_u32 = e->ip4_address.as_u32;
1730 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
1732 vnet_arp_populate_ip4_over_ethernet (vnm, e->sw_if_index, &delme);
1736 vnet_arp_flush_ip4_over_ethernet (vnm, e->sw_if_index, &delme);
1740 vec_free (to_delete);
1745 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ethernet_arp_sw_interface_up_down);
1748 increment_ip4_and_mac_address (ethernet_arp_ip4_over_ethernet_address_t * a)
1753 for (i = 3; i >= 0; i--)
1755 old = a->ip4.as_u8[i];
1756 a->ip4.as_u8[i] += 1;
1757 if (old < a->ip4.as_u8[i])
1761 for (i = 5; i >= 0; i--)
1763 old = a->ethernet[i];
1764 a->ethernet[i] += 1;
1765 if (old < a->ethernet[i])
1771 vnet_arp_set_ip4_over_ethernet (vnet_main_t * vnm,
1772 u32 sw_if_index, void *a_arg, int is_static)
1774 ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
1775 vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
1777 args.sw_if_index = sw_if_index;
1778 args.is_static = is_static;
1780 clib_memcpy (&args.a, a, sizeof (*a));
1782 vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
1783 (u8 *) & args, sizeof (args));
1788 vnet_proxy_arp_add_del (ip4_address_t * lo_addr,
1789 ip4_address_t * hi_addr, u32 fib_index, int is_del)
1791 ethernet_arp_main_t *am = ðernet_arp_main;
1792 ethernet_proxy_arp_t *pa;
1793 u32 found_at_index = ~0;
1795 vec_foreach (pa, am->proxy_arps)
1797 if (pa->lo_addr == lo_addr->as_u32
1798 && pa->hi_addr == hi_addr->as_u32 && pa->fib_index == fib_index)
1800 found_at_index = pa - am->proxy_arps;
1805 if (found_at_index != ~0)
1807 /* Delete, otherwise it's already in the table */
1809 vec_delete (am->proxy_arps, 1, found_at_index);
1812 /* delete, no such entry */
1814 return VNET_API_ERROR_NO_SUCH_ENTRY;
1816 /* add, not in table */
1817 vec_add2 (am->proxy_arps, pa, 1);
1818 pa->lo_addr = lo_addr->as_u32;
1819 pa->hi_addr = hi_addr->as_u32;
1820 pa->fib_index = fib_index;
1825 * Remove any proxy arp entries asdociated with the
1829 vnet_proxy_arp_fib_reset (u32 fib_id)
1831 ip4_main_t *im = &ip4_main;
1832 ethernet_arp_main_t *am = ðernet_arp_main;
1833 ethernet_proxy_arp_t *pa;
1834 u32 *entries_to_delete = 0;
1839 p = hash_get (im->fib_index_by_table_id, fib_id);
1841 return VNET_API_ERROR_NO_SUCH_ENTRY;
1844 vec_foreach (pa, am->proxy_arps)
1846 if (pa->fib_index == fib_index)
1848 vec_add1 (entries_to_delete, pa - am->proxy_arps);
1852 for (i = 0; i < vec_len (entries_to_delete); i++)
1854 vec_delete (am->proxy_arps, 1, entries_to_delete[i]);
1857 vec_free (entries_to_delete);
1862 static clib_error_t *
1863 ip_arp_add_del_command_fn (vlib_main_t * vm,
1864 unformat_input_t * input, vlib_cli_command_t * cmd)
1866 vnet_main_t *vnm = vnet_get_main ();
1868 ethernet_arp_ip4_over_ethernet_address_t lo_addr, hi_addr, addr;
1877 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1879 /* set ip arp TenGigE1/1/0/1 1.2.3.4 aa:bb:... or aabb.ccdd... */
1880 if (unformat (input, "%U %U %U",
1881 unformat_vnet_sw_interface, vnm, &sw_if_index,
1882 unformat_ip4_address, &addr.ip4,
1883 unformat_ethernet_address, &addr.ethernet))
1886 else if (unformat (input, "delete") || unformat (input, "del"))
1889 else if (unformat (input, "static"))
1892 else if (unformat (input, "count %d", &count))
1895 else if (unformat (input, "fib-id %d", &fib_id))
1897 ip4_main_t *im = &ip4_main;
1898 uword *p = hash_get (im->fib_index_by_table_id, fib_id);
1900 return clib_error_return (0, "fib ID %d doesn't exist\n", fib_id);
1904 else if (unformat (input, "proxy %U - %U",
1905 unformat_ip4_address, &lo_addr.ip4,
1906 unformat_ip4_address, &hi_addr.ip4))
1914 (void) vnet_proxy_arp_add_del (&lo_addr.ip4, &hi_addr.ip4,
1923 for (i = 0; i < count; i++)
1927 uword event_type, *event_data = 0;
1929 /* Park the debug CLI until the arp entry is installed */
1930 vnet_register_ip4_arp_resolution_event
1931 (vnm, &addr.ip4, vlib_current_process (vm),
1932 1 /* type */ , 0 /* data */ );
1934 vnet_arp_set_ip4_over_ethernet
1935 (vnm, sw_if_index, &addr, is_static);
1937 vlib_process_wait_for_event (vm);
1938 event_type = vlib_process_get_events (vm, &event_data);
1939 vec_reset_length (event_data);
1940 if (event_type != 1)
1941 clib_warning ("event type %d unexpected", event_type);
1944 vnet_arp_unset_ip4_over_ethernet (vnm, sw_if_index, &addr);
1946 increment_ip4_and_mac_address (&addr);
1951 return clib_error_return (0, "unknown input `%U'",
1952 format_unformat_error, input);
1960 * Add or delete IPv4 ARP cache entries.
1962 * @note 'set ip arp' options (e.g. delete, static, 'fib-id <id>',
1963 * 'count <number>', 'interface ip4_addr mac_addr') can be added in
1964 * any order and combination.
1968 * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
1969 * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
1970 * @cliexcmd{set ip arp GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1971 * @cliexcmd{set ip arp delete GigabitEthernet2/0/0 6.0.0.3 de:ad:be:ef:ba:be}
1973 * To add or delete an IPv4 ARP cache entry to or from a specific fib
1975 * @cliexcmd{set ip arp fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1976 * @cliexcmd{set ip arp fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1978 * Add or delete IPv4 static ARP cache entries as follows:
1979 * @cliexcmd{set ip arp static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1980 * @cliexcmd{set ip arp static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1982 * For testing / debugging purposes, the 'set ip arp' command can add or
1983 * delete multiple entries. Supply the 'count N' parameter:
1984 * @cliexcmd{set ip arp count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
1987 VLIB_CLI_COMMAND (ip_arp_add_del_command, static) = {
1988 .path = "set ip arp",
1990 "set ip arp [del] <intfc> <ip-address> <mac-address> [static] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
1991 .function = ip_arp_add_del_command_fn,
1995 static clib_error_t *
1996 set_int_proxy_arp_command_fn (vlib_main_t * vm,
1998 input, vlib_cli_command_t * cmd)
2000 vnet_main_t *vnm = vnet_get_main ();
2002 vnet_sw_interface_t *si;
2006 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2008 if (unformat (input, "%U", unformat_vnet_sw_interface,
2011 else if (unformat (input, "enable") || unformat (input, "on"))
2013 else if (unformat (input, "disable") || unformat (input, "off"))
2020 return clib_error_return (0, "unknown input '%U'",
2021 format_unformat_error, input);
2023 si = vnet_get_sw_interface (vnm, sw_if_index);
2026 si->flags |= VNET_SW_INTERFACE_FLAG_PROXY_ARP;
2028 si->flags &= ~VNET_SW_INTERFACE_FLAG_PROXY_ARP;
2035 * Enable proxy-arp on an interface. The vpp stack will answer ARP
2036 * requests for the indicated address range. Multiple proxy-arp
2037 * ranges may be provisioned.
2039 * @note Proxy ARP as a technology is infamous for blackholing traffic.
2040 * Also, the underlying implementation has not been performance-tuned.
2041 * Avoid creating an unnecessarily large set of ranges.
2044 * To enable proxy arp on a range of addresses, use:
2045 * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11}
2046 * Append 'del' to delete a range of proxy ARP addresses:
2047 * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11 del}
2048 * You must then specifically enable proxy arp on individual interfaces:
2049 * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 enable}
2050 * To disable proxy arp on an individual interface:
2051 * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 disable}
2053 VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = {
2054 .path = "set interface proxy-arp",
2056 "set interface proxy-arp <intfc> [enable|disable]",
2057 .function = set_int_proxy_arp_command_fn,
2063 * ARP/ND Termination in a L2 Bridge Domain based on IP4/IP6 to MAC
2064 * hash tables mac_by_ip4 and mac_by_ip6 for each BD.
2068 ARP_TERM_NEXT_L2_OUTPUT,
2073 u32 arp_term_next_node_index[32];
2076 arp_term_l2bd (vlib_main_t * vm,
2077 vlib_node_runtime_t * node, vlib_frame_t * frame)
2079 l2input_main_t *l2im = &l2input_main;
2080 u32 n_left_from, next_index, *from, *to_next;
2081 u32 n_replies_sent = 0;
2082 u16 last_bd_index = ~0;
2083 l2_bridge_domain_t *last_bd_config = 0;
2084 l2_input_config_t *cfg0;
2086 from = vlib_frame_vector_args (frame);
2087 n_left_from = frame->n_vectors;
2088 next_index = node->cached_next_index;
2090 while (n_left_from > 0)
2094 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2096 while (n_left_from > 0 && n_left_to_next > 0)
2099 ethernet_header_t *eth0;
2100 ethernet_arp_header_t *arp0;
2103 u32 pi0, error0, next0, sw_if_index0;
2114 n_left_to_next -= 1;
2116 p0 = vlib_get_buffer (vm, pi0);
2117 eth0 = vlib_buffer_get_current (p0);
2118 l3h0 = (u8 *) eth0 + vnet_buffer (p0)->l2.l2_len;
2119 ethertype0 = clib_net_to_host_u16 (*(u16 *) (l3h0 - 2));
2120 arp0 = (ethernet_arp_header_t *) l3h0;
2122 if (PREDICT_FALSE ((ethertype0 != ETHERNET_TYPE_ARP) ||
2124 clib_host_to_net_u16
2125 (ETHERNET_ARP_OPCODE_request))))
2128 /* Must be ARP request packet here */
2129 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
2130 (p0->flags & VLIB_BUFFER_IS_TRACED)))
2132 u8 *t0 = vlib_add_trace (vm, node, p0,
2133 sizeof (ethernet_arp_input_trace_t));
2134 clib_memcpy (t0, l3h0, sizeof (ethernet_arp_input_trace_t));
2137 error0 = ETHERNET_ARP_ERROR_replies_sent;
2140 clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet)
2141 ? ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0);
2144 clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
2145 ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0);
2147 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
2152 /* Trash ARP packets whose ARP-level source addresses do not
2153 match their L2-frame-level source addresses */
2156 (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
2157 sizeof (eth0->src_address))))
2159 error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
2163 /* Check if anyone want ARP request events for L2 BDs */
2165 pending_resolution_t *mc;
2166 ethernet_arp_main_t *am = ðernet_arp_main;
2167 uword *p = hash_get (am->mac_changes_by_address, 0);
2168 if (p && (vnet_buffer (p0)->l2.shg == 0))
2169 { // Only SHG 0 interface which is more likely local
2170 u32 next_index = p[0];
2171 while (next_index != (u32) ~ 0)
2173 int (*fp) (u32, u8 *, u32, u32);
2175 mc = pool_elt_at_index (am->mac_changes, next_index);
2176 fp = mc->data_callback;
2177 /* Call the callback, return 1 to suppress dup events */
2179 rv = (*fp) (mc->data,
2180 arp0->ip4_over_ethernet[0].ethernet,
2182 arp0->ip4_over_ethernet[0].ip4.as_u32);
2183 /* Signal the resolver process */
2185 vlib_process_signal_event (vm, mc->node_index,
2186 mc->type_opaque, mc->data);
2187 next_index = mc->next_index;
2192 /* lookup BD mac_by_ip4 hash table for MAC entry */
2193 ip0 = arp0->ip4_over_ethernet[1].ip4.as_u32;
2194 bd_index0 = vnet_buffer (p0)->l2.bd_index;
2195 if (PREDICT_FALSE ((bd_index0 != last_bd_index)
2196 || (last_bd_index == (u16) ~ 0)))
2198 last_bd_index = bd_index0;
2199 last_bd_config = vec_elt_at_index (l2im->bd_configs, bd_index0);
2201 macp0 = (u8 *) hash_get (last_bd_config->mac_by_ip4, ip0);
2203 if (PREDICT_FALSE (!macp0))
2204 goto next_l2_feature; /* MAC not found */
2206 /* MAC found, send ARP reply -
2207 Convert ARP request packet to ARP reply */
2208 arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
2209 arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
2210 arp0->ip4_over_ethernet[0].ip4.as_u32 = ip0;
2211 clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, macp0, 6);
2212 clib_memcpy (eth0->dst_address, eth0->src_address, 6);
2213 clib_memcpy (eth0->src_address, macp0, 6);
2214 n_replies_sent += 1;
2217 /* For BVI, need to use l2-fwd node to send ARP reply as
2218 l2-output node cannot output packet to BVI properly */
2219 cfg0 = vec_elt_at_index (l2im->configs, sw_if_index0);
2220 if (PREDICT_FALSE (cfg0->bvi))
2222 vnet_buffer (p0)->l2.feature_bitmap |= L2INPUT_FEAT_FWD;
2223 vnet_buffer (p0)->sw_if_index[VLIB_RX] = 0;
2224 goto next_l2_feature;
2227 /* Send ARP/ND reply back out input interface through l2-output */
2228 vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
2229 next0 = ARP_TERM_NEXT_L2_OUTPUT;
2230 /* Note that output to VXLAN tunnel will fail due to SHG which
2231 is probably desireable since ARP termination is not intended
2232 for ARP requests from other hosts. If output to VXLAN tunnel is
2233 required, however, can just clear the SHG in packet as follows:
2234 vnet_buffer(p0)->l2.shg = 0; */
2235 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2236 to_next, n_left_to_next, pi0,
2241 /* IP6 ND event notification or solicitation handling to generate
2242 local response instead of flooding */
2243 iph0 = (ip6_header_t *) l3h0;
2244 if (PREDICT_FALSE (ethertype0 == ETHERNET_TYPE_IP6 &&
2245 iph0->protocol == IP_PROTOCOL_ICMP6 &&
2246 !ip6_address_is_unspecified
2247 (&iph0->src_address)))
2249 sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
2250 if (vnet_ip6_nd_term
2251 (vm, node, p0, eth0, iph0, sw_if_index0,
2252 vnet_buffer (p0)->l2.bd_index, vnet_buffer (p0)->l2.shg))
2253 goto output_response;
2258 u32 feature_bitmap0 =
2259 vnet_buffer (p0)->l2.feature_bitmap & ~L2INPUT_FEAT_ARP_TERM;
2260 vnet_buffer (p0)->l2.feature_bitmap = feature_bitmap0;
2262 feat_bitmap_get_next_node_index (arp_term_next_node_index,
2264 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2265 to_next, n_left_to_next,
2271 if (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ||
2272 (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
2273 arp0->ip4_over_ethernet[1].ip4.as_u32))
2275 error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
2277 next0 = ARP_TERM_NEXT_DROP;
2278 p0->error = node->errors[error0];
2280 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2281 to_next, n_left_to_next, pi0,
2285 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2288 vlib_error_count (vm, node->node_index,
2289 ETHERNET_ARP_ERROR_replies_sent, n_replies_sent);
2290 return frame->n_vectors;
2294 VLIB_REGISTER_NODE (arp_term_l2bd_node, static) = {
2295 .function = arp_term_l2bd,
2296 .name = "arp-term-l2bd",
2297 .vector_size = sizeof (u32),
2298 .n_errors = ETHERNET_ARP_N_ERROR,
2299 .error_strings = ethernet_arp_error_strings,
2300 .n_next_nodes = ARP_TERM_N_NEXT,
2302 [ARP_TERM_NEXT_L2_OUTPUT] = "l2-output",
2303 [ARP_TERM_NEXT_DROP] = "error-drop",
2305 .format_buffer = format_ethernet_arp_header,
2306 .format_trace = format_arp_term_input_trace,
2311 arp_term_init (vlib_main_t * vm)
2313 // Initialize the feature next-node indexes
2314 feat_bitmap_init_next_nodes (vm,
2315 arp_term_l2bd_node.index,
2317 l2input_get_feat_names (),
2318 arp_term_next_node_index);
2322 VLIB_INIT_FUNCTION (arp_term_init);
2325 change_arp_mac (u32 sw_if_index, ethernet_arp_ip4_entry_t * e)
2327 if (e->sw_if_index == sw_if_index)
2329 adj_nbr_walk_nh4 (e->sw_if_index,
2330 &e->ip4_address, arp_mk_complete_walk, e);
2335 ethernet_arp_change_mac (vnet_main_t * vnm, u32 sw_if_index)
2337 ethernet_arp_main_t *am = ðernet_arp_main;
2338 ethernet_arp_ip4_entry_t *e;
2341 pool_foreach (e, am->ip4_entry_pool,
2343 change_arp_mac (sw_if_index, e);
2349 * fd.io coding-style-patch-verification: ON
2352 * eval: (c-set-style "gnu")