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