1154 lines
38 KiB
Python
1154 lines
38 KiB
Python
|
#!/usr/bin/env python3
|
||
|
""" PVTI tests """
|
||
|
|
||
|
import datetime
|
||
|
import base64
|
||
|
import os
|
||
|
import copy
|
||
|
import struct
|
||
|
|
||
|
from hashlib import blake2s
|
||
|
from config import config
|
||
|
from scapy.packet import Raw
|
||
|
from scapy.compat import raw
|
||
|
from scapy.layers.l2 import Ether
|
||
|
from scapy.layers.inet import IP, UDP
|
||
|
from scapy.layers.inet6 import IPv6
|
||
|
from scapy.layers.vxlan import VXLAN
|
||
|
|
||
|
from vpp_interface import VppInterface
|
||
|
from vpp_pg_interface import is_ipv6_misc
|
||
|
from vpp_ip_route import VppIpRoute, VppRoutePath
|
||
|
from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort
|
||
|
from vpp_vxlan_tunnel import VppVxlanTunnel
|
||
|
from vpp_object import VppObject
|
||
|
from vpp_papi import VppEnum
|
||
|
from asfframework import tag_run_solo, tag_fixme_vpp_debug
|
||
|
from framework import VppTestCase
|
||
|
from re import compile
|
||
|
import unittest
|
||
|
|
||
|
|
||
|
from scapy.packet import Packet, bind_layers
|
||
|
from scapy.layers.l2 import Ether
|
||
|
from scapy.layers.inet import IP, UDP
|
||
|
from scapy.layers.inet6 import IPv6
|
||
|
from scapy.fields import (
|
||
|
FlagsField,
|
||
|
XByteField,
|
||
|
XShortField,
|
||
|
ThreeBytesField,
|
||
|
ConditionalField,
|
||
|
ShortField,
|
||
|
ByteEnumField,
|
||
|
X3BytesField,
|
||
|
LEIntField,
|
||
|
ByteField,
|
||
|
StrLenField,
|
||
|
PacketListField,
|
||
|
LEShortField,
|
||
|
IntField,
|
||
|
ShortField,
|
||
|
XIntField,
|
||
|
)
|
||
|
|
||
|
import sys
|
||
|
|
||
|
|
||
|
def eprint(*args, **kwargs):
|
||
|
print(*args, file=sys.stderr, **kwargs)
|
||
|
|
||
|
|
||
|
#
|
||
|
# A custom decoder for Scapy for PVTI packet format
|
||
|
#
|
||
|
|
||
|
|
||
|
class PVTIChunk(Packet):
|
||
|
name = "PVTIChunk"
|
||
|
fields_desc = [
|
||
|
ShortField("total_chunk_length", None),
|
||
|
XShortField("_pad0", 0),
|
||
|
XIntField("_pad1", 0),
|
||
|
StrLenField("data", "", length_from=lambda pkt: pkt.total_chunk_length - 8),
|
||
|
]
|
||
|
|
||
|
# This prevents the first chunk from consuming the entire remaining
|
||
|
# contents of the packet
|
||
|
def extract_padding(self, s):
|
||
|
return "", s
|
||
|
|
||
|
def post_build(self, p, pay):
|
||
|
if self.total_chunk_length is None and self.data:
|
||
|
chunk_header_size = 8
|
||
|
l = chunk_header_size + len(self.data)
|
||
|
p = struct.pack("!H", l) + p[2:]
|
||
|
return p + pay
|
||
|
|
||
|
|
||
|
class PVTI(Packet):
|
||
|
name = "PVTI"
|
||
|
PVTI_ALIGN_BYTES = 9
|
||
|
fields_desc = [
|
||
|
IntField("seq", 0x0),
|
||
|
ByteField("stream_index", 0),
|
||
|
ByteField("chunk_count", None),
|
||
|
ByteField("reass_chunk_count", 0),
|
||
|
ByteField("mandatory_flags_mask", 0),
|
||
|
ByteField("flags_value", 0),
|
||
|
ByteField("pad_bytes", PVTI_ALIGN_BYTES),
|
||
|
StrLenField(
|
||
|
"pad", b"\xca" * PVTI_ALIGN_BYTES, length_from=lambda pkt: pkt.pad_bytes
|
||
|
),
|
||
|
PacketListField("chunks", [], PVTIChunk, count_from=lambda p: p.chunk_count),
|
||
|
]
|
||
|
|
||
|
def mysummary(self):
|
||
|
return self.sprintf("PVTI (len=%PVTI.total_len%)")
|
||
|
|
||
|
def post_build(self, p, pay):
|
||
|
if self.chunk_count is None:
|
||
|
l = len(self.chunks)
|
||
|
# offset of the chunk count within the fields
|
||
|
offset_of_chunk_count = 5
|
||
|
p = (
|
||
|
p[:offset_of_chunk_count]
|
||
|
+ struct.pack("b", l)
|
||
|
+ p[offset_of_chunk_count + 1 :]
|
||
|
)
|
||
|
return p + pay
|
||
|
|
||
|
|
||
|
bind_layers(UDP, PVTI, dport=12312)
|
||
|
# By default, set both ports to the test
|
||
|
# bind_layers(UDP, PVTI, sport=6192, dport=6192)
|
||
|
|
||
|
|
||
|
# PVTI ENcapsulator/DEcapsulator
|
||
|
class PvtiEnDe(object):
|
||
|
"""
|
||
|
PVTI encapsulator/decapsulator
|
||
|
"""
|
||
|
|
||
|
def __init__(
|
||
|
self,
|
||
|
local_ip,
|
||
|
local_port,
|
||
|
remote_ip,
|
||
|
remote_port,
|
||
|
underlay_mtu=1500,
|
||
|
for_rx_test=False,
|
||
|
):
|
||
|
self.for_rx_test = for_rx_test
|
||
|
self.local_ip = local_ip
|
||
|
self.local_port = local_port
|
||
|
self.remote_ip = remote_ip
|
||
|
self.remote_port = remote_port
|
||
|
self.underlay_mtu = underlay_mtu
|
||
|
self.stream_index = 0
|
||
|
self.tx_chunks = []
|
||
|
self.tx_n_reass_chunks = 0
|
||
|
self.tx_seq = 42
|
||
|
# payload = chunk headers + data
|
||
|
self.max_payload_len = underlay_mtu - len(raw(IP() / UDP() / PVTI()))
|
||
|
self.pvti_header_len = len(raw(PVTI()))
|
||
|
self.chunk_header_len = len(raw(PVTIChunk()))
|
||
|
|
||
|
def get_curr_payload_len(self):
|
||
|
tx_len = 0
|
||
|
for c in self.tx_chunks:
|
||
|
tx_len = tx_len + len(c.data) + self.chunk_header_len
|
||
|
return tx_len
|
||
|
|
||
|
def get_payload_room(self):
|
||
|
return self.max_payload_len - self.get_curr_payload_len()
|
||
|
|
||
|
def flush_tx_chunks(self, more_frags=False):
|
||
|
if self.for_rx_test:
|
||
|
ip_dst = self.local_ip
|
||
|
ip_src = self.remote_ip
|
||
|
else:
|
||
|
ip_src = self.local_ip
|
||
|
ip_dst = self.remote_ip
|
||
|
p = (
|
||
|
IP(
|
||
|
src=ip_src,
|
||
|
dst=ip_dst,
|
||
|
ttl=127,
|
||
|
frag=0,
|
||
|
flags=0,
|
||
|
id=self.tx_seq,
|
||
|
)
|
||
|
/ UDP(sport=self.local_port, dport=self.remote_port, chksum=0)
|
||
|
/ PVTI(
|
||
|
reass_chunk_count=self.tx_n_reass_chunks,
|
||
|
seq=self.tx_seq,
|
||
|
stream_index=self.stream_index,
|
||
|
chunks=self.tx_chunks,
|
||
|
)
|
||
|
)
|
||
|
|
||
|
p = IP(raw(p))
|
||
|
|
||
|
self.tx_n_reass_chunks = 0
|
||
|
self.tx_chunks = []
|
||
|
self.tx_seq = self.tx_seq + 1
|
||
|
return p
|
||
|
|
||
|
def encap_pkt(self, p):
|
||
|
out = []
|
||
|
if IP in p:
|
||
|
p[IP].ttl = p[IP].ttl - 1
|
||
|
payload_wip = p[IP].build()
|
||
|
elif IPv6 in p:
|
||
|
p[IPv6].hlim = p[IPv6].hlim - 1
|
||
|
payload_wip = p[IPv6].build()
|
||
|
|
||
|
split_chunks = False
|
||
|
huge_solo_packet = (
|
||
|
len(payload_wip) + self.chunk_header_len > self.get_payload_room()
|
||
|
) and len(self.tx_chunks) == 0
|
||
|
|
||
|
while True:
|
||
|
available_room = self.get_payload_room()
|
||
|
chunk_wip_len = len(payload_wip) + self.chunk_header_len
|
||
|
xpad0 = 0xABAB
|
||
|
xpad1 = 0xABABABAB
|
||
|
|
||
|
if chunk_wip_len <= available_room:
|
||
|
# happy case - there is enough space to fit the entire chunk
|
||
|
if split_chunks:
|
||
|
self.tx_n_reass_chunks = self.tx_n_reass_chunks + 1
|
||
|
tx = PVTIChunk(data=payload_wip, _pad0=xpad0, _pad1=xpad1)
|
||
|
self.tx_chunks.append(tx)
|
||
|
if chunk_wip_len == available_room:
|
||
|
# an unlikely perfect fit - send this packet.
|
||
|
out.append(self.flush_tx_chunks())
|
||
|
break
|
||
|
elif available_room < self.chunk_header_len + 1:
|
||
|
# Can not fit even a chunk header + 1 byte of data
|
||
|
# Flush and retry
|
||
|
out.append(self.flush_tx_chunks())
|
||
|
continue
|
||
|
else:
|
||
|
# Chop as much as we can from the packet
|
||
|
chop_len = available_room - self.chunk_header_len
|
||
|
if split_chunks:
|
||
|
self.tx_n_reass_chunks = self.tx_n_reass_chunks + 1
|
||
|
tx = PVTIChunk(data=payload_wip[:chop_len], _pad0=xpad0, _pad1=xpad1)
|
||
|
self.tx_chunks.append(tx)
|
||
|
out.append(self.flush_tx_chunks())
|
||
|
split_chunks = True
|
||
|
payload_wip = payload_wip[chop_len:]
|
||
|
continue
|
||
|
return out
|
||
|
|
||
|
def encap_packets(self, pkts):
|
||
|
out = []
|
||
|
self.start_encap()
|
||
|
for p in pkts:
|
||
|
out.extend(self.encap_pkt(p))
|
||
|
last_pkt = self.finish_encap()
|
||
|
if last_pkt != None:
|
||
|
out.append(last_pkt)
|
||
|
return out
|
||
|
|
||
|
def start_encap(self):
|
||
|
return None
|
||
|
|
||
|
def finish_encap(self):
|
||
|
out = None
|
||
|
if len(self.tx_chunks) > 0:
|
||
|
out = self.flush_tx_chunks()
|
||
|
return out
|
||
|
|
||
|
|
||
|
""" TestPvti is a subclass of VPPTestCase classes.
|
||
|
|
||
|
PVTI test.
|
||
|
|
||
|
"""
|
||
|
|
||
|
|
||
|
def get_field_bytes(pkt, name):
|
||
|
fld, val = pkt.getfield_and_val(name)
|
||
|
return fld.i2m(pkt, val)
|
||
|
|
||
|
|
||
|
class VppPvtiInterface(VppInterface):
|
||
|
"""
|
||
|
VPP PVTI interface
|
||
|
"""
|
||
|
|
||
|
def __init__(
|
||
|
self, test, local_ip, local_port, remote_ip, remote_port, underlay_mtu=1500
|
||
|
):
|
||
|
super(VppPvtiInterface, self).__init__(test)
|
||
|
|
||
|
self.local_ip = local_ip
|
||
|
self.local_port = local_port
|
||
|
self.remote_ip = remote_ip
|
||
|
self.remote_port = remote_port
|
||
|
self.underlay_mtu = underlay_mtu
|
||
|
|
||
|
def get_ende(self, for_rx_test=False):
|
||
|
return PvtiEnDe(
|
||
|
self.local_ip,
|
||
|
self.local_port,
|
||
|
self.remote_ip,
|
||
|
self.remote_port,
|
||
|
self.underlay_mtu,
|
||
|
for_rx_test,
|
||
|
)
|
||
|
|
||
|
def verify_encap_packets(self, orig_pkts, recv_pkts):
|
||
|
ende = self.get_ende()
|
||
|
recv2_pkts = ende.encap_packets(orig_pkts)
|
||
|
out1 = []
|
||
|
out2 = []
|
||
|
for i, pkt in enumerate(recv_pkts):
|
||
|
if IP in pkt:
|
||
|
rx_pkt = pkt[IP]
|
||
|
elif IPv6 in pkt:
|
||
|
rx_pkt = pkt[IPv6]
|
||
|
else:
|
||
|
raise "Neither IPv4 nor IPv6"
|
||
|
py_pkt = recv2_pkts[i]
|
||
|
if rx_pkt != py_pkt:
|
||
|
eprint("received packet:")
|
||
|
rx_pkt.show()
|
||
|
eprint("python packet:")
|
||
|
py_pkt.show()
|
||
|
out1.append(rx_pkt)
|
||
|
out2.append(py_pkt)
|
||
|
return (out1, out2)
|
||
|
|
||
|
def add_vpp_config(self):
|
||
|
r = self.test.vapi.pvti_interface_create(
|
||
|
interface={
|
||
|
"local_ip": self.local_ip,
|
||
|
"local_port": self.local_port,
|
||
|
"remote_ip": self.remote_ip,
|
||
|
"remote_port": self.remote_port,
|
||
|
"underlay_mtu": self.underlay_mtu,
|
||
|
}
|
||
|
)
|
||
|
self.set_sw_if_index(r.sw_if_index)
|
||
|
self.test.registry.register(self, self.test.logger)
|
||
|
return self
|
||
|
|
||
|
def remove_vpp_config(self):
|
||
|
self.test.vapi.pvti_interface_delete(sw_if_index=self._sw_if_index)
|
||
|
|
||
|
def query_vpp_config(self):
|
||
|
ts = self.test.vapi.pvti_interface_dump(sw_if_index=0xFFFFFFFF)
|
||
|
for t in ts:
|
||
|
if (
|
||
|
t.interface.sw_if_index == self._sw_if_index
|
||
|
and str(t.interface.local_ip) == self.local_ip
|
||
|
and t.interface.local_port == self.local_port
|
||
|
and t.interface.remote_port == self.remote_port
|
||
|
and str(t.interface.remote_ip) == self.remote_ip
|
||
|
):
|
||
|
self.test.logger.info("QUERY AYXX: true")
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.object_id()
|
||
|
|
||
|
def object_id(self):
|
||
|
return "pvti-%d" % self._sw_if_index
|
||
|
|
||
|
|
||
|
@unittest.skipIf("pvti" in config.excluded_plugins, "Exclude PVTI plugin tests")
|
||
|
# @tag_run_solo
|
||
|
class TestPvti(VppTestCase):
|
||
|
"""Packet Vector Tunnel Interface (PVTI) Test Case"""
|
||
|
|
||
|
error_str = compile(r"Error")
|
||
|
|
||
|
# maxDiff = None
|
||
|
|
||
|
wg4_output_node_name = "/err/wg4-output-tun/"
|
||
|
wg4_input_node_name = "/err/wg4-input/"
|
||
|
wg6_output_node_name = "/err/wg6-output-tun/"
|
||
|
wg6_input_node_name = "/err/wg6-input/"
|
||
|
kp4_error = wg4_output_node_name + "Keypair error"
|
||
|
mac4_error = wg4_input_node_name + "Invalid MAC handshake"
|
||
|
peer4_in_err = wg4_input_node_name + "Peer error"
|
||
|
peer4_out_err = wg4_output_node_name + "Peer error"
|
||
|
kp6_error = wg6_output_node_name + "Keypair error"
|
||
|
mac6_error = wg6_input_node_name + "Invalid MAC handshake"
|
||
|
peer6_in_err = wg6_input_node_name + "Peer error"
|
||
|
peer6_out_err = wg6_output_node_name + "Peer error"
|
||
|
cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
|
||
|
cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
|
||
|
ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
|
||
|
ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
|
||
|
|
||
|
@classmethod
|
||
|
def setUpClass(cls):
|
||
|
super(TestPvti, cls).setUpClass()
|
||
|
try:
|
||
|
cls.create_pg_interfaces(range(2))
|
||
|
for i in cls.pg_interfaces:
|
||
|
i.admin_up()
|
||
|
i.config_ip4()
|
||
|
i.config_ip6()
|
||
|
i.resolve_arp()
|
||
|
i.resolve_ndp()
|
||
|
|
||
|
except Exception:
|
||
|
super(TestPvti, cls).tearDownClass()
|
||
|
raise
|
||
|
|
||
|
@classmethod
|
||
|
def tearDownClass(cls):
|
||
|
super(TestPvti, cls).tearDownClass()
|
||
|
|
||
|
def setUp(self):
|
||
|
super(VppTestCase, self).setUp()
|
||
|
self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
|
||
|
self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
|
||
|
self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
|
||
|
self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
|
||
|
self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
|
||
|
self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
|
||
|
self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
|
||
|
self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
|
||
|
self.base_cookie_dec4_err = self.statistics.get_err_counter(
|
||
|
self.cookie_dec4_err
|
||
|
)
|
||
|
self.base_cookie_dec6_err = self.statistics.get_err_counter(
|
||
|
self.cookie_dec6_err
|
||
|
)
|
||
|
self.base_ratelimited4_err = self.statistics.get_err_counter(
|
||
|
self.ratelimited4_err
|
||
|
)
|
||
|
self.base_ratelimited6_err = self.statistics.get_err_counter(
|
||
|
self.ratelimited6_err
|
||
|
)
|
||
|
|
||
|
def create_packets(
|
||
|
self, src_ip_if, count=1, size=150, for_rx=False, is_ip6=False, af_mix=False
|
||
|
):
|
||
|
pkts = []
|
||
|
total_packet_count = count
|
||
|
padstr0 = ""
|
||
|
padstr1 = ""
|
||
|
for i in range(0, 2000):
|
||
|
padstr0 = padstr0 + (".%03x" % i)
|
||
|
padstr1 = padstr1 + ("+%03x" % i)
|
||
|
|
||
|
for i in range(0, total_packet_count):
|
||
|
if af_mix:
|
||
|
is_ip6 = i % 2 == 1
|
||
|
|
||
|
dst_mac = src_ip_if.local_mac
|
||
|
src_mac = src_ip_if.remote_mac
|
||
|
if for_rx:
|
||
|
dst_ip4 = src_ip_if.remote_ip4
|
||
|
dst_ip6 = src_ip_if.remote_ip6
|
||
|
src_ip4 = "10.0.%d.4" % i
|
||
|
src_ip6 = "2001:db8::%x" % i
|
||
|
else:
|
||
|
src_ip4 = src_ip_if.remote_ip4
|
||
|
src_ip6 = src_ip_if.remote_ip6
|
||
|
dst_ip4 = "10.0.%d.4" % i
|
||
|
dst_ip6 = "2001:db8::%x" % i
|
||
|
src_l4 = 1234 + i
|
||
|
dst_l4 = 4321 + i
|
||
|
|
||
|
ulp = UDP(sport=src_l4, dport=dst_l4)
|
||
|
payload = "test pkt #%d" % i
|
||
|
if i % 2 == 1:
|
||
|
padstr = padstr1
|
||
|
else:
|
||
|
padstr = padstr0
|
||
|
|
||
|
p = Ether(dst=dst_mac, src=src_mac)
|
||
|
if is_ip6:
|
||
|
p /= IPv6(src=src_ip6, dst=dst_ip6)
|
||
|
else:
|
||
|
p /= IP(src=src_ip4, dst=dst_ip4, frag=0, flags=0)
|
||
|
|
||
|
p /= ulp / Raw(payload)
|
||
|
|
||
|
if i % 2 == 1 or total_packet_count == 1:
|
||
|
self.extend_packet(p, size, padstr)
|
||
|
else:
|
||
|
self.extend_packet(p, 150, padstr)
|
||
|
pkts.append(p)
|
||
|
return pkts
|
||
|
|
||
|
def add_rx_ether_header(self, in_pkts, rx_intf=None):
|
||
|
out = []
|
||
|
if rx_intf is None:
|
||
|
rx_intf = self.pg0
|
||
|
dst_mac = rx_intf.local_mac
|
||
|
src_mac = rx_intf.remote_mac
|
||
|
pkts = []
|
||
|
for p in in_pkts:
|
||
|
p0 = Ether(dst=dst_mac, src=src_mac) / p[IP]
|
||
|
out.append(p0)
|
||
|
return out
|
||
|
|
||
|
def encap_for_rx_test(self, pkts, rx_intf=None):
|
||
|
ende = self.pvti0.get_ende(for_rx_test=True)
|
||
|
encap_pkts = ende.encap_packets(pkts)
|
||
|
return self.add_rx_ether_header(encap_pkts, rx_intf)
|
||
|
|
||
|
def decrement_ttl_and_build(self, send_pkts):
|
||
|
out = []
|
||
|
pkts = copy.deepcopy(send_pkts)
|
||
|
for p in pkts:
|
||
|
p[IP].ttl = p[IP].ttl - 1
|
||
|
out.append(Ether(p.build()))
|
||
|
return out
|
||
|
|
||
|
def create_rx_packets(self, dst_ip_if, rx_intf=None, count=1, size=150):
|
||
|
pkts = []
|
||
|
total_packet_count = count
|
||
|
padstr = ""
|
||
|
if rx_intf is None:
|
||
|
rx_intf = self.pg0
|
||
|
for i in range(0, 2000):
|
||
|
padstr = padstr + (".%03x" % i)
|
||
|
|
||
|
dst_mac = rx_intf.local_mac
|
||
|
src_mac = rx_intf.remote_mac
|
||
|
|
||
|
for i in range(0, total_packet_count):
|
||
|
dst_ip4 = dst_ip_if.remote_ip4
|
||
|
src_ip4 = "10.0.%d.4" % i
|
||
|
src_l4 = 1234 + i
|
||
|
dst_l4 = 4321 + i
|
||
|
|
||
|
ulp = UDP(sport=src_l4, dport=dst_l4)
|
||
|
payload = "test"
|
||
|
|
||
|
# if i % 2 == 1 or total_packet_count == 1:
|
||
|
# self.extend_packet(p, size, padstr)
|
||
|
# else:
|
||
|
# self.extend_packet(p, 150, padstr)
|
||
|
|
||
|
pvti = PVTI(seq=42 + i, chunks=[])
|
||
|
for j in range(0, 32):
|
||
|
p = (
|
||
|
IP(src=src_ip4, dst=dst_ip4, frag=0, flags=0, id=j + 0x4000)
|
||
|
/ ulp
|
||
|
/ Raw(payload)
|
||
|
)
|
||
|
chunk0 = PVTIChunk(data=raw(p))
|
||
|
pvti.chunks.append(chunk0)
|
||
|
|
||
|
p = (
|
||
|
Ether(dst=dst_mac, src=src_mac)
|
||
|
/ IP(src="192.0.2.1", dst=rx_intf.local_ip4, id=0x3000 + i)
|
||
|
/ UDP(sport=12312, dport=12312)
|
||
|
/ pvti
|
||
|
)
|
||
|
# p.show()
|
||
|
# Ether(raw(p)).show()
|
||
|
|
||
|
pkts.append(p)
|
||
|
return pkts
|
||
|
|
||
|
def send_and_assert_no_replies_ignoring_init(
|
||
|
self, intf, pkts, remark="", timeout=None
|
||
|
):
|
||
|
self.pg_send(intf, pkts)
|
||
|
|
||
|
def _filter_out_fn(p):
|
||
|
return is_ipv6_misc(p) or is_handshake_init(p)
|
||
|
|
||
|
try:
|
||
|
if not timeout:
|
||
|
timeout = 1
|
||
|
for i in self.pg_interfaces:
|
||
|
i.assert_nothing_captured(
|
||
|
timeout=timeout, remark=remark, filter_out_fn=_filter_out_fn
|
||
|
)
|
||
|
timeout = 0.1
|
||
|
finally:
|
||
|
pass
|
||
|
|
||
|
def test_0000_pvti_interface(self):
|
||
|
"""Simple interface creation"""
|
||
|
local_port = 12312
|
||
|
peer_addr = self.pg0.remote_ip4 # "192.0.2.1"
|
||
|
peer_port = 31234
|
||
|
peer_port = 12312
|
||
|
|
||
|
# Create interface
|
||
|
pvti0 = VppPvtiInterface(
|
||
|
self, self.pg1.local_ip4, local_port, peer_addr, peer_port
|
||
|
).add_vpp_config()
|
||
|
|
||
|
self.logger.info(self.vapi.cli("sh int"))
|
||
|
self.logger.info(self.vapi.cli("show pvti interface"))
|
||
|
self.logger.info(self.vapi.cli("show pvti tx peers"))
|
||
|
self.logger.info(self.vapi.cli("show pvti rx peers"))
|
||
|
|
||
|
# delete interface
|
||
|
pvti0.remove_vpp_config()
|
||
|
# self.logger.info(self.vapi.cli("show pvti interface"))
|
||
|
# pvti0.add_vpp_config()
|
||
|
|
||
|
def test_0001_pvti_send_simple_1pkt(self):
|
||
|
"""v4o4 TX: Simple packet: 1 -> 1"""
|
||
|
|
||
|
self.prepare_for_test("v4o4_1pkt_simple")
|
||
|
pkts = self.create_packets(self.pg1)
|
||
|
|
||
|
recv_pkts = self.send_and_expect(self.pg1, pkts, self.pg0)
|
||
|
for p in recv_pkts:
|
||
|
self.logger.info(p)
|
||
|
|
||
|
c_pkts, py_pkts = self.pvti0.verify_encap_packets(pkts, recv_pkts)
|
||
|
self.assertEqual(c_pkts, py_pkts)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_0101_pvti_send_simple_1pkt(self):
|
||
|
"""v6o4 TX: Simple packet: 1 -> 1"""
|
||
|
|
||
|
self.prepare_for_test("v6o4_1pkt_simple")
|
||
|
pkts = self.create_packets(self.pg1, is_ip6=True)
|
||
|
|
||
|
recv_pkts = self.send_and_expect(self.pg1, pkts, self.pg0, n_rx=1)
|
||
|
for p in recv_pkts:
|
||
|
self.logger.info(p)
|
||
|
|
||
|
c_pkts, py_pkts = self.pvti0.verify_encap_packets(pkts, recv_pkts)
|
||
|
self.assertEqual(c_pkts, py_pkts)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_0002_pvti_send_simple_2pkt(self):
|
||
|
"""TX: Simple packet: 2 -> 1"""
|
||
|
self.prepare_for_test("2pkt_simple")
|
||
|
|
||
|
send_pkts = self.create_packets(self.pg1, count=2)
|
||
|
pkts = copy.deepcopy(send_pkts)
|
||
|
rx = self.send_and_expect(self.pg1, pkts, self.pg0, n_rx=1)
|
||
|
for p in rx:
|
||
|
self.logger.info(p)
|
||
|
# p.show()
|
||
|
|
||
|
payload0 = rx[0][PVTI].chunks[0].data
|
||
|
payload1 = rx[0][PVTI].chunks[1].data
|
||
|
|
||
|
pktA0 = IP(payload0)
|
||
|
pktA1 = IP(payload1)
|
||
|
|
||
|
p0 = pkts[0][IP]
|
||
|
p0.ttl = p0.ttl - 1
|
||
|
pktB0 = IP(p0.build())
|
||
|
|
||
|
p1 = pkts[1][IP]
|
||
|
p1.ttl = p1.ttl - 1
|
||
|
pktB1 = IP(p1.build())
|
||
|
|
||
|
self.assertEqual(pktA0, pktB0)
|
||
|
self.assertEqual(pktA1, pktB1)
|
||
|
|
||
|
c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
|
||
|
self.assertEqual(c_pkts, py_pkts)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def prepare_for_test(self, test_name, underlay_mtu=1500, is_ip6=False):
|
||
|
local_port = 12312
|
||
|
peer_ip4_addr = "192.0.2.1"
|
||
|
peer_ip6_addr = "2001:db8:dead::1"
|
||
|
peer_port = 31234
|
||
|
peer_port = 12312
|
||
|
for i in self.pg_interfaces:
|
||
|
i.test_name = test_name
|
||
|
if is_ip6:
|
||
|
self.pvti0 = VppPvtiInterface(
|
||
|
self,
|
||
|
self.pg1.local_ip6,
|
||
|
local_port,
|
||
|
peer_ip6_addr,
|
||
|
peer_port,
|
||
|
underlay_mtu,
|
||
|
).add_vpp_config()
|
||
|
else:
|
||
|
self.pvti0 = VppPvtiInterface(
|
||
|
self,
|
||
|
self.pg1.local_ip4,
|
||
|
local_port,
|
||
|
peer_ip4_addr,
|
||
|
peer_port,
|
||
|
underlay_mtu,
|
||
|
).add_vpp_config()
|
||
|
self.pvti0.config_ip4()
|
||
|
self.pvti0.config_ip6()
|
||
|
self.pvti0.admin_up()
|
||
|
|
||
|
self.logger.info(self.vapi.cli("ip route add 0.0.0.0/0 via 172.16.3.3"))
|
||
|
## FIXME: using direct "interface" below results in blackouts. intermittently.
|
||
|
# self.logger.info(self.vapi.cli("ip route 0.0.0.0/0 via pvti0"))
|
||
|
self.logger.info(self.vapi.cli("ip route add ::/0 via pvti0"))
|
||
|
self.logger.info(self.vapi.cli("ip route add 192.0.2.1/32 via pg0"))
|
||
|
self.logger.info(self.vapi.cli("ip neighbor pg0 192.0.2.1 000c.0102.0304"))
|
||
|
self.logger.info(self.vapi.cli("ip route 2001:db8:dead::1/128 via pg0"))
|
||
|
self.logger.info(
|
||
|
self.vapi.cli("ip neighbor pg0 2001:db8:dead::1 000c.0102.0304")
|
||
|
)
|
||
|
self.logger.info(self.vapi.cli("ip neighbor pg1 172.16.2.2 000c.0102.0304"))
|
||
|
self.logger.info(self.vapi.cli("sh int"))
|
||
|
self.logger.info(self.vapi.cli("sh ip fib"))
|
||
|
self.logger.info(self.vapi.cli("show pvti interface"))
|
||
|
self.logger.info(self.vapi.cli("set interface ip pvti-bypass pg0"))
|
||
|
|
||
|
def cleanup_after_test(self):
|
||
|
self.logger.info(self.vapi.cli("ip neighbor del pg0 192.0.2.1 000c.0102.0304"))
|
||
|
self.logger.info(self.vapi.cli("ip neighbor del pg1 172.16.2.2 000c.0102.0304"))
|
||
|
self.logger.info(self.vapi.cli("ip route del 192.0.2.1/32 via pg0"))
|
||
|
# self.logger.info(self.vapi.cli("ip route del 0.0.0.0/0 via pvti0"))
|
||
|
self.logger.info(self.vapi.cli("ip route del ::/0 via pvti0"))
|
||
|
self.logger.info(self.vapi.cli("sh int"))
|
||
|
self.logger.info(self.vapi.cli("show pvti interface"))
|
||
|
self.pvti0.remove_vpp_config()
|
||
|
|
||
|
def test_0003_pvti_send_simple_1pkt_big(self):
|
||
|
"""TX: Simple big packet: 1 -> 2"""
|
||
|
self.prepare_for_test("1big_pkt")
|
||
|
|
||
|
send_pkts = self.create_packets(self.pg1, count=1, size=1900)
|
||
|
pkts = copy.deepcopy(send_pkts)
|
||
|
self.logger.info("count: ")
|
||
|
self.logger.info(len(pkts))
|
||
|
rx = self.send_and_expect(self.pg1, pkts, self.pg0, n_rx=2)
|
||
|
for p in rx:
|
||
|
self.logger.info(p)
|
||
|
self.logger.info(len(p[PVTI].chunks[0].data))
|
||
|
# p.show()
|
||
|
payload = rx[0][PVTI].chunks[0].data + rx[1][PVTI].chunks[0].data
|
||
|
|
||
|
pkt1 = IP(payload)
|
||
|
p0 = pkts[0][IP]
|
||
|
p0.ttl = p0.ttl - 1
|
||
|
|
||
|
pkt0 = IP(p0.build())
|
||
|
|
||
|
self.assertEqual(pkt0, pkt1)
|
||
|
|
||
|
c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
|
||
|
self.assertEqual(c_pkts, py_pkts)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_0004_pvti_send_simple_5pkt_big(self):
|
||
|
"""v4o4 TX: Simple big packets: 5 -> 2"""
|
||
|
self.prepare_for_test("v4o4_5big_pkt")
|
||
|
|
||
|
send_pkts = self.create_packets(self.pg1, count=5, size=1050)
|
||
|
self.logger.info("count: %d " % len(send_pkts))
|
||
|
# self.logger.info(len(pkts))
|
||
|
rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=2)
|
||
|
for p in rx:
|
||
|
self.logger.info(p)
|
||
|
self.logger.info(len(p[PVTI].chunks[0].data))
|
||
|
# p.show()
|
||
|
|
||
|
c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
|
||
|
self.assertEqual(c_pkts, py_pkts)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_0104_pvti_send_simple_5pkt_big(self):
|
||
|
"""v6o4 TX: Simple big packets: 5 -> 2"""
|
||
|
self.prepare_for_test("v4o4_5big_pkt")
|
||
|
|
||
|
send_pkts = self.create_packets(self.pg1, count=5, size=1050, is_ip6=True)
|
||
|
self.logger.info("count: %d " % len(send_pkts))
|
||
|
# self.logger.info(len(pkts))
|
||
|
rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=2)
|
||
|
for p in rx:
|
||
|
self.logger.info(p)
|
||
|
self.logger.info(len(p[PVTI].chunks[0].data))
|
||
|
# p.show()
|
||
|
|
||
|
c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
|
||
|
self.assertEqual(c_pkts, py_pkts)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def Xtest_0204_pvti_send_simple_5pkt_mix(self):
|
||
|
"""vXo4 TX: Simple packets mix: 5 -> 2"""
|
||
|
# FIXME: This test is disabled for now, but left here, to have this comment
|
||
|
# The mix of IPv4 and IPv6 packets in VPP will forward two
|
||
|
# different graphs, so after encap it will result in two
|
||
|
# PV packets: one with IPv4 chunks, and one with IPv6 chunks.
|
||
|
# The python test encapsulator does not do this, and it is probably
|
||
|
# a useless idea to introduce attempts to mimic this behavior,
|
||
|
# because in any case one can not expect the orderly scheduling
|
||
|
# of IPv4 vs IPv6 graph processing.
|
||
|
self.prepare_for_test("vXo4_5big_pkt")
|
||
|
|
||
|
send_pkts = self.create_packets(self.pg1, count=5, size=1050, af_mix=True)
|
||
|
# self.logger.info(len(pkts))
|
||
|
rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=2)
|
||
|
for p in rx:
|
||
|
self.logger.info(p)
|
||
|
self.logger.info(len(p[PVTI].chunks[0].data))
|
||
|
|
||
|
c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
|
||
|
self.assertEqual(c_pkts, py_pkts)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_0005_pvti_send_mix_3pkt_medium_mtu(self):
|
||
|
"""TX: small+big+small packets over medium mtu: 3 -> 3"""
|
||
|
self.prepare_for_test("3pkt_small_mtu", underlay_mtu=400)
|
||
|
|
||
|
send_pkts = self.create_packets(self.pg1, count=3, size=500)
|
||
|
pkts = copy.deepcopy(send_pkts)
|
||
|
self.logger.info("count: %d " % len(send_pkts))
|
||
|
# self.logger.info(len(pkts))
|
||
|
rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=3)
|
||
|
for p in rx:
|
||
|
self.logger.info(p)
|
||
|
self.logger.info(len(p[PVTI].chunks[0].data))
|
||
|
# p.show()
|
||
|
|
||
|
# check the middle chunk which is spread across two packets
|
||
|
payload = rx[0][PVTI].chunks[1].data + rx[1][PVTI].chunks[0].data
|
||
|
|
||
|
pkt1 = IP(payload)
|
||
|
|
||
|
p0 = pkts[1][IP]
|
||
|
p0.ttl = p0.ttl - 1
|
||
|
|
||
|
pkt0 = IP(p0.build())
|
||
|
self.assertEqual(pkt0, pkt1)
|
||
|
|
||
|
c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
|
||
|
self.assertEqual(c_pkts, py_pkts)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_0006_pvti_send_mix_4pkt_medium_mtu(self):
|
||
|
"""TX: small+big+small packets over 600 mtu: 4 -> 3"""
|
||
|
self.prepare_for_test("6pkt_small_mtu", underlay_mtu=600)
|
||
|
|
||
|
send_pkts = self.create_packets(self.pg1, count=4, size=500)
|
||
|
pkts = copy.deepcopy(send_pkts)
|
||
|
# self.logger.info(len(pkts))
|
||
|
rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=3)
|
||
|
for p in rx:
|
||
|
self.logger.info(p)
|
||
|
self.logger.info(len(p[PVTI].chunks[0].data))
|
||
|
# p.show()
|
||
|
|
||
|
# check the middle chunk which is spread across two packets
|
||
|
payload = rx[0][PVTI].chunks[1].data + rx[1][PVTI].chunks[0].data
|
||
|
|
||
|
pkt1 = IP(payload)
|
||
|
|
||
|
p0 = pkts[1][IP]
|
||
|
p0.ttl = p0.ttl - 1
|
||
|
|
||
|
pkt0 = IP(p0.build())
|
||
|
self.assertEqual(pkt0, pkt1)
|
||
|
|
||
|
c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
|
||
|
self.assertEqual(c_pkts, py_pkts)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_0007_pvti_send_simple_1_3_pkt(self):
|
||
|
"""TX: Simple packet: 1 -> 3, small mtu"""
|
||
|
|
||
|
self.prepare_for_test("1_3_pkt_simple", underlay_mtu=520)
|
||
|
send_pkts = self.create_packets(self.pg1, count=1, size=1400)
|
||
|
pkts = copy.deepcopy(send_pkts)
|
||
|
|
||
|
rx = self.send_and_expect(self.pg1, pkts, self.pg0, n_rx=3)
|
||
|
for p in rx:
|
||
|
self.logger.info(p)
|
||
|
|
||
|
c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
|
||
|
self.assertEqual(c_pkts, py_pkts)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_0008_pvti_chained_1_3_pkt(self):
|
||
|
"""TX: Chained packet: 2700 byte 1 -> 3, mtu 1000"""
|
||
|
|
||
|
self.prepare_for_test("1_3_pkt_simple", underlay_mtu=1000)
|
||
|
send_pkts = self.create_packets(self.pg1, count=1, size=2700)
|
||
|
pkts = copy.deepcopy(send_pkts)
|
||
|
|
||
|
pkt0 = Ether(raw(pkts[0]))[IP]
|
||
|
|
||
|
rx = self.send_and_expect(self.pg1, send_pkts, self.pg0, n_rx=3)
|
||
|
for p in rx:
|
||
|
self.logger.info(p)
|
||
|
|
||
|
p0 = pkts[0][IP]
|
||
|
p0.ttl = p0.ttl - 1
|
||
|
pkt0 = IP(p0.build())
|
||
|
|
||
|
payload = (
|
||
|
rx[0][PVTI].chunks[0].data
|
||
|
+ rx[1][PVTI].chunks[0].data
|
||
|
+ rx[2][PVTI].chunks[0].data
|
||
|
# + rx[2][PVTI].chunks[1].data
|
||
|
)
|
||
|
pkt1 = IP(payload)
|
||
|
|
||
|
self.assertEqual(pkt0, pkt1)
|
||
|
|
||
|
# FIXME: this will fail because the send path
|
||
|
# does not combine the data from two chained blocks.
|
||
|
# when this succeeds, the above checks in this testcase will need to be redone
|
||
|
# c_pkts, py_pkts = self.pvti0.verify_encap_packets(send_pkts, rx)
|
||
|
# self.assertEqual(c_pkts, py_pkts)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_1001_pvti_rx_simple_1pkt(self):
|
||
|
"""RX: Simple packet: 1 -> 32"""
|
||
|
|
||
|
self.prepare_for_test("1pkt_rx_simple")
|
||
|
pkts = self.create_rx_packets(self.pg1, rx_intf=self.pg0)
|
||
|
self.logger.info(self.vapi.cli("show pvti interface"))
|
||
|
self.logger.info(self.vapi.cli("show udp ports"))
|
||
|
|
||
|
recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=32)
|
||
|
for p in recv_pkts:
|
||
|
self.logger.info(p)
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_1002_pvti_rx_big_1buf(self):
|
||
|
"""RX: Orig Big packet, single buf: 2 -> 1"""
|
||
|
|
||
|
self.prepare_for_test("1buf_rx_big")
|
||
|
|
||
|
pkts_orig = self.create_packets(self.pg1, count=1, size=1900, for_rx=True)
|
||
|
pkts = self.encap_for_rx_test(pkts_orig, rx_intf=self.pg0)
|
||
|
self.logger.info(self.vapi.cli("show pvti interface"))
|
||
|
self.logger.info(self.vapi.cli("show udp ports"))
|
||
|
|
||
|
known_good_pkts = self.decrement_ttl_and_build(pkts_orig)
|
||
|
|
||
|
recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=1)
|
||
|
for i, p in enumerate(recv_pkts):
|
||
|
self.logger.info(p)
|
||
|
self.assertEqual(p[IP], known_good_pkts[i][IP])
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_1003_pvti_rx_big_2buf(self):
|
||
|
"""RX: Very Big packet, chained buf: 3 -> 1"""
|
||
|
|
||
|
self.prepare_for_test("2buf_rx_big")
|
||
|
|
||
|
pkts_orig = self.create_packets(self.pg1, count=1, size=3000, for_rx=True)
|
||
|
|
||
|
pkts = self.encap_for_rx_test(pkts_orig, rx_intf=self.pg0)
|
||
|
self.logger.info(self.vapi.cli("show pvti interface"))
|
||
|
self.logger.info(self.vapi.cli("show udp ports"))
|
||
|
|
||
|
known_good_pkts = self.decrement_ttl_and_build(pkts_orig)
|
||
|
|
||
|
recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=1)
|
||
|
for i, p in enumerate(recv_pkts):
|
||
|
self.logger.info(p)
|
||
|
if p[IP] != known_good_pkts[i][IP]:
|
||
|
p[IP].show()
|
||
|
known_good_pkts[i][IP].show()
|
||
|
self.assertEqual(p[IP], known_good_pkts[i][IP])
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_1004_pvti_rx_big_2buf_and_small(self):
|
||
|
"""RX: Very Big packet, chained buf: 3 -> 1 + small pkt"""
|
||
|
|
||
|
self.prepare_for_test("2buf_rx_big_and_small")
|
||
|
|
||
|
pkts_orig = self.create_packets(self.pg1, count=2, size=3000, for_rx=True)
|
||
|
|
||
|
pkts = self.encap_for_rx_test(pkts_orig, rx_intf=self.pg0)
|
||
|
self.logger.info(self.vapi.cli("show pvti interface"))
|
||
|
self.logger.info(self.vapi.cli("show udp ports"))
|
||
|
|
||
|
known_good_pkts = self.decrement_ttl_and_build(pkts_orig)
|
||
|
|
||
|
recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=2)
|
||
|
for i, p in enumerate(recv_pkts):
|
||
|
self.logger.info(p)
|
||
|
if p[IP] != known_good_pkts[i][IP]:
|
||
|
p[IP].show()
|
||
|
known_good_pkts[i][IP].show()
|
||
|
self.assertEqual(p[IP], known_good_pkts[i][IP])
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_1005_pvti_rx_big_2buf_and_small_drop(self):
|
||
|
"""RX: Very Big packet, chained buf: 3 -> 1 + small pkt, encap pkt lost"""
|
||
|
|
||
|
self.prepare_for_test("2buf_rx_big_and_small_drop")
|
||
|
|
||
|
pkts_orig = self.create_packets(self.pg1, count=3, size=3000, for_rx=True)
|
||
|
|
||
|
pkts = self.encap_for_rx_test(pkts_orig, rx_intf=self.pg0)
|
||
|
# drop the second packet after encapsulation (the one with the second frag of the large packet)
|
||
|
pkts.pop(1)
|
||
|
self.logger.info(self.vapi.cli("show pvti interface"))
|
||
|
self.logger.info(self.vapi.cli("show udp ports"))
|
||
|
|
||
|
known_good_pkts = self.decrement_ttl_and_build(pkts_orig)
|
||
|
|
||
|
# drop the large original packet, leaving just two small ones
|
||
|
known_good_pkts.pop(1)
|
||
|
|
||
|
recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=2)
|
||
|
for i, p in enumerate(recv_pkts):
|
||
|
self.logger.info(p)
|
||
|
if p[IP] != known_good_pkts[i][IP]:
|
||
|
p[IP].show()
|
||
|
known_good_pkts[i][IP].show()
|
||
|
self.assertEqual(p[IP], known_good_pkts[i][IP])
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
def test_1006_pvti_rx_big_2buf_and_small_drop2(self):
|
||
|
"""RX: Very Big packet, chained buf: 3 -> 1 + small pkt, non-initial frag pkt lost"""
|
||
|
|
||
|
self.prepare_for_test("2buf_rx_big_and_small_drop2")
|
||
|
|
||
|
pkts_orig = self.create_packets(self.pg1, count=3, size=6000, for_rx=True)
|
||
|
|
||
|
pkts = self.encap_for_rx_test(pkts_orig, rx_intf=self.pg0)
|
||
|
# drop the second packet after encapsulation (the one with the second frag of the large packet)
|
||
|
pkts.pop(2)
|
||
|
self.logger.info(self.vapi.cli("show pvti interface"))
|
||
|
self.logger.info(self.vapi.cli("show udp ports"))
|
||
|
|
||
|
known_good_pkts = self.decrement_ttl_and_build(pkts_orig)
|
||
|
# drop the large original packet, leaving just two small ones
|
||
|
known_good_pkts.pop(1)
|
||
|
|
||
|
recv_pkts = self.send_and_expect(self.pg0, pkts, self.pg1, n_rx=2)
|
||
|
for i, p in enumerate(recv_pkts):
|
||
|
self.logger.info(p)
|
||
|
if p[IP] != known_good_pkts[i][IP]:
|
||
|
p[IP].show()
|
||
|
known_good_pkts[i][IP].show()
|
||
|
self.assertEqual(p[IP], known_good_pkts[i][IP])
|
||
|
|
||
|
self.cleanup_after_test()
|
||
|
|
||
|
|
||
|
class PvtiHandoffTests(TestPvti):
|
||
|
"""Pvti Tests in multi worker setup"""
|
||
|
|
||
|
vpp_worker_count = 2
|
||
|
|
||
|
def xtest_wg_peer_init(self):
|
||
|
"""Handoff"""
|
||
|
|
||
|
port = 12383
|
||
|
|
||
|
# Create interfaces
|
||
|
wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
|
||
|
wg0.admin_up()
|
||
|
wg0.config_ip4()
|
||
|
|
||
|
self.pg_enable_capture(self.pg_interfaces)
|
||
|
self.pg_start()
|
||
|
|
||
|
peer_1 = VppWgPeer(
|
||
|
self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
|
||
|
).add_vpp_config()
|
||
|
self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
|
||
|
|
||
|
r1 = VppIpRoute(
|
||
|
self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
|
||
|
).add_vpp_config()
|
||
|
|
||
|
# skip the first automatic handshake
|
||
|
self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
|
||
|
|
||
|
# send a valid handsake init for which we expect a response
|
||
|
p = peer_1.mk_handshake(self.pg1)
|
||
|
|
||
|
rx = self.send_and_expect(self.pg1, [p], self.pg1)
|
||
|
|
||
|
peer_1.consume_response(rx[0])
|
||
|
|
||
|
# send a data packet from the peer through the tunnel
|
||
|
# this completes the handshake and pins the peer to worker 0
|
||
|
p = (
|
||
|
IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
|
||
|
/ UDP(sport=222, dport=223)
|
||
|
/ Raw()
|
||
|
)
|
||
|
d = peer_1.encrypt_transport(p)
|
||
|
p = peer_1.mk_tunnel_header(self.pg1) / (
|
||
|
Pvti(message_type=4, reserved_zero=0)
|
||
|
/ PvtiTransport(
|
||
|
receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
|
||
|
)
|
||
|
)
|
||
|
rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
|
||
|
|
||
|
for rx in rxs:
|
||
|
self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
|
||
|
self.assertEqual(rx[IP].ttl, 19)
|
||
|
|
||
|
# send a packets that are routed into the tunnel
|
||
|
# and pins the peer tp worker 1
|
||
|
pe = (
|
||
|
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
|
||
|
/ IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
|
||
|
/ UDP(sport=555, dport=556)
|
||
|
/ Raw(b"\x00" * 80)
|
||
|
)
|
||
|
rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
|
||
|
peer_1.validate_encapped(rxs, pe)
|
||
|
|
||
|
# send packets into the tunnel, from the other worker
|
||
|
p = [
|
||
|
(
|
||
|
peer_1.mk_tunnel_header(self.pg1)
|
||
|
/ Pvti(message_type=4, reserved_zero=0)
|
||
|
/ PvtiTransport(
|
||
|
receiver_index=peer_1.sender,
|
||
|
counter=ii + 1,
|
||
|
encrypted_encapsulated_packet=peer_1.encrypt_transport(
|
||
|
(
|
||
|
IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
|
||
|
/ UDP(sport=222, dport=223)
|
||
|
/ Raw()
|
||
|
)
|
||
|
),
|
||
|
)
|
||
|
)
|
||
|
for ii in range(255)
|
||
|
]
|
||
|
|
||
|
rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
|
||
|
|
||
|
for rx in rxs:
|
||
|
self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
|
||
|
self.assertEqual(rx[IP].ttl, 19)
|
||
|
|
||
|
# send a packets that are routed into the tunnel
|
||
|
# from worker 0
|
||
|
rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
|
||
|
|
||
|
peer_1.validate_encapped(rxs, pe)
|
||
|
|
||
|
r1.remove_vpp_config()
|
||
|
peer_1.remove_vpp_config()
|
||
|
wg0.remove_vpp_config()
|