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