From mboxrd@z Thu Jan 1 00:00:00 1970 From: Paul Durrant Subject: [PATCH net v2] xen-netback: fix fragment detection in checksum setup Date: Thu, 28 Nov 2013 13:23:52 +0000 Message-ID: <1385645032-29848-1-git-send-email-paul.durrant@citrix.com> Mime-Version: 1.0 Content-Type: text/plain Cc: Paul Durrant , Wei Liu , Ian Campbell , David Vrabel To: , Return-path: Received: from smtp.citrix.com ([66.165.176.89]:5958 "EHLO SMTP.CITRIX.COM" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751148Ab3K1NXz (ORCPT ); Thu, 28 Nov 2013 08:23:55 -0500 Sender: netdev-owner@vger.kernel.org List-ID: The code to detect fragments in checksum_setup() was missing for IPv4 and too eager for IPv6. (It transpires that Windows seems to send IPv6 packets with a fragment header even if they are not a fragment - i.e. offset is zero, and M bit is not set). Signed-off-by: Paul Durrant Cc: Wei Liu Cc: Ian Campbell Cc: David Vrabel --- v2 - Added comments noting what fragment/offset masks mean drivers/net/xen-netback/netback.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 919b650..c7464d8 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -1165,15 +1165,28 @@ static int checksum_setup_ip(struct xenvif *vif, struct sk_buff *skb, struct iphdr *iph = (void *)skb->data; unsigned int header_size; unsigned int off; + bool fragment; int err = -EPROTO; + fragment = false; + off = sizeof(struct iphdr); header_size = skb->network_header + off + MAX_IPOPTLEN; maybe_pull_tail(skb, header_size); + /* 3fff -> fragment offset != 0 OR more fragments */ + if (ntohs(iph->frag_off) & 0x3fff) + fragment = true; + off = iph->ihl * 4; + if (fragment) { + if (net_ratelimit()) + netdev_err(vif->dev, "Packet is a fragment!\n"); + goto out; + } + switch (iph->protocol) { case IPPROTO_TCP: if (!skb_partial_csum_set(skb, off, @@ -1237,6 +1250,7 @@ static int checksum_setup_ipv6(struct xenvif *vif, struct sk_buff *skb, bool fragment; bool done; + fragment = false; done = false; off = sizeof(struct ipv6hdr); @@ -1275,9 +1289,22 @@ static int checksum_setup_ipv6(struct xenvif *vif, struct sk_buff *skb, off += (hp->hdrlen+2)<<2; break; } - case IPPROTO_FRAGMENT: - fragment = true; - /* fall through */ + case IPPROTO_FRAGMENT: { + struct frag_hdr *hp = (void *)(skb->data + off); + + header_size = skb->network_header + + off + + sizeof(struct frag_hdr); + maybe_pull_tail(skb, header_size); + + /* fff9 -> fragment offset != 0 OR more fragments */ + if (ntohs(hp->frag_off) & 0xfff9) + fragment = true; + + nexthdr = hp->nexthdr; + off += sizeof(struct frag_hdr); + break; + } default: done = true; break; -- 1.7.10.4