aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/l2/feat_bitmap.c
blob: 6c046467f2ccee4cf06cec0c8b8ee9b080b1a5de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
 * feat_bitmap.c: bitmap for managing feature invocation
 *
 * Copyright (c) 2013 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vnet/pg/pg.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/ethernet/packet.h>
#include <vlib/cli.h>
#include <vnet/l2/l2_input.h>
#include <vnet/l2/feat_bitmap.h>

#include <vppinfra/error.h>
#include <vppinfra/hash.h>
#include <vppinfra/cache.h>


/*
 * Drop node for feature bitmaps
 * For features that just do a drop, or are not yet implemented.
 * Initial feature dispatch nodes don't need to set b0->error
 * in case of a possible drop because that will be done here.
 *The next node is always error-drop.
 */

static vlib_node_registration_t feat_bitmap_drop_node;

#define foreach_feat_bitmap_drop_error		\
_(NO_FWD,     "L2 feature forwarding disabled")	\
_(NYI,        "L2 feature not implemented")

typedef enum
{
#define _(sym,str) FEAT_BITMAP_DROP_ERROR_##sym,
  foreach_feat_bitmap_drop_error
#undef _
    FEAT_BITMAP_DROP_N_ERROR,
} feat_bitmap_drop_error_t;

static char *feat_bitmap_drop_error_strings[] = {
#define _(sym,string) string,
  foreach_feat_bitmap_drop_error
#undef _
};

typedef enum
{
  FEAT_BITMAP_DROP_NEXT_DROP,
  FEAT_BITMAP_DROP_N_NEXT,
} feat_bitmap_drop_next_t;

typedef struct
{
  u32 feature_bitmap;
} feat_bitmap_drop_trace_t;

/* packet trace format function */
static u8 *
format_feat_bitmap_drop_trace (u8 * s, va_list * args)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
  feat_bitmap_drop_trace_t *t = va_arg (*args, feat_bitmap_drop_trace_t *);

  s =
    format (s, "feat_bitmap_drop: feature bitmap 0x%08x", t->feature_bitmap);
  return s;
}

static uword
feat_bitmap_drop_node_fn (vlib_main_t * vm,
			  vlib_node_runtime_t * node, vlib_frame_t * frame)
{
  u32 n_left_from, *from, *to_next;
  feat_bitmap_drop_next_t next_index;

  from = vlib_frame_vector_args (frame);
  n_left_from = frame->n_vectors;	/* number of packets to process */
  next_index = node->cached_next_index;

  while (n_left_from > 0)
    {
      u32 n_left_to_next;

      /* get space to enqueue frame to graph node "next_index" */
      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  u32 bi0;
	  vlib_buffer_t *b0;
	  u32 next0;

	  /* speculatively enqueue b0 to the current next frame */
	  bi0 = from[0];
	  to_next[0] = bi0;
	  from += 1;
	  to_next += 1;
	  n_left_from -= 1;
	  n_left_to_next -= 1;

	  b0 = vlib_get_buffer (vm, bi0);

	  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
			     && (b0->flags & VLIB_BUFFER_IS_TRACED)))
	    {
	      feat_bitmap_drop_trace_t *t =
		vlib_add_trace (vm, node, b0, sizeof (*t));
	      t->feature_bitmap = vnet_buffer (b0)->l2.feature_bitmap;
	    }

	  if (vnet_buffer (b0)->l2.feature_bitmap == 1)
	    {
	      /*
	       * If we are executing the last feature, this is the
	       * No forwarding catch-all
	       */
	      b0->error = node->errors[FEAT_BITMAP_DROP_ERROR_NO_FWD];
	    }
	  else
	    {
	      b0->error = node->errors[FEAT_BITMAP_DROP_ERROR_NYI];
	    }
	  next0 = FEAT_BITMAP_DROP_NEXT_DROP;

	  /* verify speculative enqueue, maybe switch current next frame */
	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, next0);
	}

      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }
  return frame->n_vectors;
}

clib_error_t *
feat_bitmap_drop_init (vlib_main_t * vm)
{
  return 0;
}

VLIB_INIT_FUNCTION (feat_bitmap_drop_init);

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (feat_bitmap_drop_node,static) = {
  .function = feat_bitmap_drop_node_fn,
  .name = "feature-bitmap-drop",
  .vector_size = sizeof (u32),
  .format_trace = format_feat_bitmap_drop_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = ARRAY_LEN(feat_bitmap_drop_error_strings),
  .error_strings = feat_bitmap_drop_error_strings,

  .n_next_nodes = FEAT_BITMAP_DROP_N_NEXT,

  /* edit / add dispositions here */
  .next_nodes = {
    [FEAT_BITMAP_DROP_NEXT_DROP]  = "error-drop",
  },
};
/* *INDENT-ON* */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
pan>.append(int(test_id)) return test["id"] in test_ids_to_run # Test Config variables client_namespace = test_config["client_namespace"] server_namespace = test_config["server_namespace"] tests = filter(filter_tests, test_config["tests"]) af_packet_config = test_config["af_packet"] layer2 = test_config["L2"] layer3 = test_config["L3"] def create_test(test_name, test, ip_version, mtu): """Create and return a unittest method for a test.""" @unittest.skipUnless(config.extended, "part of extended tests") def test_func(self): self.logger.debug(f"Starting unittest:{test_name}") self.setUpTestToplogy(test=test, ip_version=ip_version) result = self.set_interfaces_mtu( mtu=mtu, ip_version=ip_version, vpp_interfaces=self.vpp_interfaces, linux_interfaces=self.linux_interfaces, ) # Start the Iperf server in dual stack mode & run iperf client if result is True: start_iperf(ip_version=6, server_only=True, logger=self.logger) self.assertTrue( start_iperf( ip_version=ip_version, server_ipv4_address=self.server_ip4_address, server_ipv6_address=self.server_ip6_address, client_only=True, duration=2, logger=self.logger, ) ) else: print( f"Skipping test:{test_name} as mtu:{mtu} is " f"invalid for TCP/IPv{ip_version}" ) test_func.__name__ = test_name return test_func def generate_vpp_interface_tests(): """Generate unittests for testing vpp interfaces.""" for test in tests: for ip_version in test_config["ip_versions"]: for mtu in test_config["mtus"]: test_name = ( f"test_id_{test['id']}_" + f"client_{test['client_if_type']}" + f"_v{test['client_if_version']}_" + f"gso_{test.get('client_if_gso', 0)}_" + f"gro_{test.get('client_if_gro', 0)}_" + f"to_server_{test['server_if_type']}" + f"_v{test['server_if_version']}_" + f"gso_{test.get('server_if_gso', 0)}_" + f"gro_{test.get('server_if_gro', 0)}_" + f"mtu_{mtu}_mode_{test['x_connect_mode']}_" + f"tcp_ipv{ip_version}" ) test_func = create_test( test_name=test_name, test=test, ip_version=ip_version, mtu=mtu ) setattr(TestVPPInterfacesQemu, test_name, test_func) class TestVPPInterfacesQemu(VppTestCase): """Test VPP interfaces inside a QEMU VM for IPv4/v6. Test Setup: Linux_ns1--iperfClient--host-int1--vpp-af_packet-int1--VPP-BD --vppaf_packet_int2--host-int2--iperfServer--Linux_ns2 """ @classmethod def setUpClass(cls): super(TestVPPInterfacesQemu, cls).setUpClass() @classmethod def tearDownClass(cls): super(TestVPPInterfacesQemu, cls).tearDownClass() def setUpTestToplogy(self, test, ip_version): """Setup the test topology. 1. Create Linux Namespaces for iPerf Client & Server. 2. Create VPP iPerf client and server virtual interfaces. 3. Enable desired vif features such as GSO & GRO. 3. Cross-Connect interfaces in VPP using L2 or L3. """ super(TestVPPInterfacesQemu, self).setUp() client_if_type = test["client_if_type"] server_if_type = test["server_if_type"] client_if_version = test["client_if_version"] server_if_version = test["server_if_version"] x_connect_mode = test["x_connect_mode"] # server ip4/ip6 addresses required by iperf client server_ip4_prefix = ( layer2["server_ip4_prefix"] if x_connect_mode == "L2" else layer3["server_ip4_prefix"] ) server_ip6_prefix = ( layer2["server_ip6_prefix"] if x_connect_mode == "L2" else layer3["server_ip6_prefix"] ) self.server_ip4_address = str(ip_interface(server_ip4_prefix).ip) self.server_ip6_address = str(ip_interface(server_ip6_prefix).ip) # next-hop IP address on VPP for routing from client & server namespaces vpp_client_prefix = ( layer3["vpp_client_ip4_prefix"] if ip_version == 4 else layer3["vpp_client_ip6_prefix"] ) vpp_client_nexthop = str(ip_interface(vpp_client_prefix).ip) vpp_server_prefix = ( layer3["vpp_server_ip4_prefix"] if ip_version == 4 else layer3["vpp_server_ip6_prefix"] ) vpp_server_nexthop = str(ip_interface(vpp_server_prefix).ip) create_namespace([client_namespace, server_namespace]) self.vpp_interfaces = [] self.linux_interfaces = [] enable_client_if_gso = test.get("client_if_gso", 0) enable_server_if_gso = test.get("server_if_gso", 0) enable_client_if_gro = test.get("client_if_gro", 0) enable_server_if_gro = test.get("server_if_gro", 0) ## Handle client interface types if client_if_type == "af_packet": create_host_interface( af_packet_config["iprf_client_interface_on_linux"], af_packet_config["iprf_client_interface_on_vpp"], client_namespace, layer2["client_ip4_prefix"] if x_connect_mode == "L2" else layer3["client_ip4_prefix"], layer2["client_ip6_prefix"] if x_connect_mode == "L2" else layer3["client_ip6_prefix"], ) self.ingress_if_idx = self.create_af_packet( version=client_if_version, host_if_name=af_packet_config["iprf_client_interface_on_vpp"], enable_gso=enable_client_if_gso, ) self.vpp_interfaces.append(self.ingress_if_idx) self.linux_interfaces.append( ["", af_packet_config["iprf_client_interface_on_vpp"]] ) self.linux_interfaces.append( [client_namespace, af_packet_config["iprf_client_interface_on_linux"]] ) if enable_client_if_gso == 0: disable_interface_gso( "", af_packet_config["iprf_client_interface_on_vpp"] ) disable_interface_gso( client_namespace, af_packet_config["iprf_client_interface_on_linux"] ) elif client_if_type == "tap" or client_if_type == "tun": self.ingress_if_idx = self.create_tap_tun( id=101, host_namespace=client_namespace, ip_version=ip_version, host_ip4_prefix=layer2["client_ip4_prefix"] if x_connect_mode == "L2" else layer3["client_ip4_prefix"], host_ip6_prefix=layer2["client_ip6_prefix"] if x_connect_mode == "L2" else layer3["client_ip6_prefix"], host_ip4_gw=vpp_client_nexthop if x_connect_mode == "L3" and ip_version == 4 else None, host_ip6_gw=vpp_client_nexthop if x_connect_mode == "L3" and ip_version == 6 else None, int_type=client_if_type, host_if_name=f"{client_if_type}0", enable_gso=enable_client_if_gso, enable_gro=enable_client_if_gro, ) self.vpp_interfaces.append(self.ingress_if_idx) self.linux_interfaces.append([client_namespace, f"{client_if_type}0"]) # Seeing TCP timeouts if tx=on & rx=on Linux tap & tun interfaces disable_interface_gso(client_namespace, f"{client_if_type}0") else: print( f"Unsupported client interface type: {client_if_type} " f"for test - ID={test['id']}" ) sys.exit(1) if server_if_type == "af_packet": create_host_interface( af_packet_config["iprf_server_interface_on_linux"], af_packet_config["iprf_server_interface_on_vpp"], server_namespace, server_ip4_prefix, server_ip6_prefix, ) self.egress_if_idx = self.create_af_packet( version=server_if_version, host_if_name=af_packet_config["iprf_server_interface_on_vpp"], enable_gso=enable_server_if_gso, ) self.vpp_interfaces.append(self.egress_if_idx) self.linux_interfaces.append( ["", af_packet_config["iprf_server_interface_on_vpp"]] ) self.linux_interfaces.append( [server_namespace, af_packet_config["iprf_server_interface_on_linux"]] ) if enable_server_if_gso == 0: disable_interface_gso( "", af_packet_config["iprf_server_interface_on_vpp"] ) disable_interface_gso( server_namespace, af_packet_config["iprf_server_interface_on_linux"] ) elif server_if_type == "tap" or server_if_type == "tun": self.egress_if_idx = self.create_tap_tun( id=102, host_namespace=server_namespace, ip_version=ip_version, host_ip4_prefix=layer2["server_ip4_prefix"] if x_connect_mode == "L2" else layer3["server_ip4_prefix"], host_ip6_prefix=layer2["server_ip6_prefix"] if x_connect_mode == "L2" else layer3["server_ip6_prefix"], int_type=server_if_type, host_if_name=f"{server_if_type}0", enable_gso=enable_server_if_gso, enable_gro=enable_server_if_gro, ) self.vpp_interfaces.append(self.egress_if_idx) self.linux_interfaces.append([server_namespace, f"{server_if_type}0"]) # Seeing TCP timeouts if tx=on & rx=on Linux tap & tun interfaces disable_interface_gso(server_namespace, f"{server_if_type}0") else: print( f"Unsupported server interface type: {server_if_type} " f"for test - ID={test['id']}" ) sys.exit(1) if x_connect_mode == "L2": self.l2_connect_interfaces(1, self.ingress_if_idx, self.egress_if_idx) elif x_connect_mode == "L3": # L3 connect client & server side vrf_id = layer3["ip4_vrf"] if ip_version == 4 else layer3["ip6_vrf"] self.l3_connect_interfaces( ip_version, vrf_id, (self.ingress_if_idx, vpp_client_prefix), (self.egress_if_idx, vpp_server_prefix), ) # Setup namespace routing if ip_version == 4: add_namespace_route(client_namespace, "0.0.0.0/0", vpp_client_nexthop) add_namespace_route(server_namespace, "0.0.0.0/0", vpp_server_nexthop) else: add_namespace_route(client_namespace, "::/0", vpp_client_nexthop) add_namespace_route(server_namespace, "::/0", vpp_server_nexthop) # Wait for Linux IPv6 stack to become ready if ip_version == 6: time.sleep(2) def tearDown(self): try: self.vapi.tap_delete_v2(self.ingress_if_idx) except Exception: pass try: self.vapi.tap_delete_v2(self.egress_if_idx) except Exception: pass try: for interface in self.vapi.af_packet_dump(): if ( interface.host_if_name == af_packet_config["iprf_client_interface_on_vpp"] ): self.vapi.af_packet_delete( af_packet_config["iprf_client_interface_on_vpp"] ) elif ( interface.host_if_name == af_packet_config["iprf_server_interface_on_vpp"] ): self.vapi.af_packet_delete( af_packet_config["iprf_server_interface_on_vpp"] ) except Exception: pass try: delete_host_interfaces( af_packet_config["iprf_client_interface_on_linux"], af_packet_config["iprf_server_interface_on_linux"], af_packet_config["iprf_client_interface_on_vpp"], af_packet_config["iprf_server_interface_on_vpp"], ) except Exception: pass try: self.vapi.ip_table_add_del(is_add=0, table={"table_id": layer3["ip4_vrf"]}) except Exception: pass try: self.vapi.ip_table_add_del(is_add=0, table={"table_id": layer3["ip6_vrf"]}) except Exception: pass try: delete_namespace( [ client_namespace, server_namespace, ] ) except Exception: pass try: stop_iperf() except Exception: pass def create_af_packet(self, version, host_if_name, enable_gso=0): """Create an af_packetv3 interface in VPP. Parameters: version -- 2 for af_packet_create_v2 -- 3 for af_packet_create_v3 host_if_name -- host interface name enable_gso -- Enable GSO on the interface when True """ af_packet_mode = VppEnum.vl_api_af_packet_mode_t af_packet_interface_mode = af_packet_mode.AF_PACKET_API_MODE_ETHERNET af_packet_flags = VppEnum.vl_api_af_packet_flags_t af_packet_interface_flags = af_packet_flags.AF_PACKET_API_FLAG_QDISC_BYPASS if enable_gso: af_packet_interface_flags = ( af_packet_interface_flags | af_packet_flags.AF_PACKET_API_FLAG_CKSUM_GSO ) if version == 2: af_packet_interface_flags = ( af_packet_interface_flags | af_packet_flags.AF_PACKET_API_FLAG_VERSION_2 ) api_args = { "use_random_hw_addr": True, "host_if_name": host_if_name, "flags": af_packet_interface_flags, } api_args["mode"] = af_packet_interface_mode result = self.vapi.af_packet_create_v3(**api_args) sw_if_index = result.sw_if_index # Enable software GSO chunking when interface doesn't support GSO offload if enable_gso == 0: self.vapi.feature_gso_enable_disable( sw_if_index=sw_if_index, enable_disable=1 ) else: self.vapi.feature_gso_enable_disable( sw_if_index=sw_if_index, enable_disable=0 ) self.vapi.sw_interface_set_flags(sw_if_index=sw_if_index, flags=1) return sw_if_index def create_tap_tun( self, id, host_namespace, ip_version, host_ip4_prefix=None, host_ip6_prefix=None, host_ip4_gw=None, host_ip6_gw=None, int_type="tap", host_if_name=None, enable_gso=0, enable_gro=0, ): """Create a tapv2 or tunv2 interface in VPP and attach to host. Parameters: id -- interface ID host_namespace -- host namespace to attach the tap/tun interface to ip_version -- 4 or 6 host_ip4_prefix -- ipv4 host interface address in CIDR notation if ip_version=4 host_ip6_prefix -- ipv6 host interface address in CIDR notation if ip_version=6 host_ip4_gw -- host IPv4 default gateway IP Address host_ip6_gw -- host IPv6 default gateway IP address int_type -- "tap" for tapv2 & "tun" for tunv2 interface host_if_name -- host side interface name enable_gso -- enable GSO enable_gro -- enable GSO/GRO-Coalesce """ TapFlags = VppEnum.vl_api_tap_flags_t tap_flags = 0 if int_type == "tun": tap_flags = TapFlags.TAP_API_FLAG_TUN if enable_gro: tap_flags = tap_flags | ( TapFlags.TAP_API_FLAG_GSO | TapFlags.TAP_API_FLAG_GRO_COALESCE ) elif enable_gso: tap_flags = tap_flags | TapFlags.TAP_API_FLAG_GSO elif int_type == "tap": if enable_gro: tap_flags = ( TapFlags.TAP_API_FLAG_GSO | TapFlags.TAP_API_FLAG_GRO_COALESCE ) elif enable_gso: tap_flags = TapFlags.TAP_API_FLAG_GSO api_args = { "id": id, "host_namespace_set": True, "host_namespace": host_namespace, "host_if_name_set": False, "host_bridge_set": False, "host_mac_addr_set": False, } if tap_flags != 0: api_args["tap_flags"] = tap_flags if ip_version == 4: api_args["host_ip4_prefix"] = ip_interface(host_ip4_prefix) api_args["host_ip4_prefix_set"] = True if host_ip4_gw: api_args["host_ip4_gw"] = ip_address(host_ip4_gw) api_args["host_ip4_gw_set"] = True if ip_version == 6: api_args["host_ip6_prefix"] = ip_interface(host_ip6_prefix) api_args["host_ip6_prefix_set"] = True if host_ip6_gw: api_args["host_ip6_gw"] = ip_address(host_ip6_gw) api_args["host_ip6_gw_set"] = True if host_if_name: api_args["host_if_name"] = host_if_name api_args["host_if_name_set"] = True result = self.vapi.tap_create_v2(**api_args) sw_if_index = result.sw_if_index # Enable software GSO chunking when interface doesn't support GSO offload and # GRO coalesce if enable_gso == 0 and enable_gro == 0: self.vapi.feature_gso_enable_disable( sw_if_index=sw_if_index, enable_disable=1 ) else: self.vapi.feature_gso_enable_disable( sw_if_index=sw_if_index, enable_disable=0 ) # Admin up self.vapi.sw_interface_set_flags(sw_if_index=sw_if_index, flags=1) return sw_if_index def dump_bridge_domain_details(self, bd_id): return self.vapi.bridge_domain_dump(bd_id=bd_id) def l2_connect_interfaces(self, bridge_id, *sw_if_idxs): for if_idx in sw_if_idxs: self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=if_idx, bd_id=bridge_id, shg=0, port_type=0, enable=True ) def l3_connect_interfaces(self, ip_version, vrf_id, *if_idx_ip_prefixes): """Setup routing for (if_idx, ip_prefix) inside VPP. arguments: if_idx_ip_prefixes -- sequence of (if_idx, ip_prefix) tuples ip_version -- 4 or 6 vrf_id -- vrf_id """ is_ipv6 = 0 if ip_version == 4 else 1 self.vapi.ip_table_add_del( is_add=1, table={"table_id": vrf_id, "is_ip6": is_ipv6} ) for sw_if_index, ip_prefix in if_idx_ip_prefixes: self.vapi.sw_interface_set_table( sw_if_index=sw_if_index, is_ipv6=is_ipv6, vrf_id=vrf_id ) self.vapi.sw_interface_add_del_address( sw_if_index=sw_if_index, is_add=1, prefix=ip_interface(ip_prefix) ) def set_interfaces_mtu(self, mtu, ip_version, **kwargs): """Set MTUs on VPP and Linux interfaces. arguments -- mtu -- mtu value ip_version - 4 or 6 kwargs['vpp_interfaces'] -- list of vpp interface if indexes kwargs['linux_interfaces'] -- list of tuples (namespace, interface_names) return True if mtu is set, else False """ vpp_interfaces = kwargs.get("vpp_interfaces") linux_interfaces = kwargs.get("linux_interfaces") # IPv6 on Linux requires an MTU value >=1280 if (ip_version == 6 and mtu >= 1280) or ip_version == 4: for sw_if_idx in vpp_interfaces: self.vapi.sw_interface_set_mtu( sw_if_index=sw_if_idx, mtu=[mtu, 0, 0, 0] ) for namespace, interface_name in linux_interfaces: set_interface_mtu( namespace=namespace, interface=interface_name, mtu=mtu, logger=self.logger, ) return True else: return False generate_vpp_interface_tests() if __name__ == "__main__": unittest.main(testRunner=VppTestRunner)