#include <vnet/l2/l2_input.h>
#include <vppinfra/mhash.h>
+void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
+
typedef struct {
u32 sw_if_index;
u32 fib_index;
u16 flags;
#define ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC (1 << 0)
+#define ETHERNET_ARP_IP4_ENTRY_FLAG_GLEAN (2 << 0)
u64 cpu_time_last_updated;
+
+ u32 * adjacencies;
} ethernet_arp_ip4_entry_t;
typedef struct {
uword * mac_changes_by_address;
pending_resolution_t * mac_changes;
- u32 * arp_input_next_index_by_hw_if_index;
-
ethernet_arp_ip4_entry_t * ip4_entry_pool;
mhash_t ip4_entry_by_key;
ethernet_arp_ip4_entry_t * e = va_arg (*va, ethernet_arp_ip4_entry_t *);
vnet_sw_interface_t * si;
ip4_fib_t * fib;
+ u8 * flags = 0;
if (! e)
- return format (s, "%=12s%=6s%=16s%=4s%=20s%=24s", "Time", "FIB", "IP4",
- "Static", "Ethernet", "Interface");
+ return format (s, "%=12s%=6s%=16s%=6s%=20s%=24s", "Time", "FIB", "IP4",
+ "Flags", "Ethernet", "Interface");
fib = find_ip4_fib_by_table_index_or_id (&ip4_main, e->key.fib_index,
IP4_ROUTE_FLAG_FIB_INDEX);
si = vnet_get_sw_interface (vnm, e->key.sw_if_index);
- s = format (s, "%=12U%=6u%=16U%=4s%=20U%=25U",
+
+ if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_GLEAN)
+ flags = format(flags, "G");
+
+ if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC)
+ flags = format(flags, "S");
+
+ s = format (s, "%=12U%=6u%=16U%=6s%=20U%=24U",
format_vlib_cpu_time, vnm->vlib_main, e->cpu_time_last_updated,
fib->table_id,
format_ip4_address, &e->key.ip4_address,
- (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC) ? "S" : "",
+ flags ? (char *) flags : "",
format_ethernet_address, e->ethernet_address,
format_vnet_sw_interface_name, vnm, si);
+ vec_free(flags);
return s;
}
ethernet_arp_ip4_over_ethernet_address_t delme;
e = pool_elt_at_index (am->ip4_entry_pool, to_delete[i]);
- memcpy (&delme.ethernet, e->ethernet_address, 6);
+ clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
delme.ip4.as_u32 = e->key.ip4_address.as_u32;
vnet_arp_unset_ip4_over_ethernet (vnm, e->key.sw_if_index,
args.fib_index = fib_index;
args.is_static = is_static;
args.is_remove = 0;
- memcpy (&args.a, a, sizeof (*a));
+ clib_memcpy (&args.a, a, sizeof (*a));
vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
(u8 *) &args, sizeof (args));
ethernet_arp_ip4_over_ethernet_address_t * a = a_arg;
vlib_main_t * vm = vlib_get_main();
ip4_main_t * im = &ip4_main;
+ ip_lookup_main_t * lm = &im->lookup_main;
int make_new_arp_cache_entry=1;
uword * p;
ip4_add_del_route_args_t args;
- ip_adjacency_t adj;
+ ip_adjacency_t adj, * existing_adj;
pending_resolution_t * pr, * mc;
u32 next_index;
+ u32 adj_index;
fib_index = (fib_index != (u32)~0)
? fib_index : im->fib_index_by_sw_if_index[sw_if_index];
e = pool_elt_at_index (am->ip4_entry_pool, p[0]);
/* Refuse to over-write static arp. */
- if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC)
+ if (!is_static &&
+ (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC))
return -2;
make_new_arp_cache_entry = 0;
}
/* Note: always install the route. It might have been deleted */
memset(&adj, 0, sizeof(adj));
adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
+ adj.n_adj = 1; /* otherwise signature compare fails */
vnet_rewrite_for_sw_interface
(vnm,
&adj.rewrite_header,
sizeof (adj.rewrite_data));
- args.table_index_or_table_id = fib_index;
- args.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_ADD | IP4_ROUTE_FLAG_NEIGHBOR;
- args.dst_address = a->ip4;
- args.dst_address_length = 32;
- args.adj_index = ~0;
- args.add_adj = &adj;
- args.n_add_adj = 1;
+ /* result of this lookup should be next-hop adjacency */
+ adj_index = ip4_fib_lookup_with_table (im, fib_index, &a->ip4, 0);
+ existing_adj = ip_get_adjacency(lm, adj_index);
+
+ if (existing_adj->lookup_next_index == IP_LOOKUP_NEXT_ARP &&
+ existing_adj->arp.next_hop.ip4.as_u32 == a->ip4.as_u32)
+ {
+ u32 * ai;
+ u32 * adjs = vec_dup(e->adjacencies);
+ /* Update all adj assigned to this arp entry */
+ vec_foreach(ai, adjs)
+ {
+ int i;
+ ip_adjacency_t * uadj = ip_get_adjacency(lm, *ai);
+ for (i = 0; i < uadj->n_adj; i++)
+ if (uadj[i].lookup_next_index == IP_LOOKUP_NEXT_ARP &&
+ uadj[i].arp.next_hop.ip4.as_u32 == a->ip4.as_u32)
+ ip_update_adjacency (lm, *ai + i, &adj);
+ }
+ vec_free(adjs);
+ }
+ else
+ {
+ /* Check that new adjacency actually isn't exactly the same as
+ * what is already there. If we over-write the adjacency with
+ * exactly the same info, its technically a new adjacency with
+ * new counters, but to user it appears as counters reset.
+ */
+ if (vnet_ip_adjacency_share_compare (&adj, existing_adj) == 0) {
+ /* create new adj */
+ args.table_index_or_table_id = fib_index;
+ args.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_ADD | IP4_ROUTE_FLAG_NEIGHBOR;
+ args.dst_address = a->ip4;
+ args.dst_address_length = 32;
+ args.adj_index = ~0;
+ args.add_adj = &adj;
+ args.n_add_adj = 1;
+ ip4_add_del_route (im, &args);
+ }
+ }
- ip4_add_del_route (im, &args);
if (make_new_arp_cache_entry)
{
pool_get (am->ip4_entry_pool, e);
}
/* Update time stamp and ethernet address. */
- memcpy (e->ethernet_address, a->ethernet, sizeof (e->ethernet_address));
+ clib_memcpy (e->ethernet_address, a->ethernet, sizeof (e->ethernet_address));
e->cpu_time_last_updated = clib_cpu_time_now ();
if (is_static)
e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC;
/* Either we drop the packet or we send a reply to the sender. */
typedef enum {
ARP_INPUT_NEXT_DROP,
+ ARP_INPUT_NEXT_REPLY_TX,
ARP_INPUT_N_NEXT,
} arp_input_next_t;
e = pool_elt_at_index (am->ip4_entry_pool, index);
- memcpy (&delme.ethernet, e->ethernet_address, 6);
+ clib_memcpy (&delme.ethernet, e->ethernet_address, 6);
delme.ip4.as_u32 = e->key.ip4_address.as_u32;
vnet_arp_unset_ip4_over_ethernet (vnm, e->key.sw_if_index,
e->key.fib_index, &delme);
}
-static u32 arp_unnumbered (vlib_buffer_t * p0,
- u32 pi0,
- ethernet_header_t * eth0,
- ip_interface_address_t * ifa0)
+static void arp_unnumbered (vlib_buffer_t * p0,
+ u32 pi0,
+ ethernet_header_t * eth0,
+ ip_interface_address_t * ifa0)
{
- ethernet_arp_main_t * am = ðernet_arp_main;
vlib_main_t * vm = vlib_get_main();
vnet_main_t * vnm = vnet_get_main();
vnet_interface_main_t * vim = &vnm->interface_main;
ethernet_arp_header_t * arp0;
/* Save the dst mac address */
- memcpy(dst_mac_address, eth0->dst_address, sizeof (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 = ifa0->sw_if_index;
b0 = vlib_get_buffer (vm, buffers[i]);
/* xerox (partially built) ARP pkt */
- memcpy (b0->data, p0->data, p0->current_length + p0->current_data);
+ 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(b0)->sw_if_index[VLIB_TX] = hi->sw_if_index;
/* Fix ARP pkt src address */
- memcpy (arp0->ip4_over_ethernet[0].ethernet, hi->hw_address, 6);
+ clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, hi->hw_address, 6);
/* Build L2 encaps for this swif */
header_size = sizeof (ethernet_header_t);
}
/* Restore the original dst address, set src address */
- memcpy (eth0->dst_address, dst_mac_address, sizeof (eth0->dst_address));
- memcpy (eth0->src_address, hi->hw_address, sizeof (eth0->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)
}
}
- hi = vnet_get_sup_hw_interface (vnm, broadcast_swifs[0]);
+ /* The regular path outputs the original pkt.. */
+ vnet_buffer (p0)->sw_if_index[VLIB_TX] = broadcast_swifs[0];
vec_free (broadcast_swifs);
vec_free (buffers);
-
- /* The regular path outputs the original pkt.. */
- return vec_elt (am->arp_input_next_index_by_hw_if_index, hi->hw_if_index);
}
static uword
vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
- /* Can happen in a multi-core env. */
- if (PREDICT_FALSE(hw_if0->hw_if_index >= vec_len (am->arp_input_next_index_by_hw_if_index)))
- {
- error0 = ETHERNET_ARP_ERROR_missing_interface_address;
- goto drop2;
- }
-
- next0 = vec_elt (am->arp_input_next_index_by_hw_if_index, hw_if0->hw_if_index);
+ /* Send reply back through input interface */
+ vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
+ next0 = ARP_INPUT_NEXT_REPLY_TX;
arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
- memcpy (arp0->ip4_over_ethernet[0].ethernet, hw_if0->hw_address, 6);
+ clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, hw_if0->hw_address, 6);
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);
- memcpy (eth0->dst_address, eth0->src_address, 6);
- memcpy (eth0->src_address, hw_if0->hw_address, 6);
+ clib_memcpy (eth0->dst_address, eth0->src_address, 6);
+ clib_memcpy (eth0->src_address, hw_if0->hw_address, 6);
/* Figure out how much to rewind current data from adjacency. */
if (ifa0)
goto drop2;
}
if (is_unnum0)
- next0 = arp_unnumbered (p0, pi0, eth0, ifa0);
+ arp_unnumbered (p0, pi0, eth0, ifa0);
else
vlib_buffer_advance (p0, -adj0->rewrite_header.data_bytes);
}
.n_next_nodes = ARP_INPUT_N_NEXT,
.next_nodes = {
[ARP_INPUT_NEXT_DROP] = "error-drop",
+ [ARP_INPUT_NEXT_REPLY_TX] = "interface-output",
},
.format_buffer = format_ethernet_arp_header,
.format_trace = format_ethernet_arp_input_trace,
};
-clib_error_t *
-ethernet_arp_hw_interface_link_up_down (vnet_main_t * vnm,
- u32 hw_if_index,
- u32 flags)
-{
- ethernet_arp_main_t * am = ðernet_arp_main;
- vnet_hw_interface_t * hw_if;
-
- hw_if = vnet_get_hw_interface (vnm, hw_if_index);
-
- /* Fill in lookup tables with default table (0). */
- vec_validate_init_empty (am->arp_input_next_index_by_hw_if_index, hw_if_index, ~0);
- am->arp_input_next_index_by_hw_if_index[hw_if_index]
- = vlib_node_add_next (vnm->vlib_main, arp_input_node.index, hw_if->output_node_index);
-
- return 0;
-}
-
-VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (ethernet_arp_hw_interface_link_up_down);
-
static int
ip4_arp_entry_sort (void *a1, void *a2)
{
es = 0;
pool_foreach (e, am->ip4_entry_pool, ({ vec_add1 (es, e[0]); }));
- vec_sort_with_function (es, ip4_arp_entry_sort);
- vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, 0);
- vec_foreach (e, es) {
- if (sw_if_index != ~0 && e->key.sw_if_index != sw_if_index)
- continue;
- vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, e);
- }
- vec_free (es);
+ if ( es )
+ {
+ vec_sort_with_function (es, ip4_arp_entry_sort);
+ vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, 0);
+ vec_foreach (e, es) {
+ if (sw_if_index != ~0 && e->key.sw_if_index != sw_if_index)
+ continue;
+ vlib_cli_output (vm, "%U", format_ethernet_arp_ip4_entry, vnm, e);
+ }
+ vec_free (es);
+ }
if (vec_len (am->proxy_arps))
{
return 0;
}
+static void
+arp_ip4_entry_del_adj(ethernet_arp_ip4_entry_t *e, u32 adj_index)
+{
+ int done = 0;
+ int i;
+
+ while (!done)
+ {
+ vec_foreach_index(i, e->adjacencies)
+ if (vec_elt(e->adjacencies, i) == adj_index)
+ {
+ vec_del1(e->adjacencies, i);
+ continue;
+ }
+ done = 1;
+ }
+}
+
+static void
+arp_ip4_entry_add_adj(ethernet_arp_ip4_entry_t *e, u32 adj_index)
+{
+ int i;
+ vec_foreach_index(i, e->adjacencies)
+ if (vec_elt(e->adjacencies, i) == adj_index)
+ return;
+ vec_add1(e->adjacencies, adj_index);
+}
+
+static void
+arp_add_del_adj_cb (struct ip_lookup_main_t * lm,
+ u32 adj_index,
+ ip_adjacency_t * adj,
+ u32 is_del)
+{
+ ethernet_arp_main_t * am = ðernet_arp_main;
+ ip4_main_t * im = &ip4_main;
+ ethernet_arp_ip4_key_t k;
+ ethernet_arp_ip4_entry_t * e = 0;
+ uword * p;
+ u32 ai;
+
+ for(ai = adj->heap_handle; ai < adj->heap_handle + adj->n_adj ; ai++)
+ {
+ adj = ip_get_adjacency (lm, ai);
+ if (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP && adj->arp.next_hop.ip4.as_u32)
+ {
+ k.sw_if_index = adj->rewrite_header.sw_if_index;
+ k.ip4_address.as_u32 = adj->arp.next_hop.ip4.as_u32;
+ k.fib_index = im->fib_index_by_sw_if_index[adj->rewrite_header.sw_if_index];
+ p = mhash_get (&am->ip4_entry_by_key, &k);
+ if (p)
+ e = pool_elt_at_index (am->ip4_entry_pool, p[0]);
+ }
+ else
+ continue;
+
+ if (is_del)
+ {
+ if (!e)
+ clib_warning("Adjacency contains unknown ARP next hop %U (del)",
+ format_ip46_address, &adj->arp.next_hop, IP46_TYPE_IP4);
+ else
+ arp_ip4_entry_del_adj(e, adj->heap_handle);
+ }
+ else /* add */
+ {
+ if (!e)
+ clib_warning("Adjacency contains unknown ARP next hop %U (add)",
+ format_ip46_address, &adj->arp.next_hop, IP46_TYPE_IP4);
+ else
+ arp_ip4_entry_add_adj(e, adj->heap_handle);
+ }
+ }
+}
+
static clib_error_t * ethernet_arp_init (vlib_main_t * vm)
{
ethernet_arp_main_t * am = ðernet_arp_main;
pg_node_t * pn;
+ clib_error_t * error;
+ ip4_main_t * im = &ip4_main;
+ ip_lookup_main_t * lm = &im->lookup_main;
+
+ if ((error = vlib_call_init_function (vm, ethernet_init)))
+ return error;
ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, arp_input_node.index);
foreach_ethernet_arp_error
#undef _
}
-
+
+ ip_register_add_del_adjacency_callback(lm, arp_add_del_adj_cb);
+
return 0;
}
args.sw_if_index = sw_if_index;
args.fib_index = fib_index;
args.is_remove = 1;
- memcpy (&args.a, a, sizeof (*a));
+ clib_memcpy (&args.a, a, sizeof (*a));
vl_api_rpc_call_main_thread (set_ip4_over_ethernet_rpc_callback,
(u8 *) &args, sizeof (args));
return 0;
}
+u32
+vnet_arp_glean_add(u32 fib_index, void * next_hop_arg)
+{
+ ethernet_arp_main_t * am = ðernet_arp_main;
+ ip4_main_t * im = &ip4_main;
+ ip_lookup_main_t * lm = &im->lookup_main;
+ ip4_address_t * next_hop = next_hop_arg;
+ ip_adjacency_t add_adj, *adj;
+ ip4_add_del_route_args_t args;
+ ethernet_arp_ip4_entry_t * e;
+ ethernet_arp_ip4_key_t k;
+ u32 adj_index;
+
+ adj_index = ip4_fib_lookup_with_table(im, fib_index, next_hop, 0);
+ adj = ip_get_adjacency(lm, adj_index);
+
+ if (!adj || adj->lookup_next_index != IP_LOOKUP_NEXT_ARP)
+ return ~0;
+
+ if (adj->arp.next_hop.ip4.as_u32 != 0)
+ return adj_index;
+
+ k.sw_if_index = adj->rewrite_header.sw_if_index;
+ k.fib_index = fib_index;
+ k.ip4_address.as_u32 = next_hop->as_u32;
+
+ if (mhash_get (&am->ip4_entry_by_key, &k))
+ return adj_index;
+
+ pool_get (am->ip4_entry_pool, e);
+ mhash_set (&am->ip4_entry_by_key, &k, e - am->ip4_entry_pool, /* old value */ 0);
+ e->key = k;
+ e->cpu_time_last_updated = clib_cpu_time_now ();
+ e->flags = ETHERNET_ARP_IP4_ENTRY_FLAG_GLEAN;
+
+ memset(&args, 0, sizeof(args));
+ clib_memcpy(&add_adj, adj, sizeof(add_adj));
+ ip46_address_set_ip4(&add_adj.arp.next_hop, next_hop); /* install neighbor /32 route */
+ args.table_index_or_table_id = fib_index;
+ args.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_ADD| IP4_ROUTE_FLAG_NEIGHBOR;
+ args.dst_address.as_u32 = next_hop->as_u32;
+ args.dst_address_length = 32;
+ args.adj_index = ~0;
+ args.add_adj = &add_adj;
+ args.n_add_adj = 1;
+ ip4_add_del_route (im, &args);
+ return ip4_fib_lookup_with_table (im, fib_index, next_hop, 0);
+}
+
static clib_error_t *
ip_arp_add_del_command_fn (vlib_main_t * vm,
unformat_input_t * input,
VLIB_CLI_COMMAND (ip_arp_add_del_command, static) = {
.path = "set ip arp",
- .short_help = "set ip arp [del] <intfc> <ip-address> <mac-address>",
+ .short_help = "set ip arp [del] <intfc> <ip-address> <mac-address> [static] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
.function = ip_arp_add_del_command_fn,
};
{
u8 *t0 = vlib_add_trace (
vm, node, p0, sizeof(ethernet_arp_input_trace_t));
- memcpy (t0, l3h0, sizeof(ethernet_arp_input_trace_t));
+ clib_memcpy (t0, l3h0, sizeof(ethernet_arp_input_trace_t));
}
if (PREDICT_FALSE (
ip0 = arp0->ip4_over_ethernet[1].ip4.as_u32;
bd_index0 = vnet_buffer(p0)->l2.bd_index;
if (PREDICT_FALSE (
- (bd_index0 != last_bd_index) || (last_bd_index == ~0)))
+ (bd_index0 != last_bd_index) || (last_bd_index == (u16) ~0)))
{
last_bd_index = bd_index0;
last_bd_config = vec_elt_at_index(l2im->bd_configs, bd_index0);
arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
arp0->ip4_over_ethernet[0].ip4.as_u32 = ip0;
- memcpy (arp0->ip4_over_ethernet[0].ethernet, macp0, 6);
- memcpy (eth0->dst_address, eth0->src_address, 6);
- memcpy (eth0->src_address, macp0, 6);
+ clib_memcpy (arp0->ip4_over_ethernet[0].ethernet, macp0, 6);
+ clib_memcpy (eth0->dst_address, eth0->src_address, 6);
+ clib_memcpy (eth0->src_address, macp0, 6);
n_replies_sent += 1;
// For BVI, need to use l2-fwd node to send ARP reply as