#!/usr/bin/env python3 """GSO functional tests""" # # Add tests for: # - GSO # - Verify that sending Jumbo frame without GSO enabled correctly # - Verify that sending Jumbo frame with GSO enabled correctly # - Verify that sending Jumbo frame with GSO enabled only on ingress interface # import unittest from scapy.packet import Raw from scapy.layers.l2 import GRE from scapy.layers.inet6 import IPv6, Ether, IP, ICMPv6PacketTooBig from scapy.layers.inet6 import ipv6nh, IPerror6 from scapy.layers.inet import TCP, ICMP from scapy.layers.vxlan import VXLAN from scapy.layers.ipsec import ESP from vpp_papi import VppEnum from framework import VppTestCase from asfframework import VppTestRunner from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathProto from vpp_ipip_tun_interface import VppIpIpTunInterface from vpp_vxlan_tunnel import VppVxlanTunnel from vpp_gre_interface import VppGreInterface from vpp_ipsec import VppIpsecSA, VppIpsecTunProtect from template_ipsec import ( IPsecIPv4Params, IPsecIPv6Params, config_tun_params, ) """ Test_gso is a subclass of VPPTestCase classes. GSO tests. """ class TestGSO(VppTestCase): """GSO Test Case""" def __init__(self, *args): VppTestCase.__init__(self, *args) @classmethod def setUpClass(self): super(TestGSO, self).setUpClass() res = self.create_pg_interfaces(range(2)) res_gso = self.create_pg_interfaces(range(2, 4), 1, 1460) self.create_pg_interfaces(range(4, 5), 1, 8940) self.pg_interfaces.append(res[0]) self.pg_interfaces.append(res[1]) self.pg_interfaces.append(res_gso[0]) self.pg_interfaces.append(res_gso[1]) @classmethod def tearDownClass(self): super(TestGSO, self).tearDownClass() def setUp(self): super(TestGSO, self).setUp() for i in self.pg_interfaces: i.admin_up() i.config_ip4() i.config_ip6() i.disable_ipv6_ra() i.resolve_arp() i.resolve_ndp() self.single_tunnel_bd = 10 self.vxlan = VppVxlanTunnel( self, src=self.pg0.local_ip4, dst=self.pg0.remote_ip4, vni=self.single_tunnel_bd, ) self.vxlan2 = VppVxlanTunnel( self, src=self.pg0.local_ip6, dst=self.pg0.remote_ip6, vni=self.single_tunnel_bd, ) self.ipip4 = VppIpIpTunInterface( self, self.pg0, self.pg0.local_ip4, self.pg0.remote_ip4 ) self.ipip6 = VppIpIpTunInterface( self, self.pg0, self.pg0.local_ip6, self.pg0.remote_ip6 ) self.gre4 = VppGreInterface(self, self.pg0.local_ip4, self.pg0.remote_ip4) self.gre6 = VppGreInterface(self, self.pg0.local_ip6, self.pg0.remote_ip6) def tearDown(self): super(TestGSO, self).tearDown() if not self.vpp_dead: for i in self.pg_interfaces: i.unconfig_ip4() i.unconfig_ip6() i.admin_down() def test_gso(self): """GSO test""" # # Send jumbo frame with gso disabled and DF bit is set # p4 = ( Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) ) rxs = self.send_and_expect(self.pg0, [p4], self.pg0) for rx in rxs: self.assertEqual(rx[Ether].src, self.pg0.local_mac) self.assertEqual(rx[Ether].dst, self.pg0.remote_mac) self.assertEqual(rx[IP].src, self.pg0.local_ip4) self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) self.assertEqual(rx[ICMP].type, 3) # "dest-unreach" self.assertEqual(rx[ICMP].code, 4) # "fragmentation-needed" # # Send checksum offload frames # p40 = ( Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst=self.pg0.remote_ip4, flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 1460) ) rxs = self.send_and_expect(self.pg2, 100 * [p40], self.pg0) for rx in rxs: self.assertEqual(rx[Ether].src, self.pg0.local_mac) self.assertEqual(rx[Ether].dst, self.pg0.remote_mac) self.assertEqual(rx[IP].src, self.pg2.remote_ip4) self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) payload_len = rx[IP].len - 20 - 20 self.assert_ip_checksum_valid(rx) self.assert_tcp_checksum_valid(rx) self.assertEqual(payload_len, len(rx[Raw])) p60 = ( Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst=self.pg0.remote_ip6) / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 1440) ) rxs = self.send_and_expect(self.pg2, 100 * [p60], self.pg0) for rx in rxs: self.assertEqual(rx[Ether].src, self.pg0.local_mac) self.assertEqual(rx[Ether].dst, self.pg0.remote_mac) self.assertEqual(rx[IPv6].src, self.pg2.remote_ip6) self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6) payload_len = rx[IPv6].plen - 20 self.assert_tcp_checksum_valid(rx) self.assertEqual(payload_len, len(rx[Raw])) # # Send jumbo frame with gso enabled and DF bit is set # input and output interfaces support GSO # self.vapi.feature_gso_enable_disable( sw_if_index=self.pg3.sw_if_index, enable_disable=1 ) p41 = ( Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4, flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) ) rxs = self.send_and_expect(self.pg2, 100 * [p41], self.pg3, 100) for rx in rxs: self.assertEqual(rx[Ether].src, self.pg3.local_mac) self.assertEqual(rx[Ether].dst, self.pg3.remote_mac) self.assertEqual(rx[IP].src, self.pg2.remote_ip4) self.assertEqual(rx[IP].dst, self.pg3.remote_ip4) self.assertEqual(rx[IP].len, 65240) # 65200 + 20 (IP) + 20 (TCP) self.assertEqual(rx[TCP].sport, 1234) self.assertEqual(rx[TCP].dport, 1234) # # ipv6 # p61 = ( Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst=self.pg3.remote_ip6) / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) ) rxs = self.send_and_expect(self.pg2, 100 * [p61], self.pg3, 100) for rx in rxs: self.assertEqual(rx[Ether].src, self.pg3.local_mac) self.assertEqual(rx[Ether].dst, self.pg3.remote_mac) self.assertEqual(rx[IPv6].src, self.pg2.remote_ip6) self.assertEqual(rx[IPv6].dst, self.pg3.remote_ip6) self.assertEqual(rx[IPv6].plen, 65220) # 65200 + 20 (TCP) self.assertEqual(rx[TCP].sport, 1234) self.assertEqual(rx[TCP].dport, 1234) # # Send jumbo frame with gso enabled only on input interface # and DF bit is set. GSO packet will be chunked into gso_size # data payload # self.vapi.feature_gso_enable_disable( sw_if_index=self.pg0.sw_if_index, enable_disable=1 ) p42 = ( Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst=self.pg0.remote_ip4, flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) ) rxs = self.send_and_expect(self.pg2, 5 * [p42], self.pg0, 225) size = 0 for rx in rxs: self.assertEqual(rx[Ether].src, self.pg0.local_mac) self.assertEqual(rx[Ether].dst, self.pg0.remote_mac) self.assertEqual(rx[IP].src, self.pg2.remote_ip4) self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) payload_len = rx[IP].len - 20 - 20 # len - 20 (IP4) - 20 (TCP) self.assert_ip_checksum_valid(rx) self.assert_tcp_checksum_valid(rx) self.assertEqual(rx[TCP].sport, 1234) self.assertEqual(rx[TCP].dport, 1234) self.assertEqual(payload_len, len(rx[Raw])) size += payload_len self.assertEqual(size, 65200 * 5) # # ipv6 # p62 = ( Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst=self.pg0.remote_ip6) / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) ) rxs = self.send_and_expect(self.pg2, 5 * [p62], self.pg0, 225) size = 0 for rx in rxs: self.assertEqual(rx[Ether].src, self.pg0.local_mac) self.assertEqual(rx[Ether].dst, self.pg0.remote_mac) self.assertEqual(rx[IPv6].src, self.pg2.remote_ip6) self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6) payload_len = rx[IPv6].plen - 20 self.assert_tcp_checksum_valid(rx) self.assertEqual(rx[TCP].sport, 1234) self.assertEqual(rx[TCP].dport, 1234) self.assertEqual(payload_len, len(rx[Raw])) size += payload_len self.assertEqual(size, 65200 * 5) # # Send jumbo frame with gso enabled only on input interface # and DF bit is unset. GSO packet will be fragmented. # self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [576, 0, 0, 0]) self.vapi.feature_gso_enable_disable( sw_if_index=self.pg1.sw_if_index, enable_disable=1 ) p43 = ( Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst=self.pg1.remote_ip4) / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) ) rxs = self.send_and_expect(self.pg2, 5 * [p43], self.pg1, 5 * 119) size = 0 for rx in rxs: self.assertEqual(rx[Ether].src, self.pg1.local_mac) self.assertEqual(rx[Ether].dst, self.pg1.remote_mac) self.assertEqual(rx[IP].src, self.pg2.remote_ip4) self.assertEqual(rx[IP].dst, self.pg1.remote_ip4) self.assert_ip_checksum_valid(rx) size += rx[IP].len - 20 size -= 20 * 5 # TCP header self.assertEqual(size, 65200 * 5) # # IPv6 # Send jumbo frame with gso enabled only on input interface. # ICMPv6 Packet Too Big will be sent back to sender. # self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1280, 0, 0, 0]) p63 = ( Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst=self.pg1.remote_ip6) / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) ) rxs = self.send_and_expect_some(self.pg2, 5 * [p63], self.pg2, 5) for rx in rxs: self.assertEqual(rx[Ether].src, self.pg2.local_mac) self.assertEqual(rx[Ether].dst, self.pg2.remote_mac) self.assertEqual(rx[IPv6].src, self.pg2.local_ip6) self.assertEqual(rx[IPv6].dst, self.pg2.remote_ip6) self.assertEqual(rx[IPv6].plen, 1240) # MTU - IPv6 header self.assertEqual(ipv6nh[rx[IPv6].nh], "ICMPv6") self.assertEqual(rx[ICMPv6PacketTooBig].mtu, 1280) self.assertEqual(rx[IPerror6].src, self.pg2.remote_ip6) self.assertEqual(rx[IPerror6].dst, self.pg1.remote_ip6) self.assertEqual(rx[IPerror6].plen - 20, 65200) # # Send jumbo frame with gso enabled only on input interface with 9K MTU # and DF bit is unset. GSO packet will be fragmented. MSS is 8960. GSO # size will be min(MSS, 2048 - 14 - 20) vlib_buffer_t size # self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [9000, 0, 0, 0]) self.vapi.sw_interface_set_mtu(self.pg4.sw_if_index, [9000, 0, 0, 0]) p44 = ( Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / IP(src=self.pg4.remote_ip4, dst=self.pg1.remote_ip4) / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) ) rxs = self.send_and_expect(self.pg4, 5 * [p44], self.pg1, 165) size = 0 for rx in rxs: self.assertEqual(rx[Ether].src, self.pg1.local_mac) self.assertEqual(rx[Ether].dst, self.pg1.remote_mac) self.assertEqual(rx[IP].src, self.pg4.remote_ip4) self.assertEqual(rx[IP].dst, self.pg1.remote_ip4) payload_len = rx[IP].len - 20 - 20 # len - 20 (IP4) - 20 (TCP) self.assert_ip_checksum_valid(rx) self.assert_tcp_checksum_valid(rx) self.assertEqual(payload_len, len(rx[Raw])) size += payload_len self.assertEqual(size, 65200 * 5) # # IPv6 # p64 = ( Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / IPv6(src=self.pg4.remote_ip6, dst=self.pg1.remote_ip6) / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) ) rxs = self.send_and_expect(self.pg4, 5 * [p64], self.pg1, 170) size = 0 for rx in rxs: self.assertEqual(rx[Ether].src, self.pg1.local_mac) self.assertEqual(rx[Ether].dst, self.pg1.remote_mac) self.assertEqual(rx[IPv6].src, self.pg4.remote_ip6) self.assertEqual(rx[IPv6].dst, self.pg1.remote_ip6) payload_len = rx[IPv6].plen - 20 self.assert_tcp_checksum_valid(rx) self.assertEqual(payload_len, len(rx[Raw])) size += payload_len self.assertEqual(size, 65200 * 5) self.vapi.feature_gso_enable_disable( sw_if_index=self.pg0.sw_if_index, enable_disable=0 ) self.vapi.feature_gso_enable_disable( sw_if_index=self.pg1.sw_if_index, enable_disable=0 ) def test_gso_vxlan(self): """GSO VXLAN test""" self.logger.info(self.vapi.cli("sh int addr")) # # Send jumbo frame with gso enabled only on input interface and # create VXLAN VTEP on VPP pg0, and put vxlan_tunnel0 and pg2 # into BD. # # # enable ipv4/vxlan # self.vxlan.add_vpp_config() self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=self.vxlan.sw_if_index, bd_id=self.single_tunnel_bd ) self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=self.pg2.sw_if_index, bd_id=self.single_tunnel_bd ) self.vapi.feature_gso_enable_disable( sw_if_index=self.pg0.sw_if_index, enable_disable=1 ) # # IPv4/IPv4 - VXLAN # p45 = ( Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst="172.16.3.3", flags="DF") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) ) rxs = self.send_and_expect(self.pg2, 5 * [p45], self.pg0, 225) size = 0 for rx in rxs: self.assertEqual(rx[Ether].src, self.pg0.local_mac) self.assertEqual(rx[Ether].dst, self.pg0.remote_mac) self.assertEqual(rx[IP].src, self.pg0.local_ip4) self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) self.assert_ip_checksum_valid(rx) self.assert_udp_checksum_valid(rx, ignore_zero_checksum=True) self.assertEqual(rx[VXLAN].vni, 10) inner = rx[VXLAN].payload self.assertEqual(rx[IP].len - 20 - 8 - 8, len(inner)) self.assertEqual(inner[Ether].src, self.pg2.remote_mac) self.assertEqual(inner[Ether].dst, self.pg2.local_mac) self.assertEqual(inner[IP].src, self.pg2.remote_ip4) self.assertEqual(inner[IP].dst, "172.16.3.3") self.assert_ip_checksum_valid(inner) self.assert_tcp_checksum_valid(inner) payload_len = inner[IP].len - 20 - 20 self.assertEqual(payload_len, len(inner[Raw])) size += payload_len self.assertEqual(size, 65200 * 5) # # IPv4/IPv6 - VXLAN # p65 = ( Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IPv6(src=self.pg2.remote_ip6, dst="fd01:3::3") / TCP(sport=1234, dport=1234) / Raw(b"\xa5" * 65200) ) rxs = self.send_and_expect(self.pg2, 5 * [p65], self.pg0, 225) size = 0 for rx in rxs: self.assertEqual(rx[Ether].src, self.pg0.local_mac) self.assertEqual(rx[Ether].dst, self.pg0.remote_mac) self.assertEqual(rx[IP].src, self.pg0.local_ip4) self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) self.assert_ip_checksum_valid(rx) self.assert_udp_checksum_valid(rx, ignore_zero_checksum=True) self.assertEqual(rx[VXLAN].vni, 10) inner = rx[VXLAN].payload self.assertEqual(rx[IP].len - 20 - 8 - 8, len(inner)) self.assertEqual(inner[Ether].src, self.pg2.remote_mac) self.assertEqual(inner[Ether].dst, self.pg2.local_mac) self.assertEqual(inner[IPv6].src, self.pg2.remote_ip6) self.assertEqual(inner[IPv6].dst, "fd01:3::3") self.assert_tcp_checksum_valid(inner) payload_len = inner[IPv6].plen - 20 self.assertEqual(payload_len, len(inner[Raw])) size += payload_len self.assertEqual(size, 65200 * 5) # # disable ipv4/vxlan # self.vxlan.remove_vpp_config() # # enable ipv6/vxlan # self.vxlan2.add_vpp_config() self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=self.vxlan2.sw_if_index, bd_id=self.single_tunnel_bd ) # # IPv6/IPv4 - VXLAN # p46 = ( Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.remote_ip4, dst="172.16.3.3", flags="DF") / TCP(sport=1234, dport=1234)
#!/usr/bin/env python3
import inspect
import os
import reprlib
import unittest
from framework import VppTestCase
from multiprocessing import Process, Pipe
from pickle import dumps
import sys
from enum import IntEnum, IntFlag
class SerializableClassCopy(object):
"""
Empty class used as a basis for a serializable copy of another class.
"""
pass
def __repr__(self):
return '<SerializableClassCopy dict=%s>' % self.__dict__
class RemoteClassAttr(object):
"""
Wrapper around attribute of a remotely executed class.
"""
def __init__(self, remote, attr):
self._path = [attr] if attr else []
self._remote = remote
def path_to_str(self):
return '.'.join(self._path)
def get_remote_value(self):
return self._remote._remote_exec(RemoteClass.GET, self.path_to_str())
def __repr__(self):
return self._remote._remote_exec(RemoteClass.REPR, self.path_to_str())
def __str__(self):
return self._remote._remote_exec(RemoteClass.STR, self.path_to_str())
def __getattr__(self, attr):
if attr[0] == '_':
if not (attr.startswith('__') and attr.endswith('__')):
raise AttributeError('tried to get private attribute: %s ',
attr)
self._path.append(attr)
return self
def __setattr__(self, attr, val):
if attr[0] == '_':
if not (attr.startswith('__') and attr.endswith('__')):
super(RemoteClassAttr, self).__setattr__(attr, val)
return
self._path.append(attr)
self._remote._remote_exec(RemoteClass.SETATTR, self.path_to_str(),
value=val)
def __call__(self, *args, **kwargs):
return self._remote._remote_exec(RemoteClass.CALL, self.path_to_str(),
*args, **kwargs)
class RemoteClass(Process):
"""
This class can wrap around and adapt the interface of another class,
and then delegate its execution to a newly forked child process.
Usage:
# Create a remotely executed instance of MyClass
object = RemoteClass(MyClass, arg1='foo', arg2='bar')
object.start_remote()
# Access the object normally as if it was an instance of your class.
object.my_attribute = 20
print object.my_attribute
print object.my_method(object.my_attribute)
object.my_attribute.nested_attribute = 'test'
# If you need the value of a remote attribute, use .get_remote_value
method. This method is automatically called when needed in the context
of a remotely executed class. E.g.:
if (object.my_attribute.get_remote_value() > 20):
object.my_attribute2 = object.my_attribute
# Destroy the instance
object.quit_remote()
object.terminate()
"""
GET = 0 # Get attribute remotely
CALL = 1 # Call method remotely
SETATTR = 2 # Set attribute remotely
REPR = 3 # Get representation of a remote object
STR = 4 # Get string representation of a remote object
QUIT = 5 # Quit remote execution
PIPE_PARENT = 0 # Parent end of the pipe
PIPE_CHILD = 1 # Child end of the pipe
DEFAULT_TIMEOUT = 2 # default timeout for an operation to execute
def __init__(self, cls, *args, **kwargs):
super(RemoteClass, self).__init__()
self._cls = cls
self._args = args
self._kwargs = kwargs
self._timeout = RemoteClass.DEFAULT_TIMEOUT
self._pipe = Pipe() # pipe for input/output arguments
def __repr__(self):
return reprlib.repr(RemoteClassAttr(self, None))
def __str__(self):
return str(RemoteClassAttr(self, None))
def __call__(self, *args, **kwargs):
return self.RemoteClassAttr(self, None)()
def __getattr__(self, attr):
if attr[0] == '_' or not self.is_alive():
if not (attr.startswith('__') and attr.endswith('__')):
if hasattr(super(RemoteClass, self), '__getattr__'):
return super(RemoteClass, self).__getattr__(attr)
raise AttributeError('missing: %s', attr)
return RemoteClassAttr(self, attr)
def __setattr__(self, attr, val):
if attr[0] == '_' or not self.is_alive():
if not (attr.startswith('__') and attr.endswith('__')):
super(RemoteClass, self).__setattr__(attr, val)
return
setattr(RemoteClassAttr(self, None), attr, val)
def _remote_exec(self, op, path=None, *args, **kwargs):
"""
Execute given operation on a given, possibly nested, member remotely.
"""
# automatically resolve remote objects in the arguments
mutable_args = list(args)
for i, val in enumerate(mutable_args):
if isinstance(val, RemoteClass) or \
isinstance(val, RemoteClassAttr):
mutable_args[i] = val.get_remote_value()
args = tuple(mutable_args)
for key, val in kwargs.items():
if isinstance(val, RemoteClass) or \
isinstance(val, RemoteClassAttr):
kwargs[key] = val.get_remote_value()
# send request
args = self._make_serializable(args)
kwargs = self._make_serializable(kwargs)
self._pipe[RemoteClass.PIPE_PARENT].send((op, path, args, kwargs))
timeout = self._timeout
# adjust timeout specifically for the .sleep method
if path is not None and path.split('.')[-1] == 'sleep':
if args and isinstance(args[0], (long, int)):
timeout += args[0]
elif 'timeout' in kwargs:
timeout += kwargs['timeout']
if not self._pipe[RemoteClass.PIPE_PARENT].poll(timeout):
return None
try