summaryrefslogtreecommitdiffstats
path: root/src/vnet/arp
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2019-09-30 10:53:31 +0000
committerOle Trøan <otroan@employees.org>2019-12-17 10:56:20 +0000
commitcbe25aab3be72154f2c706c39eeba6a77f34450f (patch)
tree131fb53b5ec973be045ffb9e2eb797af01d112a0 /src/vnet/arp
parent96453fd2417ebd1d69354a7fb692976129cea80e (diff)
ip: Protocol Independent IP Neighbors
Type: feature - ip-neighbour: generic neighbour handling; APIs, DBs, event handling, aging - arp: ARP protocol implementation - ip6-nd; IPv6 neighbor discovery implementation; separate ND, MLD, RA - ip6-link; manage link-local addresses - l2-arp-term; events separated from IP neighbours, since they are not the same. vnet retains just enough education to perform ND/ARP packet construction. arp and ip6-nd to be moved to plugins soon. Change-Id: I88dedd0006b299344f4c7024a0aa5baa6b9a8bbe Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'src/vnet/arp')
-rw-r--r--src/vnet/arp/arp.api100
-rw-r--r--src/vnet/arp/arp.c934
-rw-r--r--src/vnet/arp/arp.h85
-rw-r--r--src/vnet/arp/arp_api.c190
-rw-r--r--src/vnet/arp/arp_packet.h91
-rw-r--r--src/vnet/arp/arp_proxy.c407
-rw-r--r--src/vnet/arp/arp_test.c168
7 files changed, 1975 insertions, 0 deletions
diff --git a/src/vnet/arp/arp.api b/src/vnet/arp/arp.api
new file mode 100644
index 00000000000..27bfa3b65c6
--- /dev/null
+++ b/src/vnet/arp/arp.api
@@ -0,0 +1,100 @@
+/* Hey Emacs use -*- mode: C -*- */
+/*
+ * Copyright (c) 2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** \file
+ This file defines the vpp control-plane API messages
+ used to control the ABF plugin
+*/
+
+option version = "1.0.0";
+
+import "vnet/ip/ip_types.api";
+import "vnet/ethernet/ethernet_types.api";
+import "vnet/interface_types.api";
+
+/** \brief Proxy ARP configuration type
+ @param table_id - VRF / Fib table ID
+ @param low - Low address of the Proxy ARP range
+ @param hi - High address of the Proxy ARP range
+*/
+typedef proxy_arp
+{
+ u32 table_id;
+ vl_api_ip4_address_t low;
+ vl_api_ip4_address_t hi;
+};
+
+/** \brief Proxy ARP add / del request
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param is_add - 1 if adding the Proxy ARP range, 0 if deleting
+ @param proxy - Proxy configuration
+*/
+autoreply define proxy_arp_add_del
+{
+ u32 client_index;
+ u32 context;
+ bool is_add;
+ vl_api_proxy_arp_t proxy;
+};
+
+/** \brief Proxy ARP dump request
+ */
+define proxy_arp_dump
+{
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Proxy ARP dump details reply
+ * @param proxy - Same data as used to configure
+ */
+define proxy_arp_details
+{
+ u32 context;
+ vl_api_proxy_arp_t proxy;
+};
+
+/** \brief Proxy ARP add / del interface request
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param sw_if_index - Which interface to enable / disable Proxy Arp on
+ @param enable - 1 to enable Proxy ARP on interface, 0 to disable
+*/
+autoreply define proxy_arp_intfc_enable_disable
+{
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t sw_if_index;
+ bool enable;
+};
+
+/** \brief Proxy ARP interface dump request
+ */
+define proxy_arp_intfc_dump
+{
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Proxy ARP interface dump details reply
+ * @param sw_if_index The interface on which ARP proxy is enabled.
+ */
+define proxy_arp_intfc_details
+{
+ u32 context;
+ u32 sw_if_index;
+};
diff --git a/src/vnet/arp/arp.c b/src/vnet/arp/arp.c
new file mode 100644
index 00000000000..14a1ae97d1e
--- /dev/null
+++ b/src/vnet/arp/arp.c
@@ -0,0 +1,934 @@
+/*
+ * ethernet/arp.c: IP v4 ARP node
+ *
+ * Copyright (c) 2010 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/arp/arp.h>
+#include <vnet/arp/arp_packet.h>
+
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/fib_entry_src.h>
+#include <vnet/adj/adj_nbr.h>
+#include <vnet/adj/adj_mcast.h>
+
+#include <vnet/ip-neighbor/ip_neighbor.h>
+#include <vnet/ip-neighbor/ip_neighbor_dp.h>
+
+#include <vlibmemory/api.h>
+
+/**
+ * @file
+ * @brief IPv4 ARP.
+ *
+ * This file contains code to manage the IPv4 ARP tables (IP Address
+ * to MAC Address lookup).
+ */
+
+/**
+ * @brief Per-interface ARP configuration and state
+ */
+typedef struct ethernet_arp_interface_t_
+{
+ /**
+ * Is ARP enabled on this interface
+ */
+ u32 enabled;
+} ethernet_arp_interface_t;
+
+typedef struct
+{
+ /* Hash tables mapping name to opcode. */
+ uword *opcode_by_name;
+
+ /** Per interface state */
+ ethernet_arp_interface_t *ethernet_arp_by_sw_if_index;
+
+ /* ARP feature arc index */
+ u8 feature_arc_index;
+} ethernet_arp_main_t;
+
+static ethernet_arp_main_t ethernet_arp_main;
+
+static const u8 vrrp_prefix[] = { 0x00, 0x00, 0x5E, 0x00, 0x01 };
+
+static uword
+unformat_ethernet_arp_opcode_host_byte_order (unformat_input_t * input,
+ va_list * args)
+{
+ int *result = va_arg (*args, int *);
+ ethernet_arp_main_t *am = &ethernet_arp_main;
+ int x, i;
+
+ /* Numeric opcode. */
+ if (unformat (input, "0x%x", &x) || unformat (input, "%d", &x))
+ {
+ if (x >= (1 << 16))
+ return 0;
+ *result = x;
+ return 1;
+ }
+
+ /* Named type. */
+ if (unformat_user (input, unformat_vlib_number_by_name,
+ am->opcode_by_name, &i))
+ {
+ *result = i;
+ return 1;
+ }
+
+ return 0;
+}
+
+static uword
+unformat_ethernet_arp_opcode_net_byte_order (unformat_input_t * input,
+ va_list * args)
+{
+ int *result = va_arg (*args, int *);
+ if (!unformat_user
+ (input, unformat_ethernet_arp_opcode_host_byte_order, result))
+ return 0;
+
+ *result = clib_host_to_net_u16 ((u16) * result);
+ return 1;
+}
+
+typedef struct
+{
+ u8 packet_data[64];
+} ethernet_arp_input_trace_t;
+
+static u8 *
+format_ethernet_arp_input_trace (u8 * s, va_list * va)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
+ ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
+
+ s = format (s, "%U",
+ format_ethernet_arp_header,
+ t->packet_data, sizeof (t->packet_data));
+
+ return s;
+}
+
+static int
+arp_is_enabled (ethernet_arp_main_t * am, u32 sw_if_index)
+{
+ if (vec_len (am->ethernet_arp_by_sw_if_index) <= sw_if_index)
+ return 0;
+
+ return (am->ethernet_arp_by_sw_if_index[sw_if_index].enabled);
+}
+
+static void
+arp_enable (ethernet_arp_main_t * am, u32 sw_if_index)
+{
+ if (arp_is_enabled (am, sw_if_index))
+ return;
+
+ vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index);
+
+ am->ethernet_arp_by_sw_if_index[sw_if_index].enabled = 1;
+
+ vnet_feature_enable_disable ("arp", "arp-reply", sw_if_index, 1, NULL, 0);
+ vnet_feature_enable_disable ("arp", "arp-disabled", sw_if_index, 0, NULL,
+ 0);
+}
+
+static void
+arp_disable (ethernet_arp_main_t * am, u32 sw_if_index)
+{
+ if (!arp_is_enabled (am, sw_if_index))
+ return;
+
+ vnet_feature_enable_disable ("arp", "arp-disabled", sw_if_index, 1, NULL,
+ 0);
+ vnet_feature_enable_disable ("arp", "arp-reply", sw_if_index, 0, NULL, 0);
+
+ am->ethernet_arp_by_sw_if_index[sw_if_index].enabled = 0;
+}
+
+static int
+arp_unnumbered (vlib_buffer_t * p0,
+ u32 input_sw_if_index, u32 conn_sw_if_index)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ vnet_interface_main_t *vim = &vnm->interface_main;
+ vnet_sw_interface_t *si;
+
+ /* verify that the input interface is unnumbered to the connected.
+ * the connected interface is the interface on which the subnet is
+ * configured */
+ si = &vim->sw_interfaces[input_sw_if_index];
+
+ if (!(si->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED &&
+ (si->unnumbered_sw_if_index == conn_sw_if_index)))
+ {
+ /* the input interface is not unnumbered to the interface on which
+ * the sub-net is configured that covers the ARP request.
+ * So this is not the case for unnumbered.. */
+ return 0;
+ }
+
+ return !0;
+}
+
+always_inline u32
+arp_learn (u32 sw_if_index,
+ const ethernet_arp_ip4_over_ethernet_address_t * addr)
+{
+ ip_neighbor_learn_t l = {
+ .ip.ip4 = addr->ip4,
+ .type = IP46_TYPE_IP4,
+ .mac = addr->mac,
+ .sw_if_index = sw_if_index,
+ };
+
+ ip_neighbor_learn_dp (&l);
+
+ return (ETHERNET_ARP_ERROR_l3_src_address_learned);
+}
+
+typedef enum arp_input_next_t_
+{
+ ARP_INPUT_NEXT_DROP,
+ ARP_INPUT_NEXT_DISABLED,
+ ARP_INPUT_N_NEXT,
+} arp_input_next_t;
+
+static uword
+arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ u32 n_left_from, next_index, *from, *to_next, n_left_to_next;
+ ethernet_arp_main_t *am = &ethernet_arp_main;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ if (node->flags & VLIB_NODE_FLAG_TRACE)
+ vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
+ /* stride */ 1,
+ sizeof (ethernet_arp_input_trace_t));
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ const ethernet_arp_header_t *arp0;
+ arp_input_next_t next0;
+ vlib_buffer_t *p0;
+ u32 pi0, error0;
+
+ pi0 = to_next[0] = from[0];
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer (vm, pi0);
+ arp0 = vlib_buffer_get_current (p0);
+
+ error0 = ETHERNET_ARP_ERROR_replies_sent;
+ next0 = ARP_INPUT_NEXT_DROP;
+
+ error0 =
+ (arp0->l2_type !=
+ clib_net_to_host_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet) ?
+ ETHERNET_ARP_ERROR_l2_type_not_ethernet : error0);
+ error0 =
+ (arp0->l3_type !=
+ clib_net_to_host_u16 (ETHERNET_TYPE_IP4) ?
+ ETHERNET_ARP_ERROR_l3_type_not_ip4 : error0);
+ error0 =
+ (0 == arp0->ip4_over_ethernet[0].ip4.as_u32 ?
+ ETHERNET_ARP_ERROR_l3_dst_address_unset : error0);
+
+ if (ETHERNET_ARP_ERROR_replies_sent == error0)
+ {
+ next0 = ARP_INPUT_NEXT_DISABLED;
+ vnet_feature_arc_start (am->feature_arc_index,
+ vnet_buffer (p0)->sw_if_index[VLIB_RX],
+ &next0, p0);
+ }
+ else
+ p0->error = node->errors[error0];
+
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+ n_left_to_next, pi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+typedef enum arp_disabled_next_t_
+{
+ ARP_DISABLED_NEXT_DROP,
+ ARP_DISABLED_N_NEXT,
+} arp_disabled_next_t;
+
+#define foreach_arp_disabled_error \
+ _ (DISABLED, "ARP Disabled on this interface") \
+
+typedef enum
+{
+#define _(sym,string) ARP_DISABLED_ERROR_##sym,
+ foreach_arp_disabled_error
+#undef _
+ ARP_DISABLED_N_ERROR,
+} arp_disabled_error_t;
+
+static char *arp_disabled_error_strings[] = {
+#define _(sym,string) string,
+ foreach_arp_disabled_error
+#undef _
+};
+
+static uword
+arp_disabled (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ u32 n_left_from, next_index, *from, *to_next, n_left_to_next;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ if (node->flags & VLIB_NODE_FLAG_TRACE)
+ vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
+ /* stride */ 1,
+ sizeof (ethernet_arp_input_trace_t));
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ arp_disabled_next_t next0 = ARP_DISABLED_NEXT_DROP;
+ vlib_buffer_t *p0;
+ u32 pi0, error0;
+
+ next0 = ARP_DISABLED_NEXT_DROP;
+ error0 = ARP_DISABLED_ERROR_DISABLED;
+
+ pi0 = to_next[0] = from[0];
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer (vm, pi0);
+ p0->error = node->errors[error0];
+
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+ n_left_to_next, pi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+enum arp_dst_fib_type
+{
+ ARP_DST_FIB_NONE,
+ ARP_DST_FIB_ADJ,
+ ARP_DST_FIB_CONN
+};
+
+/*
+ * we're looking for FIB sources that indicate the destination
+ * is attached. There may be interposed DPO prior to the one
+ * we are looking for
+ */
+static enum arp_dst_fib_type
+arp_dst_fib_check (const fib_node_index_t fei, fib_entry_flag_t * flags)
+{
+ const fib_entry_t *entry = fib_entry_get (fei);
+ const fib_entry_src_t *entry_src;
+ fib_source_t src;
+ /* *INDENT-OFF* */
+ FOR_EACH_SRC_ADDED(entry, entry_src, src,
+ ({
+ *flags = fib_entry_get_flags_for_source (fei, src);
+ if (fib_entry_is_sourced (fei, FIB_SOURCE_ADJ))
+ return ARP_DST_FIB_ADJ;
+ else if (FIB_ENTRY_FLAG_CONNECTED & *flags)
+ return ARP_DST_FIB_CONN;
+ }))
+ /* *INDENT-ON* */
+
+ return ARP_DST_FIB_NONE;
+}
+
+static uword
+arp_reply (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 n_left_from, next_index, *from, *to_next;
+ u32 n_replies_sent = 0;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ if (node->flags & VLIB_NODE_FLAG_TRACE)
+ vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
+ /* stride */ 1,
+ sizeof (ethernet_arp_input_trace_t));
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ vlib_buffer_t *p0;
+ ethernet_arp_header_t *arp0;
+ ethernet_header_t *eth_rx;
+ const ip4_address_t *if_addr0;
+ u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0;
+ u8 dst_is_local0, is_vrrp_reply0;
+ fib_node_index_t dst_fei, src_fei;
+ const fib_prefix_t *pfx0;
+ fib_entry_flag_t src_flags, dst_flags;
+
+ pi0 = from[0];
+ to_next[0] = pi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer (vm, pi0);
+ arp0 = vlib_buffer_get_current (p0);
+ /* Fill in ethernet header. */
+ eth_rx = ethernet_buffer_get_header (p0);
+
+ next0 = ARP_REPLY_NEXT_DROP;
+ error0 = ETHERNET_ARP_ERROR_replies_sent;
+ sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
+
+ /* Check that IP address is local and matches incoming interface. */
+ fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
+ if (~0 == fib_index0)
+ {
+ error0 = ETHERNET_ARP_ERROR_interface_no_table;
+ goto drop;
+
+ }
+
+ {
+ /*
+ * we're looking for FIB entries that indicate the source
+ * is attached. There may be more specific non-attached
+ * routes that match the source, but these do not influence
+ * whether we respond to an ARP request, i.e. they do not
+ * influence whether we are the correct way for the sender
+ * to reach us, they only affect how we reach the sender.
+ */
+ fib_entry_t *src_fib_entry;
+ const fib_prefix_t *pfx;
+ fib_entry_src_t *src;
+ fib_source_t source;
+ int attached;
+ int mask;
+
+ mask = 32;
+ attached = 0;
+
+ do
+ {
+ src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
+ &arp0->
+ ip4_over_ethernet[0].ip4,
+ mask);
+ src_fib_entry = fib_entry_get (src_fei);
+
+ /*
+ * It's possible that the source that provides the
+ * flags we need, or the flags we must not have,
+ * is not the best source, so check then all.
+ */
+ /* *INDENT-OFF* */
+ FOR_EACH_SRC_ADDED(src_fib_entry, src, source,
+ ({
+ src_flags = fib_entry_get_flags_for_source (src_fei, source);
+
+ /* Reject requests/replies with our local interface
+ address. */
+ if (FIB_ENTRY_FLAG_LOCAL & src_flags)
+ {
+ error0 = ETHERNET_ARP_ERROR_l3_src_address_is_local;
+ /*
+ * When VPP has an interface whose address is also
+ * applied to a TAP interface on the host, then VPP's
+ * TAP interface will be unnumbered to the 'real'
+ * interface and do proxy ARP from the host.
+ * The curious aspect of this setup is that ARP requests
+ * from the host will come from the VPP's own address.
+ * So don't drop immediately here, instead go see if this
+ * is a proxy ARP case.
+ */
+ goto next_feature;
+ }
+ /* A Source must also be local to subnet of matching
+ * interface address. */
+ if ((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
+ (FIB_ENTRY_FLAG_CONNECTED & src_flags))
+ {
+ attached = 1;
+ break;
+ }
+ /*
+ * else
+ * The packet was sent from an address that is not
+ * connected nor attached i.e. it is not from an
+ * address that is covered by a link's sub-net,
+ * nor is it a already learned host resp.
+ */
+ }));
+ /* *INDENT-ON* */
+
+ /*
+ * shorter mask lookup for the next iteration.
+ */
+ pfx = fib_entry_get_prefix (src_fei);
+ mask = pfx->fp_len - 1;
+
+ /*
+ * continue until we hit the default route or we find
+ * the attached we are looking for. The most likely
+ * outcome is we find the attached with the first source
+ * on the first lookup.
+ */
+ }
+ while (!attached &&
+ !fib_entry_is_sourced (src_fei, FIB_SOURCE_DEFAULT_ROUTE));
+
+ if (!attached)
+ {
+ /*
+ * the matching route is a not attached, i.e. it was
+ * added as a result of routing, rather than interface/ARP
+ * configuration. If the matching route is not a host route
+ * (i.e. a /32)
+ */
+ error0 = ETHERNET_ARP_ERROR_l3_src_address_not_local;
+ goto drop;
+ }
+ }
+
+ dst_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
+ &arp0->ip4_over_ethernet[1].ip4,
+ 32);
+ switch (arp_dst_fib_check (dst_fei, &dst_flags))
+ {
+ case ARP_DST_FIB_ADJ:
+ /*
+ * We matched an adj-fib on ths source subnet (a /32 previously
+ * added as a result of ARP). If this request is a gratuitous
+ * ARP, then learn from it.
+ * The check for matching an adj-fib, is to prevent hosts
+ * from spamming us with gratuitous ARPS that might otherwise
+ * blow our ARP cache
+ */
+ if (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
+ arp0->ip4_over_ethernet[1].ip4.as_u32)
+ error0 =
+ arp_learn (sw_if_index0, &arp0->ip4_over_ethernet[0]);
+ goto drop;
+ case ARP_DST_FIB_CONN:
+ /* destination is connected, continue to process */
+ break;
+ case ARP_DST_FIB_NONE:
+ /* destination is not connected, stop here */
+ error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
+ goto next_feature;
+ }
+
+ dst_is_local0 = (FIB_ENTRY_FLAG_LOCAL & dst_flags);
+ pfx0 = fib_entry_get_prefix (dst_fei);
+ if_addr0 = &pfx0->fp_addr.ip4;
+
+ is_vrrp_reply0 =
+ ((arp0->opcode ==
+ clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
+ &&
+ (!memcmp
+ (arp0->ip4_over_ethernet[0].mac.bytes, vrrp_prefix,
+ sizeof (vrrp_prefix))));
+
+ /* Trash ARP packets whose ARP-level source addresses do not
+ match their L2-frame-level source addresses, unless it's
+ a reply from a VRRP virtual router */
+ if (!ethernet_mac_address_equal
+ (eth_rx->src_address,
+ arp0->ip4_over_ethernet[0].mac.bytes) && !is_vrrp_reply0)
+ {
+ error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
+ goto drop;
+ }
+
+ /* Learn or update sender's mapping only for replies to addresses
+ * that are local to the subnet */
+ if (arp0->opcode ==
+ clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
+ {
+ if (dst_is_local0)
+ error0 =
+ arp_learn (sw_if_index0, &arp0->ip4_over_ethernet[0]);
+ else
+ /* a reply for a non-local destination could be a GARP.
+ * GARPs for hosts we know were handled above, so this one
+ * we drop */
+ error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
+
+ goto next_feature;
+ }
+ else if (arp0->opcode ==
+ clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request) &&
+ (dst_is_local0 == 0))
+ {
+ goto next_feature;
+ }
+
+ /* Honor unnumbered interface, if any */
+ conn_sw_if_index0 = fib_entry_get_resolving_interface (dst_fei);
+ if (sw_if_index0 != conn_sw_if_index0 ||
+ sw_if_index0 != fib_entry_get_resolving_interface (src_fei))
+ {
+ /*
+ * The interface the ARP is sent to or was received on is not the
+ * interface on which the covering prefix is configured.
+ * Maybe this is a case for unnumbered.
+ */
+ if (!arp_unnumbered (p0, sw_if_index0, conn_sw_if_index0))
+ {
+ error0 = ETHERNET_ARP_ERROR_unnumbered_mismatch;
+ goto drop;
+ }
+ }
+ if (arp0->ip4_over_ethernet[0].ip4.as_u32 ==
+ arp0->ip4_over_ethernet[1].ip4.as_u32)
+ {
+ error0 = ETHERNET_ARP_ERROR_gratuitous_arp;
+ goto drop;
+ }
+
+ next0 = arp_mk_reply (vnm, p0, sw_if_index0,
+ if_addr0, arp0, eth_rx);
+
+ /* We are going to reply to this request, so, in the absence of
+ errors, learn the sender */
+ if (!error0)
+ error0 = arp_learn (sw_if_index0, &arp0->ip4_over_ethernet[1]);
+
+ n_replies_sent += 1;
+ goto enqueue;
+
+ next_feature:
+ vnet_feature_next (&next0, p0);
+ goto enqueue;
+
+ drop:
+ p0->error = node->errors[error0];
+
+ enqueue:
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+ n_left_to_next, pi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ vlib_error_count (vm, node->node_index,
+ ETHERNET_ARP_ERROR_replies_sent, n_replies_sent);
+
+ return frame->n_vectors;
+}
+
+
+static char *ethernet_arp_error_strings[] = {
+#define _(sym,string) string,
+ foreach_ethernet_arp_error
+#undef _
+};
+
+/* *INDENT-OFF* */
+
+VLIB_REGISTER_NODE (arp_input_node, static) =
+{
+ .function = arp_input,
+ .name = "arp-input",
+ .vector_size = sizeof (u32),
+ .n_errors = ETHERNET_ARP_N_ERROR,
+ .error_strings = ethernet_arp_error_strings,
+ .n_next_nodes = ARP_INPUT_N_NEXT,
+ .next_nodes = {
+ [ARP_INPUT_NEXT_DROP] = "error-drop",
+ [ARP_INPUT_NEXT_DISABLED] = "arp-disabled",
+ },
+ .format_buffer = format_ethernet_arp_header,
+ .format_trace = format_ethernet_arp_input_trace,
+};
+
+VLIB_REGISTER_NODE (arp_disabled_node, static) =
+{
+ .function = arp_disabled,
+ .name = "arp-disabled",
+ .vector_size = sizeof (u32),
+ .n_errors = ARP_DISABLED_N_ERROR,
+ .error_strings = arp_disabled_error_strings,
+ .n_next_nodes = ARP_DISABLED_N_NEXT,
+ .next_nodes = {
+ [ARP_INPUT_NEXT_DROP] = "error-drop",
+ },
+ .format_buffer = format_ethernet_arp_header,
+ .format_trace = format_ethernet_arp_input_trace,
+};
+
+VLIB_REGISTER_NODE (arp_reply_node, static) =
+{
+ .function = arp_reply,
+ .name = "arp-reply",
+ .vector_size = sizeof (u32),
+ .n_errors = ETHERNET_ARP_N_ERROR,
+ .error_strings = ethernet_arp_error_strings,
+ .n_next_nodes = ARP_REPLY_N_NEXT,
+ .next_nodes = {
+ [ARP_REPLY_NEXT_DROP] = "error-drop",
+ [ARP_REPLY_NEXT_REPLY_TX] = "interface-output",
+ },
+ .format_buffer = format_ethernet_arp_header,
+ .format_trace = format_ethernet_arp_input_trace,
+};
+
+/* Built-in ARP rx feature path definition */
+VNET_FEATURE_ARC_INIT (arp_feat, static) =
+{
+ .arc_name = "arp",
+ .start_nodes = VNET_FEATURES ("arp-input"),
+ .last_in_arc = "error-drop",
+ .arc_index_ptr = &ethernet_arp_main.feature_arc_index,
+};
+
+VNET_FEATURE_INIT (arp_reply_feat_node, static) =
+{
+ .arc_name = "arp",
+ .node_name = "arp-reply",
+ .runs_before = VNET_FEATURES ("arp-disabled"),
+};
+
+VNET_FEATURE_INIT (arp_proxy_feat_node, static) =
+{
+ .arc_name = "arp",
+ .node_name = "arp-proxy",
+ .runs_after = VNET_FEATURES ("arp-reply"),
+ .runs_before = VNET_FEATURES ("arp-disabled"),
+};
+
+VNET_FEATURE_INIT (arp_disabled_feat_node, static) =
+{
+ .arc_name = "arp",
+ .node_name = "arp-disabled",
+ .runs_before = VNET_FEATURES ("error-drop"),
+};
+
+VNET_FEATURE_INIT (arp_drop_feat_node, static) =
+{
+ .arc_name = "arp",
+ .node_name = "error-drop",
+ .runs_before = 0, /* last feature */
+};
+
+/* *INDENT-ON* */
+
+typedef struct
+{
+ pg_edit_t l2_type, l3_type;
+ pg_edit_t n_l2_address_bytes, n_l3_address_bytes;
+ pg_edit_t opcode;
+ struct
+ {
+ pg_edit_t mac;
+ pg_edit_t ip4;
+ } ip4_over_ethernet[2];
+} pg_ethernet_arp_header_t;
+
+static inline void
+pg_ethernet_arp_header_init (pg_ethernet_arp_header_t * p)
+{
+ /* Initialize fields that are not bit fields in the IP header. */
+#define _(f) pg_edit_init (&p->f, ethernet_arp_header_t, f);
+ _(l2_type);
+ _(l3_type);
+ _(n_l2_address_bytes);
+ _(n_l3_address_bytes);
+ _(opcode);
+ _(ip4_over_ethernet[0].mac);
+ _(ip4_over_ethernet[0].ip4);
+ _(ip4_over_ethernet[1].mac);
+ _(ip4_over_ethernet[1].ip4);
+#undef _
+}
+
+uword
+unformat_pg_arp_header (unformat_input_t * input, va_list * args)
+{
+ pg_stream_t *s = va_arg (*args, pg_stream_t *);
+ pg_ethernet_arp_header_t *p;
+ u32 group_index;
+
+ p = pg_create_edit_group (s, sizeof (p[0]), sizeof (ethernet_arp_header_t),
+ &group_index);
+ pg_ethernet_arp_header_init (p);
+
+ /* Defaults. */
+ pg_edit_set_fixed (&p->l2_type, ETHERNET_ARP_HARDWARE_TYPE_ethernet);
+ pg_edit_set_fixed (&p->l3_type, ETHERNET_TYPE_IP4);
+ pg_edit_set_fixed (&p->n_l2_address_bytes, 6);
+ pg_edit_set_fixed (&p->n_l3_address_bytes, 4);
+
+ if (!unformat (input, "%U: %U/%U -> %U/%U",
+ unformat_pg_edit,
+ unformat_ethernet_arp_opcode_net_byte_order, &p->opcode,
+ unformat_pg_edit,
+ unformat_mac_address_t, &p->ip4_over_ethernet[0].mac,
+ unformat_pg_edit,
+ unformat_ip4_address, &p->ip4_over_ethernet[0].ip4,
+ unformat_pg_edit,
+ unformat_mac_address_t, &p->ip4_over_ethernet[1].mac,
+ unformat_pg_edit,
+ unformat_ip4_address, &p->ip4_over_ethernet[1].ip4))
+ {
+ /* Free up any edits we may have added. */
+ pg_free_edit_group (s);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * callback when an interface address is added or deleted
+ */
+static void
+arp_enable_disable_interface (ip4_main_t * im,
+ uword opaque, u32 sw_if_index, u32 is_enable)
+{
+ ethernet_arp_main_t *am = &ethernet_arp_main;
+
+ if (is_enable)
+ arp_enable (am, sw_if_index);
+ else
+ arp_disable (am, sw_if_index);
+}
+
+/*
+ * Remove any arp entries associated with the specified interface
+ */
+static clib_error_t *
+vnet_arp_add_del_sw_interface (vnet_main_t * vnm, u32 sw_if_index, u32 is_add)
+{
+ ethernet_arp_main_t *am = &ethernet_arp_main;
+
+ if (!is_add && sw_if_index != ~0)
+ {
+ arp_disable (am, sw_if_index);
+ }
+ else if (is_add)
+ {
+ vnet_feature_enable_disable ("arp", "arp-disabled",
+ sw_if_index, 1, NULL, 0);
+ }
+
+ return (NULL);
+}
+
+VNET_SW_INTERFACE_ADD_DEL_FUNCTION (vnet_arp_add_del_sw_interface);
+
+const static ip_neighbor_vft_t arp_vft = {
+ .inv_proxy4_add = arp_proxy_add,
+ .inv_proxy4_del = arp_proxy_del,
+ .inv_proxy4_enable = arp_proxy_disable,
+ .inv_proxy4_disable = arp_proxy_disable,
+};
+
+static clib_error_t *
+ethernet_arp_init (vlib_main_t * vm)
+{
+ ethernet_arp_main_t *am = &ethernet_arp_main;
+ ip4_main_t *im = &ip4_main;
+ pg_node_t *pn;
+
+ ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, arp_input_node.index);
+
+ pn = pg_get_node (arp_input_node.index);
+ pn->unformat_edit = unformat_pg_arp_header;
+
+ am->opcode_by_name = hash_create_string (0, sizeof (uword));
+#define _(o) hash_set_mem (am->opcode_by_name, #o, ETHERNET_ARP_OPCODE_##o);
+ foreach_ethernet_arp_opcode;
+#undef _
+
+ /* don't trace ARP error packets */
+ {
+ vlib_node_runtime_t *rt =
+ vlib_node_get_runtime (vm, arp_input_node.index);
+
+#define _(a,b) \
+ vnet_pcap_drop_trace_filter_add_del \
+ (rt->errors[ETHERNET_ARP_ERROR_##a], \
+ 1 /* is_add */);
+ foreach_ethernet_arp_error
+#undef _
+ }
+
+ {
+ ip4_enable_disable_interface_callback_t cb = {
+ .function = arp_enable_disable_interface,
+ };
+ vec_add1 (im->enable_disable_interface_callbacks, cb);
+ }
+
+ ip_neighbor_register (IP46_TYPE_IP4, &arp_vft);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_INIT_FUNCTION (ethernet_arp_init) =
+{
+ .runs_after = VLIB_INITS("ethernet_init",
+ "ip_neighbor_init"),
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/arp/arp.h b/src/vnet/arp/arp.h
new file mode 100644
index 00000000000..7446564b0cf
--- /dev/null
+++ b/src/vnet/arp/arp.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ARP_H__
+#define __ARP_H__
+
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/arp_packet.h>
+
+#define foreach_ethernet_arp_error \
+ _ (replies_sent, "ARP replies sent") \
+ _ (l2_type_not_ethernet, "L2 type not ethernet") \
+ _ (l3_type_not_ip4, "L3 type not IP4") \
+ _ (l3_src_address_not_local, "IP4 source address not local to subnet") \
+ _ (l3_dst_address_not_local, "IP4 destination address not local to subnet") \
+ _ (l3_dst_address_unset, "IP4 destination address is unset") \
+ _ (l3_src_address_is_local, "IP4 source address matches local interface") \
+ _ (l3_src_address_learned, "ARP request IP4 source address learned") \
+ _ (replies_received, "ARP replies received") \
+ _ (opcode_not_request, "ARP opcode not request") \
+ _ (proxy_arp_replies_sent, "Proxy ARP replies sent") \
+ _ (l2_address_mismatch, "ARP hw addr does not match L2 frame src addr") \
+ _ (gratuitous_arp, "ARP probe or announcement dropped") \
+ _ (interface_no_table, "Interface is not mapped to an IP table") \
+ _ (interface_not_ip_enabled, "Interface is not IP enabled") \
+ _ (unnumbered_mismatch, "RX interface is unnumbered to different subnet") \
+
+typedef enum
+{
+#define _(sym,string) ETHERNET_ARP_ERROR_##sym,
+ foreach_ethernet_arp_error
+#undef _
+ ETHERNET_ARP_N_ERROR,
+} ethernet_arp_reply_error_t;
+
+extern int arp_proxy_add (u32 fib_index,
+ const ip4_address_t * lo_addr,
+ const ip4_address_t * hi_addr);
+extern int arp_proxy_del (u32 fib_index,
+ const ip4_address_t * lo_addr,
+ const ip4_address_t * hi_addr);
+
+extern int arp_proxy_enable (u32 sw_if_index);
+extern int arp_proxy_disable (u32 sw_if_index);
+
+/**
+ * call back function when walking the DB of proxy ARPs
+ * @return 0 to stop the walk !0 to continue
+ */
+typedef walk_rc_t (proxy_arp_walk_t) (const ip4_address_t * lo_addr,
+ const ip4_address_t * hi_addr,
+ u32 fib_index, void *dat);
+
+extern void proxy_arp_walk (proxy_arp_walk_t cb, void *data);
+
+/**
+ * call back function when walking the DB of proxy ARP interface
+ * @return 0 to stop the walk !0 to continue
+ */
+typedef walk_rc_t (proxy_arp_intf_walk_t) (u32 sw_if_index, void *data);
+
+extern void proxy_arp_intfc_walk (proxy_arp_intf_walk_t cb, void *data);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/arp/arp_api.c b/src/vnet/arp/arp_api.c
new file mode 100644
index 00000000000..170bace2b0a
--- /dev/null
+++ b/src/vnet/arp/arp_api.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+
+#include <vnet/arp/arp.h>
+
+#include <vnet/fib/fib_table.h>
+#include <vnet/ip/ip_types_api.h>
+
+#include <vpp/app/version.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+/* define message IDs */
+#include <vnet/format_fns.h>
+#include <vnet/arp/arp.api_enum.h>
+#include <vnet/arp/arp.api_types.h>
+
+/**
+ * Base message ID fot the plugin
+ */
+static u32 arp_base_msg_id;
+#define REPLY_MSG_ID_BASE arp_base_msg_id
+
+#include <vlibapi/api_helper_macros.h>
+
+static void
+vl_api_proxy_arp_add_del_t_handler (vl_api_proxy_arp_add_del_t * mp)
+{
+ vl_api_proxy_arp_add_del_reply_t *rmp;
+ ip4_address_t lo, hi;
+ u32 fib_index;
+ int rv;
+
+ fib_index = fib_table_find (FIB_PROTOCOL_IP4, ntohl (mp->proxy.table_id));
+
+ if (~0 == fib_index)
+ {
+ rv = VNET_API_ERROR_NO_SUCH_FIB;
+ goto out;
+ }
+
+ ip4_address_decode (mp->proxy.low, &lo);
+ ip4_address_decode (mp->proxy.hi, &hi);
+
+ if (mp->is_add)
+ rv = arp_proxy_add (fib_index, &lo, &hi);
+ else
+ rv = arp_proxy_del (fib_index, &lo, &hi);
+
+out:
+ REPLY_MACRO (VL_API_PROXY_ARP_ADD_DEL_REPLY);
+}
+
+typedef struct proxy_arp_walk_ctx_t_
+{
+ vl_api_registration_t *reg;
+ u32 context;
+} proxy_arp_walk_ctx_t;
+
+static walk_rc_t
+send_proxy_arp_details (const ip4_address_t * lo_addr,
+ const ip4_address_t * hi_addr,
+ u32 fib_index, void *data)
+{
+ vl_api_proxy_arp_details_t *mp;
+ proxy_arp_walk_ctx_t *ctx;
+
+ ctx = data;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_PROXY_ARP_DETAILS + REPLY_MSG_ID_BASE);
+ mp->context = ctx->context;
+ mp->proxy.table_id = htonl (fib_index);
+
+ ip4_address_encode (lo_addr, mp->proxy.low);
+ ip4_address_encode (hi_addr, mp->proxy.hi);
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+vl_api_proxy_arp_dump_t_handler (vl_api_proxy_arp_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ proxy_arp_walk_ctx_t wctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ proxy_arp_walk (send_proxy_arp_details, &wctx);
+}
+
+static walk_rc_t
+send_proxy_arp_intfc_details (u32 sw_if_index, void *data)
+{
+ vl_api_proxy_arp_intfc_details_t *mp;
+ proxy_arp_walk_ctx_t *ctx;
+
+ ctx = data;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_PROXY_ARP_INTFC_DETAILS + REPLY_MSG_ID_BASE);
+ mp->context = ctx->context;
+ mp->sw_if_index = htonl (sw_if_index);
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+vl_api_proxy_arp_intfc_dump_t_handler (vl_api_proxy_arp_intfc_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ proxy_arp_walk_ctx_t wctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ proxy_arp_intfc_walk (send_proxy_arp_intfc_details, &wctx);
+}
+
+static void
+ vl_api_proxy_arp_intfc_enable_disable_t_handler
+ (vl_api_proxy_arp_intfc_enable_disable_t * mp)
+{
+ vl_api_proxy_arp_intfc_enable_disable_reply_t *rmp;
+ int rv;
+
+ VALIDATE_SW_IF_INDEX (mp);
+
+ if (mp->enable)
+ rv = arp_proxy_enable (ntohl (mp->sw_if_index));
+ else
+ rv = arp_proxy_disable (ntohl (mp->sw_if_index));
+
+ BAD_SW_IF_INDEX_LABEL;
+
+ REPLY_MACRO (VL_API_PROXY_ARP_INTFC_ENABLE_DISABLE_REPLY);
+}
+
+#include <vnet/arp/arp.api.c>
+
+static clib_error_t *
+arp_api_init (vlib_main_t * vm)
+{
+ /* Ask for a correctly-sized block of API message decode slots */
+ arp_base_msg_id = setup_message_id_table ();
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (arp_api_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/arp/arp_packet.h b/src/vnet/arp/arp_packet.h
new file mode 100644
index 00000000000..a860c258f75
--- /dev/null
+++ b/src/vnet/arp/arp_packet.h
@@ -0,0 +1,91 @@
+/*
+ * ethernet/arp.c: IP v4 ARP node
+ *
+ * Copyright (c) 2010 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ARP_PACKET_H__
+#define __ARP_PACKET_H__
+
+#include <vnet/arp/arp.h>
+
+/* Either we drop the packet or we send a reply to the sender. */
+typedef enum
+{
+ ARP_REPLY_NEXT_DROP,
+ ARP_REPLY_NEXT_REPLY_TX,
+ ARP_REPLY_N_NEXT,
+} arp_reply_next_t;
+
+static_always_inline u32
+arp_mk_reply (vnet_main_t * vnm,
+ vlib_buffer_t * p0,
+ u32 sw_if_index0,
+ const ip4_address_t * if_addr0,
+ ethernet_arp_header_t * arp0, ethernet_header_t * eth_rx)
+{
+ vnet_hw_interface_t *hw_if0;
+ u8 *rewrite0, rewrite0_len;
+ ethernet_header_t *eth_tx;
+ u32 next0;
+
+ /* Send a reply.
+ An adjacency to the sender is not always present,
+ so we use the interface to build us a rewrite string
+ which will contain all the necessary tags. */
+ rewrite0 = ethernet_build_rewrite (vnm, sw_if_index0,
+ VNET_LINK_ARP, eth_rx->src_address);
+ rewrite0_len = vec_len (rewrite0);
+
+ /* Figure out how much to rewind current data from adjacency. */
+ vlib_buffer_advance (p0, -rewrite0_len);
+ eth_tx = vlib_buffer_get_current (p0);
+
+ vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
+ hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
+
+ /* Send reply back through input interface */
+ vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
+ next0 = ARP_REPLY_NEXT_REPLY_TX;
+
+ arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
+
+ arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
+
+ mac_address_from_bytes (&arp0->ip4_over_ethernet[0].mac,
+ hw_if0->hw_address);
+ clib_mem_unaligned (&arp0->ip4_over_ethernet[0].ip4.data_u32, u32) =
+ if_addr0->data_u32;
+
+ /* Hardware must be ethernet-like. */
+ ASSERT (vec_len (hw_if0->hw_address) == 6);
+
+ /* the rx nd tx ethernet headers wil overlap in the case
+ * when we received a tagged VLAN=0 packet, but we are sending
+ * back untagged */
+ clib_memcpy_fast (eth_tx, rewrite0, vec_len (rewrite0));
+ vec_free (rewrite0);
+
+ return (next0);
+}
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/arp/arp_proxy.c b/src/vnet/arp/arp_proxy.c
new file mode 100644
index 00000000000..346a21775f8
--- /dev/null
+++ b/src/vnet/arp/arp_proxy.c
@@ -0,0 +1,407 @@
+/*
+ * ethernet/arp.c: IP v4 ARP node
+ *
+ * Copyright (c) 2010 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/arp/arp.h>
+#include <vnet/arp/arp_packet.h>
+
+#include <vnet/fib/ip4_fib.h>
+
+typedef struct
+{
+ ip4_address_t lo_addr;
+ ip4_address_t hi_addr;
+ u32 fib_index;
+} ethernet_proxy_arp_t;
+
+typedef struct arp_proxy_main_t_
+{
+ /** Per interface state */
+ bool *enabled_by_sw_if_index;
+
+ /* Proxy arp vector */
+ ethernet_proxy_arp_t *proxy_arps;
+} arp_proxy_main_t;
+
+arp_proxy_main_t arp_proxy_main;
+
+void
+proxy_arp_walk (proxy_arp_walk_t cb, void *data)
+{
+ arp_proxy_main_t *am = &arp_proxy_main;
+ ethernet_proxy_arp_t *pa;
+
+ vec_foreach (pa, am->proxy_arps)
+ {
+ if (!cb (&pa->lo_addr, &pa->hi_addr, pa->fib_index, data))
+ break;
+ }
+}
+
+int
+arp_proxy_disable (u32 sw_if_index)
+{
+ arp_proxy_main_t *am = &arp_proxy_main;
+
+ vec_validate (am->enabled_by_sw_if_index, sw_if_index);
+
+ if (am->enabled_by_sw_if_index[sw_if_index])
+ {
+ vnet_feature_enable_disable ("arp", "arp-proxy",
+ sw_if_index, 0, NULL, 0);
+ }
+ am->enabled_by_sw_if_index[sw_if_index] = false;
+
+ return (0);
+}
+
+int
+arp_proxy_enable (u32 sw_if_index)
+{
+ arp_proxy_main_t *am = &arp_proxy_main;
+
+ vec_validate (am->enabled_by_sw_if_index, sw_if_index);
+
+ if (!am->enabled_by_sw_if_index[sw_if_index])
+ {
+ vnet_feature_enable_disable ("arp", "arp-proxy",
+ sw_if_index, 1, NULL, 0);
+ }
+ am->enabled_by_sw_if_index[sw_if_index] = true;
+
+ return (0);
+}
+
+static int
+vnet_proxy_arp_add_del (const ip4_address_t * lo_addr,
+ const ip4_address_t * hi_addr,
+ u32 fib_index, int is_del)
+{
+ arp_proxy_main_t *am = &arp_proxy_main;
+ ethernet_proxy_arp_t *pa;
+ u32 found_at_index = ~0;
+
+ vec_foreach (pa, am->proxy_arps)
+ {
+ if (pa->lo_addr.as_u32 == lo_addr->as_u32 &&
+ pa->hi_addr.as_u32 == hi_addr->as_u32 && pa->fib_index == fib_index)
+ {
+ found_at_index = pa - am->proxy_arps;
+ break;
+ }
+ }
+
+ if (found_at_index != ~0)
+ {
+ /* Delete, otherwise it's already in the table */
+ if (is_del)
+ vec_delete (am->proxy_arps, 1, found_at_index);
+ return 0;
+ }
+ /* delete, no such entry */
+ if (is_del)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ /* add, not in table */
+ vec_add2 (am->proxy_arps, pa, 1);
+ pa->lo_addr.as_u32 = lo_addr->as_u32;
+ pa->hi_addr.as_u32 = hi_addr->as_u32;
+ pa->fib_index = fib_index;
+ return 0;
+}
+
+int
+arp_proxy_add (u32 fib_index,
+ const ip4_address_t * lo, const ip4_address_t * hi)
+{
+ return (vnet_proxy_arp_add_del (lo, hi, fib_index, 0));
+}
+
+int
+arp_proxy_del (u32 fib_index,
+ const ip4_address_t * lo, const ip4_address_t * hi)
+{
+ return (vnet_proxy_arp_add_del (lo, hi, fib_index, 1));
+}
+
+void
+proxy_arp_intfc_walk (proxy_arp_intf_walk_t cb, void *data)
+{
+ arp_proxy_main_t *am = &arp_proxy_main;
+ bool *enabled;
+
+ vec_foreach (enabled, am->enabled_by_sw_if_index)
+ {
+ if (*enabled)
+ cb (enabled - am->enabled_by_sw_if_index, data);
+ }
+}
+
+static clib_error_t *
+set_int_proxy_arp_command_fn (vlib_main_t * vm,
+ unformat_input_t *
+ input, vlib_cli_command_t * cmd)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 sw_if_index;
+ int enable = 0;
+
+ sw_if_index = ~0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "%U", unformat_vnet_sw_interface,
+ vnm, &sw_if_index))
+ ;
+ else if (unformat (input, "enable") || unformat (input, "on"))
+ enable = 1;
+ else if (unformat (input, "disable") || unformat (input, "off"))
+ enable = 0;
+ else
+ break;
+ }
+
+ if (~0 == sw_if_index)
+ return clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, input);
+
+ if (enable)
+ arp_proxy_enable (sw_if_index);
+ else
+ arp_proxy_disable (sw_if_index);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+/*?
+ * Enable proxy-arp on an interface. The vpp stack will answer ARP
+ * requests for the indicated address range. Multiple proxy-arp
+ * ranges may be provisioned.
+ *
+ * @note Proxy ARP as a technology is infamous for blackholing traffic.
+ * Also, the underlying implementation has not been performance-tuned.
+ * Avoid creating an unnecessarily large set of ranges.
+ *
+ * @cliexpar
+ * To enable proxy arp on a range of addresses, use:
+ * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11}
+ * Append 'del' to delete a range of proxy ARP addresses:
+ * @cliexcmd{set ip arp proxy 6.0.0.1 - 6.0.0.11 del}
+ * You must then specifically enable proxy arp on individual interfaces:
+ * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 enable}
+ * To disable proxy arp on an individual interface:
+ * @cliexcmd{set interface proxy-arp GigabitEthernet0/8/0 disable}
+ ?*/
+VLIB_CLI_COMMAND (set_int_proxy_enable_command, static) = {
+ .path = "set interface proxy-arp",
+ .short_help =
+ "set interface proxy-arp <intfc> [enable|disable]",
+ .function = set_int_proxy_arp_command_fn,
+};
+/* *INDENT-ON* */
+
+typedef struct
+{
+ u8 packet_data[64];
+} ethernet_arp_input_trace_t;
+
+static u8 *
+format_ethernet_arp_input_trace (u8 * s, va_list * va)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
+ ethernet_arp_input_trace_t *t = va_arg (*va, ethernet_arp_input_trace_t *);
+
+ s = format (s, "%U",
+ format_ethernet_arp_header,
+ t->packet_data, sizeof (t->packet_data));
+
+ return s;
+}
+
+static uword
+arp_proxy (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ arp_proxy_main_t *am = &arp_proxy_main;
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 n_left_from, next_index, *from, *to_next;
+ u32 n_arp_replies_sent = 0;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ if (node->flags & VLIB_NODE_FLAG_TRACE)
+ vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
+ /* stride */ 1,
+ sizeof (ethernet_arp_input_trace_t));
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ vlib_buffer_t *p0;
+ ethernet_arp_header_t *arp0;
+ ethernet_header_t *eth_rx;
+ ip4_address_t proxy_src;
+ u32 pi0, error0, next0, sw_if_index0, fib_index0;
+ u8 is_request0;
+ ethernet_proxy_arp_t *pa;
+
+ pi0 = from[0];
+ to_next[0] = pi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer (vm, pi0);
+ arp0 = vlib_buffer_get_current (p0);
+ /* Fill in ethernet header. */
+ eth_rx = ethernet_buffer_get_header (p0);
+
+ is_request0 = arp0->opcode
+ == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
+
+ error0 = ETHERNET_ARP_ERROR_replies_sent;
+ sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
+ next0 = ARP_REPLY_NEXT_DROP;
+
+ fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
+ if (~0 == fib_index0)
+ {
+ error0 = ETHERNET_ARP_ERROR_interface_no_table;
+ }
+
+ if (0 == error0 && is_request0)
+ {
+ u32 this_addr = clib_net_to_host_u32
+ (arp0->ip4_over_ethernet[1].ip4.as_u32);
+
+ vec_foreach (pa, am->proxy_arps)
+ {
+ u32 lo_addr = clib_net_to_host_u32 (pa->lo_addr.as_u32);
+ u32 hi_addr = clib_net_to_host_u32 (pa->hi_addr.as_u32);
+
+ /* an ARP request hit in the proxy-arp table? */
+ if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
+ (fib_index0 == pa->fib_index))
+ {
+ proxy_src.as_u32 =
+ arp0->ip4_over_ethernet[1].ip4.data_u32;
+
+ /*
+ * change the interface address to the proxied
+ */
+ n_arp_replies_sent++;
+
+ next0 =
+ arp_mk_reply (vnm, p0, sw_if_index0, &proxy_src, arp0,
+ eth_rx);
+ }
+ }
+ }
+ else
+ {
+ p0->error = node->errors[error0];
+ }
+
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+ n_left_to_next, pi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ vlib_error_count (vm, node->node_index,
+ ETHERNET_ARP_ERROR_replies_sent, n_arp_replies_sent);
+
+ return frame->n_vectors;
+}
+
+static char *ethernet_arp_error_strings[] = {
+#define _(sym,string) string,
+ foreach_ethernet_arp_error
+#undef _
+};
+
+VLIB_REGISTER_NODE (arp_proxy_node, static) =
+{
+ .function = arp_proxy,.name = "arp-proxy",.vector_size =
+ sizeof (u32),.n_errors = ETHERNET_ARP_N_ERROR,.error_strings =
+ ethernet_arp_error_strings,.n_next_nodes = ARP_REPLY_N_NEXT,.next_nodes =
+ {
+ [ARP_REPLY_NEXT_DROP] = "error-drop",
+ [ARP_REPLY_NEXT_REPLY_TX] = "interface-output",}
+,.format_buffer = format_ethernet_arp_header,.format_trace =
+ format_ethernet_arp_input_trace,};
+
+static clib_error_t *
+show_ip4_arp (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ arp_proxy_main_t *am = &arp_proxy_main;
+ ethernet_proxy_arp_t *pa;
+
+ if (vec_len (am->proxy_arps))
+ {
+ vlib_cli_output (vm, "Proxy arps enabled for:");
+ vec_foreach (pa, am->proxy_arps)
+ {
+ vlib_cli_output (vm, "Fib_index %d %U - %U ",
+ pa->fib_index,
+ format_ip4_address, &pa->lo_addr,
+ format_ip4_address, &pa->hi_addr);
+ }
+ }
+
+ return (NULL);
+}
+
+/*?
+ * Display all the IPv4 ARP proxy entries.
+ *
+ * @cliexpar
+ * Example of how to display the IPv4 ARP table:
+ * @cliexstart{show ip arp}
+ * Time FIB IP4 Flags Ethernet Interface
+ * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
+ * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
+ * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
+ * Proxy arps enabled for:
+ * Fib_index 0 6.0.0.1 - 6.0.0.11
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_ip4_arp_command, static) = {
+ .path = "show arp proxy",
+ .function = show_ip4_arp,
+ .short_help = "show ip arp",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/arp/arp_test.c b/src/vnet/arp/arp_test.c
new file mode 100644
index 00000000000..861d0739406
--- /dev/null
+++ b/src/vnet/arp/arp_test.c
@@ -0,0 +1,168 @@
+/*
+ *------------------------------------------------------------------
+ * arp_test.c
+ *
+ * Copyright (c) 2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <vat/vat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vppinfra/error.h>
+
+#include <vnet/ip/ip_format_fns.h>
+
+#include <vpp/api/types.h>
+
+typedef struct
+{
+ /* API message ID base */
+ u16 msg_id_base;
+ u32 ping_id;
+ vat_main_t *vat_main;
+} arp_test_main_t;
+
+arp_test_main_t arp_test_main;
+
+#define __plugin_msg_base arp_test_main.msg_id_base
+#include <vlibapi/vat_helper_macros.h>
+uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
+
+/* Declare message IDs */
+#include <arp/arp.api_enum.h>
+#include <arp/arp.api_types.h>
+#include <vpp/api/vpe.api_types.h>
+
+static int
+api_proxy_arp_dump (vat_main_t * vam)
+{
+ return -1;
+}
+
+static int
+api_proxy_arp_intfc_dump (vat_main_t * vam)
+{
+ return -1;
+}
+
+static void
+vl_api_proxy_arp_details_t_handler (vl_api_proxy_arp_details_t * mp)
+{
+}
+
+static void
+vl_api_proxy_arp_intfc_details_t_handler (vl_api_proxy_arp_intfc_details_t *
+ mp)
+{
+}
+
+static int
+api_proxy_arp_add_del (vat_main_t * vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_proxy_arp_add_del_t *mp;
+ u32 vrf_id = 0;
+ u8 is_add = 1;
+ vl_api_ip4_address_t lo, hi;
+ u8 range_set = 0;
+ int ret;
+
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "vrf %d", &vrf_id))
+ ;
+ else if (unformat (i, "%U - %U", unformat_vl_api_ip4_address, &lo,
+ unformat_vl_api_ip4_address, &hi))
+ range_set = 1;
+ else if (unformat (i, "del"))
+ is_add = 0;
+ else
+ {
+ clib_warning ("parse error '%U'", format_unformat_error, i);
+ return -99;
+ }
+ }
+
+ if (range_set == 0)
+ {
+ errmsg ("address range not set");
+ return -99;
+ }
+
+ M (PROXY_ARP_ADD_DEL, mp);
+
+ mp->proxy.table_id = ntohl (vrf_id);
+ mp->is_add = is_add;
+ clib_memcpy (mp->proxy.low, &lo, sizeof (lo));
+ clib_memcpy (mp->proxy.hi, &hi, sizeof (hi));
+
+ S (mp);
+ W (ret);
+ return ret;
+}
+
+static int
+api_proxy_arp_intfc_enable_disable (vat_main_t * vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_proxy_arp_intfc_enable_disable_t *mp;
+ u32 sw_if_index;
+ u8 enable = 1;
+ u8 sw_if_index_set = 0;
+ int ret;
+
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
+ sw_if_index_set = 1;
+ else if (unformat (i, "sw_if_index %d", &sw_if_index))
+ sw_if_index_set = 1;
+ else if (unformat (i, "enable"))
+ enable = 1;
+ else if (unformat (i, "disable"))
+ enable = 0;
+ else
+ {
+ clib_warning ("parse error '%U'", format_unformat_error, i);
+ return -99;
+ }
+ }
+
+ if (sw_if_index_set == 0)
+ {
+ errmsg ("missing interface name or sw_if_index");
+ return -99;
+ }
+
+ M (PROXY_ARP_INTFC_ENABLE_DISABLE, mp);
+
+ mp->sw_if_index = ntohl (sw_if_index);
+ mp->enable = enable;
+
+ S (mp);
+ W (ret);
+ return ret;
+}
+
+#include <arp/arp.api_test.c>
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */