loop counter to prevent infiinte number of look ups per-packet 77/13877/3
authorNeale Ranns <neale.ranns@cisco.com>
Wed, 1 Aug 2018 19:53:17 +0000 (12:53 -0700)
committerDave Barach <openvpp@barachs.net>
Fri, 3 Aug 2018 11:49:39 +0000 (11:49 +0000)
Change-Id: I59235d11baac18785a4c90cdaf14e8f3ddf06dab
Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
src/vnet/buffer.c
src/vnet/buffer.h
src/vnet/dpo/lookup_dpo.c
src/vnet/mpls/mpls_lookup.c
src/vnet/mpls/mpls_lookup.h
test/test_ip4.py
test/test_ip6.py

index f793fb4..de1c735 100644 (file)
@@ -42,6 +42,9 @@ format_vnet_buffer (u8 * s, va_list * args)
     a = format (a, "qos %d.%d ",
                vnet_buffer2 (b)->qos.bits, vnet_buffer2 (b)->qos.source);
 
+  if (b->flags & VNET_BUFFER_F_LOOP_COUNTER_VALID)
+    a = format (a, "loop-counter %d ", vnet_buffer2 (b)->loop_counter);
+
   s = format (s, "%U", format_vlib_buffer, b);
   if (a)
     s = format (s, "\n%U%v", format_white_space, indent, a);
index b4ce9c7..e4167ea 100644 (file)
@@ -51,6 +51,7 @@
   _( 3, VLAN_2_DEEP, "vlan-2-deep")                    \
   _( 4, VLAN_1_DEEP, "vlan-1-deep")                    \
   _( 5, SPAN_CLONE, "span-clone")                      \
+  _( 6, LOOP_COUNTER_VALID, 0)                          \
   _( 7, LOCALLY_ORIGINATED, "local")                   \
   _( 8, IS_IP4, "ip4")                                 \
   _( 9, IS_IP6, "ip6")                                 \
@@ -357,7 +358,8 @@ typedef struct
     u8 source;
   } qos;
 
-  u8 __unused[2];
+  u8 loop_counter;
+  u8 __unused[1];
 
   /* Group Based Policy */
   struct
index 059fa3f..ebdbc12 100644 (file)
 static const char *const lookup_input_names[] = LOOKUP_INPUTS;
 static const char *const lookup_cast_names[] = LOOKUP_CASTS;
 
+/**
+ * If a packet encounters a lookup DPO more than the many times
+ * then we assume there is a loop in the forward graph and drop the packet
+ */
+#define MAX_LUKPS_PER_PACKET 4
+
 /**
  * @brief Enumeration of the lookup subtypes
  */
@@ -471,6 +477,23 @@ lookup_dpo_ip4_inline (vlib_main_t * vm,
                (cm, thread_index, lbi1, 1,
                 vlib_buffer_length_in_chain (vm, b1));
 
+            if (!(b0->flags & VNET_BUFFER_F_LOOP_COUNTER_VALID)) {
+                vnet_buffer2(b0)->loop_counter = 0;
+                b0->flags |= VNET_BUFFER_F_LOOP_COUNTER_VALID;
+            }
+            if (!(b1->flags & VNET_BUFFER_F_LOOP_COUNTER_VALID)) {
+                vnet_buffer2(b1)->loop_counter = 0;
+                b1->flags |= VNET_BUFFER_F_LOOP_COUNTER_VALID;
+            }
+
+            vnet_buffer2(b0)->loop_counter++;
+            vnet_buffer2(b1)->loop_counter++;
+
+            if (PREDICT_FALSE(vnet_buffer2(b0)->loop_counter > MAX_LUKPS_PER_PACKET))
+                next0 = IP_LOOKUP_NEXT_DROP;
+            if (PREDICT_FALSE(vnet_buffer2(b1)->loop_counter > MAX_LUKPS_PER_PACKET))
+                next1 = IP_LOOKUP_NEXT_DROP;
+
            if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
            {
                lookup_trace_t *tr = vlib_add_trace (vm, node,
@@ -572,6 +595,16 @@ lookup_dpo_ip4_inline (vlib_main_t * vm,
                (cm, thread_index, lbi0, 1,
                 vlib_buffer_length_in_chain (vm, b0));
 
+            if (!(b0->flags & VNET_BUFFER_F_LOOP_COUNTER_VALID)) {
+                vnet_buffer2(b0)->loop_counter = 0;
+                b0->flags |= VNET_BUFFER_F_LOOP_COUNTER_VALID;
+            }
+
+            vnet_buffer2(b0)->loop_counter++;
+
+            if (PREDICT_FALSE(vnet_buffer2(b0)->loop_counter > MAX_LUKPS_PER_PACKET))
+                next0 = IP_LOOKUP_NEXT_DROP;
+
            if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
            {
                lookup_trace_t *tr = vlib_add_trace (vm, node,
@@ -780,6 +813,23 @@ lookup_dpo_ip6_inline (vlib_main_t * vm,
            hash_c0 = vnet_buffer (b0)->ip.flow_hash = 0;
            hash_c1 = vnet_buffer (b1)->ip.flow_hash = 0;
 
+            if (!(b0->flags & VNET_BUFFER_F_LOOP_COUNTER_VALID)) {
+                vnet_buffer2(b0)->loop_counter = 0;
+                b0->flags |= VNET_BUFFER_F_LOOP_COUNTER_VALID;
+            }
+            if (!(b1->flags & VNET_BUFFER_F_LOOP_COUNTER_VALID)) {
+                vnet_buffer2(b1)->loop_counter = 0;
+                b1->flags |= VNET_BUFFER_F_LOOP_COUNTER_VALID;
+            }
+
+            vnet_buffer2(b0)->loop_counter++;
+            vnet_buffer2(b1)->loop_counter++;
+
+            if (PREDICT_FALSE(vnet_buffer2(b0)->loop_counter > MAX_LUKPS_PER_PACKET))
+                next0 = IP_LOOKUP_NEXT_DROP;
+            if (PREDICT_FALSE(vnet_buffer2(b1)->loop_counter > MAX_LUKPS_PER_PACKET))
+                next1 = IP_LOOKUP_NEXT_DROP;
+
            if (PREDICT_FALSE (lb0->lb_n_buckets > 1))
            {
                flow_hash_config0 = lb0->lb_hash_config;
@@ -910,6 +960,16 @@ lookup_dpo_ip6_inline (vlib_main_t * vm,
            next0 = dpo0->dpoi_next_node;
            vnet_buffer(b0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
 
+            if (!(b0->flags & VNET_BUFFER_F_LOOP_COUNTER_VALID)) {
+                vnet_buffer2(b0)->loop_counter = 0;
+                b0->flags |= VNET_BUFFER_F_LOOP_COUNTER_VALID;
+            }
+
+            vnet_buffer2(b0)->loop_counter++;
+
+            if (PREDICT_FALSE(vnet_buffer2(b0)->loop_counter > MAX_LUKPS_PER_PACKET))
+                next0 = IP_LOOKUP_NEXT_DROP;
+
            vlib_increment_combined_counter
                (cm, thread_index, lbi0, 1,
                 vlib_buffer_length_in_chain (vm, b0));
@@ -1085,11 +1145,21 @@ lookup_dpo_mpls_inline (vlib_main_t * vm,
                      vlib_buffer_length_in_chain (vm, b0));
             }
 
-          vnet_buffer (b0)->mpls.ttl = ((char*)hdr0)[3];
+            vnet_buffer (b0)->mpls.ttl = ((char*)hdr0)[3];
             vnet_buffer (b0)->mpls.exp = (((char*)hdr0)[2] & 0xe) >> 1;
             vnet_buffer (b0)->mpls.first = 1;
             vlib_buffer_advance(b0, sizeof(*hdr0));
 
+            if (!(b0->flags & VNET_BUFFER_F_LOOP_COUNTER_VALID)) {
+                vnet_buffer2(b0)->loop_counter = 0;
+                b0->flags |= VNET_BUFFER_F_LOOP_COUNTER_VALID;
+            }
+
+            vnet_buffer2(b0)->loop_counter++;
+
+            if (PREDICT_FALSE(vnet_buffer2(b0)->loop_counter > MAX_LUKPS_PER_PACKET))
+                next0 = MPLS_LOOKUP_NEXT_DROP;
+
            if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
             {
                 lookup_trace_t *tr = vlib_add_trace (vm, node, 
@@ -1163,6 +1233,7 @@ VLIB_REGISTER_NODE (lookup_mpls_dst_itf_node) = {
 VLIB_NODE_FUNCTION_MULTIARCH (lookup_mpls_dst_itf_node, lookup_mpls_dst_itf)
 
 typedef enum lookup_ip_dst_mcast_next_t_ {
+    LOOKUP_IP_DST_MCAST_NEXT_DROP,
     LOOKUP_IP_DST_MCAST_NEXT_RPF,
     LOOKUP_IP_DST_MCAST_N_NEXT,
 } mfib_forward_lookup_next_t;
@@ -1249,6 +1320,16 @@ lookup_dpo_ip_dst_mcast_inline (vlib_main_t * vm,
 
             vnet_buffer (b0)->ip.adj_index[VLIB_TX] = mfei0;
 
+            if (!(b0->flags & VNET_BUFFER_F_LOOP_COUNTER_VALID)) {
+                vnet_buffer2(b0)->loop_counter = 0;
+                b0->flags |= VNET_BUFFER_F_LOOP_COUNTER_VALID;
+            }
+
+            vnet_buffer2(b0)->loop_counter++;
+
+            if (PREDICT_FALSE(vnet_buffer2(b0)->loop_counter > MAX_LUKPS_PER_PACKET))
+                next0 = LOOKUP_IP_DST_MCAST_NEXT_DROP;
+
             vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
                                             n_left_to_next, bi0, next0);
         }
@@ -1273,6 +1354,7 @@ VLIB_REGISTER_NODE (lookup_ip4_dst_mcast_node) = {
     .format_trace = format_lookup_trace,
     .n_next_nodes = LOOKUP_IP_DST_MCAST_N_NEXT,
     .next_nodes = {
+        [LOOKUP_IP_DST_MCAST_NEXT_DROP] = "ip4-drop",
         [LOOKUP_IP_DST_MCAST_NEXT_RPF] = "ip4-mfib-forward-rpf",
     },
 };
@@ -1295,6 +1377,7 @@ VLIB_REGISTER_NODE (lookup_ip6_dst_mcast_node) = {
     .format_trace = format_lookup_trace,
     .n_next_nodes = LOOKUP_IP_DST_MCAST_N_NEXT,
     .next_nodes = {
+        [LOOKUP_IP_DST_MCAST_NEXT_DROP] = "ip6-drop",
         [LOOKUP_IP_DST_MCAST_NEXT_RPF] = "ip6-mfib-forward-rpf",
     },
 };
index 04a983b..56492f1 100644 (file)
@@ -696,7 +696,7 @@ VLIB_REGISTER_NODE (mpls_load_balance_node) = {
   .n_next_nodes = 1,
   .next_nodes =
   {
-      [0] = "mpls-drop",
+      [MPLS_LOOKUP_NEXT_DROP] = "mpls-drop",
   },
 
 };
index 28c9124..4311dc0 100644 (file)
  */
 u32 mpls_lookup_to_replicate_edge;
 
+/**
+ * Enum of statically configred MPLS lookup next nodes
+ */
+typedef enum mpls_lookup_next_t_
+{
+    MPLS_LOOKUP_NEXT_DROP = 0,
+} mpls_lookup_next_t;
+
 /*
  * Compute flow hash. 
  * We'll use it to select which adjacency to use for this flow.  And other things.
index e501bff..d15453d 100644 (file)
@@ -1246,6 +1246,24 @@ class TestIPDeag(VppTestCase):
         route_in_src.add_vpp_config()
         self.send_and_expect(self.pg0, pkts_src, self.pg2)
 
+        #
+        # loop in the lookup DP
+        #
+        route_loop = VppIpRoute(self, "2.2.2.3", 32,
+                                [VppRoutePath("0.0.0.0",
+                                              0xffffffff,
+                                              nh_table_id=0)])
+        route_loop.add_vpp_config()
+
+        p_l = (Ether(src=self.pg0.remote_mac,
+                     dst=self.pg0.local_mac) /
+               IP(src="2.2.2.4", dst="2.2.2.3") /
+               TCP(sport=1234, dport=1234) /
+               Raw('\xa5' * 100))
+
+        self.send_and_assert_no_replies(self.pg0, p_l * 257,
+                                        "IP lookup loop")
+
 
 class TestIPInput(VppTestCase):
     """ IPv4 Input Exceptions """
index 2f19bcf..9a0c752 100644 (file)
@@ -9,7 +9,7 @@ from vpp_sub_interface import VppSubInterface, VppDot1QSubint
 from vpp_pg_interface import is_ipv6_misc
 from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, VppIpMRoute, \
     VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \
-    VppMplsRoute, DpoProto, VppMplsTable
+    VppMplsRoute, DpoProto, VppMplsTable, VppIpTable
 from vpp_neighbor import find_nbr, VppNeighbor
 
 from scapy.packet import Raw
@@ -1957,6 +1957,125 @@ class TestIP6Punt(VppTestCase):
                                    is_ip6=1)
 
 
+class TestIPDeag(VppTestCase):
+    """ IPv6 Deaggregate Routes """
+
+    def setUp(self):
+        super(TestIPDeag, self).setUp()
+
+        self.create_pg_interfaces(range(3))
+
+        for i in self.pg_interfaces:
+            i.admin_up()
+            i.config_ip6()
+            i.resolve_ndp()
+
+    def tearDown(self):
+        super(TestIPDeag, self).tearDown()
+        for i in self.pg_interfaces:
+            i.unconfig_ip6()
+            i.admin_down()
+
+    def test_ip_deag(self):
+        """ IP Deag Routes """
+
+        #
+        # Create a table to be used for:
+        #  1 - another destination address lookup
+        #  2 - a source address lookup
+        #
+        table_dst = VppIpTable(self, 1, is_ip6=1)
+        table_src = VppIpTable(self, 2, is_ip6=1)
+        table_dst.add_vpp_config()
+        table_src.add_vpp_config()
+
+        #
+        # Add a route in the default table to point to a deag/
+        # second lookup in each of these tables
+        #
+        route_to_dst = VppIpRoute(self, "1::1", 128,
+                                  [VppRoutePath("::",
+                                                0xffffffff,
+                                                nh_table_id=1,
+                                                proto=DpoProto.DPO_PROTO_IP6)],
+                                  is_ip6=1)
+        route_to_src = VppIpRoute(self, "1::2", 128,
+                                  [VppRoutePath("::",
+                                                0xffffffff,
+                                                nh_table_id=2,
+                                                is_source_lookup=1,
+                                                proto=DpoProto.DPO_PROTO_IP6)],
+                                  is_ip6=1)
+        route_to_dst.add_vpp_config()
+        route_to_src.add_vpp_config()
+
+        #
+        # packets to these destination are dropped, since they'll
+        # hit the respective default routes in the second table
+        #
+        p_dst = (Ether(src=self.pg0.remote_mac,
+                       dst=self.pg0.local_mac) /
+                 IPv6(src="5::5", dst="1::1") /
+                 TCP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+        p_src = (Ether(src=self.pg0.remote_mac,
+                       dst=self.pg0.local_mac) /
+                 IPv6(src="2::2", dst="1::2") /
+                 TCP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+        pkts_dst = p_dst * 257
+        pkts_src = p_src * 257
+
+        self.send_and_assert_no_replies(self.pg0, pkts_dst,
+                                        "IP in dst table")
+        self.send_and_assert_no_replies(self.pg0, pkts_src,
+                                        "IP in src table")
+
+        #
+        # add a route in the dst table to forward via pg1
+        #
+        route_in_dst = VppIpRoute(self, "1::1", 128,
+                                  [VppRoutePath(self.pg1.remote_ip6,
+                                                self.pg1.sw_if_index,
+                                                proto=DpoProto.DPO_PROTO_IP6)],
+                                  is_ip6=1,
+                                  table_id=1)
+        route_in_dst.add_vpp_config()
+
+        self.send_and_expect(self.pg0, pkts_dst, self.pg1)
+
+        #
+        # add a route in the src table to forward via pg2
+        #
+        route_in_src = VppIpRoute(self, "2::2", 128,
+                                  [VppRoutePath(self.pg2.remote_ip6,
+                                                self.pg2.sw_if_index,
+                                                proto=DpoProto.DPO_PROTO_IP6)],
+                                  is_ip6=1,
+                                  table_id=2)
+        route_in_src.add_vpp_config()
+        self.send_and_expect(self.pg0, pkts_src, self.pg2)
+
+        #
+        # loop in the lookup DP
+        #
+        route_loop = VppIpRoute(self, "3::3", 128,
+                                [VppRoutePath("::",
+                                              0xffffffff,
+                                              proto=DpoProto.DPO_PROTO_IP6)],
+                                is_ip6=1)
+        route_loop.add_vpp_config()
+
+        p_l = (Ether(src=self.pg0.remote_mac,
+                     dst=self.pg0.local_mac) /
+               IPv6(src="3::4", dst="3::3") /
+               TCP(sport=1234, dport=1234) /
+               Raw('\xa5' * 100))
+
+        self.send_and_assert_no_replies(self.pg0, p_l * 257,
+                                        "IP lookup loop")
+
+
 class TestIP6Input(VppTestCase):
     """ IPv6 Input Exceptions """