ACL plugin enable macip for ip4/ip6 traffic
[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.api_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.api_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.api_acl_interface_set_acl_list(
94                    self.ifs[acl_side].sw_if_index, 2, 1,
95                    [reflect_acl_index,
96                     deny_acl_index])
97             self.testcase.api_acl_interface_set_acl_list(
98                    self.ifs[1-acl_side].sw_if_index, 0, 0, [])
99         else:
100             self.testcase.api_acl_interface_set_acl_list(
101                    self.ifs[acl_side].sw_if_index, 2, 1,
102                    [deny_acl_index,
103                     reflect_acl_index])
104             self.testcase.api_acl_interface_set_acl_list(
105                    self.ifs[1-acl_side].sw_if_index, 0, 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 api_acl_add_replace(self, acl_index, r, count=-1, tag="",
156                             expected_retval=0):
157         """Add/replace an ACL
158
159         :param int acl_index: ACL index to replace, 4294967295 to create new.
160         :param acl_rule r: ACL rules array.
161         :param str tag: symbolic tag (description) for this ACL.
162         :param int count: number of rules.
163         """
164         if (count < 0):
165             count = len(r)
166         return self.vapi.api(self.vapi.papi.acl_add_replace,
167                              {'acl_index': acl_index,
168                               'r': r,
169                               'count': count,
170                               'tag': tag
171                               }, expected_retval=expected_retval)
172
173     def api_acl_interface_set_acl_list(self, sw_if_index, count, n_input, acls,
174                                        expected_retval=0):
175         return self.vapi.api(self.vapi.papi.acl_interface_set_acl_list,
176                              {'sw_if_index': sw_if_index,
177                               'count': count,
178                               'n_input': n_input,
179                               'acls': acls
180                               }, expected_retval=expected_retval)
181
182     def api_acl_dump(self, acl_index, expected_retval=0):
183         return self.vapi.api(self.vapi.papi.acl_dump,
184                              {'acl_index': acl_index},
185                              expected_retval=expected_retval)
186
187     def run_basic_conn_test(self, af, acl_side):
188         """ Basic conn timeout test """
189         conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
190         conn1.apply_acls(0, acl_side)
191         conn1.send_through(0)
192         # the return packets should pass
193         conn1.send_through(1)
194         # send some packets on conn1, ensure it doesn't go away
195         for i in IterateWithSleep(self, 20, "Keep conn active", 0.3):
196             conn1.send_through(1)
197         # allow the conn to time out
198         for i in IterateWithSleep(self, 30, "Wait for timeout", 0.1):
199             pass
200         # now try to send a packet on the reflected side
201         try:
202             p2 = conn1.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         self.assert_equal(p2, None, "packet on long-idle conn")
208
209     def run_active_conn_test(self, af, acl_side):
210         """ Idle connection behind active connection test """
211         base = 10000 + 1000*acl_side
212         conn1 = Conn(self, self.pg0, self.pg1, af, UDP, base + 1, 2323)
213         conn2 = Conn(self, self.pg0, self.pg1, af, UDP, base + 2, 2323)
214         conn3 = Conn(self, self.pg0, self.pg1, af, UDP, base + 3, 2323)
215         conn1.apply_acls(0, acl_side)
216         conn1.send(0)
217         conn1.recv(1)
218         # create and check that the conn2/3 work
219         self.sleep(0.1)
220         conn2.send_pingpong(0)
221         self.sleep(0.1)
222         conn3.send_pingpong(0)
223         # send some packets on conn1, keep conn2/3 idle
224         for i in IterateWithSleep(self, 20, "Keep conn active", 0.2):
225             conn1.send_through(1)
226         try:
227             p2 = conn2.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         # We should have not received the packet on a long-idle
233         # connection, because it should have timed out
234         # If it didn't - it is a problem
235         self.assert_equal(p2, None, "packet on long-idle conn")
236
237     def run_clear_conn_test(self, af, acl_side):
238         """ Clear the connections via CLI """
239         conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
240         conn1.apply_acls(0, acl_side)
241         conn1.send_through(0)
242         # the return packets should pass
243         conn1.send_through(1)
244         # send some packets on conn1, ensure it doesn't go away
245         for i in IterateWithSleep(self, 20, "Keep conn active", 0.3):
246             conn1.send_through(1)
247         # clear all connections
248         self.vapi.ppcli("clear acl-plugin sessions")
249         # now try to send a packet on the reflected side
250         try:
251             p2 = conn1.send_through(1).command()
252         except:
253             # If we asserted while waiting, it's good.
254             # the conn should have timed out.
255             p2 = None
256         self.assert_equal(p2, None, "packet on supposedly deleted conn")
257
258     def run_tcp_transient_setup_conn_test(self, af, acl_side):
259         conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53001, 5151)
260         conn1.apply_acls(0, acl_side)
261         conn1.send_through(0, 'S')
262         # the return packets should pass
263         conn1.send_through(1, 'SA')
264         # allow the conn to time out
265         for i in IterateWithSleep(self, 30, "Wait for timeout", 0.1):
266             pass
267         # ensure conn times out
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_established_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         # ensure conn times out for real
291         for i in IterateWithSleep(self, 130, "Wait for timeout", 0.1):
292             pass
293         try:
294             p2 = conn1.send_through(1).command()
295         except:
296             # If we asserted while waiting, it's good.
297             # the conn should have timed out.
298             p2 = None
299         self.assert_equal(p2, None, "packet on supposedly deleted conn")
300
301     def run_tcp_transient_teardown_conn_test(self, af, acl_side):
302         conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53002, 5052)
303         conn1.apply_acls(0, acl_side)
304         conn1.send_through(0, 'S')
305         # the return packets should pass
306         conn1.send_through(1, 'SA')
307         # complete the threeway handshake
308         # (NB: sequence numbers not tracked, so not set!)
309         conn1.send_through(0, 'A')
310         # allow the conn to time out if it's in embryonic timer
311         for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
312             pass
313         # Try to send the packet from the "forbidden" side - it must pass
314         conn1.send_through(1, 'A')
315         # Send the FIN to bounce the session out of established
316         conn1.send_through(1, 'FA')
317         # If conn landed on transient timer it will time out here
318         for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
319             pass
320         # Now it should have timed out already
321         try:
322             p2 = conn1.send_through(1).command()
323         except:
324             # If we asserted while waiting, it's good.
325             # the conn should have timed out.
326             p2 = None
327         self.assert_equal(p2, None, "packet on supposedly deleted conn")
328
329     def test_0000_conn_prepare_test(self):
330         """ Prepare the settings """
331         self.vapi.ppcli("set acl-plugin session timeout udp idle 1")
332
333     def test_0001_basic_conn_test(self):
334         """ IPv4: Basic conn timeout test reflect on ingress """
335         self.run_basic_conn_test(AF_INET, 0)
336
337     def test_0002_basic_conn_test(self):
338         """ IPv4: Basic conn timeout test reflect on egress """
339         self.run_basic_conn_test(AF_INET, 1)
340
341     def test_0005_clear_conn_test(self):
342         """ IPv4: reflect egress, clear conn """
343         self.run_clear_conn_test(AF_INET, 1)
344
345     def test_0006_clear_conn_test(self):
346         """ IPv4: reflect ingress, clear conn """
347         self.run_clear_conn_test(AF_INET, 0)
348
349     def test_0011_active_conn_test(self):
350         """ IPv4: Idle conn behind active conn, reflect on ingress """
351         self.run_active_conn_test(AF_INET, 0)
352
353     def test_0012_active_conn_test(self):
354         """ IPv4: Idle conn behind active conn, reflect on egress """
355         self.run_active_conn_test(AF_INET, 1)
356
357     def test_1001_basic_conn_test(self):
358         """ IPv6: Basic conn timeout test reflect on ingress """
359         self.run_basic_conn_test(AF_INET6, 0)
360
361     def test_1002_basic_conn_test(self):
362         """ IPv6: Basic conn timeout test reflect on egress """
363         self.run_basic_conn_test(AF_INET6, 1)
364
365     def test_1005_clear_conn_test(self):
366         """ IPv6: reflect egress, clear conn """
367         self.run_clear_conn_test(AF_INET6, 1)
368
369     def test_1006_clear_conn_test(self):
370         """ IPv6: reflect ingress, clear conn """
371         self.run_clear_conn_test(AF_INET6, 0)
372
373     def test_1011_active_conn_test(self):
374         """ IPv6: Idle conn behind active conn, reflect on ingress """
375         self.run_active_conn_test(AF_INET6, 0)
376
377     def test_1012_active_conn_test(self):
378         """ IPv6: Idle conn behind active conn, reflect on egress """
379         self.run_active_conn_test(AF_INET6, 1)
380
381     def test_2000_prepare_for_tcp_test(self):
382         """ Prepare for TCP session tests """
383         # ensure the session hangs on if it gets treated as UDP
384         self.vapi.ppcli("set acl-plugin session timeout udp idle 200")
385         # let the TCP connection time out at 5 seconds
386         self.vapi.ppcli("set acl-plugin session timeout tcp idle 10")
387         self.vapi.ppcli("set acl-plugin session timeout tcp transient 1")
388
389     def test_2001_tcp_transient_conn_test(self):
390         """ IPv4: transient TCP session (incomplete 3WHS), ref. on ingress """
391         self.run_tcp_transient_setup_conn_test(AF_INET, 0)
392
393     def test_2002_tcp_transient_conn_test(self):
394         """ IPv4: transient TCP session (incomplete 3WHS), ref. on egress """
395         self.run_tcp_transient_setup_conn_test(AF_INET, 1)
396
397     def test_2003_tcp_transient_conn_test(self):
398         """ IPv4: established TCP session (complete 3WHS), ref. on ingress """
399         self.run_tcp_established_conn_test(AF_INET, 0)
400
401     def test_2004_tcp_transient_conn_test(self):
402         """ IPv4: established TCP session (complete 3WHS), ref. on egress """
403         self.run_tcp_established_conn_test(AF_INET, 1)
404
405     def test_2005_tcp_transient_teardown_conn_test(self):
406         """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
407         self.run_tcp_transient_teardown_conn_test(AF_INET, 0)
408
409     def test_2006_tcp_transient_teardown_conn_test(self):
410         """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
411         self.run_tcp_transient_teardown_conn_test(AF_INET, 1)
412
413     def test_3001_tcp_transient_conn_test(self):
414         """ IPv6: transient TCP session (incomplete 3WHS), ref. on ingress """
415         self.run_tcp_transient_setup_conn_test(AF_INET6, 0)
416
417     def test_3002_tcp_transient_conn_test(self):
418         """ IPv6: transient TCP session (incomplete 3WHS), ref. on egress """
419         self.run_tcp_transient_setup_conn_test(AF_INET6, 1)
420
421     def test_3003_tcp_transient_conn_test(self):
422         """ IPv6: established TCP session (complete 3WHS), ref. on ingress """
423         self.run_tcp_established_conn_test(AF_INET6, 0)
424
425     def test_3004_tcp_transient_conn_test(self):
426         """ IPv6: established TCP session (complete 3WHS), ref. on egress """
427         self.run_tcp_established_conn_test(AF_INET6, 1)
428
429     def test_3005_tcp_transient_teardown_conn_test(self):
430         """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
431         self.run_tcp_transient_teardown_conn_test(AF_INET6, 0)
432
433     def test_3006_tcp_transient_teardown_conn_test(self):
434         """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
435         self.run_tcp_transient_teardown_conn_test(AF_INET6, 1)