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", "{",
15 "ipv4-outbound-spd-flow-cache on",
17 cls.logger.info("VPP modified cmdline is %s" % " "
18 .join(cls.vpp_cmdline))
21 class IPSec4SpdTestCaseAdd(SpdFlowCacheOutbound):
22 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
24 def test_ipsec_spd_outbound_add(self):
25 # In this test case, packets in IPv4 FWD path are configured
26 # to go through IPSec outbound SPD policy lookup.
27 # 2 SPD rules (1 HIGH and 1 LOW) are added.
28 # High priority rule action is set to BYPASS.
29 # Low priority rule action is set to DISCARD.
30 # Traffic sent on pg0 interface should match high priority
31 # rule and should be sent out on pg1 interface.
32 self.create_interfaces(2)
34 self.spd_create_and_intf_add(1, [self.pg1])
35 policy_0 = self.spd_add_rem_policy( # outbound, priority 10
36 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
37 is_out=1, priority=10, policy_type="bypass")
38 policy_1 = self.spd_add_rem_policy( # outbound, priority 5
39 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
40 is_out=1, priority=5, policy_type="discard")
42 # check flow cache is empty before sending traffic
43 self.verify_num_outbound_flow_cache_entries(0)
45 # create the packet stream
46 packets = self.create_stream(self.pg0, self.pg1, pkt_count)
47 # add the stream to the source interface + enable capture
48 self.pg0.add_stream(packets)
49 self.pg0.enable_capture()
50 self.pg1.enable_capture()
51 # start the packet generator
54 capture = self.pg1.get_capture()
55 for packet in capture:
57 self.logger.debug(ppp("SPD - Got packet:", packet))
59 self.logger.error(ppp("Unexpected or invalid packet:", packet))
61 self.logger.debug("SPD: Num packets: %s", len(capture.res))
63 # assert nothing captured on pg0
64 self.pg0.assert_nothing_captured()
65 # verify captured packets
66 self.verify_capture(self.pg0, self.pg1, capture)
67 # verify all policies matched the expected number of times
68 self.verify_policy_match(pkt_count, policy_0)
69 self.verify_policy_match(0, policy_1)
70 # check policy in SPD has been cached after traffic
71 # matched BYPASS rule in SPD
72 self.verify_num_outbound_flow_cache_entries(1)
75 class IPSec4SpdTestCaseRemove(SpdFlowCacheOutbound):
76 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
78 def test_ipsec_spd_outbound_remove(self):
79 # In this test case, packets in IPv4 FWD path are configured
80 # to go through IPSec outbound SPD policy lookup.
81 # 2 SPD rules (1 HIGH and 1 LOW) are added.
82 # High priority rule action is set to BYPASS.
83 # Low priority rule action is set to DISCARD.
84 # High priority rule is then removed.
85 # Traffic sent on pg0 interface should match low priority
86 # rule and should be discarded after SPD lookup.
87 self.create_interfaces(2)
89 self.spd_create_and_intf_add(1, [self.pg1])
90 policy_0 = self.spd_add_rem_policy( # outbound, priority 10
91 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
92 is_out=1, priority=10, policy_type="bypass")
93 policy_1 = self.spd_add_rem_policy( # outbound, priority 5
94 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
95 is_out=1, priority=5, policy_type="discard")
97 # check flow cache is empty before sending traffic
98 self.verify_num_outbound_flow_cache_entries(0)
100 # create the packet stream
101 packets = self.create_stream(self.pg0, self.pg1, pkt_count)
102 # add the stream to the source interface + enable capture
103 self.pg0.add_stream(packets)
104 self.pg0.enable_capture()
105 self.pg1.enable_capture()
106 # start the packet generator
109 capture = self.pg1.get_capture()
110 for packet in capture:
112 self.logger.debug(ppp("SPD - Got packet:", packet))
114 self.logger.error(ppp("Unexpected or invalid packet:", packet))
117 # assert nothing captured on pg0
118 self.pg0.assert_nothing_captured()
119 # verify capture on pg1
120 self.logger.debug("SPD: Num packets: %s", len(capture.res))
121 self.verify_capture(self.pg0, self.pg1, capture)
122 # verify all policies matched the expected number of times
123 self.verify_policy_match(pkt_count, policy_0)
124 self.verify_policy_match(0, policy_1)
125 # check policy in SPD has been cached after traffic
126 # matched BYPASS rule in SPD
127 self.verify_num_outbound_flow_cache_entries(1)
129 # now remove the bypass rule
130 self.spd_add_rem_policy( # outbound, priority 10
131 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
132 is_out=1, priority=10, policy_type="bypass",
134 # verify flow cache counter has been reset by rule removal
135 self.verify_num_outbound_flow_cache_entries(0)
137 # resend the same packets
138 self.pg0.add_stream(packets)
139 self.pg0.enable_capture() # flush the old captures
140 self.pg1.enable_capture()
142 # assert nothing captured on pg0
143 self.pg0.assert_nothing_captured()
144 # all packets will be dropped by SPD rule
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(pkt_count, policy_1)
149 # previous stale entry in flow cache should have been overwritten,
150 # with one active entry
151 self.verify_num_outbound_flow_cache_entries(1)
154 class IPSec4SpdTestCaseReadd(SpdFlowCacheOutbound):
155 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
156 (add, remove, re-add)"""
157 def test_ipsec_spd_outbound_readd(self):
158 # In this test case, packets in IPv4 FWD path are configured
159 # to go through IPSec outbound SPD policy lookup.
160 # 2 SPD rules (1 HIGH and 1 LOW) are added.
161 # High priority rule action is set to BYPASS.
162 # Low priority rule action is set to DISCARD.
163 # Traffic sent on pg0 interface should match high priority
164 # rule and should be sent out on pg1 interface.
165 # High priority rule is then removed.
166 # Traffic sent on pg0 interface should match low priority
167 # rule and should be discarded after SPD lookup.
168 # Readd high priority rule.
169 # Traffic sent on pg0 interface should match high priority
170 # rule and should be sent out on pg1 interface.
171 self.create_interfaces(2)
173 self.spd_create_and_intf_add(1, [self.pg1])
174 policy_0 = self.spd_add_rem_policy( # outbound, priority 10
175 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
176 is_out=1, priority=10, policy_type="bypass")
177 policy_1 = self.spd_add_rem_policy( # outbound, priority 5
178 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
179 is_out=1, priority=5, policy_type="discard")
181 # check flow cache is empty before sending traffic
182 self.verify_num_outbound_flow_cache_entries(0)
184 # create the packet stream
185 packets = self.create_stream(self.pg0, self.pg1, pkt_count)
186 # add the stream to the source interface + enable capture
187 self.pg0.add_stream(packets)
188 self.pg0.enable_capture()
189 self.pg1.enable_capture()
190 # start the packet generator
193 capture = self.pg1.get_capture()
194 for packet in capture:
196 self.logger.debug(ppp("SPD - Got packet:", packet))
198 self.logger.error(ppp("Unexpected or invalid packet:", packet))
200 self.logger.debug("SPD: Num packets: %s", len(capture.res))
202 # assert nothing captured on pg0
203 self.pg0.assert_nothing_captured()
204 # verify capture on pg1
205 self.verify_capture(self.pg0, self.pg1, capture)
206 # verify all policies matched the expected number of times
207 self.verify_policy_match(pkt_count, policy_0)
208 self.verify_policy_match(0, policy_1)
209 # check policy in SPD has been cached after traffic
210 # matched BYPASS rule in SPD
211 self.verify_num_outbound_flow_cache_entries(1)
213 # now remove the bypass rule, leaving only the discard rule
214 self.spd_add_rem_policy( # outbound, priority 10
215 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
216 is_out=1, priority=10, policy_type="bypass",
218 # verify flow cache counter has been reset by rule removal
219 self.verify_num_outbound_flow_cache_entries(0)
221 # resend the same packets
222 self.pg0.add_stream(packets)
223 self.pg0.enable_capture() # flush the old captures
224 self.pg1.enable_capture()
227 # assert nothing captured on pg0
228 self.pg0.assert_nothing_captured()
229 # all packets will be dropped by SPD rule
230 self.pg1.assert_nothing_captured()
231 # verify all policies matched the expected number of times
232 self.verify_policy_match(pkt_count, policy_0)
233 self.verify_policy_match(pkt_count, policy_1)
234 # previous stale entry in flow cache should have been overwritten
235 self.verify_num_outbound_flow_cache_entries(1)
237 # now readd the bypass rule
238 policy_0 = self.spd_add_rem_policy( # outbound, priority 10
239 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
240 is_out=1, priority=10, policy_type="bypass")
241 # verify flow cache counter has been reset by rule addition
242 self.verify_num_outbound_flow_cache_entries(0)
244 # resend the same packets
245 self.pg0.add_stream(packets)
246 self.pg0.enable_capture() # flush the old captures
247 self.pg1.enable_capture()
251 capture = self.pg1.get_capture(pkt_count)
252 for packet in capture:
254 self.logger.debug(ppp("SPD - Got packet:", packet))
256 self.logger.error(ppp("Unexpected or invalid packet:", packet))
258 self.logger.debug("SPD: Num packets: %s", len(capture.res))
260 # assert nothing captured on pg0
261 self.pg0.assert_nothing_captured()
262 # verify captured packets
263 self.verify_capture(self.pg0, self.pg1, capture)
264 # verify all policies matched the expected number of times
265 self.verify_policy_match(pkt_count, policy_0)
266 self.verify_policy_match(pkt_count, policy_1)
267 # previous stale entry in flow cache should have been overwritten
268 self.verify_num_outbound_flow_cache_entries(1)
271 class IPSec4SpdTestCaseMultiple(SpdFlowCacheOutbound):
272 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
273 (multiple interfaces, multiple rules)"""
274 def test_ipsec_spd_outbound_multiple(self):
275 # In this test case, packets in IPv4 FWD path are configured to go
276 # through IPSec outbound SPD policy lookup.
277 # Multiples rules on multiple interfaces are tested at the same time.
278 # 3x interfaces are configured, binding the same SPD to each.
279 # Each interface has 2 SPD rules (1 BYPASS and 1 DISCARD).
280 # On pg0 & pg1, the BYPASS rule is HIGH priority
281 # On pg2, the DISCARD rule is HIGH priority
282 # Traffic should be received on pg0 & pg1 and dropped on pg2.
283 self.create_interfaces(3)
285 # bind SPD to all interfaces
286 self.spd_create_and_intf_add(1, self.pg_interfaces)
287 # add rules on all interfaces
288 policy_01 = self.spd_add_rem_policy( # outbound, priority 10
289 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
290 is_out=1, priority=10, policy_type="bypass")
291 policy_02 = self.spd_add_rem_policy( # outbound, priority 5
292 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
293 is_out=1, priority=5, policy_type="discard")
295 policy_11 = self.spd_add_rem_policy( # outbound, priority 10
296 1, self.pg1, self.pg2, socket.IPPROTO_UDP,
297 is_out=1, priority=10, policy_type="bypass")
298 policy_12 = self.spd_add_rem_policy( # outbound, priority 5
299 1, self.pg1, self.pg2, socket.IPPROTO_UDP,
300 is_out=1, priority=5, policy_type="discard")
302 policy_21 = self.spd_add_rem_policy( # outbound, priority 5
303 1, self.pg2, self.pg0, socket.IPPROTO_UDP,
304 is_out=1, priority=5, policy_type="bypass")
305 policy_22 = self.spd_add_rem_policy( # outbound, priority 10
306 1, self.pg2, self.pg0, socket.IPPROTO_UDP,
307 is_out=1, priority=10, policy_type="discard")
309 # check flow cache is empty (0 active elements) before sending traffic
310 self.verify_num_outbound_flow_cache_entries(0)
312 # create the packet streams
313 packets0 = self.create_stream(self.pg0, self.pg1, pkt_count)
314 packets1 = self.create_stream(self.pg1, self.pg2, pkt_count)
315 packets2 = self.create_stream(self.pg2, self.pg0, pkt_count)
316 # add the streams to the source interfaces
317 self.pg0.add_stream(packets0)
318 self.pg1.add_stream(packets1)
319 self.pg2.add_stream(packets2)
320 # enable capture on all interfaces
321 for pg in self.pg_interfaces:
323 # start the packet generator
328 for pg in [self.pg1, self.pg2]: # we are expecting captures on pg1/pg2
329 if_caps.append(pg.get_capture())
330 for packet in if_caps[-1]:
332 self.logger.debug(ppp("SPD - Got packet:", packet))
335 ppp("Unexpected or invalid packet:", packet))
337 self.logger.debug("SPD: Num packets: %s", len(if_caps[0].res))
338 self.logger.debug("SPD: Num packets: %s", len(if_caps[1].res))
340 # verify captures that matched BYPASS rule
341 self.verify_capture(self.pg0, self.pg1, if_caps[0])
342 self.verify_capture(self.pg1, self.pg2, if_caps[1])
343 # verify that traffic to pg0 matched DISCARD rule and was dropped
344 self.pg0.assert_nothing_captured()
345 # verify all packets that were expected to match rules, matched
347 self.verify_policy_match(pkt_count, policy_01)
348 self.verify_policy_match(0, policy_02)
350 self.verify_policy_match(pkt_count, policy_11)
351 self.verify_policy_match(0, policy_12)
353 self.verify_policy_match(0, policy_21)
354 self.verify_policy_match(pkt_count, policy_22)
355 # check that 3 matching policies in SPD have been cached
356 self.verify_num_outbound_flow_cache_entries(3)
359 class IPSec4SpdTestCaseOverwriteStale(SpdFlowCacheOutbound):
360 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
361 (overwrite stale entries)"""
362 def test_ipsec_spd_outbound_overwrite(self):
363 # The operation of the flow cache is setup so that the entire cache
364 # is invalidated when adding or removing an SPD policy rule.
365 # For performance, old cache entries are not zero'd, but remain
366 # in the table as "stale" entries. If a flow matches a stale entry,
367 # and the epoch count does NOT match the current count, the entry
369 # In this test, 3 active rules are created and matched to enter
370 # them into the flow cache.
371 # A single entry is removed to invalidate the entire cache.
372 # We then readd the rule and test that overwriting of the previous
373 # stale entries occurs as expected, and that the flow cache entry
374 # counter is updated correctly.
375 self.create_interfaces(3)
377 # bind SPD to all interfaces
378 self.spd_create_and_intf_add(1, self.pg_interfaces)
379 # add output rules on all interfaces
381 policy_0 = self.spd_add_rem_policy( # outbound
382 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
383 is_out=1, priority=10, policy_type="bypass")
385 policy_1 = self.spd_add_rem_policy( # outbound
386 1, self.pg1, self.pg2, socket.IPPROTO_UDP,
387 is_out=1, priority=10, policy_type="bypass")
389 policy_2 = self.spd_add_rem_policy( # outbound
390 1, self.pg2, self.pg0, socket.IPPROTO_UDP,
391 is_out=1, priority=10, policy_type="discard")
393 # check flow cache is empty (0 active elements) before sending traffic
394 self.verify_num_outbound_flow_cache_entries(0)
396 # create the packet streams
397 packets0 = self.create_stream(self.pg0, self.pg1, pkt_count)
398 packets1 = self.create_stream(self.pg1, self.pg2, pkt_count)
399 packets2 = self.create_stream(self.pg2, self.pg0, pkt_count)
400 # add the streams to the source interfaces
401 self.pg0.add_stream(packets0)
402 self.pg1.add_stream(packets1)
403 self.pg2.add_stream(packets2)
404 # enable capture on all interfaces
405 for pg in self.pg_interfaces:
407 # start the packet generator
410 # get captures from ifs
412 for pg in [self.pg1, self.pg2]: # we are expecting captures on pg1/pg2
413 if_caps.append(pg.get_capture())
414 for packet in if_caps[-1]:
416 self.logger.debug(ppp("SPD Add - Got packet:", packet))
419 ppp("Unexpected or invalid packet:", packet))
422 # verify captures that matched BYPASS rules
423 self.verify_capture(self.pg0, self.pg1, if_caps[0])
424 self.verify_capture(self.pg1, self.pg2, if_caps[1])
425 # verify that traffic to pg0 matched DISCARD rule and was dropped
426 self.pg0.assert_nothing_captured()
427 # verify all policies matched the expected number of times
428 self.verify_policy_match(pkt_count, policy_0)
429 self.verify_policy_match(pkt_count, policy_1)
430 self.verify_policy_match(pkt_count, policy_2)
431 # check flow/policy match was cached for: 3x output policies
432 self.verify_num_outbound_flow_cache_entries(3)
434 # adding an inbound policy should not invalidate output flow cache
435 self.spd_add_rem_policy( # inbound
436 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
437 is_out=0, priority=10, policy_type="bypass")
438 # check flow cache counter has not been reset
439 self.verify_num_outbound_flow_cache_entries(3)
441 # remove a bypass policy - flow cache counter will be reset, and
442 # there will be 3x stale entries in flow cache
443 self.spd_add_rem_policy( # outbound
444 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
445 is_out=1, priority=10, policy_type="bypass",
448 policy_0 = self.spd_add_rem_policy( # outbound
449 1, self.pg0, self.pg1, socket.IPPROTO_UDP,
450 is_out=1, priority=10, policy_type="bypass")
451 # check counter was reset with flow cache invalidation
452 self.verify_num_outbound_flow_cache_entries(0)
454 # resend the same packets
455 self.pg0.add_stream(packets0)
456 self.pg1.add_stream(packets1)
457 self.pg2.add_stream(packets2)
458 for pg in self.pg_interfaces:
459 pg.enable_capture() # flush previous captures
462 # get captures from ifs
464 for pg in [self.pg1, self.pg2]: # we are expecting captures on pg1/pg2
465 if_caps.append(pg.get_capture())
466 for packet in if_caps[-1]:
468 self.logger.debug(ppp("SPD Add - Got packet:", packet))
471 ppp("Unexpected or invalid packet:", packet))
474 # verify captures that matched BYPASS rules
475 self.verify_capture(self.pg0, self.pg1, if_caps[0])
476 self.verify_capture(self.pg1, self.pg2, if_caps[1])
477 # verify that traffic to pg0 matched DISCARD rule and was dropped
478 self.pg0.assert_nothing_captured()
479 # verify all policies matched the expected number of times
480 self.verify_policy_match(pkt_count, policy_0)
481 self.verify_policy_match(pkt_count*2, policy_1)
482 self.verify_policy_match(pkt_count*2, policy_2)
483 # we are overwriting 3x stale entries - check flow cache counter
485 self.verify_num_outbound_flow_cache_entries(3)
488 class IPSec4SpdTestCaseCollision(SpdFlowCacheOutbound):
489 """ IPSec/IPv4 outbound: Policy mode test case with flow cache \
491 # Override class setup to restrict vector size to 16 elements.
492 # This forces using only the lower 4 bits of the hash as a key,
493 # making hash collisions easy to find.
495 def setUpConstants(cls):
496 super(SpdFlowCacheOutbound, cls).setUpConstants()
497 cls.vpp_cmdline.extend(["ipsec", "{",
498 "ipv4-outbound-spd-flow-cache on",
499 "ipv4-outbound-spd-hash-buckets 16",
501 cls.logger.info("VPP modified cmdline is %s" % " "
502 .join(cls.vpp_cmdline))
504 def test_ipsec_spd_outbound_collision(self):
505 # The flow cache operation is setup to overwrite an entry
506 # if a hash collision occurs.
507 # In this test, 2 packets are configured that result in a
508 # hash with the same lower 4 bits.
509 # After the first packet is received, there should be one
510 # active entry in the flow cache.
511 # After the second packet with the same lower 4 bit hash
512 # is received, this should overwrite the same entry.
513 # Therefore there will still be a total of one (1) entry,
514 # in the flow cache with two matching policies.
515 # crc32_supported() method is used to check cpu for crc32
516 # intrinsic support for hashing.
517 # If crc32 is not supported, we fall back to clib_xxhash()
518 self.create_interfaces(3)
520 # bind SPD to all interfaces
521 self.spd_create_and_intf_add(1, self.pg_interfaces)
523 policy_0 = self.spd_add_rem_policy( # outbound, priority 10
524 1, self.pg1, self.pg2, socket.IPPROTO_UDP,
525 is_out=1, priority=10, policy_type="bypass")
526 policy_1 = self.spd_add_rem_policy( # outbound, priority 10
527 1, self.pg2, self.pg0, socket.IPPROTO_UDP,
528 is_out=1, priority=10, 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 generating collision on last 4 bits
534 if self.crc32_supported():
537 packets1 = self.create_stream(self.pg1, self.pg2, pkt_count, 1, 1)
539 packets2 = self.create_stream(self.pg2, self.pg0, pkt_count, 6, 6)
542 packets1 = self.create_stream(self.pg1, self.pg2, pkt_count, 2, 2)
544 packets2 = self.create_stream(self.pg2, self.pg0, pkt_count, 3, 3)
546 # add the streams to the source interfaces
547 self.pg1.add_stream(packets1)
548 self.pg2.add_stream(packets2)
549 # enable capture on all interfaces
550 for pg in self.pg_interfaces:
552 # start the packet generator
555 # get captures from ifs - the proper pkt_count of packets was saved by
556 # create_packet_info() based on dst_if parameter
558 for pg in [self.pg2, self.pg0]: # we are expecting captures on pg2/pg0
559 if_caps.append(pg.get_capture())
560 for packet in if_caps[-1]:
562 self.logger.debug(ppp(
563 "SPD - Got packet:", packet))
565 self.logger.error(ppp(
566 "Unexpected or invalid packet:", packet))
568 self.logger.debug("SPD: Num packets: %s", len(if_caps[0].res))
569 self.logger.debug("SPD: Num packets: %s", len(if_caps[1].res))
571 # verify captures that matched BYPASS rule
572 self.verify_capture(self.pg1, self.pg2, if_caps[0])
573 self.verify_capture(self.pg2, self.pg0, if_caps[1])
574 # verify all packets that were expected to match rules, matched
575 self.verify_policy_match(pkt_count, policy_0)
576 self.verify_policy_match(pkt_count, policy_1)
577 # we have matched 2 policies, but due to the hash collision
578 # one active entry is expected
579 self.verify_num_outbound_flow_cache_entries(1)
582 if __name__ == '__main__':
583 unittest.main(testRunner=VppTestRunner)