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