]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/core/dev.c
[NET]: Added GSO header verification
[net-next-2.6.git] / net / core / dev.c
index ea2469398bd5158d8f5caee2a2adf1f0d2402ba8..4f2014994a847ce7066a7c8deee9d12befb9e354 100644 (file)
@@ -230,7 +230,7 @@ extern void netdev_unregister_sysfs(struct net_device *);
  *     For efficiency
  */
 
-int netdev_nit;
+static int netdev_nit;
 
 /*
  *     Add a protocol ID to the list. Now that the input handler is
@@ -1190,11 +1190,14 @@ out:
 /**
  *     skb_gso_segment - Perform segmentation on skb.
  *     @skb: buffer to segment
- *     @sg: whether scatter-gather is supported on the target.
+ *     @features: features for the output path (see dev->features)
  *
  *     This function segments the given skb and returns a list of segments.
+ *
+ *     It may return NULL if the skb requires no segmentation.  This is
+ *     only possible when GSO is used for verifying header integrity.
  */
-struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg)
+struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features)
 {
        struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
        struct packet_type *ptype;
@@ -1210,12 +1213,14 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg)
        rcu_read_lock();
        list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) {
                if (ptype->type == type && !ptype->dev && ptype->gso_segment) {
-                       segs = ptype->gso_segment(skb, sg);
+                       segs = ptype->gso_segment(skb, features);
                        break;
                }
        }
        rcu_read_unlock();
 
+       __skb_push(skb, skb->data - skb->mac.raw);
+
        return segs;
 }
 
@@ -1291,9 +1296,15 @@ static int dev_gso_segment(struct sk_buff *skb)
 {
        struct net_device *dev = skb->dev;
        struct sk_buff *segs;
+       int features = dev->features & ~(illegal_highdma(dev, skb) ?
+                                        NETIF_F_SG : 0);
+
+       segs = skb_gso_segment(skb, features);
+
+       /* Verifying header integrity only. */
+       if (!segs)
+               return 0;
 
-       segs = skb_gso_segment(skb, dev->features & NETIF_F_SG &&
-                                   !illegal_highdma(dev, skb));
        if (unlikely(IS_ERR(segs)))
                return PTR_ERR(segs);
 
@@ -1310,13 +1321,17 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                if (netdev_nit)
                        dev_queue_xmit_nit(skb, dev);
 
-               if (!netif_needs_gso(dev, skb))
-                       return dev->hard_start_xmit(skb, dev);
+               if (netif_needs_gso(dev, skb)) {
+                       if (unlikely(dev_gso_segment(skb)))
+                               goto out_kfree_skb;
+                       if (skb->next)
+                               goto gso;
+               }
 
-               if (unlikely(dev_gso_segment(skb)))
-                       goto out_kfree_skb;
+               return dev->hard_start_xmit(skb, dev);
        }
 
+gso:
        do {
                struct sk_buff *nskb = skb->next;
                int rc;
@@ -1325,9 +1340,12 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                nskb->next = NULL;
                rc = dev->hard_start_xmit(nskb, dev);
                if (unlikely(rc)) {
+                       nskb->next = skb->next;
                        skb->next = nskb;
                        return rc;
                }
+               if (unlikely(netif_queue_stopped(dev) && skb->next))
+                       return NETDEV_TX_BUSY;
        } while (skb->next);
        
        skb->destructor = DEV_GSO_CB(skb)->destructor;