blob: 06f3cf7e1782c3cf9d6efb4abaae4b9bdcd48eaa [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
16
17
18def to_acl_rule(self, is_permit, wildcard_sport=False):
19 p = self
20 rule_family = AF_INET6 if p.haslayer(IPv6) else AF_INET
21 rule_prefix_len = 128 if p.haslayer(IPv6) else 32
22 rule_l3_layer = IPv6 if p.haslayer(IPv6) else IP
23 rule_l4_sport = p.sport
24 rule_l4_dport = p.dport
25 if p.haslayer(IPv6):
26 rule_l4_proto = p[IPv6].nh
27 else:
28 rule_l4_proto = p[IP].proto
29
30 if wildcard_sport:
31 rule_l4_sport_first = 0
32 rule_l4_sport_last = 65535
33 else:
34 rule_l4_sport_first = rule_l4_sport
35 rule_l4_sport_last = rule_l4_sport
36
37 new_rule = {
38 'is_permit': is_permit,
39 'is_ipv6': p.haslayer(IPv6),
40 'src_ip_addr': inet_pton(rule_family,
41 p[rule_l3_layer].src),
42 'src_ip_prefix_len': rule_prefix_len,
43 'dst_ip_addr': inet_pton(rule_family,
44 p[rule_l3_layer].dst),
45 'dst_ip_prefix_len': rule_prefix_len,
46 'srcport_or_icmptype_first': rule_l4_sport_first,
47 'srcport_or_icmptype_last': rule_l4_sport_last,
48 'dstport_or_icmpcode_first': rule_l4_dport,
49 'dstport_or_icmpcode_last': rule_l4_dport,
50 'proto': rule_l4_proto,
51 }
52 return new_rule
53
54Packet.to_acl_rule = to_acl_rule
55
56
57class IterateWithSleep():
58 def __init__(self, testcase, n_iters, description, sleep_sec):
59 self.curr = 0
60 self.testcase = testcase
61 self.n_iters = n_iters
62 self.sleep_sec = sleep_sec
63 self.description = description
64
65 def __iter__(self):
66 for x in range(0, self.n_iters):
67 yield x
68 self.testcase.sleep(self.sleep_sec)
69
70
71class Conn():
72 def __init__(self, testcase, if1, if2, af, l4proto, port1, port2):
73 self.testcase = testcase
74 self.ifs = [None, None]
75 self.ifs[0] = if1
76 self.ifs[1] = if2
77 self.address_family = af
78 self.l4proto = l4proto
79 self.ports = [None, None]
80 self.ports[0] = port1
81 self.ports[1] = port2
82 self
83
Andrew Yourtchenkob639b592017-08-09 11:28:02 +020084 def pkt(self, side, flags=None):
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +020085 is_ip6 = 1 if self.address_family == AF_INET6 else 0
86 s0 = side
87 s1 = 1-side
88 src_if = self.ifs[s0]
89 dst_if = self.ifs[s1]
90 layer_3 = [IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4),
91 IPv6(src=src_if.remote_ip6, dst=dst_if.remote_ip6)]
92 payload = "x"
Andrew Yourtchenkob639b592017-08-09 11:28:02 +020093 l4args = {'sport': self.ports[s0], 'dport': self.ports[s1]}
94 if flags is not None:
95 l4args['flags'] = flags
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +020096 p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
97 layer_3[is_ip6] /
Andrew Yourtchenkob639b592017-08-09 11:28:02 +020098 self.l4proto(**l4args) /
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +020099 Raw(payload))
100 return p
101
102 def apply_acls(self, reflect_side, acl_side):
103 pkts = []
104 pkts.append(self.pkt(0))
105 pkts.append(self.pkt(1))
106 pkt = pkts[reflect_side]
107
108 r = []
109 r.append(pkt.to_acl_rule(2, wildcard_sport=True))
110 r.append(self.wildcard_rule(0))
111 res = self.testcase.api_acl_add_replace(0xffffffff, r)
112 self.testcase.assert_equal(res.retval, 0, "error adding ACL")
113 reflect_acl_index = res.acl_index
114
115 r = []
116 r.append(self.wildcard_rule(0))
117 res = self.testcase.api_acl_add_replace(0xffffffff, r)
118 self.testcase.assert_equal(res.retval, 0, "error adding deny ACL")
119 deny_acl_index = res.acl_index
120
121 if reflect_side == acl_side:
122 self.testcase.api_acl_interface_set_acl_list(
123 self.ifs[acl_side].sw_if_index, 2, 1,
124 [reflect_acl_index,
125 deny_acl_index])
126 self.testcase.api_acl_interface_set_acl_list(
127 self.ifs[1-acl_side].sw_if_index, 0, 0, [])
128 else:
129 self.testcase.api_acl_interface_set_acl_list(
130 self.ifs[acl_side].sw_if_index, 2, 1,
131 [deny_acl_index,
132 reflect_acl_index])
133 self.testcase.api_acl_interface_set_acl_list(
134 self.ifs[1-acl_side].sw_if_index, 0, 0, [])
135
136 def wildcard_rule(self, is_permit):
137 any_addr = ["0.0.0.0", "::"]
138 rule_family = self.address_family
139 is_ip6 = 1 if rule_family == AF_INET6 else 0
140 new_rule = {
141 'is_permit': is_permit,
142 'is_ipv6': is_ip6,
143 'src_ip_addr': inet_pton(rule_family, any_addr[is_ip6]),
144 'src_ip_prefix_len': 0,
145 'dst_ip_addr': inet_pton(rule_family, any_addr[is_ip6]),
146 'dst_ip_prefix_len': 0,
147 'srcport_or_icmptype_first': 0,
148 'srcport_or_icmptype_last': 65535,
149 'dstport_or_icmpcode_first': 0,
150 'dstport_or_icmpcode_last': 65535,
151 'proto': 0,
152 }
153 return new_rule
154
Andrew Yourtchenkob639b592017-08-09 11:28:02 +0200155 def send(self, side, flags=None):
156 self.ifs[side].add_stream(self.pkt(side, flags))
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200157 self.ifs[1-side].enable_capture()
158 self.testcase.pg_start()
159
160 def recv(self, side):
161 p = self.ifs[side].wait_for_packet(1)
162 return p
163
Andrew Yourtchenkob639b592017-08-09 11:28:02 +0200164 def send_through(self, side, flags=None):
165 self.send(side, flags)
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200166 p = self.recv(1-side)
167 return p
168
Andrew Yourtchenkob639b592017-08-09 11:28:02 +0200169 def send_pingpong(self, side, flags1=None, flags2=None):
170 p1 = self.send_through(side, flags1)
171 p2 = self.send_through(1-side, flags2)
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200172 return [p1, p2]
173
174
175@unittest.skipUnless(running_extended_tests(), "part of extended tests")
176class ACLPluginConnTestCase(VppTestCase):
177 """ ACL plugin connection-oriented extended testcases """
178
179 @classmethod
180 def setUpClass(self):
181 super(ACLPluginConnTestCase, self).setUpClass()
182 # create pg0 and pg1
183 self.create_pg_interfaces(range(2))
184 for i in self.pg_interfaces:
185 i.admin_up()
186 i.config_ip4()
187 i.config_ip6()
188 i.resolve_arp()
189 i.resolve_ndp()
190
Andrew Yourtchenko7f4d5772017-05-24 13:20:47 +0200191 def tearDown(self):
192 """Run standard test teardown and log various show commands
193 """
194 super(ACLPluginConnTestCase, self).tearDown()
195 if not self.vpp_dead:
196 self.logger.info(self.vapi.cli("show ip arp"))
197 self.logger.info(self.vapi.cli("show ip6 neighbors"))
198 self.logger.info(self.vapi.cli("show acl-plugin sessions"))
199 self.logger.info(self.vapi.cli("show acl-plugin acl"))
200 self.logger.info(self.vapi.cli("show acl-plugin interface"))
201 self.logger.info(self.vapi.cli("show acl-plugin tables"))
202
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200203 def api_acl_add_replace(self, acl_index, r, count=-1, tag="",
204 expected_retval=0):
205 """Add/replace an ACL
206
207 :param int acl_index: ACL index to replace, 4294967295 to create new.
208 :param acl_rule r: ACL rules array.
209 :param str tag: symbolic tag (description) for this ACL.
210 :param int count: number of rules.
211 """
212 if (count < 0):
213 count = len(r)
214 return self.vapi.api(self.vapi.papi.acl_add_replace,
215 {'acl_index': acl_index,
216 'r': r,
217 'count': count,
218 'tag': tag
219 }, expected_retval=expected_retval)
220
221 def api_acl_interface_set_acl_list(self, sw_if_index, count, n_input, acls,
222 expected_retval=0):
223 return self.vapi.api(self.vapi.papi.acl_interface_set_acl_list,
224 {'sw_if_index': sw_if_index,
225 'count': count,
226 'n_input': n_input,
227 'acls': acls
228 }, expected_retval=expected_retval)
229
230 def api_acl_dump(self, acl_index, expected_retval=0):
231 return self.vapi.api(self.vapi.papi.acl_dump,
232 {'acl_index': acl_index},
233 expected_retval=expected_retval)
234
235 def run_basic_conn_test(self, af, acl_side):
236 """ Basic conn timeout test """
237 conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
238 conn1.apply_acls(0, acl_side)
239 conn1.send_through(0)
240 # the return packets should pass
241 conn1.send_through(1)
242 # send some packets on conn1, ensure it doesn't go away
243 for i in IterateWithSleep(self, 20, "Keep conn active", 0.3):
244 conn1.send_through(1)
245 # allow the conn to time out
246 for i in IterateWithSleep(self, 30, "Wait for timeout", 0.1):
247 pass
248 # now try to send a packet on the reflected side
249 try:
250 p2 = conn1.send_through(1).command()
251 except:
252 # If we asserted while waiting, it's good.
253 # the conn should have timed out.
254 p2 = None
255 self.assert_equal(p2, None, "packet on long-idle conn")
256
257 def run_active_conn_test(self, af, acl_side):
258 """ Idle connection behind active connection test """
259 base = 10000 + 1000*acl_side
260 conn1 = Conn(self, self.pg0, self.pg1, af, UDP, base + 1, 2323)
261 conn2 = Conn(self, self.pg0, self.pg1, af, UDP, base + 2, 2323)
262 conn3 = Conn(self, self.pg0, self.pg1, af, UDP, base + 3, 2323)
263 conn1.apply_acls(0, acl_side)
264 conn1.send(0)
265 conn1.recv(1)
266 # create and check that the conn2/3 work
267 self.sleep(0.1)
268 conn2.send_pingpong(0)
269 self.sleep(0.1)
270 conn3.send_pingpong(0)
271 # send some packets on conn1, keep conn2/3 idle
272 for i in IterateWithSleep(self, 20, "Keep conn active", 0.2):
273 conn1.send_through(1)
274 try:
275 p2 = conn2.send_through(1).command()
276 except:
277 # If we asserted while waiting, it's good.
278 # the conn should have timed out.
279 p2 = None
280 # We should have not received the packet on a long-idle
281 # connection, because it should have timed out
282 # If it didn't - it is a problem
283 self.assert_equal(p2, None, "packet on long-idle conn")
284
Andrew Yourtchenkoeb467542017-06-21 11:24:25 +0200285 def run_clear_conn_test(self, af, acl_side):
286 """ Clear the connections via CLI """
287 conn1 = Conn(self, self.pg0, self.pg1, af, UDP, 42001, 4242)
288 conn1.apply_acls(0, acl_side)
289 conn1.send_through(0)
290 # the return packets should pass
291 conn1.send_through(1)
292 # send some packets on conn1, ensure it doesn't go away
293 for i in IterateWithSleep(self, 20, "Keep conn active", 0.3):
294 conn1.send_through(1)
295 # clear all connections
296 self.vapi.ppcli("clear acl-plugin sessions")
297 # now try to send a packet on the reflected side
298 try:
299 p2 = conn1.send_through(1).command()
300 except:
301 # If we asserted while waiting, it's good.
302 # the conn should have timed out.
303 p2 = None
304 self.assert_equal(p2, None, "packet on supposedly deleted conn")
305
Andrew Yourtchenkob639b592017-08-09 11:28:02 +0200306 def run_tcp_transient_setup_conn_test(self, af, acl_side):
307 conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53001, 5151)
308 conn1.apply_acls(0, acl_side)
309 conn1.send_through(0, 'S')
310 # the return packets should pass
311 conn1.send_through(1, 'SA')
312 # allow the conn to time out
313 for i in IterateWithSleep(self, 30, "Wait for timeout", 0.1):
314 pass
315 # ensure conn times out
316 try:
317 p2 = conn1.send_through(1).command()
318 except:
319 # If we asserted while waiting, it's good.
320 # the conn should have timed out.
321 p2 = None
322 self.assert_equal(p2, None, "packet on supposedly deleted conn")
323
324 def run_tcp_established_conn_test(self, af, acl_side):
325 conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53002, 5052)
326 conn1.apply_acls(0, acl_side)
327 conn1.send_through(0, 'S')
328 # the return packets should pass
329 conn1.send_through(1, 'SA')
330 # complete the threeway handshake
331 # (NB: sequence numbers not tracked, so not set!)
332 conn1.send_through(0, 'A')
333 # allow the conn to time out if it's in embryonic timer
334 for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
335 pass
336 # Try to send the packet from the "forbidden" side - it must pass
337 conn1.send_through(1, 'A')
338 # ensure conn times out for real
339 for i in IterateWithSleep(self, 130, "Wait for timeout", 0.1):
340 pass
341 try:
342 p2 = conn1.send_through(1).command()
343 except:
344 # If we asserted while waiting, it's good.
345 # the conn should have timed out.
346 p2 = None
347 self.assert_equal(p2, None, "packet on supposedly deleted conn")
348
349 def run_tcp_transient_teardown_conn_test(self, af, acl_side):
350 conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53002, 5052)
351 conn1.apply_acls(0, acl_side)
352 conn1.send_through(0, 'S')
353 # the return packets should pass
354 conn1.send_through(1, 'SA')
355 # complete the threeway handshake
356 # (NB: sequence numbers not tracked, so not set!)
357 conn1.send_through(0, 'A')
358 # allow the conn to time out if it's in embryonic timer
359 for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
360 pass
361 # Try to send the packet from the "forbidden" side - it must pass
362 conn1.send_through(1, 'A')
363 # Send the FIN to bounce the session out of established
364 conn1.send_through(1, 'FA')
365 # If conn landed on transient timer it will time out here
366 for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
367 pass
368 # Now it should have timed out already
369 try:
370 p2 = conn1.send_through(1).command()
371 except:
372 # If we asserted while waiting, it's good.
373 # the conn should have timed out.
374 p2 = None
375 self.assert_equal(p2, None, "packet on supposedly deleted conn")
376
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200377 def test_0000_conn_prepare_test(self):
378 """ Prepare the settings """
379 self.vapi.ppcli("set acl-plugin session timeout udp idle 1")
380
381 def test_0001_basic_conn_test(self):
382 """ IPv4: Basic conn timeout test reflect on ingress """
383 self.run_basic_conn_test(AF_INET, 0)
384
385 def test_0002_basic_conn_test(self):
386 """ IPv4: Basic conn timeout test reflect on egress """
387 self.run_basic_conn_test(AF_INET, 1)
388
Andrew Yourtchenkoeb467542017-06-21 11:24:25 +0200389 def test_0005_clear_conn_test(self):
390 """ IPv4: reflect egress, clear conn """
391 self.run_clear_conn_test(AF_INET, 1)
392
393 def test_0006_clear_conn_test(self):
394 """ IPv4: reflect ingress, clear conn """
395 self.run_clear_conn_test(AF_INET, 0)
396
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200397 def test_0011_active_conn_test(self):
398 """ IPv4: Idle conn behind active conn, reflect on ingress """
399 self.run_active_conn_test(AF_INET, 0)
400
401 def test_0012_active_conn_test(self):
402 """ IPv4: Idle conn behind active conn, reflect on egress """
403 self.run_active_conn_test(AF_INET, 1)
404
405 def test_1001_basic_conn_test(self):
406 """ IPv6: Basic conn timeout test reflect on ingress """
407 self.run_basic_conn_test(AF_INET6, 0)
408
409 def test_1002_basic_conn_test(self):
410 """ IPv6: Basic conn timeout test reflect on egress """
411 self.run_basic_conn_test(AF_INET6, 1)
412
Andrew Yourtchenkoeb467542017-06-21 11:24:25 +0200413 def test_1005_clear_conn_test(self):
414 """ IPv6: reflect egress, clear conn """
415 self.run_clear_conn_test(AF_INET6, 1)
416
417 def test_1006_clear_conn_test(self):
418 """ IPv6: reflect ingress, clear conn """
419 self.run_clear_conn_test(AF_INET6, 0)
420
Andrew Yourtchenko57d7dbc2017-05-02 20:08:51 +0200421 def test_1011_active_conn_test(self):
422 """ IPv6: Idle conn behind active conn, reflect on ingress """
423 self.run_active_conn_test(AF_INET6, 0)
424
425 def test_1012_active_conn_test(self):
426 """ IPv6: Idle conn behind active conn, reflect on egress """
427 self.run_active_conn_test(AF_INET6, 1)
Andrew Yourtchenkob639b592017-08-09 11:28:02 +0200428
429 def test_2000_prepare_for_tcp_test(self):
430 """ Prepare for TCP session tests """
431 # ensure the session hangs on if it gets treated as UDP
432 self.vapi.ppcli("set acl-plugin session timeout udp idle 200")
433 # let the TCP connection time out at 5 seconds
434 self.vapi.ppcli("set acl-plugin session timeout tcp idle 10")
435 self.vapi.ppcli("set acl-plugin session timeout tcp transient 1")
436
437 def test_2001_tcp_transient_conn_test(self):
438 """ IPv4: transient TCP session (incomplete 3WHS), ref. on ingress """
439 self.run_tcp_transient_setup_conn_test(AF_INET, 0)
440
441 def test_2002_tcp_transient_conn_test(self):
442 """ IPv4: transient TCP session (incomplete 3WHS), ref. on egress """
443 self.run_tcp_transient_setup_conn_test(AF_INET, 1)
444
445 def test_2003_tcp_transient_conn_test(self):
446 """ IPv4: established TCP session (complete 3WHS), ref. on ingress """
447 self.run_tcp_established_conn_test(AF_INET, 0)
448
449 def test_2004_tcp_transient_conn_test(self):
450 """ IPv4: established TCP session (complete 3WHS), ref. on egress """
451 self.run_tcp_established_conn_test(AF_INET, 1)
452
453 def test_2005_tcp_transient_teardown_conn_test(self):
454 """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
455 self.run_tcp_transient_teardown_conn_test(AF_INET, 0)
456
457 def test_2006_tcp_transient_teardown_conn_test(self):
458 """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
459 self.run_tcp_transient_teardown_conn_test(AF_INET, 1)
460
461 def test_3001_tcp_transient_conn_test(self):
462 """ IPv6: transient TCP session (incomplete 3WHS), ref. on ingress """
463 self.run_tcp_transient_setup_conn_test(AF_INET6, 0)
464
465 def test_3002_tcp_transient_conn_test(self):
466 """ IPv6: transient TCP session (incomplete 3WHS), ref. on egress """
467 self.run_tcp_transient_setup_conn_test(AF_INET6, 1)
468
469 def test_3003_tcp_transient_conn_test(self):
470 """ IPv6: established TCP session (complete 3WHS), ref. on ingress """
471 self.run_tcp_established_conn_test(AF_INET6, 0)
472
473 def test_3004_tcp_transient_conn_test(self):
474 """ IPv6: established TCP session (complete 3WHS), ref. on egress """
475 self.run_tcp_established_conn_test(AF_INET6, 1)
476
477 def test_3005_tcp_transient_teardown_conn_test(self):
478 """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
479 self.run_tcp_transient_teardown_conn_test(AF_INET6, 0)
480
481 def test_3006_tcp_transient_teardown_conn_test(self):
482 """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
483 self.run_tcp_transient_teardown_conn_test(AF_INET6, 1)