In flow dissector there are no limits to the number of nested
encapsulations that might be dissected which makes for a nice DOS
attack. This patch limits for dissecting nested encapsulations
as well as for dissecting over extension headers.

Reported-by: Hannes Frederic Sowa <han...@stressinduktion.org>
Signed-off-by: Tom Herbert <t...@quantonium.net>
---
 net/core/flow_dissector.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 45 insertions(+), 3 deletions(-)

diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 5110180a3e96..1bca748de27d 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -396,6 +396,35 @@ __skb_flow_dissect_ipv6(const struct sk_buff *skb,
        key_ip->ttl = iph->hop_limit;
 }
 
+/* Maximum number of nested encapsulations that can be processed in
+ * __skb_flow_dissect
+ */
+#define MAX_FLOW_DISSECT_ENCAPS        5
+
+static bool skb_flow_dissect_encap_allowed(int *num_encaps, unsigned int 
*flags)
+{
+       ++*num_encaps;
+
+       if (*num_encaps >= MAX_FLOW_DISSECT_ENCAPS) {
+               if (*num_encaps == MAX_FLOW_DISSECT_ENCAPS) {
+                       /* Allow one more pass but ignore disregard
+                        * further encapsulations
+                        */
+                       *flags |= FLOW_DISSECTOR_F_STOP_AT_ENCAP;
+               } else {
+                       /* Max encaps reached */
+                       return  false;
+               }
+       }
+
+       return true;
+}
+
+/* Maximum number of extension headers can be processed in __skb_flow_dissect
+ * per IPv6 packet
+ */
+#define MAX_FLOW_DISSECT_EH    5
+
 /**
  * __skb_flow_dissect - extract the flow_keys struct and return it
  * @skb: sk_buff to extract the flow from, can be NULL if the rest are 
specified
@@ -426,6 +455,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
        struct flow_dissector_key_tags *key_tags;
        struct flow_dissector_key_vlan *key_vlan;
        enum flow_dissect_ret fdret;
+       int num_eh, num_encaps = 0;
        bool skip_vlan = false;
        u8 ip_proto = 0;
        bool ret;
@@ -714,7 +744,9 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
        case FLOW_DISSECT_RET_OUT_GOOD:
                goto out_good;
        case FLOW_DISSECT_RET_PROTO_AGAIN:
-               goto proto_again;
+               if (skb_flow_dissect_encap_allowed(&num_encaps, &flags))
+                       goto proto_again;
+               goto out_good;
        case FLOW_DISSECT_RET_CONTINUE:
        case FLOW_DISSECT_RET_IPPROTO_AGAIN:
        case FLOW_DISSECT_RET_IPPROTO_AGAIN_EH:
@@ -724,6 +756,8 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
                goto out_bad;
        }
 
+       num_eh = 0;
+
 ip_proto_again:
        fdret = FLOW_DISSECT_RET_CONTINUE;
 
@@ -844,10 +878,18 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
        /* Process result of IP proto processing */
        switch (fdret) {
        case FLOW_DISSECT_RET_PROTO_AGAIN:
-               goto proto_again;
+               if (skb_flow_dissect_encap_allowed(&num_encaps, &flags))
+                       goto proto_again;
+               break;
        case FLOW_DISSECT_RET_IPPROTO_AGAIN:
+               if (skb_flow_dissect_encap_allowed(&num_encaps, &flags))
+                       goto ip_proto_again;
+               break;
        case FLOW_DISSECT_RET_IPPROTO_AGAIN_EH:
-               goto ip_proto_again;
+               ++num_eh;
+               if (num_eh <= MAX_FLOW_DISSECT_EH)
+                       goto ip_proto_again;
+               break;
        case FLOW_DISSECT_RET_OUT_GOOD:
        case FLOW_DISSECT_RET_CONTINUE:
                break;
-- 
2.11.0

Reply via email to