From 1dabaafcebb02699cae1ebd2b58e34dfe6b0f064 Mon Sep 17 00:00:00 2001 From: Pierre Pfister Date: Mon, 25 Apr 2016 14:15:15 +0100 Subject: [PATCH] Port glean neighbor entry support to IPv6 This patch is more or less a port of I71f3ba0c8192 to IPv6. In practice it allows creating a route via a neighbor which is not resolved yet. It also adds static flag to IPv6 neighbor entries. And as Damjan suggested, it formalizes ip46_address_t by using the IPv4 embedded IPv6 address format. Change-Id: Ifa7328a03380ea4ff118b7ca4897b4ab23a3e57c Signed-off-by: Pierre Pfister --- vnet/vnet/ethernet/arp.c | 11 +- vnet/vnet/ip/format.c | 10 -- vnet/vnet/ip/format.h | 9 ++ vnet/vnet/ip/ip.h | 5 - vnet/vnet/ip/ip6.h | 6 +- vnet/vnet/ip/ip6_format.c | 25 ++++ vnet/vnet/ip/ip6_forward.c | 49 +++++++- vnet/vnet/ip/ip6_neighbor.c | 275 +++++++++++++++++++++++++++++++++++++------- vnet/vnet/ip/ip6_packet.h | 14 +++ vnet/vnet/ip/lookup.c | 6 +- vnet/vnet/ip/lookup.h | 5 +- vpp/api/api.c | 2 +- 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] ", + .short_help = "set ip arp [del] [static] [count ] [fib-id ] [proxy - ]", .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 -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] ", + .short_help = "set ip6 neighbor [del] [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 +#include + 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 #include #include +#include /* 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), -- 2.16.6