X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=vnet%2Fvnet%2Fip%2Fip6_neighbor.c;h=92417a4402566338e9aa45b7bae8bd8e5cc1161a;hb=3e046ea96e7e9d98a8dd67eab84031e1d71b4422;hp=2593cb46156d37b3442d0df511632874d08d289d;hpb=56faee837281c7f9c28aa40dbf0f6e4620b76be8;p=vpp.git diff --git a/vnet/vnet/ip/ip6_neighbor.c b/vnet/vnet/ip/ip6_neighbor.c index 2593cb46156..92417a44025 100644 --- a/vnet/vnet/ip/ip6_neighbor.c +++ b/vnet/vnet/ip/ip6_neighbor.c @@ -16,34 +16,26 @@ */ #include +#include #include #include #include +#include +#include +#include -#if DPDK==1 -#include -#endif - -typedef struct { - ip6_address_t ip6_address; - u32 sw_if_index; - u32 pad; -} ip6_neighbor_key_t; +/** + * @file + * @brief IPv6 Neighbor Adjacency and Neighbor Discovery. + * + * The files contains the API and CLI code for managing IPv6 neighbor + * adjacency tables and neighbor discovery logic. + */ -/* can't use sizeof link_layer_address, that's 8 */ +/* can't use sizeof link_layer_address, that's 8 */ #define ETHER_MAC_ADDR_LEN 6 -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 */ +/* advertised prefix option */ typedef struct { /* basic advertised information */ ip6_address_t prefix; @@ -121,9 +113,9 @@ typedef struct { u32 seed; u64 randomizer; int ref_count; - u32 all_nodes_adj_index; - u32 all_routers_adj_index; - u32 all_mldv2_routers_adj_index; + adj_index_t all_nodes_adj_index; + adj_index_t all_routers_adj_index; + adj_index_t all_mldv2_routers_adj_index; /* timing information */ #define DEF_MAX_RADV_INTERVAL 200 @@ -169,6 +161,9 @@ typedef struct { uword node_index; uword type_opaque; uword data; + /* Used for nd event notification only */ + void * data_callback; + u32 pid; } pending_resolution_t; @@ -180,6 +175,10 @@ typedef struct { mhash_t pending_resolutions_by_address; pending_resolution_t * pending_resolutions; + /* Mac address change notification */ + mhash_t mac_changes_by_address; + pending_resolution_t * mac_changes; + u32 * neighbor_input_next_index_by_hw_if_index; ip6_neighbor_t * neighbor_pool; @@ -197,6 +196,7 @@ typedef struct { } ip6_neighbor_main_t; static ip6_neighbor_main_t ip6_neighbor_main; +static ip6_address_t ip6a_zero; /* ip6 address 0 */ static u8 * format_ip6_neighbor_ip6_entry (u8 * s, va_list * va) { @@ -209,8 +209,8 @@ static u8 * format_ip6_neighbor_ip6_entry (u8 * s, va_list * va) if (! n) 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_DYNAMIC) + flags = format(flags, "D"); if (n->flags & IP6_NEIGHBOR_FLAG_STATIC) flags = format(flags, "S"); @@ -248,6 +248,7 @@ ip6_neighbor_sw_interface_up_down (vnet_main_t * vnm, { n = pool_elt_at_index (nm->neighbor_pool, to_delete[i]); mhash_unset (&nm->neighbor_index_by_key, &n->key, 0); + fib_table_entry_delete_index (n->fib_entry_index, FIB_SOURCE_ADJ); pool_put (nm->neighbor_pool, n); } @@ -297,7 +298,6 @@ typedef struct { ip6_address_t addr; } ip6_neighbor_set_unset_rpc_args_t; -#if DPDK > 0 static void ip6_neighbor_set_unset_rpc_callback ( ip6_neighbor_set_unset_rpc_args_t * a); @@ -320,7 +320,186 @@ static void set_unset_ip6_neighbor_rpc vl_api_rpc_call_main_thread (ip6_neighbor_set_unset_rpc_callback, (u8 *) &args, sizeof (args)); } -#endif + +static void +ip6_nbr_probe (ip_adjacency_t *adj) +{ + icmp6_neighbor_solicitation_header_t * h; + vnet_main_t * vnm = vnet_get_main(); + ip6_main_t * im = &ip6_main; + ip_interface_address_t * ia; + ip6_address_t * dst, *src; + vnet_hw_interface_t * hi; + vnet_sw_interface_t * si; + vlib_buffer_t * b; + int bogus_length; + vlib_main_t * vm; + u32 bi = 0; + + vm = vlib_get_main(); + + si = vnet_get_sw_interface(vnm, adj->rewrite_header.sw_if_index); + dst = &adj->sub_type.nbr.next_hop.ip6; + + if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)) + { + return; + } + src = ip6_interface_address_matching_destination(im, dst, + adj->rewrite_header.sw_if_index, + &ia); + if (! src) + { + return; + } + + h = vlib_packet_template_get_packet(vm, + &im->discover_neighbor_packet_template, + &bi); + + hi = vnet_get_sup_hw_interface(vnm, adj->rewrite_header.sw_if_index); + + h->ip.dst_address.as_u8[13] = dst->as_u8[13]; + h->ip.dst_address.as_u8[14] = dst->as_u8[14]; + h->ip.dst_address.as_u8[15] = dst->as_u8[15]; + h->ip.src_address = src[0]; + h->neighbor.target_address = dst[0]; + + clib_memcpy (h->link_layer_option.ethernet_address, + hi->hw_address, + vec_len(hi->hw_address)); + + h->neighbor.icmp.checksum = + ip6_tcp_udp_icmp_compute_checksum(vm, 0, &h->ip, &bogus_length); + ASSERT(bogus_length == 0); + + b = vlib_get_buffer (vm, bi); + vnet_buffer (b)->sw_if_index[VLIB_RX] = + vnet_buffer (b)->sw_if_index[VLIB_TX] = + adj->rewrite_header.sw_if_index; + + /* Add encapsulation string for software interface (e.g. ethernet header). */ + vnet_rewrite_one_header(adj[0], h, sizeof (ethernet_header_t)); + vlib_buffer_advance(b, -adj->rewrite_header.data_bytes); + + { + vlib_frame_t * f = vlib_get_frame_to_node(vm, hi->output_node_index); + u32 * to_next = vlib_frame_vector_args(f); + to_next[0] = bi; + f->n_vectors = 1; + vlib_put_frame_to_node(vm, hi->output_node_index, f); + } +} + +static void +ip6_nd_mk_complete (adj_index_t ai, ip6_neighbor_t * nbr) +{ + adj_nbr_update_rewrite (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE, + ethernet_build_rewrite (vnet_get_main (), + nbr->key.sw_if_index, + adj_get_link_type(ai), + nbr->link_layer_address)); +} + +static void +ip6_nd_mk_incomplete (adj_index_t ai, ip6_neighbor_t * nbr) +{ + adj_nbr_update_rewrite ( + ai, + ADJ_NBR_REWRITE_FLAG_INCOMPLETE, + ethernet_build_rewrite (vnet_get_main (), + nbr->key.sw_if_index, + adj_get_link_type(ai), + VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST)); +} + +#define IP6_NBR_MK_KEY(k, sw_if_index, addr) \ +{ \ + k.sw_if_index = sw_if_index; \ + k.ip6_address = *addr; \ + k.pad = 0; \ +} + +static ip6_neighbor_t * +ip6_nd_find (u32 sw_if_index, + const ip6_address_t * addr) +{ + ip6_neighbor_main_t * nm = &ip6_neighbor_main; + ip6_neighbor_t * n = NULL; + ip6_neighbor_key_t k; + uword *p; + + IP6_NBR_MK_KEY(k, sw_if_index, addr); + + p = mhash_get (&nm->neighbor_index_by_key, &k); + if (p) { + n = pool_elt_at_index (nm->neighbor_pool, p[0]); + } + + return (n); +} + +static adj_walk_rc_t +ip6_nd_mk_complete_walk (adj_index_t ai, void *ctx) +{ + ip6_neighbor_t *nbr = ctx; + + ip6_nd_mk_complete (ai, nbr); + + return (ADJ_WALK_RC_CONTINUE); +} + +static adj_walk_rc_t +ip6_nd_mk_incomplete_walk (adj_index_t ai, void *ctx) +{ + ip6_neighbor_t *nbr = ctx; + + ip6_nd_mk_incomplete (ai, nbr); + + return (ADJ_WALK_RC_CONTINUE); +} + +void +ip6_ethernet_update_adjacency (vnet_main_t * vnm, + u32 sw_if_index, + u32 ai) +{ + ip6_neighbor_t *nbr; + ip_adjacency_t *adj; + + adj = adj_get (ai); + + nbr = ip6_nd_find (sw_if_index, &adj->sub_type.nbr.next_hop.ip6); + + if (NULL != nbr) + { + adj_nbr_walk_nh6 (sw_if_index, &nbr->key.ip6_address, + ip6_nd_mk_complete_walk, nbr); + } + else + { + /* + * no matching ND entry. + * construct the rewrite required to for an ND packet, and stick + * that in the adj's pipe to smoke. + */ + adj_nbr_update_rewrite (ai, + ADJ_NBR_REWRITE_FLAG_INCOMPLETE, + ethernet_build_rewrite (vnm, + sw_if_index, + VNET_LINK_IP6, + VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST)); + + /* + * since the FIB has added this adj for a route, it makes sense it may + * want to forward traffic sometime soon. Let's send a speculative ND. + * just one. If we were to do periodically that wouldn't be bad either, + * but that's more code than i'm prepared to write at this time for + * relatively little reward. + */ + ip6_nbr_probe (adj); + } +} int vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm, @@ -330,34 +509,25 @@ vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm, 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 = 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; + pending_resolution_t * pr, * mc; -#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 */, is_static); return 0; } -#endif k.sw_if_index = sw_if_index; k.ip6_address = a[0]; k.pad = 0; - vlib_worker_thread_barrier_sync (vm); - p = mhash_get (&nm->neighbor_index_by_key, &k); if (p) { n = pool_elt_at_index (nm->neighbor_pool, p[0]); @@ -368,99 +538,116 @@ vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm, 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 = 0; - - if (n) - adjs = vec_dup(n->adjacencies); - else - clib_warning ("ip6 neighbor n not set"); - - /* 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.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) { + fib_prefix_t pfx = { + .fp_len = 128, + .fp_proto = FIB_PROTOCOL_IP6, + .fp_addr = { + .ip6 = k.ip6_address, + }, + }; + u32 fib_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; + + clib_memcpy (n->link_layer_address, + link_layer_address, + n_bytes_link_layer_address); + + /* + * create the adj-fib. the entry in the FIB table for and to the peer. + */ + fib_index = ip6_main.fib_index_by_sw_if_index[n->key.sw_if_index]; + n->fib_entry_index = + fib_table_entry_update_one_path(fib_index, + &pfx, + FIB_SOURCE_ADJ, + FIB_ENTRY_FLAG_NONE, + FIB_PROTOCOL_IP6, + &pfx.fp_addr, + n->key.sw_if_index, + ~0, + 1, + NULL, // no label stack + FIB_ROUTE_PATH_FLAG_NONE); + } + else + { + /* + * prevent a DoS attack from the data-plane that + * spams us with no-op updates to the MAC address + */ + if (0 == memcmp(n->link_layer_address, + link_layer_address, + n_bytes_link_layer_address)) + return -1; + + clib_memcpy (n->link_layer_address, + link_layer_address, + n_bytes_link_layer_address); } - /* Update time stamp and ethernet address. */ - clib_memcpy (n->link_layer_address, link_layer_address, n_bytes_link_layer_address); + /* Update time stamp and flags. */ n->cpu_time_last_updated = clib_cpu_time_now (); if (is_static) n->flags |= IP6_NEIGHBOR_FLAG_STATIC; + else + n->flags |= IP6_NEIGHBOR_FLAG_DYNAMIC; + + adj_nbr_walk_nh6 (sw_if_index, + &n->key.ip6_address, + ip6_nd_mk_complete_walk, n); /* Customer(s) waiting for this address to be resolved? */ p = mhash_get (&nm->pending_resolutions_by_address, a); - if (p == 0) - goto out; - - next_index = p[0]; + if (p) + { + next_index = p[0]; - while (next_index != (u32)~0) + while (next_index != (u32)~0) + { + pr = pool_elt_at_index (nm->pending_resolutions, next_index); + vlib_process_signal_event (vm, pr->node_index, + pr->type_opaque, + pr->data); + next_index = pr->next_index; + pool_put (nm->pending_resolutions, pr); + } + + mhash_unset (&nm->pending_resolutions_by_address, a, 0); + } + + /* Customer(s) requesting ND event for this address? */ + p = mhash_get (&nm->mac_changes_by_address, a); + if (p) { - pr = pool_elt_at_index (nm->pending_resolutions, next_index); - vlib_process_signal_event (vm, pr->node_index, - pr->type_opaque, - pr->data); - next_index = pr->next_index; - pool_put (nm->pending_resolutions, pr); + next_index = p[0]; + + while (next_index != (u32)~0) + { + int (*fp)(u32, u8 *, u32, ip6_address_t *); + int rv = 1; + mc = pool_elt_at_index (nm->mac_changes, next_index); + fp = mc->data_callback; + + /* Call the user's data callback, return 1 to suppress dup events */ + if (fp) + rv = (*fp)(mc->data, link_layer_address, sw_if_index, &ip6a_zero); + /* + * Signal the resolver process, as long as the user + * says they want to be notified + */ + if (rv == 0) + vlib_process_signal_event (vm, mc->node_index, + mc->type_opaque, + mc->data); + next_index = mc->next_index; + } } - mhash_unset (&nm->pending_resolutions_by_address, a, 0); - -out: - vlib_worker_thread_barrier_release(vm); return 0; } @@ -474,26 +661,20 @@ vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm, ip6_neighbor_main_t * nm = &ip6_neighbor_main; ip6_neighbor_key_t k; ip6_neighbor_t * n; - ip6_main_t * im = &ip6_main; - ip6_add_del_route_args_t args; uword * p; int rv = 0; -#if DPDK > 0 if (os_get_cpu_number()) { set_unset_ip6_neighbor_rpc (vm, sw_if_index, a, link_layer_address, 0 /* unset */, 0); return 0; } -#endif k.sw_if_index = sw_if_index; k.ip6_address = a[0]; k.pad = 0; - vlib_worker_thread_barrier_sync (vm); - p = mhash_get (&nm->neighbor_index_by_key, &k); if (p == 0) { @@ -502,74 +683,20 @@ vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm, } n = pool_elt_at_index (nm->neighbor_pool, p[0]); + + adj_nbr_walk_nh6 (sw_if_index, + &n->key.ip6_address, + ip6_nd_mk_incomplete_walk, + n); + mhash_unset (&nm->neighbor_index_by_key, &n->key, 0); + fib_table_entry_delete_index (n->fib_entry_index, FIB_SOURCE_ADJ); pool_put (nm->neighbor_pool, n); - 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_DEL - | IP6_ROUTE_FLAG_NEIGHBOR; - args.dst_address = a[0]; - args.dst_address_length = 128; - args.adj_index = ~0; - args.add_adj = NULL; - args.n_add_adj = 0; - ip6_add_del_route (im, &args); out: - vlib_worker_thread_barrier_release(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) { @@ -581,7 +708,6 @@ static void ip6_neighbor_set_unset_rpc_callback vnet_unset_ip6_ethernet_neighbor (vm, a->sw_if_index, &a->addr, a->link_layer_address, 6); } -#endif static int ip6_neighbor_sort (void *a1, void *a2) @@ -596,13 +722,31 @@ ip6_neighbor_sort (void *a1, void *a2) return cmp; } +ip6_neighbor_t * +ip6_neighbors_entries (u32 sw_if_index) +{ + ip6_neighbor_main_t * nm = &ip6_neighbor_main; + ip6_neighbor_t *n, *ns = 0; + + /* *INDENT-OFF* */ + pool_foreach (n, nm->neighbor_pool, ({ + if (sw_if_index != ~0 && n->key.sw_if_index != sw_if_index) + continue; + vec_add1 (ns, n[0]); + })); + /* *INDENT-ON* */ + + if (ns) + vec_sort_with_function (ns, ip6_neighbor_sort); + return ns; +} + static clib_error_t * show_ip6_neighbors (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { vnet_main_t * vnm = vnet_get_main(); - ip6_neighbor_main_t * nm = &ip6_neighbor_main; ip6_neighbor_t * n, * ns; clib_error_t * error = 0; u32 sw_if_index; @@ -611,25 +755,46 @@ show_ip6_neighbors (vlib_main_t * vm, sw_if_index = ~0; (void) unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index); - ns = 0; - pool_foreach (n, nm->neighbor_pool, ({ vec_add1 (ns, n[0]); })); - vec_sort_with_function (ns, ip6_neighbor_sort); - vlib_cli_output (vm, "%U", format_ip6_neighbor_ip6_entry, vm, 0); - vec_foreach (n, ns) { - if (sw_if_index != ~0 && n->key.sw_if_index != sw_if_index) - continue; - vlib_cli_output (vm, "%U", format_ip6_neighbor_ip6_entry, vm, n); - } - vec_free (ns); + ns = ip6_neighbors_entries (sw_if_index); + if (ns) + { + vlib_cli_output (vm, "%U", format_ip6_neighbor_ip6_entry, vm, 0); + vec_foreach (n, ns) { + vlib_cli_output (vm, "%U", format_ip6_neighbor_ip6_entry, vm, n); + } + vec_free (ns); + } return error; } +/*? + * This command is used to display the adjacent IPv6 hosts found via + * neighbor discovery. Optionally, limit the output to the specified + * interface. + * + * @cliexpar + * Example of how to display the IPv6 neighbor adjacency table: + * @cliexstart{show ip6 neighbors} + * Time Address Flags Link layer Interface + * 34.0910 ::a:1:1:0:7 02:fe:6a:07:39:6f GigabitEthernet2/0/0 + * 173.2916 ::b:5:1:c:2 02:fe:50:62:3a:94 GigabitEthernet2/0/0 + * 886.6654 ::1:1:c:0:9 S 02:fe:e4:45:27:5b GigabitEthernet3/0/0 + * @cliexend + * Example of how to display the IPv6 neighbor adjacency table for given interface: + * @cliexstart{show ip6 neighbors GigabitEthernet2/0/0} + * Time Address Flags Link layer Interface + * 34.0910 ::a:1:1:0:7 02:fe:6a:07:39:6f GigabitEthernet2/0/0 + * 173.2916 ::b:5:1:c:2 02:fe:50:62:3a:94 GigabitEthernet2/0/0 + * @cliexend +?*/ +/* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_ip6_neighbors_command, static) = { .path = "show ip6 neighbors", .function = show_ip6_neighbors, - .short_help = "Show ip6 neighbors", + .short_help = "show ip6 neighbors []", }; +/* *INDENT-ON* */ static clib_error_t * set_ip6_neighbor (vlib_main_t * vm, @@ -673,11 +838,25 @@ set_ip6_neighbor (vlib_main_t * vm, return 0; } +/*? + * This command is used to manually add an entry to the IPv6 neighbor + * adjacency table. Optionally, the entry can be added as static. It is + * also used to remove an entry from the table. Use the 'show ip6 + * neighbors' command to display all learned and manually entered entries. + * + * @cliexpar + * Example of how to add a static entry to the IPv6 neighbor adjacency table: + * @cliexcmd{set ip6 neighbor GigabitEthernet2/0/0 ::1:1:c:0:9 02:fe:e4:45:27:5b static} + * Example of how to delete an entry from the IPv6 neighbor adjacency table: + * @cliexcmd{set ip6 neighbor del GigabitEthernet2/0/0 ::1:1:c:0:9 02:fe:e4:45:27:5b} +?*/ +/* *INDENT-OFF* */ VLIB_CLI_COMMAND (set_ip6_neighbor_command, static) = { .path = "set ip6 neighbor", .function = set_ip6_neighbor, - .short_help = "set ip6 neighbor [del] [static]", + .short_help = "set ip6 neighbor [del] [static]", }; +/* *INDENT-ON* */ typedef enum { ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP, @@ -693,7 +872,6 @@ icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm, { vnet_main_t * vnm = vnet_get_main(); ip6_main_t * im = &ip6_main; - ip_lookup_main_t * lm = &im->lookup_main; uword n_packets = frame->n_vectors; u32 * from, * to_next; u32 n_left_from, n_left_to_next, next_index, n_advertisements_sent; @@ -752,17 +930,25 @@ icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm, if (!ip6_sadd_unspecified && !ip6_sadd_link_local) { u32 src_adj_index0 = ip6_src_lookup_for_packet (im, p0, ip0); - ip_adjacency_t * adj0 = ip_get_adjacency (&im->lookup_main, src_adj_index0); - /* Allow all realistic-looking rewrite adjacencies to pass */ - ni0 = adj0->lookup_next_index; - is_rewrite0 = (ni0 >= IP_LOOKUP_NEXT_ARP) && - (ni0 < IP6_LOOKUP_N_NEXT); - - error0 = ((adj0->rewrite_header.sw_if_index != sw_if_index0 - || ! is_rewrite0) - ? ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK - : error0); + if (ADJ_INDEX_INVALID != src_adj_index0) + { + ip_adjacency_t * adj0 = ip_get_adjacency (&im->lookup_main, src_adj_index0); + + /* Allow all realistic-looking rewrite adjacencies to pass */ + ni0 = adj0->lookup_next_index; + is_rewrite0 = (ni0 >= IP_LOOKUP_NEXT_ARP) && + (ni0 < IP6_LOOKUP_N_NEXT); + + error0 = ((adj0->rewrite_header.sw_if_index != sw_if_index0 + || ! is_rewrite0) + ? ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK + : error0); + } + else + { + error0 = ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK; + } } o0 = (void *) (h0 + 1); @@ -785,21 +971,29 @@ icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm, if (is_solicitation && error0 == ICMP6_ERROR_NONE) { - /* Check that target address is one that we know about. */ - ip_interface_address_t * ia0; - ip6_address_fib_t ip6_af0; - void * oldheap; - - ip6_addr_fib_init (&ip6_af0, &h0->target_address, - vec_elt (im->fib_index_by_sw_if_index, - sw_if_index0)); - - /* Gross kludge, "thank you" MJ, don't even ask */ - oldheap = clib_mem_set_heap (clib_per_cpu_mheaps[0]); - ia0 = ip_get_interface_address (lm, &ip6_af0); - clib_mem_set_heap (oldheap); - error0 = ia0 == 0 ? - ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN : error0; + /* Check that target address is local to this router. */ + fib_node_index_t fei; + u32 fib_index; + + fib_index = ip6_fib_table_get_index_for_sw_if_index(sw_if_index0); + + if (~0 == fib_index) + { + error0 = ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN; + } + else + { + fei = ip6_fib_table_lookup_exact_match(fib_index, + &h0->target_address, + 128); + + if (FIB_NODE_INDEX_INVALID == fei || + !(FIB_ENTRY_FLAG_LOCAL & + fib_entry_get_flags_for_source(fei, FIB_SOURCE_INTERFACE))) + { + error0 = ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN; + } + } } if (is_solicitation) @@ -856,7 +1050,8 @@ icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm, vlib_buffer_advance(p0, - ethernet_buffer_header_size(p0)); eth0 = vlib_buffer_get_current(p0); clib_memcpy(eth0->dst_address, eth0->src_address, 6); - clib_memcpy(eth0->src_address, eth_if0->address, 6); + if (eth_if0) + clib_memcpy(eth0->src_address, eth_if0->address, 6); /* Setup input and output sw_if_index for packet */ ASSERT(vnet_buffer(p0)->sw_if_index[VLIB_RX] == sw_if_index0); @@ -1017,13 +1212,20 @@ icmp6_router_solicitation(vlib_main_t * vm, if (!is_unspecified && !is_link_local) { u32 src_adj_index0 = ip6_src_lookup_for_packet (im, p0, ip0); - ip_adjacency_t * adj0 = ip_get_adjacency (&im->lookup_main, src_adj_index0); - error0 = ((adj0->rewrite_header.sw_if_index != sw_if_index0 - || (adj0->lookup_next_index != IP_LOOKUP_NEXT_ARP - && adj0->lookup_next_index != IP_LOOKUP_NEXT_REWRITE)) - ? ICMP6_ERROR_ROUTER_SOLICITATION_SOURCE_NOT_ON_LINK - : error0); + if (ADJ_INDEX_INVALID != src_adj_index0) + { + ip_adjacency_t * adj0 = ip_get_adjacency (&im->lookup_main, + src_adj_index0); + + error0 = (adj0->rewrite_header.sw_if_index != sw_if_index0 + ? ICMP6_ERROR_ROUTER_SOLICITATION_SOURCE_NOT_ON_LINK + : error0); + } + else + { + error0 = ICMP6_ERROR_ROUTER_SOLICITATION_SOURCE_NOT_ON_LINK; + } } /* check for source LL option and process */ @@ -1266,9 +1468,10 @@ icmp6_router_solicitation(vlib_main_t * vm, : error0); next0 = is_dropped ? next0 : ICMP6_ROUTER_SOLICITATION_NEXT_REPLY_RW; - vnet_buffer (p0)->ip.adj_index[VLIB_RX] = adj_index0; + vnet_buffer (p0)->ip.adj_index[VLIB_TX] = adj_index0; } } + p0->flags |= VNET_BUFFER_LOCALLY_ORIGINATED; radv_info->n_solicitations_dropped += is_dropped; radv_info->n_solicitations_rcvd += is_solicitation; @@ -1570,11 +1773,9 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add) { - ip6_main_t * im = &ip6_main; ip6_neighbor_main_t * nm = &ip6_neighbor_main; - ip_lookup_main_t * lm = &im->lookup_main; ip6_radv_t * a= 0; - u32 ri = ~0;; + u32 ri = ~0; vnet_sw_interface_t * sw_if0; ethernet_interface_t * eth_if0 = 0; @@ -1600,9 +1801,9 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm, ip6_mldp_group_t *m; /* remove adjacencies */ - ip_del_adjacency (lm, a->all_nodes_adj_index); - ip_del_adjacency (lm, a->all_routers_adj_index); - ip_del_adjacency (lm, a->all_mldv2_routers_adj_index); + adj_unlock(a->all_nodes_adj_index); + adj_unlock(a->all_routers_adj_index); + adj_unlock(a->all_mldv2_routers_adj_index); /* clean up prefix_pool */ pool_foreach (p, a->adv_prefixes_pool, ({ @@ -1666,11 +1867,10 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm, a->min_delay_between_radv = MIN_DELAY_BETWEEN_RAS; a->max_delay_between_radv = MAX_DELAY_BETWEEN_RAS; a->max_rtr_default_lifetime = MAX_DEF_RTR_LIFETIME; - a->seed = random_default_seed(); - - /* for generating random interface ids */ - a->randomizer = 0x1119194911191949; - a->randomizer = random_u64 ((u32 *)&a->randomizer); + a->seed = (u32) clib_cpu_time_now(); + (void) random_u32 (&a->seed); + a->randomizer = clib_cpu_time_now(); + (void) random_u64 (&a->randomizer); a->initial_adverts_count = MAX_INITIAL_RTR_ADVERTISEMENTS ; a->initial_adverts_sent = a->initial_adverts_count-1; @@ -1692,66 +1892,34 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm, mhash_init (&a->address_to_mldp_index, sizeof (uword), sizeof (ip6_address_t)); { - ip_adjacency_t *adj; u8 link_layer_address[6] = {0x33, 0x33, 0x00, 0x00, 0x00, IP6_MULTICAST_GROUP_ID_all_hosts}; - adj = ip_add_adjacency (lm, /* template */ 0, /* block size */ 1, - &a->all_nodes_adj_index); - - adj->lookup_next_index = IP_LOOKUP_NEXT_REWRITE; - adj->if_address_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)); + a->all_nodes_adj_index = adj_rewrite_add_and_lock(FIB_PROTOCOL_IP6, + VNET_LINK_IP6, + sw_if_index, + link_layer_address); } { - ip_adjacency_t *adj; u8 link_layer_address[6] = {0x33, 0x33, 0x00, 0x00, 0x00, IP6_MULTICAST_GROUP_ID_all_routers}; - adj = ip_add_adjacency (lm, /* template */ 0, /* block size */ 1, - &a->all_routers_adj_index); - - adj->lookup_next_index = IP_LOOKUP_NEXT_REWRITE; - adj->if_address_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)); + a->all_routers_adj_index = adj_rewrite_add_and_lock(FIB_PROTOCOL_IP6, + VNET_LINK_IP6, + sw_if_index, + link_layer_address); } { - ip_adjacency_t *adj; u8 link_layer_address[6] = {0x33, 0x33, 0x00, 0x00, 0x00, IP6_MULTICAST_GROUP_ID_mldv2_routers}; - adj = ip_add_adjacency (lm, /* template */ 0, /* block size */ 1, - &a->all_mldv2_routers_adj_index); - - adj->lookup_next_index = IP_LOOKUP_NEXT_REWRITE; - adj->if_address_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)); + a->all_mldv2_routers_adj_index = + adj_rewrite_add_and_lock(FIB_PROTOCOL_IP6, + VNET_LINK_IP6, + sw_if_index, + link_layer_address); } /* add multicast groups we will always be reporting */ @@ -1962,15 +2130,16 @@ ip6_neighbor_send_mldpv2_report(u32 sw_if_index) /* * OK to override w/ no regard for actual FIB, because - * ip6-rewrite-local only looks at the adjacency. + * ip6-rewrite only looks at the adjacency. */ vnet_buffer (b0)->sw_if_index[VLIB_RX] = vnet_main.local_interface_sw_if_index; - vnet_buffer (b0)->ip.adj_index[VLIB_RX] = + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = radv_info->all_mldv2_routers_adj_index; + b0->flags |= VNET_BUFFER_LOCALLY_ORIGINATED; - vlib_node_t * node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite-local"); + vlib_node_t * node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite"); f = vlib_get_frame_to_node (vm, node->index); to_next = vlib_frame_vector_args (f); @@ -1992,7 +2161,7 @@ VLIB_REGISTER_NODE (ip6_icmp_router_solicitation_node,static) = { .n_next_nodes = ICMP6_ROUTER_SOLICITATION_N_NEXT, .next_nodes = { [ICMP6_ROUTER_SOLICITATION_NEXT_DROP] = "error-drop", - [ICMP6_ROUTER_SOLICITATION_NEXT_REPLY_RW] = "ip6-rewrite-local", + [ICMP6_ROUTER_SOLICITATION_NEXT_REPLY_RW] = "ip6-rewrite", [ICMP6_ROUTER_SOLICITATION_NEXT_REPLY_TX] = "interface-output", }, }; @@ -2813,11 +2982,49 @@ show_ip6_interface_cmd (vlib_main_t * vm, return error; } +/*? + * This command is used to display various IPv6 attributes on a given + * interface. + * + * @cliexpar + * Example of how to display IPv6 settings: + * @cliexstart{show ip6 interface GigabitEthernet2/0/0} + * GigabitEthernet2/0/0 is admin up + * Link-local address(es): + * fe80::ab8/64 + * Joined group address(es): + * ff02::1 + * ff02::2 + * ff02::16 + * ff02::1:ff00:ab8 + * Advertised Prefixes: + * prefix fe80::fe:28ff:fe9c:75b3, length 64 + * MTU is 1500 + * ICMP error messages are unlimited + * ICMP redirects are disabled + * ICMP unreachables are not sent + * ND DAD is disabled + * ND advertised reachable time is 0 + * ND advertised retransmit interval is 0 (msec) + * ND router advertisements are sent every 200 seconds (min interval is 150) + * ND router advertisements live for 600 seconds + * Hosts use stateless autoconfig for addresses + * ND router advertisements sent 19336 + * ND router solicitations received 0 + * ND router solicitations dropped 0 + * @cliexend + * Example of output if IPv6 is not enabled on the interface: + * @cliexstart{show ip6 interface GigabitEthernet2/0/0} + * show ip6 interface: IPv6 not enabled on interface + * @cliexend +?*/ +/* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_ip6_interface_command, static) = { .path = "show ip6 interface", .function = show_ip6_interface_cmd, - .short_help = "show ip6 interface ", + .short_help = "show ip6 interface ", }; +/* *INDENT-ON* */ clib_error_t * disable_ip6_interface(vlib_main_t * vm, @@ -2934,7 +3141,8 @@ enable_ip6_interface(vlib_main_t * vm, /* essentially "enables" ipv6 on this interface */ error = ip6_add_del_interface_address (vm, sw_if_index, - &link_local_address, 64 /* address width */, + &link_local_address, + 128 /* address width */, 0 /* is_del */); if(error) @@ -2976,11 +3184,20 @@ enable_ip6_interface_cmd (vlib_main_t * vm, return error; } +/*? + * This command is used to enable IPv6 on a given interface. + * + * @cliexpar + * Example of how enable IPv6 on a given interface: + * @cliexcmd{enable ip6 interface GigabitEthernet2/0/0} +?*/ +/* *INDENT-OFF* */ VLIB_CLI_COMMAND (enable_ip6_interface_command, static) = { .path = "enable ip6 interface", .function = enable_ip6_interface_cmd, - .short_help = "enable ip6 interface ", + .short_help = "enable ip6 interface ", }; +/* *INDENT-ON* */ static clib_error_t * disable_ip6_interface_cmd (vlib_main_t * vm, @@ -3007,17 +3224,143 @@ disable_ip6_interface_cmd (vlib_main_t * vm, return error; } +/*? + * This command is used to disable IPv6 on a given interface. + * + * @cliexpar + * Example of how disable IPv6 on a given interface: + * @cliexcmd{disable ip6 interface GigabitEthernet2/0/0} +?*/ +/* *INDENT-OFF* */ VLIB_CLI_COMMAND (disable_ip6_interface_command, static) = { - .path = "disable ip6 interface", + .path = "disable ip6 interface", .function = disable_ip6_interface_cmd, - .short_help = "disable ip6 interface ", + .short_help = "disable ip6 interface ", }; +/* *INDENT-ON* */ +/*? + * This command is used to configure the neighbor discovery + * parameters on a given interface. Use the 'show ip6 interface' + * command to display some of the current neighbor discovery parameters + * on a given interface. This command has three formats: + * + * + * Format 1 - Router Advertisement Options: (Only one can be entered in a single command) + * + * 'ip6 nd [no] [ra-managed-config-flag] | [ra-other-config-flag] | [ra-suppress] | [ra-suppress-link-layer] | [ra-send-unicast] | [ra-lifetime ] | [ra-initial ] | [ra-interval []] | [ra-cease]' + * + * Where: + * + * [no] ra-managed-config-flag - Advertises in ICMPv6 + * router-advertisement messages to use stateful address + * auto-configuration to obtain address information (sets the M-bit). + * Default is the M-bit is not set and the 'no' option + * returns it to this default state. + * + * [no] ra-other-config-flag - Indicates in ICMPv6 + * router-advertisement messages that hosts use stateful auto + * configuration to obtain nonaddress related information (sets + * the O-bit). Default is the O-bit is not set and the 'no' + * option returns it to this default state. + * + * [no] ra-suppress - Disables sending ICMPv6 router-advertisement + * messages. The 'no' option implies to enable sending ICMPv6 + * router-advertisement messages. + * + * [no] ra-suppress-link-layer - Indicates not to include the + * optional source link-layer address in the ICMPv6 router-advertisement + * messages. Default is to include the optional source link-layer address + * and the 'no' option returns it to this default state. + * + * [no] ra-send-unicast - Use the source address of the + * router-solicitation message if availiable. The default is to use + * multicast address of all nodes, and the 'no' option returns + * it to this default state. + * + * [no] ra-lifetime - Advertises the lifetime of a + * default router in ICMPv6 router-advertisement messages. The range is + * from 0 to 9000 seconds. '' must be greater than + * ''. The default value is 600 seconds and the + * 'no' option returns it to this default value. + * + * [no] ra-initial - Number of initial ICMPv6 + * router-advertisement messages sent and the interval between each + * message. Range for count is 1 - 3 and default is 3. Range for interval + * is 1 to 16 seconds, and default is 16 seconds. The 'no' option + * returns both to their default value. + * + * [no] ra-interval [] - Configures the + * interval between sending ICMPv6 router-advertisement messages. The + * range for max-interval is from 4 to 200 seconds. min-interval can not + * be more than 75% of max-interval. If not set, min-interval will be + * set to 75% of max-interval. The range for min-interval is from 3 to + * 150 seconds. The 'no' option returns both to their default + * value. + * + * [no] ra-cease - Cease sending ICMPv6 router-advertisement messages. + * The 'no' options implies to start (or restart) sending + * ICMPv6 router-advertisement messages. + * + * + * Format 2 - Prefix Options: + * + * 'ip6 nd [no] prefix / [ | infinite] [no-advertise] [off-link] [no-autoconfig] [no-onlink]' + * + * Where: + * + * no - All additional flags are ignored and the prefix is deleted. + * + * - '' is the + * length of time in seconds during what the prefix is valid for the purpose of + * on-link determination. Range is 7203 to 2592000 seconds and default is 2592000 + * seconds (30 days). '' is the prefered-lifetime and is the + * length of time in seconds during what addresses generated from the prefix remain + * preferred. Range is 0 to 604800 seconds and default is 604800 seconds (7 days). + * + * infinite - Both '' and '<' + * are inifinte, no timeout. + * + * no-advertise - Do not send full router address in prefix + * advertisement. Default is to advertise (i.e. - This flag is off by default). + * + * off-link - Prefix is off-link, clear L-bit in packet. Default is on-link + * (i.e. - This flag is off and L-bit in packet is set by default and this prefix can + * be used for on-link determination). 'no-onlink' also controls the L-bit. + * + * no-autoconfig - Do not use prefix for autoconfiguration, clear A-bit in packet. + * Default is autoconfig (i.e. - This flag is off and A-bit in packet is set by default. + * + * no-onlink - Do not use prefix for onlink determination, clear L-bit in packet. + * Default is on-link (i.e. - This flag is off and L-bit in packet is set by default and + * this prefix can be used for on-link determination). 'off-link' also controls + * the L-bit. + * + * + * Format 3: - Default of Prefix: + * + * 'ip6 nd [no] prefix / default' + * + * When a new prefix is added (or existing one is being overwritten) default + * uses default values for the prefix. If no is used, the default + * is ignored and the prefix is deleted. + * + * + * @cliexpar + * Example of how set a router advertisement option: + * @cliexcmd{ip6 nd GigabitEthernet2/0/0 ra-interval 100 20} + * Example of how to add a prefix: + * @cliexcmd{ip6 nd GigabitEthernet2/0/0 prefix fe80::fe:28ff:fe9c:75b3/64 infinite no-advertise} + * Example of how to delete a prefix: + * @cliexcmd{ip6 nd GigabitEthernet2/0/0 no prefix fe80::fe:28ff:fe9c:75b3/64} +?*/ +/* *INDENT-OFF* */ VLIB_CLI_COMMAND (ip6_nd_command, static) = { .path = "ip6 nd", - .short_help = "Set ip6 neighbor discovery parameters", + .short_help = "ip6 nd ...", .function = ip6_neighbor_cmd, }; +/* *INDENT-ON* */ clib_error_t * set_ip6_link_local_address(vlib_main_t * vm, @@ -3111,11 +3454,23 @@ set_ip6_link_local_address_cmd (vlib_main_t * vm, return error; } +/*? + * This command is used to assign an IPv6 Link-local address to an + * interface. This command will enable IPv6 on an interface if it + * is not already enabled. Use the 'show ip6 interface' command + * to display the assigned Link-local address. + * + * @cliexpar + * Example of how to assign an IPv6 Link-local address to an interface: + * @cliexcmd{set ip6 link-local address GigabitEthernet2/0/0 FE80::AB8/64} +?*/ +/* *INDENT-OFF* */ VLIB_CLI_COMMAND (set_ip6_link_local_address_command, static) = { .path = "set ip6 link-local address", - .short_help = "Set ip6 interface link-local address ", + .short_help = "set ip6 link-local address /", .function = set_ip6_link_local_address_cmd, }; +/* *INDENT-ON* */ /* callback when an interface address is added or deleted */ static void @@ -3220,87 +3575,10 @@ 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, IP46_TYPE_IP6); - 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, IP46_TYPE_IP6); - 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), @@ -3327,6 +3605,10 @@ static clib_error_t * ip6_neighbor_init (vlib_main_t * vm) /* value size */ sizeof (uword), /* key size */ sizeof (ip6_address_t)); + mhash_init (&nm->mac_changes_by_address, + /* value size */ sizeof (uword), + /* key size */ sizeof (ip6_address_t)); + /* default, configurable */ nm->limit_neighbor_cache_size = 50000; @@ -3336,8 +3618,6 @@ 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; } @@ -3374,3 +3654,202 @@ void vnet_register_ip6_neighbor_resolution_event (vnet_main_t * vnm, pr - nm->pending_resolutions, 0 /* old value */); } +int vnet_add_del_ip6_nd_change_event (vnet_main_t * vnm, + void * data_callback, + u32 pid, + void * address_arg, + uword node_index, + uword type_opaque, + uword data, + int is_add) +{ + ip6_neighbor_main_t * nm = &ip6_neighbor_main; + ip6_address_t * address = address_arg; + uword * p; + pending_resolution_t * mc; + void (*fp)(u32, u8 *) = data_callback; + + if (is_add) + { + pool_get (nm->mac_changes, mc); + + mc->next_index = ~0; + mc->node_index = node_index; + mc->type_opaque = type_opaque; + mc->data = data; + mc->data_callback = data_callback; + mc->pid = pid; + + p = mhash_get (&nm->mac_changes_by_address, address); + if (p) + { + /* Insert new resolution at the head of the list */ + mc->next_index = p[0]; + mhash_unset (&nm->mac_changes_by_address, address, 0); + } + + mhash_set (&nm->mac_changes_by_address, address, + mc - nm->mac_changes, 0); + return 0; + } + else + { + u32 index; + pending_resolution_t * mc_last = 0; + + p = mhash_get (&nm->mac_changes_by_address, address); + if (p == 0) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + index = p[0]; + + while (index != (u32)~0) + { + mc = pool_elt_at_index (nm->mac_changes, index); + if (mc->node_index == node_index && + mc->type_opaque == type_opaque && + mc->pid == pid) + { + /* Clients may need to clean up pool entries, too */ + if (fp) + (*fp)(mc->data, 0 /* no new mac addrs */); + if (index == p[0]) + { + mhash_unset (&nm->mac_changes_by_address, address, 0); + if (mc->next_index != ~0) + mhash_set (&nm->mac_changes_by_address, address, + mc->next_index, 0); + pool_put (nm->mac_changes, mc); + return 0; + } + else + { + ASSERT(mc_last); + mc_last->next_index = mc->next_index; + pool_put (nm->mac_changes, mc); + return 0; + } + } + mc_last = mc; + index = mc->next_index; + } + + return VNET_API_ERROR_NO_SUCH_ENTRY; + } +} + +int vnet_ip6_nd_term (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_buffer_t * p0, + ethernet_header_t * eth, + ip6_header_t * ip, + u32 sw_if_index, + u16 bd_index, + u8 shg) +{ + ip6_neighbor_main_t * nm = &ip6_neighbor_main; + icmp6_neighbor_solicitation_or_advertisement_header_t * ndh; + pending_resolution_t * mc; + uword *p; + + ndh = ip6_next_header (ip); + if (ndh->icmp.type != ICMP6_neighbor_solicitation && + ndh->icmp.type != ICMP6_neighbor_advertisement) + return 0; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (p0->flags & VLIB_BUFFER_IS_TRACED))) + { + u8 *t0 = vlib_add_trace (vm, node, p0, + sizeof (icmp6_input_trace_t)); + clib_memcpy (t0, ip, sizeof (icmp6_input_trace_t)); + } + + /* Check if anyone want ND events for L2 BDs */ + p = mhash_get (&nm->mac_changes_by_address, &ip6a_zero); + if (p && shg == 0 && /* Only SHG 0 interface which is more likely local */ + !ip6_address_is_link_local_unicast (&ip->src_address)) + { + u32 next_index = p[0]; + while (next_index != (u32)~0) + { + int (*fp)(u32, u8 *, u32, ip6_address_t *); + int rv = 1; + mc = pool_elt_at_index (nm->mac_changes, next_index); + fp = mc->data_callback; + /* Call the callback, return 1 to suppress dup events */ + if (fp) rv = (*fp)(mc->data, + eth->src_address, + sw_if_index, + &ip->src_address); + /* Signal the resolver process */ + if (rv == 0) + vlib_process_signal_event (vm, mc->node_index, + mc->type_opaque, + mc->data); + next_index = mc->next_index; + } + } + + /* Check if MAC entry exsist for solicited target IP */ + if (ndh->icmp.type == ICMP6_neighbor_solicitation) + { + icmp6_neighbor_discovery_ethernet_link_layer_address_option_t * opt; + l2_bridge_domain_t *bd_config; + u8 * macp; + + opt = (void *) (ndh + 1); + if ((opt->header.type != + ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address) || + (opt->header.n_data_u64s != 1)) + return 0; /* source link layer address option not present */ + + bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index); + macp = (u8 *) hash_get_mem (bd_config->mac_by_ip6, &ndh->target_address); + if (macp) + { /* found ip-mac entry, generate eighbor advertisement response */ + int bogus_length; + vlib_node_runtime_t * error_node = + vlib_node_get_runtime (vm, ip6_icmp_input_node.index); + ip->dst_address = ip->src_address; + ip->src_address = ndh->target_address; + ip->hop_limit = 255; + opt->header.type = + ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address; + clib_memcpy (opt->ethernet_address, macp, 6); + ndh->icmp.type = ICMP6_neighbor_advertisement; + ndh->advertisement_flags = clib_host_to_net_u32 + (ICMP6_NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED | + ICMP6_NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE); + ndh->icmp.checksum = 0; + ndh->icmp.checksum = ip6_tcp_udp_icmp_compute_checksum(vm, p0, ip, + &bogus_length); + clib_memcpy(eth->dst_address, eth->src_address, 6); + clib_memcpy(eth->src_address, macp, 6); + vlib_error_count (vm, error_node->node_index, + ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_TX, 1); + return 1; + } + } + + return 0; + +} + +void +ethernet_ndp_change_mac (vlib_main_t * vm, u32 sw_if_index) +{ + ip6_neighbor_main_t * nm = &ip6_neighbor_main; + ip6_neighbor_t * n; + + /* *INDENT-OFF* */ + pool_foreach (n, nm->neighbor_pool, ({ + if (n->key.sw_if_index == sw_if_index) + { + adj_nbr_walk_nh6 (sw_if_index, + &n->key.ip6_address, + ip6_nd_mk_complete_walk, n); + } + })); + /* *INDENT-ON* */ +}