summaryrefslogtreecommitdiffstats
path: root/vnet/vnet/ip/ip6_neighbor.c
diff options
context:
space:
mode:
Diffstat (limited to 'vnet/vnet/ip/ip6_neighbor.c')
-rw-r--r--vnet/vnet/ip/ip6_neighbor.c275
1 files changed, 231 insertions, 44 deletions
diff --git a/vnet/vnet/ip/ip6_neighbor.c b/vnet/vnet/ip/ip6_neighbor.c
index acb1d8dcf74..329cc6d7d67 100644
--- a/vnet/vnet/ip/ip6_neighbor.c
+++ b/vnet/vnet/ip/ip6_neighbor.c
@@ -36,7 +36,11 @@ typedef struct {
typedef struct {
ip6_neighbor_key_t key;
u8 link_layer_address[8];
+ u16 flags;
+#define IP6_NEIGHBOR_FLAG_STATIC (1 << 0)
+#define IP6_NEIGHBOR_FLAG_GLEAN (2 << 0)
u64 cpu_time_last_updated;
+ u32 *adjacencies;
} ip6_neighbor_t;
/* advertised prefix option */
@@ -200,17 +204,26 @@ static u8 * format_ip6_neighbor_ip6_entry (u8 * s, va_list * va)
ip6_neighbor_t * n = va_arg (*va, ip6_neighbor_t *);
vnet_main_t * vnm = vnet_get_main();
vnet_sw_interface_t * si;
+ u8 * flags = 0;
if (! n)
- return format (s, "%=12s%=20s%=20s%=40s", "Time", "Address", "Link layer", "Interface");
+ return format (s, "%=12s%=20s%=6s%=20s%=40s", "Time", "Address", "Flags", "Link layer", "Interface");
+
+ if (n->flags & IP6_NEIGHBOR_FLAG_GLEAN)
+ flags = format(flags, "G");
+
+ if (n->flags & IP6_NEIGHBOR_FLAG_STATIC)
+ flags = format(flags, "S");
si = vnet_get_sw_interface (vnm, n->key.sw_if_index);
- s = format (s, "%=12U%=20U%=20U%=40U",
+ s = format (s, "%=12U%=20U%=6s%=20U%=40U",
format_vlib_cpu_time, vm, n->cpu_time_last_updated,
format_ip6_address, &n->key.ip6_address,
+ flags ? (char *)flags : "",
format_ethernet_address, n->link_layer_address,
format_vnet_sw_interface_name, vnm, si);
+ vec_free(flags);
return s;
}
@@ -278,7 +291,7 @@ static void unset_random_neighbor_entry (void)
typedef struct {
u8 is_add;
- u8 pad;
+ u8 is_static;
u8 link_layer_address[6];
u32 sw_if_index;
ip6_address_t addr;
@@ -293,13 +306,14 @@ static void set_unset_ip6_neighbor_rpc
u32 sw_if_index,
ip6_address_t * a,
u8 *link_layer_addreess,
- int is_add)
+ int is_add, int is_static)
{
ip6_neighbor_set_unset_rpc_args_t args;
void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
args.sw_if_index = sw_if_index;
args.is_add = is_add;
+ args.is_static = is_static;
clib_memcpy (&args.addr, a, sizeof (*a));
clib_memcpy (args.link_layer_address, link_layer_addreess, 6);
@@ -313,22 +327,27 @@ vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm,
u32 sw_if_index,
ip6_address_t * a,
u8 * link_layer_address,
- uword n_bytes_link_layer_address)
+ uword n_bytes_link_layer_address,
+ int is_static)
{
vnet_main_t * vnm = vnet_get_main();
ip6_neighbor_main_t * nm = &ip6_neighbor_main;
ip6_neighbor_key_t k;
- ip6_neighbor_t * n;
+ ip6_neighbor_t * n = 0;
ip6_main_t * im = &ip6_main;
+ ip_lookup_main_t * lm = &im->lookup_main;
+ int make_new_nd_cache_entry=1;
uword * p;
u32 next_index;
+ u32 adj_index;
+ ip_adjacency_t *existing_adj;
pending_resolution_t * pr;
#if DPDK > 0
if (os_get_cpu_number())
{
set_unset_ip6_neighbor_rpc (vm, sw_if_index, a, link_layer_address,
- 1 /* set new neighbor */);
+ 1 /* set new neighbor */, is_static);
return 0;
}
#endif
@@ -340,44 +359,80 @@ vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm,
vlib_worker_thread_barrier_sync (vm);
p = mhash_get (&nm->neighbor_index_by_key, &k);
- if (p)
+ if (p) {
n = pool_elt_at_index (nm->neighbor_pool, p[0]);
- else
+ /* Refuse to over-write static neighbor entry. */
+ if (!is_static &&
+ (n->flags & IP6_NEIGHBOR_FLAG_STATIC))
+ return -2;
+ make_new_nd_cache_entry = 0;
+ }
+
+ /* Note: always install the route. It might have been deleted */
+ ip6_add_del_route_args_t args;
+ ip_adjacency_t adj;
+
+ memset (&adj, 0, sizeof(adj));
+ adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
+ adj.explicit_fib_index = ~0;
+
+ vnet_rewrite_for_sw_interface
+ (vnm,
+ VNET_L3_PACKET_TYPE_IP6,
+ sw_if_index,
+ ip6_rewrite_node.index,
+ link_layer_address,
+ &adj.rewrite_header,
+ sizeof (adj.rewrite_data));
+
+ /* result of this lookup should be next-hop adjacency */
+ adj_index = ip6_fib_lookup_with_table (im, im->fib_index_by_sw_if_index[sw_if_index], a);
+ existing_adj = ip_get_adjacency(lm, adj_index);
+
+ if (existing_adj->lookup_next_index == IP_LOOKUP_NEXT_ARP &&
+ existing_adj->arp.next_hop.ip6.as_u64[0] == a->as_u64[0] &&
+ existing_adj->arp.next_hop.ip6.as_u64[1] == a->as_u64[1])
+ {
+ u32 * ai;
+ u32 * adjs = vec_dup(n->adjacencies);
+ /* Update all adj assigned to this arp entry */
+ vec_foreach(ai, adjs)
{
- ip6_add_del_route_args_t args;
- ip_adjacency_t adj;
-
- memset (&adj, 0, sizeof(adj));
- adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
- adj.explicit_fib_index = ~0;
-
- vnet_rewrite_for_sw_interface
- (vnm,
- VNET_L3_PACKET_TYPE_IP6,
- sw_if_index,
- ip6_rewrite_node.index,
- link_layer_address,
- &adj.rewrite_header,
- sizeof (adj.rewrite_data));
-
- args.table_index_or_table_id = im->fib_index_by_sw_if_index[sw_if_index];
- args.flags = IP6_ROUTE_FLAG_FIB_INDEX | IP6_ROUTE_FLAG_ADD | IP6_ROUTE_FLAG_NEIGHBOR;
- args.dst_address = a[0];
- args.dst_address_length = 128;
- args.adj_index = ~0;
- args.add_adj = &adj;
- args.n_add_adj = 1;
-
- ip6_add_del_route (im, &args);
- pool_get (nm->neighbor_pool, n);
- mhash_set (&nm->neighbor_index_by_key, &k, n - nm->neighbor_pool,
- /* old value */ 0);
- n->key = k;
+ 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.ip6.as_u64[0] == a->as_u64[0] &&
+ uadj[i].arp.next_hop.ip6.as_u64[1] == a->as_u64[1])
+ ip_update_adjacency (lm, *ai + i, &adj);
}
+ vec_free(adjs);
+ }
+ else
+ {
+ /* create new adj */
+ args.table_index_or_table_id = im->fib_index_by_sw_if_index[sw_if_index];
+ args.flags = IP6_ROUTE_FLAG_FIB_INDEX | IP6_ROUTE_FLAG_ADD | IP6_ROUTE_FLAG_NEIGHBOR;
+ args.dst_address = a[0];
+ args.dst_address_length = 128;
+ args.adj_index = ~0;
+ args.add_adj = &adj;
+ args.n_add_adj = 1;
+ ip6_add_del_route (im, &args);
+ }
+
+ if (make_new_nd_cache_entry) {
+ pool_get (nm->neighbor_pool, n);
+ mhash_set (&nm->neighbor_index_by_key, &k, n - nm->neighbor_pool,
+ /* old value */ 0);
+ n->key = k;
+ }
/* Update time stamp and ethernet address. */
clib_memcpy (n->link_layer_address, link_layer_address, n_bytes_link_layer_address);
n->cpu_time_last_updated = clib_cpu_time_now ();
+ if (is_static)
+ n->flags |= IP6_NEIGHBOR_FLAG_STATIC;
/* Customer(s) waiting for this address to be resolved? */
p = mhash_get (&nm->pending_resolutions_by_address, a);
@@ -422,7 +477,7 @@ vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm,
if (os_get_cpu_number())
{
set_unset_ip6_neighbor_rpc (vm, sw_if_index, a, link_layer_address,
- 0 /* unset */);
+ 0 /* unset */, 0);
return 0;
}
#endif
@@ -458,6 +513,56 @@ vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm,
return rv;
}
+
+u32
+vnet_ip6_neighbor_glean_add(u32 fib_index, void * next_hop_arg)
+{
+ ip6_neighbor_main_t * nm = &ip6_neighbor_main;
+ ip6_main_t * im = &ip6_main;
+ ip_lookup_main_t * lm = &im->lookup_main;
+ ip6_address_t * next_hop = next_hop_arg;
+ ip_adjacency_t add_adj, *adj;
+ ip6_add_del_route_args_t args;
+ ip6_neighbor_t * n;
+ ip6_neighbor_key_t k;
+ u32 adj_index;
+
+ adj_index = ip6_fib_lookup_with_table(im, fib_index, next_hop);
+ adj = ip_get_adjacency(lm, adj_index);
+
+ if (!adj || adj->lookup_next_index != IP_LOOKUP_NEXT_ARP)
+ return ~0;
+
+ if (adj->arp.next_hop.ip6.as_u64[0] ||
+ adj->arp.next_hop.ip6.as_u64[1])
+ return adj_index;
+
+ k.sw_if_index = adj->rewrite_header.sw_if_index;
+ k.ip6_address = *next_hop;
+ k.pad = 0;
+ if (mhash_get (&nm->neighbor_index_by_key, &k))
+ return adj_index;
+
+ pool_get (nm->neighbor_pool, n);
+ mhash_set (&nm->neighbor_index_by_key, &k, n - nm->neighbor_pool, /* old value */ 0);
+ n->key = k;
+ n->cpu_time_last_updated = clib_cpu_time_now ();
+ n->flags = IP6_NEIGHBOR_FLAG_GLEAN;
+
+ memset(&args, 0, sizeof(args));
+ memcpy(&add_adj, adj, sizeof(add_adj));
+ add_adj.arp.next_hop.ip6 = *next_hop; /* install neighbor /128 route */
+ args.table_index_or_table_id = fib_index;
+ args.flags = IP6_ROUTE_FLAG_FIB_INDEX | IP6_ROUTE_FLAG_ADD | IP6_ROUTE_FLAG_NEIGHBOR;
+ args.dst_address = *next_hop;
+ args.dst_address_length = 128;
+ args.adj_index = ~0;
+ args.add_adj = &add_adj;
+ args.n_add_adj = 1;
+ ip6_add_del_route (im, &args);
+ return ip6_fib_lookup_with_table (im, fib_index, next_hop);
+}
+
#if DPDK > 0
static void ip6_neighbor_set_unset_rpc_callback
( ip6_neighbor_set_unset_rpc_args_t * a)
@@ -465,7 +570,7 @@ static void ip6_neighbor_set_unset_rpc_callback
vlib_main_t * vm = vlib_get_main();
if (a->is_add)
vnet_set_ip6_ethernet_neighbor (vm, a->sw_if_index, &a->addr,
- a->link_layer_address, 6);
+ a->link_layer_address, 6, a->is_static);
else
vnet_unset_ip6_ethernet_neighbor (vm, a->sw_if_index, &a->addr,
a->link_layer_address, 6);
@@ -530,6 +635,7 @@ set_ip6_neighbor (vlib_main_t * vm,
u8 mac_address[6];
int addr_valid = 0;
int is_del = 0;
+ int is_static = 0;
u32 sw_if_index;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
@@ -543,6 +649,8 @@ set_ip6_neighbor (vlib_main_t * vm,
else if (unformat (input, "delete") || unformat (input, "del"))
is_del = 1;
+ else if (unformat (input, "static"))
+ is_static = 1;
else
break;
}
@@ -552,7 +660,7 @@ set_ip6_neighbor (vlib_main_t * vm,
if (!is_del)
vnet_set_ip6_ethernet_neighbor (vm, sw_if_index, &addr,
- mac_address, sizeof(mac_address));
+ mac_address, sizeof(mac_address), is_static);
else
vnet_unset_ip6_ethernet_neighbor (vm, sw_if_index, &addr,
mac_address, sizeof(mac_address));
@@ -562,7 +670,7 @@ set_ip6_neighbor (vlib_main_t * vm,
VLIB_CLI_COMMAND (set_ip6_neighbor_command, static) = {
.path = "set ip6 neighbor",
.function = set_ip6_neighbor,
- .short_help = "set ip6 neighbor [del] <intfc> <ip6-address> <mac-address>",
+ .short_help = "set ip6 neighbor [del] <intfc> <ip6-address> <mac-address> [static]",
};
typedef enum {
@@ -666,7 +774,7 @@ icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm,
vnet_set_ip6_ethernet_neighbor (
vm, sw_if_index0,
is_solicitation ? &ip0->src_address : &h0->target_address,
- o0->ethernet_address, sizeof (o0->ethernet_address));
+ o0->ethernet_address, sizeof (o0->ethernet_address), 0);
}
if (is_solicitation && error0 == ICMP6_ERROR_NONE)
@@ -931,7 +1039,7 @@ icmp6_router_solicitation(vlib_main_t * vm,
vnet_set_ip6_ethernet_neighbor (vm, sw_if_index0,
&ip0->src_address,
o0->ethernet_address,
- sizeof (o0->ethernet_address));
+ sizeof (o0->ethernet_address), 0);
}
/* default is to drop */
@@ -3072,10 +3180,87 @@ clib_error_t *ip6_set_neighbor_limit (u32 neighbor_limit)
return 0;
}
+
+static void
+ip6_neighbor_entry_del_adj(ip6_neighbor_t *n, u32 adj_index)
+{
+ int done = 0;
+ int i;
+ while (!done)
+ {
+ vec_foreach_index(i, n->adjacencies)
+ if (vec_elt(n->adjacencies, i) == adj_index)
+ {
+ vec_del1(n->adjacencies, i);
+ continue;
+ }
+ done = 1;
+ }
+}
+
+static void
+ip6_neighbor_entry_add_adj(ip6_neighbor_t *n, u32 adj_index)
+{
+ int i;
+ vec_foreach_index(i, n->adjacencies)
+ if (vec_elt(n->adjacencies, i) == adj_index)
+ return;
+ vec_add1(n->adjacencies, adj_index);
+}
+
+static void
+ip6_neighbor_add_del_adj_cb (struct ip_lookup_main_t * lm,
+ u32 adj_index,
+ ip_adjacency_t * adj,
+ u32 is_del)
+{
+ ip6_neighbor_main_t * nm = &ip6_neighbor_main;
+ ip6_neighbor_key_t k;
+ ip6_neighbor_t *n = 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.ip6.as_u64[0] || adj->arp.next_hop.ip6.as_u64[1]))
+ {
+ k.sw_if_index = adj->rewrite_header.sw_if_index;
+ k.ip6_address.as_u64[0] = adj->arp.next_hop.ip6.as_u64[0];
+ k.ip6_address.as_u64[1] = adj->arp.next_hop.ip6.as_u64[1];
+ k.pad = 0;
+ p = mhash_get (&nm->neighbor_index_by_key, &k);
+ if (p)
+ n = pool_elt_at_index (nm->neighbor_pool, p[0]);
+ }
+ else
+ continue;
+
+ if (is_del)
+ {
+ if (!n)
+ clib_warning("Adjacency contains unknown ND next hop %U (del)",
+ format_ip46_address, &adj->arp.next_hop);
+ else
+ ip6_neighbor_entry_del_adj(n, adj->heap_handle);
+ }
+ else /* add */
+ {
+ if (!n)
+ clib_warning("Adjacency contains unknown ND next hop %U (add)",
+ format_ip46_address, &adj->arp.next_hop);
+ else
+ ip6_neighbor_entry_add_adj(n, adj->heap_handle);
+ }
+ }
+}
+
static clib_error_t * ip6_neighbor_init (vlib_main_t * vm)
{
ip6_neighbor_main_t * nm = &ip6_neighbor_main;
ip6_main_t * im = &ip6_main;
+ ip_lookup_main_t * lm = &im->lookup_main;
mhash_init (&nm->neighbor_index_by_key,
/* value size */ sizeof (uword),
@@ -3111,6 +3296,8 @@ static clib_error_t * ip6_neighbor_init (vlib_main_t * vm)
(im->discover_neighbor_next_index_by_hw_if_index, 32, 0 /* drop */);
#endif
+ ip_register_add_del_adjacency_callback(lm, ip6_neighbor_add_del_adj_cb);
+
return 0;
}