lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Date:	Tue, 24 Jul 2007 21:56:02 +1000
From:	Rusty Russell <rusty@...tcorp.com.au>
To:	virtualization <virtualization@...ts.linux-foundation.org>
Cc:	Christian Borntraeger <borntraeger@...ibm.com>,
	Herbert Xu <herbert@...dor.apana.org.au>,
	netdev <netdev@...r.kernel.org>
Subject: [PATCH] virtio_net.c gso & feature support

Feedback welcome, as always!

(There's been talk of a virtualization git tree, in which case there'll
be a decent home for these patches soon).

Cheers,
Rusty.
==
Add feature and GSO support to virtio net driver.

If you don't do GSO, you can simply ignore the first sg element of
every outgoing packet, and tack a dummy one on every incoming.

Signed-off-by: Rusty Russell <rusty@...tcorp.com.au>
---
 drivers/net/virtio_net.c   |  130 ++++++++++++++++++++++++++++++++++++++++----
 include/linux/virtio_net.h |   25 +++++++-
 2 files changed, 143 insertions(+), 12 deletions(-)

===================================================================
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -16,12 +16,13 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-//#define DEBUG
+#define DEBUG
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/module.h>
 #include <linux/virtio.h>
 #include <linux/scatterlist.h>
+#include <linux/virtio_net.h>
 
 /* FIXME: Make dynamic */
 #define MAX_PACKET_LEN (ETH_HLEN+ETH_DATA_LEN)
@@ -40,6 +41,18 @@ struct virtnet_info
 	struct sk_buff_head send;
 };
 
+static inline struct virtio_net_hdr *skb_vnet_hdr(struct sk_buff *skb)
+{
+	return (struct virtio_net_hdr *)skb->cb;
+}
+
+static void vnet_hdr_to_sg(struct scatterlist *sg, struct sk_buff *skb)
+{
+	sg->page = virt_to_page(skb_vnet_hdr(skb));
+	sg->offset = offset_in_page(skb_vnet_hdr(skb));
+	sg->length = sizeof(struct virtio_net_hdr);
+}
+
 static bool skb_xmit_done(struct virtqueue *vq)
 {
 	struct virtnet_info *vi = vq->priv;
@@ -52,12 +65,14 @@ static void receive_skb(struct net_devic
 static void receive_skb(struct net_device *dev, struct sk_buff *skb,
 			unsigned len)
 {
-	if (unlikely(len < ETH_HLEN)) {
+	struct virtio_net_hdr *hdr = skb_vnet_hdr(skb);
+
+	if (unlikely(len < sizeof(struct virtio_net_hdr) + ETH_HLEN)) {
 		pr_debug("%s: short packet %i\n", dev->name, len);
 		dev->stats.rx_length_errors++;
-		dev_kfree_skb(skb);
-		return;
-	}
+		goto drop;
+	}
+	len -= sizeof(struct virtio_net_hdr);
 	BUG_ON(len > MAX_PACKET_LEN);
 
 	skb_trim(skb, len);
@@ -66,13 +81,70 @@ static void receive_skb(struct net_devic
 		 ntohs(skb->protocol), skb->len, skb->pkt_type);
 	dev->stats.rx_bytes += skb->len;
 	dev->stats.rx_packets++;
+
+	if (hdr->flags & VIRTIO_NET_F_NEEDS_CSUM) {
+		pr_debug("Needs csum!\n");
+		skb->ip_summed = CHECKSUM_PARTIAL;
+		skb->csum_start = hdr->csum_start;
+		skb->csum_offset = hdr->csum_offset;
+		if (skb->csum_start > skb->len - 2
+		    || skb->csum_offset > skb->len - 2) {
+			if (net_ratelimit())
+				printk(KERN_WARNING "%s: csum=%u/%u len=%u\n",
+				       dev->name, skb->csum_start,
+				       skb->csum_offset, skb->len);
+			goto frame_err;
+		}
+	}
+
+	if (hdr->gso_type != VIRTIO_NET_GSO_NONE) {
+		pr_debug("GSO!\n");
+		switch (hdr->gso_type) {
+		case VIRTIO_NET_GSO_TCP:
+			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+			break;
+		case VIRTIO_NET_GSO_TCP_ECN:
+			skb_shinfo(skb)->gso_type = SKB_GSO_TCP_ECN;
+			break;
+		case VIRTIO_NET_GSO_UDP:
+			skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+			break;
+		case VIRTIO_NET_GSO_TCPV6:
+			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
+			break;
+		default:
+			if (net_ratelimit())
+				printk(KERN_WARNING "%s: bad gso type %u.\n",
+				       dev->name, hdr->gso_type);
+			goto frame_err;
+		}
+
+		skb_shinfo(skb)->gso_size = hdr->gso_size;
+		if (skb_shinfo(skb)->gso_size == 0) {
+			if (net_ratelimit())
+				printk(KERN_WARNING "%s: zero gso size.\n",
+				       dev->name);
+			goto frame_err;
+		}
+
+		/* Header must be checked, and gso_segs computed. */
+		skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
+		skb_shinfo(skb)->gso_segs = 0;
+	}
+
 	netif_receive_skb(skb);
+	return;
+
+frame_err:
+	dev->stats.rx_frame_errors++;
+drop:
+	dev_kfree_skb(skb);
 }
 
 static void try_fill_recv(struct virtnet_info *vi)
 {
 	struct sk_buff *skb;
-	struct scatterlist sg[MAX_SKB_FRAGS];
+	struct scatterlist sg[1+MAX_SKB_FRAGS];
 	int num, err;
 
 	for (;;) {
@@ -81,7 +153,8 @@ static void try_fill_recv(struct virtnet
 			break;
 
 		skb_put(skb, MAX_PACKET_LEN);
-		num = skb_to_sgvec(skb, sg, 0, skb->len);
+		vnet_hdr_to_sg(sg, skb);
+		num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;
 		skb_queue_head(&vi->recv, skb);
 
 		err = vi->vq_recv->ops->add_buf(vi->vq_recv, sg, 0, num, skb);
@@ -161,7 +234,8 @@ static int start_xmit(struct sk_buff *sk
 {
 	struct virtnet_info *vi = netdev_priv(dev);
 	int num, err;
-	struct scatterlist sg[MAX_SKB_FRAGS];
+	struct scatterlist sg[1+MAX_SKB_FRAGS];
+	struct virtio_net_hdr *hdr;
 	const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;
 
 	pr_debug("%s: xmit %p %02x:%02x:%02x:%02x:%02x:%02x\n",
@@ -170,7 +244,41 @@ static int start_xmit(struct sk_buff *sk
 
 	free_old_xmit_skbs(vi);
 
-	num = skb_to_sgvec(skb, sg, 0, skb->len);
+	/* Encode metadata header at front. */
+	hdr = skb_vnet_hdr(skb);
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		hdr->flags = VIRTIO_NET_F_NEEDS_CSUM;
+		hdr->csum_start = skb->csum_start - skb_headroom(skb);
+		hdr->csum_offset = skb->csum_offset;
+	} else {
+		hdr->flags = 0;
+		hdr->csum_offset = hdr->csum_start = 0;
+	}
+
+	if (skb_is_gso(skb)) {
+		printk("xmit: gso size %u len %u (%u/%u/%u)\n",
+		       skb_shinfo(skb)->gso_size, skb->len,
+		       skb_transport_header(skb) - skb->head,
+		       skb_network_header(skb) - skb->head,
+		       skb->data - skb->head);
+		hdr->gso_size = skb_shinfo(skb)->gso_size;
+		if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_ECN)
+			hdr->gso_type = VIRTIO_NET_GSO_TCP_ECN;
+		else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4)
+			hdr->gso_type = VIRTIO_NET_GSO_TCP;
+		else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
+			hdr->gso_type = VIRTIO_NET_GSO_TCPV6;
+		else if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
+			hdr->gso_type = VIRTIO_NET_GSO_UDP;
+		else
+			BUG();
+	} else {
+		hdr->gso_type = VIRTIO_NET_GSO_NONE;
+		hdr->gso_size = 0;
+	}
+
+	vnet_hdr_to_sg(sg, skb);
+	num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;
 	__skb_queue_head(&vi->send, skb);
 	err = vi->vq_send->ops->add_buf(vi->vq_send, sg, num, 0, skb);
 	if (err) {
@@ -220,7 +328,8 @@ struct net_device *virtnet_probe(struct 
 struct net_device *virtnet_probe(struct virtqueue *vq_recv,
 				 struct virtqueue *vq_send,
 				 struct device *device,
-				 const u8 mac[ETH_ALEN])
+				 const u8 mac[ETH_ALEN],
+				 unsigned long features)
 {
 	int err;
 	struct net_device *dev;
@@ -239,6 +348,7 @@ struct net_device *virtnet_probe(struct 
 	dev->poll = virtnet_poll;
 	dev->hard_start_xmit = start_xmit;
 	dev->weight = 16;
+	dev->features = features;
 	SET_NETDEV_DEV(dev, device);
 
 	vi = netdev_priv(dev);
===================================================================
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -1,6 +1,26 @@
 #ifndef _LINUX_VIRTIO_NET_H
 #define _LINUX_VIRTIO_NET_H
 #include <linux/types.h>
+
+/* This is the first element of the scatter-gather list.
+ * If you don't specify GSO or CSUM features, you can simply ignore the
+ * header. */
+struct virtio_net_hdr
+{
+#define VIRTIO_NET_F_NEEDS_CSUM	1	// Use csum_start, csum_offset
+      __u16 flags;
+#define VIRTIO_NET_GSO_NONE	0	// Not a GSO frame
+#define VIRTIO_NET_GSO_TCP	1	// GSO frame, IPv4 TCP (TSO)
+#define VIRTIO_NET_GSO_TCP_ECN	2	// GSO frame, IPv4 TCP w/ ECN
+#define VIRTIO_NET_GSO_UDP	3	// GSO frame, IPv4 UDP (UFO)
+#define VIRTIO_NET_GSO_TCPV6	4	// GSO frame, IPv6 TCP
+      __u16 gso_type;
+      __u16 gso_size;
+      __u16 csum_start;
+      __u16 csum_offset;
+};
+
+#ifdef __KERNEL__
 #include <linux/etherdevice.h>
 struct device;
 struct net_device;
@@ -9,7 +29,8 @@ struct net_device *virtnet_probe(struct 
 struct net_device *virtnet_probe(struct virtqueue *vq_recv,
 				 struct virtqueue *vq_send,
 				 struct device *dev,
-				 const u8 mac[ETH_ALEN]);
+				 const u8 mac[ETH_ALEN],
+				 unsigned long features);
 void virtnet_remove(struct net_device *dev);
-
+#endif /* __KERNEL__ */
 #endif /* _LINUX_VIRTIO_NET_H */


-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists