A Protocol Independent Hierarchical FIB (VPP-352)
[vpp.git] / vnet / vnet / vxlan / vxlan.c
index 9e0d0a5..da359a8 100644 (file)
 #include <vnet/vxlan/vxlan.h>
 #include <vnet/ip/format.h>
 
+/**
+ * @file
+ * @brief VXLAN.
+ *
+ * VXLAN provides the features needed to allow L2 bridge domains (BDs)
+ * to span multiple servers. This is done by building an L2 overlay on
+ * top of an L3 network underlay using VXLAN tunnels.
+ *
+ * This makes it possible for servers to be co-located in the same data
+ * center or be separated geographically as long as they are reachable
+ * through the underlay L3 network.
+ *
+ * You can refer to this kind of L2 overlay bridge domain as a VXLAN
+ * (Virtual eXtensible VLAN) segment.
+ */
+
+
 vxlan_main_t vxlan_main;
 
 static u8 * format_decap_next (u8 * s, va_list * args)
@@ -45,8 +62,8 @@ u8 * format_vxlan_tunnel (u8 * s, va_list * args)
   s = format (s, 
               "[%d] %U (src) %U (dst) vni %d encap_fib_index %d",
               t - ngm->tunnels,
-              format_ip46_address, &t->src,
-              format_ip46_address, &t->dst,
+              format_ip46_address, &t->src, IP46_TYPE_ANY,
+              format_ip46_address, &t->dst, IP46_TYPE_ANY,
               t->vni,
               t->encap_fib_index);
   s = format (s, " decap_next %U\n", format_decap_next, t->decap_next_index);
@@ -207,6 +224,7 @@ int vnet_vxlan_add_del_tunnel
   int rv;
   vxlan4_tunnel_key_t key4;
   vxlan6_tunnel_key_t key6;
+  l2output_main_t * l2om = &l2output_main;
 
   if (!a->is_ip6) {
     key4.src = a->dst.ip4.as_u32; /* decap src in key is encap dst in config */
@@ -218,7 +236,7 @@ int vnet_vxlan_add_del_tunnel
     key6.src.as_u64[1] = a->dst.ip6.as_u64[1];
     key6.vni = clib_host_to_net_u32 (a->vni << 8);
 
-    p = hash_get (vxm->vxlan6_tunnel_by_key, pointer_to_uword(&key6));
+    p = hash_get_mem (vxm->vxlan6_tunnel_by_key, &key6);
   }
   
   if (a->is_add)
@@ -243,10 +261,16 @@ int vnet_vxlan_add_del_tunnel
       else            foreach_copy_ipv6
 #undef _
       
-      if (a->is_ip6) {
-        /* copy the key */
-        t->key6 = key6;
-      }
+      /* copy the key */
+      if (a->is_ip6)
+        {
+          t->key6 = clib_mem_alloc (sizeof(vxlan6_tunnel_key_t));
+          clib_memcpy (t->key6, &key6, sizeof(key6));
+        }
+      else
+        {
+          t->key4 = 0; /* not yet used */
+        }
 
       if (!a->is_ip6) t->flags |= VXLAN_TUNNEL_IS_IPV4;
 
@@ -265,7 +289,7 @@ int vnet_vxlan_add_del_tunnel
       if (!a->is_ip6)
         hash_set (vxm->vxlan4_tunnel_by_key, key4.as_u64, t - vxm->tunnels);
       else
-        hash_set (vxm->vxlan6_tunnel_by_key, pointer_to_uword(&t->key6), t - vxm->tunnels);
+        hash_set_mem (vxm->vxlan6_tunnel_by_key, t->key6, t - vxm->tunnels);
       
       if (vec_len (vxm->free_vxlan_tunnel_hw_if_indices) > 0)
         {
@@ -312,14 +336,25 @@ int vnet_vxlan_add_del_tunnel
          l2im->configs[sw_if_index].feature_bitmap = L2INPUT_FEAT_DROP;
          l2im->configs[sw_if_index].bd_index = 0;
        }
+      
+      /* 
+       * Directs the l2 output path to work out the interface
+       * output next-arc itself. Needed when recycling a tunnel.
+       */
+      vec_validate_init_empty(l2om->next_nodes.output_node_index_vec, 
+                              sw_if_index, ~0);
+      l2om->next_nodes.output_node_index_vec[t->sw_if_index] 
+        = ~0;
       vnet_sw_interface_set_flags (vnm, sw_if_index, 
                                    VNET_SW_INTERFACE_FLAG_ADMIN_UP);
       if (!a->is_ip6) {
-      vec_validate (im4->fib_index_by_sw_if_index, sw_if_index);
-      im4->fib_index_by_sw_if_index[sw_if_index] = t->encap_fib_index;
+        vec_validate (im4->fib_index_by_sw_if_index, sw_if_index);
+        im4->fib_index_by_sw_if_index[sw_if_index] = t->encap_fib_index;
+        ip4_sw_interface_enable_disable(sw_if_index, 1);
       } else {
         vec_validate (im6->fib_index_by_sw_if_index, sw_if_index);
         im6->fib_index_by_sw_if_index[sw_if_index] = t->encap_fib_index;
+        ip6_sw_interface_enable_disable(sw_if_index, 1);
       }
     }
   else
@@ -337,17 +372,22 @@ int vnet_vxlan_add_del_tunnel
 
       vxm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0;
 
+      /* Directs the l2 path to turf packets sent to this sw_if_index */
+      l2om->next_nodes.output_node_index_vec[t->sw_if_index] 
+        = L2OUTPUT_NEXT_DEL_TUNNEL;
+
       if (!a->is_ip6)
-        hash_unset (vxm->vxlan4_tunnel_by_key, key4.as_u64);
+        {
+          hash_unset (vxm->vxlan4_tunnel_by_key, key4.as_u64);
+          ip4_sw_interface_enable_disable(sw_if_index, 1);
+       }
       else
-        hash_unset (vxm->vxlan6_tunnel_by_key, pointer_to_uword(&key6));
-
+        {
+         hash_unset_mem (vxm->vxlan6_tunnel_by_key, t->key6);
+         clib_mem_free (t->key6);
+          ip6_sw_interface_enable_disable(sw_if_index, 1);
+       }
       vec_free (t->rewrite);
-      if (!a->is_ip6) {
-        t->rewrite = vxlan4_dummy_rewrite;
-      } else {
-        t->rewrite = vxlan6_dummy_rewrite;
-      }
       pool_put (vxm->tunnels, t);
     }
 
@@ -530,13 +570,35 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm,
   return 0;
 }
 
+/*?
+ * Add or delete a VXLAN Tunnel.
+ *
+ * VXLAN provides the features needed to allow L2 bridge domains (BDs)
+ * to span multiple servers. This is done by building an L2 overlay on
+ * top of an L3 network underlay using VXLAN tunnels.
+ *
+ * This makes it possible for servers to be co-located in the same data
+ * center or be separated geographically as long as they are reachable
+ * through the underlay L3 network.
+ *
+ * You can refer to this kind of L2 overlay bridge domain as a VXLAN
+ * (Virtual eXtensible VLAN) segment.
+ *
+ * @cliexpar
+ * Example of how to create a VXLAN Tunnel:
+ * @cliexcmd{create vxlan tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 encap-vrf-id 7 decap-next l2}
+ * Example of how to delete a VXLAN Tunnel:
+ * @cliexcmd{create vxlan tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 del}
+ ?*/
+/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (create_vxlan_tunnel_command, static) = {
   .path = "create vxlan tunnel",
   .short_help = 
   "create vxlan tunnel src <local-vtep-addr> dst <remote-vtep-addr> vni <nn>" 
-  " [encap-vrf-id <nn>] [decap-next [l2|ip4|ip6] [del]\n",
+  " [encap-vrf-id <nn>] [decap-next [l2|ip4|ip6]] [del]",
   .function = vxlan_add_del_tunnel_command_fn,
 };
+/* *INDENT-ON* */
 
 static clib_error_t *
 show_vxlan_tunnel_command_fn (vlib_main_t * vm,
@@ -557,19 +619,27 @@ show_vxlan_tunnel_command_fn (vlib_main_t * vm,
   return 0;
 }
 
+/*?
+ * Display all the VXLAN Tunnel entries.
+ *
+ * @cliexpar
+ * Example of how to display the VXLAN Tunnel entries:
+ * @cliexstart{show vxlan tunnel}
+ * [0] 10.0.3.1 (src) 10.0.3.3 (dst) vni 13 encap_fib_index 1 decap_next l2
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
 VLIB_CLI_COMMAND (show_vxlan_tunnel_command, static) = {
     .path = "show vxlan tunnel",
+    .short_help = "show vxlan tunnel",
     .function = show_vxlan_tunnel_command_fn,
 };
+/* *INDENT-ON* */
 
 
 clib_error_t *vxlan_init (vlib_main_t *vm)
 {
   vxlan_main_t * vxm = &vxlan_main;
-  ip4_vxlan_header_t * hdr4;
-  ip4_header_t * ip4;
-  ip6_vxlan_header_t * hdr6;
-  ip6_header_t * ip6;
   
   vxm->vnet_main = vnet_get_main();
   vxm->vlib_main = vm;
@@ -579,21 +649,6 @@ clib_error_t *vxlan_init (vlib_main_t *vm)
         sizeof(vxlan6_tunnel_key_t),
         sizeof(uword));
 
-  /* init dummy rewrite string for deleted vxlan tunnels */
-  _vec_len(vxlan4_dummy_rewrite) = sizeof(ip4_vxlan_header_t);
-  hdr4 = (ip4_vxlan_header_t *) vxlan4_dummy_rewrite;
-  ip4 = &hdr4->ip4;
-  /* minimal rewrite setup, see vxlan_rewite() above as reference */
-  ip4->ip_version_and_header_length = 0x45;
-  ip4->checksum = ip4_header_checksum (ip4);
-
-  /* Same again for IPv6 */
-  _vec_len(vxlan6_dummy_rewrite) = sizeof(ip6_vxlan_header_t);
-  hdr6 = (ip6_vxlan_header_t *) vxlan6_dummy_rewrite;
-  ip6 = &hdr6->ip6;
-  /* minimal rewrite setup, see vxlan_rewite() above as reference */
-  ip6->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32(6 << 28);
   udp_register_dst_port (vm, UDP_DST_PORT_vxlan, 
                          vxlan4_input_node.index, /* is_ip4 */ 1);
   udp_register_dst_port (vm, UDP_DST_PORT_vxlan6,