LISP: support for neighbor discovery

Change-Id: I0f1a051dd3b5786dc7c457bc6fc7ce4fcd0f530c
Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
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* */