Flow dissector does not limit the number of encapsulated packets or IPv6
header extensions that will be processed. This could easily be
suceptible to DOS attack-- for instance a 1500 byte packet could contain
75 IPIP headers.

This patch places limits on the number of encapsulations and IPv6 extension
headers that are processed in flow dissector

Signed-off-by: Tom Herbert <t...@herbertland.com>
---
 net/core/flow_dissector.c | 37 +++++++++++++++++++++++++++----------
 1 file changed, 27 insertions(+), 10 deletions(-)

diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 1a7b80f..919bd02 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -91,6 +91,22 @@ __be32 __skb_flow_get_ports(const struct sk_buff *skb, int 
thoff, u8 ip_proto,
 }
 EXPORT_SYMBOL(__skb_flow_get_ports);
 
+#define MAX_DISSECT_DEPTH      10
+#define MAX_DISSECT_EXT                10
+
+#define __DISSECT_AGAIN(_target, _depth, _limit) do {  \
+       (_depth)++;                                     \
+       if ((_depth) > (_limit))                                \
+               goto out_good;                          \
+       else                                            \
+               goto _target;                           \
+} while (0)
+
+#define DISSECT_AGAIN(target) \
+       __DISSECT_AGAIN(target, depth, MAX_DISSECT_DEPTH)
+#define DISSECT_AGAIN_EXT(target) \
+       __DISSECT_AGAIN(target, ext_cnt, MAX_DISSECT_EXT)
+
 /**
  * __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
@@ -123,6 +139,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
        bool skip_vlan = false;
        u8 ip_proto = 0;
        bool ret = false;
+       int depth = 0, ext_cnt = 0;
 
        if (!data) {
                data = skb->data;
@@ -262,7 +279,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
                        proto = vlan->h_vlan_encapsulated_proto;
                        nhoff += sizeof(*vlan);
                        if (skip_vlan)
-                               goto again;
+                               DISSECT_AGAIN(again);
                }
 
                skip_vlan = true;
@@ -285,7 +302,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
                        }
                }
 
-               goto again;
+               DISSECT_AGAIN(again);
        }
        case htons(ETH_P_PPP_SES): {
                struct {
@@ -299,9 +316,9 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
                nhoff += PPPOE_SES_HLEN;
                switch (proto) {
                case htons(PPP_IP):
-                       goto ip;
+                       DISSECT_AGAIN(ip);
                case htons(PPP_IPV6):
-                       goto ipv6;
+                       DISSECT_AGAIN(ipv6);
                default:
                        goto out_bad;
                }
@@ -472,7 +489,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
                if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP)
                        goto out_good;
 
-               goto again;
+               DISSECT_AGAIN(again);
        }
        case NEXTHDR_HOP:
        case NEXTHDR_ROUTING:
@@ -490,7 +507,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
                ip_proto = opthdr[0];
                nhoff += (opthdr[1] + 1) << 3;
 
-               goto ip_proto_again;
+               DISSECT_AGAIN_EXT(ip_proto_again);
        }
        case NEXTHDR_FRAGMENT: {
                struct frag_hdr _fh, *fh;
@@ -512,7 +529,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
                if (!(fh->frag_off & htons(IP6_OFFSET))) {
                        key_control->flags |= FLOW_DIS_FIRST_FRAG;
                        if (flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG)
-                               goto ip_proto_again;
+                               DISSECT_AGAIN_EXT(ip_proto_again);
                }
                goto out_good;
        }
@@ -523,7 +540,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
                if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP)
                        goto out_good;
 
-               goto ip;
+               DISSECT_AGAIN(ip);
        case IPPROTO_IPV6:
                proto = htons(ETH_P_IPV6);
 
@@ -531,10 +548,10 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
                if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP)
                        goto out_good;
 
-               goto ipv6;
+               DISSECT_AGAIN(ipv6);
        case IPPROTO_MPLS:
                proto = htons(ETH_P_MPLS_UC);
-               goto mpls;
+               DISSECT_AGAIN(mpls);
        default:
                break;
        }
-- 
2.9.3

Reply via email to