VPP-363: add ability to change mac address of the interface
[vpp.git] / vnet / vnet / ip / ip6_neighbor.c
index 70d77a5..5d059e9 100644 (file)
@@ -19,6 +19,9 @@
 #include <vnet/ethernet/ethernet.h>
 #include <vppinfra/mhash.h>
 #include <vppinfra/md5.h>
+#include <vnet/adj/adj.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/ip6_fib.h>
 
 #if DPDK==1
 #include <vnet/devices/dpdk/dpdk.h>
@@ -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,42 +317,89 @@ 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;
-  memcpy (&args.addr, a, sizeof (*a));
-  memcpy (args.link_layer_address, link_layer_addreess, 6);
+  args.is_static = is_static;
+  clib_memcpy (&args.addr, a, sizeof (*a));
+  clib_memcpy (args.link_layer_address, link_layer_addreess, 6);
   
   vl_api_rpc_call_main_thread (ip6_neighbor_set_unset_rpc_callback,
                                (u8 *) &args, sizeof (args));
 }
 #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. */
-  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] <intfc> <ip6-address> <mac-address>",
+  .short_help = "set ip6 neighbor [del] <intfc> <ip6-address> <mac-address> [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)
@@ -722,7 +853,7 @@ icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm,
              eth_if0 = ethernet_get_interface (&ethernet_main, sw_if0->hw_if_index);
              if (eth_if0 && o0)
                {
-                  memcpy (o0->ethernet_address, eth_if0->address, 6);
+                  clib_memcpy (o0->ethernet_address, eth_if0->address, 6);
                  o0->header.type = 
                       ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address;
                }
@@ -739,10 +870,10 @@ 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);
-              memcpy(eth0->dst_address, eth0->src_address, 6);
-              memcpy(eth0->src_address, eth_if0->address, 6);
+              clib_memcpy(eth0->dst_address, eth0->src_address, 6);
+              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);
@@ -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 */
@@ -1013,7 +1151,7 @@ icmp6_router_solicitation(vlib_main_t * vm,
                          h.header.n_data_u64s = 1;
 
                          /* copy ll address */
-                         memcpy(&h.ethernet_address[0], eth_if0->address,  6);
+                         clib_memcpy(&h.ethernet_address[0], eth_if0->address,  6);
 
                          vlib_buffer_add_data (vm,
                                                p0->free_list_index,
@@ -1081,7 +1219,7 @@ icmp6_router_solicitation(vlib_main_t * vm,
                                  }
                                h.unused  = 0;
                                
-                               memcpy(&h.dst_address, &pr_info->prefix,  sizeof(ip6_address_t));
+                               clib_memcpy(&h.dst_address, &pr_info->prefix,  sizeof(ip6_address_t));
 
                                payload_length += sizeof( icmp6_neighbor_discovery_prefix_information_option_t); 
 
@@ -1131,8 +1269,8 @@ icmp6_router_solicitation(vlib_main_t * vm,
                            * interface MAC to SMAC */
                           vlib_buffer_reset (p0);
                           eth0 = vlib_buffer_get_current(p0);
-                          memcpy(eth0->dst_address, eth0->src_address, 6);
-                          memcpy(eth0->src_address, eth_if0->address, 6);
+                          clib_memcpy(eth0->dst_address, eth0->src_address, 6);
+                          clib_memcpy(eth0->src_address, eth_if0->address, 6);
                          next0 = is_dropped ? 
                               next0 : ICMP6_ROUTER_SOLICITATION_NEXT_REPLY_TX;
                           vnet_buffer(p0)->sw_if_index[VLIB_TX] = sw_if_index0;
@@ -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);
@@ -1568,7 +1706,7 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm,
         /* fill in radv_info for this interface that will be needed later */
         a->adv_link_mtu = hw_if0->max_l3_packet_bytes[VLIB_RX];
         
-        memcpy (a->link_layer_address, eth_if0->address, 6);
+        clib_memcpy (a->link_layer_address, eth_if0->address, 6);
         
         /* fill in default link-local address  (this may be overridden) */
         ip6_link_local_address_from_ethernet_address (&a->link_local_address, eth_if0->address);
@@ -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  */
@@ -1666,7 +1772,7 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm,
             mcast_group_info->type = 4;
             mcast_group_info->mcast_source_address_pool = 0;
             mcast_group_info->num_sources = 0;
-            memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
+            clib_memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
           } 
         
         ip6_set_reserved_multicast_address (&addr,
@@ -1688,7 +1794,7 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm,
             mcast_group_info->type = 4;
             mcast_group_info->mcast_source_address_pool = 0;
             mcast_group_info->num_sources = 0;
-            memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
+            clib_memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
           } 
         
         ip6_set_reserved_multicast_address (&addr,
@@ -1710,7 +1816,7 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm,
             mcast_group_info->type = 4;
             mcast_group_info->mcast_source_address_pool = 0;
             mcast_group_info->num_sources = 0;
-            memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
+            clib_memcpy(&mcast_group_info->mcast_address, &addr, sizeof(ip6_address_t));
           } 
        }
    } 
@@ -1824,7 +1930,7 @@ ip6_neighbor_send_mldpv2_report(u32 sw_if_index)
        rr.type = m->type;
        rr.aux_data_len_u32s = 0;
        rr.num_sources = clib_host_to_net_u16 (m->num_sources);
-       memcpy(&rr.mcast_addr, &m->mcast_address, sizeof(ip6_address_t));
+       clib_memcpy(&rr.mcast_addr, &m->mcast_address, sizeof(ip6_address_t));
 
        num_addr_records++;
 
@@ -1894,8 +2000,8 @@ ip6_neighbor_process_timer_event (vlib_main_t * vm,
   ip6_radv_t *radv_info; 
   vlib_frame_t * f = 0; 
   u32 n_this_frame = 0;
-  u32 n_left_to_next;
-  u32 * to_next;
+  u32 n_left_to_next = 0;
+  u32 * to_next = 0;
   u32 bo0; 
   icmp6_router_solicitation_header_t * h0;
   vlib_buffer_t * b0;
@@ -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;
@@ -2270,7 +2376,7 @@ ip6_neighbor_ra_prefix(vlib_main_t * vm, u32 sw_if_index,
          memset(prefix, 0x0, sizeof(ip6_radv_prefix_t));
          
          prefix->prefix_len = prefix_len;
-         memcpy(&prefix->prefix,  prefix_addr, sizeof(ip6_address_t));
+         clib_memcpy(&prefix->prefix,  prefix_addr, sizeof(ip6_address_t));
          
          /* initialize default values */
          prefix->adv_on_link_flag = 1;      /* L bit set */
@@ -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 <iface name>",
+  .short_help = "show ip6 interface <iface name>",
 };
 
 clib_error_t *
@@ -2775,7 +2915,7 @@ enable_ip6_interface(vlib_main_t * vm,
                      md5_add (&m, &link_local_address, 16);
                      md5_finish (&m,  digest);
                      
-                     memcpy(&link_local_address, digest, 16);
+                     clib_memcpy(&link_local_address, digest, 16);
                      
                      radv_info->randomizer = link_local_address.as_u64[0];
                      
@@ -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)
@@ -3028,7 +3169,7 @@ ip6_neighbor_add_del_interface_address (ip6_main_t * im,
              mcast_group_info->type = 4;
              mcast_group_info->mcast_source_address_pool = 0;
              mcast_group_info->num_sources = 0;
-             memcpy(&mcast_group_info->mcast_address, &a, sizeof(ip6_address_t));
+             clib_memcpy(&mcast_group_info->mcast_address, &a, sizeof(ip6_address_t));
            } 
        }
     }
@@ -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* */
+}