2 """ ACL plugin extended stateful tests """
5 from framework import VppTestCase, VppTestRunner, running_extended_tests
6 from scapy.layers.l2 import Ether
7 from scapy.packet import Raw
8 from scapy.layers.inet import IP, UDP, TCP
9 from scapy.packet import Packet
10 from socket import inet_pton, AF_INET, AF_INET6
11 from scapy.layers.inet6 import IPv6, ICMPv6Unknown, ICMPv6EchoRequest
12 from scapy.layers.inet6 import ICMPv6EchoReply, IPv6ExtHdrRouting
13 from scapy.layers.inet6 import IPv6ExtHdrFragment
14 from pprint import pprint
15 from random import randint
18 def to_acl_rule(self, is_permit, wildcard_sport=False):
20 rule_family = AF_INET6 if p.haslayer(IPv6) else AF_INET
21 rule_prefix_len = 128 if p.haslayer(IPv6) else 32
22 rule_l3_layer = IPv6 if p.haslayer(IPv6) else IP
23 rule_l4_sport = p.sport
24 rule_l4_dport = p.dport
26 rule_l4_proto = p[IPv6].nh
28 rule_l4_proto = p[IP].proto
31 rule_l4_sport_first = 0
32 rule_l4_sport_last = 65535
34 rule_l4_sport_first = rule_l4_sport
35 rule_l4_sport_last = rule_l4_sport
38 'is_permit': is_permit,
39 'is_ipv6': p.haslayer(IPv6),
40 'src_ip_addr': inet_pton(rule_family,
41 p[rule_l3_layer].src),
42 'src_ip_prefix_len': rule_prefix_len,
43 'dst_ip_addr': inet_pton(rule_family,
44 p[rule_l3_layer].dst),
45 'dst_ip_prefix_len': rule_prefix_len,
46 'srcport_or_icmptype_first': rule_l4_sport_first,
47 'srcport_or_icmptype_last': rule_l4_sport_last,
48 'dstport_or_icmpcode_first': rule_l4_dport,
49 'dstport_or_icmpcode_last': rule_l4_dport,
50 'proto': rule_l4_proto,
54 Packet.to_acl_rule = to_acl_rule
57 class IterateWithSleep():
58 def __init__(self, testcase, n_iters, description, sleep_sec):
60 self.testcase = testcase
61 self.n_iters = n_iters
62 self.sleep_sec = sleep_sec
63 self.description = description
66 for x in range(0, self.n_iters):
68 self.testcase.sleep(self.sleep_sec)
72 def __init__(self, testcase, if1, if2, af, l4proto, port1, port2):
73 self.testcase = testcase
74 self.ifs = [None, None]
77 self.address_family = af
78 self.l4proto = l4proto
79 self.ports = [None, None]
84 def pkt(self, side, flags=None):
85 is_ip6 = 1 if self.address_family == AF_INET6 else 0
90 layer_3 = [IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4),
91 IPv6(src=src_if.remote_ip6, dst=dst_if.remote_ip6)]
93 l4args = {'sport': self.ports[s0], 'dport': self.ports[s1]}
95 l4args['flags'] = flags
96 p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
98 self.l4proto(**l4args) /
102 def apply_acls(self, reflect_side, acl_side):
104 pkts.append(self.pkt(0))
105 pkts.append(self.pkt(1))
106 pkt = pkts[reflect_side]
109 r.append(pkt.to_acl_rule(2, wildcard_sport=True))
110 r.append(self.wildcard_rule(0))
111 res = self.testcase.api_acl_add_replace(0xffffffff, r)
112 self.testcase.assert_equal(res.retval, 0, "error adding ACL")
113 reflect_acl_index = res.acl_index
116 r.append(self.wildcard_rule(0))
117 res = self.testcase.api_acl_add_replace(0xffffffff, r)
118 self.testcase.assert_equal(res.retval, 0, "error adding deny ACL")
119 deny_acl_index = res.acl_index
121 if reflect_side == acl_side:
122 self.testcase.api_acl_interface_set_acl_list(
123 self.ifs[acl_side].sw_if_index, 2, 1,
126 self.testcase.api_acl_interface_set_acl_list(
127 self.ifs[1-acl_side].sw_if_index, 0, 0, [])
129 self.testcase.api_acl_interface_set_acl_list(
130 self.ifs[acl_side].sw_if_index, 2, 1,
133 self.testcase.api_acl_interface_set_acl_list(
134 self.ifs[1-acl_side].sw_if_index, 0, 0, [])
136 def wildcard_rule(self, is_permit):
137 any_addr = ["0.0.0.0", "::"]
138 rule_family = self.address_family
139 is_ip6 = 1 if rule_family == AF_INET6 else 0
141 'is_permit': is_permit,
143 'src_ip_addr': inet_pton(rule_family, any_addr[is_ip6]),
144 'src_ip_prefix_len': 0,
145 'dst_ip_addr': inet_pton(rule_family, any_addr[is_ip6]),
146 'dst_ip_prefix_len': 0,
147 'srcport_or_icmptype_first': 0,
148 'srcport_or_icmptype_last': 65535,
149 'dstport_or_icmpcode_first': 0,
150 'dstport_or_icmpcode_last': 65535,
155 def send(self, side, flags=None):
156 self.ifs[side].add_stream(self.pkt(side, flags))
157 self.ifs[1-side].enable_capture()
158 self.testcase.pg_start()
160 def recv(self, side):
161 p = self.ifs[side].wait_for_packet(1)
164 def send_through(self, side, flags=None):
165 self.send(side, flags)
166 p = self.recv(1-side)
169 def send_pingpong(self, side, flags1=None, flags2=None):
170 p1 = self.send_through(side, flags1)
171 p2 = self.send_through(1-side, flags2)
175 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
176 class ACLPluginConnTestCase(VppTestCase):
177 """ ACL plugin connection-oriented extended testcases """
180 def setUpClass(self):
181 super(ACLPluginConnTestCase, self).setUpClass()
183 self.create_pg_interfaces(range(2))
184 for i in self.pg_interfaces:
192 """Run standard test teardown and log various show commands
194 super(ACLPluginConnTestCase, self).tearDown()
195 if not self.vpp_dead:
196 self.logger.info(self.vapi.cli("show ip arp"))
197 self.logger.info(self.vapi.cli("show ip6 neighbors"))
198 self.logger.info(self.vapi.cli("show acl-plugin sessions"))
199 self.logger.info(self.vapi.cli("show acl-plugin acl"))
200 self.logger.info(self.vapi.cli("show acl-plugin interface"))
201 self.logger.info(self.vapi.cli("show acl-plugin tables"))
203 def api_acl_add_replace(self, acl_index, r, count=-1, tag="",
205 """Add/replace an ACL
207 :param int acl_index: ACL index to replace, 4294967295 to create new.
208 :param acl_rule r: ACL rules array.
209 :param str tag: symbolic tag (description) for this ACL.
210 :param int count: number of rules.
214 return self.vapi.api(self.vapi.papi.acl_add_replace,
215 {'acl_index': acl_index,
219 }, expected_retval=expected_retval)
221 def api_acl_interface_set_acl_list(self, sw_if_index, count, n_input, acls,
223 return self.vapi.api(self.vapi.papi.acl_interface_set_acl_list,
224 {'sw_if_index': sw_if_index,
228 }, expected_retval=expected_retval)
230 def api_acl_dump(self, acl_index, expected_retval=0):
231 return self.vapi.api(self.vapi.papi.acl_dump,
232 {'acl_index': acl_index},
233 expected_retval=expected_retval)
235 def run_basic_conn_test(self, af, acl_side):
236 """ Basic conn timeout test """
237 conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
238 conn1.apply_acls(0, acl_side)
239 conn1.send_through(0)
240 # the return packets should pass
241 conn1.send_through(1)
242 # send some packets on conn1, ensure it doesn't go away
243 for i in IterateWithSleep(self, 20, "Keep conn active", 0.3):
244 conn1.send_through(1)
245 # allow the conn to time out
246 for i in IterateWithSleep(self, 30, "Wait for timeout", 0.1):
248 # now try to send a packet on the reflected side
250 p2 = conn1.send_through(1).command()
252 # If we asserted while waiting, it's good.
253 # the conn should have timed out.
255 self.assert_equal(p2, None, "packet on long-idle conn")
257 def run_active_conn_test(self, af, acl_side):
258 """ Idle connection behind active connection test """
259 base = 10000 + 1000*acl_side
260 conn1 = Conn(self, self.pg0, self.pg1, af, UDP, base + 1, 2323)
261 conn2 = Conn(self, self.pg0, self.pg1, af, UDP, base + 2, 2323)
262 conn3 = Conn(self, self.pg0, self.pg1, af, UDP, base + 3, 2323)
263 conn1.apply_acls(0, acl_side)
266 # create and check that the conn2/3 work
268 conn2.send_pingpong(0)
270 conn3.send_pingpong(0)
271 # send some packets on conn1, keep conn2/3 idle
272 for i in IterateWithSleep(self, 20, "Keep conn active", 0.2):
273 conn1.send_through(1)
275 p2 = conn2.send_through(1).command()
277 # If we asserted while waiting, it's good.
278 # the conn should have timed out.
280 # We should have not received the packet on a long-idle
281 # connection, because it should have timed out
282 # If it didn't - it is a problem
283 self.assert_equal(p2, None, "packet on long-idle conn")
285 def run_clear_conn_test(self, af, acl_side):
286 """ Clear the connections via CLI """
287 conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
288 conn1.apply_acls(0, acl_side)
289 conn1.send_through(0)
290 # the return packets should pass
291 conn1.send_through(1)
292 # send some packets on conn1, ensure it doesn't go away
293 for i in IterateWithSleep(self, 20, "Keep conn active", 0.3):
294 conn1.send_through(1)
295 # clear all connections
296 self.vapi.ppcli("clear acl-plugin sessions")
297 # now try to send a packet on the reflected side
299 p2 = conn1.send_through(1).command()
301 # If we asserted while waiting, it's good.
302 # the conn should have timed out.
304 self.assert_equal(p2, None, "packet on supposedly deleted conn")
306 def run_tcp_transient_setup_conn_test(self, af, acl_side):
307 conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53001, 5151)
308 conn1.apply_acls(0, acl_side)
309 conn1.send_through(0, 'S')
310 # the return packets should pass
311 conn1.send_through(1, 'SA')
312 # allow the conn to time out
313 for i in IterateWithSleep(self, 30, "Wait for timeout", 0.1):
315 # ensure conn times out
317 p2 = conn1.send_through(1).command()
319 # If we asserted while waiting, it's good.
320 # the conn should have timed out.
322 self.assert_equal(p2, None, "packet on supposedly deleted conn")
324 def run_tcp_established_conn_test(self, af, acl_side):
325 conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53002, 5052)
326 conn1.apply_acls(0, acl_side)
327 conn1.send_through(0, 'S')
328 # the return packets should pass
329 conn1.send_through(1, 'SA')
330 # complete the threeway handshake
331 # (NB: sequence numbers not tracked, so not set!)
332 conn1.send_through(0, 'A')
333 # allow the conn to time out if it's in embryonic timer
334 for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
336 # Try to send the packet from the "forbidden" side - it must pass
337 conn1.send_through(1, 'A')
338 # ensure conn times out for real
339 for i in IterateWithSleep(self, 130, "Wait for timeout", 0.1):
342 p2 = conn1.send_through(1).command()
344 # If we asserted while waiting, it's good.
345 # the conn should have timed out.
347 self.assert_equal(p2, None, "packet on supposedly deleted conn")
349 def run_tcp_transient_teardown_conn_test(self, af, acl_side):
350 conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53002, 5052)
351 conn1.apply_acls(0, acl_side)
352 conn1.send_through(0, 'S')
353 # the return packets should pass
354 conn1.send_through(1, 'SA')
355 # complete the threeway handshake
356 # (NB: sequence numbers not tracked, so not set!)
357 conn1.send_through(0, 'A')
358 # allow the conn to time out if it's in embryonic timer
359 for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
361 # Try to send the packet from the "forbidden" side - it must pass
362 conn1.send_through(1, 'A')
363 # Send the FIN to bounce the session out of established
364 conn1.send_through(1, 'FA')
365 # If conn landed on transient timer it will time out here
366 for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
368 # Now it should have timed out already
370 p2 = conn1.send_through(1).command()
372 # If we asserted while waiting, it's good.
373 # the conn should have timed out.
375 self.assert_equal(p2, None, "packet on supposedly deleted conn")
377 def test_0000_conn_prepare_test(self):
378 """ Prepare the settings """
379 self.vapi.ppcli("set acl-plugin session timeout udp idle 1")
381 def test_0001_basic_conn_test(self):
382 """ IPv4: Basic conn timeout test reflect on ingress """
383 self.run_basic_conn_test(AF_INET, 0)
385 def test_0002_basic_conn_test(self):
386 """ IPv4: Basic conn timeout test reflect on egress """
387 self.run_basic_conn_test(AF_INET, 1)
389 def test_0005_clear_conn_test(self):
390 """ IPv4: reflect egress, clear conn """
391 self.run_clear_conn_test(AF_INET, 1)
393 def test_0006_clear_conn_test(self):
394 """ IPv4: reflect ingress, clear conn """
395 self.run_clear_conn_test(AF_INET, 0)
397 def test_0011_active_conn_test(self):
398 """ IPv4: Idle conn behind active conn, reflect on ingress """
399 self.run_active_conn_test(AF_INET, 0)
401 def test_0012_active_conn_test(self):
402 """ IPv4: Idle conn behind active conn, reflect on egress """
403 self.run_active_conn_test(AF_INET, 1)
405 def test_1001_basic_conn_test(self):
406 """ IPv6: Basic conn timeout test reflect on ingress """
407 self.run_basic_conn_test(AF_INET6, 0)
409 def test_1002_basic_conn_test(self):
410 """ IPv6: Basic conn timeout test reflect on egress """
411 self.run_basic_conn_test(AF_INET6, 1)
413 def test_1005_clear_conn_test(self):
414 """ IPv6: reflect egress, clear conn """
415 self.run_clear_conn_test(AF_INET6, 1)
417 def test_1006_clear_conn_test(self):
418 """ IPv6: reflect ingress, clear conn """
419 self.run_clear_conn_test(AF_INET6, 0)
421 def test_1011_active_conn_test(self):
422 """ IPv6: Idle conn behind active conn, reflect on ingress """
423 self.run_active_conn_test(AF_INET6, 0)
425 def test_1012_active_conn_test(self):
426 """ IPv6: Idle conn behind active conn, reflect on egress """
427 self.run_active_conn_test(AF_INET6, 1)
429 def test_2000_prepare_for_tcp_test(self):
430 """ Prepare for TCP session tests """
431 # ensure the session hangs on if it gets treated as UDP
432 self.vapi.ppcli("set acl-plugin session timeout udp idle 200")
433 # let the TCP connection time out at 5 seconds
434 self.vapi.ppcli("set acl-plugin session timeout tcp idle 10")
435 self.vapi.ppcli("set acl-plugin session timeout tcp transient 1")
437 def test_2001_tcp_transient_conn_test(self):
438 """ IPv4: transient TCP session (incomplete 3WHS), ref. on ingress """
439 self.run_tcp_transient_setup_conn_test(AF_INET, 0)
441 def test_2002_tcp_transient_conn_test(self):
442 """ IPv4: transient TCP session (incomplete 3WHS), ref. on egress """
443 self.run_tcp_transient_setup_conn_test(AF_INET, 1)
445 def test_2003_tcp_transient_conn_test(self):
446 """ IPv4: established TCP session (complete 3WHS), ref. on ingress """
447 self.run_tcp_established_conn_test(AF_INET, 0)
449 def test_2004_tcp_transient_conn_test(self):
450 """ IPv4: established TCP session (complete 3WHS), ref. on egress """
451 self.run_tcp_established_conn_test(AF_INET, 1)
453 def test_2005_tcp_transient_teardown_conn_test(self):
454 """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
455 self.run_tcp_transient_teardown_conn_test(AF_INET, 0)
457 def test_2006_tcp_transient_teardown_conn_test(self):
458 """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
459 self.run_tcp_transient_teardown_conn_test(AF_INET, 1)
461 def test_3001_tcp_transient_conn_test(self):
462 """ IPv6: transient TCP session (incomplete 3WHS), ref. on ingress """
463 self.run_tcp_transient_setup_conn_test(AF_INET6, 0)
465 def test_3002_tcp_transient_conn_test(self):
466 """ IPv6: transient TCP session (incomplete 3WHS), ref. on egress """
467 self.run_tcp_transient_setup_conn_test(AF_INET6, 1)
469 def test_3003_tcp_transient_conn_test(self):
470 """ IPv6: established TCP session (complete 3WHS), ref. on ingress """
471 self.run_tcp_established_conn_test(AF_INET6, 0)
473 def test_3004_tcp_transient_conn_test(self):
474 """ IPv6: established TCP session (complete 3WHS), ref. on egress """
475 self.run_tcp_established_conn_test(AF_INET6, 1)
477 def test_3005_tcp_transient_teardown_conn_test(self):
478 """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
479 self.run_tcp_transient_teardown_conn_test(AF_INET6, 0)
481 def test_3006_tcp_transient_teardown_conn_test(self):
482 """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
483 self.run_tcp_transient_teardown_conn_test(AF_INET6, 1)