aboutsummaryrefslogtreecommitdiffstats
path: root/docs/report/vpp_performance_tests/overview.rst
blob: 9647edeabdde2f28dd420e92b1ac9b473a0faef9 (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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
Overview
========

VPP performance test results are reported for a range of processors.
For description of physical testbeds used for VPP performance tests
please refer to :ref:`tested_physical_topologies`.

.. _tested_logical_topologies:

Logical Topologies
------------------

CSIT VPP performance tests are executed on physical testbeds described
in :ref:`tested_physical_topologies`. Based on the packet path thru
server SUTs, three distinct logical topology types are used for VPP DUT
data plane testing:

#. NIC-to-NIC switching topologies.
#. VM service switching topologies.
#. Container service switching topologies.

NIC-to-NIC Switching
~~~~~~~~~~~~~~~~~~~~

The simplest logical topology for software data plane application like
VPP is NIC-to-NIC switching. Tested topologies for 2-Node and 3-Node
testbeds are shown in figures below.

.. only:: latex

    .. raw:: latex

        \begin{figure}[H]
            \centering
                \graphicspath{{../_tmp/src/vpp_performance_tests/}}
                \includegraphics[width=0.90\textwidth]{logical-2n-nic2nic}
                \label{fig:logical-2n-nic2nic}
        \end{figure}

.. only:: html

    .. figure:: logical-2n-nic2nic.svg
        :alt: logical-2n-nic2nic
        :align: center


.. only:: latex

    .. raw:: latex

        \begin{figure}[H]
            \centering
                \graphicspath{{../_tmp/src/vpp_performance_tests/}}
                \includegraphics[width=0.90\textwidth]{logical-3n-nic2nic}
                \label{fig:logical-3n-nic2nic}
        \end{figure}

.. only:: html

    .. figure:: logical-3n-nic2nic.svg
        :alt: logical-3n-nic2nic
        :align: center

Server Systems Under Test (SUT) run VPP application in Linux user-mode
as a Device Under Test (DUT). Server Traffic Generator (TG) runs T-Rex
application. Physical connectivity between SUTs and TG is provided using
different drivers and NIC models that need to be tested for performance
(packet/bandwidth throughput and latency).

From SUT and DUT perspectives, all performance tests involve forwarding
packets between two (or more) physical Ethernet ports (10GE, 25GE, 40GE,
100GE). In most cases both physical ports on SUT are located on the same
NIC. The only exceptions are link bonding and 100GE tests. In the latter
case only one port per NIC can be driven at linerate due to PCIe Gen3
x16 slot bandwidth limiations. 100GE NICs are not supported in PCIe Gen3
x8 slots.

Note that reported VPP DUT performance results are specific to the SUTs
tested. SUTs with other processors than the ones used in FD.io lab are
likely to yield different results. A good rule of thumb, that can be
applied to estimate VPP packet thoughput for NIC-to-NIC switching
topology, is to expect the forwarding performance to be proportional to
processor core frequency for the same processor architecture, assuming
processor is the only limiting factor and all other SUT parameters are
equivalent to FD.io CSIT environment.

VM Service Switching
~~~~~~~~~~~~~~~~~~~~

VM service switching topology test cases require VPP DUT to communicate
with Virtual Machines (VMs) over vhost-user virtual interfaces.

Two types of VM service topologies are tested in |csit-release|:

#. "Parallel" topology with packets flowing within SUT from NIC(s) via
   VPP DUT to VM, back to VPP DUT, then out thru NIC(s).

#. "Chained" topology (a.k.a. "Snake") with packets flowing within SUT
   from NIC(s) via VPP DUT to VM, back to VPP DUT, then to the next VM,
   back to VPP DUT and so on and so forth until the last VM in a chain,
   then back to VPP DUT and out thru NIC(s).

For each of the above topologies, VPP DUT is tested in a range of L2
or IPv4/IPv6 configurations depending on the test suite. Sample VPP DUT
"Chained" VM service topologies for 2-Node and 3-Node testbeds with each
SUT running N of VM instances is shown in the figures below.

.. only:: latex

    .. raw:: latex

        \begin{figure}[H]
            \centering
                \graphicspath{{../_tmp/src/vpp_performance_tests/}}
                \includegraphics[width=0.90\textwidth]{logical-2n-vm-vhost}
                \label{fig:logical-2n-vm-vhost}
        \end{figure}

.. only:: html

    .. figure:: logical-2n-vm-vhost.svg
        :alt: logical-2n-vm-vhost
        :align: center


.. only:: latex

    .. raw:: latex

        \begin{figure}[H]
            \centering
                \graphicspath{{../_tmp/src/vpp_performance_tests/}}
                \includegraphics[width=0.90\textwidth]{logical-3n-vm-vhost}
                \label{fig:logical-3n-vm-vhost}
        \end{figure}

.. only:: html

    .. figure:: logical-3n-vm-vhost.svg
        :alt: logical-3n-vm-vhost
        :align: center

In "Chained" VM topologies, packets are switched by VPP DUT multiple
times: twice for a single VM, three times for two VMs, N+1 times for N
VMs. Hence the external throughput rates measured by TG and listed in
this report must be multiplied by N+1 to represent the actual VPP DUT
aggregate packet forwarding rate.

For "Parallel" service topology packets are always switched twice by VPP
DUT per service chain.

Note that reported VPP DUT performance results are specific to the SUTs
tested. SUTs with other processor than the ones used in FD.io lab are
likely to yield different results. Similarly to NIC-to-NIC switching
topology, here one can also expect the forwarding performance to be
proportional to processor core frequency for the same processor
architecture, assuming processor is the only limiting factor. However
due to much higher dependency on intensive memory operations in VM
service chained topologies and sensitivity to Linux scheduler settings
and behaviour, this estimation may not always yield good enough
accuracy.

Container Service Switching
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Container service switching topology test cases require VPP DUT to
communicate with Containers (Ctrs) over memif virtual interfaces.

Three types of VM service topologies are tested in |csit-release|:

#. "Parallel" topology with packets flowing within SUT from NIC(s) via
   VPP DUT to Container, back to VPP DUT, then out thru NIC(s).

#. "Chained" topology (a.k.a. "Snake") with packets flowing within SUT
   from NIC(s) via VPP DUT to Container, back to VPP DUT, then to the
   next Container, back to VPP DUT and so on and so forth until the
   last Container in a chain, then back to VPP DUT and out thru NIC(s).

#. "Horizontal" topology with packets flowing within SUT from NIC(s) via
   VPP DUT to Container, then via "horizontal" memif to the next
   Container, and so on and so forth until the last Container, then
   back to VPP DUT and out thru NIC(s).

For each of the above topologies, VPP DUT is tested in a range of L2
or IPv4/IPv6 configurations depending on the test suite. Sample VPP DUT
"Chained" Container service topologies for 2-Node and 3-Node testbeds
with each SUT running N of Container instances is shown in the figures
below.

.. only:: latex

    .. raw:: latex

        \begin{figure}[H]
            \centering
                \graphicspath{{../_tmp/src/vpp_performance_tests/}}
                \includegraphics[width=0.90\textwidth]{logical-2n-container-memif}
                \label{fig:logical-2n-container-memif}
        \end{figure}

.. only:: html

    .. figure:: logical-2n-container-memif.svg
        :alt: logical-2n-container-memif
        :align: center


.. only:: latex

    .. raw:: latex

        \begin{figure}[H]
            \centering
                \graphicspath{{../_tmp/src/vpp_performance_tests/}}
                \includegraphics[width=0.90\textwidth]{logical-3n-container-memif}
                \label{fig:logical-3n-container-memif}
        \end{figure}

.. only:: html

    .. figure:: logical-3n-container-memif.svg
        :alt: logical-3n-container-memif
        :align: center

In "Chained" Container topologies, packets are switched by VPP DUT
multiple times: twice for a single Container, three times for two
Containers, N+1 times for N Containers. Hence the external throughput
rates measured by TG and listed in this report must be multiplied by N+1
to represent the actual VPP DUT aggregate packet forwarding rate.

For a "Parallel" and "Horizontal" service topologies packets are always
switched by VPP DUT twice per service chain.

Note that reported VPP DUT performance results are specific to the SUTs
tested. SUTs with other processor than the ones used in FD.io lab are
likely to yield different results. Similarly to NIC-to-NIC switching
topology, here one can also expect the forwarding performance to be
proportional to processor core frequency for the same processor
architecture, assuming processor is the only limiting factor. However
due to much higher dependency on intensive memory operations in
Container service chained topologies and sensitivity to Linux scheduler
settings and behaviour, this estimation may not always yield good enough
accuracy.

Performance Tests Coverage
--------------------------

Performance tests measure following metrics for tested VPP DUT
topologies and configurations:

- Packet Throughput: measured in accordance with :rfc:`2544`, using
  FD.io CSIT Multiple Loss Ratio search (MLRsearch), an optimized binary
  search algorithm, producing throughput at different Packet Loss Ratio
  (PLR) values:

  - Non Drop Rate (NDR): packet throughput at PLR=0%.
  - Partial Drop Rate (PDR): packet throughput at PLR=0.5%.

- One-Way Packet Latency: measured at different offered packet loads:

  - 90% of discovered PDR throughput.
  - 50% of discovered PDR throughput.
  - 10% of discovered PDR throughput.
  - Minimal offered load.

- Maximum Receive Rate (MRR): measure packet forwarding rate under the
  maximum load offered by traffic generator over a set trial duration,
  regardless of packet loss. Maximum load for specified Ethernet frame
  size is set to the bi-directional link rate, unless there is a known
  limitation preventing Traffic Generator from achieving the line rate.

.. todo::

   - Connections per second (CPS): TODO

|csit-release| includes following VPP data plane functionality
performance tested across a range of NIC drivers and NIC models:

+-----------------------+----------------------------------------------+
| Functionality         |  Description                                 |
+=======================+==============================================+
| ACL                   | L2 Bridge-Domain switching and               |
|                       | IPv4and IPv6 routing with iACL and oACL IP   |
|                       | address, MAC address and L4 port security.   |
+-----------------------+----------------------------------------------+
| ADL                   | IPv4 and IPv6 routing with ADL address       |
|                       | security.                                    |
+-----------------------+----------------------------------------------+
| GENEVE                | GENEVE tunnels for IPv4 routing.             |
+-----------------------+----------------------------------------------+
| IPv4                  | IPv4 routing.                                |
+-----------------------+----------------------------------------------+
| IPv6                  | IPv6 routing.                                |
+-----------------------+----------------------------------------------+
| IPv4 Scale            | IPv4 routing with 20k, 200k and 2M FIB       |
|                       | entries.                                     |
+-----------------------+----------------------------------------------+
| IPv6 Scale            | IPv6 routing with 20k, 200k and 2M FIB       |
|                       | entries.                                     |
+-----------------------+----------------------------------------------+
| IPSecAsyncHW          | IPSec encryption with AES-GCM, CBC-SHA-256   |
|                       | ciphers in async mode, in combination with   |
|                       | IPv4 routing. Intel QAT HW acceleration.     |
+-----------------------+----------------------------------------------+
| IPSecHW               | IPSec encryption with AES-GCM, CBC-SHA-256   |
|                       | ciphers, in combination with IPv4 routing.   |
|                       | Intel QAT HW acceleration.                   |
+-----------------------+----------------------------------------------+
| IPSec+LISP            | IPSec encryption with CBC-SHA1 ciphers, in   |
|                       | combination with LISP-GPE overlay tunneling  |
|                       | for IPv4-over-IPv4.                          |
+-----------------------+----------------------------------------------+
| IPSecSW               | IPSec encryption with AES-GCM, CBC-SHA-256   |
|                       | ciphers, in combination with IPv4 routing.   |
+-----------------------+----------------------------------------------+
| KVM VMs vhost-user    | Virtual topologies with service              |
|                       | chains of 1 VM using vhost-user              |
|                       | interfaces, with different VPP forwarding    |
|                       | modes incl. L2XC, L2BD, VXLAN with L2BD,     |
|                       | IPv4 routing.                                |
+-----------------------+----------------------------------------------+
| L2BD                  | L2 Bridge-Domain switching of untagged       |
|                       | Ethernet frames with MAC learning; disabled  |
|                       | MAC learning i.e. static MAC tests to be     |
|                       | added.                                       |
+-----------------------+----------------------------------------------+
| L2BD Scale            | L2 Bridge-Domain switching of untagged       |
|                       | Ethernet frames with MAC learning; disabled  |
|                       | MAC learning i.e. static MAC tests to be     |
|                       | added with 20k, 200k and 2M FIB entries.     |
+-----------------------+----------------------------------------------+
| L2XC                  | L2 Cross-Connect switching of untagged,      |
|                       | dot1q, dot1ad VLAN tagged Ethernet frames.   |
+-----------------------+----------------------------------------------+
| LISP                  | LISP overlay tunneling for IPv4-over-IPv4,   |
|                       | IPv6-over-IPv4, IPv6-over-IPv6,              |
|                       | IPv4-over-IPv6 in IPv4 and IPv6 routing      |
|                       | modes.                                       |
+-----------------------+----------------------------------------------+
| LXC/DRC Containers    | Container VPP memif virtual interface tests  |
| Memif                 | with different VPP forwarding modes incl.    |
|                       | L2XC, L2BD.                                  |
+-----------------------+----------------------------------------------+
| NAT44                 | (Source) Network Address Translation         |
|                       | deterministic mode and endpoint-dependent    |
|                       | mode tests with varying number of users and  |
|                       | ports per user for IPv4.                     |
+-----------------------+----------------------------------------------+
| QoS Policer           | Ingress packet rate measuring, marking and   |
|                       | limiting (IPv4).                             |
+-----------------------+----------------------------------------------+
| SRv6 Routing          | Segment Routing IPv6 tests.                  |
+-----------------------+----------------------------------------------+
| VPP TCP/IP stack      | Tests of VPP TCP/IP stack used with VPP      |
|                       | built-in HTTP server.                        |
+-----------------------+----------------------------------------------+
| VTS                   | Virtual Topology System use case tests       |
|                       | combining VXLAN overlay tunneling with L2BD, |
|                       | ACL and KVM VM vhost-user features.          |
+-----------------------+----------------------------------------------+
| VXLAN                 | VXLAN overlay tunnelling integration with    |
|                       | L2XC and L2BD.                               |
+-----------------------+----------------------------------------------+

Execution of performance tests takes time, especially the throughput
tests. Due to limited HW testbed resources available within FD.io labs
hosted by :abbr:`LF (Linux Foundation)`, the number of tests for some
NIC models has been limited to few baseline tests.

Performance Tests Naming
------------------------

FD.io |csit-release| follows a common structured naming convention for
all performance and system functional tests, introduced in CSIT-17.01.

The naming should be intuitive for majority of the tests. Complete
description of FD.io CSIT test naming convention is provided on
:ref:`csit_test_naming`.
span> else: self._passive = passive self._datapath = datapath # l2 ip4 ip6 self._collect = layer # l2 l3 l4 self._timeout = timeout self._mtu = mtu self._configured = False def add_vpp_config(self): self.enable_exporter() l2_flag = 0 l3_flag = 0 l4_flag = 0 if 'l2' in self._collect.lower(): l2_flag = (VppEnum.vl_api_flowprobe_record_flags_t. FLOWPROBE_RECORD_FLAG_L2) if 'l3' in self._collect.lower(): l3_flag = (VppEnum.vl_api_flowprobe_record_flags_t. FLOWPROBE_RECORD_FLAG_L3) if 'l4' in self._collect.lower(): l4_flag = (VppEnum.vl_api_flowprobe_record_flags_t. FLOWPROBE_RECORD_FLAG_L4) self._test.vapi.flowprobe_params( record_flags=(l2_flag | l3_flag | l4_flag), active_timer=self._active, passive_timer=self._passive) self.enable_flowprobe_feature() self._test.vapi.cli("ipfix flush") self._configured = True def remove_vpp_config(self): self.disable_exporter() self.disable_flowprobe_feature() self._test.vapi.cli("ipfix flush") self._configured = False def enable_exporter(self): self._test.vapi.set_ipfix_exporter( collector_address=self._test.pg0.remote_ip4, src_address=self._test.pg0.local_ip4, path_mtu=self._mtu, template_interval=self._timeout) def enable_flowprobe_feature(self): self._test.vapi.ppcli("flowprobe feature add-del %s %s" % (self._intf, self._datapath)) def disable_exporter(self): self._test.vapi.cli("set ipfix exporter collector 0.0.0.0") def disable_flowprobe_feature(self): self._test.vapi.cli("flowprobe feature add-del %s %s disable" % (self._intf, self._datapath)) def object_id(self): return "ipfix-collector-%s-%s" % (self._src, self.dst) def query_vpp_config(self): return self._configured def verify_templates(self, decoder=None, timeout=1, count=3): templates = [] p = self._test.wait_for_cflow_packet(self._test.collector, 2, timeout) self._test.assertTrue(p.haslayer(IPFIX)) if decoder is not None and p.haslayer(Template): templates.append(p[Template].templateID) decoder.add_template(p.getlayer(Template)) if count > 1: p = self._test.wait_for_cflow_packet(self._test.collector, 2) self._test.assertTrue(p.haslayer(IPFIX)) if decoder is not None and p.haslayer(Template): templates.append(p[Template].templateID) decoder.add_template(p.getlayer(Template)) if count > 2: p = self._test.wait_for_cflow_packet(self._test.collector, 2) self._test.assertTrue(p.haslayer(IPFIX)) if decoder is not None and p.haslayer(Template): templates.append(p[Template].templateID) decoder.add_template(p.getlayer(Template)) return templates class MethodHolder(VppTestCase): """ Flow-per-packet plugin: test L2, IP4, IP6 reporting """ # Test variables debug_print = False max_number_of_packets = 10 pkts = [] @classmethod def setUpClass(cls): """ Perform standard class setup (defined by class method setUpClass in class VppTestCase) before running the test case, set test case related variables and configure VPP. """ super(MethodHolder, cls).setUpClass() try: # Create pg interfaces cls.create_pg_interfaces(range(9)) # Packet sizes cls.pg_if_packet_sizes = [64, 512, 1518, 9018] # Create BD with MAC learning and unknown unicast flooding disabled # and put interfaces to this BD cls.vapi.bridge_domain_add_del(bd_id=1, uu_flood=1, learn=1) cls.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=cls.pg1._sw_if_index, bd_id=1) cls.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=cls.pg2._sw_if_index, bd_id=1) # Set up all interfaces for i in cls.pg_interfaces: i.admin_up() cls.pg0.config_ip4() cls.pg0.configure_ipv4_neighbors() cls.collector = cls.pg0 cls.pg1.config_ip4() cls.pg1.resolve_arp() cls.pg2.config_ip4() cls.pg2.resolve_arp() cls.pg3.config_ip4() cls.pg3.resolve_arp() cls.pg4.config_ip4() cls.pg4.resolve_arp() cls.pg7.config_ip4() cls.pg8.config_ip4() cls.pg8.configure_ipv4_neighbors() cls.pg5.config_ip6() cls.pg5.resolve_ndp() cls.pg5.disable_ipv6_ra() cls.pg6.config_ip6() cls.pg6.resolve_ndp() cls.pg6.disable_ipv6_ra() except Exception: super(MethodHolder, cls).tearDownClass() raise @classmethod def tearDownClass(cls): super(MethodHolder, cls).tearDownClass() def create_stream(self, src_if=None, dst_if=None, packets=None, size=None, ip_ver='v4'): """Create a packet stream to tickle the plugin :param VppInterface src_if: Source interface for packet stream :param VppInterface src_if: Dst interface for packet stream """ if src_if is None: src_if = self.pg1 if dst_if is None: dst_if = self.pg2 self.pkts = [] if packets is None: packets = random.randint(1, self.max_number_of_packets) pkt_size = size for p in range(0, packets): if size is None: pkt_size = random.choice(self.pg_if_packet_sizes) info = self.create_packet_info(src_if, dst_if) payload = self.info_to_payload(info) p = Ether(src=src_if.remote_mac, dst=src_if.local_mac) if ip_ver == 'v4': p /= IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) else: p /= IPv6(src=src_if.remote_ip6, dst=dst_if.remote_ip6) p /= UDP(sport=1234, dport=4321) p /= Raw(payload) info.data = p.copy() self.extend_packet(p, pkt_size) self.pkts.append(p) def verify_cflow_data(self, decoder, capture, cflow): octets = 0 packets = 0 for p in capture: octets += p[IP].len packets += 1 if cflow.haslayer(Data): data = decoder.decode_data_set(cflow.getlayer(Set)) for record in data: self.assertEqual(int(binascii.hexlify(record[1]), 16), octets) self.assertEqual(int(binascii.hexlify(record[2]), 16), packets) def send_packets(self, src_if=None, dst_if=None): if src_if is None: src_if = self.pg1 if dst_if is None: dst_if = self.pg2 self.pg_enable_capture([dst_if]) src_if.add_stream(self.pkts) self.pg_start() return dst_if.get_capture(len(self.pkts)) def verify_cflow_data_detail(self, decoder, capture, cflow, data_set={1: 'octets', 2: 'packets'}, ip_ver='v4'): if self.debug_print: print(capture[0].show()) if cflow.haslayer(Data): data = decoder.decode_data_set(cflow.getlayer(Set)) if self.debug_print: print(data) if ip_ver == 'v4': ip_layer = capture[0][IP] else: ip_layer = capture[0][IPv6] if data_set is not None: for record in data: # skip flow if ingress/egress interface is 0 if int(binascii.hexlify(record[10]), 16) == 0: continue if int(binascii.hexlify(record[14]), 16) == 0: continue for field in data_set: if field not in record.keys(): continue value = data_set[field] if value == 'octets': value = ip_layer.len if ip_ver == 'v6': value += 40 # ??? is this correct elif value == 'packets': value = 1 elif value == 'src_ip': if ip_ver == 'v4': ip = socket.inet_pton(socket.AF_INET, ip_layer.src) else: ip = socket.inet_pton(socket.AF_INET6, ip_layer.src) value = int(binascii.hexlify(ip), 16) elif value == 'dst_ip': if ip_ver == 'v4': ip = socket.inet_pton(socket.AF_INET, ip_layer.dst) else: ip = socket.inet_pton(socket.AF_INET6, ip_layer.dst) value = int(binascii.hexlify(ip), 16) elif value == 'sport': value = int(capture[0][UDP].sport) elif value == 'dport': value = int(capture[0][UDP].dport) self.assertEqual(int(binascii.hexlify( record[field]), 16), value) def verify_cflow_data_notimer(self, decoder, capture, cflows): idx = 0 for cflow in cflows: if cflow.haslayer(Data): data = decoder.decode_data_set(cflow.getlayer(Set)) else: raise Exception("No CFLOW data") for rec in data: p = capture[idx] idx += 1 self.assertEqual(p[IP].len, int( binascii.hexlify(rec[1]), 16)) self.assertEqual(1, int( binascii.hexlify(rec[2]), 16)) self.assertEqual(len(capture), idx) def wait_for_cflow_packet(self, collector_intf, set_id=2, timeout=1, expected=True): """ wait for CFLOW packet and verify its correctness :param timeout: how long to wait :returns: tuple (packet, time spent waiting for packet) """ self.logger.info("IPFIX: Waiting for CFLOW packet") deadline = time.time() + timeout counter = 0 # self.logger.debug(self.vapi.ppcli("show flow table")) while True: counter += 1 # sanity check self.assert_in_range(counter, 0, 100, "number of packets ignored") time_left = deadline - time.time() try: if time_left < 0 and expected: # self.logger.debug(self.vapi.ppcli("show flow table")) raise CaptureTimeoutError( "Packet did not arrive within timeout") p = collector_intf.wait_for_packet(timeout=time_left) except CaptureTimeoutError: if expected: # self.logger.debug(self.vapi.ppcli("show flow table")) raise CaptureTimeoutError( "Packet did not arrive within timeout") else: return if not expected: raise CaptureTimeoutError("Packet arrived even not expected") self.assertEqual(p[Set].setID, set_id) # self.logger.debug(self.vapi.ppcli("show flow table")) self.logger.debug(ppp("IPFIX: Got packet:", p)) break return p class Flowprobe(MethodHolder): """Template verification, timer tests""" @classmethod def setUpClass(cls): super(Flowprobe, cls).setUpClass() @classmethod def tearDownClass(cls): super(Flowprobe, cls).tearDownClass() def test_0001(self): """ timer less than template timeout""" self.logger.info("FFP_TEST_START_0001") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, active=2) ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder) self.create_stream(packets=1) self.send_packets() capture = self.pg2.get_capture(1) # make sure the one packet we expect actually showed up cflow = self.wait_for_cflow_packet(self.collector, templates[1], 15) self.verify_cflow_data(ipfix_decoder, capture, cflow) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0001") def test_0002(self): """ timer greater than template timeout""" self.logger.info("FFP_TEST_START_0002") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, timeout=3, active=4) ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately ipfix.verify_templates() self.create_stream(packets=2) self.send_packets() capture = self.pg2.get_capture(2) # next set of template packet should arrive after 20 seconds # template packet should arrive within 20 s templates = ipfix.verify_templates(ipfix_decoder, timeout=5) # make sure the one packet we expect actually showed up cflow = self.wait_for_cflow_packet(self.collector, templates[1], 15) self.verify_cflow_data(ipfix_decoder, capture, cflow) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0002") def test_cflow_packet(self): """verify cflow packet fields""" self.logger.info("FFP_TEST_START_0000") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, intf='pg8', datapath="ip4", layer='l2 l3 l4', active=2) ipfix.add_vpp_config() route_9001 = VppIpRoute(self, "9.0.0.0", 24, [VppRoutePath(self.pg8._remote_hosts[0].ip4, self.pg8.sw_if_index)]) route_9001.add_vpp_config() ipfix_decoder = IPFIXDecoder() templates = ipfix.verify_templates(ipfix_decoder, count=1) self.pkts = [(Ether(dst=self.pg7.local_mac, src=self.pg7.remote_mac) / IP(src=self.pg7.remote_ip4, dst="9.0.0.100") / TCP(sport=1234, dport=4321, flags=80) / Raw(b'\xa5' * 100))] nowUTC = int(time.time()) nowUNIX = nowUTC+2208988800 self.send_packets(src_if=self.pg7, dst_if=self.pg8) cflow = self.wait_for_cflow_packet(self.collector, templates[0], 10) self.collector.get_capture(2) if cflow[0].haslayer(IPFIX): self.assertEqual(cflow[IPFIX].version, 10) self.assertEqual(cflow[IPFIX].observationDomainID, 1) self.assertEqual(cflow[IPFIX].sequenceNumber, 0) self.assertAlmostEqual(cflow[IPFIX].exportTime, nowUTC, delta=5) if cflow.haslayer(Data): record = ipfix_decoder.decode_data_set(cflow[0].getlayer(Set))[0] # ingress interface self.assertEqual(int(binascii.hexlify(record[10]), 16), 8) # egress interface self.assertEqual(int(binascii.hexlify(record[14]), 16), 9) # packets self.assertEqual(int(binascii.hexlify(record[2]), 16), 1) # src mac self.assertEqual(mac_ntop(record[56]), self.pg8.local_mac) # dst mac self.assertEqual(mac_ntop(record[80]), self.pg8.remote_mac) flowTimestamp = int(binascii.hexlify(record[156]), 16) >> 32 # flow start timestamp self.assertAlmostEqual(flowTimestamp, nowUNIX, delta=1) flowTimestamp = int(binascii.hexlify(record[157]), 16) >> 32 # flow end timestamp self.assertAlmostEqual(flowTimestamp, nowUNIX, delta=1) # ethernet type self.assertEqual(int(binascii.hexlify(record[256]), 16), 8) # src ip self.assertEqual(inet_ntop(socket.AF_INET, record[8]), self.pg7.remote_ip4) # dst ip self.assertEqual(inet_ntop(socket.AF_INET, record[12]), "9.0.0.100") # protocol (TCP) self.assertEqual(int(binascii.hexlify(record[4]), 16), 6) # src port self.assertEqual(int(binascii.hexlify(record[7]), 16), 1234) # dst port self.assertEqual(int(binascii.hexlify(record[11]), 16), 4321) # tcp flags self.assertEqual(int(binascii.hexlify(record[6]), 16), 80) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0000") class Datapath(MethodHolder): """collect information on Ethernet, IP4 and IP6 datapath (no timers)""" @classmethod def setUpClass(cls): super(Datapath, cls).setUpClass() @classmethod def tearDownClass(cls): super(Datapath, cls).tearDownClass() def test_templatesL2(self): """ verify template on L2 datapath""" self.logger.info("FFP_TEST_START_0000") self.pg_enable_capture(self.pg_interfaces) ipfix = VppCFLOW(test=self, layer='l2') ipfix.add_vpp_config() # template packet should arrive immediately self.vapi.ipfix_flush() ipfix.verify_templates(timeout=3, count=1) self.collector.get_capture(1) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0000") def test_L2onL2(self): """ L2 data on L2 datapath""" self.logger.info("FFP_TEST_START_0001") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, layer='l2') ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder, count=1) self.create_stream(packets=1) capture = self.send_packets() # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() cflow = self.wait_for_cflow_packet(self.collector, templates[0]) self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, {2: 'packets', 256: 8}) self.collector.get_capture(2) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0001") def test_L3onL2(self): """ L3 data on L2 datapath""" self.logger.info("FFP_TEST_START_0002") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, layer='l3') ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder, count=2) self.create_stream(packets=1) capture = self.send_packets() # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() cflow = self.wait_for_cflow_packet(self.collector, templates[0]) self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, {2: 'packets', 4: 17, 8: 'src_ip', 12: 'dst_ip'}) self.collector.get_capture(3) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0002") def test_L4onL2(self): """ L4 data on L2 datapath""" self.logger.info("FFP_TEST_START_0003") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, layer='l4') ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder, count=2) self.create_stream(packets=1) capture = self.send_packets() # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() cflow = self.wait_for_cflow_packet(self.collector, templates[0]) self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, {2: 'packets', 7: 'sport', 11: 'dport'}) self.collector.get_capture(3) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0003") def test_templatesIp4(self): """ verify templates on IP4 datapath""" self.logger.info("FFP_TEST_START_0000") self.pg_enable_capture(self.pg_interfaces) ipfix = VppCFLOW(test=self, datapath='ip4') ipfix.add_vpp_config() # template packet should arrive immediately self.vapi.ipfix_flush() ipfix.verify_templates(timeout=3, count=1) self.collector.get_capture(1) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0000") def test_L2onIP4(self): """ L2 data on IP4 datapath""" self.logger.info("FFP_TEST_START_0001") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, intf='pg4', layer='l2', datapath='ip4') ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder, count=1) self.create_stream(src_if=self.pg3, dst_if=self.pg4, packets=1) capture = self.send_packets(src_if=self.pg3, dst_if=self.pg4) # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() cflow = self.wait_for_cflow_packet(self.collector, templates[0]) self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, {2: 'packets', 256: 8}) # expected two templates and one cflow packet self.collector.get_capture(2) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0001") def test_L3onIP4(self): """ L3 data on IP4 datapath""" self.logger.info("FFP_TEST_START_0002") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, intf='pg4', layer='l3', datapath='ip4') ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder, count=1) self.create_stream(src_if=self.pg3, dst_if=self.pg4, packets=1) capture = self.send_packets(src_if=self.pg3, dst_if=self.pg4) # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() cflow = self.wait_for_cflow_packet(self.collector, templates[0]) self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, {1: 'octets', 2: 'packets', 8: 'src_ip', 12: 'dst_ip'}) # expected two templates and one cflow packet self.collector.get_capture(2) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0002") def test_L4onIP4(self): """ L4 data on IP4 datapath""" self.logger.info("FFP_TEST_START_0003") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, intf='pg4', layer='l4', datapath='ip4') ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder, count=1) self.create_stream(src_if=self.pg3, dst_if=self.pg4, packets=1) capture = self.send_packets(src_if=self.pg3, dst_if=self.pg4) # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() cflow = self.wait_for_cflow_packet(self.collector, templates[0]) self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, {2: 'packets', 7: 'sport', 11: 'dport'}) # expected two templates and one cflow packet self.collector.get_capture(2) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0003") def test_templatesIP6(self): """ verify templates on IP6 datapath""" self.logger.info("FFP_TEST_START_0000") self.pg_enable_capture(self.pg_interfaces) ipfix = VppCFLOW(test=self, datapath='ip6') ipfix.add_vpp_config() # template packet should arrive immediately ipfix.verify_templates(count=1) self.collector.get_capture(1) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0000") def test_L2onIP6(self): """ L2 data on IP6 datapath""" self.logger.info("FFP_TEST_START_0001") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, intf='pg6', layer='l2', datapath='ip6') ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder, count=1) self.create_stream(src_if=self.pg5, dst_if=self.pg6, packets=1, ip_ver='IPv6') capture = self.send_packets(src_if=self.pg5, dst_if=self.pg6) # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() cflow = self.wait_for_cflow_packet(self.collector, templates[0]) self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, {2: 'packets', 256: 56710}, ip_ver='v6') # expected two templates and one cflow packet self.collector.get_capture(2) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0001") def test_L3onIP6(self): """ L3 data on IP6 datapath""" self.logger.info("FFP_TEST_START_0002") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, intf='pg6', layer='l3', datapath='ip6') ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder, count=1) self.create_stream(src_if=self.pg5, dst_if=self.pg6, packets=1, ip_ver='IPv6') capture = self.send_packets(src_if=self.pg5, dst_if=self.pg6) # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() cflow = self.wait_for_cflow_packet(self.collector, templates[0]) self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, {2: 'packets', 27: 'src_ip', 28: 'dst_ip'}, ip_ver='v6') # expected two templates and one cflow packet self.collector.get_capture(2) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0002") def test_L4onIP6(self): """ L4 data on IP6 datapath""" self.logger.info("FFP_TEST_START_0003") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, intf='pg6', layer='l4', datapath='ip6') ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder, count=1) self.create_stream(src_if=self.pg5, dst_if=self.pg6, packets=1, ip_ver='IPv6') capture = self.send_packets(src_if=self.pg5, dst_if=self.pg6) # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() cflow = self.wait_for_cflow_packet(self.collector, templates[0]) self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, {2: 'packets', 7: 'sport', 11: 'dport'}, ip_ver='v6') # expected two templates and one cflow packet self.collector.get_capture(2) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0003") def test_0001(self): """ no timers, one CFLOW packet, 9 Flows inside""" self.logger.info("FFP_TEST_START_0001") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self) ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder) self.create_stream(packets=9) capture = self.send_packets() # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() cflow = self.wait_for_cflow_packet(self.collector, templates[1]) self.verify_cflow_data_notimer(ipfix_decoder, capture, [cflow]) self.collector.get_capture(4) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0001") def test_0002(self): """ no timers, two CFLOW packets (mtu=256), 3 Flows in each""" self.logger.info("FFP_TEST_START_0002") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self, mtu=256) ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately self.vapi.ipfix_flush() templates = ipfix.verify_templates(ipfix_decoder) self.create_stream(packets=6) capture = self.send_packets() # make sure the one packet we expect actually showed up cflows = [] self.vapi.ipfix_flush() cflows.append(self.wait_for_cflow_packet(self.collector, templates[1])) cflows.append(self.wait_for_cflow_packet(self.collector, templates[1])) self.verify_cflow_data_notimer(ipfix_decoder, capture, cflows) self.collector.get_capture(5) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0002") @unittest.skipUnless(running_extended_tests, "part of extended tests") class DisableIPFIX(MethodHolder): """Disable IPFIX""" @classmethod def setUpClass(cls): super(DisableIPFIX, cls).setUpClass() @classmethod def tearDownClass(cls): super(DisableIPFIX, cls).tearDownClass() def test_0001(self): """ disable IPFIX after first packets""" self.logger.info("FFP_TEST_START_0001") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self) ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder) self.create_stream() self.send_packets() # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() self.wait_for_cflow_packet(self.collector, templates[1]) self.collector.get_capture(4) # disable IPFIX ipfix.disable_exporter() self.pg_enable_capture([self.collector]) self.send_packets() # make sure no one packet arrived in 1 minute self.vapi.ipfix_flush() self.wait_for_cflow_packet(self.collector, templates[1], expected=False) self.collector.get_capture(0) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0001") @unittest.skipUnless(running_extended_tests, "part of extended tests") class ReenableIPFIX(MethodHolder): """Re-enable IPFIX""" @classmethod def setUpClass(cls): super(ReenableIPFIX, cls).setUpClass() @classmethod def tearDownClass(cls): super(ReenableIPFIX, cls).tearDownClass() def test_0011(self): """ disable IPFIX after first packets and re-enable after few packets """ self.logger.info("FFP_TEST_START_0001") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self) ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder) self.create_stream(packets=5) self.send_packets() # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() self.wait_for_cflow_packet(self.collector, templates[1]) self.collector.get_capture(4) # disable IPFIX ipfix.disable_exporter() self.vapi.ipfix_flush() self.pg_enable_capture([self.collector]) self.send_packets() # make sure no one packet arrived in active timer span self.vapi.ipfix_flush() self.wait_for_cflow_packet(self.collector, templates[1], expected=False) self.collector.get_capture(0) self.pg2.get_capture(5) # enable IPFIX ipfix.enable_exporter() capture = self.collector.get_capture(4) nr_templates = 0 nr_data = 0 for p in capture: self.assertTrue(p.haslayer(IPFIX)) if p.haslayer(Template): nr_templates += 1 self.assertTrue(nr_templates, 3) for p in capture: self.assertTrue(p.haslayer(IPFIX)) if p.haslayer(Data): nr_data += 1 self.assertTrue(nr_templates, 1) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0001") @unittest.skipUnless(running_extended_tests, "part of extended tests") class DisableFP(MethodHolder): """Disable Flowprobe feature""" @classmethod def setUpClass(cls): super(DisableFP, cls).setUpClass() @classmethod def tearDownClass(cls): super(DisableFP, cls).tearDownClass() def test_0001(self): """ disable flowprobe feature after first packets""" self.logger.info("FFP_TEST_START_0001") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self) ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately templates = ipfix.verify_templates(ipfix_decoder) self.create_stream() self.send_packets() # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() self.wait_for_cflow_packet(self.collector, templates[1]) self.collector.get_capture(4) # disable IPFIX ipfix.disable_flowprobe_feature() self.pg_enable_capture([self.collector]) self.send_packets() # make sure no one packet arrived in active timer span self.vapi.ipfix_flush() self.wait_for_cflow_packet(self.collector, templates[1], expected=False) self.collector.get_capture(0) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0001") @unittest.skipUnless(running_extended_tests, "part of extended tests") class ReenableFP(MethodHolder): """Re-enable Flowprobe feature""" @classmethod def setUpClass(cls): super(ReenableFP, cls).setUpClass() @classmethod def tearDownClass(cls): super(ReenableFP, cls).tearDownClass() def test_0001(self): """ disable flowprobe feature after first packets and re-enable after few packets """ self.logger.info("FFP_TEST_START_0001") self.pg_enable_capture(self.pg_interfaces) self.pkts = [] ipfix = VppCFLOW(test=self) ipfix.add_vpp_config() ipfix_decoder = IPFIXDecoder() # template packet should arrive immediately self.vapi.ipfix_flush() templates = ipfix.verify_templates(ipfix_decoder, timeout=3) self.create_stream() self.send_packets() # make sure the one packet we expect actually showed up self.vapi.ipfix_flush() self.wait_for_cflow_packet(self.collector, templates[1], 5) self.collector.get_capture(4) # disable FPP feature ipfix.disable_flowprobe_feature() self.pg_enable_capture([self.collector]) self.send_packets() # make sure no one packet arrived in active timer span self.vapi.ipfix_flush() self.wait_for_cflow_packet(self.collector, templates[1], 5, expected=False) self.collector.get_capture(0) # enable FPP feature ipfix.enable_flowprobe_feature() self.vapi.ipfix_flush() templates = ipfix.verify_templates(ipfix_decoder, timeout=3) self.send_packets() # make sure the next packets (templates and data) we expect actually # showed up self.vapi.ipfix_flush() self.wait_for_cflow_packet(self.collector, templates[1], 5) self.collector.get_capture(4) ipfix.remove_vpp_config() self.logger.info("FFP_TEST_FINISH_0001") if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)