6 from framework import VppTestRunner
7 from template_ipsec import SpdFlowCacheTemplate
10 class SpdFlowCacheInbound(SpdFlowCacheTemplate):
11 # Override setUpConstants to enable inbound flow cache in config
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))
19 class IPSec4SpdTestCaseBypass(SpdFlowCacheInbound):
20 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
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.
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.
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)
37 self.spd_create_and_intf_add(1, [self.pg1, self.pg0])
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
51 policy_1 = self.spd_add_rem_policy( # inbound, priority 15
58 policy_type="discard",
61 # create output rule so we can capture forwarded packets
62 policy_2 = self.spd_add_rem_policy( # outbound, priority 10
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()
81 # check capture on pg1
82 capture = self.pg1.get_capture()
83 for packet in capture:
85 self.logger.debug(ppp("SPD Add - Got packet:", packet))
87 self.logger.error(ppp("Unexpected or invalid packet:", packet))
89 self.logger.debug("SPD: Num packets: %s", len(capture.res))
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)
101 class IPSec4SpdTestCaseDiscard(SpdFlowCacheInbound):
102 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
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)
112 self.spd_create_and_intf_add(1, [self.pg1, self.pg0])
115 policy_0 = self.spd_add_rem_policy( # inbound, priority 10
122 policy_type="discard",
125 # create output rule so we can capture forwarded packets
126 policy_1 = self.spd_add_rem_policy( # outbound, priority 10
133 policy_type="bypass",
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()
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)
153 class IPSec4SpdTestCaseRemoveInbound(SpdFlowCacheInbound):
154 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
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.
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.
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.
169 # The BYPASS rules is then removed, and we check that all traffic
170 # is now correctly dropped.
171 self.create_interfaces(2)
174 self.spd_create_and_intf_add(1, [self.pg1, self.pg0])
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
186 policy_type="bypass",
188 policy_1 = self.spd_add_rem_policy( # inbound, priority 15
195 policy_type="discard",
198 # create output rule so we can capture forwarded packets
199 policy_2 = self.spd_add_rem_policy( # outbound, priority 10
206 policy_type="bypass",
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()
218 # check capture on pg1
219 capture = self.pg1.get_capture()
220 for packet in capture:
222 self.logger.debug(ppp("SPD Add - Got packet:", packet))
224 self.logger.error(ppp("Unexpected or invalid packet:", packet))
226 self.logger.debug("SPD: Num packets: %s", len(capture.res))
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)
237 # remove the input bypass rule
238 self.spd_add_rem_policy( # inbound, priority 10
245 policy_type="bypass",
248 # verify flow cache counter has been reset by rule removal
249 self.verify_num_inbound_flow_cache_entries(0)
251 # resend the same packets
252 self.pg0.add_stream(packets)
253 self.pg1.enable_capture() # flush the old capture
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)
267 class IPSec4SpdTestCaseReaddInbound(SpdFlowCacheInbound):
268 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
269 (add, remove, re-add bypass)"""
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.
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.
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.
283 # The BYPASS rules is then removed, and we check that all traffic
284 # is now correctly dropped.
286 # The BYPASS rule is then readded, checking traffic is not forwarded
288 self.create_interfaces(2)
291 self.spd_create_and_intf_add(1, [self.pg1, self.pg0])
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
303 policy_type="bypass",
305 policy_1 = self.spd_add_rem_policy( # inbound, priority 15
312 policy_type="discard",
315 # create output rule so we can capture forwarded packets
316 policy_2 = self.spd_add_rem_policy( # outbound, priority 10
323 policy_type="bypass",
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()
335 # check capture on pg1
336 capture = self.pg1.get_capture()
337 for packet in capture:
339 self.logger.debug(ppp("SPD Add - Got packet:", packet))
341 self.logger.error(ppp("Unexpected or invalid packet:", packet))
343 self.logger.debug("SPD: Num packets: %s", len(capture.res))
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)
354 # remove the input bypass rule
355 self.spd_add_rem_policy( # inbound, priority 10
362 policy_type="bypass",
365 # verify flow cache counter has been reset by rule removal
366 self.verify_num_inbound_flow_cache_entries(0)
368 # resend the same packets
369 self.pg0.add_stream(packets)
370 self.pg1.enable_capture() # flush the old capture
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)
383 # readd the input bypass rule
384 policy_0 = self.spd_add_rem_policy( # inbound, priority 10
391 policy_type="bypass",
393 # verify flow cache counter has been reset by rule addition
394 self.verify_num_inbound_flow_cache_entries(0)
396 # resend the same packets
397 self.pg0.add_stream(packets)
398 self.pg1.enable_capture() # flush the old capture
401 # check capture on pg1
402 capture = self.pg1.get_capture()
403 for packet in capture:
405 self.logger.debug(ppp("SPD Add - Got packet:", packet))
407 self.logger.error(ppp("Unexpected or invalid packet:", packet))
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)
421 class IPSec4SpdTestCaseMultipleInbound(SpdFlowCacheInbound):
422 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
423 (multiple interfaces, multiple rules)"""
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.
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
433 # Traffic should be forwarded with destinations pg1 & pg2
434 # and dropped to pg0.
435 self.create_interfaces(3)
437 # bind SPD to all interfaces
438 self.spd_create_and_intf_add(1, self.pg_interfaces)
439 # add input rules on all interfaces
441 policy_0 = self.spd_add_rem_policy( # inbound, priority 10
448 policy_type="bypass",
451 policy_1 = self.spd_add_rem_policy( # inbound, priority 10
458 policy_type="bypass",
461 policy_2 = self.spd_add_rem_policy( # inbound, priority 10
468 policy_type="discard",
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
480 policy_type="bypass",
484 # check flow cache is empty (0 active elements) before sending traffic
485 self.verify_num_inbound_flow_cache_entries(0)
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:
498 # start the packet generator
501 # get captures from ifs
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]:
507 self.logger.debug(ppp("SPD Add - Got packet:", packet))
509 self.logger.error(ppp("Unexpected or invalid packet:", packet))
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)
525 class IPSec4SpdTestCaseOverwriteStaleInbound(SpdFlowCacheInbound):
526 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
527 (overwrite stale entries)"""
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
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)
544 # bind SPD to all interfaces
545 self.spd_create_and_intf_add(1, self.pg_interfaces)
546 # add input rules on all interfaces
548 policy_0 = self.spd_add_rem_policy( # inbound
555 policy_type="bypass",
558 policy_1 = self.spd_add_rem_policy( # inbound
565 policy_type="bypass",
568 policy_2 = self.spd_add_rem_policy( # inbound
575 policy_type="discard",
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
587 policy_type="bypass",
591 # check flow cache is empty (0 active elements) before sending traffic
592 self.verify_num_inbound_flow_cache_entries(0)
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:
605 # start the packet generator
608 # get captures from ifs
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]:
614 self.logger.debug(ppp("SPD Add - Got packet:", packet))
616 self.logger.error(ppp("Unexpected or invalid packet:", packet))
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)
631 # adding an outbound policy should not invalidate output flow cache
632 self.spd_add_rem_policy( # outbound
639 policy_type="bypass",
642 # check inbound flow cache counter has not been reset
643 self.verify_num_inbound_flow_cache_entries(3)
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
654 policy_type="bypass",
658 policy_0 = self.spd_add_rem_policy( # inbound, priority 10
665 policy_type="bypass",
667 # check counter was reset
668 self.verify_num_inbound_flow_cache_entries(0)
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
678 # get captures from ifs
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]:
684 self.logger.debug(ppp("SPD Add - Got packet:", packet))
686 self.logger.error(ppp("Unexpected or invalid packet:", packet))
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
700 self.verify_num_inbound_flow_cache_entries(3)
703 class IPSec4SpdTestCaseCollisionInbound(SpdFlowCacheInbound):
704 """ IPSec/IPv4 inbound: Policy mode test case with flow cache \
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.
711 def setUpConstants(cls):
712 super(SpdFlowCacheInbound, cls).setUpConstants()
713 cls.vpp_cmdline.extend(
717 "ipv4-inbound-spd-flow-cache on",
718 "ipv4-inbound-spd-hash-buckets 16",
722 cls.logger.info("VPP modified cmdline is %s" % " ".join(cls.vpp_cmdline))
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)
740 # bind SPD to all interfaces
741 self.spd_create_and_intf_add(1, self.pg_interfaces)
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
752 policy_type="bypass",
757 if self.crc32_supported(): # create crc32 collision on last 4 bits
758 hashed_with_crc32 = True
760 policy_1 = self.spd_add_rem_policy( # inbound, priority 10
767 policy_type="bypass",
769 policy_2 = self.spd_add_rem_policy( # inbound, priority 10
776 policy_type="bypass",
779 # we expect to get captures on pg1 + pg3
780 capture_intfs.append(self.pg1)
781 capture_intfs.append(self.pg3)
783 # check flow cache is empty before sending traffic
784 self.verify_num_inbound_flow_cache_entries(0)
786 # create the packet streams
789 packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 1)
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
798 policy_1 = self.spd_add_rem_policy( # inbound, priority 10
805 policy_type="bypass",
807 policy_2 = self.spd_add_rem_policy( # inbound, priority 10
814 policy_type="bypass",
817 capture_intfs.append(self.pg1)
818 capture_intfs.append(self.pg2)
820 # check flow cache is empty before sending traffic
821 self.verify_num_inbound_flow_cache_entries(0)
823 # create the packet streams
825 packets1 = self.create_stream(self.pg2, self.pg1, pkt_count, 1, 1)
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)
832 # enable capture on interfaces we expect capture on & send pkts
833 for pg in capture_intfs:
839 for pg in capture_intfs:
840 if_caps.append(pg.get_capture())
841 for packet in if_caps[-1]:
843 self.logger.debug(ppp("SPD Add - Got packet:", packet))
845 self.logger.error(ppp("Unexpected or invalid packet:", packet))
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])
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)
865 if __name__ == "__main__":
866 unittest.main(testRunner=VppTestRunner)