FIB: store the node type not the function pointer.
[vpp.git] / src / vnet / gre / interface.c
index d4476ac..ae5da93 100644 (file)
@@ -20,6 +20,7 @@
 #include <vnet/gre/gre.h>
 #include <vnet/ip/format.h>
 #include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/ip6_fib.h>
 #include <vnet/adj/adj_midchain.h>
 #include <vnet/adj/adj_nbr.h>
 #include <vnet/mpls/mpls.h>
@@ -27,7 +28,7 @@
 static const char *gre_tunnel_type_names[] = GRE_TUNNEL_TYPE_NAMES;
 
 static inline u64
-gre_mk_key (const ip4_address_t *src,
+gre4_mk_key (const ip4_address_t *src,
             const ip4_address_t *dst,
             u32 out_fib_index)
 {
@@ -48,30 +49,51 @@ format_gre_tunnel (u8 * s, va_list * args)
 {
   gre_tunnel_t * t = va_arg (*args, gre_tunnel_t *);
   gre_main_t * gm = &gre_main;
-
-  s = format (s,
-              "[%d] %U (src) %U (dst) payload %U outer_fib_index %d",
-              t - gm->tunnels,
-              format_ip4_address, &t->tunnel_src,
-              format_ip4_address, &t->tunnel_dst,
-              format_gre_tunnel_type, t->type,
-              t->outer_fib_index);
+  u8 is_ipv6 = t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6 ? 1 : 0;
+
+  if (!is_ipv6)
+      s = format (s,
+                  "[%d] %U (src) %U (dst) payload %U outer_fib_index %d",
+                  t - gm->tunnels,
+                  format_ip4_address, &t->tunnel_src.ip4,
+                  format_ip4_address, &t->tunnel_dst.fp_addr.ip4,
+                  format_gre_tunnel_type, t->type,
+                  t->outer_fib_index);
+  else
+      s = format (s,
+                  "[%d] %U (src) %U (dst) payload %U outer_fib_index %d",
+                  t - gm->tunnels,
+                  format_ip6_address, &t->tunnel_src.ip6,
+                  format_ip6_address, &t->tunnel_dst.fp_addr.ip6,
+                  format_gre_tunnel_type, t->type,
+                  t->outer_fib_index);
 
   return s;
 }
 
 static gre_tunnel_t *
-gre_tunnel_db_find (const ip4_address_t *src,
-                    const ip4_address_t *dst,
-                    u32 out_fib_index)
+gre_tunnel_db_find (const ip46_address_t *src,
+                    const ip46_address_t *dst,
+                    u32 out_fib_index,
+                    u8 is_ipv6)
 {
   gre_main_t * gm = &gre_main;
   uword * p;
-  u64 key;
-
-  key = gre_mk_key(src, dst, out_fib_index);
+  u64 key4, key6[4];
 
-  p = hash_get (gm->tunnel_by_key, key);
+  if (!is_ipv6)
+    {
+      key4 = gre4_mk_key(&src->ip4, &dst->ip4, out_fib_index);
+      p = hash_get (gm->tunnel_by_key4, key4);
+    }
+  else
+    {
+      key6[0] = src->ip6.as_u64[0];
+      key6[1] = src->ip6.as_u64[1];
+      key6[2] = dst->ip6.as_u64[0];
+      key6[3] = dst->ip6.as_u64[1];
+      p = hash_get_mem (gm->tunnel_by_key6, key6);
+    }
 
   if (NULL == p)
     return (NULL);
@@ -83,28 +105,55 @@ static void
 gre_tunnel_db_add (const gre_tunnel_t *t)
 {
   gre_main_t * gm = &gre_main;
-  u64 key;
+  u64 key4, key6[4], *key6_copy;
+  u8 is_ipv6 = t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6 ? 1 : 0;
 
-  key = gre_mk_key(&t->tunnel_src, &t->tunnel_dst, t->outer_fib_index);
-  hash_set (gm->tunnel_by_key, key, t - gm->tunnels);
+  if (!is_ipv6)
+    {
+      key4 = gre4_mk_key(&t->tunnel_src.ip4, &t->tunnel_dst.fp_addr.ip4,
+                       t->outer_fib_index);
+      hash_set (gm->tunnel_by_key4, key4, t - gm->tunnels);
+    }
+  else
+    {
+      key6[0] = t->tunnel_src.ip6.as_u64[0];
+      key6[1] = t->tunnel_src.ip6.as_u64[1];
+      key6[2] = t->tunnel_dst.fp_addr.ip6.as_u64[0];
+      key6[3] = t->tunnel_dst.fp_addr.ip6.as_u64[1];
+      key6_copy = clib_mem_alloc (sizeof (key6));
+      clib_memcpy (key6_copy, key6, sizeof (key6));
+      hash_set_mem (gm->tunnel_by_key6, key6_copy, t - gm->tunnels);
+    }
 }
 
 static void
 gre_tunnel_db_remove (const gre_tunnel_t *t)
 {
   gre_main_t * gm = &gre_main;
-  u64 key;
+  u64 key4, key6[4];
+  u8 is_ipv6 = t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6 ? 1 : 0;
+
+  if (!is_ipv6)
+    {
+      key4 = gre4_mk_key(&t->tunnel_src.ip4, &t->tunnel_dst.fp_addr.ip4,
+                         t->outer_fib_index);
+      hash_unset (gm->tunnel_by_key4, key4);
+    }
+  else
+    {
+      key6[0] = t->tunnel_src.ip6.as_u64[0];
+      key6[1] = t->tunnel_src.ip6.as_u64[1];
+      key6[2] = t->tunnel_dst.fp_addr.ip6.as_u64[0];
+      key6[3] = t->tunnel_dst.fp_addr.ip6.as_u64[1];
+      hash_unset_mem (gm->tunnel_by_key6, key6);
+    }
 
-  key = gre_mk_key(&t->tunnel_src, &t->tunnel_dst, t->outer_fib_index);
-  hash_unset (gm->tunnel_by_key, key);
 }
 
 static gre_tunnel_t *
 gre_tunnel_from_fib_node (fib_node_t *node)
 {
-#if (CLIB_DEBUG > 0)
     ASSERT(FIB_NODE_TYPE_GRE_TUNNEL == node->fn_type);
-#endif
     return ((gre_tunnel_t*) (((char*)node) -
                              STRUCT_OFFSET_OF(gre_tunnel_t, node)));
 }
@@ -230,26 +279,31 @@ const static fib_node_vft_t gre_vft = {
     .fnv_back_walk = gre_tunnel_back_walk,
 };
 
-static int 
+static int
 vnet_gre_tunnel_add (vnet_gre_add_del_tunnel_args_t *a,
                      u32 * sw_if_indexp)
 {
   gre_main_t * gm = &gre_main;
   vnet_main_t * vnm = gm->vnet_main;
-  ip4_main_t * im = &ip4_main;
+  ip4_main_t * im4 = &ip4_main;
+  ip6_main_t * im6 = &ip6_main;
   gre_tunnel_t * t;
   vnet_hw_interface_t * hi;
   u32 hw_if_index, sw_if_index;
   u32 outer_fib_index;
   u8 address[6];
   clib_error_t *error;
+  u8 is_ipv6 = a->is_ipv6;
 
-  outer_fib_index = ip4_fib_index_from_table_id(a->outer_fib_id);
+  if (!is_ipv6)
+    outer_fib_index = ip4_fib_index_from_table_id(a->outer_fib_id);
+  else
+    outer_fib_index = ip6_fib_index_from_table_id(a->outer_fib_id);
 
   if (~0 == outer_fib_index)
     return VNET_API_ERROR_NO_SUCH_FIB;
 
-  t = gre_tunnel_db_find(&a->src, &a->dst, a->outer_fib_id);
+  t = gre_tunnel_db_find(&a->src, &a->dst, a->outer_fib_id, a->is_ipv6);
 
   if (NULL != t)
     return VNET_API_ERROR_INVALID_VALUE;
@@ -336,51 +390,50 @@ vnet_gre_tunnel_add (vnet_gre_add_del_tunnel_args_t *a,
   vec_validate_init_empty (gm->tunnel_index_by_sw_if_index, sw_if_index, ~0);
   gm->tunnel_index_by_sw_if_index[sw_if_index] = t - gm->tunnels;
 
-  vec_validate (im->fib_index_by_sw_if_index, sw_if_index);
+  if (!is_ipv6)
+    {
+      vec_validate (im4->fib_index_by_sw_if_index, sw_if_index);
+      hi->min_packet_bytes = 64 + sizeof (gre_header_t) + sizeof (ip4_header_t);
+    }
+  else
+    {
+      vec_validate (im6->fib_index_by_sw_if_index, sw_if_index);
+      hi->min_packet_bytes = 64 + sizeof (gre_header_t) + sizeof (ip6_header_t);
+    }
 
-  hi->min_packet_bytes = 64 + sizeof (gre_header_t) + sizeof (ip4_header_t);
   hi->per_packet_overhead_bytes =
       /* preamble */ 8 + /* inter frame gap */ 12;
 
   /* Standard default gre MTU. */
   hi->max_l3_packet_bytes[VLIB_RX] = hi->max_l3_packet_bytes[VLIB_TX] = 9000;
 
-  clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src));
-  clib_memcpy (&t->tunnel_dst, &a->dst, sizeof (t->tunnel_dst));
-
-  gre_tunnel_db_add(t);
-
   /*
    * source the FIB entry for the tunnel's destination
    * and become a child thereof. The tunnel will then get poked
    * when the forwarding for the entry updates, and the tunnel can
    * re-stack accordingly
    */
-  const fib_prefix_t tun_dst_pfx = {
-      .fp_len = 32,
-      .fp_proto = FIB_PROTOCOL_IP4,
-      .fp_addr = {
-          .ip4 = t->tunnel_dst,
-      }
-  };
+
+  clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src));
+  t->tunnel_dst.fp_len = !is_ipv6 ? 32 : 128;
+  t->tunnel_dst.fp_proto = !is_ipv6 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
+  t->tunnel_dst.fp_addr = a->dst;
+
+  gre_tunnel_db_add(t);
 
   t->fib_entry_index =
       fib_table_entry_special_add(outer_fib_index,
-                                  &tun_dst_pfx,
+                                  &t->tunnel_dst,
                                   FIB_SOURCE_RR,
-                                  FIB_ENTRY_FLAG_NONE,
-                                  ADJ_INDEX_INVALID);
+                                  FIB_ENTRY_FLAG_NONE);
   t->sibling_index =
       fib_entry_child_add(t->fib_entry_index,
                           FIB_NODE_TYPE_GRE_TUNNEL,
                           t - gm->tunnels);
 
-  clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src));
-  clib_memcpy (&t->tunnel_dst, &a->dst, sizeof (t->tunnel_dst));
-
   if (GRE_TUNNEL_TYPE_TEB == t->type)
   {
-      t->l2_adj_index = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+      t->l2_adj_index = adj_nbr_add_or_lock(t->tunnel_dst.fp_proto,
                                            VNET_LINK_ETHERNET,
                                            &zero_addr,
                                            sw_if_index);
@@ -393,7 +446,7 @@ vnet_gre_tunnel_add (vnet_gre_add_del_tunnel_args_t *a,
   return 0;
 }
 
-static int 
+static int
 vnet_gre_tunnel_delete (vnet_gre_add_del_tunnel_args_t *a,
                         u32 * sw_if_indexp)
 {
@@ -402,7 +455,7 @@ vnet_gre_tunnel_delete (vnet_gre_add_del_tunnel_args_t *a,
   gre_tunnel_t * t;
   u32 sw_if_index;
 
-  t = gre_tunnel_db_find(&a->src, &a->dst, a->outer_fib_id);
+  t = gre_tunnel_db_find(&a->src, &a->dst, a->outer_fib_id, a->is_ipv6);
 
   if (NULL == t)
     return VNET_API_ERROR_NO_SUCH_ENTRY;
@@ -484,7 +537,7 @@ create_gre_tunnel_command_fn (vlib_main_t * vm,
 {
   unformat_input_t _line_input, * line_input = &_line_input;
   vnet_gre_add_del_tunnel_args_t _a, * a = &_a;
-  ip4_address_t src, dst;
+  ip46_address_t src, dst;
   u32 outer_fib_id = 0;
   u8 teb = 0;
   int rv;
@@ -492,6 +545,8 @@ create_gre_tunnel_command_fn (vlib_main_t * vm,
   u8 is_add = 1;
   u32 sw_if_index;
   clib_error_t *error = NULL;
+  u8 ipv4_set = 0;
+  u8 ipv6_set = 0;
 
   /* Get a line of input. */
   if (! unformat_user (input, unformat_line_input, line_input))
@@ -500,11 +555,19 @@ create_gre_tunnel_command_fn (vlib_main_t * vm,
   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
     if (unformat (line_input, "del"))
       is_add = 0;
-    else if (unformat (line_input, "src %U", unformat_ip4_address, &src))
+    else if (unformat (line_input, "src %U", unformat_ip4_address, &src.ip4)) {
       num_m_args++;
-    else if (unformat (line_input, "dst %U", unformat_ip4_address, &dst))
+      ipv4_set = 1;
+    } else if (unformat (line_input, "dst %U", unformat_ip4_address, &dst.ip4)) {
       num_m_args++;
-    else if (unformat (line_input, "outer-fib-id %d", &outer_fib_id))
+      ipv4_set = 1;
+    } else if (unformat (line_input, "src %U", unformat_ip6_address, &src.ip6)) {
+      num_m_args++;
+      ipv6_set = 1;
+    } else if (unformat (line_input, "dst %U", unformat_ip6_address, &dst.ip6)) {
+      num_m_args++;
+      ipv6_set = 1;
+    } else if (unformat (line_input, "outer-fib-id %d", &outer_fib_id))
       ;
     else if (unformat (line_input, "teb"))
       teb = 1;
@@ -522,17 +585,37 @@ create_gre_tunnel_command_fn (vlib_main_t * vm,
       goto done;
     }
 
-  if (memcmp (&src, &dst, sizeof(src)) == 0)
+  if ((ipv4_set && memcmp (&src.ip4, &dst.ip4, sizeof(src.ip4)) == 0) ||
+      (ipv6_set && memcmp (&src.ip6, &dst.ip6, sizeof(src.ip6)) == 0))
     {
       error = clib_error_return (0, "src and dst are identical");
       goto done;
     }
 
+  if (ipv4_set && ipv6_set)
+      return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
+
+  if ((ipv4_set && memcmp (&dst.ip4, &zero_addr.ip4, sizeof(dst.ip4)) == 0) ||
+      (ipv6_set && memcmp (&dst.ip6, &zero_addr.ip6, sizeof(dst.ip6)) == 0))
+    {
+      error = clib_error_return (0, "dst address cannot be zero");
+      goto done;
+    }
+
   memset (a, 0, sizeof (*a));
   a->outer_fib_id = outer_fib_id;
   a->teb = teb;
-  clib_memcpy(&a->src, &src, sizeof(src));
-  clib_memcpy(&a->dst, &dst, sizeof(dst));
+  a->is_ipv6 = ipv6_set;
+  if (!ipv6_set)
+    {
+      clib_memcpy(&a->src.ip4, &src.ip4, sizeof(src.ip4));
+      clib_memcpy(&a->dst.ip4, &dst.ip4, sizeof(dst.ip4));
+    }
+  else
+    {
+      clib_memcpy(&a->src.ip6, &src.ip6, sizeof(src.ip6));
+      clib_memcpy(&a->dst.ip6, &dst.ip6, sizeof(dst.ip6));
+    }
 
   if (is_add)
     rv = vnet_gre_tunnel_add(a, &sw_if_index);