Skip to content

Commit

Permalink
tunnels: Don't apply GRO to multiple layers of encapsulation.
Browse files Browse the repository at this point in the history
When drivers express support for TSO of encapsulated packets, they
only mean that they can do it for one layer of encapsulation.
Supporting additional levels would mean updating, at a minimum,
more IP length fields and they are unaware of this.

No encapsulation device expresses support for handling offloaded
encapsulated packets, so we won't generate these types of frames
in the transmit path. However, GRO doesn't have a check for
multiple levels of encapsulation and will attempt to build them.

UDP tunnel GRO actually does prevent this situation but it only
handles multiple UDP tunnels stacked on top of each other. This
generalizes that solution to prevent any kind of tunnel stacking
that would cause problems.

Fixes: bf5a755 ("net-gre-gro: Add GRE support to the GRO stack")
Signed-off-by: Jesse Gross <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
jessegross authored and davem330 committed Mar 20, 2016
1 parent b8cba75 commit fac8e0f
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 8 deletions.
4 changes: 2 additions & 2 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -2096,8 +2096,8 @@ struct napi_gro_cb {
/* This is non-zero if the packet may be of the same flow. */
u8 same_flow:1;

/* Used in udp_gro_receive */
u8 udp_mark:1;
/* Used in tunnel GRO receive */
u8 encap_mark:1;

/* GRO checksum is valid */
u8 csum_valid:1;
Expand Down
2 changes: 1 addition & 1 deletion net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -4438,7 +4438,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
NAPI_GRO_CB(skb)->same_flow = 0;
NAPI_GRO_CB(skb)->flush = 0;
NAPI_GRO_CB(skb)->free = 0;
NAPI_GRO_CB(skb)->udp_mark = 0;
NAPI_GRO_CB(skb)->encap_mark = 0;
NAPI_GRO_CB(skb)->gro_remcsum_start = 0;

/* Setup for GRO checksum validation */
Expand Down
15 changes: 14 additions & 1 deletion net/ipv4/af_inet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,19 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
return pp;
}

static struct sk_buff **ipip_gro_receive(struct sk_buff **head,
struct sk_buff *skb)
{
if (NAPI_GRO_CB(skb)->encap_mark) {
NAPI_GRO_CB(skb)->flush = 1;
return NULL;
}

NAPI_GRO_CB(skb)->encap_mark = 1;

return inet_gro_receive(head, skb);
}

#define SECONDS_PER_DAY 86400

/* inet_current_timestamp - Return IP network timestamp
Expand Down Expand Up @@ -1682,7 +1695,7 @@ static struct packet_offload ip_packet_offload __read_mostly = {
static const struct net_offload ipip_offload = {
.callbacks = {
.gso_segment = inet_gso_segment,
.gro_receive = inet_gro_receive,
.gro_receive = ipip_gro_receive,
.gro_complete = ipip_gro_complete,
},
};
Expand Down
5 changes: 5 additions & 0 deletions net/ipv4/gre_offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ static struct sk_buff **gre_gro_receive(struct sk_buff **head,
struct packet_offload *ptype;
__be16 type;

if (NAPI_GRO_CB(skb)->encap_mark)
goto out;

NAPI_GRO_CB(skb)->encap_mark = 1;

off = skb_gro_offset(skb);
hlen = off + sizeof(*greh);
greh = skb_gro_header_fast(skb, off);
Expand Down
6 changes: 3 additions & 3 deletions net/ipv4/udp_offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,14 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
unsigned int off = skb_gro_offset(skb);
int flush = 1;

if (NAPI_GRO_CB(skb)->udp_mark ||
if (NAPI_GRO_CB(skb)->encap_mark ||
(skb->ip_summed != CHECKSUM_PARTIAL &&
NAPI_GRO_CB(skb)->csum_cnt == 0 &&
!NAPI_GRO_CB(skb)->csum_valid))
goto out;

/* mark that this skb passed once through the udp gro layer */
NAPI_GRO_CB(skb)->udp_mark = 1;
/* mark that this skb passed once through the tunnel gro layer */
NAPI_GRO_CB(skb)->encap_mark = 1;

rcu_read_lock();
uo_priv = rcu_dereference(udp_offload_base);
Expand Down
15 changes: 14 additions & 1 deletion net/ipv6/ip6_offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,19 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
return pp;
}

static struct sk_buff **sit_gro_receive(struct sk_buff **head,
struct sk_buff *skb)
{
if (NAPI_GRO_CB(skb)->encap_mark) {
NAPI_GRO_CB(skb)->flush = 1;
return NULL;
}

NAPI_GRO_CB(skb)->encap_mark = 1;

return ipv6_gro_receive(head, skb);
}

static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
{
const struct net_offload *ops;
Expand Down Expand Up @@ -302,7 +315,7 @@ static struct packet_offload ipv6_packet_offload __read_mostly = {
static const struct net_offload sit_offload = {
.callbacks = {
.gso_segment = ipv6_gso_segment,
.gro_receive = ipv6_gro_receive,
.gro_receive = sit_gro_receive,
.gro_complete = sit_gro_complete,
},
};
Expand Down

0 comments on commit fac8e0f

Please sign in to comment.