aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCiara Loftus <ciara.loftus@intel.com>2016-09-30 15:47:03 +0100
committerCiara Loftus <ciara.loftus@intel.com>2017-04-05 09:06:23 +0100
commit7eac916e1b00d6a3393a09925e1634d71bf30568 (patch)
tree94a3167a1abf03e62a2207f28905263a2b09229e
parent63d5bae6401049debadfa9fcc3f18d8118b80441 (diff)
GRE over IPv6
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>
-rw-r--r--src/vat/api_format.c78
-rw-r--r--src/vnet/gre/gre.c110
-rw-r--r--src/vnet/gre/gre.h30
-rw-r--r--src/vnet/gre/gre_api.c44
-rw-r--r--src/vnet/gre/interface.c194
-rw-r--r--src/vnet/gre/node.c320
-rw-r--r--src/vnet/ipsec-gre/node.c24
-rw-r--r--test/test_gre.py149
-rw-r--r--test/vpp_gre_interface.py35
9 files changed, 787 insertions, 197 deletions
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index 06884eb1b91..090d990b9a5 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -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") \
diff --git a/src/vnet/gre/gre.c b/src/vnet/gre/gre.c
index 3d3813881ae..a153c3c5b81 100644
--- a/src/vnet/gre/gre.c
+++ b/src/vnet/gre/gre.c
@@ -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)
{
@@ -236,11 +267,36 @@ gre_fixup (vlib_main_t *vm,
}
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
diff --git a/src/vnet/gre/gre.h b/src/vnet/gre/gre.h
index 788cba27926..ad3e025a0ff 100644
--- a/src/vnet/gre/gre.h
+++ b/src/vnet/gre/gre.h
@@ -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;
diff --git a/src/vnet/gre/gre_api.c b/src/vnet/gre/gre_api.c
index 333838c06ad..ceeb1d4c697 100644
--- a/src/vnet/gre/gre_api.c
+++ b/src/vnet/gre/gre_api.c
@@ -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);
}
diff --git a/src/vnet/gre/interface.c b/src/vnet/gre/interface.c
index d4476ac4682..91a3899ff53 100644
--- a/src/vnet/gre/interface.c
+++ b/src/vnet/gre/interface.c
@@ -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);
diff --git a/src/vnet/gre/node.c b/src/vnet/gre/node.c
index 5926e83da5a..2683586ea8f 100644
--- a/src/vnet/gre/node.c
+++ b/src/vnet/gre/node.c
@@ -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);
-
diff --git a/src/vnet/ipsec-gre/node.c b/src/vnet/ipsec-gre/node.c
index d20f248a6c8..217d323ad65 100644
--- a/src/vnet/ipsec-gre/node.c
+++ b/src/vnet/ipsec-gre/node.c
@@ -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)
{
diff --git a/test/test_gre.py b/test/test_gre.py
index f2a5e0b0222..18b67dbd187 100644
--- a/test/test_gre.py
+++ b/test/test_gre.py
@@ -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 """
diff --git a/test/vpp_gre_interface.py b/test/vpp_gre_interface.py
index 58a68290c60..1c71875f73a 100644
--- a/test/vpp_gre_interface.py
+++ b/test/vpp_gre_interface.py
@@ -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)