5 from asfframework import VppTestRunner
6 from template_ipsec import SpdFlowCacheTemplate
9 class SpdFlowCacheInbound(SpdFlowCacheTemplate):
10 # Override setUpConstants to enable inbound flow cache in config
12 def setUpConstants(cls):
13 super(SpdFlowCacheInbound, cls).setUpConstants()
14 cls.vpp_cmdline.extend(["ipsec", "{", "ipv4-inbound-spd-flow-cache on", "}"])
15 cls.logger.info("VPP modified cmdline is %s" % " ".join(cls.vpp_cmdline))
18 class IPSec4SpdTestCaseBypass(SpdFlowCacheInbound):
19 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
22 def test_ipsec_spd_inbound_bypass(self):
23 # In this test case, packets in IPv4 FWD path are configured
24 # to go through IPSec inbound SPD policy lookup.
26 # 2 inbound SPD rules (1 HIGH and 1 LOW) are added.
27 # - High priority rule action is set to DISCARD.
28 # - Low priority rule action is set to BYPASS.
30 # Since BYPASS rules take precedence over DISCARD
31 # (the order being PROTECT, BYPASS, DISCARD) we expect the
32 # BYPASS rule to match and traffic to be correctly forwarded.
33 self.create_interfaces(2)
36 self.spd_create_and_intf_add(1, [self.pg1, self.pg0])
39 # bypass rule should take precedence over discard rule,
40 # even though it's lower priority
41 policy_0 = self.spd_add_rem_policy( # inbound, priority 10
50 policy_1 = self.spd_add_rem_policy( # inbound, priority 15
57 policy_type="discard",
60 # create output rule so we can capture forwarded packets
61 policy_2 = self.spd_add_rem_policy( # outbound, priority 10
71 # check flow cache is empty before sending traffic
72 self.verify_num_inbound_flow_cache_entries(0)
73 # create the packet stream
74 packets = self.create_stream(self.pg0, self.pg1, pkt_count)
75 # add the stream to the source interface
76 self.pg0.add_stream(packets)
77 self.pg1.enable_capture()
80 # check capture on pg1
81 capture = self.pg1.get_capture()
82 for packet in capture:
84 self.logger.debug(ppp("SPD Add - Got packet:", packet))
86 self.logger.error(ppp("Unexpected or invalid packet:", packet))
88 self.logger.debug("SPD: Num packets: %s", len(capture.res))
90 # verify captured packets
91 self.verify_capture(self.pg0, self.pg1, capture)
92 # verify all policies matched the expected number of times
93 self.verify_policy_match(pkt_count, policy_0)
94 self.verify_policy_match(0, policy_1)
95 self.verify_policy_match(pkt_count, policy_2)
96 # check input policy has been cached
97 self.verify_num_inbound_flow_cache_entries(1)
100 class IPSec4SpdTestCaseDiscard(SpdFlowCacheInbound):
101 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
104 def test_ipsec_spd_inbound_discard(self):
105 # In this test case, packets in IPv4 FWD path are configured
106 # to go through IPSec inbound SPD policy lookup.
107 # 1 DISCARD rule is added, so all traffic should be dropped.
108 self.create_interfaces(2)
111 self.spd_create_and_intf_add(1, [self.pg1, self.pg0])
114 policy_0 = self.spd_add_rem_policy( # inbound, priority 10
121 policy_type="discard",
124 # create output rule so we can capture forwarded packets
125 policy_1 = self.spd_add_rem_policy( # outbound, priority 10
132 policy_type="bypass",
135 # check flow cache is empty before sending traffic
136 self.verify_num_inbound_flow_cache_entries(0)
137 # create the packet stream
138 packets = self.create_stream(self.pg0, self.pg1, pkt_count)
139 # add the stream to the source interface
140 self.pg0.add_stream(packets)
141 self.pg1.enable_capture()
143 # inbound discard rule should have dropped traffic
144 self.pg1.assert_nothing_captured()
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 # only inbound discard rule should have been cached
149 self.verify_num_inbound_flow_cache_entries(1)
152 class IPSec4SpdTestCaseRemoveInbound(SpdFlowCacheInbound):
153 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
156 def test_ipsec_spd_inbound_remove(self):
157 # In this test case, packets in IPv4 FWD path are configured
158 # to go through IPSec inbound SPD policy lookup.
160 # 2 inbound SPD rules (1 HIGH and 1 LOW) are added.
161 # - High priority rule action is set to DISCARD.
162 # - Low priority rule action is set to BYPASS.
164 # Since BYPASS rules take precedence over DISCARD
165 # (the order being PROTECT, BYPASS, DISCARD) we expect the
166 # BYPASS rule to match and traffic to be correctly forwarded.
168 # The BYPASS rules is then removed, and we check that all traffic
169 # is now correctly dropped.
170 self.create_interfaces(2)
173 self.spd_create_and_intf_add(1, [self.pg1, self.pg0])
176 # bypass rule should take precedence over discard rule,
177 # even though it's lower priority
178 policy_0 = self.spd_add_rem_policy( # inbound, priority 10
185 policy_type="bypass",
187 policy_1 = self.spd_add_rem_policy( # inbound, priority 15
194 policy_type="discard",
197 # create output rule so we can capture forwarded packets
198 policy_2 = self.spd_add_rem_policy( # outbound, priority 10
205 policy_type="bypass",
208 # check flow cache is empty before sending traffic
209 self.verify_num_inbound_flow_cache_entries(0)
210 # create the packet stream
211 packets = self.create_stream(self.pg0, self.pg1, pkt_count)
212 # add the stream to the source interface
213 self.pg0.add_stream(packets)
214 self.pg1.enable_capture()
217 # check capture on pg1
218 capture = self.pg1.get_capture()
219 for packet in capture:
221 self.logger.debug(ppp("SPD Add - Got packet:", packet))
223 self.logger.error(ppp("Unexpected or invalid packet:", packet))
225 self.logger.debug("SPD: Num packets: %s", len(capture.res))
227 # verify captured packets
228 self.verify_capture(self.pg0, self.pg1, capture)
229 # verify all policies matched the expected number of times
230 self.verify_policy_match(pkt_count, policy_0)
231 self.verify_policy_match(0, policy_1)
232 self.verify_policy_match(pkt_count, policy_2)
233 # check input policy has been cached
234 self.verify_num_inbound_flow_cache_entries(1)
236 # remove the input bypass rule
237 self.spd_add_rem_policy( # inbound, priority 10
244 policy_type="bypass",
247 # verify flow cache counter has been reset by rule removal
248 self.verify_num_inbound_flow_cache_entries(0)
250 # resend the same packets
251 self.pg0.add_stream(packets)
252 self.pg1.enable_capture() # flush the old capture
255 # inbound discard rule should have dropped traffic
256 self.pg1.assert_nothing_captured()
257 # verify all policies matched the expected number of times
258 self.verify_policy_match(pkt_count, policy_0)
259 self.verify_policy_match(pkt_count, policy_1)
260 self.verify_policy_match(pkt_count, policy_2)
261 # by removing the bypass rule, we should have reset the flow cache
262 # we only expect the discard rule to now be in the flow cache
263 self.verify_num_inbound_flow_cache_entries(1)
266 class IPSec4SpdTestCaseReaddInbound(SpdFlowCacheInbound):
267 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
268 (add, remove, re-add bypass)"""
270 def test_ipsec_spd_inbound_readd(self):
271 # In this test case, packets in IPv4 FWD path are configured
272 # to go through IPSec inbound SPD policy lookup.
274 # 2 inbound SPD rules (1 HIGH and 1 LOW) are added.
275 # - High priority rule action is set to DISCARD.
276 # - Low priority rule action is set to BYPASS.
278 # Since BYPASS rules take precedence over DISCARD
279 # (the order being PROTECT, BYPASS, DISCARD) we expect the
280 # BYPASS rule to match and traffic to be correctly forwarded.
282 # The BYPASS rules is then removed, and we check that all traffic
283 # is now correctly dropped.
285 # The BYPASS rule is then readded, checking traffic is not forwarded
287 self.create_interfaces(2)
290 self.spd_create_and_intf_add(1, [self.pg1, self.pg0])
293 # bypass rule should take precedence over discard rule,
294 # even though it's lower priority
295 policy_0 = self.spd_add_rem_policy( # inbound, priority 10
302 policy_type="bypass",
304 policy_1 = self.spd_add_rem_policy( # inbound, priority 15
311 policy_type="discard",
314 # create output rule so we can capture forwarded packets
315 policy_2 = self.spd_add_rem_policy( # outbound, priority 10
322 policy_type="bypass",
325 # check flow cache is empty before sending traffic
326 self.verify_num_inbound_flow_cache_entries(0)
327 # create the packet stream
328 packets = self.create_stream(self.pg0, self.pg1, pkt_count)
329 # add the stream to the source interface
330 self.pg0.add_stream(packets)
331 self.pg1.enable_capture()
334 # check capture on pg1
335 capture = self.pg1.get_capture()
336 for packet in capture:
338 self.logger.debug(ppp("SPD Add - Got packet:", packet))
340 self.logger.error(ppp("Unexpected or invalid packet:", packet))
342 self.logger.debug("SPD: Num packets: %s", len(capture.res))
344 # verify captured packets
345 self.verify_capture(self.pg0, self.pg1, capture)
346 # verify all policies matched the expected number of times
347 self.verify_policy_match(pkt_count, policy_0)
348 self.verify_policy_match(0, policy_1)
349 self.verify_policy_match(pkt_count, policy_2)
350 # check input policy has been cached
351 self.verify_num_inbound_flow_cache_entries(1)
353 # remove the input bypass rule
354 self.spd_add_rem_policy( # inbound, priority 10
361 policy_type="bypass",
364 # verify flow cache counter has been reset by rule removal
365 self.verify_num_inbound_flow_cache_entries(0)
367 # resend the same packets
368 self.pg0.add_stream(packets)
369 self.pg1.enable_capture() # flush the old capture
372 # inbound discard rule should have dropped traffic
373 self.pg1.assert_nothing_captured()
374 # verify all policies matched the expected number of times
375 self.verify_policy_match(pkt_count, policy_0)
376 self.verify_policy_match(pkt_count, policy_1)
377 self.verify_policy_match(pkt_count, policy_2)
378 # by removing the bypass rule, flow cache was reset
379 # we only expect the discard rule to now be in the flow cache
380 self.verify_num_inbound_flow_cache_entries(1)
382 # readd the input bypass rule
383 policy_0 = self.spd_add_rem_policy( # inbound, priority 10
390 policy_type="bypass",
392 # verify flow cache counter has been reset by rule addition
393 self.verify_num_inbound_flow_cache_entries(0)
395 # resend the same packets
396 self.pg0.add_stream(packets)
397 self.pg1.enable_capture() # flush the old capture
400 # check capture on pg1
401 capture = self.pg1.get_capture()
402 for packet in capture:
404 self.logger.debug(ppp("SPD Add - Got packet:", packet))
406 self.logger.error(ppp("Unexpected or invalid packet:", packet))
409 # verify captured packets
410 self.verify_capture(self.pg0, self.pg1, capture)
411 # verify all policies matched the expected number of times
412 self.verify_policy_match(pkt_count, policy_0)
413 self.verify_policy_match(pkt_count, policy_1)
414 self.verify_policy_match(pkt_count * 2, policy_2)
415 # by readding the bypass rule, we reset the flow cache
416 # we only expect the bypass rule to now be in the flow cache
417 self.verify_num_inbound_flow_cache_entries(1)
420 class IPSec4SpdTestCaseMultipleInbound(SpdFlowCacheInbound):
421 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
422 (multiple interfaces, multiple rules)"""
424 def test_ipsec_spd_inbound_multiple(self):
425 # In this test case, packets in IPv4 FWD path are configured to go
426 # through IPSec outbound SPD policy lookup.
428 # Multiples rules on multiple interfaces are tested at the same time.
429 # 3x interfaces are configured, binding the same SPD to each.
430 # Each interface has 1 SPD rule- 2x BYPASS and 1x DISCARD
432 # Traffic should be forwarded with destinations pg1 & pg2
433 # and dropped to pg0.
434 self.create_interfaces(3)
436 # bind SPD to all interfaces
437 self.spd_create_and_intf_add(1, self.pg_interfaces)
438 # add input rules on all interfaces
440 policy_0 = self.spd_add_rem_policy( # inbound, priority 10
447 policy_type="bypass",
450 policy_1 = self.spd_add_rem_policy( # inbound, priority 10
457 policy_type="bypass",
460 policy_2 = self.spd_add_rem_policy( # inbound, priority 10
467 policy_type="discard",
470 # create output rules covering the the full ip range
471 # 0.0.0.0 -> 255.255.255.255, so we can capture forwarded packets
472 policy_3 = self.spd_add_rem_policy( # outbound, priority 10
479 policy_type="bypass",
483 # check flow cache is empty (0 active elements) before sending traffic
484 self.verify_num_inbound_flow_cache_entries(0)
486 # create the packet streams
487 packets0 = self.create_stream(self.pg0, self.pg1, pkt_count)
488 packets1 = self.create_stream(self.pg1, self.pg2, pkt_count)
489 packets2 = self.create_stream(self.pg2, self.pg0, pkt_count)
490 # add the streams to the source interfaces
491 self.pg0.add_stream(packets0)
492 self.pg1.add_stream(packets1)
493 self.pg2.add_stream(packets2)
494 # enable capture on all interfaces
495 for pg in self.pg_interfaces:
497 # start the packet generator
500 # get captures from ifs
502 for pg in [self.pg1, self.pg2]: # we are expecting captures on pg1/pg2
503 if_caps.append(pg.get_capture())
504 for packet in if_caps[-1]:
506 self.logger.debug(ppp("SPD Add - Got packet:", packet))
508 self.logger.error(ppp("Unexpected or invalid packet:", packet))
511 # verify captures that matched BYPASS rules
512 self.verify_capture(self.pg0, self.pg1, if_caps[0])
513 self.verify_capture(self.pg1, self.pg2, if_caps[1])
514 # verify that traffic to pg0 matched DISCARD rule and was dropped
515 self.pg0.assert_nothing_captured()
516 # verify all policies matched the expected number of times
517 self.verify_policy_match(pkt_count, policy_0)
518 self.verify_policy_match(pkt_count, policy_1)
519 self.verify_policy_match(pkt_count, policy_2)
520 # check flow/policy match was cached for: 3x input policies
521 self.verify_num_inbound_flow_cache_entries(3)
524 class IPSec4SpdTestCaseOverwriteStaleInbound(SpdFlowCacheInbound):
525 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
526 (overwrite stale entries)"""
528 def test_ipsec_spd_inbound_overwrite(self):
529 # The operation of the flow cache is setup so that the entire cache
530 # is invalidated when adding or removing an SPD policy rule.
531 # For performance, old cache entries are not zero'd, but remain
532 # in the table as "stale" entries. If a flow matches a stale entry,
533 # and the epoch count does NOT match the current count, the entry
535 # In this test, 3 active rules are created and matched to enter
536 # them into the flow cache.
537 # A single entry is removed to invalidate the entire cache.
538 # We then readd the rule and test that overwriting of the previous
539 # stale entries occurs as expected, and that the flow cache entry
540 # counter is updated correctly.
541 self.create_interfaces(3)
543 # bind SPD to all interfaces
544 self.spd_create_and_intf_add(1, self.pg_interfaces)
545 # add input rules on all interfaces
547 policy_0 = self.spd_add_rem_policy( # inbound
554 policy_type="bypass",
557 policy_1 = self.spd_add_rem_policy( # inbound
564 policy_type="bypass",
567 policy_2 = self.spd_add_rem_policy( # inbound
574 policy_type="discard",
577 # create output rules covering the the full ip range
578 # 0.0.0.0 -> 255.255.255.255, so we can capture forwarded packets
579 policy_3 = self.spd_add_rem_policy( # outbound
586 policy_type="bypass",
590 # check flow cache is empty (0 active elements) before sending traffic
591 self.verify_num_inbound_flow_cache_entries(0)
593 # create the packet streams
594 packets0 = self.create_stream(self.pg0, self.pg1, pkt_count)
595 packets1 = self.create_stream(self.pg1, self.pg2, pkt_count)
596 packets2 = self.create_stream(self.pg2, self.pg0, pkt_count)
597 # add the streams to the source interfaces
598 self.pg0.add_stream(packets0)
599 self.pg1.add_stream(packets1)
600 self.pg2.add_stream(packets2)
601 # enable capture on all interfaces
602 for pg in self.pg_interfaces:
604 # start the packet generator
607 # get captures from ifs
609 for pg in [self.pg1, self.pg2]: # we are expecting captures on pg1/pg2
610 if_caps.append(pg.get_capture())
611 for packet in if_caps[-1]:
613 self.logger.debug(ppp("SPD Add - Got packet:", packet))
615 self.logger.error(ppp("Unexpected or invalid packet:", packet))
618 # verify captures that matched BYPASS rules
619 self.verify_capture(self.pg0, self.pg1, if_caps[0])
620 self.verify_capture(self.pg1, self.pg2, if_caps[1])
621 # verify that traffic to pg0 matched DISCARD rule and was dropped
622 self.pg0.assert_nothing_captured()
623 # verify all policies matched the expected number of times
624 self.verify_policy_match(pkt_count, policy_0)
625 self.verify_policy_match(pkt_count, policy_1)
626 self.verify_policy_match(pkt_count, policy_2)
627 # check flow/policy match was cached for: 3x input policies
628 self.verify_num_inbound_flow_cache_entries(3)
630 # adding an outbound policy should not invalidate output flow cache
631 self.spd_add_rem_policy( # outbound
638 policy_type="bypass",
641 # check inbound flow cache counter has not been reset
642 self.verify_num_inbound_flow_cache_entries(3)
644 # remove + readd bypass policy - flow cache counter will be reset,
645 # and there will be 3x stale entries in flow cache
646 self.spd_add_rem_policy( # inbound, priority 10
653 policy_type="bypass",
657 policy_0 = self.spd_add_rem_policy( # inbound, priority 10
664 policy_type="bypass",
666 # check counter was reset
667 self.verify_num_inbound_flow_cache_entries(0)
669 # resend the same packets
670 self.pg0.add_stream(packets0)
671 self.pg1.add_stream(packets1)
672 self.pg2.add_stream(packets2)
673 for pg in self.pg_interfaces:
674 pg.enable_capture() # flush previous captures
677 # get captures from ifs
679 for pg in [self.pg1, self.pg2]: # we are expecting captures on pg1/pg2
680 if_caps.append(pg.get_capture())
681 for packet in if_caps[-1]:
683 self.logger.debug(ppp("SPD Add - Got packet:", packet))
685 self.logger.error(ppp("Unexpected or invalid packet:", packet))
688 # verify captures that matched BYPASS rules
689 self.verify_capture(self.pg0, self.pg1, if_caps[0])
690 self.verify_capture(self.pg1, self.pg2, if_caps[1])
691 # verify that traffic to pg0 matched DISCARD rule and was dropped
692 self.pg0.assert_nothing_captured()
693 # verify all policies matched the expected number of times
694 self.verify_policy_match(pkt_count, policy_0)
695 self.verify_policy_match(pkt_count * 2, policy_1)
696 self.verify_policy_match(pkt_count * 2, policy_2)
697 # we are overwriting 3x stale entries - check flow cache counter
699 self.verify_num_inbound_flow_cache_entries(3)
702 class IPSec4SpdTestCaseCollisionInbound(SpdFlowCacheInbound):
703 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
706 # Override class setup to restrict hash table size to 16 buckets.
707 # This forces using only the lower 4 bits of the hash as a key,
708 # making hash collisions easy to find.
710 def setUpConstants(cls):
711 super(SpdFlowCacheInbound, cls).setUpConstants()
712 cls.vpp_cmdline.extend(
716 "ipv4-inbound-spd-flow-cache on",
717 "ipv4-inbound-spd-hash-buckets 16",
721 cls.logger.info("VPP modified cmdline is %s" % " ".join(cls.vpp_cmdline))
723 def test_ipsec_spd_inbound_collision(self):
724 # The flow cache operation is setup to overwrite an entry
725 # if a hash collision occurs.
726 # In this test, 2 packets are configured that result in a
727 # hash with the same lower 4 bits.
728 # After the first packet is received, there should be one
729 # active entry in the flow cache.
730 # After the second packet with the same lower 4 bit hash
731 # is received, this should overwrite the same entry.
732 # Therefore there will still be a total of one (1) entry,
733 # in the flow cache with two matching policies.
734 # crc32_supported() method is used to check cpu for crc32
735 # intrinsic support for hashing.
736 # If crc32 is not supported, we fall back to clib_xxhash()
737 self.create_interfaces(4)
739 # bind SPD to all interfaces
740 self.spd_create_and_intf_add(1, self.pg_interfaces)
742 # create output rules covering the the full ip range
743 # 0.0.0.0 -> 255.255.255.255, so we can capture forwarded packets
744 policy_0 = self.spd_add_rem_policy( # outbound
751 policy_type="bypass",
756 if self.crc32_supported(): # create crc32 collision on last 4 bits
757 hashed_with_crc32 = True
759 policy_1 = self.spd_add_rem_policy( # inbound, priority 10
766 policy_type="bypass",
768 policy_2 = self.spd_add_rem_policy( # inbound, priority 10
775 policy_type="bypass",
778 # we expect to get captures on pg1 + pg3
779 capture_intfs.append(self.pg1)
780 capture_intfs.append(self.pg3)
782 # check flow cache is empty before sending traffic
783 self.verify_num_inbound_flow_cache_entries(0)
785 # create the packet streams
788 packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 1)
790 packets2 = self.create_stream(self.pg0, self.pg3, pkt_count, 1, 1)
791 # add the streams to the source interfaces
792 self.pg2.add_stream(packets1)
793 self.pg0.add_stream(packets2)
794 else: # create xxhash collision on last 4 bits
795 hashed_with_crc32 = False
797 policy_1 = self.spd_add_rem_policy( # inbound, priority 10
804 policy_type="bypass",
806 policy_2 = self.spd_add_rem_policy( # inbound, priority 10
813 policy_type="bypass",
816 capture_intfs.append(self.pg1)
817 capture_intfs.append(self.pg2)
819 # check flow cache is empty before sending traffic
820 self.verify_num_inbound_flow_cache_entries(0)
822 # create the packet streams
824 packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 1)
826 packets2 = self.create_stream(self.pg3, self.pg2, pkt_count, 1, 1)
827 # add the streams to the source interfaces
828 self.pg2.add_stream(packets1)
829 self.pg3.add_stream(packets2)
831 # enable capture on interfaces we expect capture on & send pkts
832 for pg in capture_intfs:
838 for pg in capture_intfs:
839 if_caps.append(pg.get_capture())
840 for packet in if_caps[-1]:
842 self.logger.debug(ppp("SPD Add - Got packet:", packet))
844 self.logger.error(ppp("Unexpected or invalid packet:", packet))
847 # verify captures that matched BYPASS rule
848 if hashed_with_crc32:
849 self.verify_capture(self.pg2, self.pg1, if_caps[0])
850 self.verify_capture(self.pg0, self.pg3, if_caps[1])
851 else: # hashed with xxhash
852 self.verify_capture(self.pg2, self.pg1, if_caps[0])
853 self.verify_capture(self.pg3, self.pg2, if_caps[1])
855 # verify all policies matched the expected number of times
856 self.verify_policy_match(pkt_count, policy_1)
857 self.verify_policy_match(pkt_count, policy_2)
858 self.verify_policy_match(pkt_count * 2, policy_0) # output policy
859 # we have matched 2 policies, but due to the hash collision
860 # one active entry is expected
861 self.verify_num_inbound_flow_cache_entries(1)
864 if __name__ == "__main__":
865 unittest.main(testRunner=VppTestRunner)