X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=vnet%2Fvnet%2Fip%2Fip6_neighbor.c;h=5d059e9b9cf93aa2d06d39ffcfa0513a0214eeaf;hb=c631f2de6dd06b4cbb92bf8398839b882344fd25;hp=acb1d8dcf740d8d86cebac1d877c7722ade7cd7a;hpb=f1213b82771ce929c076339c24a777cfd59690e6;p=vpp.git diff --git a/vnet/vnet/ip/ip6_neighbor.c b/vnet/vnet/ip/ip6_neighbor.c index acb1d8dcf74..5d059e9b9cf 100644 --- a/vnet/vnet/ip/ip6_neighbor.c +++ b/vnet/vnet/ip/ip6_neighbor.c @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #if DPDK==1 #include @@ -36,7 +39,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_DYNAMIC (2 << 0) u64 cpu_time_last_updated; + adj_index_t adj_index; } ip6_neighbor_t; /* advertised prefix option */ @@ -117,9 +124,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 @@ -165,6 +172,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; @@ -176,6 +186,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; @@ -193,6 +207,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) { @@ -200,17 +215,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_DYNAMIC) + flags = format(flags, "D"); + + 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 +302,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 +317,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); @@ -308,27 +333,73 @@ static void set_unset_ip6_neighbor_rpc } #endif +static void +ip6_nd_mk_complete (ip6_neighbor_t * nbr) +{ + fib_prefix_t pfx = { + .fp_len = 128, + .fp_proto = FIB_PROTOCOL_IP6, + .fp_addr = { + .ip6 = nbr->key.ip6_address, + }, + }; + ip6_main_t *im; + u32 fib_index; + + im = &ip6_main; + fib_index = im->fib_index_by_sw_if_index[nbr->key.sw_if_index]; + + /* only once please */ + if (ADJ_INDEX_INVALID == nbr->adj_index) + { + nbr->adj_index = + adj_nbr_add_or_lock_w_rewrite(FIB_PROTOCOL_IP6, + FIB_LINK_IP6, + &pfx.fp_addr, + nbr->key.sw_if_index, + nbr->link_layer_address); + ASSERT(ADJ_INDEX_INVALID != nbr->adj_index); + + fib_table_entry_update_one_path(fib_index, + &pfx, + FIB_SOURCE_ADJ, + FIB_ENTRY_FLAG_NONE, + FIB_PROTOCOL_IP6, + &pfx.fp_addr, + nbr->key.sw_if_index, + ~0, + 1, + MPLS_LABEL_INVALID, + FIB_ROUTE_PATH_FLAG_NONE); + } + else + { + adj_nbr_update_rewrite(nbr->adj_index, + nbr->link_layer_address); + } +} + int 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_main_t * im = &ip6_main; + ip6_neighbor_t * n = 0; + int make_new_nd_cache_entry=1; uword * p; u32 next_index; - 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 */); + 1 /* set new neighbor */, is_static); return 0; } #endif @@ -340,69 +411,121 @@ 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 - { - 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; - } + /* Refuse to over-write static neighbor entry. */ + if (!is_static && + (n->flags & IP6_NEIGHBOR_FLAG_STATIC)) + return -2; + make_new_nd_cache_entry = 0; + } + + 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; + n->adj_index = ADJ_INDEX_INVALID; + } /* Update time stamp and ethernet address. */ - clib_memcpy (n->link_layer_address, link_layer_address, n_bytes_link_layer_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; + else + n->flags |= IP6_NEIGHBOR_FLAG_DYNAMIC; + + ip6_nd_mk_complete(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; } +static void +ip6_nd_mk_incomplete (ip6_neighbor_t *nbr) +{ + fib_prefix_t pfx = { + .fp_len = 128, + .fp_proto = FIB_PROTOCOL_IP6, + .fp_addr = { + .ip6 = nbr->key.ip6_address, + }, + }; + u32 fib_index; + ip6_main_t *im; + + im = &ip6_main; + fib_index = im->fib_index_by_sw_if_index[nbr->key.sw_if_index]; + + /* + * revert the adj this ND entry sourced to incomplete + */ + adj_nbr_update_rewrite(nbr->adj_index, + NULL); + + /* + * remove the FIB entry the ND entry sourced + */ + fib_table_entry_delete(fib_index, &pfx, FIB_SOURCE_ADJ); + + /* + * Unlock the adj now that the ARP entry is no longer a source + */ + adj_unlock(nbr->adj_index); + nbr->adj_index = ADJ_INDEX_INVALID; +} + int vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm, u32 sw_if_index, @@ -413,8 +536,6 @@ 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; @@ -422,7 +543,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 @@ -441,18 +562,11 @@ vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm, } n = pool_elt_at_index (nm->neighbor_pool, p[0]); + + ip6_nd_mk_incomplete(n); mhash_unset (&nm->neighbor_index_by_key, &n->key, 0); 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; @@ -465,7 +579,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 +644,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 +658,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 +669,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 +679,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 { @@ -579,7 +696,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; @@ -638,17 +754,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 < IP_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); @@ -666,26 +790,33 @@ 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) { - /* 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(fei))) + { + error0 = ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN; + } + } } if (is_solicitation) @@ -739,7 +870,7 @@ icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm, /* Reuse current MAC header, copy SMAC to DMAC and * interface MAC to SMAC */ - vlib_buffer_reset (p0); + 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); @@ -903,13 +1034,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 */ @@ -931,7 +1069,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 */ @@ -1456,11 +1594,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; @@ -1486,9 +1622,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, ({ @@ -1522,6 +1658,7 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm, pool_put (nm->if_radv_pool, a); nm->if_radv_pool_index_by_sw_if_index[sw_if_index] = ~0; ri = ~0; + ip6_sw_interface_enable_disable(sw_if_index, 0); } } else @@ -1530,6 +1667,7 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm, { vnet_hw_interface_t * hw_if0; + ip6_sw_interface_enable_disable(sw_if_index, 1); hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index); pool_get (nm->if_radv_pool, a); @@ -1578,66 +1716,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, + FIB_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, + FIB_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, + FIB_LINK_IP6, + sw_if_index, + link_layer_address); } /* add multicast groups we will always be reporting */ @@ -2121,7 +2227,7 @@ VLIB_REGISTER_NODE (ip6_icmp_neighbor_advertisement_node,static) = { /* API support functions */ int ip6_neighbor_ra_config(vlib_main_t * vm, u32 sw_if_index, - u8 surpress, u8 managed, u8 other, + u8 suppress, u8 managed, u8 other, u8 ll_option, u8 send_unicast, u8 cease, u8 use_lifetime, u32 lifetime, u32 initial_count, u32 initial_interval, @@ -2174,7 +2280,7 @@ ip6_neighbor_ra_config(vlib_main_t * vm, u32 sw_if_index, if "flag" is set and is_no is true then restore default value else set value corresponding to "flag" if "flag" is clear don't change corresponding value */ - radv_info->send_radv = (surpress != 0) ? ( (is_no != 0) ? 1 : 0 ) : radv_info->send_radv; + radv_info->send_radv = (suppress != 0) ? ( (is_no != 0) ? 1 : 0 ) : radv_info->send_radv; radv_info->adv_managed_flag = ( managed != 0) ? ( (is_no) ? 0 : 1) : radv_info->adv_managed_flag; radv_info->adv_other_flag = (other != 0) ? ( (is_no) ? 0: 1) : radv_info->adv_other_flag; radv_info->adv_link_layer_address = ( ll_option != 0) ? ( (is_no) ? 1 : 0) : radv_info->adv_link_layer_address; @@ -2341,8 +2447,8 @@ ip6_neighbor_cmd(vlib_main_t * vm, unformat_input_t * main_input, vlib_cli_comma ip6_neighbor_main_t * nm = &ip6_neighbor_main; clib_error_t * error = 0; u8 is_no = 0; - u8 surpress = 0, managed = 0, other = 0; - u8 surpress_ll_option = 0, send_unicast = 0, cease= 0; + u8 suppress = 0, managed = 0, other = 0; + u8 suppress_ll_option = 0, send_unicast = 0, cease= 0; u8 use_lifetime = 0; u32 sw_if_index, ra_lifetime = 0, ra_initial_count = 0, ra_initial_interval = 0; u32 ra_max_interval = 0 , ra_min_interval = 0; @@ -2426,14 +2532,16 @@ ip6_neighbor_cmd(vlib_main_t * vm, unformat_input_t * main_input, vlib_cli_comma other = 1; break; } - else if (unformat (line_input, "ra-surpress")) + else if (unformat (line_input, "ra-suppress") || + unformat (line_input, "ra-surpress")) { - surpress = 1; + suppress = 1; break; } - else if (unformat (line_input, "ra-surpress-link-layer")) + else if (unformat (line_input, "ra-suppress-link-layer") || + unformat (line_input, "ra-surpress-link-layer")) { - surpress_ll_option = 1; + suppress_ll_option = 1; break; } else if (unformat (line_input, "ra-send-unicast")) @@ -2475,8 +2583,8 @@ ip6_neighbor_cmd(vlib_main_t * vm, unformat_input_t * main_input, vlib_cli_comma if(add_radv_info) { ip6_neighbor_ra_config(vm, sw_if_index, - surpress, managed, other, - surpress_ll_option, send_unicast, cease, + suppress, managed, other, + suppress_ll_option, send_unicast, cease, use_lifetime, ra_lifetime, ra_initial_count, ra_initial_interval, ra_max_interval, ra_min_interval, @@ -2548,6 +2656,24 @@ ip6_neighbor_cmd(vlib_main_t * vm, unformat_input_t * main_input, vlib_cli_comma return error; } +static void +ip6_print_addrs(vlib_main_t * vm, + u32 *addrs) +{ + ip_lookup_main_t * lm = &ip6_main.lookup_main; + u32 i; + + for (i = 0; i < vec_len (addrs); i++) + { + ip_interface_address_t * a = pool_elt_at_index(lm->if_address_pool, addrs[i]); + ip6_address_t * address = ip_interface_address_get_address (lm, a); + + vlib_cli_output (vm, "\t\t%U/%d", + format_ip6_address, address, + a->address_length); + } +} + static clib_error_t * show_ip6_interface_cmd (vlib_main_t * vm, unformat_input_t * input, @@ -2580,8 +2706,9 @@ show_ip6_interface_cmd (vlib_main_t * vm, vnet_get_sw_interface (vnm, sw_if_index), (vnet_sw_interface_is_admin_up (vnm, sw_if_index) ? "up" : "down")); - u32 ai; - u32 *global_scope = 0,i; + u32 ai; + u32 *link_scope = 0, *global_scope = 0; + u32 *local_scope = 0, *unknown_scope = 0; ip_interface_address_t * a; vec_validate_init_empty (lm->if_address_pool_index_by_sw_if_index, sw_if_index, ~0); @@ -2592,33 +2719,46 @@ show_ip6_interface_cmd (vlib_main_t * vm, a = pool_elt_at_index(lm->if_address_pool, ai); ip6_address_t * address = ip_interface_address_get_address (lm, a); - if( ip6_address_is_link_local_unicast (address)) - vlib_cli_output (vm, "\tIPv6 is enabled, link-local address is %U\n", format_ip6_address, - address); - - if((address->as_u8[0] & 0xe0) == 0x20) + if (ip6_address_is_link_local_unicast (address)) + vec_add1 (link_scope, ai); + else if(ip6_address_is_global_unicast (address)) vec_add1 (global_scope, ai); + else if(ip6_address_is_local_unicast (address)) + vec_add1 (local_scope, ai); + else + vec_add1 (unknown_scope, ai); ai = a->next_this_sw_interface; } - vlib_cli_output (vm, "\tGlobal unicast address(es):\n"); - for (i = 0; i < vec_len (global_scope); i++) - { - a = pool_elt_at_index(lm->if_address_pool, global_scope[i]); - ip6_address_t * address = ip_interface_address_get_address (lm, a); - ip6_address_t mask, subnet; + if (vec_len (link_scope)) + { + vlib_cli_output (vm, "\tLink-local address(es):\n"); + ip6_print_addrs (vm, link_scope); + vec_free (link_scope); + } - subnet = *address; - ip6_address_mask_from_width(&mask, a->address_length); - ip6_address_mask(&subnet, &mask); + if (vec_len (local_scope)) + { + vlib_cli_output (vm, "\tLocal unicast address(es):\n"); + ip6_print_addrs (vm, local_scope); + vec_free (local_scope); + } + + if (vec_len (global_scope)) + { + vlib_cli_output (vm, "\tGlobal unicast address(es):\n"); + ip6_print_addrs (vm, global_scope); + vec_free (global_scope); + } + + if (vec_len (unknown_scope)) + { + vlib_cli_output (vm, "\tOther-scope address(es):\n"); + ip6_print_addrs (vm, unknown_scope); + vec_free (unknown_scope); + } - vlib_cli_output (vm, "\t\t%U, subnet is %U/%d", - format_ip6_address, address, - format_ip6_address,&subnet, - a->address_length); - } - vec_free (global_scope); vlib_cli_output (vm, "\tJoined group address(es):\n"); ip6_mldp_group_t *m; pool_foreach (m, radv_info->mldp_group_pool, ({ @@ -2657,7 +2797,7 @@ show_ip6_interface_cmd (vlib_main_t * vm, } else { - error = clib_error_return (0, "Ipv6 not enabled on interface", + error = clib_error_return (0, "IPv6 not enabled on interface", format_unformat_error, input); } @@ -2668,7 +2808,7 @@ show_ip6_interface_cmd (vlib_main_t * vm, 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 ", }; clib_error_t * @@ -2786,7 +2926,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) @@ -3102,6 +3243,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; @@ -3147,3 +3292,203 @@ 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 */ + 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) + { + if (ADJ_INDEX_INVALID != n->adj_index) + { + adj_nbr_update_rewrite(n->adj_index, + n->link_layer_address); + } + } + })); + /* *INDENT-ON* */ +}