Skip to content

Commit

Permalink
ixgbe: Fix ATR so that it correctly handles IPv6 extension headers
Browse files Browse the repository at this point in the history
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: Alexander Duyck <aduyck@mirantis.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
  • Loading branch information
Alexander Duyck authored and Jeff Kirsher committed Mar 30, 2016
1 parent 9f12df9 commit e2873d4
Showing 1 changed file with 21 additions and 24 deletions.
45 changes: 21 additions & 24 deletions drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7558,8 +7558,10 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
struct ipv6hdr *ipv6;
} hdr;
struct tcphdr *th;
unsigned int hlen;
struct sk_buff *skb;
__be16 vlan_id;
int l4_proto;

/* if ring doesn't have a interrupt vector, cannot perform ATR */
if (!q_vector)
Expand All @@ -7571,10 +7573,14 @@ static void ixgbe_atr(struct ixgbe_ring *ring,

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 */
skb = first->skb;
hdr.network = skb_network_header(skb);
th = tcp_hdr(skb);
#ifdef CONFIG_IXGBE_VXLAN
if (skb->encapsulation &&
first->protocol == htons(ETH_P_IP) &&
Expand All @@ -7583,43 +7589,34 @@ static void ixgbe_atr(struct ixgbe_ring *ring,

/* verify the port is recognized as VXLAN */
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);
th = inner_tcp_hdr(skb);
}
}
#endif /* CONFIG_IXGBE_VXLAN */

/* Currently only IPv4/IPv6 with TCP is supported */
switch (hdr.ipv4->version) {
case IPVERSION:
if (hdr.ipv4->protocol != IPPROTO_TCP)
return;
/* access ihl as u8 to avoid unaligned access on ia64 */
hlen = (hdr.network[0] & 0x0F) << 2;
l4_proto = hdr.ipv4->protocol;
break;
case 6:
if (likely((unsigned char *)th - hdr.network ==
sizeof(struct ipv6hdr))) {
if (hdr.ipv6->nexthdr != IPPROTO_TCP)
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;
}
hlen = hdr.network - skb->data;
l4_proto = ipv6_find_hdr(skb, &hlen, IPPROTO_TCP, NULL, NULL);
hlen -= hdr.network - skb->data;
break;
default:
return;
}

/* skip this packet since it is invalid or the socket is closing */
if (!th || th->fin)
if (l4_proto != IPPROTO_TCP)
return;

th = (struct tcphdr *)(hdr.network + hlen);

/* skip this packet since the socket is closing */
if (th->fin)
return;

/* sample on all syn packets or once every atr sample count */
Expand Down

0 comments on commit e2873d4

Please sign in to comment.