[NET]: Added GSO header verification
[linux-2.6.git] / net / core / dev.c
index f1c52cb..4f20149 100644 (file)
@@ -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;