tests: replace pycodestyle with black
[vpp.git] / test / test_ipsec_spd_flow_cache_output.py
1 import socket
2 import unittest
3
4 from util import ppp
5 from framework import VppTestRunner
6 from template_ipsec import SpdFlowCacheTemplate
7
8
9 class SpdFlowCacheOutbound(SpdFlowCacheTemplate):
10     # Override setUpConstants to enable outbound flow cache in config
11     @classmethod
12     def setUpConstants(cls):
13         super(SpdFlowCacheOutbound, cls).setUpConstants()
14         cls.vpp_cmdline.extend(["ipsec", "{", "ipv4-outbound-spd-flow-cache on", "}"])
15         cls.logger.info("VPP modified cmdline is %s" % " ".join(cls.vpp_cmdline))
16
17
18 class IPSec4SpdTestCaseAdd(SpdFlowCacheOutbound):
19     """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
20         (add rule)"""
21
22     def test_ipsec_spd_outbound_add(self):
23         # In this test case, packets in IPv4 FWD path are configured
24         # to go through IPSec outbound SPD policy lookup.
25         # 2 SPD rules (1 HIGH and 1 LOW) are added.
26         # High priority rule action is set to BYPASS.
27         # Low priority rule action is set to DISCARD.
28         # Traffic sent on pg0 interface should match high priority
29         # rule and should be sent out on pg1 interface.
30         self.create_interfaces(2)
31         pkt_count = 5
32         self.spd_create_and_intf_add(1, [self.pg1])
33         policy_0 = self.spd_add_rem_policy(  # outbound, priority 10
34             1,
35             self.pg0,
36             self.pg1,
37             socket.IPPROTO_UDP,
38             is_out=1,
39             priority=10,
40             policy_type="bypass",
41         )
42         policy_1 = self.spd_add_rem_policy(  # outbound, priority 5
43             1,
44             self.pg0,
45             self.pg1,
46             socket.IPPROTO_UDP,
47             is_out=1,
48             priority=5,
49             policy_type="discard",
50         )
51
52         # check flow cache is empty before sending traffic
53         self.verify_num_outbound_flow_cache_entries(0)
54
55         # create the packet stream
56         packets = self.create_stream(self.pg0, self.pg1, pkt_count)
57         # add the stream to the source interface + enable capture
58         self.pg0.add_stream(packets)
59         self.pg0.enable_capture()
60         self.pg1.enable_capture()
61         # start the packet generator
62         self.pg_start()
63         # get capture
64         capture = self.pg1.get_capture()
65         for packet in capture:
66             try:
67                 self.logger.debug(ppp("SPD - Got packet:", packet))
68             except Exception:
69                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
70                 raise
71         self.logger.debug("SPD: Num packets: %s", len(capture.res))
72
73         # assert nothing captured on pg0
74         self.pg0.assert_nothing_captured()
75         # verify captured packets
76         self.verify_capture(self.pg0, self.pg1, capture)
77         # verify all policies matched the expected number of times
78         self.verify_policy_match(pkt_count, policy_0)
79         self.verify_policy_match(0, policy_1)
80         # check policy in SPD has been cached after traffic
81         # matched BYPASS rule in SPD
82         self.verify_num_outbound_flow_cache_entries(1)
83
84
85 class IPSec4SpdTestCaseRemove(SpdFlowCacheOutbound):
86     """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
87         (remove rule)"""
88
89     def test_ipsec_spd_outbound_remove(self):
90         # In this test case, packets in IPv4 FWD path are configured
91         # to go through IPSec outbound SPD policy lookup.
92         # 2 SPD rules (1 HIGH and 1 LOW) are added.
93         # High priority rule action is set to BYPASS.
94         # Low priority rule action is set to DISCARD.
95         # High priority rule is then removed.
96         # Traffic sent on pg0 interface should match low priority
97         # rule and should be discarded after SPD lookup.
98         self.create_interfaces(2)
99         pkt_count = 5
100         self.spd_create_and_intf_add(1, [self.pg1])
101         policy_0 = self.spd_add_rem_policy(  # outbound, priority 10
102             1,
103             self.pg0,
104             self.pg1,
105             socket.IPPROTO_UDP,
106             is_out=1,
107             priority=10,
108             policy_type="bypass",
109         )
110         policy_1 = self.spd_add_rem_policy(  # outbound, priority 5
111             1,
112             self.pg0,
113             self.pg1,
114             socket.IPPROTO_UDP,
115             is_out=1,
116             priority=5,
117             policy_type="discard",
118         )
119
120         # check flow cache is empty before sending traffic
121         self.verify_num_outbound_flow_cache_entries(0)
122
123         # create the packet stream
124         packets = self.create_stream(self.pg0, self.pg1, pkt_count)
125         # add the stream to the source interface + enable capture
126         self.pg0.add_stream(packets)
127         self.pg0.enable_capture()
128         self.pg1.enable_capture()
129         # start the packet generator
130         self.pg_start()
131         # get capture
132         capture = self.pg1.get_capture()
133         for packet in capture:
134             try:
135                 self.logger.debug(ppp("SPD - Got packet:", packet))
136             except Exception:
137                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
138                 raise
139
140         # assert nothing captured on pg0
141         self.pg0.assert_nothing_captured()
142         # verify capture on pg1
143         self.logger.debug("SPD: Num packets: %s", len(capture.res))
144         self.verify_capture(self.pg0, self.pg1, capture)
145         # verify all policies matched the expected number of times
146         self.verify_policy_match(pkt_count, policy_0)
147         self.verify_policy_match(0, policy_1)
148         # check policy in SPD has been cached after traffic
149         # matched BYPASS rule in SPD
150         self.verify_num_outbound_flow_cache_entries(1)
151
152         # now remove the bypass rule
153         self.spd_add_rem_policy(  # outbound, priority 10
154             1,
155             self.pg0,
156             self.pg1,
157             socket.IPPROTO_UDP,
158             is_out=1,
159             priority=10,
160             policy_type="bypass",
161             remove=True,
162         )
163         # verify flow cache counter has been reset by rule removal
164         self.verify_num_outbound_flow_cache_entries(0)
165
166         # resend the same packets
167         self.pg0.add_stream(packets)
168         self.pg0.enable_capture()  # flush the old captures
169         self.pg1.enable_capture()
170         self.pg_start()
171         # assert nothing captured on pg0
172         self.pg0.assert_nothing_captured()
173         # all packets will be dropped by SPD rule
174         self.pg1.assert_nothing_captured()
175         # verify all policies matched the expected number of times
176         self.verify_policy_match(pkt_count, policy_0)
177         self.verify_policy_match(pkt_count, policy_1)
178         # previous stale entry in flow cache should have been overwritten,
179         # with one active entry
180         self.verify_num_outbound_flow_cache_entries(1)
181
182
183 class IPSec4SpdTestCaseReadd(SpdFlowCacheOutbound):
184     """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
185         (add, remove, re-add)"""
186
187     def test_ipsec_spd_outbound_readd(self):
188         # In this test case, packets in IPv4 FWD path are configured
189         # to go through IPSec outbound SPD policy lookup.
190         # 2 SPD rules (1 HIGH and 1 LOW) are added.
191         # High priority rule action is set to BYPASS.
192         # Low priority rule action is set to DISCARD.
193         # Traffic sent on pg0 interface should match high priority
194         # rule and should be sent out on pg1 interface.
195         # High priority rule is then removed.
196         # Traffic sent on pg0 interface should match low priority
197         # rule and should be discarded after SPD lookup.
198         # Readd high priority rule.
199         # Traffic sent on pg0 interface should match high priority
200         # rule and should be sent out on pg1 interface.
201         self.create_interfaces(2)
202         pkt_count = 5
203         self.spd_create_and_intf_add(1, [self.pg1])
204         policy_0 = self.spd_add_rem_policy(  # outbound, priority 10
205             1,
206             self.pg0,
207             self.pg1,
208             socket.IPPROTO_UDP,
209             is_out=1,
210             priority=10,
211             policy_type="bypass",
212         )
213         policy_1 = self.spd_add_rem_policy(  # outbound, priority 5
214             1,
215             self.pg0,
216             self.pg1,
217             socket.IPPROTO_UDP,
218             is_out=1,
219             priority=5,
220             policy_type="discard",
221         )
222
223         # check flow cache is empty before sending traffic
224         self.verify_num_outbound_flow_cache_entries(0)
225
226         # create the packet stream
227         packets = self.create_stream(self.pg0, self.pg1, pkt_count)
228         # add the stream to the source interface + enable capture
229         self.pg0.add_stream(packets)
230         self.pg0.enable_capture()
231         self.pg1.enable_capture()
232         # start the packet generator
233         self.pg_start()
234         # get capture
235         capture = self.pg1.get_capture()
236         for packet in capture:
237             try:
238                 self.logger.debug(ppp("SPD - Got packet:", packet))
239             except Exception:
240                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
241                 raise
242         self.logger.debug("SPD: Num packets: %s", len(capture.res))
243
244         # assert nothing captured on pg0
245         self.pg0.assert_nothing_captured()
246         # verify capture on pg1
247         self.verify_capture(self.pg0, self.pg1, capture)
248         # verify all policies matched the expected number of times
249         self.verify_policy_match(pkt_count, policy_0)
250         self.verify_policy_match(0, policy_1)
251         # check policy in SPD has been cached after traffic
252         # matched BYPASS rule in SPD
253         self.verify_num_outbound_flow_cache_entries(1)
254
255         # now remove the bypass rule, leaving only the discard rule
256         self.spd_add_rem_policy(  # outbound, priority 10
257             1,
258             self.pg0,
259             self.pg1,
260             socket.IPPROTO_UDP,
261             is_out=1,
262             priority=10,
263             policy_type="bypass",
264             remove=True,
265         )
266         # verify flow cache counter has been reset by rule removal
267         self.verify_num_outbound_flow_cache_entries(0)
268
269         # resend the same packets
270         self.pg0.add_stream(packets)
271         self.pg0.enable_capture()  # flush the old captures
272         self.pg1.enable_capture()
273         self.pg_start()
274
275         # assert nothing captured on pg0
276         self.pg0.assert_nothing_captured()
277         # all packets will be dropped by SPD rule
278         self.pg1.assert_nothing_captured()
279         # verify all policies matched the expected number of times
280         self.verify_policy_match(pkt_count, policy_0)
281         self.verify_policy_match(pkt_count, policy_1)
282         # previous stale entry in flow cache should have been overwritten
283         self.verify_num_outbound_flow_cache_entries(1)
284
285         # now readd the bypass rule
286         policy_0 = self.spd_add_rem_policy(  # outbound, priority 10
287             1,
288             self.pg0,
289             self.pg1,
290             socket.IPPROTO_UDP,
291             is_out=1,
292             priority=10,
293             policy_type="bypass",
294         )
295         # verify flow cache counter has been reset by rule addition
296         self.verify_num_outbound_flow_cache_entries(0)
297
298         # resend the same packets
299         self.pg0.add_stream(packets)
300         self.pg0.enable_capture()  # flush the old captures
301         self.pg1.enable_capture()
302         self.pg_start()
303
304         # get capture
305         capture = self.pg1.get_capture(pkt_count)
306         for packet in capture:
307             try:
308                 self.logger.debug(ppp("SPD - Got packet:", packet))
309             except Exception:
310                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
311                 raise
312         self.logger.debug("SPD: Num packets: %s", len(capture.res))
313
314         # assert nothing captured on pg0
315         self.pg0.assert_nothing_captured()
316         # verify captured packets
317         self.verify_capture(self.pg0, self.pg1, capture)
318         # verify all policies matched the expected number of times
319         self.verify_policy_match(pkt_count, policy_0)
320         self.verify_policy_match(pkt_count, policy_1)
321         # previous stale entry in flow cache should have been overwritten
322         self.verify_num_outbound_flow_cache_entries(1)
323
324
325 class IPSec4SpdTestCaseMultiple(SpdFlowCacheOutbound):
326     """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
327         (multiple interfaces, multiple rules)"""
328
329     def test_ipsec_spd_outbound_multiple(self):
330         # In this test case, packets in IPv4 FWD path are configured to go
331         # through IPSec outbound SPD policy lookup.
332         # Multiples rules on multiple interfaces are tested at the same time.
333         # 3x interfaces are configured, binding the same SPD to each.
334         # Each interface has 2 SPD rules (1 BYPASS and 1 DISCARD).
335         # On pg0 & pg1, the BYPASS rule is HIGH priority
336         # On pg2, the DISCARD rule is HIGH priority
337         # Traffic should be received on pg0 & pg1 and dropped on pg2.
338         self.create_interfaces(3)
339         pkt_count = 5
340         # bind SPD to all interfaces
341         self.spd_create_and_intf_add(1, self.pg_interfaces)
342         # add rules on all interfaces
343         policy_01 = self.spd_add_rem_policy(  # outbound, priority 10
344             1,
345             self.pg0,
346             self.pg1,
347             socket.IPPROTO_UDP,
348             is_out=1,
349             priority=10,
350             policy_type="bypass",
351         )
352         policy_02 = self.spd_add_rem_policy(  # outbound, priority 5
353             1,
354             self.pg0,
355             self.pg1,
356             socket.IPPROTO_UDP,
357             is_out=1,
358             priority=5,
359             policy_type="discard",
360         )
361
362         policy_11 = self.spd_add_rem_policy(  # outbound, priority 10
363             1,
364             self.pg1,
365             self.pg2,
366             socket.IPPROTO_UDP,
367             is_out=1,
368             priority=10,
369             policy_type="bypass",
370         )
371         policy_12 = self.spd_add_rem_policy(  # outbound, priority 5
372             1,
373             self.pg1,
374             self.pg2,
375             socket.IPPROTO_UDP,
376             is_out=1,
377             priority=5,
378             policy_type="discard",
379         )
380
381         policy_21 = self.spd_add_rem_policy(  # outbound, priority 5
382             1,
383             self.pg2,
384             self.pg0,
385             socket.IPPROTO_UDP,
386             is_out=1,
387             priority=5,
388             policy_type="bypass",
389         )
390         policy_22 = self.spd_add_rem_policy(  # outbound, priority 10
391             1,
392             self.pg2,
393             self.pg0,
394             socket.IPPROTO_UDP,
395             is_out=1,
396             priority=10,
397             policy_type="discard",
398         )
399
400         # interfaces bound to an SPD, will by default drop inbound
401         # traffic with no matching policies. add catch-all inbound
402         # bypass rule to SPD:
403         self.spd_add_rem_policy(  # inbound, all interfaces
404             1,
405             None,
406             None,
407             socket.IPPROTO_UDP,
408             is_out=0,
409             priority=10,
410             policy_type="bypass",
411             all_ips=True,
412         )
413
414         # check flow cache is empty (0 active elements) before sending traffic
415         self.verify_num_outbound_flow_cache_entries(0)
416
417         # create the packet streams
418         packets0 = self.create_stream(self.pg0, self.pg1, pkt_count)
419         packets1 = self.create_stream(self.pg1, self.pg2, pkt_count)
420         packets2 = self.create_stream(self.pg2, self.pg0, pkt_count)
421         # add the streams to the source interfaces
422         self.pg0.add_stream(packets0)
423         self.pg1.add_stream(packets1)
424         self.pg2.add_stream(packets2)
425         # enable capture on all interfaces
426         for pg in self.pg_interfaces:
427             pg.enable_capture()
428         # start the packet generator
429         self.pg_start()
430
431         # get captures
432         if_caps = []
433         for pg in [self.pg1, self.pg2]:  # we are expecting captures on pg1/pg2
434             if_caps.append(pg.get_capture())
435             for packet in if_caps[-1]:
436                 try:
437                     self.logger.debug(ppp("SPD - Got packet:", packet))
438                 except Exception:
439                     self.logger.error(ppp("Unexpected or invalid packet:", packet))
440                     raise
441         self.logger.debug("SPD: Num packets: %s", len(if_caps[0].res))
442         self.logger.debug("SPD: Num packets: %s", len(if_caps[1].res))
443
444         # verify captures that matched BYPASS rule
445         self.verify_capture(self.pg0, self.pg1, if_caps[0])
446         self.verify_capture(self.pg1, self.pg2, if_caps[1])
447         # verify that traffic to pg0 matched DISCARD rule and was dropped
448         self.pg0.assert_nothing_captured()
449         # verify all packets that were expected to match rules, matched
450         # pg0 -> pg1
451         self.verify_policy_match(pkt_count, policy_01)
452         self.verify_policy_match(0, policy_02)
453         # pg1 -> pg2
454         self.verify_policy_match(pkt_count, policy_11)
455         self.verify_policy_match(0, policy_12)
456         # pg2 -> pg0
457         self.verify_policy_match(0, policy_21)
458         self.verify_policy_match(pkt_count, policy_22)
459         # check that 3 matching policies in SPD have been cached
460         self.verify_num_outbound_flow_cache_entries(3)
461
462
463 class IPSec4SpdTestCaseOverwriteStale(SpdFlowCacheOutbound):
464     """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
465         (overwrite stale entries)"""
466
467     def test_ipsec_spd_outbound_overwrite(self):
468         # The operation of the flow cache is setup so that the entire cache
469         # is invalidated when adding or removing an SPD policy rule.
470         # For performance, old cache entries are not zero'd, but remain
471         # in the table as "stale" entries. If a flow matches a stale entry,
472         # and the epoch count does NOT match the current count, the entry
473         # is overwritten.
474         # In this test, 3 active rules are created and matched to enter
475         # them into the flow cache.
476         # A single entry is removed to invalidate the entire cache.
477         # We then readd the rule and test that overwriting of the previous
478         # stale entries occurs as expected, and that the flow cache entry
479         # counter is updated correctly.
480         self.create_interfaces(3)
481         pkt_count = 2
482         # bind SPD to all interfaces
483         self.spd_create_and_intf_add(1, self.pg_interfaces)
484         # add output rules on all interfaces
485         # pg0 -> pg1
486         policy_0 = self.spd_add_rem_policy(  # outbound
487             1,
488             self.pg0,
489             self.pg1,
490             socket.IPPROTO_UDP,
491             is_out=1,
492             priority=10,
493             policy_type="bypass",
494         )
495         # pg1 -> pg2
496         policy_1 = self.spd_add_rem_policy(  # outbound
497             1,
498             self.pg1,
499             self.pg2,
500             socket.IPPROTO_UDP,
501             is_out=1,
502             priority=10,
503             policy_type="bypass",
504         )
505         # pg2 -> pg0
506         policy_2 = self.spd_add_rem_policy(  # outbound
507             1,
508             self.pg2,
509             self.pg0,
510             socket.IPPROTO_UDP,
511             is_out=1,
512             priority=10,
513             policy_type="discard",
514         )
515
516         # interfaces bound to an SPD, will by default drop inbound
517         # traffic with no matching policies. add catch-all inbound
518         # bypass rule to SPD:
519         self.spd_add_rem_policy(  # inbound, all interfaces
520             1,
521             None,
522             None,
523             socket.IPPROTO_UDP,
524             is_out=0,
525             priority=10,
526             policy_type="bypass",
527             all_ips=True,
528         )
529
530         # check flow cache is empty (0 active elements) before sending traffic
531         self.verify_num_outbound_flow_cache_entries(0)
532
533         # create the packet streams
534         packets0 = self.create_stream(self.pg0, self.pg1, pkt_count)
535         packets1 = self.create_stream(self.pg1, self.pg2, pkt_count)
536         packets2 = self.create_stream(self.pg2, self.pg0, pkt_count)
537         # add the streams to the source interfaces
538         self.pg0.add_stream(packets0)
539         self.pg1.add_stream(packets1)
540         self.pg2.add_stream(packets2)
541         # enable capture on all interfaces
542         for pg in self.pg_interfaces:
543             pg.enable_capture()
544         # start the packet generator
545         self.pg_start()
546
547         # get captures from ifs
548         if_caps = []
549         for pg in [self.pg1, self.pg2]:  # we are expecting captures on pg1/pg2
550             if_caps.append(pg.get_capture())
551             for packet in if_caps[-1]:
552                 try:
553                     self.logger.debug(ppp("SPD Add - Got packet:", packet))
554                 except Exception:
555                     self.logger.error(ppp("Unexpected or invalid packet:", packet))
556                     raise
557
558         # verify captures that matched BYPASS rules
559         self.verify_capture(self.pg0, self.pg1, if_caps[0])
560         self.verify_capture(self.pg1, self.pg2, if_caps[1])
561         # verify that traffic to pg0 matched DISCARD rule and was dropped
562         self.pg0.assert_nothing_captured()
563         # verify all policies matched the expected number of times
564         self.verify_policy_match(pkt_count, policy_0)
565         self.verify_policy_match(pkt_count, policy_1)
566         self.verify_policy_match(pkt_count, policy_2)
567         # check flow/policy match was cached for: 3x output policies
568         self.verify_num_outbound_flow_cache_entries(3)
569
570         # adding an inbound policy should not invalidate output flow cache
571         self.spd_add_rem_policy(  # inbound
572             1,
573             self.pg0,
574             self.pg1,
575             socket.IPPROTO_UDP,
576             is_out=0,
577             priority=10,
578             policy_type="bypass",
579         )
580         # check flow cache counter has not been reset
581         self.verify_num_outbound_flow_cache_entries(3)
582
583         # remove a bypass policy - flow cache counter will be reset, and
584         # there will be 3x stale entries in flow cache
585         self.spd_add_rem_policy(  # outbound
586             1,
587             self.pg0,
588             self.pg1,
589             socket.IPPROTO_UDP,
590             is_out=1,
591             priority=10,
592             policy_type="bypass",
593             remove=True,
594         )
595         # readd policy
596         policy_0 = self.spd_add_rem_policy(  # outbound
597             1,
598             self.pg0,
599             self.pg1,
600             socket.IPPROTO_UDP,
601             is_out=1,
602             priority=10,
603             policy_type="bypass",
604         )
605         # check counter was reset with flow cache invalidation
606         self.verify_num_outbound_flow_cache_entries(0)
607
608         # resend the same packets
609         self.pg0.add_stream(packets0)
610         self.pg1.add_stream(packets1)
611         self.pg2.add_stream(packets2)
612         for pg in self.pg_interfaces:
613             pg.enable_capture()  # flush previous captures
614         self.pg_start()
615
616         # get captures from ifs
617         if_caps = []
618         for pg in [self.pg1, self.pg2]:  # we are expecting captures on pg1/pg2
619             if_caps.append(pg.get_capture())
620             for packet in if_caps[-1]:
621                 try:
622                     self.logger.debug(ppp("SPD Add - Got packet:", packet))
623                 except Exception:
624                     self.logger.error(ppp("Unexpected or invalid packet:", packet))
625                     raise
626
627         # verify captures that matched BYPASS rules
628         self.verify_capture(self.pg0, self.pg1, if_caps[0])
629         self.verify_capture(self.pg1, self.pg2, if_caps[1])
630         # verify that traffic to pg0 matched DISCARD rule and was dropped
631         self.pg0.assert_nothing_captured()
632         # verify all policies matched the expected number of times
633         self.verify_policy_match(pkt_count, policy_0)
634         self.verify_policy_match(pkt_count * 2, policy_1)
635         self.verify_policy_match(pkt_count * 2, policy_2)
636         # we are overwriting 3x stale entries - check flow cache counter
637         # is correct
638         self.verify_num_outbound_flow_cache_entries(3)
639
640
641 class IPSec4SpdTestCaseCollision(SpdFlowCacheOutbound):
642     """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
643         (hash collision)"""
644
645     # Override class setup to restrict vector size to 16 elements.
646     # This forces using only the lower 4 bits of the hash as a key,
647     # making hash collisions easy to find.
648     @classmethod
649     def setUpConstants(cls):
650         super(SpdFlowCacheOutbound, cls).setUpConstants()
651         cls.vpp_cmdline.extend(
652             [
653                 "ipsec",
654                 "{",
655                 "ipv4-outbound-spd-flow-cache on",
656                 "ipv4-outbound-spd-hash-buckets 16",
657                 "}",
658             ]
659         )
660         cls.logger.info("VPP modified cmdline is %s" % " ".join(cls.vpp_cmdline))
661
662     def test_ipsec_spd_outbound_collision(self):
663         # The flow cache operation is setup to overwrite an entry
664         # if a hash collision occurs.
665         # In this test, 2 packets are configured that result in a
666         # hash with the same lower 4 bits.
667         # After the first packet is received, there should be one
668         # active entry in the flow cache.
669         # After the second packet with the same lower 4 bit hash
670         # is received, this should overwrite the same entry.
671         # Therefore there will still be a total of one (1) entry,
672         # in the flow cache with two matching policies.
673         # crc32_supported() method is used to check cpu for crc32
674         # intrinsic support for hashing.
675         # If crc32 is not supported, we fall back to clib_xxhash()
676         self.create_interfaces(3)
677         pkt_count = 5
678         # bind SPD to all interfaces
679         self.spd_create_and_intf_add(1, self.pg_interfaces)
680         # add rules
681         policy_0 = self.spd_add_rem_policy(  # outbound, priority 10
682             1,
683             self.pg1,
684             self.pg2,
685             socket.IPPROTO_UDP,
686             is_out=1,
687             priority=10,
688             policy_type="bypass",
689         )
690         policy_1 = self.spd_add_rem_policy(  # outbound, priority 10
691             1,
692             self.pg2,
693             self.pg0,
694             socket.IPPROTO_UDP,
695             is_out=1,
696             priority=10,
697             policy_type="bypass",
698         )
699
700         # interfaces bound to an SPD, will by default drop inbound
701         # traffic with no matching policies. add catch-all inbound
702         # bypass rule to SPD:
703         self.spd_add_rem_policy(  # inbound, all interfaces
704             1,
705             None,
706             None,
707             socket.IPPROTO_UDP,
708             is_out=0,
709             priority=10,
710             policy_type="bypass",
711             all_ips=True,
712         )
713
714         # check flow cache is empty (0 active elements) before sending traffic
715         self.verify_num_outbound_flow_cache_entries(0)
716
717         # create the packet streams generating collision on last 4 bits
718         if self.crc32_supported():
719             # packet hashes to:
720             # 432c99c2
721             packets1 = self.create_stream(self.pg1, self.pg2, pkt_count, 1, 1)
722             # 31f8f3f2
723             packets2 = self.create_stream(self.pg2, self.pg0, pkt_count, 6, 6)
724         else:  # clib_xxhash
725             # ec3a258551bc0306
726             packets1 = self.create_stream(self.pg1, self.pg2, pkt_count, 2, 2)
727             # 61fee526d18d7a6
728             packets2 = self.create_stream(self.pg2, self.pg0, pkt_count, 3, 3)
729
730         # add the streams to the source interfaces
731         self.pg1.add_stream(packets1)
732         self.pg2.add_stream(packets2)
733         # enable capture on all interfaces
734         for pg in self.pg_interfaces:
735             pg.enable_capture()
736         # start the packet generator
737         self.pg_start()
738
739         # get captures from ifs - the proper pkt_count of packets was saved by
740         # create_packet_info() based on dst_if parameter
741         if_caps = []
742         for pg in [self.pg2, self.pg0]:  # we are expecting captures on pg2/pg0
743             if_caps.append(pg.get_capture())
744             for packet in if_caps[-1]:
745                 try:
746                     self.logger.debug(ppp("SPD - Got packet:", packet))
747                 except Exception:
748                     self.logger.error(ppp("Unexpected or invalid packet:", packet))
749                     raise
750         self.logger.debug("SPD: Num packets: %s", len(if_caps[0].res))
751         self.logger.debug("SPD: Num packets: %s", len(if_caps[1].res))
752
753         # verify captures that matched BYPASS rule
754         self.verify_capture(self.pg1, self.pg2, if_caps[0])
755         self.verify_capture(self.pg2, self.pg0, if_caps[1])
756         # verify all packets that were expected to match rules, matched
757         self.verify_policy_match(pkt_count, policy_0)
758         self.verify_policy_match(pkt_count, policy_1)
759         # we have matched 2 policies, but due to the hash collision
760         # one active entry is expected
761         self.verify_num_outbound_flow_cache_entries(1)
762
763
764 if __name__ == "__main__":
765     unittest.main(testRunner=VppTestRunner)