LISP: support for neighbor discovery

Change-Id: I0f1a051dd3b5786dc7c457bc6fc7ce4fcd0f530c
Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index ff3354c..520ae4f 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -3393,6 +3393,71 @@
 }
 
 static void
+  vl_api_one_ndp_entries_get_reply_t_handler
+  (vl_api_one_ndp_entries_get_reply_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  u32 i, n;
+  int retval = clib_net_to_host_u32 (mp->retval);
+
+  if (retval)
+    goto end;
+
+  n = clib_net_to_host_u32 (mp->count);
+
+  for (i = 0; i < n; i++)
+    print (vam->ofp, "%U -> %U", format_ip6_address, &mp->entries[i].ip6,
+	   format_ethernet_address, mp->entries[i].mac);
+
+end:
+  vam->retval = retval;
+  vam->result_ready = 1;
+}
+
+static void
+  vl_api_one_ndp_entries_get_reply_t_handler_json
+  (vl_api_one_ndp_entries_get_reply_t * mp)
+{
+  u8 *s = 0;
+  vat_main_t *vam = &vat_main;
+  vat_json_node_t *e = 0, root;
+  u32 i, n;
+  int retval = clib_net_to_host_u32 (mp->retval);
+  vl_api_one_ndp_entry_t *arp_entry;
+
+  if (retval)
+    goto end;
+
+  n = clib_net_to_host_u32 (mp->count);
+  vat_json_init_array (&root);
+
+  for (i = 0; i < n; i++)
+    {
+      e = vat_json_array_add (&root);
+      arp_entry = &mp->entries[i];
+
+      vat_json_init_object (e);
+      s = format (0, "%U", format_ethernet_address, arp_entry->mac);
+      vec_add1 (s, 0);
+
+      vat_json_object_add_string_copy (e, "mac", s);
+      vec_free (s);
+
+      s = format (0, "%U", format_ip6_address, &arp_entry->ip6);
+      vec_add1 (s, 0);
+      vat_json_object_add_string_copy (e, "ip6", s);
+      vec_free (s);
+    }
+
+  vat_json_print (vam->ofp, &root);
+  vat_json_free (&root);
+
+end:
+  vam->retval = retval;
+  vam->result_ready = 1;
+}
+
+static void
   vl_api_one_l2_arp_entries_get_reply_t_handler
   (vl_api_one_l2_arp_entries_get_reply_t * mp)
 {
@@ -3458,6 +3523,57 @@
 }
 
 static void
+vl_api_one_ndp_bd_get_reply_t_handler (vl_api_one_ndp_bd_get_reply_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  u32 i, n;
+  int retval = clib_net_to_host_u32 (mp->retval);
+
+  if (retval)
+    goto end;
+
+  n = clib_net_to_host_u32 (mp->count);
+
+  for (i = 0; i < n; i++)
+    {
+      print (vam->ofp, "%d", clib_net_to_host_u32 (mp->bridge_domains[i]));
+    }
+
+end:
+  vam->retval = retval;
+  vam->result_ready = 1;
+}
+
+static void
+  vl_api_one_ndp_bd_get_reply_t_handler_json
+  (vl_api_one_ndp_bd_get_reply_t * mp)
+{
+  vat_main_t *vam = &vat_main;
+  vat_json_node_t root;
+  u32 i, n;
+  int retval = clib_net_to_host_u32 (mp->retval);
+
+  if (retval)
+    goto end;
+
+  n = clib_net_to_host_u32 (mp->count);
+  vat_json_init_array (&root);
+
+  for (i = 0; i < n; i++)
+    {
+      vat_json_array_add_uint (&root,
+			       clib_net_to_host_u32 (mp->bridge_domains[i]));
+    }
+
+  vat_json_print (vam->ofp, &root);
+  vat_json_free (&root);
+
+end:
+  vam->retval = retval;
+  vam->result_ready = 1;
+}
+
+static void
   vl_api_one_l2_arp_bd_get_reply_t_handler
   (vl_api_one_l2_arp_bd_get_reply_t * mp)
 {
@@ -4590,6 +4706,10 @@
 #define vl_api_one_l2_arp_entries_get_reply_t_endian vl_noop_handler
 #define vl_api_one_l2_arp_entries_get_reply_t_print vl_noop_handler
 #define vl_api_one_l2_arp_bd_get_reply_t_endian vl_noop_handler
+#define vl_api_one_ndp_bd_get_reply_t_endian vl_noop_handler
+#define vl_api_one_ndp_bd_get_reply_t_print vl_noop_handler
+#define vl_api_one_ndp_entries_get_reply_t_print vl_noop_handler
+#define vl_api_one_ndp_entries_get_reply_t_endian vl_noop_handler
 
 /*
  * Generate boilerplate reply handlers, which
@@ -4706,6 +4826,7 @@
 _(one_use_petr_reply)                                   \
 _(one_stats_enable_disable_reply)                       \
 _(one_add_del_l2_arp_entry_reply)                       \
+_(one_add_del_ndp_entry_reply)                          \
 _(one_stats_flush_reply)                                \
 _(gpe_enable_disable_reply)                             \
 _(gpe_set_encap_mode_reply)                             \
@@ -4950,6 +5071,9 @@
 _(ONE_STATS_ENABLE_DISABLE_REPLY, one_stats_enable_disable_reply)       \
 _(SHOW_ONE_STATS_ENABLE_DISABLE_REPLY,                                  \
   show_one_stats_enable_disable_reply)                                  \
+_(ONE_ADD_DEL_NDP_ENTRY_REPLY, one_add_del_ndp_entry_reply)             \
+_(ONE_NDP_BD_GET_REPLY, one_ndp_bd_get_reply)                           \
+_(ONE_NDP_ENTRIES_GET_REPLY, one_ndp_entries_get_reply)                 \
 _(ONE_ADD_DEL_L2_ARP_ENTRY_REPLY, one_add_del_l2_arp_entry_reply)       \
 _(ONE_L2_ARP_BD_GET_REPLY, one_l2_arp_bd_get_reply)                     \
 _(ONE_L2_ARP_ENTRIES_GET_REPLY, one_l2_arp_entries_get_reply)           \
@@ -15414,6 +15538,58 @@
 #define api_show_lisp_rloc_probe_state api_show_one_rloc_probe_state
 
 static int
+api_one_add_del_ndp_entry (vat_main_t * vam)
+{
+  vl_api_one_add_del_ndp_entry_t *mp;
+  unformat_input_t *input = vam->input;
+  u8 is_add = 1;
+  u8 mac_set = 0;
+  u8 bd_set = 0;
+  u8 ip_set = 0;
+  u8 mac[6] = { 0, };
+  u8 ip6[16] = { 0, };
+  u32 bd = ~0;
+  int ret;
+
+  /* Parse args required to build the message */
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "del"))
+	is_add = 0;
+      else if (unformat (input, "mac %U", unformat_ethernet_address, mac))
+	mac_set = 1;
+      else if (unformat (input, "ip %U", unformat_ip6_address, ip6))
+	ip_set = 1;
+      else if (unformat (input, "bd %d", &bd))
+	bd_set = 1;
+      else
+	{
+	  errmsg ("parse error '%U'", format_unformat_error, input);
+	  return -99;
+	}
+    }
+
+  if (!bd_set || !ip_set || (!mac_set && is_add))
+    {
+      errmsg ("Missing BD, IP or MAC!");
+      return -99;
+    }
+
+  M (ONE_ADD_DEL_NDP_ENTRY, mp);
+  mp->is_add = is_add;
+  clib_memcpy (mp->mac, mac, 6);
+  mp->bd = clib_host_to_net_u32 (bd);
+  clib_memcpy (mp->ip6, ip6, sizeof (mp->ip6));
+
+  /* send */
+  S (mp);
+
+  /* wait for reply */
+  W (ret);
+  return ret;
+}
+
+static int
 api_one_add_del_l2_arp_entry (vat_main_t * vam)
 {
   vl_api_one_add_del_l2_arp_entry_t *mp;
@@ -15465,6 +15641,60 @@
 }
 
 static int
+api_one_ndp_bd_get (vat_main_t * vam)
+{
+  vl_api_one_ndp_bd_get_t *mp;
+  int ret;
+
+  M (ONE_NDP_BD_GET, mp);
+
+  /* send */
+  S (mp);
+
+  /* wait for reply */
+  W (ret);
+  return ret;
+}
+
+static int
+api_one_ndp_entries_get (vat_main_t * vam)
+{
+  vl_api_one_ndp_entries_get_t *mp;
+  unformat_input_t *input = vam->input;
+  u8 bd_set = 0;
+  u32 bd = ~0;
+  int ret;
+
+  /* Parse args required to build the message */
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "bd %d", &bd))
+	bd_set = 1;
+      else
+	{
+	  errmsg ("parse error '%U'", format_unformat_error, input);
+	  return -99;
+	}
+    }
+
+  if (!bd_set)
+    {
+      errmsg ("Expected bridge domain!");
+      return -99;
+    }
+
+  M (ONE_NDP_ENTRIES_GET, mp);
+  mp->bd = clib_host_to_net_u32 (bd);
+
+  /* send */
+  S (mp);
+
+  /* wait for reply */
+  W (ret);
+  return ret;
+}
+
+static int
 api_one_l2_arp_bd_get (vat_main_t * vam)
 {
   vl_api_one_l2_arp_bd_get_t *mp;
@@ -20411,6 +20641,9 @@
 _(one_locator_dump, "ls_index <index> | ls_name <name>")                \
 _(one_eid_table_dump, "[eid <ipv4|ipv6>/<prefix> | <mac>] [vni] "       \
                        "[local] | [remote]")                            \
+_(one_add_del_ndp_entry, "[del] mac <mac> bd <bd> ip6 <ip6>")           \
+_(one_ndp_bd_get, "")                                                   \
+_(one_ndp_entries_get, "bd <bridge-domain>")                            \
 _(one_add_del_l2_arp_entry, "[del] mac <mac> bd <bd> ip4 <ip4>")        \
 _(one_l2_arp_bd_get, "")                                                \
 _(one_l2_arp_entries_get, "bd <bridge-domain>")                         \
diff --git a/src/vnet/lisp-cp/control.c b/src/vnet/lisp-cp/control.c
index 74597a6..7aa3b41 100644
--- a/src/vnet/lisp-cp/control.c
+++ b/src/vnet/lisp-cp/control.c
@@ -908,7 +908,11 @@
 add_l2_arp_bd (BVT (clib_bihash_kv) * kvp, void *arg)
 {
   u32 **ht = arg;
-  u32 bd = (u32) kvp->key[0];
+  u32 version = (u32) kvp->key[0];
+  if (IP6 == version)
+    return;
+
+  u32 bd = (u32) (kvp->key[0] >> 32);
   hash_set (ht[0], bd, 0);
 }
 
@@ -918,8 +922,31 @@
   lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
   u32 *bds = 0;
 
-  gid_dict_foreach_l2_arp_entry (&lcm->mapping_index_by_gid,
-				 add_l2_arp_bd, &bds);
+  gid_dict_foreach_l2_arp_ndp_entry (&lcm->mapping_index_by_gid,
+				     add_l2_arp_bd, &bds);
+  return bds;
+}
+
+static void
+add_ndp_bd (BVT (clib_bihash_kv) * kvp, void *arg)
+{
+  u32 **ht = arg;
+  u32 version = (u32) kvp->key[0];
+  if (IP4 == version)
+    return;
+
+  u32 bd = (u32) (kvp->key[0] >> 32);
+  hash_set (ht[0], bd, 0);
+}
+
+u32 *
+vnet_lisp_ndp_bds_get (void)
+{
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  u32 *bds = 0;
+
+  gid_dict_foreach_l2_arp_ndp_entry (&lcm->mapping_index_by_gid,
+				     add_ndp_bd, &bds);
   return bds;
 }
 
@@ -927,15 +954,21 @@
 {
   void *vector;
   u32 bd;
-} lisp_add_l2_arp_args_t;
+} lisp_add_l2_arp_ndp_args_t;
 
 static void
 add_l2_arp_entry (BVT (clib_bihash_kv) * kvp, void *arg)
 {
-  lisp_add_l2_arp_args_t *a = arg;
+  lisp_add_l2_arp_ndp_args_t *a = arg;
   lisp_api_l2_arp_entry_t **vector = a->vector, e;
 
-  if ((u32) kvp->key[0] == a->bd)
+  u32 version = (u32) kvp->key[0];
+  if (IP6 == version)
+    return;
+
+  u32 bd = (u32) (kvp->key[0] >> 32);
+
+  if (bd == a->bd)
     {
       mac_copy (e.mac, (void *) &kvp->value);
       e.ip4 = (u32) kvp->key[1];
@@ -948,13 +981,48 @@
 {
   lisp_api_l2_arp_entry_t *entries = 0;
   lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
-  lisp_add_l2_arp_args_t a;
+  lisp_add_l2_arp_ndp_args_t a;
 
   a.vector = &entries;
   a.bd = bd;
 
-  gid_dict_foreach_l2_arp_entry (&lcm->mapping_index_by_gid,
-				 add_l2_arp_entry, &a);
+  gid_dict_foreach_l2_arp_ndp_entry (&lcm->mapping_index_by_gid,
+				     add_l2_arp_entry, &a);
+  return entries;
+}
+
+static void
+add_ndp_entry (BVT (clib_bihash_kv) * kvp, void *arg)
+{
+  lisp_add_l2_arp_ndp_args_t *a = arg;
+  lisp_api_ndp_entry_t **vector = a->vector, e;
+
+  u32 version = (u32) kvp->key[0];
+  if (IP4 == version)
+    return;
+
+  u32 bd = (u32) (kvp->key[0] >> 32);
+
+  if (bd == a->bd)
+    {
+      mac_copy (e.mac, (void *) &kvp->value);
+      clib_memcpy (e.ip6, &kvp->key[1], 16);
+      vec_add1 (vector[0], e);
+    }
+}
+
+lisp_api_ndp_entry_t *
+vnet_lisp_ndp_entries_get_by_bd (u32 bd)
+{
+  lisp_api_ndp_entry_t *entries = 0;
+  lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
+  lisp_add_l2_arp_ndp_args_t a;
+
+  a.vector = &entries;
+  a.bd = bd;
+
+  gid_dict_foreach_l2_arp_ndp_entry (&lcm->mapping_index_by_gid,
+				     add_ndp_entry, &a);
   return entries;
 }
 
@@ -2236,7 +2304,9 @@
 #define foreach_lisp_cp_lookup_error           \
 _(DROP, "drop")                                \
 _(MAP_REQUESTS_SENT, "map-request sent")       \
-_(ARP_REPLY_TX, "ARP replies sent")
+_(ARP_REPLY_TX, "ARP replies sent")            \
+_(NDP_NEIGHBOR_ADVERTISEMENT_TX,               \
+  "neighbor advertisement sent")
 
 static char *lisp_cp_lookup_error_strings[] = {
 #define _(sym,string) string,
@@ -2255,7 +2325,7 @@
 typedef enum
 {
   LISP_CP_LOOKUP_NEXT_DROP,
-  LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX,
+  LISP_CP_LOOKUP_NEXT_ARP_NDP_REPLY_TX,
   LISP_CP_LOOKUP_N_NEXT,
 } lisp_cp_lookup_next_t;
 
@@ -3069,6 +3139,7 @@
 {
   ethernet_header_t *eh;
   u32 vni = 0;
+  icmp6_neighbor_discovery_ethernet_link_layer_address_option_t *opt;
 
   memset (src, 0, sizeof (*src));
   memset (dst, 0, sizeof (*dst));
@@ -3116,6 +3187,33 @@
 	}
       else
 	{
+	  if (clib_net_to_host_u16 (eh->type) == ETHERNET_TYPE_IP6)
+	    {
+	      ip6_header_t *ip;
+	      ip = (ip6_header_t *) (eh + 1);
+
+	      if (IP_PROTOCOL_ICMP6 == ip->protocol)
+		{
+		  icmp6_neighbor_solicitation_or_advertisement_header_t *ndh;
+		  ndh = ip6_next_header (ip);
+		  if (ndh->icmp.type == ICMP6_neighbor_solicitation)
+		    {
+		      opt = (void *) (ndh + 1);
+		      if ((opt->header.type !=
+			   ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address)
+			  || (opt->header.n_data_u64s != 1))
+			return;	/* source link layer address option not present */
+
+		      gid_address_type (dst) = GID_ADDR_NDP;
+		      gid_address_ndp_bd (dst) =
+			lisp_get_bd_from_buffer_eth (b);
+		      ip_address_set (&gid_address_arp_ndp_ip (dst),
+				      &ndh->target_address, IP6);
+		      return;
+		    }
+		}
+	    }
+
 	  gid_address_type (src) = GID_ADDR_MAC;
 	  gid_address_type (dst) = GID_ADDR_MAC;
 	  mac_copy (&gid_address_mac (src), eh->src_address);
@@ -3151,6 +3249,7 @@
 		       vlib_node_runtime_t * node,
 		       vlib_frame_t * from_frame, int overlay)
 {
+  icmp6_neighbor_discovery_ethernet_link_layer_address_option_t *opt;
   u32 *from, *to_next, di, si;
   lisp_cp_main_t *lcm = vnet_lisp_cp_get_main ();
   u32 pkts_mapped = 0, next_index;
@@ -3174,6 +3273,9 @@
 	  ethernet_arp_header_t *arp0;
 	  ethernet_header_t *eth0;
 	  vnet_hw_interface_t *hw_if0;
+	  ethernet_header_t *eh0;
+	  icmp6_neighbor_solicitation_or_advertisement_header_t *ndh;
+	  ip6_header_t *ip0;
 
 	  pi0 = from[0];
 	  from += 1;
@@ -3190,41 +3292,70 @@
 	  if (gid_address_type (&dst) == GID_ADDR_ARP)
 	    {
 	      mac0 = gid_dictionary_lookup (&lcm->mapping_index_by_gid, &dst);
-	      if (GID_LOOKUP_MISS_L2 != mac0)
-		{
-		  /* send ARP reply */
+	      if (GID_LOOKUP_MISS_L2 == mac0)
+		goto drop;
 
-		  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
-		  vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0;
+	      /* send ARP reply */
+	      sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+	      vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0;
 
-		  hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
+	      hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
 
-		  eth0 = vlib_buffer_get_current (b0);
-		  arp0 = (ethernet_arp_header_t *) (((u8 *) eth0)
-						    + sizeof (*eth0));
-		  arp0->opcode =
-		    clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
-		  arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
-		  clib_memcpy (arp0->ip4_over_ethernet[0].ethernet,
-			       (u8 *) & mac0, 6);
-		  clib_memcpy (&arp0->ip4_over_ethernet[0].ip4,
-			       &gid_address_arp_ip4 (&dst), 4);
+	      eth0 = vlib_buffer_get_current (b0);
+	      arp0 = (ethernet_arp_header_t *) (((u8 *) eth0)
+						+ sizeof (*eth0));
+	      arp0->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply);
+	      arp0->ip4_over_ethernet[1] = arp0->ip4_over_ethernet[0];
+	      clib_memcpy (arp0->ip4_over_ethernet[0].ethernet,
+			   (u8 *) & mac0, 6);
+	      clib_memcpy (&arp0->ip4_over_ethernet[0].ip4,
+			   &gid_address_arp_ip4 (&dst), 4);
 
-		  /* Hardware must be ethernet-like. */
-		  ASSERT (vec_len (hw_if0->hw_address) == 6);
+	      /* Hardware must be ethernet-like. */
+	      ASSERT (vec_len (hw_if0->hw_address) == 6);
 
-		  clib_memcpy (eth0->dst_address, eth0->src_address, 6);
-		  clib_memcpy (eth0->src_address, hw_if0->hw_address, 6);
+	      clib_memcpy (eth0->dst_address, eth0->src_address, 6);
+	      clib_memcpy (eth0->src_address, hw_if0->hw_address, 6);
 
-		  b0->error = node->errors[LISP_CP_LOOKUP_ERROR_ARP_REPLY_TX];
-		  next0 = LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX;
-		  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
-						   to_next,
-						   n_left_to_next, pi0,
-						   next0);
-		  continue;
-		}
-	      goto done;
+	      b0->error = node->errors[LISP_CP_LOOKUP_ERROR_ARP_REPLY_TX];
+	      next0 = LISP_CP_LOOKUP_NEXT_ARP_NDP_REPLY_TX;
+	      goto enqueue;
+	    }
+	  else if (gid_address_type (&dst) == GID_ADDR_NDP)
+	    {
+	      mac0 = gid_dictionary_lookup (&lcm->mapping_index_by_gid, &dst);
+	      if (GID_LOOKUP_MISS_L2 == mac0)
+		goto drop;
+
+	      sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+	      vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0;
+
+	      eh0 = vlib_buffer_get_current (b0);
+	      ip0 = (ip6_header_t *) (eh0 + 1);
+	      ndh = ip6_next_header (ip0);
+	      int bogus_length;
+	      ip0->dst_address = ip0->src_address;
+	      ip0->src_address = ndh->target_address;
+	      ip0->hop_limit = 255;
+	      opt = (void *) (ndh + 1);
+	      opt->header.type =
+		ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address;
+	      clib_memcpy (opt->ethernet_address, (u8 *) & mac0, 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, b0, ip0,
+						   &bogus_length);
+	      clib_memcpy (eh0->dst_address, eh0->src_address, 6);
+	      clib_memcpy (eh0->src_address, (u8 *) & mac0, 6);
+	      b0->error =
+		node->errors
+		[LISP_CP_LOOKUP_ERROR_NDP_NEIGHBOR_ADVERTISEMENT_TX];
+	      next0 = LISP_CP_LOOKUP_NEXT_ARP_NDP_REPLY_TX;
+	      goto enqueue;
 	    }
 
 	  /* if we have remote mapping for destination already in map-chache
@@ -3267,8 +3398,10 @@
 	      pkts_mapped++;
 	    }
 
-	done:
+	drop:
 	  b0->error = node->errors[LISP_CP_LOOKUP_ERROR_DROP];
+	  next0 = LISP_CP_LOOKUP_NEXT_DROP;
+	enqueue:
 	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
 	    {
 	      lisp_cp_lookup_trace_t *tr = vlib_add_trace (vm, node, b0,
@@ -3281,7 +3414,6 @@
 	    }
 	  gid_address_free (&dst);
 	  gid_address_free (&src);
-	  next0 = LISP_CP_LOOKUP_NEXT_DROP;
 	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
 					   to_next,
 					   n_left_to_next, pi0, next0);
@@ -3339,7 +3471,7 @@
 
   .next_nodes = {
       [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop",
-      [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output",
+      [LISP_CP_LOOKUP_NEXT_ARP_NDP_REPLY_TX] = "interface-output",
   },
 };
 /* *INDENT-ON* */
@@ -3359,7 +3491,7 @@
 
   .next_nodes = {
       [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop",
-      [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output",
+      [LISP_CP_LOOKUP_NEXT_ARP_NDP_REPLY_TX] = "interface-output",
   },
 };
 /* *INDENT-ON* */
@@ -3379,7 +3511,7 @@
 
   .next_nodes = {
       [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop",
-      [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output",
+      [LISP_CP_LOOKUP_NEXT_ARP_NDP_REPLY_TX] = "interface-output",
   },
 };
 /* *INDENT-ON* */
@@ -3399,7 +3531,7 @@
 
   .next_nodes = {
       [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop",
-      [LISP_CP_LOOKUP_NEXT_ARP_REPLY_TX] = "interface-output",
+      [LISP_CP_LOOKUP_NEXT_ARP_NDP_REPLY_TX] = "interface-output",
   },
 };
 /* *INDENT-ON* */
diff --git a/src/vnet/lisp-cp/control.h b/src/vnet/lisp-cp/control.h
index 12bfcb5..d40f6f5 100644
--- a/src/vnet/lisp-cp/control.h
+++ b/src/vnet/lisp-cp/control.h
@@ -103,6 +103,12 @@
   u32 ip4;
 } lisp_api_l2_arp_entry_t;
 
+typedef struct
+{
+  u8 mac[6];
+  u8 ip6[16];
+} lisp_api_ndp_entry_t;
+
 typedef enum
 {
   MR_MODE_DST_ONLY = 0,
@@ -395,6 +401,8 @@
 u32 vnet_lisp_map_register_get_ttl (void);
 int vnet_lisp_map_register_fallback_threshold_set (u32 value);
 u32 vnet_lisp_map_register_fallback_threshold_get (void);
+u32 *vnet_lisp_ndp_bds_get (void);
+lisp_api_ndp_entry_t *vnet_lisp_ndp_entries_get_by_bd (u32 bd);
 
 map_records_arg_t *parse_map_reply (vlib_buffer_t * b);
 
diff --git a/src/vnet/lisp-cp/gid_dictionary.c b/src/vnet/lisp-cp/gid_dictionary.c
index cf9a741..c3b9330 100644
--- a/src/vnet/lisp-cp/gid_dictionary.c
+++ b/src/vnet/lisp-cp/gid_dictionary.c
@@ -139,12 +139,13 @@
 }
 
 void
-gid_dict_foreach_l2_arp_entry (gid_dictionary_t * db, void (*cb)
-			       (BVT (clib_bihash_kv) * kvp, void *arg),
-			       void *ht)
+gid_dict_foreach_l2_arp_ndp_entry (gid_dictionary_t * db, void (*cb)
+				   (BVT (clib_bihash_kv) * kvp, void *arg),
+				   void *ht)
 {
-  gid_l2_arp_table_t *tab = &db->arp_table;
-  BV (clib_bihash_foreach_key_value_pair) (&tab->arp_lookup_table, cb, ht);
+  gid_l2_arp_ndp_table_t *tab = &db->arp_ndp_table;
+  BV (clib_bihash_foreach_key_value_pair) (&tab->arp_ndp_lookup_table, cb,
+					   ht);
 }
 
 static void
@@ -338,11 +339,19 @@
 }
 
 static void
-make_arp_key (BVT (clib_bihash_kv) * kv, u32 bd, ip4_address_t * addr)
+make_arp_ndp_key (BVT (clib_bihash_kv) * kv, u32 bd, ip_address_t * addr)
 {
-  kv->key[0] = (u64) bd;
-  kv->key[1] = (u64) addr->as_u32;
-  kv->key[2] = (u64) 0;
+  kv->key[0] = ((u64) bd << 32) | (u32) ip_addr_version (addr);
+  if (ip_addr_version (addr) == IP4)
+    {
+      kv->key[1] = (u64) addr->ip.v4.as_u32;
+      kv->key[2] = (u64) 0;
+    }
+  else
+    {
+      kv->key[1] = (u64) addr->ip.v6.as_u64[0];
+      kv->key[2] = (u64) addr->ip.v6.as_u64[1];
+    }
 }
 
 static void
@@ -354,13 +363,14 @@
 }
 
 static u64
-arp_lookup (gid_l2_arp_table_t * db, u32 bd, ip4_address_t * key)
+arp_ndp_lookup (gid_l2_arp_ndp_table_t * db, u32 bd, ip_address_t * key)
 {
   int rv;
   BVT (clib_bihash_kv) kv, value;
 
-  make_arp_key (&kv, bd, key);
-  rv = BV (clib_bihash_search_inline_2) (&db->arp_lookup_table, &kv, &value);
+  make_arp_ndp_key (&kv, bd, key);
+  rv = BV (clib_bihash_search_inline_2) (&db->arp_ndp_lookup_table, &kv,
+					 &value);
 
   if (rv == 0)
     return value.value;
@@ -414,8 +424,9 @@
 	}
       break;
     case GID_ADDR_ARP:
-      return arp_lookup (&db->arp_table, gid_address_arp_bd (key),
-			 &gid_address_arp_ip4 (key));
+    case GID_ADDR_NDP:
+      return arp_ndp_lookup (&db->arp_ndp_table, gid_address_arp_ndp_bd (key),
+			     &gid_address_arp_ndp_ip (key));
     case GID_ADDR_NSH:
       return nsh_lookup (&db->nsh_table, gid_address_vni (key),
 			 gid_address_nsh_spi (key), gid_address_nsh_si (key));
@@ -890,25 +901,27 @@
 }
 
 static u64
-add_del_arp (gid_l2_arp_table_t * db, u32 bd, ip4_address_t * key, u64 value,
-	     u8 is_add)
+add_del_arp_ndp (gid_l2_arp_ndp_table_t * db, u32 bd, ip_address_t * key,
+		 u64 value, u8 is_add)
 {
   BVT (clib_bihash_kv) kv, result;
   u32 old_val = ~0;
 
-  make_arp_key (&kv, bd, key);
-  if (BV (clib_bihash_search) (&db->arp_lookup_table, &kv, &result) == 0)
+  make_arp_ndp_key (&kv, bd, key);
+  if (BV (clib_bihash_search) (&db->arp_ndp_lookup_table, &kv, &result) == 0)
     old_val = result.value;
 
   if (is_add)
     {
       kv.value = value;
-      BV (clib_bihash_add_del) (&db->arp_lookup_table, &kv, 1 /* is_add */ );
+      BV (clib_bihash_add_del) (&db->arp_ndp_lookup_table, &kv,
+				1 /* is_add */ );
       db->count++;
     }
   else
     {
-      BV (clib_bihash_add_del) (&db->arp_lookup_table, &kv, 0 /* is_add */ );
+      BV (clib_bihash_add_del) (&db->arp_ndp_lookup_table, &kv,
+				0 /* is_add */ );
       db->count--;
     }
   return old_val;
@@ -955,8 +968,10 @@
       return add_del_sd (db, gid_address_vni (key), &gid_address_sd (key),
 			 (u32) value, is_add);
     case GID_ADDR_ARP:
-      return add_del_arp (&db->arp_table, gid_address_arp_bd (key),
-			  &gid_address_arp_ip4 (key), value, is_add);
+    case GID_ADDR_NDP:
+      return add_del_arp_ndp (&db->arp_ndp_table,
+			      gid_address_arp_ndp_bd (key),
+			      &gid_address_arp_ndp_ip (key), value, is_add);
     case GID_ADDR_NSH:
       return add_del_nsh (&db->nsh_table, gid_address_vni (key),
 			  gid_address_nsh_spi (key), gid_address_nsh_si (key),
@@ -987,20 +1002,21 @@
 }
 
 static void
-arp_lookup_init (gid_l2_arp_table_t * db)
+arp_ndp_lookup_init (gid_l2_arp_ndp_table_t * db)
 {
-  if (db->arp_lookup_table_nbuckets == 0)
-    db->arp_lookup_table_nbuckets = ARP_LOOKUP_DEFAULT_HASH_NUM_BUCKETS;
+  if (db->arp_ndp_lookup_table_nbuckets == 0)
+    db->arp_ndp_lookup_table_nbuckets =
+      ARP_NDP_LOOKUP_DEFAULT_HASH_NUM_BUCKETS;
 
-  db->arp_lookup_table_nbuckets =
-    1 << max_log2 (db->arp_lookup_table_nbuckets);
+  db->arp_ndp_lookup_table_nbuckets =
+    1 << max_log2 (db->arp_ndp_lookup_table_nbuckets);
 
-  if (db->arp_lookup_table_size == 0)
-    db->arp_lookup_table_size = ARP_LOOKUP_DEFAULT_HASH_MEMORY_SIZE;
+  if (db->arp_ndp_lookup_table_size == 0)
+    db->arp_ndp_lookup_table_size = ARP_NDP_LOOKUP_DEFAULT_HASH_MEMORY_SIZE;
 
-  BV (clib_bihash_init) (&db->arp_lookup_table, "arp lookup table",
-			 db->arp_lookup_table_nbuckets,
-			 db->arp_lookup_table_size);
+  BV (clib_bihash_init) (&db->arp_ndp_lookup_table, "arp ndp lookup table",
+			 db->arp_ndp_lookup_table_nbuckets,
+			 db->arp_ndp_lookup_table_size);
 }
 
 static void
@@ -1026,7 +1042,7 @@
   ip4_lookup_init (&db->dst_ip4_table);
   ip6_lookup_init (&db->dst_ip6_table);
   mac_lookup_init (&db->sd_mac_table);
-  arp_lookup_init (&db->arp_table);
+  arp_ndp_lookup_init (&db->arp_ndp_table);
   nsh_lookup_init (&db->nsh_table);
 }
 
diff --git a/src/vnet/lisp-cp/gid_dictionary.h b/src/vnet/lisp-cp/gid_dictionary.h
index 51806bd..3f8500e 100644
--- a/src/vnet/lisp-cp/gid_dictionary.h
+++ b/src/vnet/lisp-cp/gid_dictionary.h
@@ -36,9 +36,9 @@
 #define MAC_LOOKUP_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
 #define MAC_LOOKUP_DEFAULT_HASH_MEMORY_SIZE (32<<20)
 
-/* Default size of the ARP hash table */
-#define ARP_LOOKUP_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
-#define ARP_LOOKUP_DEFAULT_HASH_MEMORY_SIZE (32<<20)
+/* Default size of the ARP/NDP hash table */
+#define ARP_NDP_LOOKUP_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
+#define ARP_NDP_LOOKUP_DEFAULT_HASH_MEMORY_SIZE (32<<20)
 
 /* Default size of the NSH hash table */
 #define NSH_LOOKUP_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
@@ -100,16 +100,16 @@
 
 typedef struct
 {
-  BVT (clib_bihash) arp_lookup_table;
-  u32 arp_lookup_table_nbuckets;
-  uword arp_lookup_table_size;
+  BVT (clib_bihash) arp_ndp_lookup_table;
+  u32 arp_ndp_lookup_table_nbuckets;
+  uword arp_ndp_lookup_table_size;
   u64 count;
-} gid_l2_arp_table_t;
+} gid_l2_arp_ndp_table_t;
 
 typedef struct
 {
-  /** L2 ARP table */
-  gid_l2_arp_table_t arp_table;
+  /** L2 ARP/NDP table */
+  gid_l2_arp_ndp_table_t arp_ndp_table;
 
   /** NSH lookup table */
   gid_nsh_table_t nsh_table;
@@ -146,9 +146,9 @@
 			    foreach_subprefix_match_cb_t cb, void *arg);
 
 void
-gid_dict_foreach_l2_arp_entry (gid_dictionary_t * db, void (*cb)
-			       (BVT (clib_bihash_kv) * kvp, void *arg),
-			       void *ht);
+gid_dict_foreach_l2_arp_ndp_entry (gid_dictionary_t * db, void (*cb)
+				   (BVT (clib_bihash_kv) * kvp, void *arg),
+				   void *ht);
 
 #endif /* VNET_LISP_GPE_GID_DICTIONARY_H_ */
 
diff --git a/src/vnet/lisp-cp/lisp_types.c b/src/vnet/lisp-cp/lisp_types.c
index 622f39a..05f046f 100644
--- a/src/vnet/lisp-cp/lisp_types.c
+++ b/src/vnet/lisp-cp/lisp_types.c
@@ -279,8 +279,9 @@
       return format (s, "[%d] %U", gid_address_vni (a), format_mac_address,
 		     &gid_address_mac (a));
     case GID_ADDR_ARP:
-      return format (s, "[%d, %U]", gid_address_arp_bd (a),
-		     format_ip4_address, &gid_address_arp_ip4 (a));
+    case GID_ADDR_NDP:
+      return format (s, "[%d, %U]", gid_address_arp_ndp_bd (a),
+		     format_ip_address, &gid_address_arp_ndp_ip (a));
     case GID_ADDR_NSH:
       return format (s, "%U", format_nsh_address, &gid_address_nsh (a));
 
diff --git a/src/vnet/lisp-cp/lisp_types.h b/src/vnet/lisp-cp/lisp_types.h
index b17110f..4a919e7 100644
--- a/src/vnet/lisp-cp/lisp_types.h
+++ b/src/vnet/lisp-cp/lisp_types.h
@@ -91,6 +91,7 @@
   GID_ADDR_SRC_DST,
   GID_ADDR_NSH,
   GID_ADDR_ARP,
+  GID_ADDR_NDP,
   GID_ADDR_NO_ADDRESS,
   GID_ADDR_TYPES
 } gid_address_type_t;
@@ -172,12 +173,15 @@
 
 typedef struct
 {
-  ip4_address_t addr;
+  ip_address_t addr;
   u32 bd;
-} lcaf_arp_t;
+} lcaf_arp_ndp_t;
 
-#define lcaf_arp_ip4(_a) (_a)->addr
-#define lcaf_arp_bd(_a) (_a)->bd
+#define lcaf_arp_ndp_ip(_a) (_a)->addr
+#define lcaf_arp_ndp_ip_ver(_a) ip_addr_version(&lcaf_arp_ndp_ip(_a))
+#define lcaf_arp_ndp_ip4(_a) ip_addr_v4(&lcaf_arp_ndp_ip(_a))
+#define lcaf_arp_ndp_ip6(_a) ip_addr_v6(&lcaf_arp_ndp_ip(_a))
+#define lcaf_arp_ndp_bd(_a) (_a)->bd
 
 typedef struct
 {
@@ -185,7 +189,7 @@
   union
   {
     source_dest_t sd;
-    lcaf_arp_t arp;
+    lcaf_arp_ndp_t arp_ndp;
     vni_t uni;
   };
   u8 type;
@@ -204,7 +208,7 @@
     lcaf_t lcaf;
     u8 mac[6];
     source_dest_t sd;
-    lcaf_arp_t arp;
+    lcaf_arp_ndp_t arp_ndp;
     nsh_t nsh;
   };
   u8 type;
@@ -275,9 +279,13 @@
 #define gid_address_sd_dst(_a) sd_dst(&gid_address_sd(_a))
 #define gid_address_sd_src_type(_a) sd_src_type(&gid_address_sd(_a))
 #define gid_address_sd_dst_type(_a) sd_dst_type(&gid_address_sd(_a))
-#define gid_address_arp(_a) (_a)->arp
-#define gid_address_arp_ip4(_a) lcaf_arp_ip4(&gid_address_arp (_a))
-#define gid_address_arp_bd(_a) lcaf_arp_bd(&gid_address_arp (_a))
+#define gid_address_arp_ndp(_a) (_a)->arp_ndp
+#define gid_address_arp_ndp_bd(_a) lcaf_arp_ndp_bd(&gid_address_arp_ndp(_a))
+#define gid_address_arp_ndp_ip(_a) lcaf_arp_ndp_ip(&gid_address_arp_ndp(_a))
+#define gid_address_arp_ip4(_a) lcaf_arp_ndp_ip4(&gid_address_arp_ndp(_a))
+#define gid_address_ndp_ip6(_a) lcaf_arp_ndp_ip6(&gid_address_arp_ndp(_a))
+#define gid_address_ndp_bd gid_address_arp_ndp_bd
+#define gid_address_arp_bd gid_address_arp_ndp_bd
 
 /* 'sub'address functions */
 #define foreach_gid_address_type_fcns  \
diff --git a/src/vnet/lisp-cp/one_api.c b/src/vnet/lisp-cp/one_api.c
index 7c8ba63..96b3d2c 100644
--- a/src/vnet/lisp-cp/one_api.c
+++ b/src/vnet/lisp-cp/one_api.c
@@ -130,7 +130,9 @@
 _(ONE_L2_ARP_BD_GET, one_l2_arp_bd_get)                                 \
 _(ONE_L2_ARP_ENTRIES_GET, one_l2_arp_entries_get)                       \
 _(ONE_ADD_DEL_L2_ARP_ENTRY, one_add_del_l2_arp_entry)                   \
-
+_(ONE_ADD_DEL_NDP_ENTRY, one_add_del_ndp_entry)                         \
+_(ONE_NDP_BD_GET, one_ndp_bd_get)                                       \
+_(ONE_NDP_ENTRIES_GET, one_ndp_entries_get)                             \
 
 static locator_t *
 unformat_one_locs (vl_api_one_remote_locator_t * rmt_locs, u32 rloc_num)
@@ -1563,7 +1565,7 @@
   gid_address_arp_bd (arp) = clib_net_to_host_u32 (mp->bd);
 
   /* vpp keeps ip4 addresses in network byte order */
-  clib_memcpy (&gid_address_arp_ip4 (arp), &mp->ip4, 4);
+  ip_address_set (&gid_address_arp_ndp_ip (arp), &mp->ip4, IP4);
 
   rv = vnet_lisp_add_del_l2_arp_entry (arp, mp->mac, mp->is_add);
 
@@ -1571,6 +1573,48 @@
 }
 
 static void
+vl_api_one_add_del_ndp_entry_t_handler (vl_api_one_add_del_ndp_entry_t * mp)
+{
+  vl_api_one_add_del_ndp_entry_reply_t *rmp;
+  int rv = 0;
+  gid_address_t _g, *g = &_g;
+  memset (g, 0, sizeof (*g));
+
+  gid_address_type (g) = GID_ADDR_NDP;
+  gid_address_ndp_bd (g) = clib_net_to_host_u32 (mp->bd);
+  ip_address_set (&gid_address_arp_ndp_ip (g), mp->ip6, IP6);
+
+  rv = vnet_lisp_add_del_l2_arp_entry (g, mp->mac, mp->is_add);
+
+  REPLY_MACRO (VL_API_ONE_ADD_DEL_NDP_ENTRY_REPLY);
+}
+
+static void
+vl_api_one_ndp_bd_get_t_handler (vl_api_one_ndp_bd_get_t * mp)
+{
+  vl_api_one_ndp_bd_get_reply_t *rmp;
+  int rv = 0;
+  u32 i = 0;
+  hash_pair_t *p;
+
+  u32 *bds = vnet_lisp_ndp_bds_get ();
+  u32 size = hash_elts (bds) * sizeof (u32);
+
+  /* *INDENT-OFF* */
+  REPLY_MACRO4 (VL_API_ONE_NDP_BD_GET_REPLY, size,
+  {
+    rmp->count = clib_host_to_net_u32 (hash_elts (bds));
+    hash_foreach_pair (p, bds,
+    ({
+      rmp->bridge_domains[i++] = clib_host_to_net_u32 (p->key);
+    }));
+  });
+  /* *INDENT-ON* */
+
+  hash_free (bds);
+}
+
+static void
 vl_api_one_l2_arp_bd_get_t_handler (vl_api_one_l2_arp_bd_get_t * mp)
 {
   vl_api_one_l2_arp_bd_get_reply_t *rmp;
@@ -1653,6 +1697,35 @@
   /* *INDENT-ON* */
 }
 
+static void
+vl_api_one_ndp_entries_get_t_handler (vl_api_one_ndp_entries_get_t * mp)
+{
+  vl_api_one_ndp_entries_get_reply_t *rmp = 0;
+  lisp_api_ndp_entry_t *entries = 0, *e;
+  u32 i = 0;
+  int rv = 0;
+
+  u32 bd = clib_net_to_host_u32 (mp->bd);
+
+  entries = vnet_lisp_ndp_entries_get_by_bd (bd);
+  u32 size = vec_len (entries) * sizeof (vl_api_one_ndp_entry_t);
+
+  /* *INDENT-OFF* */
+  REPLY_MACRO4 (VL_API_ONE_NDP_ENTRIES_GET_REPLY, size,
+  {
+    rmp->count = clib_host_to_net_u32 (vec_len (entries));
+    vec_foreach (e, entries)
+      {
+        mac_copy (rmp->entries[i].mac, e->mac);
+        clib_memcpy (rmp->entries[i].ip6, e->ip6, 16);
+        i++;
+      }
+  });
+  /* *INDENT-ON* */
+
+  vec_free (entries);
+}
+
 /*
  * one_api_hookup
  * Add vpe's API message handlers to the table.
diff --git a/src/vnet/lisp-cp/one_cli.c b/src/vnet/lisp-cp/one_cli.c
index 3e0c4c0..1e52c9a 100644
--- a/src/vnet/lisp-cp/one_cli.c
+++ b/src/vnet/lisp-cp/one_cli.c
@@ -376,6 +376,42 @@
 };
 /* *INDENT-ON* */
 
+static clib_error_t *
+lisp_show_ndp_entries_command_fn (vlib_main_t * vm,
+				  unformat_input_t * input,
+				  vlib_cli_command_t * cmd)
+{
+  u32 *ht = vnet_lisp_ndp_bds_get ();
+  lisp_api_ndp_entry_t *entries, *e;
+  hash_pair_t *p;
+
+  /* *INDENT-OFF* */
+  hash_foreach_pair (p, ht,
+  ({
+    entries = vnet_lisp_ndp_entries_get_by_bd (p->key);
+    vlib_cli_output (vm, "Table: %d", p->key);
+
+    vec_foreach (e, entries)
+      {
+        vlib_cli_output (vm, "\t%U -> %U", format_ip6_address, &e->ip6,
+                         format_mac_address, e->mac);
+      }
+    vec_free (entries);
+  }));
+  /* *INDENT-ON* */
+
+  hash_free (ht);
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (one_show_ndp_entries_command) = {
+    .path = "show one ndp entries",
+    .short_help = "Show ONE NDP entries",
+    .function = lisp_show_ndp_entries_command_fn,
+};
+/* *INDENT-ON* */
+
 /**
  * Handler for add/del remote mapping CLI.
  *