aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet
diff options
context:
space:
mode:
authorEyal Bari <ebari@cisco.com>2018-07-08 08:15:56 +0300
committerJohn Lo <loj@cisco.com>2018-07-09 21:08:54 +0000
commitdd47ecadcf63772a6037a1bb3715772d80e87f51 (patch)
tree32b9b47118a59b18be92a9d521be5d36425fa8a5 /src/vnet
parenta1ff01e5c91cadc3d95060aa1ca4b9faa6fa2f49 (diff)
vxlan:use bihash_16_8 for ipv4 lookup
Change-Id: I0d4630c88d6caacffcd073ebaa12766dfc893f70 Signed-off-by: Eyal Bari <ebari@cisco.com>
Diffstat (limited to 'src/vnet')
-rw-r--r--src/vnet/vxlan/decap.c49
-rw-r--r--src/vnet/vxlan/vxlan.c67
-rw-r--r--src/vnet/vxlan/vxlan.h26
3 files changed, 76 insertions, 66 deletions
diff --git a/src/vnet/vxlan/decap.c b/src/vnet/vxlan/decap.c
index 79d63613ec1..f91180128d1 100644
--- a/src/vnet/vxlan/decap.c
+++ b/src/vnet/vxlan/decap.c
@@ -62,11 +62,7 @@ buf_fib_index (vlib_buffer_t *b, u32 is_ip4)
return vec_elt (fib_index_by_sw_if_index, sw_if_index);
}
-typedef struct
-{
- vxlan4_tunnel_key_t key4;
- u32 tunnel_index;
-}last_tunnel_cache4;
+typedef vxlan4_tunnel_key_t last_tunnel_cache4;
always_inline vxlan_tunnel_t *
vxlan4_find_tunnel (vxlan_main_t * vxm, last_tunnel_cache4 * cache,
@@ -74,24 +70,22 @@ vxlan4_find_tunnel (vxlan_main_t * vxm, last_tunnel_cache4 * cache,
vxlan_tunnel_t ** stats_t0)
{
/* Make sure VXLAN tunnel exist according to packet SIP and VNI */
- vxlan4_tunnel_key_t key4_0 = {
- .src = ip4_0->src_address.as_u32,
- .vni = vxlan0->vni_reserved,
+ vxlan4_tunnel_key_t key4 = {
+ .key = {
+ [0] = ip4_0->src_address.as_u32,
+ [1] = (((u64) fib_index) << 32) | vxlan0->vni_reserved,
+ }
};
- if (PREDICT_FALSE (key4_0.as_u64 != cache->key4.as_u64))
+ if (PREDICT_FALSE (clib_bihash_key_compare_16_8 (key4.key, cache->key) == 0))
{
- uword * p = hash_get (vxm->vxlan4_tunnel_by_key, key4_0.as_u64);
- if (PREDICT_FALSE (p == 0))
+ int rv = clib_bihash_search_inline_16_8 (&vxm->vxlan4_tunnel_by_key, &key4);
+ if (PREDICT_FALSE (rv != 0))
return 0;
- cache->key4 = key4_0;
- cache->tunnel_index = p[0];
+ *cache = key4;
}
- vxlan_tunnel_t * t0 = pool_elt_at_index (vxm->tunnels, cache->tunnel_index);
-
- if (PREDICT_FALSE (fib_index != t0->encap_fib_index))
- return 0;
+ vxlan_tunnel_t * t0 = pool_elt_at_index (vxm->tunnels, cache->value);
/* Validate VXLAN tunnel SIP against packet DIP */
if (PREDICT_TRUE (ip4_0->dst_address.as_u32 == t0->src.ip4.as_u32))
@@ -102,12 +96,13 @@ vxlan4_find_tunnel (vxlan_main_t * vxm, last_tunnel_cache4 * cache,
if (PREDICT_TRUE (!ip4_address_is_multicast (&ip4_0->dst_address)))
return 0;
- key4_0.src = ip4_0->dst_address.as_u32;
+ key4.key[0] = ip4_0->dst_address.as_u32;
/* Make sure mcast VXLAN tunnel exist by packet DIP and VNI */
- uword * p = hash_get (vxm->vxlan4_tunnel_by_key, key4_0.as_u64);
- if (PREDICT_FALSE (p == NULL))
+ int rv = clib_bihash_search_inline_16_8 (&vxm->vxlan4_tunnel_by_key, &key4);
+ if (PREDICT_FALSE (rv != 0))
return 0;
- *stats_t0 = pool_elt_at_index (vxm->tunnels, p[0]);
+
+ *stats_t0 = pool_elt_at_index (vxm->tunnels, key4.value);
}
return t0;
@@ -126,13 +121,13 @@ vxlan6_find_tunnel (vxlan_main_t * vxm, last_tunnel_cache6 * cache,
.key = {
[0] = ip6_0->src_address.as_u64[0],
[1] = ip6_0->src_address.as_u64[1],
- [2] = (((u64) fib_index) << 32) | vxlan0->vni_reserved
+ [2] = (((u64) fib_index) << 32) | vxlan0->vni_reserved,
}
};
- if (PREDICT_FALSE (BV (clib_bihash_key_compare) (key6.key, cache->key) == 0))
+ if (PREDICT_FALSE (clib_bihash_key_compare_24_8 (key6.key, cache->key) == 0))
{
- int rv = BV (clib_bihash_search_inline) (&vxm->vxlan6_tunnel_by_key, &key6);
+ int rv = clib_bihash_search_inline_24_8 (&vxm->vxlan6_tunnel_by_key, &key6);
if (PREDICT_FALSE (rv != 0))
return 0;
@@ -152,7 +147,7 @@ vxlan6_find_tunnel (vxlan_main_t * vxm, last_tunnel_cache6 * cache,
/* Make sure mcast VXLAN tunnel exist by packet DIP and VNI */
key6.key[0] = ip6_0->dst_address.as_u64[0];
key6.key[1] = ip6_0->dst_address.as_u64[1];
- int rv = BV (clib_bihash_search_inline) (&vxm->vxlan6_tunnel_by_key, &key6);
+ int rv = clib_bihash_search_inline_24_8 (&vxm->vxlan6_tunnel_by_key, &key6);
if (PREDICT_FALSE (rv != 0))
return 0;
@@ -173,13 +168,13 @@ vxlan_input (vlib_main_t * vm,
vnet_interface_main_t * im = &vnm->interface_main;
vlib_combined_counter_main_t * rx_counter = im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX;
vlib_combined_counter_main_t * drop_counter = im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_DROP;
- last_tunnel_cache4 last4 = { .tunnel_index = ~0 };
+ last_tunnel_cache4 last4;
last_tunnel_cache6 last6;
u32 pkts_decapsulated = 0;
u32 thread_index = vlib_get_thread_index();
if (is_ip4)
- last4.key4.as_u64 = ~0;
+ memset (&last4, 0xff, sizeof last4);
else
memset (&last6, 0xff, sizeof last6);
diff --git a/src/vnet/vxlan/vxlan.c b/src/vnet/vxlan/vxlan.c
index d5938616912..e1ee3486b4b 100644
--- a/src/vnet/vxlan/vxlan.c
+++ b/src/vnet/vxlan/vxlan.c
@@ -369,17 +369,21 @@ int vnet_vxlan_add_del_tunnel
vxlan_main_t *vxm = &vxlan_main;
vxlan_tunnel_t *t = 0;
vnet_main_t *vnm = vxm->vnet_main;
- uword *p;
+ u64 *p;
u32 sw_if_index = ~0;
vxlan4_tunnel_key_t key4;
vxlan6_tunnel_key_t key6;
u32 is_ip6 = a->is_ip6;
+ int not_found;
if (!is_ip6)
{
- key4.src = a->dst.ip4.as_u32; /* decap src in key is encap dst in config */
- key4.vni = clib_host_to_net_u32 (a->vni << 8);
- p = hash_get (vxm->vxlan4_tunnel_by_key, key4.as_u64);
+ key4.key[0] = a->dst.ip4.as_u32;
+ key4.key[1] = (((u64) a->encap_fib_index) << 32)
+ | clib_host_to_net_u32 (a->vni << 8);
+ not_found =
+ clib_bihash_search_inline_16_8 (&vxm->vxlan4_tunnel_by_key, &key4);
+ p = &key4.value;
}
else
{
@@ -387,14 +391,14 @@ int vnet_vxlan_add_del_tunnel
key6.key[1] = a->dst.ip6.as_u64[1];
key6.key[2] = (((u64) a->encap_fib_index) << 32)
| clib_host_to_net_u32 (a->vni << 8);
- int rv =
- BV (clib_bihash_search_inline) (&vxm->vxlan6_tunnel_by_key, &key6);
- if (PREDICT_FALSE (rv != 0))
- p = 0;
- else
- p = &key6.value;
+ not_found =
+ clib_bihash_search_inline_24_8 (&vxm->vxlan6_tunnel_by_key, &key6);
+ p = &key6.value;
}
+ if (not_found)
+ p = 0;
+
if (a->is_add)
{
l2input_main_t *l2im = &l2input_main;
@@ -439,18 +443,25 @@ int vnet_vxlan_add_del_tunnel
t->flow_index = ~0;
/* copy the key */
+ int add_failed;
if (is_ip6)
{
key6.value = (u64) dev_instance;
- if (BV (clib_bihash_add_del) (&vxm->vxlan6_tunnel_by_key,
- &key6, 1 /*add */ ))
- {
- pool_put (vxm->tunnels, t);
- return VNET_API_ERROR_INVALID_REGISTRATION;
- }
+ add_failed = clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key,
+ &key6, 1 /*add */ );
}
else
- hash_set (vxm->vxlan4_tunnel_by_key, key4.as_u64, dev_instance);
+ {
+ key4.value = (u64) dev_instance;
+ add_failed = clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key,
+ &key4, 1 /*add */ );
+ }
+
+ if (add_failed)
+ {
+ pool_put (vxm->tunnels, t);
+ return VNET_API_ERROR_INVALID_REGISTRATION;
+ }
t->hw_if_index = vnet_register_interface
(vnm, vxlan_device_class.index, dev_instance,
@@ -587,9 +598,10 @@ int vnet_vxlan_add_del_tunnel
vxm->tunnel_index_by_sw_if_index[sw_if_index] = ~0;
if (!is_ip6)
- hash_unset (vxm->vxlan4_tunnel_by_key, key4.as_u64);
+ clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4,
+ 0 /*del */ );
else
- BV (clib_bihash_add_del) (&vxm->vxlan6_tunnel_by_key, &key6,
+ clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6,
0 /*del */ );
if (!ip46_address_is_multicast (&t->dst))
@@ -817,6 +829,8 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm,
* @cliexcmd{create vxlan tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 encap-vrf-id 7}
* Example of how to create a VXLAN Tunnel with a known name, vxlan_tunnel42:
* @cliexcmd{create vxlan tunnel src 10.0.3.1 dst 10.0.3.3 instance 42}
+ * Example of how to create a multicast VXLAN Tunnel with a known name, vxlan_tunnel23:
+ * @cliexcmd{create vxlan tunnel src 10.0.3.1 group 239.1.1.1 GigabitEtherner0/8/0 instance 23}
* Example of how to delete a VXLAN Tunnel:
* @cliexcmd{create vxlan tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 del}
?*/
@@ -872,9 +886,14 @@ show_vxlan_tunnel_command_fn (vlib_main_t * vm,
/* *INDENT-ON* */
if (raw)
- vlib_cli_output (vm, "Raw IPv6 Hash Table:\n%U\n",
- BV (format_bihash), &vxm->vxlan6_tunnel_by_key,
- 1 /* verbose */ );
+ {
+ vlib_cli_output (vm, "Raw IPv4 Hash Table:\n%U\n",
+ format_bihash_16_8, &vxm->vxlan4_tunnel_by_key,
+ 1 /* verbose */ );
+ vlib_cli_output (vm, "Raw IPv6 Hash Table:\n%U\n",
+ format_bihash_24_8, &vxm->vxlan6_tunnel_by_key,
+ 1 /* verbose */ );
+ }
return 0;
}
@@ -1198,7 +1217,9 @@ vxlan_init (vlib_main_t * vm)
&vxm->flow_id_start);
/* initialize the ip6 hash */
- BV (clib_bihash_init) (&vxm->vxlan6_tunnel_by_key, "vxlan6",
+ clib_bihash_init_16_8 (&vxm->vxlan4_tunnel_by_key, "vxlan4",
+ VXLAN_HASH_NUM_BUCKETS, VXLAN_HASH_MEMORY_SIZE);
+ clib_bihash_init_24_8 (&vxm->vxlan6_tunnel_by_key, "vxlan6",
VXLAN_HASH_NUM_BUCKETS, VXLAN_HASH_MEMORY_SIZE);
vxm->vtep6 = hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword));
vxm->mcast_shared = hash_create_mem (0,
diff --git a/src/vnet/vxlan/vxlan.h b/src/vnet/vxlan/vxlan.h
index 21b7650f454..ce410d7df67 100644
--- a/src/vnet/vxlan/vxlan.h
+++ b/src/vnet/vxlan/vxlan.h
@@ -17,6 +17,7 @@
#include <vppinfra/error.h>
#include <vppinfra/hash.h>
+#include <vppinfra/bihash_16_8.h>
#include <vppinfra/bihash_24_8.h>
#include <vnet/vnet.h>
#include <vnet/ip/ip.h>
@@ -43,27 +44,20 @@ typedef CLIB_PACKED (struct {
udp_header_t udp; /* 8 bytes */
vxlan_header_t vxlan; /* 8 bytes */
}) ip6_vxlan_header_t;
+/* *INDENT-ON* */
-typedef CLIB_PACKED (union {
- /*
- * Key fields: remote ip, vni on incoming VXLAN packet
- * all fields in NET byte order
- */
- struct
- {
- u32 src;
- u32 vni; /* shifted left 8 bits */
- };
- u64 as_u64;
-}) vxlan4_tunnel_key_t;
+/*
+* Key fields: remote ip, vni on incoming VXLAN packet
+* all fields in NET byte order
+*/
+typedef clib_bihash_kv_16_8_t vxlan4_tunnel_key_t;
/*
* Key fields: remote ip, vni and fib index on incoming VXLAN packet
* ip, vni fields in NET byte order
* fib index field in host byte order
*/
-typedef BVT (clib_bihash_kv) vxlan6_tunnel_key_t;
-/* *INDENT-ON* */
+typedef clib_bihash_kv_24_8_t vxlan6_tunnel_key_t;
typedef struct
{
@@ -146,8 +140,8 @@ typedef struct
vxlan_tunnel_t *tunnels;
/* lookup tunnel by key */
- uword *vxlan4_tunnel_by_key; /* keyed on ipv4.dst + vni */
- BVT (clib_bihash) vxlan6_tunnel_by_key; /* keyed on ipv6.dst + fib + vni */
+ clib_bihash_16_8_t vxlan4_tunnel_by_key; /* keyed on ipv4.dst + fib + vni */
+ clib_bihash_24_8_t vxlan6_tunnel_by_key; /* keyed on ipv6.dst + fib + vni */
/* local VTEP IPs ref count used by vxlan-bypass node to check if
received VXLAN packet DIP matches any local VTEP address */
#75715e } /* Generic.Subheading */ .highlight .kc { color: #66d9ef } /* Keyword.Constant */ .highlight .kd { color: #66d9ef } /* Keyword.Declaration */ .highlight .kn { color: #f92672 } /* Keyword.Namespace */ .highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ .highlight .kr { color: #66d9ef } /* Keyword.Reserved */ .highlight .kt { color: #66d9ef } /* Keyword.Type */ .highlight .ld { color: #e6db74 } /* Literal.Date */ .highlight .m { color: #ae81ff } /* Literal.Number */ .highlight .s { color: #e6db74 } /* Literal.String */ .highlight .na { color: #a6e22e } /* Name.Attribute */ .highlight .nb { color: #f8f8f2 } /* Name.Builtin */ .highlight .nc { color: #a6e22e } /* Name.Class */ .highlight .no { color: #66d9ef } /* Name.Constant */ .highlight .nd { color: #a6e22e } /* Name.Decorator */ .highlight .ni { color: #f8f8f2 } /* Name.Entity */ .highlight .ne { color: #a6e22e } /* Name.Exception */ .highlight .nf { color: #a6e22e } /* Name.Function */ .highlight .nl { color: #f8f8f2 } /* Name.Label */ .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ .highlight .nx { color: #a6e22e } /* Name.Other */ .highlight .py { color: #f8f8f2 } /* Name.Property */ .highlight .nt { color: #f92672 } /* Name.Tag */ .highlight .nv { color: #f8f8f2 } /* Name.Variable */ .highlight .ow { color: #f92672 } /* Operator.Word */ .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ .highlight .mf { color: #ae81ff } /* Literal.Number.Float */ .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ .highlight .sa { color: #e6db74 } /* Literal.String.Affix */ .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ .highlight .sc { color: #e6db74 } /* Literal.String.Char */ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ .highlight .se { color: #ae81ff } /* Literal.String.Escape */ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ .highlight .sx { color: #e6db74 } /* Literal.String.Other */ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ } @media (prefers-color-scheme: light) { .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
#!/usr/bin/env python3

import ipaddress
import unittest

from framework import VppTestCase, VppTestRunner
from vpp_ip import DpoProto
from vpp_ip_route import VppIpRoute, VppRoutePath
from util import fragment_rfc791, fragment_rfc8200

import scapy.compat
from scapy.layers.l2 import Ether
from scapy.packet import Raw
from scapy.layers.inet import IP, UDP, ICMP, TCP, IPerror, UDPerror
from scapy.layers.inet6 import IPv6, ICMPv6TimeExceeded, ICMPv6PacketTooBig
from scapy.layers.inet6 import ICMPv6EchoRequest, ICMPv6EchoReply, IPerror6


class TestMAPBR(VppTestCase):
    """MAP-T Test Cases"""

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

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

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

        #
        # Create 2 pg interfaces.
        # pg0 is IPv4
        # pg1 is IPv6
        #
        self.create_pg_interfaces(range(2))

        self.pg0.admin_up()
        self.pg0.config_ip4()
        self.pg1.generate_remote_hosts(20)
        self.pg1.configure_ipv4_neighbors()
        self.pg0.resolve_arp()

        self.pg1.admin_up()
        self.pg1.config_ip6()
        self.pg1.generate_remote_hosts(20)
        self.pg1.configure_ipv6_neighbors()

        #
        # BR configuration parameters used for all test.
        #
        self.ip4_prefix = "198.18.0.0/24"
        self.ip6_prefix = "2001:db8:f0::/48"
        self.ip6_src = "2001:db8:ffff:ff00::/64"
        self.ea_bits_len = 12
        self.psid_offset = 6
        self.psid_length = 4
        self.mtu = 1500
        self.tag = "MAP-T BR"

        self.ipv4_internet_address = self.pg0.remote_ip4
        self.ipv4_map_address = "198.18.0.12"
        self.ipv4_udp_or_tcp_internet_port = 65000
        self.ipv4_udp_or_tcp_map_port = 16606

        self.ipv6_cpe_address = "2001:db8:f0:c30:0:c612:c:3"  # 198.18.0.12
        self.ipv6_spoof_address = "2001:db8:f0:c30:0:c612:1c:3"  # 198.18.0.28
        self.ipv6_spoof_prefix = "2001:db8:f0:c30:0:a00:c:3"  # 10.0.0.12
        self.ipv6_spoof_psid = "2001:db8:f0:c30:0:c612:c:4"  # 4
        self.ipv6_spoof_subnet = "2001:db8:f1:c30:0:c612:c:3"  # f1

        self.ipv6_udp_or_tcp_internet_port = 65000
        self.ipv6_udp_or_tcp_map_port = 16606
        self.ipv6_udp_or_tcp_spoof_port = 16862

        self.ipv6_map_address = "2001:db8:ffff:ff00:ac:1001:200:0"  # 176.16.1.2
        self.ipv6_map_same_rule_diff_addr = (
            "2001:db8:ffff:ff00:c6:1200:1000:0"  # 198.18.0.16
        )
        self.ipv6_map_same_rule_same_addr = (
            "2001:db8:ffff:ff00:c6:1200:c00:0"  # 198.18.0.12
        )

        self.map_br_prefix = "2001:db8:f0::"
        self.map_br_prefix_len = 48
        self.psid_number = 3

        #
        # Add an IPv6 route to the MAP-BR.
        #
        map_route = VppIpRoute(
            self,
            self.map_br_prefix,
            self.map_br_prefix_len,
            [VppRoutePath(self.pg1.remote_ip6, self.pg1.sw_if_index)],
        )
        map_route.add_vpp_config()

        #
        # Add a MAP BR domain that maps from pg0 to pg1.
        #
        self.vapi.map_add_domain(
            ip4_prefix=self.ip4_prefix,
            ip6_prefix=self.ip6_prefix,
            ip6_src=self.ip6_src,
            ea_bits_len=self.ea_bits_len,
            psid_offset=self.psid_offset,
            psid_length=self.psid_length,
            mtu=self.mtu,
            tag=self.tag,
        )

        #
        # Set BR parameters.
        #
        self.vapi.map_param_set_fragmentation(inner=1, ignore_df=0)
        self.vapi.map_param_set_fragmentation(inner=0, ignore_df=0)
        self.vapi.map_param_set_icmp(ip4_err_relay_src=self.pg0.local_ip4)
        self.vapi.map_param_set_traffic_class(copy=1)

        #
        # Enable MAP-T on interfaces.
        #
        self.vapi.map_if_enable_disable(
            is_enable=1, sw_if_index=self.pg0.sw_if_index, is_translation=1
        )

        self.vapi.map_if_enable_disable(
            is_enable=1, sw_if_index=self.pg1.sw_if_index, is_translation=1
        )

        self.vapi.map_if_enable_disable(
            is_enable=1, sw_if_index=self.pg1.sw_if_index, is_translation=1
        )

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

    def v4_address_check(self, pkt):
        self.assertEqual(pkt[IP].src, self.ipv4_map_address)
        self.assertEqual(pkt[IP].dst, self.ipv4_internet_address)

    def v4_port_check(self, pkt, proto):
        self.assertEqual(pkt[proto].sport, self.ipv4_udp_or_tcp_map_port)
        self.assertEqual(pkt[proto].dport, self.ipv4_udp_or_tcp_internet_port)

    def v6_address_check(self, pkt):
        self.assertEqual(pkt[IPv6].src, self.ipv6_map_address)
        self.assertEqual(pkt[IPv6].dst, self.ipv6_cpe_address)

    def v6_port_check(self, pkt, proto):
        self.assertEqual(pkt[proto].sport, self.ipv6_udp_or_tcp_internet_port)
        self.assertEqual(pkt[proto].dport, self.ipv6_udp_or_tcp_map_port)

    #
    # Normal translation of UDP packets v4 -> v6 direction
    # Send 128 frame size packet for IPv4/UDP.
    # Received packet should be translated into IPv6 packet with no
    # fragment header.
    #

    def test_map_t_udp_ip4_to_ip6(self):
        """MAP-T UDP IPv4 -> IPv6"""

        eth = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
        ip = IP(src=self.pg0.remote_ip4, dst=self.ipv4_map_address, tos=0)
        udp = UDP(
            sport=self.ipv4_udp_or_tcp_internet_port,
            dport=self.ipv4_udp_or_tcp_map_port,
        )
        payload = "a" * 82
        tx_pkt = eth / ip / udp / payload

        self.pg_send(self.pg0, tx_pkt * 1)

        rx_pkts = self.pg1.get_capture(1)
        rx_pkt = rx_pkts[0]

        self.v6_address_check(rx_pkt)
        self.v6_port_check(rx_pkt, UDP)
        self.assertEqual(rx_pkt[IPv6].tc, 0)  # IPv4 ToS passed to v6 TC
        self.assertEqual(rx_pkt[IPv6].nh, IPv6(nh="UDP").nh)

    #
    # Normal translation of TCP packets v4 -> v6 direction.
    # Send 128 frame size packet for IPv4/TCP.
    # Received packet should be translated into IPv6 packet with no
    # fragment header.
    #

    def test_map_t_tcp_ip4_to_ip6(self):
        """MAP-T TCP IPv4 -> IPv6"""

        eth = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
        ip = IP(src=self.pg0.remote_ip4, dst=self.ipv4_map_address, tos=0)
        tcp = TCP(
            sport=self.ipv4_udp_or_tcp_internet_port,
            dport=self.ipv4_udp_or_tcp_map_port,
        )
        payload = "a" * 82
        tx_pkt = eth / ip / tcp / payload

        self.pg_send(self.pg0, tx_pkt * 1)

        rx_pkts = self.pg1.get_capture(1)
        rx_pkt = rx_pkts[0]

        self.v6_address_check(rx_pkt)
        self.v6_port_check(rx_pkt, TCP)
        self.assertEqual(rx_pkt[IPv6].tc, 0)  # IPv4 ToS passed to v6 TC
        self.assertEqual(rx_pkt[IPv6].nh, IPv6(nh="TCP").nh)

    #
    # Normal translation of UDP packets v6 -> v4 direction
    # Send 128 frame size packet for IPv6/UDP.
    # Received packet should be translated into an IPv4 packet with DF=1.
    #

    def test_map_t_udp_ip6_to_ip4(self):
        """MAP-T UDP IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_cpe_address, dst=self.ipv6_map_address)
        udp = UDP(
            sport=self.ipv6_udp_or_tcp_map_port,
            dport=self.ipv6_udp_or_tcp_internet_port,
        )
        payload = "a" * 82
        tx_pkt = eth / ip / udp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        rx_pkts = self.pg0.get_capture(1)
        rx_pkt = rx_pkts[0]

        self.v4_address_check(rx_pkt)
        self.v4_port_check(rx_pkt, UDP)
        self.assertEqual(rx_pkt[IP].proto, IP(proto="udp").proto)
        self.assertEqual(rx_pkt[IP].tos, 0)  # IPv6 TC passed to v4 ToS
        df_bit = IP(flags="DF").flags
        self.assertNotEqual(rx_pkt[IP].flags & df_bit, df_bit)

    #
    # Normal translation of TCP packets v6 -> v4 direction
    # Send 128 frame size packet for IPv6/TCP.
    # Received packet should be translated into an IPv4 packet with DF=1
    #

    def test_map_t_tcp_ip6_to_ip4(self):
        """MAP-T TCP IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_cpe_address, dst=self.ipv6_map_address)
        tcp = TCP(
            sport=self.ipv6_udp_or_tcp_map_port,
            dport=self.ipv6_udp_or_tcp_internet_port,
        )
        payload = "a" * 82
        tx_pkt = eth / ip / tcp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        rx_pkts = self.pg0.get_capture(1)
        rx_pkt = rx_pkts[0]

        self.v4_address_check(rx_pkt)
        self.v4_port_check(rx_pkt, TCP)
        self.assertEqual(rx_pkt[IP].proto, IP(proto="tcp").proto)
        self.assertEqual(rx_pkt[IP].tos, 0)  # IPv6 TC passed to v4 ToS
        df_bit = IP(flags="DF").flags
        self.assertNotEqual(rx_pkt[IP].flags & df_bit, df_bit)

    #
    # Translation of ICMP Echo Request v4 -> v6 direction
    # Received packet should be translated into an IPv6 Echo Request.
    #

    def test_map_t_echo_request_ip4_to_ip6(self):
        """MAP-T echo request IPv4 -> IPv6"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IP(src=self.pg0.remote_ip4, dst=self.ipv4_map_address)
        icmp = ICMP(type="echo-request", id=self.ipv6_udp_or_tcp_map_port)
        payload = "H" * 10
        tx_pkt = eth / ip / icmp / payload

        self.pg_send(self.pg0, tx_pkt * 1)

        rx_pkts = self.pg1.get_capture(1)
        rx_pkt = rx_pkts[0]

        self.assertEqual(rx_pkt[IPv6].nh, IPv6(nh="ICMPv6").nh)
        self.assertEqual(
            rx_pkt[ICMPv6EchoRequest].type, ICMPv6EchoRequest(type="Echo Request").type
        )
        self.assertEqual(rx_pkt[ICMPv6EchoRequest].code, 0)
        self.assertEqual(rx_pkt[ICMPv6EchoRequest].id, self.ipv6_udp_or_tcp_map_port)

    #
    # Translation of ICMP Echo Reply v4 -> v6 direction
    # Received packet should be translated into an IPv6 Echo Reply.
    #

    def test_map_t_echo_reply_ip4_to_ip6(self):
        """MAP-T echo reply IPv4 -> IPv6"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IP(src=self.pg0.remote_ip4, dst=self.ipv4_map_address)
        icmp = ICMP(type="echo-reply", id=self.ipv6_udp_or_tcp_map_port)
        payload = "H" * 10
        tx_pkt = eth / ip / icmp / payload

        self.pg_send(self.pg0, tx_pkt * 1)

        rx_pkts = self.pg1.get_capture(1)
        rx_pkt = rx_pkts[0]

        self.assertEqual(rx_pkt[IPv6].nh, IPv6(nh="ICMPv6").nh)
        self.assertEqual(
            rx_pkt[ICMPv6EchoReply].type, ICMPv6EchoReply(type="Echo Reply").type
        )
        self.assertEqual(rx_pkt[ICMPv6EchoReply].code, 0)
        self.assertEqual(rx_pkt[ICMPv6EchoReply].id, self.ipv6_udp_or_tcp_map_port)

    #
    # Translation of ICMP Time Exceeded v4 -> v6 direction
    # Received packet should be translated into an IPv6 Time Exceeded.
    #

    def test_map_t_time_exceeded_ip4_to_ip6(self):
        """MAP-T time exceeded IPv4 -> IPv6"""

        eth = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
        ip = IP(src=self.pg0.remote_ip4, dst=self.ipv4_map_address)
        icmp = ICMP(type="time-exceeded", code="ttl-zero-during-transit")
        ip_inner = IP(dst=self.pg0.remote_ip4, src=self.ipv4_map_address, ttl=1)
        udp_inner = UDP(
            sport=self.ipv4_udp_or_tcp_map_port,
            dport=self.ipv4_udp_or_tcp_internet_port,
        )
        payload = "H" * 10
        tx_pkt = eth / ip / icmp / ip_inner / udp_inner / payload

        self.pg_send(self.pg0, tx_pkt * 1)

        rx_pkts = self.pg1.get_capture(1)
        rx_pkt = rx_pkts[0]

        self.v6_address_check(rx_pkt)
        self.assertEqual(rx_pkt[IPv6].nh, IPv6(nh="ICMPv6").nh)
        self.assertEqual(rx_pkt[ICMPv6TimeExceeded].type, ICMPv6TimeExceeded().type)
        self.assertEqual(
            rx_pkt[ICMPv6TimeExceeded].code,
            ICMPv6TimeExceeded(code="hop limit exceeded in transit").code,
        )
        self.assertEqual(rx_pkt[ICMPv6TimeExceeded].hlim, tx_pkt[IP][1].ttl)
        self.assertTrue(rx_pkt.haslayer(IPerror6))
        self.assertTrue(rx_pkt.haslayer(UDPerror))
        self.assertEqual(rx_pkt[IPv6].src, rx_pkt[IPerror6].dst)
        self.assertEqual(rx_pkt[IPv6].dst, rx_pkt[IPerror6].src)
        self.assertEqual(rx_pkt[UDPerror].sport, self.ipv6_udp_or_tcp_map_port)
        self.assertEqual(rx_pkt[UDPerror].dport, self.ipv6_udp_or_tcp_internet_port)

    #
    # Translation of ICMP Echo Request v6 -> v4 direction
    # Received packet should be translated into an IPv4 Echo Request.
    #

    def test_map_t_echo_request_ip6_to_ip4(self):
        """MAP-T echo request IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_cpe_address, dst=self.ipv6_map_address)
        icmp = ICMPv6EchoRequest()
        icmp.id = self.ipv6_udp_or_tcp_map_port
        payload = "H" * 10
        tx_pkt = eth / ip / icmp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        rx_pkts = self.pg0.get_capture(1)
        rx_pkt = rx_pkts[0]

        self.assertEqual(rx_pkt[IP].proto, IP(proto="icmp").proto)
        self.assertEqual(rx_pkt[ICMP].type, ICMP(type="echo-request").type)
        self.assertEqual(rx_pkt[ICMP].code, 0)
        self.assertEqual(rx_pkt[ICMP].id, self.ipv6_udp_or_tcp_map_port)

    #
    # Translation of ICMP Echo Reply v6 -> v4 direction
    # Received packet should be translated into an IPv4 Echo Reply.
    #

    def test_map_t_echo_reply_ip6_to_ip4(self):
        """MAP-T echo reply IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_cpe_address, dst=self.ipv6_map_address)
        icmp = ICMPv6EchoReply(id=self.ipv6_udp_or_tcp_map_port)
        payload = "H" * 10
        tx_pkt = eth / ip / icmp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        rx_pkts = self.pg0.get_capture(1)
        rx_pkt = rx_pkts[0]

        self.assertEqual(rx_pkt[IP].proto, IP(proto="icmp").proto)
        self.assertEqual(rx_pkt[ICMP].type, ICMP(type="echo-reply").type)
        self.assertEqual(rx_pkt[ICMP].code, 0)
        self.assertEqual(rx_pkt[ICMP].id, self.ipv6_udp_or_tcp_map_port)

    #
    # Translation of ICMP Packet Too Big v6 -> v4 direction
    # Received packet should be translated into an IPv4 Dest Unreachable.
    #

    def test_map_t_packet_too_big_ip6_to_ip4(self):
        """MAP-T packet too big IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_cpe_address, dst=self.ipv6_map_address)
        icmp = ICMPv6PacketTooBig(mtu=1280)
        ip_inner = IPv6(src=self.ipv6_map_address, dst=self.ipv6_cpe_address)
        udp_inner = UDP(
            sport=self.ipv6_udp_or_tcp_internet_port,
            dport=self.ipv6_udp_or_tcp_map_port,
        )
        payload = "H" * 10
        tx_pkt = eth / ip / icmp / ip_inner / udp_inner / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        rx_pkts = self.pg0.get_capture(1)
        rx_pkt = rx_pkts[0]

        self.v4_address_check(rx_pkt)
        self.assertEqual(rx_pkt[IP].proto, IP(proto="icmp").proto)
        self.assertEqual(rx_pkt[ICMP].type, ICMP(type="dest-unreach").type)
        self.assertEqual(rx_pkt[ICMP].code, ICMP(code="fragmentation-needed").code)
        self.assertEqual(rx_pkt[ICMP].nexthopmtu, tx_pkt[ICMPv6PacketTooBig].mtu - 20)
        self.assertTrue(rx_pkt.haslayer(IPerror))
        self.assertTrue(rx_pkt.haslayer(UDPerror))
        self.assertEqual(rx_pkt[IP].src, rx_pkt[IPerror].dst)
        self.assertEqual(rx_pkt[IP].dst, rx_pkt[IPerror].src)
        self.assertEqual(rx_pkt[UDPerror].sport, self.ipv4_udp_or_tcp_internet_port)
        self.assertEqual(rx_pkt[UDPerror].dport, self.ipv4_udp_or_tcp_map_port)

    #
    # Translation of ICMP Time Exceeded v6 -> v4 direction
    # Received packet should be translated into an IPv4 Time Exceeded.
    #

    def test_map_t_time_exceeded_ip6_to_ip4(self):
        """MAP-T time exceeded IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_cpe_address, dst=self.ipv6_map_address)
        icmp = ICMPv6TimeExceeded()
        ip_inner = IPv6(src=self.ipv6_map_address, dst=self.ipv6_cpe_address, hlim=1)
        udp_inner = UDP(
            sport=self.ipv6_udp_or_tcp_internet_port,
            dport=self.ipv6_udp_or_tcp_map_port,
        )
        payload = "H" * 10
        tx_pkt = eth / ip / icmp / ip_inner / udp_inner / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        rx_pkts = self.pg0.get_capture(1)
        rx_pkt = rx_pkts[0]

        self.v4_address_check(rx_pkt)
        self.assertEqual(rx_pkt[IP].proto, IP(proto="icmp").proto)
        self.assertEqual(rx_pkt[ICMP].type, ICMP(type="time-exceeded").type)
        self.assertEqual(rx_pkt[ICMP].code, ICMP(code="ttl-zero-during-transit").code)
        self.assertEqual(rx_pkt[ICMP].ttl, tx_pkt[IPv6][1].hlim)
        self.assertTrue(rx_pkt.haslayer(IPerror))
        self.assertTrue(rx_pkt.haslayer(UDPerror))
        self.assertEqual(rx_pkt[IP].src, rx_pkt[IPerror].dst)
        self.assertEqual(rx_pkt[IP].dst, rx_pkt[IPerror].src)
        self.assertEqual(rx_pkt[UDPerror].sport, self.ipv4_udp_or_tcp_internet_port)
        self.assertEqual(rx_pkt[UDPerror].dport, self.ipv4_udp_or_tcp_map_port)

    #
    # Spoofed IPv4 Source Address v6 -> v4 direction
    # Send a packet with a wrong IPv4 address embedded in bits 72-103.
    # The BR should either drop the packet, or rewrite the spoofed
    # source IPv4 as the actual source IPv4 address.
    # The BR really should drop the packet.
    #

    def test_map_t_spoof_ipv4_src_addr_ip6_to_ip4(self):
        """MAP-T spoof ipv4 src addr IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_spoof_address, dst=self.ipv6_map_address)
        udp = UDP(
            sport=self.ipv6_udp_or_tcp_map_port,
            dport=self.ipv6_udp_or_tcp_internet_port,
        )
        payload = "a" * 82
        tx_pkt = eth / ip / udp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        self.pg0.get_capture(0, timeout=1)
        self.pg0.assert_nothing_captured(remark="Should drop IPv4 spoof address")

    #
    # Spoofed IPv4 Source Prefix v6 -> v4 direction
    # Send a packet with a wrong IPv4 prefix embedded in bits 72-103.
    # The BR should either drop the packet, or rewrite the source IPv4
    # to the prefix that matches the source IPv4 address.
    #

    def test_map_t_spoof_ipv4_src_prefix_ip6_to_ip4(self):
        """MAP-T spoof ipv4 src prefix IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_spoof_prefix, dst=self.ipv6_map_address)
        udp = UDP(
            sport=self.ipv6_udp_or_tcp_map_port,
            dport=self.ipv6_udp_or_tcp_internet_port,
        )
        payload = "a" * 82
        tx_pkt = eth / ip / udp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        self.pg0.get_capture(0, timeout=1)
        self.pg0.assert_nothing_captured(remark="Should drop IPv4 spoof prefix")

    #
    # Spoofed IPv6 PSID v6 -> v4 direction
    # Send a packet with a wrong IPv6 port PSID
    # The BR should drop the packet.
    #

    def test_map_t_spoof_psid_ip6_to_ip4(self):
        """MAP-T spoof psid IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_spoof_psid, dst=self.ipv6_map_address)
        udp = UDP(
            sport=self.ipv6_udp_or_tcp_map_port,
            dport=self.ipv6_udp_or_tcp_internet_port,
        )
        payload = "a" * 82
        tx_pkt = eth / ip / udp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        self.pg0.get_capture(0, timeout=1)
        self.pg0.assert_nothing_captured(remark="Should drop IPv6 spoof PSID")

    #
    # Spoofed IPv6 subnet field v6 -> v4 direction
    # Send a packet with a wrong IPv6 subnet as "2001:db8:f1"
    # The BR should drop the packet.
    #

    def test_map_t_spoof_subnet_ip6_to_ip4(self):
        """MAP-T spoof subnet IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_spoof_subnet, dst=self.ipv6_map_address)
        udp = UDP(
            sport=self.ipv6_udp_or_tcp_map_port,
            dport=self.ipv6_udp_or_tcp_internet_port,
        )
        payload = "a" * 82
        tx_pkt = eth / ip / udp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        self.pg0.get_capture(0, timeout=1)
        self.pg0.assert_nothing_captured(remark="Should drop IPv6 spoof subnet")

    #
    # Spoofed IPv6 port PSID v6 -> v4 direction
    # Send a packet with a wrong IPv6 port PSID
    # The BR should drop the packet.
    #

    def test_map_t_spoof_port_psid_ip6_to_ip4(self):
        """MAP-T spoof port psid IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_cpe_address, dst=self.ipv6_map_address)
        udp = UDP(
            sport=self.ipv6_udp_or_tcp_spoof_port,
            dport=self.ipv6_udp_or_tcp_internet_port,
        )
        payload = "a" * 82
        tx_pkt = eth / ip / udp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        self.pg0.get_capture(0, timeout=1)
        self.pg0.assert_nothing_captured(remark="Should drop IPv6 spoof port PSID")

    #
    # Spoofed IPv6 ICMP ID PSID v6 -> v4 direction
    # Send a packet with a wrong IPv6 IMCP ID PSID
    # The BR should drop the packet.
    #

    def test_map_t_spoof_icmp_id_psid_ip6_to_ip4(self):
        """MAP-T spoof ICMP id psid IPv6 -> IPv4"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_cpe_address, dst=self.ipv6_map_address)
        icmp = ICMPv6EchoRequest()
        icmp.id = self.ipv6_udp_or_tcp_spoof_port
        payload = "H" * 10
        tx_pkt = eth / ip / icmp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        self.pg0.get_capture(0, timeout=1)
        self.pg0.assert_nothing_captured(remark="Should drop IPv6 spoof port PSID")

    #
    # Map to Map - same rule, different address
    #

    @unittest.skip("Fixme: correct behavior needs clarification")
    def test_map_t_same_rule_diff_addr_ip6_to_ip4(self):
        """MAP-T same rule, diff addr IPv6 -> IPv6"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_cpe_address, dst=self.ipv6_map_same_rule_diff_addr)
        udp = UDP(sport=self.ipv6_udp_or_tcp_map_port, dport=1025)
        payload = "a" * 82
        tx_pkt = eth / ip / udp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        rx_pkts = self.pg1.get_capture(1)
        rx_pkt = rx_pkts[0]

    #
    # Map to Map - same rule, same address
    #

    @unittest.skip("Fixme: correct behavior needs clarification")
    def test_map_t_same_rule_same_addr_ip6_to_ip4(self):
        """MAP-T same rule, same addr IPv6 -> IPv6"""

        eth = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
        ip = IPv6(src=self.ipv6_cpe_address, dst=self.ipv6_map_same_rule_same_addr)
        udp = UDP(sport=self.ipv6_udp_or_tcp_map_port, dport=1025)
        payload = "a" * 82
        tx_pkt = eth / ip / udp / payload

        self.pg_send(self.pg1, tx_pkt * 1)

        rx_pkts = self.pg1.get_capture(1)
        rx_pkt = rx_pkts[0]


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