Commit 26c4f7da authored by Tom Herbert's avatar Tom Herbert Committed by David S. Miller

net: Fix remcsum in GRO path to not change packet

Remote checksum offload processing is currently the same for both
the GRO and non-GRO path. When the remote checksum offload option
is encountered, the checksum field referred to is modified in
the packet. So in the GRO case, the packet is modified in the
GRO path and then the operation is skipped when the packet goes
through the normal path based on skb->remcsum_offload. There is
a problem in that the packet may be modified in the GRO path, but
then forwarded off host still containing the remote checksum option.
A remote host will again perform RCO but now the checksum verification
will fail since GRO RCO already modified the checksum.

To fix this, we ensure that GRO restores a packet to it's original
state before returning. In this model, when GRO processes a remote
checksum option it still changes the checksum per the algorithm
but on return from lower layer processing the checksum is restored
to its original value.

In this patch we add define gro_remcsum structure which is passed
to skb_gro_remcsum_process to save offset and delta for the checksum
being changed. After lower layer processing, skb_gro_remcsum_cleanup
is called to restore the checksum before returning from GRO.
Signed-off-by: default avatarTom Herbert <therbert@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 13101602
...@@ -555,12 +555,12 @@ static int vxlan_fdb_append(struct vxlan_fdb *f, ...@@ -555,12 +555,12 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb,
unsigned int off, unsigned int off,
struct vxlanhdr *vh, size_t hdrlen, struct vxlanhdr *vh, size_t hdrlen,
u32 data) u32 data, struct gro_remcsum *grc)
{ {
size_t start, offset, plen; size_t start, offset, plen;
if (skb->remcsum_offload) if (skb->remcsum_offload)
return vh; return NULL;
if (!NAPI_GRO_CB(skb)->csum_valid) if (!NAPI_GRO_CB(skb)->csum_valid)
return NULL; return NULL;
...@@ -579,7 +579,8 @@ static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, ...@@ -579,7 +579,8 @@ static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb,
return NULL; return NULL;
} }
skb_gro_remcsum_process(skb, (void *)vh + hdrlen, start, offset); skb_gro_remcsum_process(skb, (void *)vh + hdrlen,
start, offset, grc);
skb->remcsum_offload = 1; skb->remcsum_offload = 1;
...@@ -597,6 +598,9 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, ...@@ -597,6 +598,9 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head,
struct vxlan_sock *vs = container_of(uoff, struct vxlan_sock, struct vxlan_sock *vs = container_of(uoff, struct vxlan_sock,
udp_offloads); udp_offloads);
u32 flags; u32 flags;
struct gro_remcsum grc;
skb_gro_remcsum_init(&grc);
off_vx = skb_gro_offset(skb); off_vx = skb_gro_offset(skb);
hlen = off_vx + sizeof(*vh); hlen = off_vx + sizeof(*vh);
...@@ -614,7 +618,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, ...@@ -614,7 +618,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head,
if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) { if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) {
vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr), vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr),
ntohl(vh->vx_vni)); ntohl(vh->vx_vni), &grc);
if (!vh) if (!vh)
goto out; goto out;
...@@ -637,6 +641,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, ...@@ -637,6 +641,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head,
pp = eth_gro_receive(head, skb); pp = eth_gro_receive(head, skb);
out: out:
skb_gro_remcsum_cleanup(skb, &grc);
NAPI_GRO_CB(skb)->flush |= flush; NAPI_GRO_CB(skb)->flush |= flush;
return pp; return pp;
...@@ -1154,12 +1159,6 @@ static struct vxlanhdr *vxlan_remcsum(struct sk_buff *skb, struct vxlanhdr *vh, ...@@ -1154,12 +1159,6 @@ static struct vxlanhdr *vxlan_remcsum(struct sk_buff *skb, struct vxlanhdr *vh,
{ {
size_t start, offset, plen; size_t start, offset, plen;
if (skb->remcsum_offload) {
/* Already processed in GRO path */
skb->remcsum_offload = 0;
return vh;
}
start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT; start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT;
offset = start + ((data & VXLAN_RCO_UDP) ? offset = start + ((data & VXLAN_RCO_UDP) ?
offsetof(struct udphdr, check) : offsetof(struct udphdr, check) :
......
...@@ -2321,8 +2321,19 @@ do { \ ...@@ -2321,8 +2321,19 @@ do { \
compute_pseudo(skb, proto)); \ compute_pseudo(skb, proto)); \
} while (0) } while (0)
struct gro_remcsum {
int offset;
__wsum delta;
};
static inline void skb_gro_remcsum_init(struct gro_remcsum *grc)
{
grc->delta = 0;
}
static inline void skb_gro_remcsum_process(struct sk_buff *skb, void *ptr, static inline void skb_gro_remcsum_process(struct sk_buff *skb, void *ptr,
int start, int offset) int start, int offset,
struct gro_remcsum *grc)
{ {
__wsum delta; __wsum delta;
...@@ -2331,10 +2342,20 @@ static inline void skb_gro_remcsum_process(struct sk_buff *skb, void *ptr, ...@@ -2331,10 +2342,20 @@ static inline void skb_gro_remcsum_process(struct sk_buff *skb, void *ptr,
delta = remcsum_adjust(ptr, NAPI_GRO_CB(skb)->csum, start, offset); delta = remcsum_adjust(ptr, NAPI_GRO_CB(skb)->csum, start, offset);
/* Adjust skb->csum since we changed the packet */ /* Adjust skb->csum since we changed the packet */
skb->csum = csum_add(skb->csum, delta);
NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta); NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta);
grc->offset = (ptr + offset) - (void *)skb->head;
grc->delta = delta;
} }
static inline void skb_gro_remcsum_cleanup(struct sk_buff *skb,
struct gro_remcsum *grc)
{
if (!grc->delta)
return;
remcsum_unadjust((__sum16 *)(skb->head + grc->offset), grc->delta);
}
static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev, static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, unsigned short type,
......
...@@ -167,4 +167,9 @@ static inline __wsum remcsum_adjust(void *ptr, __wsum csum, ...@@ -167,4 +167,9 @@ static inline __wsum remcsum_adjust(void *ptr, __wsum csum,
return delta; return delta;
} }
static inline void remcsum_unadjust(__sum16 *psum, __wsum delta)
{
*psum = csum_fold(csum_sub(delta, *psum));
}
#endif #endif
...@@ -71,12 +71,6 @@ static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr, ...@@ -71,12 +71,6 @@ static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
size_t offset = ntohs(pd[1]); size_t offset = ntohs(pd[1]);
size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start); size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
if (skb->remcsum_offload) {
/* Already processed in GRO path */
skb->remcsum_offload = 0;
return guehdr;
}
if (!pskb_may_pull(skb, plen)) if (!pskb_may_pull(skb, plen))
return NULL; return NULL;
guehdr = (struct guehdr *)&udp_hdr(skb)[1]; guehdr = (struct guehdr *)&udp_hdr(skb)[1];
...@@ -214,7 +208,8 @@ static int fou_gro_complete(struct sk_buff *skb, int nhoff, ...@@ -214,7 +208,8 @@ static int fou_gro_complete(struct sk_buff *skb, int nhoff,
static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
struct guehdr *guehdr, void *data, struct guehdr *guehdr, void *data,
size_t hdrlen, u8 ipproto) size_t hdrlen, u8 ipproto,
struct gro_remcsum *grc)
{ {
__be16 *pd = data; __be16 *pd = data;
size_t start = ntohs(pd[0]); size_t start = ntohs(pd[0]);
...@@ -222,7 +217,7 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, ...@@ -222,7 +217,7 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start); size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
if (skb->remcsum_offload) if (skb->remcsum_offload)
return guehdr; return NULL;
if (!NAPI_GRO_CB(skb)->csum_valid) if (!NAPI_GRO_CB(skb)->csum_valid)
return NULL; return NULL;
...@@ -234,7 +229,8 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, ...@@ -234,7 +229,8 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
return NULL; return NULL;
} }
skb_gro_remcsum_process(skb, (void *)guehdr + hdrlen, start, offset); skb_gro_remcsum_process(skb, (void *)guehdr + hdrlen,
start, offset, grc);
skb->remcsum_offload = 1; skb->remcsum_offload = 1;
...@@ -254,6 +250,9 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, ...@@ -254,6 +250,9 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
void *data; void *data;
u16 doffset = 0; u16 doffset = 0;
int flush = 1; int flush = 1;
struct gro_remcsum grc;
skb_gro_remcsum_init(&grc);
off = skb_gro_offset(skb); off = skb_gro_offset(skb);
len = off + sizeof(*guehdr); len = off + sizeof(*guehdr);
...@@ -295,7 +294,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, ...@@ -295,7 +294,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
if (flags & GUE_PFLAG_REMCSUM) { if (flags & GUE_PFLAG_REMCSUM) {
guehdr = gue_gro_remcsum(skb, off, guehdr, guehdr = gue_gro_remcsum(skb, off, guehdr,
data + doffset, hdrlen, data + doffset, hdrlen,
guehdr->proto_ctype); guehdr->proto_ctype, &grc);
if (!guehdr) if (!guehdr)
goto out; goto out;
...@@ -345,6 +344,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, ...@@ -345,6 +344,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
rcu_read_unlock(); rcu_read_unlock();
out: out:
NAPI_GRO_CB(skb)->flush |= flush; NAPI_GRO_CB(skb)->flush |= flush;
skb_gro_remcsum_cleanup(skb, &grc);
return pp; return pp;
} }
......
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