GRE over IPv6 24/3224/9
authorCiara Loftus <ciara.loftus@intel.com>
Fri, 30 Sep 2016 14:47:03 +0000 (15:47 +0100)
committerCiara Loftus <ciara.loftus@intel.com>
Wed, 5 Apr 2017 08:06:23 +0000 (09:06 +0100)
Refactors the GRE node to work with both IPv4 and IPv6 transports.

Note that this changes the binary configuration API to support both
address families; each address uses the same memory for either
address type and a flag to indicate which is in use.

The CLI and VAT syntax remains unchanged; the code detects whether
an IPv4 or an IPv6 address was given.

Configuration examples:

IPv4 CLI: create gre tunnel src 192.168.1.1 dst 192.168.1.2
IPv6 CLI: create gre tunnel src 2620:124:9000::1 dst 2620:124:9000::2

IPv4 VAT: gre_add_del_tunnel src 192.168.1.1 dst 192.168.1.2
IPv6 VAT: gre_add_del_tunnel src 2620:124:9000::1 dst 2620:124:9000::2

Change-Id: Ica8ee775dc101047fb8cd41617ddc8fafc2741b0
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
src/vat/api_format.c
src/vnet/gre/gre.c
src/vnet/gre/gre.h
src/vnet/gre/gre_api.c
src/vnet/gre/interface.c
src/vnet/gre/node.c
src/vnet/ipsec-gre/node.c
test/test_gre.py
test/vpp_gre_interface.py

index 06884eb..090d990 100644 (file)
@@ -11018,21 +11018,45 @@ api_gre_add_del_tunnel (vat_main_t * vam)
   unformat_input_t *line_input = vam->input;
   vl_api_gre_add_del_tunnel_t *mp;
   ip4_address_t src4, dst4;
+  ip6_address_t src6, dst6;
   u8 is_add = 1;
+  u8 ipv4_set = 0;
+  u8 ipv6_set = 0;
   u8 teb = 0;
   u8 src_set = 0;
   u8 dst_set = 0;
   u32 outer_fib_id = 0;
   int ret;
 
+  memset (&src4, 0, sizeof src4);
+  memset (&dst4, 0, sizeof dst4);
+  memset (&src6, 0, sizeof src6);
+  memset (&dst6, 0, sizeof dst6);
+
   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, &src4))
-       src_set = 1;
+       {
+         src_set = 1;
+         ipv4_set = 1;
+       }
       else if (unformat (line_input, "dst %U", unformat_ip4_address, &dst4))
-       dst_set = 1;
+       {
+         dst_set = 1;
+         ipv4_set = 1;
+       }
+      else if (unformat (line_input, "src %U", unformat_ip6_address, &src6))
+       {
+         src_set = 1;
+         ipv6_set = 1;
+       }
+      else if (unformat (line_input, "dst %U", unformat_ip6_address, &dst6))
+       {
+         dst_set = 1;
+         ipv6_set = 1;
+       }
       else if (unformat (line_input, "outer-fib-id %d", &outer_fib_id))
        ;
       else if (unformat (line_input, "teb"))
@@ -11054,15 +11078,29 @@ api_gre_add_del_tunnel (vat_main_t * vam)
       errmsg ("tunnel dst address not specified");
       return -99;
     }
+  if (ipv4_set && ipv6_set)
+    {
+      errmsg ("both IPv4 and IPv6 addresses specified");
+      return -99;
+    }
 
 
   M (GRE_ADD_DEL_TUNNEL, mp);
 
-  clib_memcpy (&mp->src_address, &src4, sizeof (src4));
-  clib_memcpy (&mp->dst_address, &dst4, sizeof (dst4));
+  if (ipv4_set)
+    {
+      clib_memcpy (&mp->src_address, &src4, 4);
+      clib_memcpy (&mp->dst_address, &dst4, 4);
+    }
+  else
+    {
+      clib_memcpy (&mp->src_address, &src6, 16);
+      clib_memcpy (&mp->dst_address, &dst6, 16);
+    }
   mp->outer_fib_id = ntohl (outer_fib_id);
   mp->is_add = is_add;
   mp->teb = teb;
+  mp->is_ipv6 = ipv6_set;
 
   S (mp);
   W (ret);
@@ -11073,11 +11111,13 @@ static void vl_api_gre_tunnel_details_t_handler
   (vl_api_gre_tunnel_details_t * mp)
 {
   vat_main_t *vam = &vat_main;
+  ip46_address_t src = to_ip46 (mp->is_ipv6, mp->src_address);
+  ip46_address_t dst = to_ip46 (mp->is_ipv6, mp->dst_address);
 
-  print (vam->ofp, "%11d%15U%15U%6d%14d",
+  print (vam->ofp, "%11d%24U%24U%6d%14d",
         ntohl (mp->sw_if_index),
-        format_ip4_address, &mp->src_address,
-        format_ip4_address, &mp->dst_address,
+        format_ip46_address, &src, IP46_TYPE_ANY,
+        format_ip46_address, &dst, IP46_TYPE_ANY,
         mp->teb, ntohl (mp->outer_fib_id));
 }
 
@@ -11087,6 +11127,7 @@ static void vl_api_gre_tunnel_details_t_handler_json
   vat_main_t *vam = &vat_main;
   vat_json_node_t *node = NULL;
   struct in_addr ip4;
+  struct in6_addr ip6;
 
   if (VAT_JSON_ARRAY != vam->json_tree.type)
     {
@@ -11097,12 +11138,23 @@ static void vl_api_gre_tunnel_details_t_handler_json
 
   vat_json_init_object (node);
   vat_json_object_add_uint (node, "sw_if_index", ntohl (mp->sw_if_index));
-  clib_memcpy (&ip4, &mp->src_address, sizeof (ip4));
-  vat_json_object_add_ip4 (node, "src_address", ip4);
-  clib_memcpy (&ip4, &mp->dst_address, sizeof (ip4));
-  vat_json_object_add_ip4 (node, "dst_address", ip4);
+  if (!mp->is_ipv6)
+    {
+      clib_memcpy (&ip4, &mp->src_address, sizeof (ip4));
+      vat_json_object_add_ip4 (node, "src_address", ip4);
+      clib_memcpy (&ip4, &mp->dst_address, sizeof (ip4));
+      vat_json_object_add_ip4 (node, "dst_address", ip4);
+    }
+  else
+    {
+      clib_memcpy (&ip6, &mp->src_address, sizeof (ip6));
+      vat_json_object_add_ip6 (node, "src_address", ip6);
+      clib_memcpy (&ip6, &mp->dst_address, sizeof (ip6));
+      vat_json_object_add_ip6 (node, "dst_address", ip6);
+    }
   vat_json_object_add_uint (node, "teb", mp->teb);
   vat_json_object_add_uint (node, "outer_fib_id", ntohl (mp->outer_fib_id));
+  vat_json_object_add_uint (node, "is_ipv6", mp->is_ipv6);
 }
 
 static int
@@ -11131,7 +11183,7 @@ api_gre_tunnel_dump (vat_main_t * vam)
 
   if (!vam->json_output)
     {
-      print (vam->ofp, "%11s%15s%15s%6s%14s",
+      print (vam->ofp, "%11s%24s%24s%6s%14s",
             "sw_if_index", "src_address", "dst_address", "teb",
             "outer_fib_id");
     }
@@ -18612,7 +18664,7 @@ _(vxlan_add_del_tunnel,                                                 \
   "vni <vni> [encap-vrf-id <nn>] [decap-next <l2|nn>] [del]")           \
 _(vxlan_tunnel_dump, "[<intfc> | sw_if_index <nn>]")                    \
 _(gre_add_del_tunnel,                                                   \
-  "src <ip4-addr> dst <ip4-addr> [outer-fib-id <nn>] [teb] [del]\n")    \
+  "src <ip-addr> dst <ip-addr> [outer-fib-id <nn>] [teb] [del]\n")    \
 _(gre_tunnel_dump, "[<intfc> | sw_if_index <nn>]")                      \
 _(l2_fib_clear_table, "")                                               \
 _(l2_interface_efp_filter, "sw_if_index <nn> enable | disable")         \
index 3d38138..a153c3c 100644 (file)
@@ -28,6 +28,13 @@ typedef struct {
   };
 } ip4_and_gre_union_t;
 
+typedef struct {
+  union {
+    ip6_and_gre_header_t ip6_and_gre;
+    u64 as_u64[3];
+  };
+} ip6_and_gre_union_t;
+
 
 /* Packet trace structure */
 typedef struct {
@@ -37,9 +44,9 @@ typedef struct {
   /* pkt length */
   u32 length;
 
-  /* tunnel ip4 addresses */
-  ip4_address_t src;
-  ip4_address_t dst;
+  /* tunnel ip addresses */
+  ip46_address_t src;
+  ip46_address_t dst;
 } gre_tx_trace_t;
 
 u8 * format_gre_tx_trace (u8 * s, va_list * args)
@@ -50,8 +57,8 @@ u8 * format_gre_tx_trace (u8 * s, va_list * args)
 
   s = format (s, "GRE: tunnel %d len %d src %U dst %U",
              t->tunnel_id, clib_net_to_host_u16 (t->length),
-             format_ip4_address, &t->src.as_u8,
-             format_ip4_address, &t->dst.as_u8);
+             format_ip46_address, &t->src, IP46_TYPE_ANY,
+             format_ip46_address, &t->dst, IP46_TYPE_ANY);
   return s;
 }
 
@@ -192,10 +199,12 @@ gre_build_rewrite (vnet_main_t * vnm,
                   const void *dst_address)
 {
   gre_main_t * gm = &gre_main;
-  ip4_and_gre_header_t * h;
+  ip4_and_gre_header_t * h4;
+  ip6_and_gre_header_t * h6;
   u8* rewrite = NULL;
   gre_tunnel_t *t;
   u32 ti;
+  u8 is_ipv6;
 
   ti = gm->tunnel_index_by_sw_if_index[sw_if_index];
 
@@ -205,23 +214,45 @@ gre_build_rewrite (vnet_main_t * vnm,
 
   t = pool_elt_at_index(gm->tunnels, ti);
 
-  vec_validate(rewrite, sizeof(*h)-1);
-  h = (ip4_and_gre_header_t*)rewrite;
-  h->gre.protocol = clib_host_to_net_u16(gre_proto_from_vnet_link(link_type));
+  is_ipv6 = t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6 ? 1 : 0;
 
-  h->ip4.ip_version_and_header_length = 0x45;
-  h->ip4.ttl = 254;
-  h->ip4.protocol = IP_PROTOCOL_GRE;
-  /* fixup ip4 header length and checksum after-the-fact */
-  h->ip4.src_address.as_u32 = t->tunnel_src.as_u32;
-  h->ip4.dst_address.as_u32 = t->tunnel_dst.as_u32;
-  h->ip4.checksum = ip4_header_checksum (&h->ip4);
+  if (!is_ipv6)
+    {
+      vec_validate(rewrite, sizeof(*h4)-1);
+      h4 = (ip4_and_gre_header_t*)rewrite;
+      h4->gre.protocol = clib_host_to_net_u16(gre_proto_from_vnet_link(link_type));
+
+      h4->ip4.ip_version_and_header_length = 0x45;
+      h4->ip4.ttl = 254;
+      h4->ip4.protocol = IP_PROTOCOL_GRE;
+      /* fixup ip4 header length and checksum after-the-fact */
+      h4->ip4.src_address.as_u32 = t->tunnel_src.ip4.as_u32;
+      h4->ip4.dst_address.as_u32 = t->tunnel_dst.fp_addr.ip4.as_u32;
+      h4->ip4.checksum = ip4_header_checksum (&h4->ip4);
+    }
+  else
+    {
+      vec_validate(rewrite, sizeof(*h6)-1);
+      h6 = (ip6_and_gre_header_t*)rewrite;
+      h6->gre.protocol = clib_host_to_net_u16(gre_proto_from_vnet_link(link_type));
+
+      h6->ip6.ip_version_traffic_class_and_flow_label = clib_host_to_net_u32(6 << 28);
+      h6->ip6.hop_limit = 255;
+      h6->ip6.protocol = IP_PROTOCOL_GRE;
+      /* fixup ip6 header length and checksum after-the-fact */
+      h6->ip6.src_address.as_u64[0] = t->tunnel_src.ip6.as_u64[0];
+      h6->ip6.src_address.as_u64[1] = t->tunnel_src.ip6.as_u64[1];
+      h6->ip6.dst_address.as_u64[0] = t->tunnel_dst.fp_addr.ip6.as_u64[0];
+      h6->ip6.dst_address.as_u64[1] = t->tunnel_dst.fp_addr.ip6.as_u64[1];
+    }
 
   return (rewrite);
 }
 
+#define is_v4_packet(_h) ((*(u8*) _h) & 0xF0) == 0x40
+
 void
-gre_fixup (vlib_main_t *vm,
+gre4_fixup (vlib_main_t *vm,
           ip_adjacency_t *adj,
           vlib_buffer_t *b0)
 {
@@ -235,12 +266,37 @@ gre_fixup (vlib_main_t *vm,
     ip0->checksum = ip4_header_checksum (ip0);
 }
 
+void
+gre6_fixup (vlib_main_t *vm,
+          ip_adjacency_t *adj,
+          vlib_buffer_t *b0)
+{
+    ip6_header_t * ip0;
+
+    ip0 = vlib_buffer_get_current (b0);
+
+    /* Fixup the payload length field in the GRE tunnel encap that was applied
+     * at the midchain node */
+    ip0->payload_length =
+        clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0))
+        - sizeof(*ip0);
+}
+
 void
 gre_update_adj (vnet_main_t * vnm,
                u32 sw_if_index,
                adj_index_t ai)
 {
-    adj_nbr_midchain_update_rewrite (ai, gre_fixup, 
+    gre_main_t * gm = &gre_main;
+    gre_tunnel_t *t;
+    u32 ti;
+    u8 is_ipv6;
+
+    ti = gm->tunnel_index_by_sw_if_index[sw_if_index];
+    t = pool_elt_at_index(gm->tunnels, ti);
+    is_ipv6 = t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6 ? 1 : 0;
+
+    adj_nbr_midchain_update_rewrite (ai, !is_ipv6 ? gre4_fixup : gre6_fixup,
                                      (VNET_LINK_ETHERNET == adj_get_link_type (ai) ?
                                       ADJ_FLAG_MIDCHAIN_NO_COUNT :
                                       ADJ_FLAG_NONE),
@@ -264,6 +320,7 @@ gre_interface_tx_inline (vlib_main_t * vm,
   u32 * from, * to_next, n_left_from, n_left_to_next;
   vnet_interface_output_runtime_t * rd = (void *) node->runtime_data;
   const gre_tunnel_t *gt = pool_elt_at_index (gm->tunnels, rd->dev_instance);
+  u8 is_ipv6 = gt->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6 ? 1 : 0;
 
   /* Vector of buffer / pkt indices we're supposed to process */
   from = vlib_frame_vector_args (frame);
@@ -303,10 +360,10 @@ gre_interface_tx_inline (vlib_main_t * vm,
            {
              gre_tx_trace_t *tr = vlib_add_trace (vm, node,
                                                   b0, sizeof (*tr));
-             tr->tunnel_id = gt - gm->tunnels;
-             tr->length = vlib_buffer_length_in_chain (vm, b0);
-             tr->src.as_u32 = gt->tunnel_src.as_u32;
-             tr->dst.as_u32 = gt->tunnel_src.as_u32;
+          tr->tunnel_id = gt - gm->tunnels;
+          tr->src = gt->tunnel_src;
+          tr->dst = gt->tunnel_src;
+          tr->length = vlib_buffer_length_in_chain (vm, b0);
            }
 
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
@@ -317,7 +374,8 @@ gre_interface_tx_inline (vlib_main_t * vm,
       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     }
 
-  vlib_node_increment_counter (vm, gre_input_node.index,
+  vlib_node_increment_counter (vm, !is_ipv6 ? gre4_input_node.index :
+                   gre6_input_node.index,
                               GRE_ERROR_PKTS_ENCAP, frame->n_vectors);
 
   return frame->n_vectors;
@@ -434,6 +492,9 @@ static clib_error_t * gre_init (vlib_main_t * vm)
   if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
     return error;
 
+  if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
+    return error;
+
   /* Set up the ip packet generator */
   pi = ip_get_protocol_info (im, IP_PROTOCOL_GRE);
   pi->format_header = format_gre_header;
@@ -441,7 +502,8 @@ static clib_error_t * gre_init (vlib_main_t * vm)
 
   gm->protocol_info_by_name = hash_create_string (0, sizeof (uword));
   gm->protocol_info_by_protocol = hash_create (0, sizeof (uword));
-  gm->tunnel_by_key = hash_create (0, sizeof (uword));
+  gm->tunnel_by_key4 = hash_create (0, sizeof (uword));
+  gm->tunnel_by_key6 = hash_create_mem (0, sizeof(u64[4]), sizeof (uword));
 
 #define _(n,s) add_protocol (gm, GRE_PROTOCOL_##s, #s);
   foreach_gre_protocol
index 788cba2..ad3e025 100644 (file)
@@ -21,8 +21,6 @@
 #include <vnet/vnet.h>
 #include <vnet/gre/packet.h>
 #include <vnet/ip/ip.h>
-#include <vnet/ip/ip4.h>
-#include <vnet/ip/ip4_packet.h>
 #include <vnet/pg/pg.h>
 #include <vnet/ip/format.h>
 #include <vnet/adj/adj_types.h>
@@ -87,11 +85,11 @@ typedef struct {
   /**
    * The tunnel's source/local address
    */
-  ip4_address_t tunnel_src;
+  ip46_address_t tunnel_src;
   /**
    * The tunnel's destination/remote address
    */
-  ip4_address_t tunnel_dst;
+  fib_prefix_t tunnel_dst;
   /**
    * The FIB in which the src.dst address are present
    */
@@ -142,10 +140,16 @@ typedef struct {
    *  Hash tables mapping name/protocol to protocol info index.
    */
   uword * protocol_info_by_name, * protocol_info_by_protocol;
+
   /**
-   * Hash mapping src/dst addr pair to tunnel
+   * Hash mapping ipv4 src/dst addr pair to tunnel
    */
-  uword * tunnel_by_key;
+  uword * tunnel_by_key4;
+
+  /**
+     * Hash mapping ipv6 src/dst addr pair to tunnel
+     */
+    uword * tunnel_by_key6;
 
   /**
    * Free vlib hw_if_indices.
@@ -176,6 +180,14 @@ typedef CLIB_PACKED (struct {
   gre_header_t gre;
 }) ip4_and_gre_header_t;
 
+/**
+ * @brief IPv6 and GRE header.
+ */
+typedef CLIB_PACKED (struct {
+  ip6_header_t ip6;
+  gre_header_t gre;
+}) ip6_and_gre_header_t;
+
 always_inline gre_protocol_info_t *
 gre_get_protocol_info (gre_main_t * em, gre_protocol_t protocol)
 {
@@ -204,7 +216,8 @@ format_function_t format_gre_protocol;
 format_function_t format_gre_header;
 format_function_t format_gre_header_with_length;
 
-extern vlib_node_registration_t gre_input_node;
+extern vlib_node_registration_t gre4_input_node;
+extern vlib_node_registration_t gre6_input_node;
 extern vnet_device_class_t gre_device_class;
 extern vnet_device_class_t gre_device_teb_class;
 
@@ -228,7 +241,8 @@ gre_register_input_protocol (vlib_main_t * vm,
 typedef struct {
   u8 is_add;
 
-  ip4_address_t src, dst;
+  ip46_address_t src, dst;
+  u8 is_ipv6;
   u32 outer_fib_id;
   u8 teb;
 } vnet_gre_add_del_tunnel_args_t;
index 333838c..ceeb1d4 100644 (file)
@@ -55,17 +55,17 @@ static void vl_api_gre_add_del_tunnel_t_handler
   int rv = 0;
   vnet_gre_add_del_tunnel_args_t _a, *a = &_a;
   u32 outer_fib_id;
-  uword *p;
-  ip4_main_t *im = &ip4_main;
+  u32 p;
   u32 sw_if_index = ~0;
 
-  p = hash_get (im->fib_index_by_table_id, ntohl (mp->outer_fib_id));
-  if (!p)
+  p = fib_table_find (!mp->is_ipv6 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6,
+                     ntohl (mp->outer_fib_id));
+  if (p == ~0)
     {
       rv = VNET_API_ERROR_NO_SUCH_FIB;
       goto out;
     }
-  outer_fib_id = p[0];
+  outer_fib_id = p;
 
   /* Check src & dst are different */
   if ((mp->is_ipv6 && memcmp (mp->src_address, mp->dst_address, 16) == 0) ||
@@ -78,10 +78,19 @@ static void vl_api_gre_add_del_tunnel_t_handler
 
   a->is_add = mp->is_add;
   a->teb = mp->teb;
+  a->is_ipv6 = mp->is_ipv6;
 
   /* ip addresses sent in network byte order */
-  clib_memcpy (&(a->src), mp->src_address, 4);
-  clib_memcpy (&(a->dst), mp->dst_address, 4);
+  if (!mp->is_ipv6)
+    {
+      clib_memcpy (&(a->src.ip4), mp->src_address, 4);
+      clib_memcpy (&(a->dst.ip4), mp->dst_address, 4);
+    }
+  else
+    {
+      clib_memcpy (&(a->src.ip6), mp->src_address, 16);
+      clib_memcpy (&(a->dst.ip6), mp->dst_address, 16);
+    }
 
   a->outer_fib_id = outer_fib_id;
   rv = vnet_gre_add_del_tunnel (a, &sw_if_index);
@@ -99,17 +108,30 @@ static void send_gre_tunnel_details
   (gre_tunnel_t * t, unix_shared_memory_queue_t * q, u32 context)
 {
   vl_api_gre_tunnel_details_t *rmp;
-  ip4_main_t *im = &ip4_main;
+  u8 is_ipv6 = t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6 ? 1 : 0;
+  fib_table_t *ft;
 
   rmp = vl_msg_api_alloc (sizeof (*rmp));
   memset (rmp, 0, sizeof (*rmp));
   rmp->_vl_msg_id = ntohs (VL_API_GRE_TUNNEL_DETAILS);
-  clib_memcpy (rmp->src_address, &(t->tunnel_src), 4);
-  clib_memcpy (rmp->dst_address, &(t->tunnel_dst), 4);
-  rmp->outer_fib_id = htonl (im->fibs[t->outer_fib_index].ft_table_id);
+  if (!is_ipv6)
+    {
+      clib_memcpy (rmp->src_address, &(t->tunnel_src.ip4.as_u8), 4);
+      clib_memcpy (rmp->dst_address, &(t->tunnel_dst.fp_addr.ip4.as_u8), 4);
+      ft = fib_table_get (t->outer_fib_index, FIB_PROTOCOL_IP4);
+      rmp->outer_fib_id = ft->ft_table_id;
+    }
+  else
+    {
+      clib_memcpy (rmp->src_address, &(t->tunnel_src.ip6.as_u8), 16);
+      clib_memcpy (rmp->dst_address, &(t->tunnel_dst.fp_addr.ip6.as_u8), 16);
+      ft = fib_table_get (t->outer_fib_index, FIB_PROTOCOL_IP6);
+      rmp->outer_fib_id = ft->ft_table_id;
+    }
   rmp->teb = (GRE_TUNNEL_TYPE_TEB == t->type);
   rmp->sw_if_index = htonl (t->sw_if_index);
   rmp->context = context;
+  rmp->is_ipv6 = is_ipv6;
 
   vl_msg_api_send_shmem (q, (u8 *) & rmp);
 }
index d4476ac..91a3899 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,20 +105,49 @@ 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 *
@@ -230,26 +281,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,37 +392,40 @@ 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);
@@ -375,12 +434,9 @@ vnet_gre_tunnel_add (vnet_gre_add_del_tunnel_args_t *a,
                           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 +449,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 +458,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 +540,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 +548,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 +558,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 +588,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);
index 5926e83..2683586 100644 (file)
@@ -39,8 +39,9 @@ typedef enum {
 typedef struct {
   u32 tunnel_id;
   u32 length;
-  ip4_address_t src;
-  ip4_address_t dst;
+  ip46_address_t src;
+  ip46_address_t dst;
+  u8 is_ipv6;
 } gre_rx_trace_t;
 
 u8 * format_gre_rx_trace (u8 * s, va_list * args)
@@ -48,28 +49,41 @@ u8 * format_gre_rx_trace (u8 * s, va_list * args)
   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   gre_rx_trace_t * t = va_arg (*args, gre_rx_trace_t *);
-    
+
   s = format (s, "GRE: tunnel %d len %d src %U dst %U",
               t->tunnel_id, clib_net_to_host_u16(t->length),
-              format_ip4_address, &t->src.as_u8,
-              format_ip4_address, &t->dst.as_u8);
+              format_ip46_address, &t->src, IP46_TYPE_ANY,
+              format_ip46_address, &t->dst, IP46_TYPE_ANY);
   return s;
 }
 
-static uword
+typedef struct {
+  /* Sparse vector mapping gre protocol in network byte order
+     to next index. */
+  u16 * next_by_protocol;
+} gre_input_runtime_t;
+
+always_inline uword
 gre_input (vlib_main_t * vm,
           vlib_node_runtime_t * node,
-          vlib_frame_t * from_frame)
+          vlib_frame_t * from_frame,
+          u8 is_ipv6)
 {
   gre_main_t * gm = &gre_main;
   __attribute__((unused)) u32 n_left_from, next_index, * from, * to_next;
-  u64 cached_tunnel_key = (u64) ~0;
+  u64 cached_tunnel_key4;
+  u64 cached_tunnel_key6[4];
   u32 cached_tunnel_sw_if_index = 0, tunnel_sw_if_index = 0;
 
   u32 cpu_index = os_get_cpu_number();
   u32 len;
   vnet_interface_main_t *im = &gm->vnet_main->interface_main;
 
+  if (!is_ipv6)
+      memset (&cached_tunnel_key4, 0xff, sizeof(cached_tunnel_key4));
+  else
+      memset (&cached_tunnel_key6, 0xff, sizeof(cached_tunnel_key6));
+
   from = vlib_frame_vector_args (from_frame);
   n_left_from = from_frame->n_vectors;
 
@@ -90,7 +104,12 @@ gre_input (vlib_main_t * vm,
           u16 version0, version1;
           int verr0, verr1;
          u32 i0, i1, next0, next1, protocol0, protocol1;
-          ip4_header_t *ip0, *ip1;
+          ip4_header_t *ip4_0, *ip4_1;
+          ip6_header_t *ip6_0, *ip6_1;
+          u32 ip4_tun_src0, ip4_tun_dst0;
+          u32 ip4_tun_src1, ip4_tun_dst1;
+          u64 ip6_tun_src0[2], ip6_tun_dst0[2];
+          u64 ip6_tun_src1[2], ip6_tun_dst1[2];
 
          /* Prefetch next iteration. */
          {
@@ -118,18 +137,38 @@ gre_input (vlib_main_t * vm,
          b0 = vlib_get_buffer (vm, bi0);
          b1 = vlib_get_buffer (vm, bi1);
 
-          /* ip4_local hands us the ip header, not the gre header */
-          ip0 = vlib_buffer_get_current (b0);
-          ip1 = vlib_buffer_get_current (b1);
-
-          /* Save src + dst ip4 address, e.g. for mpls-o-gre */
-          vnet_buffer(b0)->gre.src = ip0->src_address.as_u32;
-          vnet_buffer(b0)->gre.dst = ip0->dst_address.as_u32;
-          vnet_buffer(b1)->gre.src = ip1->src_address.as_u32;
-          vnet_buffer(b1)->gre.dst = ip1->dst_address.as_u32;
-
-          vlib_buffer_advance (b0, sizeof (*ip0));
-          vlib_buffer_advance (b1, sizeof (*ip1));
+          if (!is_ipv6)
+            {
+              /* ip4_local hands us the ip header, not the gre header */
+              ip4_0 = vlib_buffer_get_current (b0);
+              ip4_1 = vlib_buffer_get_current (b1);
+              /* Save src + dst ip4 address, e.g. for mpls-o-gre */
+              ip4_tun_src0 = ip4_0->src_address.as_u32;
+              ip4_tun_dst0 = ip4_0->dst_address.as_u32;
+              ip4_tun_src1 = ip4_1->src_address.as_u32;
+              ip4_tun_dst1 = ip4_1->dst_address.as_u32;
+
+              vlib_buffer_advance (b0, sizeof (*ip4_0));
+              vlib_buffer_advance (b1, sizeof (*ip4_1));
+            }
+          else
+            {
+              /* ip6_local hands us the ip header, not the gre header */
+              ip6_0 = vlib_buffer_get_current (b0);
+              ip6_1 = vlib_buffer_get_current (b1);
+              /* Save src + dst ip6 address, e.g. for mpls-o-gre */
+              ip6_tun_src0[0] = ip6_0->src_address.as_u64[0];
+              ip6_tun_src0[1] = ip6_0->src_address.as_u64[1];
+              ip6_tun_dst0[0] = ip6_0->dst_address.as_u64[0];
+              ip6_tun_dst0[1] = ip6_0->dst_address.as_u64[1];
+              ip6_tun_src1[0] = ip6_1->src_address.as_u64[0];
+              ip6_tun_src1[1] = ip6_1->src_address.as_u64[1];
+              ip6_tun_dst1[0] = ip6_1->dst_address.as_u64[0];
+              ip6_tun_dst1[1] = ip6_1->dst_address.as_u64[1];
+
+              vlib_buffer_advance (b0, sizeof (*ip6_0));
+              vlib_buffer_advance (b1, sizeof (*ip6_1));
+            }
 
          h0 = vlib_buffer_get_current (b0);
          h1 = vlib_buffer_get_current (b1);
@@ -164,16 +203,34 @@ gre_input (vlib_main_t * vm,
                           || next0 == GRE_INPUT_NEXT_ETHERNET_INPUT
                           || next0 == GRE_INPUT_NEXT_MPLS_INPUT))
             {
-              u64 key = ((u64)(vnet_buffer(b0)->gre.dst) << 32) |
-                         (u64)(vnet_buffer(b0)->gre.src);
 
-              if (cached_tunnel_key != key)
+              u64 key4, key6[4];
+              if (!is_ipv6)
+               {
+                  key4 = ((u64)(ip4_tun_dst0) << 32) | (u64)(ip4_tun_src0);
+               }
+              else
+               {
+                  key6[0] = ip6_tun_dst0[0];
+                  key6[1] = ip6_tun_dst0[1];
+                  key6[2] = ip6_tun_src0[0];
+                  key6[3] = ip6_tun_src0[1];
+               }
+
+              if ((!is_ipv6 && cached_tunnel_key4 != key4) ||
+                 (is_ipv6 && cached_tunnel_key6[0] != key6[0] &&
+                     cached_tunnel_key6[1] != key6[1] &&
+                     cached_tunnel_key6[2] != key6[2] &&
+                     cached_tunnel_key6[3] != key6[3]))
                 {
                   vnet_hw_interface_t * hi;
                   gre_tunnel_t * t;
                   uword * p;
 
-                  p = hash_get (gm->tunnel_by_key, key);
+                  if (!is_ipv6)
+                    p = hash_get (gm->tunnel_by_key4, key4);
+                  else
+                    p = hash_get_mem (gm->tunnel_by_key6, key6);
                   if (!p)
                     {
                       next0 = GRE_INPUT_NEXT_DROP;
@@ -213,16 +270,34 @@ drop0:
                           || next1 == GRE_INPUT_NEXT_ETHERNET_INPUT
                           || next1 == GRE_INPUT_NEXT_MPLS_INPUT))
             {
-              u64 key = ((u64)(vnet_buffer(b1)->gre.dst) << 32) |
-                         (u64)(vnet_buffer(b1)->gre.src);
-
-              if (cached_tunnel_key != key)
+              u64 key4, key6[4];
+              if (!is_ipv6)
+               {
+                  key4 = ((u64)(ip4_tun_dst1) << 32) | (u64)(ip4_tun_src1);
+               }
+              else
+               {
+                  key6[0] = ip6_tun_dst1[0];
+                  key6[1] = ip6_tun_dst1[1];
+                  key6[2] = ip6_tun_src1[0];
+                  key6[3] = ip6_tun_src1[1];
+               }
+
+              if ((!is_ipv6 && cached_tunnel_key4 != key4) ||
+                 (is_ipv6 && cached_tunnel_key6[0] != key6[0] &&
+                     cached_tunnel_key6[1] != key6[1] &&
+                     cached_tunnel_key6[2] != key6[2] &&
+                     cached_tunnel_key6[3] != key6[3]))
                 {
                   vnet_hw_interface_t * hi;
                   gre_tunnel_t * t;
                   uword * p;
 
-                  p = hash_get (gm->tunnel_by_key, key);
+                  if (!is_ipv6)
+                    p = hash_get (gm->tunnel_by_key4, key4);
+                  else
+                    p = hash_get_mem (gm->tunnel_by_key6, key6);
+
                   if (!p)
                     {
                       next1 = GRE_INPUT_NEXT_DROP;
@@ -262,9 +337,20 @@ drop1:
               gre_rx_trace_t *tr = vlib_add_trace (vm, node,
                                                    b0, sizeof (*tr));
               tr->tunnel_id = tunnel_sw_if_index;
-              tr->length = ip0->length;
-              tr->src.as_u32 = ip0->src_address.as_u32;
-              tr->dst.as_u32 = ip0->dst_address.as_u32;
+              if (!is_ipv6)
+                {
+                  tr->length = ip4_0->length;
+                  tr->src.ip4.as_u32 = ip4_0->src_address.as_u32;
+                  tr->dst.ip4.as_u32 = ip4_0->dst_address.as_u32;
+                }
+              else
+                {
+                  tr->length = ip6_0->payload_length;
+                  tr->src.ip6.as_u64[0] = ip6_0->src_address.as_u64[0];
+                  tr->src.ip6.as_u64[1] = ip6_0->src_address.as_u64[1];
+                  tr->dst.ip6.as_u64[0] = ip6_0->dst_address.as_u64[0];
+                  tr->dst.ip6.as_u64[1] = ip6_0->dst_address.as_u64[1];
+                }
             }
 
           if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
@@ -272,9 +358,20 @@ drop1:
               gre_rx_trace_t *tr = vlib_add_trace (vm, node,
                                                    b1, sizeof (*tr));
               tr->tunnel_id = tunnel_sw_if_index;
-              tr->length = ip1->length;
-              tr->src.as_u32 = ip1->src_address.as_u32;
-              tr->dst.as_u32 = ip1->dst_address.as_u32;
+              if (!is_ipv6)
+                {
+                  tr->length = ip4_1->length;
+                  tr->src.ip4.as_u32 = ip4_1->src_address.as_u32;
+                  tr->dst.ip4.as_u32 = ip4_1->dst_address.as_u32;
+                }
+              else
+                {
+                  tr->length = ip6_1->payload_length;
+                  tr->src.ip6.as_u64[0] = ip6_1->src_address.as_u64[0];
+                  tr->src.ip6.as_u64[1] = ip6_1->src_address.as_u64[1];
+                  tr->dst.ip6.as_u64[0] = ip6_1->dst_address.as_u64[0];
+                  tr->dst.ip6.as_u64[1] = ip6_1->dst_address.as_u64[1];
+                }
             }
 
           vlib_buffer_advance (b0, sizeof (*h0));
@@ -284,16 +381,19 @@ drop1:
                                           to_next, n_left_to_next,
                                           bi0, bi1, next0, next1);
        }
-    
+
       while (n_left_from > 0 && n_left_to_next > 0)
        {
          u32 bi0;
          vlib_buffer_t * b0;
          gre_header_t * h0;
-          ip4_header_t * ip0;
+          ip4_header_t * ip4_0;
+          ip6_header_t * ip6_0;
           u16 version0;
           int verr0;
          u32 i0, next0;
+      u32 ip4_tun_src0, ip4_tun_dst0;
+      u32 ip6_tun_src0[4], ip6_tun_dst0[4];
 
          bi0 = from[0];
          to_next[0] = bi0;
@@ -303,22 +403,35 @@ drop1:
          n_left_to_next -= 1;
 
          b0 = vlib_get_buffer (vm, bi0);
-          ip0 = vlib_buffer_get_current (b0);
+          ip4_0 = vlib_buffer_get_current (b0);
+          ip6_0 = (void *)ip4_0;
+
+          if (!is_ipv6)
+            {
+              ip4_tun_src0 = ip4_0->src_address.as_u32;
+              ip4_tun_dst0 = ip4_0->dst_address.as_u32;
 
-          vnet_buffer(b0)->gre.src = ip0->src_address.as_u32;
-          vnet_buffer(b0)->gre.dst = ip0->dst_address.as_u32;
+              vlib_buffer_advance (b0, sizeof (*ip4_0));
+            }
+          else
+            {
+              ip6_tun_src0[0] = ip6_0->src_address.as_u64[0];
+              ip6_tun_src0[1] = ip6_0->src_address.as_u64[1];
+              ip6_tun_dst0[0] = ip6_0->dst_address.as_u64[0];
+              ip6_tun_dst0[1] = ip6_0->dst_address.as_u64[1];
 
-          vlib_buffer_advance (b0, sizeof (*ip0));
+              vlib_buffer_advance (b0, sizeof (*ip6_0));
+            }
 
          h0 = vlib_buffer_get_current (b0);
 
          i0 = sparse_vec_index (gm->next_by_protocol, h0->protocol);
           next0 = vec_elt(gm->next_by_protocol, i0);
 
-         b0->error = 
+         b0->error =
               node->errors[i0 == SPARSE_VEC_INVALID_INDEX 
                            ? GRE_ERROR_UNKNOWN_PROTOCOL : GRE_ERROR_NONE];
-         
+
           version0 = clib_net_to_host_u16 (h0->flags_and_version);
           verr0 =  version0 & GRE_VERSION_MASK;
           b0->error = verr0 ? node->errors[GRE_ERROR_UNSUPPORTED_VERSION] 
@@ -335,16 +448,34 @@ drop1:
                           || next0 == GRE_INPUT_NEXT_ETHERNET_INPUT
                           || next0 == GRE_INPUT_NEXT_MPLS_INPUT))
             {
-              u64 key = ((u64)(vnet_buffer(b0)->gre.dst) << 32) |
-                         (u64)(vnet_buffer(b0)->gre.src);
-
-              if (cached_tunnel_key != key)
+              u64 key4, key6[4];
+              if (!is_ipv6)
+               {
+                  key4 = ((u64)(ip4_tun_dst0) << 32) | (u64)(ip4_tun_src0);
+               }
+              else
                 {
+                  key6[0] = ip6_tun_dst0[0];
+                  key6[1] = ip6_tun_dst0[1];
+                  key6[2] = ip6_tun_src0[0];
+                  key6[3] = ip6_tun_src0[1];
+                }
+
+              if ((!is_ipv6 && cached_tunnel_key4 != key4) ||
+                 (is_ipv6 && cached_tunnel_key6[0] != key6[0] &&
+                     cached_tunnel_key6[1] != key6[1] &&
+                     cached_tunnel_key6[2] != key6[2] &&
+                     cached_tunnel_key6[3] != key6[3]))
+               {
                   vnet_hw_interface_t * hi;
                   gre_tunnel_t * t;
                   uword * p;
 
-                  p = hash_get (gm->tunnel_by_key, key);
+                  if (!is_ipv6)
+                    p = hash_get (gm->tunnel_by_key4, key4);
+                  else
+                    p = hash_get_mem (gm->tunnel_by_key6, key6);
+
                   if (!p)
                     {
                       next0 = GRE_INPUT_NEXT_DROP;
@@ -379,14 +510,25 @@ drop1:
           vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index;
 
 drop:
-          if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
+          if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
             {
-              gre_rx_trace_t *tr = vlib_add_trace (vm, node, 
+              gre_rx_trace_t *tr = vlib_add_trace (vm, node,
                                                    b0, sizeof (*tr));
               tr->tunnel_id = tunnel_sw_if_index;
-              tr->length = ip0->length;
-              tr->src.as_u32 = ip0->src_address.as_u32;
-              tr->dst.as_u32 = ip0->dst_address.as_u32;
+              if (!is_ipv6)
+                {
+                  tr->length = ip4_0->length;
+                  tr->src.ip4.as_u32 = ip4_0->src_address.as_u32;
+                  tr->dst.ip4.as_u32 = ip4_0->dst_address.as_u32;
+                }
+              else
+                {
+                  tr->length = ip6_0->payload_length;
+                  tr->src.ip6.as_u64[0] = ip6_0->src_address.as_u64[0];
+                  tr->src.ip6.as_u64[1] = ip6_0->src_address.as_u64[1];
+                  tr->dst.ip6.as_u64[0] = ip6_0->dst_address.as_u64[0];
+                  tr->dst.ip6.as_u64[1] = ip6_0->dst_address.as_u64[1];
+                }
             }
 
           vlib_buffer_advance (b0, sizeof (*h0));
@@ -398,20 +540,36 @@ drop:
 
       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     }
-  vlib_node_increment_counter (vm, gre_input_node.index,
+  vlib_node_increment_counter (vm, !is_ipv6 ? gre4_input_node.index : gre6_input_node.index,
                                GRE_ERROR_PKTS_DECAP, from_frame->n_vectors);
   return from_frame->n_vectors;
 }
 
+static uword
+gre4_input (vlib_main_t * vm,
+            vlib_node_runtime_t * node,
+         vlib_frame_t * from_frame)
+{
+   return gre_input(vm, node, from_frame, /* is_ip6 */ 0);
+}
+
+static uword
+gre6_input (vlib_main_t * vm,
+            vlib_node_runtime_t * node,
+            vlib_frame_t * from_frame)
+{
+   return gre_input(vm, node, from_frame, /* is_ip6 */ 1);
+}
+
 static char * gre_error_strings[] = {
 #define gre_error(n,s) s,
 #include "error.def"
 #undef gre_error
 };
 
-VLIB_REGISTER_NODE (gre_input_node) = {
-  .function = gre_input,
-  .name = "gre-input",
+VLIB_REGISTER_NODE (gre4_input_node) = {
+  .function = gre4_input,
+  .name = "gre4-input",
   /* Takes a vector of packets. */
   .vector_size = sizeof (u32),
 
@@ -430,7 +588,31 @@ VLIB_REGISTER_NODE (gre_input_node) = {
   .unformat_buffer = unformat_gre_header,
 };
 
-VLIB_NODE_FUNCTION_MULTIARCH (gre_input_node, gre_input)
+VLIB_REGISTER_NODE (gre6_input_node) = {
+  .function = gre6_input,
+  .name = "gre6-input",
+  /* Takes a vector of packets. */
+  .vector_size = sizeof (u32),
+
+  .runtime_data_bytes = sizeof (gre_input_runtime_t),
+
+  .n_errors = GRE_N_ERROR,
+  .error_strings = gre_error_strings,
+
+  .n_next_nodes = GRE_INPUT_N_NEXT,
+  .next_nodes = {
+#define _(s,n) [GRE_INPUT_NEXT_##s] = n,
+    foreach_gre_input_next
+#undef _
+  },
+
+  .format_buffer = format_gre_header_with_length,
+  .format_trace = format_gre_rx_trace,
+  .unformat_buffer = unformat_gre_header,
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gre4_input_node, gre4_input)
+VLIB_NODE_FUNCTION_MULTIARCH (gre6_input_node, gre6_input)
 
 void
 gre_register_input_protocol (vlib_main_t * vm,
@@ -440,6 +622,7 @@ gre_register_input_protocol (vlib_main_t * vm,
   gre_main_t * em = &gre_main;
   gre_protocol_info_t * pi;
   u16 * n;
+  u32 i;
 
   {
     clib_error_t * error = vlib_call_init_function (vm, gre_input_init);
@@ -449,9 +632,9 @@ gre_register_input_protocol (vlib_main_t * vm,
 
   pi = gre_get_protocol_info (em, protocol);
   pi->node_index = node_index;
-  pi->next_index = vlib_node_add_next (vm, 
-                                      gre_input_node.index,
-                                      node_index);
+  pi->next_index = vlib_node_add_next (vm, gre4_input_node.index, node_index);
+  i = vlib_node_add_next (vm, gre6_input_node.index, node_index);
+  ASSERT(i == pi->next_index);
 
   /* Setup gre protocol -> next index sparse vector mapping. */
   n = sparse_vec_validate (em->next_by_protocol,
@@ -476,13 +659,14 @@ static clib_error_t * gre_input_init (vlib_main_t * vm)
   vlib_node_t *ethernet_input, *ip4_input, *ip6_input, *mpls_unicast_input;
 
   {
-    clib_error_t * error; 
+    clib_error_t * error;
     error = vlib_call_init_function (vm, gre_init);
     if (error)
       clib_error_report (error);
   }
 
-  gre_setup_node (vm, gre_input_node.index);
+  gre_setup_node (vm, gre4_input_node.index);
+  gre_setup_node (vm, gre6_input_node.index);
 
   gm->next_by_protocol = sparse_vec_new
     (/* elt bytes */ sizeof (gm->next_by_protocol[0]),
@@ -501,19 +685,19 @@ static clib_error_t * gre_input_init (vlib_main_t * vm)
   gre_register_input_protocol (vm, GRE_PROTOCOL_teb,
                                ethernet_input->index);
 
-  gre_register_input_protocol (vm, GRE_PROTOCOL_ip4, 
+  gre_register_input_protocol (vm, GRE_PROTOCOL_ip4,
                                ip4_input->index);
 
-  gre_register_input_protocol (vm, GRE_PROTOCOL_ip6, 
+  gre_register_input_protocol (vm, GRE_PROTOCOL_ip6,
                                ip6_input->index);
 
   gre_register_input_protocol (vm, GRE_PROTOCOL_mpls_unicast,
                                mpls_unicast_input->index);
 
-  ip4_register_protocol (IP_PROTOCOL_GRE, gre_input_node.index);
+  ip4_register_protocol (IP_PROTOCOL_GRE, gre4_input_node.index);
+  ip6_register_protocol (IP_PROTOCOL_GRE, gre6_input_node.index);
 
   return 0;
 }
 
 VLIB_INIT_FUNCTION (gre_input_init);
-
index d20f248..217d323 100644 (file)
@@ -92,6 +92,8 @@ ipsec_gre_input (vlib_main_t * vm,
   u32 n_left_from, next_index, * from, * to_next;
   u64 cached_tunnel_key = (u64) ~0;
   u32 cached_tunnel_sw_if_index = 0, tunnel_sw_if_index;
+  u32 tun_src0, tun_dst0;
+  u32 tun_src1, tun_dst1;
 
   from = vlib_frame_vector_args (from_frame);
   n_left_from = from_frame->n_vectors;
@@ -146,10 +148,10 @@ ipsec_gre_input (vlib_main_t * vm,
           ip1 = vlib_buffer_get_current (b1);
 
           /* Save src + dst ip4 address */
-          vnet_buffer(b0)->gre.src = ip0->src_address.as_u32;
-          vnet_buffer(b0)->gre.dst = ip0->dst_address.as_u32;
-          vnet_buffer(b1)->gre.src = ip1->src_address.as_u32;
-          vnet_buffer(b1)->gre.dst = ip1->dst_address.as_u32;
+          tun_src0 = ip0->src_address.as_u32;
+          tun_dst0 = ip0->dst_address.as_u32;
+          tun_src1 = ip1->src_address.as_u32;
+          tun_dst1 = ip1->dst_address.as_u32;
 
           vlib_buffer_advance (b0, sizeof (*ip0));
           vlib_buffer_advance (b1, sizeof (*ip1));
@@ -197,8 +199,7 @@ ipsec_gre_input (vlib_main_t * vm,
           /* For L2 payload set input sw_if_index to GRE tunnel for learning */
           if (PREDICT_TRUE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
             {
-              u64 key = ((u64)(vnet_buffer(b0)->gre.dst) << 32) |
-                         (u64)(vnet_buffer(b0)->gre.src);
+              u64 key = ((u64)(tun_dst0) << 32) | (u64)(tun_src0);
 
               if (cached_tunnel_key != key)
                 {
@@ -230,8 +231,7 @@ drop0:
           /* For L2 payload set input sw_if_index to GRE tunnel for learning */
           if (PREDICT_TRUE(next1 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
             {
-              u64 key = ((u64)(vnet_buffer(b1)->gre.dst) << 32) |
-                         (u64)(vnet_buffer(b1)->gre.src);
+              u64 key = ((u64)(tun_dst1) << 32) | (u64)(tun_src1);
 
               if (cached_tunnel_key != key)
                 {
@@ -297,6 +297,7 @@ drop1:
           u16 version0, protocol0;
           int verr0;
          u32 next0;
+         u32 tun_src0, tun_dst0;
 
          bi0 = from[0];
          to_next[0] = bi0;
@@ -308,8 +309,8 @@ drop1:
          b0 = vlib_get_buffer (vm, bi0);
           ip0 = vlib_buffer_get_current (b0);
 
-          vnet_buffer(b0)->gre.src = ip0->src_address.as_u32;
-          vnet_buffer(b0)->gre.dst = ip0->dst_address.as_u32;
+          tun_src0 = ip0->src_address.as_u32;
+          tun_dst0 = ip0->dst_address.as_u32;
 
           vlib_buffer_advance (b0, sizeof (*ip0));
 
@@ -337,8 +338,7 @@ drop1:
           /* For L2 payload set input sw_if_index to GRE tunnel for learning */
           if (PREDICT_FALSE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
             {
-              u64 key = ((u64)(vnet_buffer(b0)->gre.dst) << 32) |
-                         (u64)(vnet_buffer(b0)->gre.src);
+              u64 key = ((u64)(tun_dst0) << 32) | (u64)(tun_src0);
 
               if (cached_tunnel_key != key)
                 {
index f2a5e0b..18b67db 100644 (file)
@@ -5,7 +5,7 @@ from logging import *
 
 from framework import VppTestCase, VppTestRunner
 from vpp_sub_interface import VppDot1QSubint
-from vpp_gre_interface import VppGreInterface
+from vpp_gre_interface import VppGreInterface, VppGre6Interface
 from vpp_ip_route import VppIpRoute, VppRoutePath
 from vpp_papi_provider import L2_VTR_OP
 
@@ -28,14 +28,19 @@ class TestGRE(VppTestCase):
     def setUp(self):
         super(TestGRE, self).setUp()
 
-        # create 2 pg interfaces - set one in a non-default table.
-        self.create_pg_interfaces(range(2))
-
+        # create 3 pg interfaces - set one in a non-default table.
+        self.create_pg_interfaces(range(3))
         self.pg1.set_table_ip4(1)
+
         for i in self.pg_interfaces:
             i.admin_up()
-            i.config_ip4()
-            i.resolve_arp()
+
+        self.pg0.config_ip4()
+        self.pg0.resolve_arp()
+        self.pg1.config_ip4()
+        self.pg1.resolve_arp()
+        self.pg2.config_ip6()
+        self.pg2.resolve_ndp()
 
     def tearDown(self):
         super(TestGRE, self).tearDown()
@@ -57,6 +62,19 @@ class TestGRE(VppTestCase):
             pkts.append(p)
         return pkts
 
+    def create_stream_ip6(self, src_if, src_ip, dst_ip):
+        pkts = []
+        for i in range(0, 257):
+            info = self.create_packet_info(src_if, src_if)
+            payload = self.info_to_payload(info)
+            p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
+                 IPv6(src=src_ip, dst=dst_ip) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw(payload))
+            info.data = p.copy()
+            pkts.append(p)
+        return pkts
+
     def create_tunnel_stream_4o4(self, src_if,
                                  tunnel_src, tunnel_dst,
                                  src_ip, dst_ip):
@@ -91,6 +109,23 @@ class TestGRE(VppTestCase):
             pkts.append(p)
         return pkts
 
+    def create_tunnel_stream_6o6(self, src_if,
+                                 tunnel_src, tunnel_dst,
+                                 src_ip, dst_ip):
+        pkts = []
+        for i in range(0, 257):
+            info = self.create_packet_info(src_if, src_if)
+            payload = self.info_to_payload(info)
+            p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
+                 IPv6(src=tunnel_src, dst=tunnel_dst) /
+                 GRE() /
+                 IPv6(src=src_ip, dst=dst_ip) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw(payload))
+            info.data = p.copy()
+            pkts.append(p)
+        return pkts
+
     def create_tunnel_stream_l2o4(self, src_if,
                                   tunnel_src, tunnel_dst):
         pkts = []
@@ -157,6 +192,33 @@ class TestGRE(VppTestCase):
                 self.logger.error(ppp("Tx:", tx))
                 raise
 
+    def verify_tunneled_6o6(self, src_if, capture, sent,
+                            tunnel_src, tunnel_dst):
+
+        self.assertEqual(len(capture), len(sent))
+
+        for i in range(len(capture)):
+            try:
+                tx = sent[i]
+                rx = capture[i]
+
+                tx_ip = tx[IPv6]
+                rx_ip = rx[IPv6]
+
+                self.assertEqual(rx_ip.src, tunnel_src)
+                self.assertEqual(rx_ip.dst, tunnel_dst)
+
+                rx_gre = GRE(str(rx_ip[IPv6].payload))
+                rx_ip = rx_gre[IPv6]
+
+                self.assertEqual(rx_ip.src, tx_ip.src)
+                self.assertEqual(rx_ip.dst, tx_ip.dst)
+
+            except:
+                self.logger.error(ppp("Rx:", rx))
+                self.logger.error(ppp("Tx:", tx))
+                raise
+
     def verify_tunneled_l2o4(self, src_if, capture, sent,
                              tunnel_src, tunnel_dst):
         self.assertEqual(len(capture), len(sent))
@@ -275,7 +337,7 @@ class TestGRE(VppTestCase):
                 raise
 
     def test_gre(self):
-        """ GRE tunnel Tests """
+        """ GRE IPv4 tunnel Tests """
 
         #
         # Create an L3 GRE tunnel.
@@ -438,6 +500,79 @@ class TestGRE(VppTestCase):
 
         self.pg0.unconfig_ip6()
 
+    def test_gre6(self):
+        """ GRE IPv6 tunnel Tests """
+
+        #
+        # Create an L3 GRE tunnel.
+        #  - set it admin up
+        #  - assign an IP Address
+        #  - Add a route via the tunnel
+        #
+        gre_if = VppGre6Interface(self,
+                                  self.pg2.local_ip6,
+                                  "1002::1")
+        gre_if.add_vpp_config()
+        gre_if.admin_up()
+        gre_if.config_ip6()
+
+        route_via_tun = VppIpRoute(self, "4004::1", 128,
+                                   [VppRoutePath("0::0",
+                                                 gre_if.sw_if_index,
+                                                 is_ip6=1)],
+                                   is_ip6=1)
+
+        route_via_tun.add_vpp_config()
+
+        #
+        # Send a packet stream that is routed into the tunnel
+        #  - they are all dropped since the tunnel's desintation IP
+        #    is unresolved - or resolves via the default route - which
+        #    which is a drop.
+        #
+        tx = self.create_stream_ip6(self.pg2, "5005::1", "4004::1")
+        self.pg2.add_stream(tx)
+
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        self.pg2.assert_nothing_captured(
+            remark="GRE packets forwarded without DIP resolved")
+
+        #
+        # Add a route that resolves the tunnel's destination
+        #
+        route_tun_dst = VppIpRoute(self, "1002::1", 128,
+                                   [VppRoutePath(self.pg2.remote_ip6,
+                                                 self.pg2.sw_if_index,
+                                                 is_ip6=1)],
+                                   is_ip6=1)
+        route_tun_dst.add_vpp_config()
+
+        #
+        # Send a packet stream that is routed into the tunnel
+        #  - packets are GRE encapped
+        #
+        self.vapi.cli("clear trace")
+        tx = self.create_stream_ip6(self.pg2, "5005::1", "4004::1")
+        self.pg2.add_stream(tx)
+
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        rx = self.pg2.get_capture(len(tx))
+        self.verify_tunneled_6o6(self.pg2, rx, tx,
+                                 self.pg2.local_ip6, "1002::1")
+
+        #
+        # test case cleanup
+        #
+        route_tun_dst.remove_vpp_config()
+        route_via_tun.remove_vpp_config()
+        gre_if.remove_vpp_config()
+
+        self.pg2.unconfig_ip6()
+
     def test_gre_vrf(self):
         """ GRE tunnel VRF Tests """
 
index 58a6829..1c71875 100644 (file)
@@ -34,3 +34,38 @@ class VppGreInterface(VppInterface):
         r = self.test.vapi.gre_tunnel_add_del(s, d,
                                               outer_fib_id=self.t_outer_fib,
                                               is_add=0)
+
+
+class VppGre6Interface(VppInterface):
+    """
+    VPP GRE IPv6 interface
+    """
+
+    def __init__(self, test, src_ip, dst_ip, outer_fib_id=0, is_teb=0):
+        """ Create VPP loopback interface """
+        self._sw_if_index = 0
+        super(VppGre6Interface, self).__init__(test)
+        self._test = test
+        self.t_src = src_ip
+        self.t_dst = dst_ip
+        self.t_outer_fib = outer_fib_id
+        self.t_is_teb = is_teb
+
+    def add_vpp_config(self):
+        s = socket.inet_pton(socket.AF_INET6, self.t_src)
+        d = socket.inet_pton(socket.AF_INET6, self.t_dst)
+        r = self.test.vapi.gre_tunnel_add_del(s, d,
+                                              outer_fib_id=self.t_outer_fib,
+                                              is_teb=self.t_is_teb,
+                                              is_ip6=1)
+        self._sw_if_index = r.sw_if_index
+        self.generate_remote_hosts()
+
+    def remove_vpp_config(self):
+        s = socket.inet_pton(socket.AF_INET6, self.t_src)
+        d = socket.inet_pton(socket.AF_INET6, self.t_dst)
+        self.unconfig()
+        r = self.test.vapi.gre_tunnel_add_del(s, d,
+                                              outer_fib_id=self.t_outer_fib,
+                                              is_add=0,
+                                              is_ip6=1)