[patch 26/29] xen: fix netfront checksums

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



If we receive a partially checksummed packed, we need to work out how
much of it was checksummed based on its protocol.  The ideal would be
that Xen could tell us how much is checksummed, but in the meantime
we'll make do with this.

[ This is a separate patch for review; will be folded into
  xen-netfront later. ]

From: Herbert Xu <[email protected]>
Signed-off-by: Herbert Xu <[email protected]>
Signed-off-by: Jeremy Fitzhardinge <[email protected]>
Signed-off-by: Chris Wright <[email protected]>
---
 drivers/net/xen-netfront.c |   76 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 68 insertions(+), 8 deletions(-)

===================================================================
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -35,10 +35,12 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/ethtool.h>
-#include <linux/in.h>
 #include <linux/if_ether.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
 #include <linux/moduleparam.h>
 #include <linux/mm.h>
+#include <net/ip.h>
 
 #include <xen/xenbus.h>
 #include <xen/events.h>
@@ -564,8 +566,12 @@ static int xennet_start_xmit(struct sk_b
 	extra = NULL;
 
  	tx->flags = 0;
- 	if (skb->ip_summed == CHECKSUM_PARTIAL) /* local packet? */
+ 	if (skb->ip_summed == CHECKSUM_PARTIAL)
+ 		/* local packet? */
  		tx->flags |= NETTXF_csum_blank | NETTXF_data_validated;
+ 	else if (skb->ip_summed == CHECKSUM_UNNECESSARY)
+ 		/* remote but checksummed. */
+ 		tx->flags |= NETTXF_data_validated;
 
 	if (skb_shinfo(skb)->gso_size) {
 		struct xen_netif_extra_info *gso;
@@ -867,9 +873,50 @@ static RING_IDX xennet_fill_frags(struct
 	return cons;
 }
 
-static void handle_incoming_queue(struct net_device *dev,
+static int skb_checksum_setup(struct sk_buff *skb)
+{
+	struct iphdr *iph;
+	unsigned char *th;
+	int err = -EPROTO;
+
+	if (skb->protocol != htons(ETH_P_IP))
+		goto out;
+
+	iph = (void *)skb->data;
+	th = skb->data + 4 * iph->ihl;
+	if (th >= skb_tail_pointer(skb))
+		goto out;
+
+	skb->csum_start = th - skb->head;
+	switch (iph->protocol) {
+	case IPPROTO_TCP:
+		skb->csum_offset = offsetof(struct tcphdr, check);
+		break;
+	case IPPROTO_UDP:
+		skb->csum_offset = offsetof(struct udphdr, check);
+		break;
+	default:
+		if (net_ratelimit())
+			printk(KERN_ERR "Attempting to checksum a non-"
+			       "TCP/UDP packet, dropping a protocol"
+			       " %d packet", iph->protocol);
+		goto out;
+	}
+
+	if ((th + skb->csum_offset + 2) > skb_tail_pointer(skb))
+		goto out;
+
+	err = 0;
+
+out:
+	return err;
+}
+
+static int handle_incoming_queue(struct net_device *dev,
 				  struct sk_buff_head *rxq)
 {
+	struct netfront_info *np = netdev_priv(dev);
+	int packets_dropped = 0;
 	struct sk_buff *skb;
 
 	while ((skb = __skb_dequeue(rxq)) != NULL) {
@@ -886,10 +933,24 @@ static void handle_incoming_queue(struct
 		/* Ethernet work: Delayed to here as it peeks the header. */
 		skb->protocol = eth_type_trans(skb, dev);
 
+		if (skb->ip_summed == CHECKSUM_PARTIAL) {
+			if (skb_checksum_setup(skb)) {
+				kfree_skb(skb);
+				packets_dropped++;
+				np->stats.rx_errors++;
+				continue;
+			}
+		}
+
+		np->stats.rx_packets++;
+		np->stats.rx_bytes += skb->len;
+
 		/* Pass it up. */
 		netif_receive_skb(skb);
 		dev->last_rx = jiffies;
 	}
+
+	return packets_dropped;
 }
 
 static int xennet_poll(struct net_device *dev, int *pbudget)
@@ -1003,11 +1064,10 @@ err:
 		skb->truesize += skb->data_len - (RX_COPY_THRESHOLD - len);
 		skb->len += skb->data_len;
 
-		if (rx->flags & NETRXF_data_validated)
+		if (rx->flags & NETRXF_csum_blank)
+			skb->ip_summed = CHECKSUM_PARTIAL;
+		else if (rx->flags & NETRXF_data_validated)
 			skb->ip_summed = CHECKSUM_UNNECESSARY;
-
-		np->stats.rx_packets++;
-		np->stats.rx_bytes += skb->len;
 
 		__skb_queue_tail(&rxq, skb);
 
@@ -1029,7 +1089,7 @@ err:
 	while ((skb = __skb_dequeue(&errq)))
 		kfree_skb(skb);
 
-	handle_incoming_queue(dev, &rxq);
+	work_done -= handle_incoming_queue(dev, &rxq);
 
 	/* If we get a callback with very few responses, reduce fill target. */
 	/* NB. Note exponential increase, linear decrease. */

-- 

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux