summaryrefslogtreecommitdiffstats
path: root/src/vnet/ipsec
diff options
context:
space:
mode:
authorDamjan Marion <damarion@cisco.com>2019-04-24 15:20:35 +0200
committerNeale Ranns <nranns@cisco.com>2019-04-25 01:36:12 +0000
commitd1bed687231bb64cf7761da37431ba61bc32b6d8 (patch)
tree891af80a873db9dda53c18e95f5eeb9366a1cb07 /src/vnet/ipsec
parent20bc56ab58189ad9fa24feaaca3e76ea8e636140 (diff)
crypto: improve key handling
Change-Id: If96f661d507305da4b96cac7b1a8f14ba90676ad Signed-off-by: Damjan Marion <damarion@cisco.com>
Diffstat (limited to 'src/vnet/ipsec')
-rw-r--r--src/vnet/ipsec/esp.h3
-rw-r--r--src/vnet/ipsec/esp_decrypt.c5
-rw-r--r--src/vnet/ipsec/esp_encrypt.c5
-rw-r--r--src/vnet/ipsec/ipsec.c13
-rw-r--r--src/vnet/ipsec/ipsec.h2
-rw-r--r--src/vnet/ipsec/ipsec_if.c13
-rw-r--r--src/vnet/ipsec/ipsec_sa.c18
-rw-r--r--src/vnet/ipsec/ipsec_sa.h10
8 files changed, 58 insertions, 11 deletions
diff --git a/src/vnet/ipsec/esp.h b/src/vnet/ipsec/esp.h
index 052bbfc1d88..070726faa34 100644
--- a/src/vnet/ipsec/esp.h
+++ b/src/vnet/ipsec/esp.h
@@ -112,8 +112,7 @@ hmac_calc (vlib_main_t * vm, ipsec_sa_t * sa, u8 * data, int data_len,
return 0;
vnet_crypto_op_init (op, sa->integ_op_id);
- op->key = sa->integ_key.data;
- op->key_len = sa->integ_key.len;
+ op->key_index = sa->integ_key_index;
op->src = data;
op->len = data_len;
op->digest = signature;
diff --git a/src/vnet/ipsec/esp_decrypt.c b/src/vnet/ipsec/esp_decrypt.c
index e74c1bb908a..5d1f20604d5 100644
--- a/src/vnet/ipsec/esp_decrypt.c
+++ b/src/vnet/ipsec/esp_decrypt.c
@@ -203,8 +203,7 @@ esp_decrypt_inline (vlib_main_t * vm,
vec_add2_aligned (ptd->integ_ops, op, 1, CLIB_CACHE_LINE_BYTES);
vnet_crypto_op_init (op, sa0->integ_op_id);
- op->key = sa0->integ_key.data;
- op->key_len = sa0->integ_key.len;
+ op->key_index = sa0->integ_key_index;
op->src = payload;
op->flags = VNET_CRYPTO_OP_FLAG_HMAC_CHECK;
op->user_data = b - bufs;
@@ -231,7 +230,7 @@ esp_decrypt_inline (vlib_main_t * vm,
vnet_crypto_op_t *op;
vec_add2_aligned (ptd->crypto_ops, op, 1, CLIB_CACHE_LINE_BYTES);
vnet_crypto_op_init (op, sa0->crypto_dec_op_id);
- op->key = sa0->crypto_key.data;
+ op->key_index = sa0->crypto_key_index;
op->iv = payload;
if (ipsec_sa_is_set_IS_AEAD (sa0))
diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c
index 5db10b520e3..af56302bafb 100644
--- a/src/vnet/ipsec/esp_encrypt.c
+++ b/src/vnet/ipsec/esp_encrypt.c
@@ -441,7 +441,7 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
vnet_crypto_op_init (op, sa0->crypto_enc_op_id);
op->iv = payload - iv_sz;
op->src = op->dst = payload;
- op->key = sa0->crypto_key.data;
+ op->key_index = sa0->crypto_key_index;
op->len = payload_len - icv_sz;
op->flags = VNET_CRYPTO_OP_FLAG_INIT_IV;
op->user_data = b - bufs;
@@ -469,8 +469,7 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
vnet_crypto_op_init (op, sa0->integ_op_id);
op->src = payload - iv_sz - sizeof (esp_header_t);
op->digest = payload + payload_len - icv_sz;
- op->key = sa0->integ_key.data;
- op->key_len = sa0->integ_key.len;
+ op->key_index = sa0->integ_key_index;
op->digest_len = icv_sz;
op->len = payload_len - icv_sz + iv_sz + sizeof (esp_header_t);
op->user_data = b - bufs;
diff --git a/src/vnet/ipsec/ipsec.c b/src/vnet/ipsec/ipsec.c
index 73c5cf4d7ab..c530a44e870 100644
--- a/src/vnet/ipsec/ipsec.c
+++ b/src/vnet/ipsec/ipsec.c
@@ -264,43 +264,51 @@ ipsec_init (vlib_main_t * vm)
a = im->crypto_algs + IPSEC_CRYPTO_ALG_DES_CBC;
a->enc_op_id = VNET_CRYPTO_OP_DES_CBC_ENC;
a->dec_op_id = VNET_CRYPTO_OP_DES_CBC_DEC;
+ a->alg = VNET_CRYPTO_ALG_DES_CBC;
a->iv_size = a->block_size = 8;
a = im->crypto_algs + IPSEC_CRYPTO_ALG_3DES_CBC;
a->enc_op_id = VNET_CRYPTO_OP_3DES_CBC_ENC;
a->dec_op_id = VNET_CRYPTO_OP_3DES_CBC_DEC;
+ a->alg = VNET_CRYPTO_ALG_3DES_CBC;
a->iv_size = a->block_size = 8;
a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_CBC_128;
a->enc_op_id = VNET_CRYPTO_OP_AES_128_CBC_ENC;
a->dec_op_id = VNET_CRYPTO_OP_AES_128_CBC_DEC;
+ a->alg = VNET_CRYPTO_ALG_AES_128_CBC;
a->iv_size = a->block_size = 16;
a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_CBC_192;
a->enc_op_id = VNET_CRYPTO_OP_AES_192_CBC_ENC;
a->dec_op_id = VNET_CRYPTO_OP_AES_192_CBC_DEC;
+ a->alg = VNET_CRYPTO_ALG_AES_192_CBC;
a->iv_size = a->block_size = 16;
a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_CBC_256;
a->enc_op_id = VNET_CRYPTO_OP_AES_256_CBC_ENC;
a->dec_op_id = VNET_CRYPTO_OP_AES_256_CBC_DEC;
+ a->alg = VNET_CRYPTO_ALG_AES_256_CBC;
a->iv_size = a->block_size = 16;
a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_GCM_128;
a->enc_op_id = VNET_CRYPTO_OP_AES_128_GCM_ENC;
a->dec_op_id = VNET_CRYPTO_OP_AES_128_GCM_DEC;
+ a->alg = VNET_CRYPTO_ALG_AES_128_GCM;
a->iv_size = a->block_size = 8;
a->icv_size = 16;
a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_GCM_192;
a->enc_op_id = VNET_CRYPTO_OP_AES_192_GCM_ENC;
a->dec_op_id = VNET_CRYPTO_OP_AES_192_GCM_DEC;
+ a->alg = VNET_CRYPTO_ALG_AES_192_GCM;
a->iv_size = a->block_size = 8;
a->icv_size = 16;
a = im->crypto_algs + IPSEC_CRYPTO_ALG_AES_GCM_256;
a->enc_op_id = VNET_CRYPTO_OP_AES_256_GCM_ENC;
a->dec_op_id = VNET_CRYPTO_OP_AES_256_GCM_DEC;
+ a->alg = VNET_CRYPTO_ALG_AES_256_GCM;
a->iv_size = a->block_size = 8;
a->icv_size = 16;
@@ -309,22 +317,27 @@ ipsec_init (vlib_main_t * vm)
i = &im->integ_algs[IPSEC_INTEG_ALG_SHA1_96];
i->op_id = VNET_CRYPTO_OP_SHA1_HMAC;
+ i->alg = VNET_CRYPTO_ALG_HMAC_SHA1;
i->icv_size = 12;
i = &im->integ_algs[IPSEC_INTEG_ALG_SHA_256_96];
i->op_id = VNET_CRYPTO_OP_SHA1_HMAC;
+ i->alg = VNET_CRYPTO_ALG_HMAC_SHA256;
i->icv_size = 12;
i = &im->integ_algs[IPSEC_INTEG_ALG_SHA_256_128];
i->op_id = VNET_CRYPTO_OP_SHA256_HMAC;
+ i->alg = VNET_CRYPTO_ALG_HMAC_SHA256;
i->icv_size = 16;
i = &im->integ_algs[IPSEC_INTEG_ALG_SHA_384_192];
i->op_id = VNET_CRYPTO_OP_SHA384_HMAC;
+ i->alg = VNET_CRYPTO_ALG_HMAC_SHA512;
i->icv_size = 24;
i = &im->integ_algs[IPSEC_INTEG_ALG_SHA_512_256];
i->op_id = VNET_CRYPTO_OP_SHA512_HMAC;
+ i->alg = VNET_CRYPTO_ALG_HMAC_SHA512;
i->icv_size = 32;
vec_validate_aligned (im->ptd, vlib_num_workers (), CLIB_CACHE_LINE_BYTES);
diff --git a/src/vnet/ipsec/ipsec.h b/src/vnet/ipsec/ipsec.h
index 110bbcb3e5a..232390c0690 100644
--- a/src/vnet/ipsec/ipsec.h
+++ b/src/vnet/ipsec/ipsec.h
@@ -68,6 +68,7 @@ typedef struct
{
vnet_crypto_op_id_t enc_op_id;
vnet_crypto_op_id_t dec_op_id;
+ vnet_crypto_alg_t alg;
u8 iv_size;
u8 block_size;
u8 icv_size;
@@ -76,6 +77,7 @@ typedef struct
typedef struct
{
vnet_crypto_op_id_t op_id;
+ vnet_crypto_alg_t alg;
u8 icv_size;
} ipsec_main_integ_alg_t;
diff --git a/src/vnet/ipsec/ipsec_if.c b/src/vnet/ipsec/ipsec_if.c
index 8d0eef21ae0..9f64634c51e 100644
--- a/src/vnet/ipsec/ipsec_if.c
+++ b/src/vnet/ipsec/ipsec_if.c
@@ -510,6 +510,7 @@ int
ipsec_set_interface_key (vnet_main_t * vnm, u32 hw_if_index,
ipsec_if_set_key_type_t type, u8 alg, u8 * key)
{
+ vlib_main_t *vm = vlib_get_main ();
ipsec_main_t *im = &ipsec_main;
vnet_hw_interface_t *hi;
ipsec_tunnel_if_t *t;
@@ -526,24 +527,36 @@ ipsec_set_interface_key (vnet_main_t * vnm, u32 hw_if_index,
sa = pool_elt_at_index (im->sad, t->output_sa_index);
ipsec_sa_set_crypto_alg (sa, alg);
ipsec_mk_key (&sa->crypto_key, key, vec_len (key));
+ sa->crypto_calg = im->crypto_algs[alg].alg;
+ vnet_crypto_key_modify (vm, sa->crypto_key_index, sa->crypto_calg,
+ key, vec_len (key));
}
else if (type == IPSEC_IF_SET_KEY_TYPE_LOCAL_INTEG)
{
sa = pool_elt_at_index (im->sad, t->output_sa_index);
ipsec_sa_set_integ_alg (sa, alg);
ipsec_mk_key (&sa->integ_key, key, vec_len (key));
+ sa->integ_calg = im->integ_algs[alg].alg;
+ vnet_crypto_key_modify (vm, sa->integ_key_index, sa->integ_calg,
+ key, vec_len (key));
}
else if (type == IPSEC_IF_SET_KEY_TYPE_REMOTE_CRYPTO)
{
sa = pool_elt_at_index (im->sad, t->input_sa_index);
ipsec_sa_set_crypto_alg (sa, alg);
ipsec_mk_key (&sa->crypto_key, key, vec_len (key));
+ sa->crypto_calg = im->crypto_algs[alg].alg;
+ vnet_crypto_key_modify (vm, sa->crypto_key_index, sa->crypto_calg,
+ key, vec_len (key));
}
else if (type == IPSEC_IF_SET_KEY_TYPE_REMOTE_INTEG)
{
sa = pool_elt_at_index (im->sad, t->input_sa_index);
ipsec_sa_set_integ_alg (sa, alg);
ipsec_mk_key (&sa->integ_key, key, vec_len (key));
+ sa->integ_calg = im->integ_algs[alg].alg;
+ vnet_crypto_key_modify (vm, sa->integ_key_index, sa->integ_calg,
+ key, vec_len (key));
}
else
return VNET_API_ERROR_INVALID_VALUE;
diff --git a/src/vnet/ipsec/ipsec_sa.c b/src/vnet/ipsec/ipsec_sa.c
index 8f41c95159b..2d2e90a1d27 100644
--- a/src/vnet/ipsec/ipsec_sa.c
+++ b/src/vnet/ipsec/ipsec_sa.c
@@ -100,6 +100,7 @@ ipsec_sa_set_crypto_alg (ipsec_sa_t * sa, ipsec_crypto_alg_t crypto_alg)
sa->crypto_block_size = im->crypto_algs[crypto_alg].block_size;
sa->crypto_enc_op_id = im->crypto_algs[crypto_alg].enc_op_id;
sa->crypto_dec_op_id = im->crypto_algs[crypto_alg].dec_op_id;
+ sa->crypto_calg = im->crypto_algs[crypto_alg].alg;
ASSERT (sa->crypto_iv_size <= ESP_MAX_IV_SIZE);
ASSERT (sa->crypto_block_size <= ESP_MAX_BLOCK_SIZE);
if (IPSEC_CRYPTO_ALG_IS_GCM (crypto_alg))
@@ -116,6 +117,7 @@ ipsec_sa_set_integ_alg (ipsec_sa_t * sa, ipsec_integ_alg_t integ_alg)
sa->integ_alg = integ_alg;
sa->integ_icv_size = im->integ_algs[integ_alg].icv_size;
sa->integ_op_id = im->integ_algs[integ_alg].op_id;
+ sa->integ_calg = im->integ_algs[integ_alg].alg;
ASSERT (sa->integ_icv_size <= ESP_MAX_ICV_SIZE);
}
@@ -133,6 +135,7 @@ ipsec_sa_add (u32 id,
const ip46_address_t * tun_src,
const ip46_address_t * tun_dst, u32 * sa_out_index)
{
+ vlib_main_t *vm = vlib_get_main ();
ipsec_main_t *im = &ipsec_main;
clib_error_t *err;
ipsec_sa_t *sa;
@@ -164,6 +167,13 @@ ipsec_sa_add (u32 id,
ip46_address_copy (&sa->tunnel_src_addr, tun_src);
ip46_address_copy (&sa->tunnel_dst_addr, tun_dst);
+ sa->crypto_key_index = vnet_crypto_key_add (vm,
+ im->crypto_algs[crypto_alg].alg,
+ (u8 *) ck->data, ck->len);
+ sa->integ_key_index = vnet_crypto_key_add (vm,
+ im->integ_algs[integ_alg].alg,
+ (u8 *) ik->data, ik->len);
+
err = ipsec_check_support_cb (im, sa);
if (err)
{
@@ -253,6 +263,7 @@ ipsec_sa_add (u32 id,
u32
ipsec_sa_del (u32 id)
{
+ vlib_main_t *vm = vlib_get_main ();
ipsec_main_t *im = &ipsec_main;
ipsec_sa_t *sa = 0;
uword *p;
@@ -286,6 +297,8 @@ ipsec_sa_del (u32 id)
dpo_reset (&sa->dpo[IPSEC_PROTOCOL_AH]);
dpo_reset (&sa->dpo[IPSEC_PROTOCOL_ESP]);
}
+ vnet_crypto_key_del (vm, sa->crypto_key_index);
+ vnet_crypto_key_del (vm, sa->integ_key_index);
pool_put (im->sad, sa);
return 0;
}
@@ -320,6 +333,7 @@ ipsec_is_sa_used (u32 sa_index)
int
ipsec_set_sa_key (u32 id, const ipsec_key_t * ck, const ipsec_key_t * ik)
{
+ vlib_main_t *vm = vlib_get_main ();
ipsec_main_t *im = &ipsec_main;
uword *p;
u32 sa_index;
@@ -337,12 +351,16 @@ ipsec_set_sa_key (u32 id, const ipsec_key_t * ck, const ipsec_key_t * ik)
if (ck)
{
clib_memcpy (&sa->crypto_key, ck, sizeof (sa->crypto_key));
+ vnet_crypto_key_modify (vm, sa->crypto_key_index, sa->crypto_calg,
+ (u8 *) ck->data, ck->len);
}
/* new integ key */
if (ik)
{
clib_memcpy (&sa->integ_key, 0, sizeof (sa->integ_key));
+ vnet_crypto_key_modify (vm, sa->integ_key_index, sa->integ_calg,
+ (u8 *) ik->data, ik->len);
}
if (ck || ik)
diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h
index d1b44c3165f..40850766ce1 100644
--- a/src/vnet/ipsec/ipsec_sa.h
+++ b/src/vnet/ipsec/ipsec_sa.h
@@ -125,9 +125,11 @@ typedef struct
u32 last_seq_hi;
u64 replay_window;
- vnet_crypto_op_id_t crypto_enc_op_id;
- vnet_crypto_op_id_t crypto_dec_op_id;
- vnet_crypto_op_id_t integ_op_id;
+ vnet_crypto_key_index_t crypto_key_index;
+ vnet_crypto_key_index_t integ_key_index;
+ vnet_crypto_op_id_t crypto_enc_op_id:16;
+ vnet_crypto_op_id_t crypto_dec_op_id:16;
+ vnet_crypto_op_id_t integ_op_id:16;
dpo_id_t dpo[IPSEC_N_PROTOCOLS];
@@ -149,9 +151,11 @@ typedef struct
ipsec_crypto_alg_t crypto_alg;
ipsec_key_t crypto_key;
+ vnet_crypto_alg_t crypto_calg;
ipsec_integ_alg_t integ_alg;
ipsec_key_t integ_key;
+ vnet_crypto_alg_t integ_calg;
ip46_address_t tunnel_src_addr;
ip46_address_t tunnel_dst_addr;
864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973
#!/usr/bin/env python

import unittest

import scapy.compat
from scapy.packet import Raw
from scapy.layers.l2 import Ether, Dot1Q, GRE
from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6
from scapy.volatile import RandMAC, RandIP

from framework import VppTestCase, VppTestRunner
from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint
from vpp_gre_interface import VppGreInterface
from vpp_ip import DpoProto
from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, FibPathProto
from util import ppp, ppc
from vpp_papi import VppEnum


class TestGREInputNodes(VppTestCase):
    """ GRE Input Nodes Test Case """

    def setUp(self):
        super(TestGREInputNodes, self).setUp()

        # create 3 pg interfaces - set one in a non-default table.
        self.create_pg_interfaces(range(1))

        for i in self.pg_interfaces:
            i.admin_up()
            i.config_ip4()

    def tearDown(self):
        for i in self.pg_interfaces:
            i.unconfig_ip4()
            i.admin_down()
        super(TestGREInputNodes, self).tearDown()

    def test_gre_input_node(self):
        """ GRE gre input nodes not registerd unless configured """
        pkt = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
               GRE())

        self.pg0.add_stream(pkt)
        self.pg_start()
        # no tunnel created, gre-input not registered
        err = self.statistics.get_counter(
            '/err/ip4-input/unknown ip protocol')[0]
        self.assertEqual(err, 1)
        err_count = err

        # create gre tunnel
        gre_if = VppGreInterface(self, self.pg0.local_ip4, "1.1.1.2")
        gre_if.add_vpp_config()

        self.pg0.add_stream(pkt)
        self.pg_start()
        # tunnel created, gre-input registered
        err = self.statistics.get_counter(
            '/err/ip4-input/unknown ip protocol')[0]
        # expect no new errors
        self.assertEqual(err, err_count)


class TestGRE(VppTestCase):
    """ GRE Test Case """

    @classmethod
    def setUpClass(cls):
        super(TestGRE, cls).setUpClass()

    @classmethod
    def tearDownClass(cls):
        super(TestGRE, cls).tearDownClass()

    def setUp(self):
        super(TestGRE, self).setUp()

        # create 3 pg interfaces - set one in a non-default table.
        self.create_pg_interfaces(range(3))

        self.tbl = VppIpTable(self, 1)
        self.tbl.add_vpp_config()
        self.pg1.set_table_ip4(1)

        for i in self.pg_interfaces:
            i.admin_up()

        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):
        for i in self.pg_interfaces:
            i.unconfig_ip4()
            i.unconfig_ip6()
            i.admin_down()
        self.pg1.set_table_ip4(0)
        super(TestGRE, self).tearDown()

    def create_stream_ip4(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) /
                 IP(src=src_ip, dst=dst_ip) /
                 UDP(sport=1234, dport=1234) /
                 Raw(payload))
            info.data = p.copy()
            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):
        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) /
                 IP(src=tunnel_src, dst=tunnel_dst) /
                 GRE() /
                 IP(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_6o4(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) /
                 IP(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_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 = []
        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) /
                 IP(src=tunnel_src, dst=tunnel_dst) /
                 GRE() /
                 Ether(dst=RandMAC('*:*:*:*:*:*'),
                       src=RandMAC('*:*:*:*:*:*')) /
                 IP(src=scapy.compat.raw(RandIP()),
                    dst=scapy.compat.raw(RandIP())) /
                 UDP(sport=1234, dport=1234) /
                 Raw(payload))
            info.data = p.copy()
            pkts.append(p)
        return pkts

    def create_tunnel_stream_vlano4(self, src_if,
                                    tunnel_src, tunnel_dst, vlan):
        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) /
                 IP(src=tunnel_src, dst=tunnel_dst) /
                 GRE() /
                 Ether(dst=RandMAC('*:*:*:*:*:*'),
                       src=RandMAC('*:*:*:*:*:*')) /
                 Dot1Q(vlan=vlan) /
                 IP(src=scapy.compat.raw(RandIP()),
                    dst=scapy.compat.raw(RandIP())) /
                 UDP(sport=1234, dport=1234) /
                 Raw(payload))
            info.data = p.copy()
            pkts.append(p)
        return pkts

    def verify_tunneled_4o4(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[IP]
                rx_ip = rx[IP]

                self.assertEqual(rx_ip.src, tunnel_src)
                self.assertEqual(rx_ip.dst, tunnel_dst)

                rx_gre = rx[GRE]
                rx_ip = rx_gre[IP]

                self.assertEqual(rx_ip.src, tx_ip.src)
                self.assertEqual(rx_ip.dst, tx_ip.dst)
                # IP processing post pop has decremented the TTL
                self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)

            except:
                self.logger.error(ppp("Rx:", rx))
                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(scapy.compat.raw(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_4o6(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]

                rx_ip = rx[IPv6]

                self.assertEqual(rx_ip.src, tunnel_src)
                self.assertEqual(rx_ip.dst, tunnel_dst)

                rx_gre = GRE(scapy.compat.raw(rx_ip[IPv6].payload))
                tx_ip = tx[IP]
                rx_ip = rx_gre[IP]

                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_6o4(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]

                rx_ip = rx[IP]

                self.assertEqual(rx_ip.src, tunnel_src)
                self.assertEqual(rx_ip.dst, tunnel_dst)

                rx_gre = GRE(scapy.compat.raw(rx_ip[IP].payload))
                rx_ip = rx_gre[IPv6]
                tx_ip = tx[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))

        for i in range(len(capture)):
            try:
                tx = sent[i]
                rx = capture[i]

                tx_ip = tx[IP]
                rx_ip = rx[IP]

                self.assertEqual(rx_ip.src, tunnel_src)
                self.assertEqual(rx_ip.dst, tunnel_dst)

                rx_gre = rx[GRE]
                rx_l2 = rx_gre[Ether]
                rx_ip = rx_l2[IP]
                tx_gre = tx[GRE]
                tx_l2 = tx_gre[Ether]
                tx_ip = tx_l2[IP]

                self.assertEqual(rx_ip.src, tx_ip.src)
                self.assertEqual(rx_ip.dst, tx_ip.dst)
                # bridged, not L3 forwarded, so no TTL decrement
                self.assertEqual(rx_ip.ttl, tx_ip.ttl)

            except:
                self.logger.error(ppp("Rx:", rx))
                self.logger.error(ppp("Tx:", tx))
                raise

    def verify_tunneled_vlano4(self, src_if, capture, sent,
                               tunnel_src, tunnel_dst, vlan):
        try:
            self.assertEqual(len(capture), len(sent))
        except:
            ppc("Unexpected packets captured:", capture)
            raise

        for i in range(len(capture)):
            try:
                tx = sent[i]
                rx = capture[i]

                tx_ip = tx[IP]
                rx_ip = rx[IP]

                self.assertEqual(rx_ip.src, tunnel_src)
                self.assertEqual(rx_ip.dst, tunnel_dst)

                rx_gre = rx[GRE]
                rx_l2 = rx_gre[Ether]
                rx_vlan = rx_l2[Dot1Q]
                rx_ip = rx_l2[IP]

                self.assertEqual(rx_vlan.vlan, vlan)

                tx_gre = tx[GRE]
                tx_l2 = tx_gre[Ether]
                tx_ip = tx_l2[IP]

                self.assertEqual(rx_ip.src, tx_ip.src)
                self.assertEqual(rx_ip.dst, tx_ip.dst)
                # bridged, not L3 forwarded, so no TTL decrement
                self.assertEqual(rx_ip.ttl, tx_ip.ttl)

            except:
                self.logger.error(ppp("Rx:", rx))
                self.logger.error(ppp("Tx:", tx))
                raise

    def verify_decapped_4o4(self, src_if, capture, sent):
        self.assertEqual(len(capture), len(sent))

        for i in range(len(capture)):
            try:
                tx = sent[i]
                rx = capture[i]

                tx_ip = tx[IP]
                rx_ip = rx[IP]
                tx_gre = tx[GRE]
                tx_ip = tx_gre[IP]

                self.assertEqual(rx_ip.src, tx_ip.src)
                self.assertEqual(rx_ip.dst, tx_ip.dst)
                # IP processing post pop has decremented the TTL
                self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)

            except:
                self.logger.error(ppp("Rx:", rx))
                self.logger.error(ppp("Tx:", tx))
                raise

    def verify_decapped_6o4(self, src_if, capture, sent):
        self.assertEqual(len(capture), len(sent))

        for i in range(len(capture)):
            try:
                tx = sent[i]
                rx = capture[i]

                tx_ip = tx[IP]
                rx_ip = rx[IPv6]
                tx_gre = tx[GRE]
                tx_ip = tx_gre[IPv6]

                self.assertEqual(rx_ip.src, tx_ip.src)
                self.assertEqual(rx_ip.dst, tx_ip.dst)
                self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)

            except:
                self.logger.error(ppp("Rx:", rx))
                self.logger.error(ppp("Tx:", tx))
                raise

    def test_gre(self):
        """ GRE IPv4 tunnel Tests """

        #
        # Create an L3 GRE tunnel.
        #  - set it admin up
        #  - assign an IP Addres
        #  - Add a route via the tunnel
        #
        gre_if = VppGreInterface(self,
                                 self.pg0.local_ip4,
                                 "1.1.1.2")
        gre_if.add_vpp_config()

        #
        # The double create (create the same tunnel twice) should fail,
        # and we should still be able to use the original
        #
        try:
            gre_if.add_vpp_config()
        except Exception:
            pass
        else:
            self.fail("Double GRE tunnel add does not fail")

        gre_if.admin_up()
        gre_if.config_ip4()

        route_via_tun = VppIpRoute(self, "4.4.4.4", 32,
                                   [VppRoutePath("0.0.0.0",
                                                 gre_if.sw_if_index)])

        route_via_tun.add_vpp_config()

        #
        # Send a packet stream that is routed into the tunnel
        #  - they are all dropped since the tunnel's destintation IP
        #    is unresolved - or resolves via the default route - which
        #    which is a drop.
        #
        tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "4.4.4.4")

        self.send_and_assert_no_replies(self.pg0, tx)

        #
        # Add a route that resolves the tunnel's destination
        #
        route_tun_dst = VppIpRoute(self, "1.1.1.2", 32,
                                   [VppRoutePath(self.pg0.remote_ip4,
                                                 self.pg0.sw_if_index)])
        route_tun_dst.add_vpp_config()

        #
        # Send a packet stream that is routed into the tunnel
        #  - packets are GRE encapped
        #
        tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "4.4.4.4")
        rx = self.send_and_expect(self.pg0, tx, self.pg0)
        self.verify_tunneled_4o4(self.pg0, rx, tx,
                                 self.pg0.local_ip4, "1.1.1.2")

        #
        # Send tunneled packets that match the created tunnel and
        # are decapped and forwarded
        #
        tx = self.create_tunnel_stream_4o4(self.pg0,
                                           "1.1.1.2",
                                           self.pg0.local_ip4,
                                           self.pg0.local_ip4,
                                           self.pg0.remote_ip4)
        rx = self.send_and_expect(self.pg0, tx, self.pg0)
        self.verify_decapped_4o4(self.pg0, rx, tx)

        #
        # Send tunneled packets that do not match the tunnel's src
        #
        self.vapi.cli("clear trace")
        tx = self.create_tunnel_stream_4o4(self.pg0,
                                           "1.1.1.3",
                                           self.pg0.local_ip4,
                                           self.pg0.local_ip4,
                                           self.pg0.remote_ip4)
        self.send_and_assert_no_replies(
            self.pg0, tx,
            remark="GRE packets forwarded despite no SRC address match")

        #
        # Configure IPv6 on the PG interface so we can route IPv6
        # packets
        #
        self.pg0.config_ip6()
        self.pg0.resolve_ndp()

        #
        # Send IPv6 tunnel encapslated packets
        #  - dropped since IPv6 is not enabled on the tunnel
        #
        tx = self.create_tunnel_stream_6o4(self.pg0,
                                           "1.1.1.2",
                                           self.pg0.local_ip4,
                                           self.pg0.local_ip6,
                                           self.pg0.remote_ip6)
        self.send_and_assert_no_replies(self.pg0, tx,
                                        "IPv6 GRE packets forwarded "
                                        "despite IPv6 not enabled on tunnel")

        #
        # Enable IPv6 on the tunnel
        #
        gre_if.config_ip6()

        #
        # Send IPv6 tunnel encapslated packets
        #  - forwarded since IPv6 is enabled on the tunnel
        #
        tx = self.create_tunnel_stream_6o4(self.pg0,
                                           "1.1.1.2",
                                           self.pg0.local_ip4,
                                           self.pg0.local_ip6,
                                           self.pg0.remote_ip6)
        rx = self.send_and_expect(self.pg0, tx, self.pg0)
        self.verify_decapped_6o4(self.pg0, rx, tx)

        #
        # Send v6 packets for v4 encap
        #
        route6_via_tun = VppIpRoute(
            self, "2001::1", 128,
            [VppRoutePath("::",
                          gre_if.sw_if_index,
                          proto=DpoProto.DPO_PROTO_IP6)])
        route6_via_tun.add_vpp_config()

        tx = self.create_stream_ip6(self.pg0, "2001::2", "2001::1")
        rx = self.send_and_expect(self.pg0, tx, self.pg0)

        self.verify_tunneled_6o4(self.pg0, rx, tx,
                                 self.pg0.local_ip4, "1.1.1.2")

        #
        # test case cleanup
        #
        route_tun_dst.remove_vpp_config()
        route_via_tun.remove_vpp_config()
        route6_via_tun.remove_vpp_config()
        gre_if.remove_vpp_config()

        self.pg0.unconfig_ip6()

    def test_gre6(self):
        """ GRE IPv6 tunnel Tests """

        self.pg1.config_ip6()
        self.pg1.resolve_ndp()

        #
        # Create an L3 GRE tunnel.
        #  - set it admin up
        #  - assign an IP Address
        #  - Add a route via the tunnel
        #
        gre_if = VppGreInterface(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)])

        route_via_tun.add_vpp_config()

        #
        # Send a packet stream that is routed into the tunnel
        #  - they are all dropped since the tunnel's destintation 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.send_and_assert_no_replies(
            self.pg2, tx,
            "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)])
        route_tun_dst.add_vpp_config()

        #
        # Send a packet stream that is routed into the tunnel
        #  - packets are GRE encapped
        #
        tx = self.create_stream_ip6(self.pg2, "5005::1", "4004::1")
        rx = self.send_and_expect(self.pg2, tx, self.pg2)
        self.verify_tunneled_6o6(self.pg2, rx, tx,
                                 self.pg2.local_ip6, "1002::1")

        #
        # Test decap. decapped packets go out pg1
        #
        tx = self.create_tunnel_stream_6o6(self.pg2,
                                           "1002::1",
                                           self.pg2.local_ip6,
                                           "2001::1",
                                           self.pg1.remote_ip6)
        rx = self.send_and_expect(self.pg2, tx, self.pg1)

        #
        # RX'd packet is UDP over IPv6, test the GRE header is gone.
        #
        self.assertFalse(rx[0].haslayer(GRE))
        self.assertEqual(rx[0][IPv6].dst, self.pg1.remote_ip6)

        #
        # Send v4 over v6
        #
        route4_via_tun = VppIpRoute(self, "1.1.1.1", 32,
                                    [VppRoutePath("0.0.0.0",
                                                  gre_if.sw_if_index)])
        route4_via_tun.add_vpp_config()

        tx = self.create_stream_ip4(self.pg0, "1.1.1.2", "1.1.1.1")
        rx = self.send_and_expect(self.pg0, tx, self.pg2)

        self.verify_tunneled_4o6(self.pg0, rx, tx,
                                 self.pg2.local_ip6, "1002::1")

        #
        # test case cleanup
        #
        route_tun_dst.remove_vpp_config()
        route_via_tun.remove_vpp_config()
        route4_via_tun.remove_vpp_config()
        gre_if.remove_vpp_config()

        self.pg2.unconfig_ip6()
        self.pg1.unconfig_ip6()

    def test_gre_vrf(self):
        """ GRE tunnel VRF Tests """

        #
        # Create an L3 GRE tunnel whose destination is in the non-default
        # table. The underlay is thus non-default - the overlay is still
        # the default.
        #  - set it admin up
        #  - assign an IP Addres
        #
        gre_if = VppGreInterface(self, self.pg1.local_ip4,
                                 "2.2.2.2",
                                 outer_fib_id=1)
        gre_if.add_vpp_config()
        gre_if.admin_up()
        gre_if.config_ip4()

        #
        # Add a route via the tunnel - in the overlay
        #
        route_via_tun = VppIpRoute(self, "9.9.9.9", 32,
                                   [VppRoutePath("0.0.0.0",
                                                 gre_if.sw_if_index)])
        route_via_tun.add_vpp_config()

        #
        # Add a route that resolves the tunnel's destination - in the
        # underlay table
        #
        route_tun_dst = VppIpRoute(self, "2.2.2.2", 32, table_id=1,
                                   paths=[VppRoutePath(self.pg1.remote_ip4,
                                                       self.pg1.sw_if_index)])
        route_tun_dst.add_vpp_config()

        #
        # Send a packet stream that is routed into the tunnel
        # packets are sent in on pg0 which is in the default table
        #  - packets are GRE encapped
        #
        self.vapi.cli("clear trace")
        tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "9.9.9.9")
        rx = self.send_and_expect(self.pg0, tx, self.pg1)
        self.verify_tunneled_4o4(self.pg1, rx, tx,
                                 self.pg1.local_ip4, "2.2.2.2")

        #
        # Send tunneled packets that match the created tunnel and
        # are decapped and forwarded. This tests the decap lookup
        # does not happen in the encap table
        #
        self.vapi.cli("clear trace")
        tx = self.create_tunnel_stream_4o4(self.pg1,
                                           "2.2.2.2",
                                           self.pg1.local_ip4,
                                           self.pg0.local_ip4,
                                           self.pg0.remote_ip4)
        rx = self.send_and_expect(self.pg1, tx, self.pg0)
        self.verify_decapped_4o4(self.pg0, rx, tx)

        #
        # Send tunneled packets that match the created tunnel
        # but arrive on an interface that is not in the tunnel's
        # encap VRF, these are dropped.
        # IP enable the interface so they aren't dropped due to
        # IP not being enabled.
        #
        self.pg2.config_ip4()
        self.vapi.cli("clear trace")
        tx = self.create_tunnel_stream_4o4(self.pg2,
                                           "2.2.2.2",
                                           self.pg1.local_ip4,
                                           self.pg0.local_ip4,
                                           self.pg0.remote_ip4)
        rx = self.send_and_assert_no_replies(
            self.pg2, tx,
            "GRE decap packets in wrong VRF")

        self.pg2.unconfig_ip4()

        #
        # test case cleanup
        #
        route_tun_dst.remove_vpp_config()
        route_via_tun.remove_vpp_config()
        gre_if.remove_vpp_config()

    def test_gre_l2(self):
        """ GRE tunnel L2 Tests """

        #
        # Add routes to resolve the tunnel destinations
        #
        route_tun1_dst = VppIpRoute(self, "2.2.2.2", 32,
                                    [VppRoutePath(self.pg0.remote_ip4,
                                                  self.pg0.sw_if_index)])
        route_tun2_dst = VppIpRoute(self, "2.2.2.3", 32,
                                    [VppRoutePath(self.pg0.remote_ip4,
                                                  self.pg0.sw_if_index)])

        route_tun1_dst.add_vpp_config()
        route_tun2_dst.add_vpp_config()

        #
        # Create 2 L2 GRE tunnels and x-connect them
        #
        gre_if1 = VppGreInterface(self, self.pg0.local_ip4,
                                  "2.2.2.2",
                                  type=(VppEnum.vl_api_gre_tunnel_type_t.
                                        GRE_API_TUNNEL_TYPE_TEB))
        gre_if2 = VppGreInterface(self, self.pg0.local_ip4,
                                  "2.2.2.3",
                                  type=(VppEnum.vl_api_gre_tunnel_type_t.
                                        GRE_API_TUNNEL_TYPE_TEB))
        gre_if1.add_vpp_config()
        gre_if2.add_vpp_config()

        gre_if1.admin_up()
        gre_if2.admin_up()

        self.vapi.sw_interface_set_l2_xconnect(gre_if1.sw_if_index,
                                               gre_if2.sw_if_index,
                                               enable=1)
        self.vapi.sw_interface_set_l2_xconnect(gre_if2.sw_if_index,
                                               gre_if1.sw_if_index,
                                               enable=1)

        #
        # Send in tunnel encapped L2. expect out tunnel encapped L2
        # in both directions
        #
        tx = self.create_tunnel_stream_l2o4(self.pg0,
                                            "2.2.2.2",
                                            self.pg0.local_ip4)
        rx = self.send_and_expect(self.pg0, tx, self.pg0)
        self.verify_tunneled_l2o4(self.pg0, rx, tx,
                                  self.pg0.local_ip4,
                                  "2.2.2.3")

        tx = self.create_tunnel_stream_l2o4(self.pg0,
                                            "2.2.2.3",
                                            self.pg0.local_ip4)
        rx = self.send_and_expect(self.pg0, tx, self.pg0)
        self.verify_tunneled_l2o4(self.pg0, rx, tx,
                                  self.pg0.local_ip4,
                                  "2.2.2.2")

        self.vapi.sw_interface_set_l2_xconnect(gre_if1.sw_if_index,
                                               gre_if2.sw_if_index,
                                               enable=0)
        self.vapi.sw_interface_set_l2_xconnect(gre_if2.sw_if_index,
                                               gre_if1.sw_if_index,
                                               enable=0)

        #
        # Create a VLAN sub-interfaces on the GRE TEB interfaces
        # then x-connect them
        #
        gre_if_11 = VppDot1QSubint(self, gre_if1, 11)
        gre_if_12 = VppDot1QSubint(self, gre_if2, 12)

        # gre_if_11.add_vpp_config()
        # gre_if_12.add_vpp_config()

        gre_if_11.admin_up()
        gre_if_12.admin_up()

        self.vapi.sw_interface_set_l2_xconnect(gre_if_11.sw_if_index,
                                               gre_if_12.sw_if_index,
                                               enable=1)
        self.vapi.sw_interface_set_l2_xconnect(gre_if_12.sw_if_index,
                                               gre_if_11.sw_if_index,
                                               enable=1)

        #
        # Configure both to pop thier respective VLAN tags,
        # so that during the x-coonect they will subsequently push
        #
        self.vapi.l2_interface_vlan_tag_rewrite(
            sw_if_index=gre_if_12.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1,
            push_dot1q=12)
        self.vapi.l2_interface_vlan_tag_rewrite(
            sw_if_index=gre_if_11.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1,
            push_dot1q=11)

        #
        # Send traffic in both directiond - expect the VLAN tags to
        # be swapped.
        #
        tx = self.create_tunnel_stream_vlano4(self.pg0,
                                              "2.2.2.2",
                                              self.pg0.local_ip4,
                                              11)
        rx = self.send_and_expect(self.pg0, tx, self.pg0)
        self.verify_tunneled_vlano4(self.pg0, rx, tx,
                                    self.pg0.local_ip4,
                                    "2.2.2.3",
                                    12)

        tx = self.create_tunnel_stream_vlano4(self.pg0,
                                              "2.2.2.3",
                                              self.pg0.local_ip4,
                                              12)
        rx = self.send_and_expect(self.pg0, tx, self.pg0)
        self.verify_tunneled_vlano4(self.pg0, rx, tx,
                                    self.pg0.local_ip4,
                                    "2.2.2.2",
                                    11)

        #
        # Cleanup Test resources
        #
        gre_if_11.remove_vpp_config()
        gre_if_12.remove_vpp_config()
        gre_if1.remove_vpp_config()
        gre_if2.remove_vpp_config()
        route_tun1_dst.add_vpp_config()
        route_tun2_dst.add_vpp_config()

    def test_gre_loop(self):
        """ GRE tunnel loop Tests """

        #
        # Create an L3 GRE tunnel.
        #  - set it admin up
        #  - assign an IP Addres
        #
        gre_if = VppGreInterface(self,
                                 self.pg0.local_ip4,
                                 "1.1.1.2")
        gre_if.add_vpp_config()
        gre_if.admin_up()
        gre_if.config_ip4()

        #
        # add a route to the tunnel's destination that points
        # through the tunnel, hence forming a loop in the forwarding
        # graph
        #
        route_dst = VppIpRoute(self, "1.1.1.2", 32,
                               [VppRoutePath("0.0.0.0",
                                             gre_if.sw_if_index)])
        route_dst.add_vpp_config()

        #
        # packets to the tunnels destination should be dropped
        #
        tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "1.1.1.2")
        self.send_and_assert_no_replies(self.pg2, tx)

        self.logger.info(self.vapi.ppcli("sh adj 7"))

        #
        # break the loop
        #
        route_dst.modify([VppRoutePath(self.pg1.remote_ip4,
                                       self.pg1.sw_if_index)])
        route_dst.add_vpp_config()

        rx = self.send_and_expect(self.pg0, tx, self.pg1)

        #
        # a good route throught the tunnel to check it restacked
        #
        route_via_tun_2 = VppIpRoute(self, "2.2.2.2", 32,
                                     [VppRoutePath("0.0.0.0",
                                                   gre_if.sw_if_index)])
        route_via_tun_2.add_vpp_config()

        tx = self.create_stream_ip4(self.pg0, "2.2.2.3", "2.2.2.2")
        rx = self.send_and_expect(self.pg0, tx, self.pg1)
        self.verify_tunneled_4o4(self.pg1, rx, tx,
                                 self.pg0.local_ip4, "1.1.1.2")

        #
        # cleanup
        #
        route_via_tun_2.remove_vpp_config()
        gre_if.remove_vpp_config()


if __name__ == '__main__':
    unittest.main(testRunner=VppTestRunner)