aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--vnet/vnet/ethernet/arp.c11
-rw-r--r--vnet/vnet/ip/format.c10
-rw-r--r--vnet/vnet/ip/format.h9
-rw-r--r--vnet/vnet/ip/ip.h5
-rw-r--r--vnet/vnet/ip/ip6.h6
-rw-r--r--vnet/vnet/ip/ip6_format.c25
-rw-r--r--vnet/vnet/ip/ip6_forward.c49
-rw-r--r--vnet/vnet/ip/ip6_neighbor.c275
-rw-r--r--vnet/vnet/ip/ip6_packet.h14
-rw-r--r--vnet/vnet/ip/lookup.c6
-rw-r--r--vnet/vnet/ip/lookup.h5
-rw-r--r--vpp/api/api.c2
12 files changed, 343 insertions, 74 deletions
diff --git a/vnet/vnet/ethernet/arp.c b/vnet/vnet/ethernet/arp.c
index 220d0d2a0ba..aa37f25fb2d 100644
--- a/vnet/vnet/ethernet/arp.c
+++ b/vnet/vnet/ethernet/arp.c
@@ -390,7 +390,8 @@ vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm,
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;
}
@@ -1313,7 +1314,7 @@ arp_add_del_adj_cb (struct ip_lookup_main_t * lm,
{
if (!e)
clib_warning("Adjacency contains unknown ARP next hop %U (del)",
- format_ip4_address, &adj->arp.next_hop);
+ format_ip46_address, &adj->arp.next_hop);
else
arp_ip4_entry_del_adj(e, adj->heap_handle);
}
@@ -1321,7 +1322,7 @@ arp_add_del_adj_cb (struct ip_lookup_main_t * lm,
{
if (!e)
clib_warning("Adjacency contains unknown ARP next hop %U (add)",
- format_ip4_address, &adj->arp.next_hop);
+ format_ip46_address, &adj->arp.next_hop);
else
arp_ip4_entry_add_adj(e, adj->heap_handle);
}
@@ -1601,7 +1602,7 @@ vnet_arp_glean_add(u32 fib_index, void * next_hop_arg)
memset(&args, 0, sizeof(args));
clib_memcpy(&add_adj, adj, sizeof(add_adj));
- add_adj.arp.next_hop.ip4.as_u32 = next_hop->as_u32; /* install neighbor /32 route */
+ 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;
@@ -1714,7 +1715,7 @@ ip_arp_add_del_command_fn (vlib_main_t * vm,
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,
};
diff --git a/vnet/vnet/ip/format.c b/vnet/vnet/ip/format.c
index 9dda4c5e10b..0061d7ee414 100644
--- a/vnet/vnet/ip/format.c
+++ b/vnet/vnet/ip/format.c
@@ -107,13 +107,3 @@ uword unformat_tcp_udp_port (unformat_input_t * input, va_list * args)
*result = port;
return 1;
}
-
-uword unformat_ip46_address (unformat_input_t * input, va_list * args)
-{
- ip46_address_t * a = va_arg (*args, ip46_address_t *);
- u32 is_ip6 = va_arg (*args, u32);
- if (is_ip6)
- return unformat_user (input, unformat_ip6_address, &a->ip6);
- else
- return unformat_user (input, unformat_ip4_address, &a->ip4);
-}
diff --git a/vnet/vnet/ip/format.h b/vnet/vnet/ip/format.h
index 511a9346bf6..4d73d6b1bf2 100644
--- a/vnet/vnet/ip/format.h
+++ b/vnet/vnet/ip/format.h
@@ -51,6 +51,15 @@ unformat_function_t unformat_tcp_udp_port;
format_function_t format_ip_adjacency;
format_function_t format_ip_adjacency_packet_data;
+format_function_t format_ip46_address;
+
+typedef enum {
+ IP46_TYPE_ANY,
+ IP46_TYPE_IP4,
+ IP46_TYPE_IP6
+} ip46_type_t;
+/* unformat_ip46_address expects arguments (ip46_address_t *, ip46_type_t)
+ * The type argument is used to enforce a particular IP version. */
unformat_function_t unformat_ip46_address;
/* IP4 */
diff --git a/vnet/vnet/ip/ip.h b/vnet/vnet/ip/ip.h
index de46ad38c5f..45062cf7b4d 100644
--- a/vnet/vnet/ip/ip.h
+++ b/vnet/vnet/ip/ip.h
@@ -69,11 +69,6 @@
#include <vnet/classify/vnet_classify.h>
-typedef union {
- ip4_address_t ip4;
- ip6_address_t ip6;
-} ip46_address_t;
-
/* Per protocol info. */
typedef struct {
/* Protocol name (also used as hash key). */
diff --git a/vnet/vnet/ip/ip6.h b/vnet/vnet/ip/ip6.h
index ff65d3ae9fe..b1043595c29 100644
--- a/vnet/vnet/ip/ip6.h
+++ b/vnet/vnet/ip/ip6.h
@@ -373,6 +373,9 @@ void ip6_adjacency_set_interface_route (vnet_main_t * vnm,
u32 sw_if_index,
u32 if_address_index);
+u32
+vnet_ip6_neighbor_glean_add(u32 fib_index, void * next_hop_arg);
+
clib_error_t *
ip6_probe_neighbor (vlib_main_t * vm, ip6_address_t * dst, u32 sw_if_index);
@@ -395,7 +398,8 @@ 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);
int
vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm,
u32 sw_if_index,
diff --git a/vnet/vnet/ip/ip6_format.c b/vnet/vnet/ip/ip6_format.c
index 1a2810e16ec..ad834f4db8a 100644
--- a/vnet/vnet/ip/ip6_format.c
+++ b/vnet/vnet/ip/ip6_format.c
@@ -320,3 +320,28 @@ uword unformat_ip6_header (unformat_input_t * input, va_list * args)
return 1;
}
+
+/* Parse an IP46 address. */
+uword unformat_ip46_address (unformat_input_t * input, va_list * args)
+{
+ ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
+ ip46_type_t type = va_arg (*args, ip46_type_t);
+ if ((type != IP46_TYPE_IP6) &&
+ unformat(input, "%U", unformat_ip4_address, &ip46->ip4)) {
+ ip46_address_mask_ip4(ip46);
+ return 1;
+ } else if ((type != IP46_TYPE_IP4) &&
+ unformat(input, "%U", unformat_ip6_address, &ip46->ip6)) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Format an IP46 address. */
+u8 * format_ip46_address (u8 * s, va_list * args)
+{
+ ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
+ return ip46_address_is_ip4(ip46)?
+ format(s, "%U", format_ip4_address, &ip46->ip4):
+ format(s, "%U", format_ip6_address, &ip46->ip6);
+}
diff --git a/vnet/vnet/ip/ip6_forward.c b/vnet/vnet/ip/ip6_forward.c
index c8b9df01a88..d001ebb25a8 100644
--- a/vnet/vnet/ip/ip6_forward.c
+++ b/vnet/vnet/ip/ip6_forward.c
@@ -353,14 +353,29 @@ ip6_add_del_route_next_hop (ip6_main_t * im,
kv.key[2] = ((u64)((fib - im->fibs))<<32) | 128;
if (BV(clib_bihash_search)(&im->ip6_lookup_table, &kv, &value) < 0)
+ {
+ ip_adjacency_t * adj;
+ nh_adj_index = ip6_fib_lookup_with_table (im, fib_index, next_hop);
+ adj = ip_get_adjacency (lm, nh_adj_index);
+ /* if ND interface adjacencty is present, we need to
+ install ND adjaceny for specific next hop */
+ if (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP &&
+ adj->arp.next_hop.ip6.as_u64[0] == 0 &&
+ adj->arp.next_hop.ip6.as_u64[1] == 0)
+ {
+ nh_adj_index = vnet_ip6_neighbor_glean_add(fib_index, next_hop);
+ }
+ else
{
vnm->api_errno = VNET_API_ERROR_UNKNOWN_DESTINATION;
error = clib_error_return (0, "next-hop %U/128 not in FIB",
format_ip6_address, next_hop);
goto done;
}
-
- nh_adj_index = value.value;
+ }
+ else
+ nh_adj_index = value.value;
+
}
}
else
@@ -424,6 +439,28 @@ ip6_add_del_route_next_hop (ip6_main_t * im,
goto done;
}
+ /* Destination is not known and default weight is set so add route
+ to existing non-multipath adjacency */
+ if (dst_adj_index == ~0 && next_hop_weight == 1 && next_hop_sw_if_index == ~0)
+ {
+ /* create new adjacency */
+ ip6_add_del_route_args_t a;
+ a.table_index_or_table_id = fib_index;
+ a.flags = ((is_del ? IP6_ROUTE_FLAG_DEL : IP6_ROUTE_FLAG_ADD)
+ | IP6_ROUTE_FLAG_FIB_INDEX
+ | IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY
+ | (flags & (IP6_ROUTE_FLAG_NO_REDISTRIBUTE
+ | IP6_ROUTE_FLAG_NOT_LAST_IN_GROUP)));
+ a.dst_address = dst_address[0];
+ a.dst_address_length = dst_address_length;
+ a.adj_index = nh_adj_index;
+ a.add_adj = 0;
+ a.n_add_adj = 0;
+
+ ip6_add_del_route (im, &a);
+ goto done;
+ }
+
old_mp_adj_index = dst_adj ? dst_adj->heap_handle : ~0;
if (! ip_multipath_adjacency_add_del_next_hop
@@ -872,6 +909,8 @@ void ip6_adjacency_set_interface_route (vnet_main_t * vnm,
n = IP_LOOKUP_NEXT_ARP;
node_index = ip6_discover_neighbor_node.index;
adj->if_address_index = if_address_index;
+ adj->arp.next_hop.ip6.as_u64[0] = 0;
+ adj->arp.next_hop.ip6.as_u64[1] = 0;
}
else
{
@@ -1840,6 +1879,12 @@ ip6_discover_neighbor (vlib_main_t * vm,
adj0 = ip_get_adjacency (lm, adj_index0);
+ if (adj0->arp.next_hop.ip6.as_u64[0] ||
+ adj0->arp.next_hop.ip6.as_u64[1]) {
+ ip0->dst_address.as_u64[0] = adj0->arp.next_hop.ip6.as_u64[0];
+ ip0->dst_address.as_u64[1] = adj0->arp.next_hop.ip6.as_u64[1];
+ }
+
a0 = hash_seeds[0];
b0 = hash_seeds[1];
c0 = hash_seeds[2];
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;
}
diff --git a/vnet/vnet/ip/ip6_packet.h b/vnet/vnet/ip/ip6_packet.h
index 9a52cf72586..7fbcab14eac 100644
--- a/vnet/vnet/ip/ip6_packet.h
+++ b/vnet/vnet/ip/ip6_packet.h
@@ -40,6 +40,9 @@
#ifndef included_ip6_packet_h
#define included_ip6_packet_h
+#include <vnet/ip/tcp_packet.h>
+#include <vnet/ip/ip4_packet.h>
+
typedef union {
u8 as_u8[16];
u16 as_u16[8];
@@ -55,6 +58,17 @@ typedef CLIB_PACKED (struct {
u32 fib_index;
}) ip6_address_fib_t;
+typedef CLIB_PACKED (union {
+ struct {
+ u32 pad[3];
+ ip4_address_t ip4;
+ };
+ ip6_address_t ip6;
+}) ip46_address_t;
+#define ip46_address_is_ip4(ip46) (((ip46)->pad[0] | (ip46)->pad[1] | (ip46)->pad[2]) == 0)
+#define ip46_address_mask_ip4(ip46) ((ip46)->pad[0] = (ip46)->pad[1] = (ip46)->pad[2] = 0)
+#define ip46_address_set_ip4(ip46, ip) (ip46_address_mask_ip4(ip46), (ip46)->ip4 = (ip)[0])
+
always_inline void
ip6_addr_fib_init (ip6_address_fib_t * addr_fib, ip6_address_t * address,
u32 fib_index)
diff --git a/vnet/vnet/ip/lookup.c b/vnet/vnet/ip/lookup.c
index 9e3cdc0c12d..df14a5fefe3 100644
--- a/vnet/vnet/ip/lookup.c
+++ b/vnet/vnet/ip/lookup.c
@@ -998,8 +998,8 @@ u8 * format_ip_adjacency (u8 * s, va_list * args)
case IP_LOOKUP_NEXT_ARP:
if (adj->if_address_index != ~0)
s = format (s, " %U", format_ip_interface_address, lm, adj->if_address_index);
- if (adj->arp.next_hop.ip4.as_u32)
- s = format (s, " via %U", format_ip4_address, &adj->arp.next_hop.ip4.as_u32);
+ if (adj->arp.next_hop.ip6.as_u64[0] || adj->arp.next_hop.ip6.as_u64[1])
+ s = format (s, " via %U", format_ip46_address, &adj->arp.next_hop);
break;
case IP_LOOKUP_NEXT_LOCAL:
if (adj->if_address_index != ~0)
@@ -1091,7 +1091,7 @@ static uword unformat_ip_adjacency (unformat_input_t * input, va_list * args)
if (unformat (input, "arp %U %U",
unformat_vnet_sw_interface, vnm, &sw_if_index,
- unformat_ip46_address, &a46, is_ip6))
+ unformat_ip46_address, &a46, is_ip6?IP46_TYPE_IP6:IP46_TYPE_IP4))
{
ip_lookup_main_t * lm = is_ip6 ? &ip6_main.lookup_main : &ip4_main.lookup_main;
ip_adjacency_t * a_adj;
diff --git a/vnet/vnet/ip/lookup.h b/vnet/vnet/ip/lookup.h
index 23cb02d79f4..62de2105133 100644
--- a/vnet/vnet/ip/lookup.h
+++ b/vnet/vnet/ip/lookup.h
@@ -43,6 +43,7 @@
#include <vnet/vnet.h>
#include <vlib/buffer.h>
#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
/* Next index stored in adjacency. */
typedef enum {
@@ -168,9 +169,7 @@ typedef struct {
union {
/* IP_LOOKUP_NEXT_ARP only */
struct {
- union {
- ip4_address_t ip4;
- } next_hop;
+ ip46_address_t next_hop;
u32 next_adj_index_with_same_next_hop;
} arp;
/* IP_LOOKUP_NEXT_CLASSIFY only */
diff --git a/vpp/api/api.c b/vpp/api/api.c
index ea766938b34..829a70fd903 100644
--- a/vpp/api/api.c
+++ b/vpp/api/api.c
@@ -2213,7 +2213,7 @@ vl_api_ip_neighbor_add_del_t_handler (vl_api_ip_neighbor_add_del_t *mp, vlib_mai
rv = vnet_set_ip6_ethernet_neighbor
(vm, ntohl(mp->sw_if_index),
(ip6_address_t *)(mp->dst_address),
- mp->mac_address, sizeof (mp->mac_address));
+ mp->mac_address, sizeof (mp->mac_address), mp->is_static);
else
rv = vnet_unset_ip6_ethernet_neighbor
(vm, ntohl(mp->sw_if_index),