From 918c023f29ab2dd8c63cfcc6a1239ee15933871a Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 24 Feb 2016 09:29:38 -0800 Subject: [PATCH 1/5] flow_dissector: Check for IP fragmentation even if not using IPv4 address This patch corrects the logic for the IPv4 parsing so that it is consistent with how we handle IPv6. Specifically if we do not have the flow key indicating we want the addresses we still may need to take a look at the IP fragmentation bits and to see if we should stop after we have recognized the L3 header. Fixes: 807e165dc44f ("flow_dissector: Add control/reporting of fragmentation") Signed-off-by: Alexander Duyck Acked-by: Tom Herbert Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 12e7003320107..1f88f8280280b 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -178,15 +178,16 @@ bool __skb_flow_dissect(const struct sk_buff *skb, ip_proto = iph->protocol; - if (!dissector_uses_key(flow_dissector, - FLOW_DISSECTOR_KEY_IPV4_ADDRS)) - break; + if (dissector_uses_key(flow_dissector, + FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { + key_addrs = skb_flow_dissector_target(flow_dissector, + FLOW_DISSECTOR_KEY_IPV4_ADDRS, + target_container); - key_addrs = skb_flow_dissector_target(flow_dissector, - FLOW_DISSECTOR_KEY_IPV4_ADDRS, target_container); - memcpy(&key_addrs->v4addrs, &iph->saddr, - sizeof(key_addrs->v4addrs)); - key_control->addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; + memcpy(&key_addrs->v4addrs, &iph->saddr, + sizeof(key_addrs->v4addrs)); + key_control->addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; + } if (ip_is_fragment(iph)) { key_control->flags |= FLOW_DIS_IS_FRAGMENT; From 43d2ccb3c122a47524019d1831a54f07f7fcb978 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 24 Feb 2016 09:29:44 -0800 Subject: [PATCH 2/5] flow_dissector: Fix fragment handling for header length computation It turns out that for IPv4 we were reporting the ip_proto of the fragment, and for IPv6 we were not. This patch updates that behavior so that we always report the IP protocol of the fragment. In addition it takes the steps of updating the payload offset code so that we will determine the start of the payload not including the L4 header for any fragment after the first. Signed-off-by: Alexander Duyck Acked-by: Tom Herbert Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 1f88f8280280b..8bd745f727349 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -448,13 +448,12 @@ bool __skb_flow_dissect(const struct sk_buff *skb, key_control->flags |= FLOW_DIS_IS_FRAGMENT; nhoff += sizeof(_fh); + ip_proto = fh->nexthdr; if (!(fh->frag_off & htons(IP6_OFFSET))) { key_control->flags |= FLOW_DIS_FIRST_FRAG; - if (flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG) { - ip_proto = fh->nexthdr; + if (flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG) goto ip_proto_again; - } } goto out_good; } @@ -741,6 +740,11 @@ u32 __skb_get_poff(const struct sk_buff *skb, void *data, { u32 poff = keys->control.thoff; + /* skip L4 headers for fragments after the first */ + if ((keys->control.flags & FLOW_DIS_IS_FRAGMENT) && + !(keys->control.flags & FLOW_DIS_FIRST_FRAG)) + return poff; + switch (keys->basic.ip_proto) { case IPPROTO_TCP: { /* access doff as u8 to avoid unaligned access */ From 224516b3a798a0563346748744f8cd19feaf09be Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 24 Feb 2016 09:29:51 -0800 Subject: [PATCH 3/5] flow_dissector: Correctly handle parsing FCoE The flow dissector bits handling FCoE didn't bother to actually validate that the space there was enough for the FCoE header. So we need to update things so that if there is room we add the header and report a good result, otherwise we do not add the header, and report the bad result. Signed-off-by: Alexander Duyck Acked-by: Tom Herbert Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 8bd745f727349..6288153d7f368 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -340,8 +340,11 @@ bool __skb_flow_dissect(const struct sk_buff *skb, } case htons(ETH_P_FCOE): - key_control->thoff = (u16)(nhoff + FCOE_HEADER_LEN); - /* fall through */ + if ((hlen - nhoff) < FCOE_HEADER_LEN) + goto out_bad; + + nhoff += FCOE_HEADER_LEN; + goto out_good; default: goto out_bad; } From b3c3106ce3f4646a008cd238b16f899ae14fd2a7 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 24 Feb 2016 09:29:57 -0800 Subject: [PATCH 4/5] flow_dissector: Use same pointer for IPv4 and IPv6 addresses The IPv6 parsing was using a local pointer when it could use the same pointer as the IPv4 portion of the code since the key_addrs can support both IPv4 and IPv6 as it is just a pointer. Signed-off-by: Alexander Duyck Acked-by: Tom Herbert Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 6288153d7f368..7c7b8739b8b8b 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -220,13 +220,12 @@ bool __skb_flow_dissect(const struct sk_buff *skb, if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { - struct flow_dissector_key_ipv6_addrs *key_ipv6_addrs; - - key_ipv6_addrs = skb_flow_dissector_target(flow_dissector, - FLOW_DISSECTOR_KEY_IPV6_ADDRS, - target_container); + key_addrs = skb_flow_dissector_target(flow_dissector, + FLOW_DISSECTOR_KEY_IPV6_ADDRS, + target_container); - memcpy(key_ipv6_addrs, &iph->saddr, sizeof(*key_ipv6_addrs)); + memcpy(&key_addrs->v6addrs, &iph->saddr, + sizeof(key_addrs->v6addrs)); key_control->addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; } From d975ddd69698d068a8ebd294c382f1c791b380da Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 24 Feb 2016 09:30:04 -0800 Subject: [PATCH 5/5] eth: Pull header from first fragment via eth_get_headlen We want to try and pull the L4 header in if it is available in the first fragment. As such add the flag to indicate we want to pull the headers on the first fragment in. Signed-off-by: Alexander Duyck Acked-by: Tom Herbert Signed-off-by: David S. Miller --- net/ethernet/eth.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 103871784e508..66dff5e3d7728 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -125,6 +125,7 @@ EXPORT_SYMBOL(eth_header); */ u32 eth_get_headlen(void *data, unsigned int len) { + const unsigned int flags = FLOW_DISSECTOR_F_PARSE_1ST_FRAG; const struct ethhdr *eth = (const struct ethhdr *)data; struct flow_keys keys; @@ -134,7 +135,7 @@ u32 eth_get_headlen(void *data, unsigned int len) /* parse any remaining L2/L3 headers, check for L4 */ if (!skb_flow_dissect_flow_keys_buf(&keys, data, eth->h_proto, - sizeof(*eth), len, 0)) + sizeof(*eth), len, flags)) return max_t(u32, keys.control.thoff, sizeof(*eth)); /* parse for any L4 headers */