#include <vnet/l2/l2_input.h>
#include <vppinfra/mhash.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/mpls/mpls.h>
/**
u32 sw_if_index;
ethernet_arp_ip4_over_ethernet_address_t a;
int is_static;
+ int is_no_fib_entry;
int flags;
#define ETHERNET_ARP_ARGS_REMOVE (1<<0)
#define ETHERNET_ARP_ARGS_FLUSH (1<<1)
#define ETHERNET_ARP_ARGS_POPULATE (1<<2)
} vnet_arp_set_ip4_over_ethernet_rpc_args_t;
+static const u8 vrrp_prefix[] = { 0x00, 0x00, 0x5E, 0x00, 0x01 };
+
+/* Node index for send_garp_na_process */
+u32 send_garp_na_process_node_index;
+
static void
set_ip4_over_ethernet_rpc_callback (vnet_arp_set_ip4_over_ethernet_rpc_args_t
* a);
if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC)
flags = format (flags, "D");
- s = format (s, "%=12U%=16U%=6s%=20U%=24U",
+ if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_NO_FIB_ENTRY)
+ flags = format (flags, "N");
+
+ s = format (s, "%=12U%=16U%=6s%=20U%U",
format_vlib_cpu_time, vnm->vlib_main, e->cpu_time_last_updated,
format_ip4_address, &e->ip4_address,
flags ? (char *) flags : "",
arp_int = &am->ethernet_arp_by_sw_if_index[sw_if_index];
e = arp_entry_find (arp_int, &adj->sub_type.nbr.next_hop.ip4);
- if (NULL != e)
- {
- adj_nbr_walk_nh4 (sw_if_index,
- &e->ip4_address, arp_mk_complete_walk, e);
- }
- else
+ switch (adj->lookup_next_index)
{
- /*
- * no matching ARP entry.
- * construct the rewire required to for an ARP packet, and stick
- * that in the adj's pipe to smoke.
- */
- adj_nbr_update_rewrite (ai,
- ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
- ethernet_build_rewrite (vnm,
- sw_if_index,
- VNET_LINK_ARP,
- VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
+ case IP_LOOKUP_NEXT_ARP:
+ case IP_LOOKUP_NEXT_GLEAN:
+ if (NULL != e)
+ {
+ adj_nbr_walk_nh4 (sw_if_index,
+ &e->ip4_address, arp_mk_complete_walk, e);
+ }
+ else
+ {
+ /*
+ * no matching ARP entry.
+ * construct the rewrite required to for an ARP packet, and stick
+ * that in the adj's pipe to smoke.
+ */
+ adj_nbr_update_rewrite
+ (ai,
+ ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
+ ethernet_build_rewrite
+ (vnm,
+ sw_if_index,
+ VNET_LINK_ARP,
+ VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
- /*
- * since the FIB has added this adj for a route, it makes sense it may
- * want to forward traffic sometime soon. Let's send a speculative ARP.
- * just one. If we were to do periodically that wouldn't be bad either,
- * but that's more code than i'm prepared to write at this time for
- * relatively little reward.
- */
- arp_nbr_probe (adj);
+ /*
+ * since the FIB has added this adj for a route, it makes sense it
+ * may want to forward traffic sometime soon. Let's send a
+ * speculative ARP. just one. If we were to do periodically that
+ * wouldn't be bad either, but that's more code than i'm prepared to
+ * write at this time for relatively little reward.
+ */
+ arp_nbr_probe (adj);
+ }
+ break;
+ case IP_LOOKUP_NEXT_MCAST:
+ {
+ /*
+ * Construct a partial rewrite from the known ethernet mcast dest MAC
+ */
+ u8 *rewrite;
+ u8 offset;
+
+ rewrite = ethernet_build_rewrite (vnm,
+ sw_if_index,
+ adj->ia_link,
+ ethernet_ip4_mcast_dst_addr ());
+ offset = vec_len (rewrite) - 2;
+
+ /*
+ * Complete the remaining fields of the adj's rewrite to direct the
+ * complete of the rewrite at switch time by copying in the IP
+ * dst address's bytes.
+ * Ofset is 2 bytes into the MAC desintation address. And we copy 23 bits
+ * from the address.
+ */
+ adj_mcast_update_rewrite (ai, rewrite, offset, 0x007fffff);
+
+ break;
+ }
+ case IP_LOOKUP_NEXT_DROP:
+ case IP_LOOKUP_NEXT_PUNT:
+ case IP_LOOKUP_NEXT_LOCAL:
+ case IP_LOOKUP_NEXT_REWRITE:
+ case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
+ case IP_LOOKUP_NEXT_MIDCHAIN:
+ case IP_LOOKUP_NEXT_ICMP_ERROR:
+ case IP_LOOKUP_N_NEXT:
+ ASSERT (0);
+ break;
}
}
+static void
+arp_adj_fib_add (ethernet_arp_ip4_entry_t * e, uint32_t fib_index)
+{
+ fib_prefix_t pfx = {
+ .fp_len = 32,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_addr.ip4 = e->ip4_address,
+ };
+
+ e->fib_entry_index =
+ fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ,
+ FIB_ENTRY_FLAG_ATTACHED,
+ DPO_PROTO_IP4, &pfx.fp_addr,
+ e->sw_if_index, ~0, 1, NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+ fib_table_lock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_ADJ);
+}
+
int
vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm,
vnet_arp_set_ip4_over_ethernet_rpc_args_t
ethernet_arp_interface_t *arp_int;
int is_static = args->is_static;
u32 sw_if_index = args->sw_if_index;
+ int is_no_fib_entry = args->is_no_fib_entry;
vec_validate (am->ethernet_arp_by_sw_if_index, sw_if_index);
if (make_new_arp_cache_entry)
{
- fib_prefix_t pfx = {
- .fp_len = 32,
- .fp_proto = FIB_PROTOCOL_IP4,
- .fp_addr = {
- .ip4 = a->ip4,
- }
- ,
- };
- u32 fib_index;
-
pool_get (am->ip4_entry_pool, e);
if (NULL == arp_int->arp_entries)
e->sw_if_index = sw_if_index;
e->ip4_address = a->ip4;
+ e->fib_entry_index = FIB_NODE_INDEX_INVALID;
clib_memcpy (e->ethernet_address,
a->ethernet, sizeof (e->ethernet_address));
- fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index);
- e->fib_entry_index =
- fib_table_entry_update_one_path (fib_index,
- &pfx,
- FIB_SOURCE_ADJ,
- FIB_ENTRY_FLAG_ATTACHED,
- FIB_PROTOCOL_IP4,
- &pfx.fp_addr,
- e->sw_if_index,
- ~0,
- 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
+ if (!is_no_fib_entry)
+ {
+ arp_adj_fib_add (e,
+ ip4_fib_table_get_index_for_sw_if_index
+ (e->sw_if_index));
+ }
+ else
+ {
+ e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_NO_FIB_ENTRY;
+ }
}
else
{
{
ethernet_arp_main_t *am = ðernet_arp_main;
ip4_address_t *address = address_arg;
- uword *p;
+
+ /* Try to find an existing entry */
+ u32 *first = (u32 *) hash_get (am->mac_changes_by_address, address->as_u32);
+ u32 *p = first;
pending_resolution_t *mc;
- void (*fp) (u32, u8 *) = data_callback;
+ while (p && *p != ~0)
+ {
+ mc = pool_elt_at_index (am->mac_changes, *p);
+ if (mc->node_index == node_index && mc->type_opaque == type_opaque
+ && mc->pid == pid)
+ break;
+ p = &mc->next_index;
+ }
+ int found = p && *p != ~0;
if (is_add)
{
- pool_get (am->mac_changes, mc);
+ if (found)
+ return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
- mc->next_index = ~0;
- mc->node_index = node_index;
- mc->type_opaque = type_opaque;
- mc->data = data;
- mc->data_callback = data_callback;
- mc->pid = pid;
+ pool_get (am->mac_changes, mc);
+ *mc = (pending_resolution_t)
+ {
+ .next_index = ~0,.node_index = node_index,.type_opaque =
+ type_opaque,.data = data,.data_callback = data_callback,.pid =
+ pid,};
- p = hash_get (am->mac_changes_by_address, address->as_u32);
+ /* Insert new resolution at the end of the list */
+ u32 new_idx = mc - am->mac_changes;
if (p)
- {
- /* Insert new resolution at the head of the list */
- mc->next_index = p[0];
- hash_unset (am->mac_changes_by_address, address->as_u32);
- }
-
- hash_set (am->mac_changes_by_address, address->as_u32,
- mc - am->mac_changes);
- return 0;
+ p[0] = new_idx;
+ else
+ hash_set (am->mac_changes_by_address, address->as_u32, new_idx);
}
else
{
- u32 index;
- pending_resolution_t *mc_last = 0;
-
- p = hash_get (am->mac_changes_by_address, address->as_u32);
- if (p == 0)
+ if (!found)
return VNET_API_ERROR_NO_SUCH_ENTRY;
- index = p[0];
+ /* Clients may need to clean up pool entries, too */
+ void (*fp) (u32, u8 *) = data_callback;
+ if (fp)
+ (*fp) (mc->data, 0 /* no new mac addrs */ );
- while (index != (u32) ~ 0)
- {
- mc = pool_elt_at_index (am->mac_changes, index);
- if (mc->node_index == node_index &&
- mc->type_opaque == type_opaque && mc->pid == pid)
- {
- /* Clients may need to clean up pool entries, too */
- if (fp)
- (*fp) (mc->data, 0 /* no new mac addrs */ );
- if (index == p[0])
- {
- hash_unset (am->mac_changes_by_address, address->as_u32);
- if (mc->next_index != ~0)
- hash_set (am->mac_changes_by_address, address->as_u32,
- mc->next_index);
- pool_put (am->mac_changes, mc);
- return 0;
- }
- else
- {
- ASSERT (mc_last);
- mc_last->next_index = mc->next_index;
- pool_put (am->mac_changes, mc);
- return 0;
- }
- }
- mc_last = mc;
- index = mc->next_index;
- }
+ /* Remove the entry from the list and delete the entry */
+ *p = mc->next_index;
+ pool_put (am->mac_changes, mc);
- return VNET_API_ERROR_NO_SUCH_ENTRY;
+ /* Remove from hash if we deleted the last entry */
+ if (*p == ~0 && p == first)
+ hash_unset (am->mac_changes_by_address, address->as_u32);
}
+ return 0;
}
/* Either we drop the packet or we send a reply to the sender. */
_ (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") \
- _ (missing_interface_address, "ARP missing interface address") \
_ (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") \
typedef enum
{
static int
arp_unnumbered (vlib_buffer_t * p0,
- u32 pi0, ethernet_header_t * eth0, u32 sw_if_index)
+ u32 input_sw_if_index, u32 conn_sw_if_index)
{
- vlib_main_t *vm = vlib_get_main ();
vnet_main_t *vnm = vnet_get_main ();
vnet_interface_main_t *vim = &vnm->interface_main;
vnet_sw_interface_t *si;
- vnet_hw_interface_t *hi;
- u32 unnum_src_sw_if_index;
- u32 *broadcast_swifs = 0;
- u32 *buffers = 0;
- u32 n_alloc = 0;
- vlib_buffer_t *b0;
- int i;
- u8 dst_mac_address[6];
- i16 header_size;
- ethernet_arp_header_t *arp0;
-
- /* Save the dst mac address */
- clib_memcpy (dst_mac_address, eth0->dst_address, sizeof (dst_mac_address));
-
- /* Figure out which sw_if_index supplied the address */
- unnum_src_sw_if_index = sw_if_index;
-
- /* Track down all users of the unnumbered source */
- /* *INDENT-OFF* */
- pool_foreach (si, vim->sw_interfaces,
- ({
- if (si->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED &&
- (si->unnumbered_sw_if_index == unnum_src_sw_if_index))
- {
- vec_add1 (broadcast_swifs, si->sw_if_index);
- }
- }));
- /* *INDENT-ON* */
-
- /* If there are no interfaces un-unmbered to this interface,
- we are done here. */
- if (0 == vec_len (broadcast_swifs))
- return 0;
-
- /* Allocate buffering if we need it */
- if (vec_len (broadcast_swifs) > 1)
- {
- vec_validate (buffers, vec_len (broadcast_swifs) - 2);
- n_alloc = vlib_buffer_alloc (vm, buffers, vec_len (buffers));
- _vec_len (buffers) = n_alloc;
- for (i = 0; i < n_alloc; i++)
- {
- b0 = vlib_get_buffer (vm, buffers[i]);
-
- /* xerox (partially built) ARP pkt */
- clib_memcpy (b0->data, p0->data,
- p0->current_length + p0->current_data);
- b0->current_data = p0->current_data;
- b0->current_length = p0->current_length;
- vnet_buffer (b0)->sw_if_index[VLIB_RX] =
- vnet_buffer (p0)->sw_if_index[VLIB_RX];
- }
- }
- vec_insert (buffers, 1, 0);
- buffers[0] = pi0;
+ /* 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];
- for (i = 0; i < vec_len (buffers); i++)
+ if (!(si->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED &&
+ (si->unnumbered_sw_if_index == conn_sw_if_index)))
{
- b0 = vlib_get_buffer (vm, buffers[i]);
- arp0 = vlib_buffer_get_current (b0);
-
- hi = vnet_get_sup_hw_interface (vnm, broadcast_swifs[i]);
- si = vnet_get_sw_interface (vnm, broadcast_swifs[i]);
-
- /* For decoration, most likely */
- vnet_buffer (b0)->sw_if_index[VLIB_TX] = hi->sw_if_index;
-
- /* Fix ARP pkt src address */
- clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, hi->hw_address, 6);
-
- /* Build L2 encaps for this swif */
- header_size = sizeof (ethernet_header_t);
- if (si->sub.eth.flags.one_tag)
- header_size += 4;
- else if (si->sub.eth.flags.two_tags)
- header_size += 8;
-
- vlib_buffer_advance (b0, -header_size);
- eth0 = vlib_buffer_get_current (b0);
-
- if (si->sub.eth.flags.one_tag)
- {
- ethernet_vlan_header_t *outer = (void *) (eth0 + 1);
-
- eth0->type = si->sub.eth.flags.dot1ad ?
- clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
- clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
- outer->priority_cfi_and_id =
- clib_host_to_net_u16 (si->sub.eth.outer_vlan_id);
- outer->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
-
- }
- else if (si->sub.eth.flags.two_tags)
- {
- ethernet_vlan_header_t *outer = (void *) (eth0 + 1);
- ethernet_vlan_header_t *inner = (void *) (outer + 1);
-
- eth0->type = si->sub.eth.flags.dot1ad ?
- clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
- clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
- outer->priority_cfi_and_id =
- clib_host_to_net_u16 (si->sub.eth.outer_vlan_id);
- outer->type = clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
- inner->priority_cfi_and_id =
- clib_host_to_net_u16 (si->sub.eth.inner_vlan_id);
- inner->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
-
- }
- else
- {
- eth0->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
- }
-
- /* Restore the original dst address, set src address */
- clib_memcpy (eth0->dst_address, dst_mac_address,
- sizeof (eth0->dst_address));
- clib_memcpy (eth0->src_address, hi->hw_address,
- sizeof (eth0->src_address));
-
- /* Transmit replicas */
- if (i > 0)
- {
- vlib_frame_t *f =
- vlib_get_frame_to_node (vm, hi->output_node_index);
- u32 *to_next = vlib_frame_vector_args (f);
- to_next[0] = buffers[i];
- f->n_vectors = 1;
- vlib_put_frame_to_node (vm, hi->output_node_index, f);
- }
+ /* 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;
}
- /* The regular path outputs the original pkt.. */
- vnet_buffer (p0)->sw_if_index[VLIB_TX] = broadcast_swifs[0];
+ return !0;
+}
- vec_free (broadcast_swifs);
- vec_free (buffers);
+static u32
+arp_learn (vnet_main_t * vnm,
+ ethernet_arp_main_t * am, u32 sw_if_index, void *addr)
+{
+ if (am->limit_arp_cache_size &&
+ pool_elts (am->ip4_entry_pool) >= am->limit_arp_cache_size)
+ unset_random_arp_entry ();
- return !0;
+ vnet_arp_set_ip4_over_ethernet (vnm, sw_if_index, addr, 0, 0);
+ return (ETHERNET_ARP_ERROR_l3_src_address_learned);
}
static uword
vlib_buffer_t *p0;
vnet_hw_interface_t *hw_if0;
ethernet_arp_header_t *arp0;
- ethernet_header_t *eth0;
- ip_adjacency_t *adj0;
+ ethernet_header_t *eth_rx, *eth_tx;
ip4_address_t *if_addr0, proxy_src;
u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0;
- u8 is_request0, dst_is_local0, is_unnum0;
+ u8 is_request0, dst_is_local0, is_unnum0, is_vrrp_reply0;
ethernet_proxy_arp_t *pa;
fib_node_index_t dst_fei, src_fei;
fib_prefix_t pfx0;
fib_entry_flag_t src_flags, dst_flags;
+ u8 *rewrite0, rewrite0_len;
pi0 = from[0];
to_next[0] = pi0;
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);
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
+ /* not playing the ARP game if the interface is not IPv4 enabled */
+ error0 =
+ (im4->ip_enabled_by_sw_if_index[sw_if_index0] == 0 ?
+ ETHERNET_ARP_ERROR_interface_not_ip_enabled : error0);
+
if (error0)
goto drop2;
dst_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
&arp0->ip4_over_ethernet[1].ip4,
32);
- dst_flags = fib_entry_get_flags_for_source (dst_fei,
- FIB_SOURCE_INTERFACE);
-
- conn_sw_if_index0 =
- fib_entry_get_resolving_interface_for_source (dst_fei,
- FIB_SOURCE_INTERFACE);
+ dst_flags = fib_entry_get_flags (dst_fei);
- if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
- {
- error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
- goto drop1;
- }
+ conn_sw_if_index0 = fib_entry_get_resolving_interface (dst_fei);
/* Honor unnumbered interface, if any */
is_unnum0 = sw_if_index0 != conn_sw_if_index0;
- /* Source must also be local to subnet of matching interface address. */
- src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
- &arp0->ip4_over_ethernet[0].ip4,
- 32);
- src_flags = fib_entry_get_flags (src_fei);
+ {
+ /*
+ * we're looking for FIB entries that indicate the source
+ * is attached. There may be more specific non-attached
+ * routes tht 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;
+ fib_entry_src_t *src;
+ fib_source_t source;
+ fib_prefix_t pfx;
+ 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 drop1;
+ }
+ /* 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.
+ */
+ fib_entry_get_prefix (src_fei, &pfx);
+ 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 (!((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
- (FIB_ENTRY_FLAG_CONNECTED & src_flags)) ||
- sw_if_index0 != fib_entry_get_resolving_interface (src_fei))
+ 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 drop2;
+ }
+ }
+
+ if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
{
- error0 = ETHERNET_ARP_ERROR_l3_src_address_not_local;
- goto drop2;
+ error0 = ETHERNET_ARP_ERROR_l3_dst_address_not_local;
+ goto drop1;
}
- /* Reject requests/replies with our local interface address. */
- if (FIB_ENTRY_FLAG_LOCAL & src_flags)
+ if (sw_if_index0 != fib_entry_get_resolving_interface (src_fei))
{
- error0 = ETHERNET_ARP_ERROR_l3_src_address_is_local;
- goto drop2;
+ /*
+ * The interface the ARP was received on is not the interface
+ * on which the covering prefix is configured. Maybe this is a
+ * case for unnumbered.
+ */
+ is_unnum0 = 1;
}
dst_is_local0 = (FIB_ENTRY_FLAG_LOCAL & dst_flags);
fib_entry_get_prefix (dst_fei, &pfx0);
if_addr0 = &pfx0.fp_addr.ip4;
- /* Fill in ethernet header. */
- eth0 = ethernet_buffer_get_header (p0);
+ is_vrrp_reply0 =
+ ((arp0->opcode ==
+ clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
+ &&
+ (!memcmp
+ (arp0->ip4_over_ethernet[0].ethernet, vrrp_prefix,
+ sizeof (vrrp_prefix))));
/* Trash ARP packets whose ARP-level source addresses do not
- match their L2-frame-level source addresses */
- if (memcmp (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
- sizeof (eth0->src_address)))
+ match their L2-frame-level source addresses, unless it's
+ a reply from a VRRP virtual router */
+ if (memcmp
+ (eth_rx->src_address, arp0->ip4_over_ethernet[0].ethernet,
+ sizeof (eth_rx->src_address)) && !is_vrrp_reply0)
{
error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
goto drop2;
}
- /* Learn or update sender's mapping only for requests or unicasts
- that don't match local interface address. */
- if (ethernet_address_cast (eth0->dst_address) ==
- ETHERNET_ADDRESS_UNICAST || is_request0)
- {
- if (am->limit_arp_cache_size &&
- pool_elts (am->ip4_entry_pool) >= am->limit_arp_cache_size)
- unset_random_arp_entry ();
-
- vnet_arp_set_ip4_over_ethernet (vnm, sw_if_index0,
- &arp0->ip4_over_ethernet[0],
- 0 /* is_static */ );
- error0 = ETHERNET_ARP_ERROR_l3_src_address_learned;
- }
-
- /* Only send a reply for requests sent which match a local interface. */
- if (!(is_request0 && dst_is_local0))
+ /* 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) &&
+ dst_is_local0)
{
- error0 =
- (arp0->opcode ==
- clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply) ?
- ETHERNET_ARP_ERROR_replies_received : error0);
+ error0 = arp_learn (vnm, am, sw_if_index0,
+ &arp0->ip4_over_ethernet[0]);
goto drop1;
}
- /* Send a reply. */
send_reply:
+ /* 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);
/* Hardware must be ethernet-like. */
ASSERT (vec_len (hw_if0->hw_address) == 6);
- clib_memcpy (eth0->dst_address, eth0->src_address, 6);
- clib_memcpy (eth0->src_address, 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 (eth_tx, rewrite0, vec_len (rewrite0));
+ vec_free (rewrite0);
- /* Figure out how much to rewind current data from adjacency. */
- /* get the adj from the destination's covering connected */
if (NULL == pa)
{
- adj0 =
- adj_get (fib_entry_get_adj_for_source
- (ip4_fib_table_lookup
- (ip4_fib_get (fib_index0),
- &arp0->ip4_over_ethernet[1].ip4, 31),
- FIB_SOURCE_INTERFACE));
- if (adj0->lookup_next_index != IP_LOOKUP_NEXT_GLEAN)
- {
- error0 = ETHERNET_ARP_ERROR_missing_interface_address;
- goto drop2;
- }
if (is_unnum0)
{
- if (!arp_unnumbered (p0, pi0, eth0, conn_sw_if_index0))
+ if (!arp_unnumbered (p0, sw_if_index0, conn_sw_if_index0))
goto drop2;
}
- else
- vlib_buffer_advance (p0, -adj0->rewrite_header.data_bytes);
}
+
+ /* We are going to reply to this request, so, in the absence of
+ errors, learn the sender */
+ if (!error0)
+ error0 = arp_learn (vnm, am, sw_if_index0,
+ &arp0->ip4_over_ethernet[1]);
+
vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
n_left_to_next, pi0, next0);
if ((this_addr >= lo_addr && this_addr <= hi_addr) &&
(fib_index0 == pa->fib_index))
{
- eth0 = ethernet_buffer_get_header (p0);
proxy_src.as_u32 =
arp0->ip4_over_ethernet[1].ip4.data_u32;
/*
- * Rewind buffer, direct code above not to
- * think too hard about it.
+ * change the interface address to the proxied
*/
if_addr0 = &proxy_src;
is_unnum0 = 0;
- i32 ethernet_start =
- vnet_buffer (p0)->ethernet.start_of_ethernet_header;
- i32 rewind = p0->current_data - ethernet_start;
- vlib_buffer_advance (p0, -rewind);
n_proxy_arp_replies_sent++;
goto send_reply;
}
}
}
+void
+arp_adj_fib_remove (ethernet_arp_ip4_entry_t * e, uint32_t fib_index)
+{
+ if (FIB_NODE_INDEX_INVALID != e->fib_entry_index)
+ {
+ fib_prefix_t pfx = {
+ .fp_len = 32,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_addr.ip4 = e->ip4_address,
+ };
+ u32 fib_index;
+
+ fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index);
+
+ fib_table_entry_path_remove (fib_index, &pfx,
+ FIB_SOURCE_ADJ,
+ DPO_PROTO_IP4,
+ &pfx.fp_addr,
+ e->sw_if_index, ~0, 1,
+ FIB_ROUTE_PATH_FLAG_NONE);
+ fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_ADJ);
+ }
+}
+
+static void
+arp_table_bind (ip4_main_t * im,
+ uword opaque,
+ u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
+{
+ ethernet_arp_main_t *am = ðernet_arp_main;
+ ethernet_arp_interface_t *eai;
+ ethernet_arp_ip4_entry_t *e;
+ hash_pair_t *pair;
+
+ /*
+ * the IP table that the interface is bound to has changed.
+ * reinstall all the adj fibs.
+ */
+
+ if (vec_len (am->ethernet_arp_by_sw_if_index) <= sw_if_index)
+ return;
+
+ eai = &am->ethernet_arp_by_sw_if_index[sw_if_index];
+
+ /* *INDENT-OFF* */
+ hash_foreach_pair (pair, eai->arp_entries,
+ ({
+ e = pool_elt_at_index(am->ip4_entry_pool,
+ pair->value[0]);
+ /*
+ * remove the adj-fib from the old table and add to the new
+ */
+ arp_adj_fib_remove(e, old_fib_index);
+ arp_adj_fib_add(e, new_fib_index);
+ }));
+ /* *INDENT-ON* */
+
+}
+
static clib_error_t *
ethernet_arp_init (vlib_main_t * vm)
{
cb.function_opaque = 0;
vec_add1 (im->add_del_interface_address_callbacks, cb);
+ ip4_table_bind_callback_t cbt;
+ cbt.function = arp_table_bind;
+ cbt.function_opaque = 0;
+ vec_add1 (im->table_bind_callbacks, cbt);
+
return 0;
}
{
ethernet_arp_main_t *am = ðernet_arp_main;
- fib_table_entry_delete_index (e->fib_entry_index, FIB_SOURCE_ADJ);
+ arp_adj_fib_remove (e,
+ ip4_fib_table_get_index_for_sw_if_index
+ (e->sw_if_index));
hash_unset (eai->arp_entries, e->ip4_address.as_u32);
pool_put (am->ip4_entry_pool, e);
}
ethernet_arp_ip4_entry_t *e;
ethernet_arp_interface_t *eai;
+ if (vec_len (am->ethernet_arp_by_sw_if_index) <= args->sw_if_index)
+ return 0;
+
eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
e = arp_entry_find (eai, &args->a.ip4);
ethernet_arp_ip4_entry_t *e;
ethernet_arp_interface_t *eai;
+ if (vec_len (am->ethernet_arp_by_sw_if_index) <= args->sw_if_index)
+ return 0;
+
eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
e = arp_entry_find (eai, &args->a.ip4);
* does in response to interface events. unset is only done
* by the control plane.
*/
- if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC)
+ if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC)
+ {
+ e->flags &= ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC;
+ }
+ else if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC)
{
arp_entry_free (eai, e);
}
ethernet_arp_ip4_entry_t *e;
ethernet_arp_interface_t *eai;
+ vec_validate (am->ethernet_arp_by_sw_if_index, args->sw_if_index);
eai = &am->ethernet_arp_by_sw_if_index[args->sw_if_index];
e = arp_entry_find (eai, &args->a.ip4);
* a)
{
vnet_main_t *vm = vnet_get_main ();
- ASSERT (os_get_cpu_number () == 0);
+ ASSERT (vlib_get_thread_index () == 0);
if (a->flags & ETHERNET_ARP_ARGS_REMOVE)
vnet_arp_unset_ip4_over_ethernet_internal (vm, a);
int
vnet_arp_set_ip4_over_ethernet (vnet_main_t * vnm,
- u32 sw_if_index, void *a_arg, int is_static)
+ u32 sw_if_index, void *a_arg,
+ int is_static, int is_no_fib_entry)
{
ethernet_arp_ip4_over_ethernet_address_t *a = a_arg;
vnet_arp_set_ip4_over_ethernet_rpc_args_t args;
args.sw_if_index = sw_if_index;
args.is_static = is_static;
+ args.is_no_fib_entry = is_no_fib_entry;
args.flags = 0;
clib_memcpy (&args.a, a, sizeof (*a));
int
vnet_proxy_arp_fib_reset (u32 fib_id)
{
- ip4_main_t *im = &ip4_main;
ethernet_arp_main_t *am = ðernet_arp_main;
ethernet_proxy_arp_t *pa;
u32 *entries_to_delete = 0;
u32 fib_index;
- uword *p;
int i;
- p = hash_get (im->fib_index_by_table_id, fib_id);
- if (!p)
+ fib_index = fib_table_find (FIB_PROTOCOL_IP4, fib_id);
+ if (~0 == fib_index)
return VNET_API_ERROR_NO_SUCH_ENTRY;
- fib_index = p[0];
vec_foreach (pa, am->proxy_arps)
{
u32 fib_index = 0;
u32 fib_id;
int is_static = 0;
+ int is_no_fib_entry = 0;
int is_proxy = 0;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
else if (unformat (input, "static"))
is_static = 1;
+ else if (unformat (input, "no-fib-entry"))
+ is_no_fib_entry = 1;
+
else if (unformat (input, "count %d", &count))
;
else if (unformat (input, "fib-id %d", &fib_id))
{
- ip4_main_t *im = &ip4_main;
- uword *p = hash_get (im->fib_index_by_table_id, fib_id);
- if (!p)
+ fib_index = fib_table_find (FIB_PROTOCOL_IP4, fib_id);
+
+ if (~0 == fib_index)
return clib_error_return (0, "fib ID %d doesn't exist\n", fib_id);
- fib_index = p[0];
}
else if (unformat (input, "proxy %U - %U",
1 /* type */ , 0 /* data */ );
vnet_arp_set_ip4_over_ethernet
- (vnm, sw_if_index, &addr, is_static);
+ (vnm, sw_if_index, &addr, is_static, is_no_fib_entry);
vlib_process_wait_for_event (vm);
event_type = vlib_process_get_events (vm, &event_data);
VLIB_CLI_COMMAND (ip_arp_add_del_command, static) = {
.path = "set ip arp",
.short_help =
- "set ip arp [del] <intfc> <ip-address> <mac-address> [static] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
+ "set ip arp [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
.function = ip_arp_add_del_command_fn,
};
/* *INDENT-ON* */
u16 bd_index0;
u32 ip0;
u8 *macp0;
+ u8 is_vrrp_reply0;
pi0 = from[0];
to_next[0] = pi0;
n_left_to_next -= 1;
p0 = vlib_get_buffer (vm, pi0);
+ // Terminate only local (SHG == 0) ARP
+ if (vnet_buffer (p0)->l2.shg != 0)
+ goto next_l2_feature;
+
eth0 = vlib_buffer_get_current (p0);
l3h0 = (u8 *) eth0 + vnet_buffer (p0)->l2.l2_len;
ethertype0 = clib_net_to_host_u16 (*(u16 *) (l3h0 - 2));
if (error0)
goto drop;
+ is_vrrp_reply0 =
+ ((arp0->opcode ==
+ clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
+ &&
+ (!memcmp
+ (arp0->ip4_over_ethernet[0].ethernet, vrrp_prefix,
+ sizeof (vrrp_prefix))));
+
/* Trash ARP packets whose ARP-level source addresses do not
- match their L2-frame-level source addresses */
+ match their L2-frame-level source addresses, unless it's
+ a reply from a VRRP virtual router */
if (PREDICT_FALSE
- (memcmp
- (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
- sizeof (eth0->src_address))))
+ (memcmp (eth0->src_address, arp0->ip4_over_ethernet[0].ethernet,
+ sizeof (eth0->src_address)) && !is_vrrp_reply0))
{
error0 = ETHERNET_ARP_ERROR_l2_address_mismatch;
goto drop;
pending_resolution_t *mc;
ethernet_arp_main_t *am = ðernet_arp_main;
uword *p = hash_get (am->mac_changes_by_address, 0);
- if (p && (vnet_buffer (p0)->l2.shg == 0))
- { // Only SHG 0 interface which is more likely local
+ if (p)
+ {
u32 next_index = p[0];
while (next_index != (u32) ~ 0)
{
/* Send ARP/ND reply back out input interface through l2-output */
vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
next0 = ARP_TERM_NEXT_L2_OUTPUT;
- /* Note that output to VXLAN tunnel will fail due to SHG which
- is probably desireable since ARP termination is not intended
- for ARP requests from other hosts. If output to VXLAN tunnel is
- required, however, can just clear the SHG in packet as follows:
- vnet_buffer(p0)->l2.shg = 0; */
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
to_next, n_left_to_next, pi0,
next0);
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
if (vnet_ip6_nd_term
(vm, node, p0, eth0, iph0, sw_if_index0,
- vnet_buffer (p0)->l2.bd_index, vnet_buffer (p0)->l2.shg))
+ vnet_buffer (p0)->l2.bd_index))
goto output_response;
}
next_l2_feature:
{
- u32 feature_bitmap0 =
- vnet_buffer (p0)->l2.feature_bitmap & ~L2INPUT_FEAT_ARP_TERM;
- vnet_buffer (p0)->l2.feature_bitmap = feature_bitmap0;
- next0 =
- feat_bitmap_get_next_node_index (arp_term_next_node_index,
- feature_bitmap0);
+ next0 = vnet_l2_feature_next (p0, arp_term_next_node_index,
+ L2INPUT_FEAT_ARP_TERM);
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
to_next, n_left_to_next,
pi0, next0);
/* *INDENT-ON* */
}
+void static
+send_ip4_garp (vlib_main_t * vm, vnet_hw_interface_t * hi)
+{
+ ip4_main_t *i4m = &ip4_main;
+ u32 sw_if_index = hi->sw_if_index;
+ ip4_address_t *ip4_addr = ip4_interface_first_address (i4m, sw_if_index, 0);
+
+ if (ip4_addr)
+ {
+ clib_warning ("Sending GARP for IP4 address %U on sw_if_idex %d",
+ format_ip4_address, ip4_addr, sw_if_index);
+
+ /* Form GARP packet for output - Gratuitous ARP is an ARP request packet
+ where the interface IP/MAC pair is used for both source and request
+ MAC/IP pairs in the request */
+ u32 bi = 0;
+ ethernet_arp_header_t *h = vlib_packet_template_get_packet
+ (vm, &i4m->ip4_arp_request_packet_template, &bi);
+ clib_memcpy (h->ip4_over_ethernet[0].ethernet, hi->hw_address,
+ sizeof (h->ip4_over_ethernet[0].ethernet));
+ clib_memcpy (h->ip4_over_ethernet[1].ethernet, hi->hw_address,
+ sizeof (h->ip4_over_ethernet[1].ethernet));
+ h->ip4_over_ethernet[0].ip4 = ip4_addr[0];
+ h->ip4_over_ethernet[1].ip4 = ip4_addr[0];
+
+ /* Setup MAC header with ARP Etype and broadcast DMAC */
+ vlib_buffer_t *b = vlib_get_buffer (vm, bi);
+ vlib_buffer_advance (b, -sizeof (ethernet_header_t));
+ ethernet_header_t *e = vlib_buffer_get_current (b);
+ e->type = clib_host_to_net_u16 (ETHERNET_TYPE_ARP);
+ clib_memcpy (e->src_address, hi->hw_address, sizeof (e->src_address));
+ memset (e->dst_address, 0xff, sizeof (e->dst_address));
+
+ /* Send GARP packet out the specified interface */
+ vnet_buffer (b)->sw_if_index[VLIB_RX] =
+ vnet_buffer (b)->sw_if_index[VLIB_TX] = sw_if_index;
+ vlib_frame_t *f = vlib_get_frame_to_node (vm, hi->output_node_index);
+ u32 *to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi;
+ f->n_vectors = 1;
+ vlib_put_frame_to_node (vm, hi->output_node_index, f);
+ }
+}
+
+static vlib_node_registration_t send_garp_na_proc_node;
+
+static uword
+send_garp_na_process (vlib_main_t * vm,
+ vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ uword event_type, *event_data = 0;
+
+ send_garp_na_process_node_index = send_garp_na_proc_node.index;
+
+ while (1)
+ {
+ vlib_process_wait_for_event (vm);
+ event_type = vlib_process_get_events (vm, &event_data);
+ if ((event_type == SEND_GARP_NA) && (vec_len (event_data) >= 1))
+ {
+ u32 hw_if_index = event_data[0];
+ vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
+ send_ip4_garp (vm, hi);
+ send_ip6_na (vm, hi);
+ }
+ vec_reset_length (event_data);
+ }
+ return 0;
+}
+
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (send_garp_na_proc_node, static) = {
+ .function = send_garp_na_process,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "send-garp-na-process",
+};
+/* *INDENT-ON* */
+
/*
* fd.io coding-style-patch-verification: ON
*