Commit e2873d43 authored by Alexander Duyck's avatar Alexander Duyck Committed by Jeff Kirsher

ixgbe: Fix ATR so that it correctly handles IPv6 extension headers

The ATR code was assuming that it would be able to use tcp_hdr for
every TCP frame that came through.  However this isn't the case as it
is possible for a frame to arrive that is TCP but sent through something
like a raw socket.  As a result the driver was setting up bad filters in
which tcp_hdr was really pointing to the network header so the data was
all invalid.

In order to correct this I have added a bit of parsing logic that will
determine the TCP header location based off of the network header and
either the offset in the case of the IPv4 header, or a walk through the
IPv6 extension headers until it encounters the header that indicates
IPPROTO_TCP.  In addition I have added checks to verify that the lowest
protocol provided is recognized as IPv4 or IPv6 to help mitigate raw
sockets using ETH_P_ALL from having ATR applied to them.
Signed-off-by: default avatarAlexander Duyck <aduyck@mirantis.com>
Tested-by: default avatarAndrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 9f12df90
...@@ -7558,8 +7558,10 @@ static void ixgbe_atr(struct ixgbe_ring *ring, ...@@ -7558,8 +7558,10 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
struct ipv6hdr *ipv6; struct ipv6hdr *ipv6;
} hdr; } hdr;
struct tcphdr *th; struct tcphdr *th;
unsigned int hlen;
struct sk_buff *skb; struct sk_buff *skb;
__be16 vlan_id; __be16 vlan_id;
int l4_proto;
/* if ring doesn't have a interrupt vector, cannot perform ATR */ /* if ring doesn't have a interrupt vector, cannot perform ATR */
if (!q_vector) if (!q_vector)
...@@ -7571,10 +7573,14 @@ static void ixgbe_atr(struct ixgbe_ring *ring, ...@@ -7571,10 +7573,14 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
ring->atr_count++; ring->atr_count++;
/* currently only IPv4/IPv6 with TCP is supported */
if ((first->protocol != htons(ETH_P_IP)) &&
(first->protocol != htons(ETH_P_IPV6)))
return;
/* snag network header to get L4 type and address */ /* snag network header to get L4 type and address */
skb = first->skb; skb = first->skb;
hdr.network = skb_network_header(skb); hdr.network = skb_network_header(skb);
th = tcp_hdr(skb);
#ifdef CONFIG_IXGBE_VXLAN #ifdef CONFIG_IXGBE_VXLAN
if (skb->encapsulation && if (skb->encapsulation &&
first->protocol == htons(ETH_P_IP) && first->protocol == htons(ETH_P_IP) &&
...@@ -7583,43 +7589,34 @@ static void ixgbe_atr(struct ixgbe_ring *ring, ...@@ -7583,43 +7589,34 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
/* verify the port is recognized as VXLAN */ /* verify the port is recognized as VXLAN */
if (adapter->vxlan_port && if (adapter->vxlan_port &&
udp_hdr(skb)->dest == adapter->vxlan_port) { udp_hdr(skb)->dest == adapter->vxlan_port)
hdr.network = skb_inner_network_header(skb); hdr.network = skb_inner_network_header(skb);
th = inner_tcp_hdr(skb);
}
} }
#endif /* CONFIG_IXGBE_VXLAN */ #endif /* CONFIG_IXGBE_VXLAN */
/* Currently only IPv4/IPv6 with TCP is supported */ /* Currently only IPv4/IPv6 with TCP is supported */
switch (hdr.ipv4->version) { switch (hdr.ipv4->version) {
case IPVERSION: case IPVERSION:
if (hdr.ipv4->protocol != IPPROTO_TCP) /* access ihl as u8 to avoid unaligned access on ia64 */
return; hlen = (hdr.network[0] & 0x0F) << 2;
l4_proto = hdr.ipv4->protocol;
break; break;
case 6: case 6:
if (likely((unsigned char *)th - hdr.network == hlen = hdr.network - skb->data;
sizeof(struct ipv6hdr))) { l4_proto = ipv6_find_hdr(skb, &hlen, IPPROTO_TCP, NULL, NULL);
if (hdr.ipv6->nexthdr != IPPROTO_TCP) hlen -= hdr.network - skb->data;
return;
} else {
__be16 frag_off;
u8 l4_hdr;
ipv6_skip_exthdr(skb, hdr.network - skb->data +
sizeof(struct ipv6hdr),
&l4_hdr, &frag_off);
if (unlikely(frag_off))
return;
if (l4_hdr != IPPROTO_TCP)
return;
}
break; break;
default: default:
return; return;
} }
/* skip this packet since it is invalid or the socket is closing */ if (l4_proto != IPPROTO_TCP)
if (!th || th->fin) return;
th = (struct tcphdr *)(hdr.network + hlen);
/* skip this packet since the socket is closing */
if (th->fin)
return; return;
/* sample on all syn packets or once every atr sample count */ /* sample on all syn packets or once every atr sample count */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment