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