aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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)