blob: b6c47373b8ff66a64a9a3f52a1c1cd1f4bb613ee [file] [log] [blame]
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +02001#!/usr/bin/env python
2""" ACL plugin extended stateful tests """
3
4import unittest
5from framework import VppTestCase, VppTestRunner, running_extended_tests
6from scapy.layers.l2 import Ether
7from scapy.packet import Raw
Andrew Yourtchenkob639b592017-08-09 11:28:02 +02008from scapy.layers.inet import IP, UDP, TCP
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +02009from scapy.packet import Packet
10from socket import inet_pton, AF_INET, AF_INET6
11from scapy.layers.inet6 import IPv6, ICMPv6Unknown, ICMPv6EchoRequest
12from scapy.layers.inet6 import ICMPv6EchoReply, IPv6ExtHdrRouting
13from scapy.layers.inet6 import IPv6ExtHdrFragment
14from pprint import pprint
15from random import randint
Andrew Yourtchenko92dc12a2017-09-07 13:22:24 +020016from util import L4_Conn
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +020017
18
19def 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
55Packet.to_acl_rule = to_acl_rule
56
57
58class 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
Andrew Yourtchenko92dc12a2017-09-07 13:22:24 +020072class Conn(L4_Conn):
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +020073 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))
Andrew Yourtchenko51d26512017-09-14 18:26:36 +020082 res = self.testcase.vapi.acl_add_replace(0xffffffff, r)
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +020083 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))
Andrew Yourtchenko51d26512017-09-14 18:26:36 +020088 res = self.testcase.vapi.acl_add_replace(0xffffffff, r)
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +020089 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:
Andrew Yourtchenko51d26512017-09-14 18:26:36 +020093 self.testcase.vapi.acl_interface_set_acl_list(
94 self.ifs[acl_side].sw_if_index, 1,
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +020095 [reflect_acl_index,
96 deny_acl_index])
Andrew Yourtchenko51d26512017-09-14 18:26:36 +020097 self.testcase.vapi.acl_interface_set_acl_list(
98 self.ifs[1-acl_side].sw_if_index, 0, [])
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +020099 else:
Andrew Yourtchenko51d26512017-09-14 18:26:36 +0200100 self.testcase.vapi.acl_interface_set_acl_list(
101 self.ifs[acl_side].sw_if_index, 1,
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200102 [deny_acl_index,
103 reflect_acl_index])
Andrew Yourtchenko51d26512017-09-14 18:26:36 +0200104 self.testcase.vapi.acl_interface_set_acl_list(
105 self.ifs[1-acl_side].sw_if_index, 0, [])
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200106
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
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200126
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800127@unittest.skipUnless(running_extended_tests, "part of extended tests")
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200128class ACLPluginConnTestCase(VppTestCase):
129 """ ACL plugin connection-oriented extended testcases """
130
131 @classmethod
Paul Vinciguerrac29a0ea2018-11-24 21:57:08 -0800132 def setUpClass(cls):
133 super(ACLPluginConnTestCase, cls).setUpClass()
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200134 # create pg0 and pg1
Paul Vinciguerrac29a0ea2018-11-24 21:57:08 -0800135 cls.create_pg_interfaces(range(2))
Andrew Yourtchenko1c6e5cf2018-02-05 17:27:57 +0100136 cmd = "set acl-plugin session table event-trace 1"
Paul Vinciguerrac29a0ea2018-11-24 21:57:08 -0800137 cls.logger.info(cls.vapi.cli(cmd))
138 for i in cls.pg_interfaces:
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200139 i.admin_up()
140 i.config_ip4()
141 i.config_ip6()
142 i.resolve_arp()
143 i.resolve_ndp()
144
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800145 @classmethod
146 def tearDownClass(cls):
147 super(ACLPluginConnTestCase, cls).tearDownClass()
148
Andrew Yourtchenko7f4d5772017-05-24 13:20:47 +0200149 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"))
Andrew Yourtchenko1c6e5cf2018-02-05 17:27:57 +0100160 self.logger.info(self.vapi.cli("show event-logger all"))
Andrew Yourtchenko7f4d5772017-05-24 13:20:47 +0200161
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200162 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
Andrew Yourtchenkoeb467542017-06-21 11:24:25 +0200212 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
Andrew Yourtchenkob639b592017-08-09 11:28:02 +0200233 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
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200304 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
Andrew Yourtchenkoeb467542017-06-21 11:24:25 +0200316 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
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200324 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
Andrew Yourtchenkoeb467542017-06-21 11:24:25 +0200340 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
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200348 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)
Andrew Yourtchenkob639b592017-08-09 11:28:02 +0200355
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)