b6c47373b8ff66a64a9a3f52a1c1cd1f4bb613ee
[vpp.git] / test / test_acl_plugin_conns.py
1 #!/usr/bin/env python
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         if not self.vpp_dead:
154             self.logger.info(self.vapi.cli("show ip arp"))
155             self.logger.info(self.vapi.cli("show ip6 neighbors"))
156             self.logger.info(self.vapi.cli("show acl-plugin sessions"))
157             self.logger.info(self.vapi.cli("show acl-plugin acl"))
158             self.logger.info(self.vapi.cli("show acl-plugin interface"))
159             self.logger.info(self.vapi.cli("show acl-plugin tables"))
160             self.logger.info(self.vapi.cli("show event-logger all"))
161
162     def run_basic_conn_test(self, af, acl_side):
163         """ Basic conn timeout test """
164         conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
165         conn1.apply_acls(0, acl_side)
166         conn1.send_through(0)
167         # the return packets should pass
168         conn1.send_through(1)
169         # send some packets on conn1, ensure it doesn't go away
170         for i in IterateWithSleep(self, 20, "Keep conn active", 0.3):
171             conn1.send_through(1)
172         # allow the conn to time out
173         for i in IterateWithSleep(self, 30, "Wait for timeout", 0.1):
174             pass
175         # now try to send a packet on the reflected side
176         try:
177             p2 = conn1.send_through(1).command()
178         except:
179             # If we asserted while waiting, it's good.
180             # the conn should have timed out.
181             p2 = None
182         self.assert_equal(p2, None, "packet on long-idle conn")
183
184     def run_active_conn_test(self, af, acl_side):
185         """ Idle connection behind active connection test """
186         base = 10000 + 1000*acl_side
187         conn1 = Conn(self, self.pg0, self.pg1, af, UDP, base + 1, 2323)
188         conn2 = Conn(self, self.pg0, self.pg1, af, UDP, base + 2, 2323)
189         conn3 = Conn(self, self.pg0, self.pg1, af, UDP, base + 3, 2323)
190         conn1.apply_acls(0, acl_side)
191         conn1.send(0)
192         conn1.recv(1)
193         # create and check that the conn2/3 work
194         self.sleep(0.1)
195         conn2.send_pingpong(0)
196         self.sleep(0.1)
197         conn3.send_pingpong(0)
198         # send some packets on conn1, keep conn2/3 idle
199         for i in IterateWithSleep(self, 20, "Keep conn active", 0.2):
200             conn1.send_through(1)
201         try:
202             p2 = conn2.send_through(1).command()
203         except:
204             # If we asserted while waiting, it's good.
205             # the conn should have timed out.
206             p2 = None
207         # We should have not received the packet on a long-idle
208         # connection, because it should have timed out
209         # If it didn't - it is a problem
210         self.assert_equal(p2, None, "packet on long-idle conn")
211
212     def run_clear_conn_test(self, af, acl_side):
213         """ Clear the connections via CLI """
214         conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
215         conn1.apply_acls(0, acl_side)
216         conn1.send_through(0)
217         # the return packets should pass
218         conn1.send_through(1)
219         # send some packets on conn1, ensure it doesn't go away
220         for i in IterateWithSleep(self, 20, "Keep conn active", 0.3):
221             conn1.send_through(1)
222         # clear all connections
223         self.vapi.ppcli("clear acl-plugin sessions")
224         # now try to send a packet on the reflected side
225         try:
226             p2 = conn1.send_through(1).command()
227         except:
228             # If we asserted while waiting, it's good.
229             # the conn should have timed out.
230             p2 = None
231         self.assert_equal(p2, None, "packet on supposedly deleted conn")
232
233     def run_tcp_transient_setup_conn_test(self, af, acl_side):
234         conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53001, 5151)
235         conn1.apply_acls(0, acl_side)
236         conn1.send_through(0, 'S')
237         # the return packets should pass
238         conn1.send_through(1, 'SA')
239         # allow the conn to time out
240         for i in IterateWithSleep(self, 30, "Wait for timeout", 0.1):
241             pass
242         # ensure conn times out
243         try:
244             p2 = conn1.send_through(1).command()
245         except:
246             # If we asserted while waiting, it's good.
247             # the conn should have timed out.
248             p2 = None
249         self.assert_equal(p2, None, "packet on supposedly deleted conn")
250
251     def run_tcp_established_conn_test(self, af, acl_side):
252         conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53002, 5052)
253         conn1.apply_acls(0, acl_side)
254         conn1.send_through(0, 'S')
255         # the return packets should pass
256         conn1.send_through(1, 'SA')
257         # complete the threeway handshake
258         # (NB: sequence numbers not tracked, so not set!)
259         conn1.send_through(0, 'A')
260         # allow the conn to time out if it's in embryonic timer
261         for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
262             pass
263         # Try to send the packet from the "forbidden" side - it must pass
264         conn1.send_through(1, 'A')
265         # ensure conn times out for real
266         for i in IterateWithSleep(self, 130, "Wait for timeout", 0.1):
267             pass
268         try:
269             p2 = conn1.send_through(1).command()
270         except:
271             # If we asserted while waiting, it's good.
272             # the conn should have timed out.
273             p2 = None
274         self.assert_equal(p2, None, "packet on supposedly deleted conn")
275
276     def run_tcp_transient_teardown_conn_test(self, af, acl_side):
277         conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53002, 5052)
278         conn1.apply_acls(0, acl_side)
279         conn1.send_through(0, 'S')
280         # the return packets should pass
281         conn1.send_through(1, 'SA')
282         # complete the threeway handshake
283         # (NB: sequence numbers not tracked, so not set!)
284         conn1.send_through(0, 'A')
285         # allow the conn to time out if it's in embryonic timer
286         for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
287             pass
288         # Try to send the packet from the "forbidden" side - it must pass
289         conn1.send_through(1, 'A')
290         # Send the FIN to bounce the session out of established
291         conn1.send_through(1, 'FA')
292         # If conn landed on transient timer it will time out here
293         for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
294             pass
295         # Now it should have timed out already
296         try:
297             p2 = conn1.send_through(1).command()
298         except:
299             # If we asserted while waiting, it's good.
300             # the conn should have timed out.
301             p2 = None
302         self.assert_equal(p2, None, "packet on supposedly deleted conn")
303
304     def test_0000_conn_prepare_test(self):
305         """ Prepare the settings """
306         self.vapi.ppcli("set acl-plugin session timeout udp idle 1")
307
308     def test_0001_basic_conn_test(self):
309         """ IPv4: Basic conn timeout test reflect on ingress """
310         self.run_basic_conn_test(AF_INET, 0)
311
312     def test_0002_basic_conn_test(self):
313         """ IPv4: Basic conn timeout test reflect on egress """
314         self.run_basic_conn_test(AF_INET, 1)
315
316     def test_0005_clear_conn_test(self):
317         """ IPv4: reflect egress, clear conn """
318         self.run_clear_conn_test(AF_INET, 1)
319
320     def test_0006_clear_conn_test(self):
321         """ IPv4: reflect ingress, clear conn """
322         self.run_clear_conn_test(AF_INET, 0)
323
324     def test_0011_active_conn_test(self):
325         """ IPv4: Idle conn behind active conn, reflect on ingress """
326         self.run_active_conn_test(AF_INET, 0)
327
328     def test_0012_active_conn_test(self):
329         """ IPv4: Idle conn behind active conn, reflect on egress """
330         self.run_active_conn_test(AF_INET, 1)
331
332     def test_1001_basic_conn_test(self):
333         """ IPv6: Basic conn timeout test reflect on ingress """
334         self.run_basic_conn_test(AF_INET6, 0)
335
336     def test_1002_basic_conn_test(self):
337         """ IPv6: Basic conn timeout test reflect on egress """
338         self.run_basic_conn_test(AF_INET6, 1)
339
340     def test_1005_clear_conn_test(self):
341         """ IPv6: reflect egress, clear conn """
342         self.run_clear_conn_test(AF_INET6, 1)
343
344     def test_1006_clear_conn_test(self):
345         """ IPv6: reflect ingress, clear conn """
346         self.run_clear_conn_test(AF_INET6, 0)
347
348     def test_1011_active_conn_test(self):
349         """ IPv6: Idle conn behind active conn, reflect on ingress """
350         self.run_active_conn_test(AF_INET6, 0)
351
352     def test_1012_active_conn_test(self):
353         """ IPv6: Idle conn behind active conn, reflect on egress """
354         self.run_active_conn_test(AF_INET6, 1)
355
356     def test_2000_prepare_for_tcp_test(self):
357         """ Prepare for TCP session tests """
358         # ensure the session hangs on if it gets treated as UDP
359         self.vapi.ppcli("set acl-plugin session timeout udp idle 200")
360         # let the TCP connection time out at 5 seconds
361         self.vapi.ppcli("set acl-plugin session timeout tcp idle 10")
362         self.vapi.ppcli("set acl-plugin session timeout tcp transient 1")
363
364     def test_2001_tcp_transient_conn_test(self):
365         """ IPv4: transient TCP session (incomplete 3WHS), ref. on ingress """
366         self.run_tcp_transient_setup_conn_test(AF_INET, 0)
367
368     def test_2002_tcp_transient_conn_test(self):
369         """ IPv4: transient TCP session (incomplete 3WHS), ref. on egress """
370         self.run_tcp_transient_setup_conn_test(AF_INET, 1)
371
372     def test_2003_tcp_transient_conn_test(self):
373         """ IPv4: established TCP session (complete 3WHS), ref. on ingress """
374         self.run_tcp_established_conn_test(AF_INET, 0)
375
376     def test_2004_tcp_transient_conn_test(self):
377         """ IPv4: established TCP session (complete 3WHS), ref. on egress """
378         self.run_tcp_established_conn_test(AF_INET, 1)
379
380     def test_2005_tcp_transient_teardown_conn_test(self):
381         """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
382         self.run_tcp_transient_teardown_conn_test(AF_INET, 0)
383
384     def test_2006_tcp_transient_teardown_conn_test(self):
385         """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
386         self.run_tcp_transient_teardown_conn_test(AF_INET, 1)
387
388     def test_3001_tcp_transient_conn_test(self):
389         """ IPv6: transient TCP session (incomplete 3WHS), ref. on ingress """
390         self.run_tcp_transient_setup_conn_test(AF_INET6, 0)
391
392     def test_3002_tcp_transient_conn_test(self):
393         """ IPv6: transient TCP session (incomplete 3WHS), ref. on egress """
394         self.run_tcp_transient_setup_conn_test(AF_INET6, 1)
395
396     def test_3003_tcp_transient_conn_test(self):
397         """ IPv6: established TCP session (complete 3WHS), ref. on ingress """
398         self.run_tcp_established_conn_test(AF_INET6, 0)
399
400     def test_3004_tcp_transient_conn_test(self):
401         """ IPv6: established TCP session (complete 3WHS), ref. on egress """
402         self.run_tcp_established_conn_test(AF_INET6, 1)
403
404     def test_3005_tcp_transient_teardown_conn_test(self):
405         """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
406         self.run_tcp_transient_teardown_conn_test(AF_INET6, 0)
407
408     def test_3006_tcp_transient_teardown_conn_test(self):
409         """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
410         self.run_tcp_transient_teardown_conn_test(AF_INET6, 1)