aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/vxlan.c
diff options
context:
space:
mode:
authorTom Herbert <therbert@google.com>2015-02-10 16:30:27 -0800
committerDavid S. Miller <davem@davemloft.net>2015-02-11 15:12:09 -0800
commit26c4f7da3e413da697a7beb22ad496390eda7da0 (patch)
tree55182c7a68fc4129369ffce2ac30a897cc3d55a1 /drivers/net/vxlan.c
parentopenvswitch: Add missing initialization in validate_and_copy_set_tun() (diff)
downloadlinux-dev-26c4f7da3e413da697a7beb22ad496390eda7da0.tar.xz
linux-dev-26c4f7da3e413da697a7beb22ad496390eda7da0.zip
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: Tom Herbert <therbert@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/vxlan.c')
-rw-r--r--drivers/net/vxlan.c19
1 files changed, 9 insertions, 10 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 0e57e862c399..30310a63475a 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -555,12 +555,12 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb,
unsigned int off,
struct vxlanhdr *vh, size_t hdrlen,
- u32 data)
+ u32 data, struct gro_remcsum *grc)
{
size_t start, offset, plen;
if (skb->remcsum_offload)
- return vh;
+ return NULL;
if (!NAPI_GRO_CB(skb)->csum_valid)
return NULL;
@@ -579,7 +579,8 @@ static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb,
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;
@@ -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,
udp_offloads);
u32 flags;
+ struct gro_remcsum grc;
+
+ skb_gro_remcsum_init(&grc);
off_vx = skb_gro_offset(skb);
hlen = off_vx + sizeof(*vh);
@@ -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)) {
vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr),
- ntohl(vh->vx_vni));
+ ntohl(vh->vx_vni), &grc);
if (!vh)
goto out;
@@ -637,6 +641,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head,
pp = eth_gro_receive(head, skb);
out:
+ skb_gro_remcsum_cleanup(skb, &grc);
NAPI_GRO_CB(skb)->flush |= flush;
return pp;
@@ -1154,12 +1159,6 @@ static struct vxlanhdr *vxlan_remcsum(struct sk_buff *skb, struct vxlanhdr *vh,
{
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;
offset = start + ((data & VXLAN_RCO_UDP) ?
offsetof(struct udphdr, check) :