acl: API cleanup
[vpp.git] / src / plugins / acl / test / test_acl_plugin_conns.py
1 #!/usr/bin/env python3
2 """ ACL plugin extended stateful tests """
3
4 import unittest
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
16 from util import L4_Conn
17 from ipaddress import ip_network
18
19 from vpp_acl import AclRule, VppAcl, VppAclInterface
20
21
22 def to_acl_rule(self, is_permit, wildcard_sport=False):
23     p = self
24     rule_family = AF_INET6 if p.haslayer(IPv6) else AF_INET
25     rule_prefix_len = 128 if p.haslayer(IPv6) else 32
26     rule_l3_layer = IPv6 if p.haslayer(IPv6) else IP
27     rule_l4_sport = p.sport
28     rule_l4_dport = p.dport
29     if p.haslayer(IPv6):
30         rule_l4_proto = p[IPv6].nh
31     else:
32         rule_l4_proto = p[IP].proto
33
34     if wildcard_sport:
35         rule_l4_sport_first = 0
36         rule_l4_sport_last = 65535
37     else:
38         rule_l4_sport_first = rule_l4_sport
39         rule_l4_sport_last = rule_l4_sport
40
41     new_rule = AclRule(is_permit=is_permit, proto=rule_l4_proto,
42                        src_prefix=ip_network(
43                            (p[rule_l3_layer].src, rule_prefix_len)),
44                        dst_prefix=ip_network(
45                            (p[rule_l3_layer].dst, rule_prefix_len)),
46                        sport_from=rule_l4_sport_first,
47                        sport_to=rule_l4_sport_last,
48                        dport_from=rule_l4_dport, dport_to=rule_l4_dport)
49
50     return new_rule
51
52
53 Packet.to_acl_rule = to_acl_rule
54
55
56 class IterateWithSleep():
57     def __init__(self, testcase, n_iters, description, sleep_sec):
58         self.curr = 0
59         self.testcase = testcase
60         self.n_iters = n_iters
61         self.sleep_sec = sleep_sec
62         self.description = description
63
64     def __iter__(self):
65         for x in range(0, self.n_iters):
66             yield x
67             self.testcase.sleep(self.sleep_sec)
68
69
70 class Conn(L4_Conn):
71     def apply_acls(self, reflect_side, acl_side):
72         pkts = []
73         pkts.append(self.pkt(0))
74         pkts.append(self.pkt(1))
75         pkt = pkts[reflect_side]
76
77         r = []
78         r.append(pkt.to_acl_rule(2, wildcard_sport=True))
79         r.append(self.wildcard_rule(0))
80         reflect_acl = VppAcl(self.testcase, r)
81         reflect_acl.add_vpp_config()
82
83         r = []
84         r.append(self.wildcard_rule(0))
85         deny_acl = VppAcl(self.testcase, r)
86         deny_acl.add_vpp_config()
87
88         if reflect_side == acl_side:
89             acl_if0 = VppAclInterface(self.testcase,
90                                       self.ifs[acl_side].sw_if_index,
91                                       [reflect_acl, deny_acl], n_input=1)
92             acl_if1 = VppAclInterface(self.testcase,
93                                       self.ifs[1-acl_side].sw_if_index, [],
94                                       n_input=0)
95             acl_if0.add_vpp_config()
96             acl_if1.add_vpp_config()
97         else:
98             acl_if0 = VppAclInterface(self.testcase,
99                                       self.ifs[acl_side].sw_if_index,
100                                       [deny_acl, reflect_acl], n_input=1)
101             acl_if1 = VppAclInterface(self.testcase,
102                                       self.ifs[1-acl_side].sw_if_index, [],
103                                       n_input=0)
104             acl_if0.add_vpp_config()
105             acl_if1.add_vpp_config()
106
107     def wildcard_rule(self, is_permit):
108         any_addr = ["0.0.0.0", "::"]
109         rule_family = self.address_family
110         is_ip6 = 1 if rule_family == AF_INET6 else 0
111         new_rule = AclRule(is_permit=is_permit, proto=0,
112                            src_prefix=ip_network(
113                                (any_addr[is_ip6], 0)),
114                            dst_prefix=ip_network(
115                                (any_addr[is_ip6], 0)),
116                            sport_from=0, sport_to=65535, dport_from=0,
117                            dport_to=65535)
118         return new_rule
119
120
121 @unittest.skipUnless(running_extended_tests, "part of extended tests")
122 class ACLPluginConnTestCase(VppTestCase):
123     """ ACL plugin connection-oriented extended testcases """
124
125     @classmethod
126     def setUpClass(cls):
127         super(ACLPluginConnTestCase, cls).setUpClass()
128         # create pg0 and pg1
129         cls.create_pg_interfaces(range(2))
130         cmd = "set acl-plugin session table event-trace 1"
131         cls.logger.info(cls.vapi.cli(cmd))
132         for i in cls.pg_interfaces:
133             i.admin_up()
134             i.config_ip4()
135             i.config_ip6()
136             i.resolve_arp()
137             i.resolve_ndp()
138
139     @classmethod
140     def tearDownClass(cls):
141         super(ACLPluginConnTestCase, cls).tearDownClass()
142
143     def tearDown(self):
144         """Run standard test teardown and log various show commands
145         """
146         super(ACLPluginConnTestCase, self).tearDown()
147
148     def show_commands_at_teardown(self):
149         self.logger.info(self.vapi.cli("show ip neighbors"))
150         self.logger.info(self.vapi.cli("show ip6 neighbors"))
151         self.logger.info(self.vapi.cli("show acl-plugin sessions"))
152         self.logger.info(self.vapi.cli("show acl-plugin acl"))
153         self.logger.info(self.vapi.cli("show acl-plugin interface"))
154         self.logger.info(self.vapi.cli("show acl-plugin tables"))
155         self.logger.info(self.vapi.cli("show event-logger all"))
156
157     def run_basic_conn_test(self, af, acl_side):
158         """ Basic conn timeout test """
159         conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
160         conn1.apply_acls(0, acl_side)
161         conn1.send_through(0)
162         # the return packets should pass
163         conn1.send_through(1)
164         # send some packets on conn1, ensure it doesn't go away
165         for i in IterateWithSleep(self, 20, "Keep conn active", 0.3):
166             conn1.send_through(1)
167         # allow the conn to time out
168         for i in IterateWithSleep(self, 30, "Wait for timeout", 0.1):
169             pass
170         # now try to send a packet on the reflected side
171         try:
172             p2 = conn1.send_through(1).command()
173         except:
174             # If we asserted while waiting, it's good.
175             # the conn should have timed out.
176             p2 = None
177         self.assert_equal(p2, None, "packet on long-idle conn")
178
179     def run_active_conn_test(self, af, acl_side):
180         """ Idle connection behind active connection test """
181         base = 10000 + 1000*acl_side
182         conn1 = Conn(self, self.pg0, self.pg1, af, UDP, base + 1, 2323)
183         conn2 = Conn(self, self.pg0, self.pg1, af, UDP, base + 2, 2323)
184         conn3 = Conn(self, self.pg0, self.pg1, af, UDP, base + 3, 2323)
185         conn1.apply_acls(0, acl_side)
186         conn1.send(0)
187         conn1.recv(1)
188         # create and check that the conn2/3 work
189         self.sleep(0.1)
190         conn2.send_pingpong(0)
191         self.sleep(0.1)
192         conn3.send_pingpong(0)
193         # send some packets on conn1, keep conn2/3 idle
194         for i in IterateWithSleep(self, 20, "Keep conn active", 0.2):
195             conn1.send_through(1)
196         try:
197             p2 = conn2.send_through(1).command()
198         except:
199             # If we asserted while waiting, it's good.
200             # the conn should have timed out.
201             p2 = None
202         # We should have not received the packet on a long-idle
203         # connection, because it should have timed out
204         # If it didn't - it is a problem
205         self.assert_equal(p2, None, "packet on long-idle conn")
206
207     def run_clear_conn_test(self, af, acl_side):
208         """ Clear the connections via CLI """
209         conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
210         conn1.apply_acls(0, acl_side)
211         conn1.send_through(0)
212         # the return packets should pass
213         conn1.send_through(1)
214         # send some packets on conn1, ensure it doesn't go away
215         for i in IterateWithSleep(self, 20, "Keep conn active", 0.3):
216             conn1.send_through(1)
217         # clear all connections
218         self.vapi.ppcli("clear acl-plugin sessions")
219         # now try to send a packet on the reflected side
220         try:
221             p2 = conn1.send_through(1).command()
222         except:
223             # If we asserted while waiting, it's good.
224             # the conn should have timed out.
225             p2 = None
226         self.assert_equal(p2, None, "packet on supposedly deleted conn")
227
228     def run_tcp_transient_setup_conn_test(self, af, acl_side):
229         conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53001, 5151)
230         conn1.apply_acls(0, acl_side)
231         conn1.send_through(0, 'S')
232         # the return packets should pass
233         conn1.send_through(1, 'SA')
234         # allow the conn to time out
235         for i in IterateWithSleep(self, 30, "Wait for timeout", 0.1):
236             pass
237         # ensure conn times out
238         try:
239             p2 = conn1.send_through(1).command()
240         except:
241             # If we asserted while waiting, it's good.
242             # the conn should have timed out.
243             p2 = None
244         self.assert_equal(p2, None, "packet on supposedly deleted conn")
245
246     def run_tcp_established_conn_test(self, af, acl_side):
247         conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53002, 5052)
248         conn1.apply_acls(0, acl_side)
249         conn1.send_through(0, 'S')
250         # the return packets should pass
251         conn1.send_through(1, 'SA')
252         # complete the threeway handshake
253         # (NB: sequence numbers not tracked, so not set!)
254         conn1.send_through(0, 'A')
255         # allow the conn to time out if it's in embryonic timer
256         for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
257             pass
258         # Try to send the packet from the "forbidden" side - it must pass
259         conn1.send_through(1, 'A')
260         # ensure conn times out for real
261         for i in IterateWithSleep(self, 130, "Wait for timeout", 0.1):
262             pass
263         try:
264             p2 = conn1.send_through(1).command()
265         except:
266             # If we asserted while waiting, it's good.
267             # the conn should have timed out.
268             p2 = None
269         self.assert_equal(p2, None, "packet on supposedly deleted conn")
270
271     def run_tcp_transient_teardown_conn_test(self, af, acl_side):
272         conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53002, 5052)
273         conn1.apply_acls(0, acl_side)
274         conn1.send_through(0, 'S')
275         # the return packets should pass
276         conn1.send_through(1, 'SA')
277         # complete the threeway handshake
278         # (NB: sequence numbers not tracked, so not set!)
279         conn1.send_through(0, 'A')
280         # allow the conn to time out if it's in embryonic timer
281         for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
282             pass
283         # Try to send the packet from the "forbidden" side - it must pass
284         conn1.send_through(1, 'A')
285         # Send the FIN to bounce the session out of established
286         conn1.send_through(1, 'FA')
287         # If conn landed on transient timer it will time out here
288         for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
289             pass
290         # Now it should have timed out already
291         try:
292             p2 = conn1.send_through(1).command()
293         except:
294             # If we asserted while waiting, it's good.
295             # the conn should have timed out.
296             p2 = None
297         self.assert_equal(p2, None, "packet on supposedly deleted conn")
298
299     def test_0000_conn_prepare_test(self):
300         """ Prepare the settings """
301         self.vapi.ppcli("set acl-plugin session timeout udp idle 1")
302
303     def test_0001_basic_conn_test(self):
304         """ IPv4: Basic conn timeout test reflect on ingress """
305         self.run_basic_conn_test(AF_INET, 0)
306
307     def test_0002_basic_conn_test(self):
308         """ IPv4: Basic conn timeout test reflect on egress """
309         self.run_basic_conn_test(AF_INET, 1)
310
311     def test_0005_clear_conn_test(self):
312         """ IPv4: reflect egress, clear conn """
313         self.run_clear_conn_test(AF_INET, 1)
314
315     def test_0006_clear_conn_test(self):
316         """ IPv4: reflect ingress, clear conn """
317         self.run_clear_conn_test(AF_INET, 0)
318
319     def test_0011_active_conn_test(self):
320         """ IPv4: Idle conn behind active conn, reflect on ingress """
321         self.run_active_conn_test(AF_INET, 0)
322
323     def test_0012_active_conn_test(self):
324         """ IPv4: Idle conn behind active conn, reflect on egress """
325         self.run_active_conn_test(AF_INET, 1)
326
327     def test_1001_basic_conn_test(self):
328         """ IPv6: Basic conn timeout test reflect on ingress """
329         self.run_basic_conn_test(AF_INET6, 0)
330
331     def test_1002_basic_conn_test(self):
332         """ IPv6: Basic conn timeout test reflect on egress """
333         self.run_basic_conn_test(AF_INET6, 1)
334
335     def test_1005_clear_conn_test(self):
336         """ IPv6: reflect egress, clear conn """
337         self.run_clear_conn_test(AF_INET6, 1)
338
339     def test_1006_clear_conn_test(self):
340         """ IPv6: reflect ingress, clear conn """
341         self.run_clear_conn_test(AF_INET6, 0)
342
343     def test_1011_active_conn_test(self):
344         """ IPv6: Idle conn behind active conn, reflect on ingress """
345         self.run_active_conn_test(AF_INET6, 0)
346
347     def test_1012_active_conn_test(self):
348         """ IPv6: Idle conn behind active conn, reflect on egress """
349         self.run_active_conn_test(AF_INET6, 1)
350
351     def test_2000_prepare_for_tcp_test(self):
352         """ Prepare for TCP session tests """
353         # ensure the session hangs on if it gets treated as UDP
354         self.vapi.ppcli("set acl-plugin session timeout udp idle 200")
355         # let the TCP connection time out at 5 seconds
356         self.vapi.ppcli("set acl-plugin session timeout tcp idle 10")
357         self.vapi.ppcli("set acl-plugin session timeout tcp transient 1")
358
359     def test_2001_tcp_transient_conn_test(self):
360         """ IPv4: transient TCP session (incomplete 3WHS), ref. on ingress """
361         self.run_tcp_transient_setup_conn_test(AF_INET, 0)
362
363     def test_2002_tcp_transient_conn_test(self):
364         """ IPv4: transient TCP session (incomplete 3WHS), ref. on egress """
365         self.run_tcp_transient_setup_conn_test(AF_INET, 1)
366
367     def test_2003_tcp_transient_conn_test(self):
368         """ IPv4: established TCP session (complete 3WHS), ref. on ingress """
369         self.run_tcp_established_conn_test(AF_INET, 0)
370
371     def test_2004_tcp_transient_conn_test(self):
372         """ IPv4: established TCP session (complete 3WHS), ref. on egress """
373         self.run_tcp_established_conn_test(AF_INET, 1)
374
375     def test_2005_tcp_transient_teardown_conn_test(self):
376         """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
377         self.run_tcp_transient_teardown_conn_test(AF_INET, 0)
378
379     def test_2006_tcp_transient_teardown_conn_test(self):
380         """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
381         self.run_tcp_transient_teardown_conn_test(AF_INET, 1)
382
383     def test_3001_tcp_transient_conn_test(self):
384         """ IPv6: transient TCP session (incomplete 3WHS), ref. on ingress """
385         self.run_tcp_transient_setup_conn_test(AF_INET6, 0)
386
387     def test_3002_tcp_transient_conn_test(self):
388         """ IPv6: transient TCP session (incomplete 3WHS), ref. on egress """
389         self.run_tcp_transient_setup_conn_test(AF_INET6, 1)
390
391     def test_3003_tcp_transient_conn_test(self):
392         """ IPv6: established TCP session (complete 3WHS), ref. on ingress """
393         self.run_tcp_established_conn_test(AF_INET6, 0)
394
395     def test_3004_tcp_transient_conn_test(self):
396         """ IPv6: established TCP session (complete 3WHS), ref. on egress """
397         self.run_tcp_established_conn_test(AF_INET6, 1)
398
399     def test_3005_tcp_transient_teardown_conn_test(self):
400         """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
401         self.run_tcp_transient_teardown_conn_test(AF_INET6, 0)
402
403     def test_3006_tcp_transient_teardown_conn_test(self):
404         """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
405         self.run_tcp_transient_teardown_conn_test(AF_INET6, 1)