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