diff options
author | Pierre Pfister <ppfister@cisco.com> | 2016-04-25 14:15:15 +0100 |
---|---|---|
committer | Damjan Marion <damarion@cisco.com> | 2016-04-26 14:09:00 +0000 |
commit | 1dabaafcebb02699cae1ebd2b58e34dfe6b0f064 (patch) | |
tree | f07da13dee800ebbbdab85233249289c38c89203 /vnet/vnet/ip/ip6_neighbor.c | |
parent | 0371807f7b1213ee474f8850dc85ae5dd0a24532 (diff) |
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 <ppfister@cisco.com>
Diffstat (limited to 'vnet/vnet/ip/ip6_neighbor.c')
-rw-r--r-- | vnet/vnet/ip/ip6_neighbor.c | 275 |
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; } |