acl-plugin: all TCP sessions treated as transient (VPP-932) 63/7963/3
authorAndrew Yourtchenko <ayourtch@gmail.com>
Wed, 9 Aug 2017 09:28:02 +0000 (11:28 +0200)
committerFlorin Coras <florin.coras@gmail.com>
Thu, 10 Aug 2017 00:05:03 +0000 (00:05 +0000)
The packet that was creating the session was not tracked,
consequently the TCP flags seen within the session record
never got the value for the session to get treated as
being in the established state.

Test-escape, so add the TCP tests which test the
three phases of the TCP session life and make them all pass.

Change-Id: Ib048bc30c809a7f03be2de7e8361c2c281270348
Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com>
(cherry picked from commit 754370f1b55d4102d21dd94676f2bda3170c7df0)

src/plugins/acl/fa_node.c
test/test_acl_plugin_conns.py

index 74079a2..1eeaa8c 100644 (file)
@@ -871,7 +871,7 @@ acl_fa_try_recycle_session (acl_main_t * am, int is_input, u16 thread_index, u32
   }
 }
 
-static void
+static fa_session_t *
 acl_fa_add_session (acl_main_t * am, int is_input, u32 sw_if_index, u64 now,
                    fa_5tuple_t * p5tuple)
 {
@@ -915,6 +915,7 @@ acl_fa_add_session (acl_main_t * am, int is_input, u32 sw_if_index, u64 now,
   clib_mem_set_heap (oldheap);
   pw->fa_session_adds_by_sw_if_index[sw_if_index]++;
   clib_smp_atomic_add(&am->fa_session_total_adds, 1);
+  return sess;
 }
 
 static int
@@ -1079,8 +1080,10 @@ acl_fa_node_fn (vlib_main_t * vm,
 
                  if (acl_fa_can_add_session (am, is_input, sw_if_index0))
                    {
-                      acl_fa_add_session (am, is_input, sw_if_index0, now,
-                                         &kv_sess);
+                      fa_session_t *sess = acl_fa_add_session (am, is_input, sw_if_index0, now,
+                                                              &kv_sess);
+                      acl_fa_track_session (am, is_input, sw_if_index0, now,
+                                            sess, &fa_5tuple);
                      pkts_new_session += 1;
                    }
                  else
index 1a9100c..06f3cf7 100644 (file)
@@ -5,7 +5,7 @@ import unittest
 from framework import VppTestCase, VppTestRunner, running_extended_tests
 from scapy.layers.l2 import Ether
 from scapy.packet import Raw
-from scapy.layers.inet import IP, UDP
+from scapy.layers.inet import IP, UDP, TCP
 from scapy.packet import Packet
 from socket import inet_pton, AF_INET, AF_INET6
 from scapy.layers.inet6 import IPv6, ICMPv6Unknown, ICMPv6EchoRequest
@@ -81,7 +81,7 @@ class Conn():
         self.ports[1] = port2
         self
 
-    def pkt(self, side):
+    def pkt(self, side, flags=None):
         is_ip6 = 1 if self.address_family == AF_INET6 else 0
         s0 = side
         s1 = 1-side
@@ -90,9 +90,12 @@ class Conn():
         layer_3 = [IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4),
                    IPv6(src=src_if.remote_ip6, dst=dst_if.remote_ip6)]
         payload = "x"
+        l4args = {'sport': self.ports[s0], 'dport': self.ports[s1]}
+        if flags is not None:
+            l4args['flags'] = flags
         p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
              layer_3[is_ip6] /
-             self.l4proto(sport=self.ports[s0], dport=self.ports[s1]) /
+             self.l4proto(**l4args) /
              Raw(payload))
         return p
 
@@ -149,8 +152,8 @@ class Conn():
              }
         return new_rule
 
-    def send(self, side):
-        self.ifs[side].add_stream(self.pkt(side))
+    def send(self, side, flags=None):
+        self.ifs[side].add_stream(self.pkt(side, flags))
         self.ifs[1-side].enable_capture()
         self.testcase.pg_start()
 
@@ -158,14 +161,14 @@ class Conn():
         p = self.ifs[side].wait_for_packet(1)
         return p
 
-    def send_through(self, side):
-        self.send(side)
+    def send_through(self, side, flags=None):
+        self.send(side, flags)
         p = self.recv(1-side)
         return p
 
-    def send_pingpong(self, side):
-        p1 = self.send_through(side)
-        p2 = self.send_through(1-side)
+    def send_pingpong(self, side, flags1=None, flags2=None):
+        p1 = self.send_through(side, flags1)
+        p2 = self.send_through(1-side, flags2)
         return [p1, p2]
 
 
@@ -300,6 +303,77 @@ class ACLPluginConnTestCase(VppTestCase):
             p2 = None
         self.assert_equal(p2, None, "packet on supposedly deleted conn")
 
+    def run_tcp_transient_setup_conn_test(self, af, acl_side):
+        conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53001, 5151)
+        conn1.apply_acls(0, acl_side)
+        conn1.send_through(0, 'S')
+        # the return packets should pass
+        conn1.send_through(1, 'SA')
+        # allow the conn to time out
+        for i in IterateWithSleep(self, 30, "Wait for timeout", 0.1):
+            pass
+        # ensure conn times out
+        try:
+            p2 = conn1.send_through(1).command()
+        except:
+            # If we asserted while waiting, it's good.
+            # the conn should have timed out.
+            p2 = None
+        self.assert_equal(p2, None, "packet on supposedly deleted conn")
+
+    def run_tcp_established_conn_test(self, af, acl_side):
+        conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53002, 5052)
+        conn1.apply_acls(0, acl_side)
+        conn1.send_through(0, 'S')
+        # the return packets should pass
+        conn1.send_through(1, 'SA')
+        # complete the threeway handshake
+        # (NB: sequence numbers not tracked, so not set!)
+        conn1.send_through(0, 'A')
+        # allow the conn to time out if it's in embryonic timer
+        for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
+            pass
+        # Try to send the packet from the "forbidden" side - it must pass
+        conn1.send_through(1, 'A')
+        # ensure conn times out for real
+        for i in IterateWithSleep(self, 130, "Wait for timeout", 0.1):
+            pass
+        try:
+            p2 = conn1.send_through(1).command()
+        except:
+            # If we asserted while waiting, it's good.
+            # the conn should have timed out.
+            p2 = None
+        self.assert_equal(p2, None, "packet on supposedly deleted conn")
+
+    def run_tcp_transient_teardown_conn_test(self, af, acl_side):
+        conn1 = Conn(self, self.pg0, self.pg1, af, TCP, 53002, 5052)
+        conn1.apply_acls(0, acl_side)
+        conn1.send_through(0, 'S')
+        # the return packets should pass
+        conn1.send_through(1, 'SA')
+        # complete the threeway handshake
+        # (NB: sequence numbers not tracked, so not set!)
+        conn1.send_through(0, 'A')
+        # allow the conn to time out if it's in embryonic timer
+        for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
+            pass
+        # Try to send the packet from the "forbidden" side - it must pass
+        conn1.send_through(1, 'A')
+        # Send the FIN to bounce the session out of established
+        conn1.send_through(1, 'FA')
+        # If conn landed on transient timer it will time out here
+        for i in IterateWithSleep(self, 30, "Wait for transient timeout", 0.1):
+            pass
+        # Now it should have timed out already
+        try:
+            p2 = conn1.send_through(1).command()
+        except:
+            # If we asserted while waiting, it's good.
+            # the conn should have timed out.
+            p2 = None
+        self.assert_equal(p2, None, "packet on supposedly deleted conn")
+
     def test_0000_conn_prepare_test(self):
         """ Prepare the settings """
         self.vapi.ppcli("set acl-plugin session timeout udp idle 1")
@@ -351,3 +425,59 @@ class ACLPluginConnTestCase(VppTestCase):
     def test_1012_active_conn_test(self):
         """ IPv6: Idle conn behind active conn, reflect on egress """
         self.run_active_conn_test(AF_INET6, 1)
+
+    def test_2000_prepare_for_tcp_test(self):
+        """ Prepare for TCP session tests """
+        # ensure the session hangs on if it gets treated as UDP
+        self.vapi.ppcli("set acl-plugin session timeout udp idle 200")
+        # let the TCP connection time out at 5 seconds
+        self.vapi.ppcli("set acl-plugin session timeout tcp idle 10")
+        self.vapi.ppcli("set acl-plugin session timeout tcp transient 1")
+
+    def test_2001_tcp_transient_conn_test(self):
+        """ IPv4: transient TCP session (incomplete 3WHS), ref. on ingress """
+        self.run_tcp_transient_setup_conn_test(AF_INET, 0)
+
+    def test_2002_tcp_transient_conn_test(self):
+        """ IPv4: transient TCP session (incomplete 3WHS), ref. on egress """
+        self.run_tcp_transient_setup_conn_test(AF_INET, 1)
+
+    def test_2003_tcp_transient_conn_test(self):
+        """ IPv4: established TCP session (complete 3WHS), ref. on ingress """
+        self.run_tcp_established_conn_test(AF_INET, 0)
+
+    def test_2004_tcp_transient_conn_test(self):
+        """ IPv4: established TCP session (complete 3WHS), ref. on egress """
+        self.run_tcp_established_conn_test(AF_INET, 1)
+
+    def test_2005_tcp_transient_teardown_conn_test(self):
+        """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
+        self.run_tcp_transient_teardown_conn_test(AF_INET, 0)
+
+    def test_2006_tcp_transient_teardown_conn_test(self):
+        """ IPv4: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
+        self.run_tcp_transient_teardown_conn_test(AF_INET, 1)
+
+    def test_3001_tcp_transient_conn_test(self):
+        """ IPv6: transient TCP session (incomplete 3WHS), ref. on ingress """
+        self.run_tcp_transient_setup_conn_test(AF_INET6, 0)
+
+    def test_3002_tcp_transient_conn_test(self):
+        """ IPv6: transient TCP session (incomplete 3WHS), ref. on egress """
+        self.run_tcp_transient_setup_conn_test(AF_INET6, 1)
+
+    def test_3003_tcp_transient_conn_test(self):
+        """ IPv6: established TCP session (complete 3WHS), ref. on ingress """
+        self.run_tcp_established_conn_test(AF_INET6, 0)
+
+    def test_3004_tcp_transient_conn_test(self):
+        """ IPv6: established TCP session (complete 3WHS), ref. on egress """
+        self.run_tcp_established_conn_test(AF_INET6, 1)
+
+    def test_3005_tcp_transient_teardown_conn_test(self):
+        """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on ingress """
+        self.run_tcp_transient_teardown_conn_test(AF_INET6, 0)
+
+    def test_3006_tcp_transient_teardown_conn_test(self):
+        """ IPv6: transient TCP session (3WHS,ACK,FINACK), ref. on egress """
+        self.run_tcp_transient_teardown_conn_test(AF_INET6, 1)