5 from framework import VppTestRunner
6 from template_ipsec import SpdFlowCacheTemplate
9 class SpdFlowCacheOutbound(SpdFlowCacheTemplate):
10 # Override setUpConstants to enable outbound flow cache in config
12 def setUpConstants(cls):
13 super(SpdFlowCacheOutbound, cls).setUpConstants()
14 cls.vpp_cmdline.extend(["ipsec", "{", "ipv4-outbound-spd-flow-cache on", "}"])
15 cls.logger.info("VPP modified cmdline is %s" % " ".join(cls.vpp_cmdline))
18 class IPSec4SpdTestCaseAdd(SpdFlowCacheOutbound):
19 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
22 def test_ipsec_spd_outbound_add(self):
23 # In this test case, packets in IPv4 FWD path are configured
24 # to go through IPSec outbound SPD policy lookup.
25 # 2 SPD rules (1 HIGH and 1 LOW) are added.
26 # High priority rule action is set to BYPASS.
27 # Low priority rule action is set to DISCARD.
28 # Traffic sent on pg0 interface should match high priority
29 # rule and should be sent out on pg1 interface.
30 self.create_interfaces(2)
32 self.spd_create_and_intf_add(1, [self.pg1])
33 policy_0 = self.spd_add_rem_policy( # outbound, priority 10
42 policy_1 = self.spd_add_rem_policy( # outbound, priority 5
49 policy_type="discard",
52 # check flow cache is empty before sending traffic
53 self.verify_num_outbound_flow_cache_entries(0)
55 # create the packet stream
56 packets = self.create_stream(self.pg0, self.pg1, pkt_count)
57 # add the stream to the source interface + enable capture
58 self.pg0.add_stream(packets)
59 self.pg0.enable_capture()
60 self.pg1.enable_capture()
61 # start the packet generator
64 capture = self.pg1.get_capture()
65 for packet in capture:
67 self.logger.debug(ppp("SPD - Got packet:", packet))
69 self.logger.error(ppp("Unexpected or invalid packet:", packet))
71 self.logger.debug("SPD: Num packets: %s", len(capture.res))
73 # assert nothing captured on pg0
74 self.pg0.assert_nothing_captured()
75 # verify captured packets
76 self.verify_capture(self.pg0, self.pg1, capture)
77 # verify all policies matched the expected number of times
78 self.verify_policy_match(pkt_count, policy_0)
79 self.verify_policy_match(0, policy_1)
80 # check policy in SPD has been cached after traffic
81 # matched BYPASS rule in SPD
82 self.verify_num_outbound_flow_cache_entries(1)
85 class IPSec4SpdTestCaseRemoveOutbound(SpdFlowCacheOutbound):
86 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
89 def test_ipsec_spd_outbound_remove(self):
90 # In this test case, packets in IPv4 FWD path are configured
91 # to go through IPSec outbound SPD policy lookup.
92 # 2 SPD rules (1 HIGH and 1 LOW) are added.
93 # High priority rule action is set to BYPASS.
94 # Low priority rule action is set to DISCARD.
95 # High priority rule is then removed.
96 # Traffic sent on pg0 interface should match low priority
97 # rule and should be discarded after SPD lookup.
98 self.create_interfaces(2)
100 self.spd_create_and_intf_add(1, [self.pg1])
101 policy_0 = self.spd_add_rem_policy( # outbound, priority 10
108 policy_type="bypass",
110 policy_1 = self.spd_add_rem_policy( # outbound, priority 5
117 policy_type="discard",
120 # check flow cache is empty before sending traffic
121 self.verify_num_outbound_flow_cache_entries(0)
123 # create the packet stream
124 packets = self.create_stream(self.pg0, self.pg1, pkt_count)
125 # add the stream to the source interface + enable capture
126 self.pg0.add_stream(packets)
127 self.pg0.enable_capture()
128 self.pg1.enable_capture()
129 # start the packet generator
132 capture = self.pg1.get_capture()
133 for packet in capture:
135 self.logger.debug(ppp("SPD - Got packet:", packet))
137 self.logger.error(ppp("Unexpected or invalid packet:", packet))
140 # assert nothing captured on pg0
141 self.pg0.assert_nothing_captured()
142 # verify capture on pg1
143 self.logger.debug("SPD: Num packets: %s", len(capture.res))
144 self.verify_capture(self.pg0, self.pg1, capture)
145 # verify all policies matched the expected number of times
146 self.verify_policy_match(pkt_count, policy_0)
147 self.verify_policy_match(0, policy_1)
148 # check policy in SPD has been cached after traffic
149 # matched BYPASS rule in SPD
150 self.verify_num_outbound_flow_cache_entries(1)
152 # now remove the bypass rule
153 self.spd_add_rem_policy( # outbound, priority 10
160 policy_type="bypass",
163 # verify flow cache counter has been reset by rule removal
164 self.verify_num_outbound_flow_cache_entries(0)
166 # resend the same packets
167 self.pg0.add_stream(packets)
168 self.pg0.enable_capture() # flush the old captures
169 self.pg1.enable_capture()
171 # assert nothing captured on pg0
172 self.pg0.assert_nothing_captured()
173 # all packets will be dropped by SPD rule
174 self.pg1.assert_nothing_captured()
175 # verify all policies matched the expected number of times
176 self.verify_policy_match(pkt_count, policy_0)
177 self.verify_policy_match(pkt_count, policy_1)
178 # previous stale entry in flow cache should have been overwritten,
179 # with one active entry
180 self.verify_num_outbound_flow_cache_entries(1)
183 class IPSec4SpdTestCaseReaddOutbound(SpdFlowCacheOutbound):
184 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
185 (add, remove, re-add)"""
187 def test_ipsec_spd_outbound_readd(self):
188 # In this test case, packets in IPv4 FWD path are configured
189 # to go through IPSec outbound SPD policy lookup.
190 # 2 SPD rules (1 HIGH and 1 LOW) are added.
191 # High priority rule action is set to BYPASS.
192 # Low priority rule action is set to DISCARD.
193 # Traffic sent on pg0 interface should match high priority
194 # rule and should be sent out on pg1 interface.
195 # High priority rule is then removed.
196 # Traffic sent on pg0 interface should match low priority
197 # rule and should be discarded after SPD lookup.
198 # Readd high priority rule.
199 # Traffic sent on pg0 interface should match high priority
200 # rule and should be sent out on pg1 interface.
201 self.create_interfaces(2)
203 self.spd_create_and_intf_add(1, [self.pg1])
204 policy_0 = self.spd_add_rem_policy( # outbound, priority 10
211 policy_type="bypass",
213 policy_1 = self.spd_add_rem_policy( # outbound, priority 5
220 policy_type="discard",
223 # check flow cache is empty before sending traffic
224 self.verify_num_outbound_flow_cache_entries(0)
226 # create the packet stream
227 packets = self.create_stream(self.pg0, self.pg1, pkt_count)
228 # add the stream to the source interface + enable capture
229 self.pg0.add_stream(packets)
230 self.pg0.enable_capture()
231 self.pg1.enable_capture()
232 # start the packet generator
235 capture = self.pg1.get_capture()
236 for packet in capture:
238 self.logger.debug(ppp("SPD - Got packet:", packet))
240 self.logger.error(ppp("Unexpected or invalid packet:", packet))
242 self.logger.debug("SPD: Num packets: %s", len(capture.res))
244 # assert nothing captured on pg0
245 self.pg0.assert_nothing_captured()
246 # verify capture on pg1
247 self.verify_capture(self.pg0, self.pg1, capture)
248 # verify all policies matched the expected number of times
249 self.verify_policy_match(pkt_count, policy_0)
250 self.verify_policy_match(0, policy_1)
251 # check policy in SPD has been cached after traffic
252 # matched BYPASS rule in SPD
253 self.verify_num_outbound_flow_cache_entries(1)
255 # now remove the bypass rule, leaving only the discard rule
256 self.spd_add_rem_policy( # outbound, priority 10
263 policy_type="bypass",
266 # verify flow cache counter has been reset by rule removal
267 self.verify_num_outbound_flow_cache_entries(0)
269 # resend the same packets
270 self.pg0.add_stream(packets)
271 self.pg0.enable_capture() # flush the old captures
272 self.pg1.enable_capture()
275 # assert nothing captured on pg0
276 self.pg0.assert_nothing_captured()
277 # all packets will be dropped by SPD rule
278 self.pg1.assert_nothing_captured()
279 # verify all policies matched the expected number of times
280 self.verify_policy_match(pkt_count, policy_0)
281 self.verify_policy_match(pkt_count, policy_1)
282 # previous stale entry in flow cache should have been overwritten
283 self.verify_num_outbound_flow_cache_entries(1)
285 # now readd the bypass rule
286 policy_0 = self.spd_add_rem_policy( # outbound, priority 10
293 policy_type="bypass",
295 # verify flow cache counter has been reset by rule addition
296 self.verify_num_outbound_flow_cache_entries(0)
298 # resend the same packets
299 self.pg0.add_stream(packets)
300 self.pg0.enable_capture() # flush the old captures
301 self.pg1.enable_capture()
305 capture = self.pg1.get_capture(pkt_count)
306 for packet in capture:
308 self.logger.debug(ppp("SPD - Got packet:", packet))
310 self.logger.error(ppp("Unexpected or invalid packet:", packet))
312 self.logger.debug("SPD: Num packets: %s", len(capture.res))
314 # assert nothing captured on pg0
315 self.pg0.assert_nothing_captured()
316 # verify captured packets
317 self.verify_capture(self.pg0, self.pg1, capture)
318 # verify all policies matched the expected number of times
319 self.verify_policy_match(pkt_count, policy_0)
320 self.verify_policy_match(pkt_count, policy_1)
321 # previous stale entry in flow cache should have been overwritten
322 self.verify_num_outbound_flow_cache_entries(1)
325 class IPSec4SpdTestCaseMultipleOutbound(SpdFlowCacheOutbound):
326 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
327 (multiple interfaces, multiple rules)"""
329 def test_ipsec_spd_outbound_multiple(self):
330 # In this test case, packets in IPv4 FWD path are configured to go
331 # through IPSec outbound SPD policy lookup.
332 # Multiples rules on multiple interfaces are tested at the same time.
333 # 3x interfaces are configured, binding the same SPD to each.
334 # Each interface has 2 SPD rules (1 BYPASS and 1 DISCARD).
335 # On pg0 & pg1, the BYPASS rule is HIGH priority
336 # On pg2, the DISCARD rule is HIGH priority
337 # Traffic should be received on pg0 & pg1 and dropped on pg2.
338 self.create_interfaces(3)
340 # bind SPD to all interfaces
341 self.spd_create_and_intf_add(1, self.pg_interfaces)
342 # add rules on all interfaces
343 policy_01 = self.spd_add_rem_policy( # outbound, priority 10
350 policy_type="bypass",
352 policy_02 = self.spd_add_rem_policy( # outbound, priority 5
359 policy_type="discard",
362 policy_11 = self.spd_add_rem_policy( # outbound, priority 10
369 policy_type="bypass",
371 policy_12 = self.spd_add_rem_policy( # outbound, priority 5
378 policy_type="discard",
381 policy_21 = self.spd_add_rem_policy( # outbound, priority 5
388 policy_type="bypass",
390 policy_22 = self.spd_add_rem_policy( # outbound, priority 10
397 policy_type="discard",
400 # interfaces bound to an SPD, will by default drop inbound
401 # traffic with no matching policies. add catch-all inbound
402 # bypass rule to SPD:
403 self.spd_add_rem_policy( # inbound, all interfaces
410 policy_type="bypass",
414 # check flow cache is empty (0 active elements) before sending traffic
415 self.verify_num_outbound_flow_cache_entries(0)
417 # create the packet streams
418 packets0 = self.create_stream(self.pg0, self.pg1, pkt_count)
419 packets1 = self.create_stream(self.pg1, self.pg2, pkt_count)
420 packets2 = self.create_stream(self.pg2, self.pg0, pkt_count)
421 # add the streams to the source interfaces
422 self.pg0.add_stream(packets0)
423 self.pg1.add_stream(packets1)
424 self.pg2.add_stream(packets2)
425 # enable capture on all interfaces
426 for pg in self.pg_interfaces:
428 # start the packet generator
433 for pg in [self.pg1, self.pg2]: # we are expecting captures on pg1/pg2
434 if_caps.append(pg.get_capture())
435 for packet in if_caps[-1]:
437 self.logger.debug(ppp("SPD - Got packet:", packet))
439 self.logger.error(ppp("Unexpected or invalid packet:", packet))
441 self.logger.debug("SPD: Num packets: %s", len(if_caps[0].res))
442 self.logger.debug("SPD: Num packets: %s", len(if_caps[1].res))
444 # verify captures that matched BYPASS rule
445 self.verify_capture(self.pg0, self.pg1, if_caps[0])
446 self.verify_capture(self.pg1, self.pg2, if_caps[1])
447 # verify that traffic to pg0 matched DISCARD rule and was dropped
448 self.pg0.assert_nothing_captured()
449 # verify all packets that were expected to match rules, matched
451 self.verify_policy_match(pkt_count, policy_01)
452 self.verify_policy_match(0, policy_02)
454 self.verify_policy_match(pkt_count, policy_11)
455 self.verify_policy_match(0, policy_12)
457 self.verify_policy_match(0, policy_21)
458 self.verify_policy_match(pkt_count, policy_22)
459 # check that 3 matching policies in SPD have been cached
460 self.verify_num_outbound_flow_cache_entries(3)
463 class IPSec4SpdTestCaseOverwriteStaleOutbound(SpdFlowCacheOutbound):
464 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
465 (overwrite stale entries)"""
467 def test_ipsec_spd_outbound_overwrite(self):
468 # The operation of the flow cache is setup so that the entire cache
469 # is invalidated when adding or removing an SPD policy rule.
470 # For performance, old cache entries are not zero'd, but remain
471 # in the table as "stale" entries. If a flow matches a stale entry,
472 # and the epoch count does NOT match the current count, the entry
474 # In this test, 3 active rules are created and matched to enter
475 # them into the flow cache.
476 # A single entry is removed to invalidate the entire cache.
477 # We then readd the rule and test that overwriting of the previous
478 # stale entries occurs as expected, and that the flow cache entry
479 # counter is updated correctly.
480 self.create_interfaces(3)
482 # bind SPD to all interfaces
483 self.spd_create_and_intf_add(1, self.pg_interfaces)
484 # add output rules on all interfaces
486 policy_0 = self.spd_add_rem_policy( # outbound
493 policy_type="bypass",
496 policy_1 = self.spd_add_rem_policy( # outbound
503 policy_type="bypass",
506 policy_2 = self.spd_add_rem_policy( # outbound
513 policy_type="discard",
516 # interfaces bound to an SPD, will by default drop inbound
517 # traffic with no matching policies. add catch-all inbound
518 # bypass rule to SPD:
519 self.spd_add_rem_policy( # inbound, all interfaces
526 policy_type="bypass",
530 # check flow cache is empty (0 active elements) before sending traffic
531 self.verify_num_outbound_flow_cache_entries(0)
533 # create the packet streams
534 packets0 = self.create_stream(self.pg0, self.pg1, pkt_count)
535 packets1 = self.create_stream(self.pg1, self.pg2, pkt_count)
536 packets2 = self.create_stream(self.pg2, self.pg0, pkt_count)
537 # add the streams to the source interfaces
538 self.pg0.add_stream(packets0)
539 self.pg1.add_stream(packets1)
540 self.pg2.add_stream(packets2)
541 # enable capture on all interfaces
542 for pg in self.pg_interfaces:
544 # start the packet generator
547 # get captures from ifs
549 for pg in [self.pg1, self.pg2]: # we are expecting captures on pg1/pg2
550 if_caps.append(pg.get_capture())
551 for packet in if_caps[-1]:
553 self.logger.debug(ppp("SPD Add - Got packet:", packet))
555 self.logger.error(ppp("Unexpected or invalid packet:", packet))
558 # verify captures that matched BYPASS rules
559 self.verify_capture(self.pg0, self.pg1, if_caps[0])
560 self.verify_capture(self.pg1, self.pg2, if_caps[1])
561 # verify that traffic to pg0 matched DISCARD rule and was dropped
562 self.pg0.assert_nothing_captured()
563 # verify all policies matched the expected number of times
564 self.verify_policy_match(pkt_count, policy_0)
565 self.verify_policy_match(pkt_count, policy_1)
566 self.verify_policy_match(pkt_count, policy_2)
567 # check flow/policy match was cached for: 3x output policies
568 self.verify_num_outbound_flow_cache_entries(3)
570 # adding an inbound policy should not invalidate output flow cache
571 self.spd_add_rem_policy( # inbound
578 policy_type="bypass",
580 # check flow cache counter has not been reset
581 self.verify_num_outbound_flow_cache_entries(3)
583 # remove a bypass policy - flow cache counter will be reset, and
584 # there will be 3x stale entries in flow cache
585 self.spd_add_rem_policy( # outbound
592 policy_type="bypass",
596 policy_0 = self.spd_add_rem_policy( # outbound
603 policy_type="bypass",
605 # check counter was reset with flow cache invalidation
606 self.verify_num_outbound_flow_cache_entries(0)
608 # resend the same packets
609 self.pg0.add_stream(packets0)
610 self.pg1.add_stream(packets1)
611 self.pg2.add_stream(packets2)
612 for pg in self.pg_interfaces:
613 pg.enable_capture() # flush previous captures
616 # get captures from ifs
618 for pg in [self.pg1, self.pg2]: # we are expecting captures on pg1/pg2
619 if_caps.append(pg.get_capture())
620 for packet in if_caps[-1]:
622 self.logger.debug(ppp("SPD Add - Got packet:", packet))
624 self.logger.error(ppp("Unexpected or invalid packet:", packet))
627 # verify captures that matched BYPASS rules
628 self.verify_capture(self.pg0, self.pg1, if_caps[0])
629 self.verify_capture(self.pg1, self.pg2, if_caps[1])
630 # verify that traffic to pg0 matched DISCARD rule and was dropped
631 self.pg0.assert_nothing_captured()
632 # verify all policies matched the expected number of times
633 self.verify_policy_match(pkt_count, policy_0)
634 self.verify_policy_match(pkt_count * 2, policy_1)
635 self.verify_policy_match(pkt_count * 2, policy_2)
636 # we are overwriting 3x stale entries - check flow cache counter
638 self.verify_num_outbound_flow_cache_entries(3)
641 class IPSec4SpdTestCaseCollisionOutbound(SpdFlowCacheOutbound):
642 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
645 # Override class setup to restrict vector size to 16 elements.
646 # This forces using only the lower 4 bits of the hash as a key,
647 # making hash collisions easy to find.
649 def setUpConstants(cls):
650 super(SpdFlowCacheOutbound, cls).setUpConstants()
651 cls.vpp_cmdline.extend(
655 "ipv4-outbound-spd-flow-cache on",
656 "ipv4-outbound-spd-hash-buckets 16",
660 cls.logger.info("VPP modified cmdline is %s" % " ".join(cls.vpp_cmdline))
662 def test_ipsec_spd_outbound_collision(self):
663 # The flow cache operation is setup to overwrite an entry
664 # if a hash collision occurs.
665 # In this test, 2 packets are configured that result in a
666 # hash with the same lower 4 bits.
667 # After the first packet is received, there should be one
668 # active entry in the flow cache.
669 # After the second packet with the same lower 4 bit hash
670 # is received, this should overwrite the same entry.
671 # Therefore there will still be a total of one (1) entry,
672 # in the flow cache with two matching policies.
673 # crc32_supported() method is used to check cpu for crc32
674 # intrinsic support for hashing.
675 # If crc32 is not supported, we fall back to clib_xxhash()
676 self.create_interfaces(3)
678 # bind SPD to all interfaces
679 self.spd_create_and_intf_add(1, self.pg_interfaces)
681 policy_0 = self.spd_add_rem_policy( # outbound, priority 10
688 policy_type="bypass",
690 policy_1 = self.spd_add_rem_policy( # outbound, priority 10
697 policy_type="bypass",
700 # interfaces bound to an SPD, will by default drop inbound
701 # traffic with no matching policies. add catch-all inbound
702 # bypass rule to SPD:
703 self.spd_add_rem_policy( # inbound, all interfaces
710 policy_type="bypass",
714 # check flow cache is empty (0 active elements) before sending traffic
715 self.verify_num_outbound_flow_cache_entries(0)
717 # create the packet streams generating collision on last 4 bits
718 if self.crc32_supported():
721 packets1 = self.create_stream(self.pg1, self.pg2, pkt_count, 1, 1)
723 packets2 = self.create_stream(self.pg2, self.pg0, pkt_count, 6, 6)
726 packets1 = self.create_stream(self.pg1, self.pg2, pkt_count, 2, 2)
728 packets2 = self.create_stream(self.pg2, self.pg0, pkt_count, 3, 3)
730 # add the streams to the source interfaces
731 self.pg1.add_stream(packets1)
732 self.pg2.add_stream(packets2)
733 # enable capture on all interfaces
734 for pg in self.pg_interfaces:
736 # start the packet generator
739 # get captures from ifs - the proper pkt_count of packets was saved by
740 # create_packet_info() based on dst_if parameter
742 for pg in [self.pg2, self.pg0]: # we are expecting captures on pg2/pg0
743 if_caps.append(pg.get_capture())
744 for packet in if_caps[-1]:
746 self.logger.debug(ppp("SPD - Got packet:", packet))
748 self.logger.error(ppp("Unexpected or invalid packet:", packet))
750 self.logger.debug("SPD: Num packets: %s", len(if_caps[0].res))
751 self.logger.debug("SPD: Num packets: %s", len(if_caps[1].res))
753 # verify captures that matched BYPASS rule
754 self.verify_capture(self.pg1, self.pg2, if_caps[0])
755 self.verify_capture(self.pg2, self.pg0, if_caps[1])
756 # verify all packets that were expected to match rules, matched
757 self.verify_policy_match(pkt_count, policy_0)
758 self.verify_policy_match(pkt_count, policy_1)
759 # we have matched 2 policies, but due to the hash collision
760 # one active entry is expected
761 self.verify_num_outbound_flow_cache_entries(1)
764 if __name__ == "__main__":
765 unittest.main(testRunner=VppTestRunner)