[NETFILTER]: nf_queue: handle GSO packets
Patrick McHardy [Sat, 5 Aug 2006 07:58:52 +0000 (00:58 -0700)]
Handle GSO packets in nf_queue by segmenting them before queueing to
avoid breaking GSO in case they get mangled.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

net/netfilter/core.c
net/netfilter/nf_internals.h
net/netfilter/nf_queue.c

index 27f639f..d80b935 100644 (file)
@@ -182,7 +182,7 @@ next_hook:
                ret = -EPERM;
        } else if ((verdict & NF_VERDICT_MASK)  == NF_QUEUE) {
                NFDEBUG("nf_hook: Verdict = QUEUE.\n");
-               if (!nf_queue(pskb, elem, pf, hook, indev, outdev, okfn,
+               if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn,
                              verdict >> NF_VERDICT_BITS))
                        goto next_hook;
        }
index 86e392b..a981971 100644 (file)
@@ -23,7 +23,7 @@ extern unsigned int nf_iterate(struct list_head *head,
                                int hook_thresh);
 
 /* nf_queue.c */
-extern int nf_queue(struct sk_buff **skb, 
+extern int nf_queue(struct sk_buff *skb,
                    struct list_head *elem, 
                    int pf, unsigned int hook,
                    struct net_device *indev,
index 662a869..4d8936e 100644 (file)
@@ -74,13 +74,13 @@ EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers);
  * Any packet that leaves via this function must come back 
  * through nf_reinject().
  */
-int nf_queue(struct sk_buff **skb, 
-            struct list_head *elem, 
-            int pf, unsigned int hook,
-            struct net_device *indev,
-            struct net_device *outdev,
-            int (*okfn)(struct sk_buff *),
-            unsigned int queuenum)
+static int __nf_queue(struct sk_buff *skb,
+                     struct list_head *elem,
+                     int pf, unsigned int hook,
+                     struct net_device *indev,
+                     struct net_device *outdev,
+                     int (*okfn)(struct sk_buff *),
+                     unsigned int queuenum)
 {
        int status;
        struct nf_info *info;
@@ -94,14 +94,14 @@ int nf_queue(struct sk_buff **skb,
        read_lock(&queue_handler_lock);
        if (!queue_handler[pf]) {
                read_unlock(&queue_handler_lock);
-               kfree_skb(*skb);
+               kfree_skb(skb);
                return 1;
        }
 
        afinfo = nf_get_afinfo(pf);
        if (!afinfo) {
                read_unlock(&queue_handler_lock);
-               kfree_skb(*skb);
+               kfree_skb(skb);
                return 1;
        }
 
@@ -109,9 +109,9 @@ int nf_queue(struct sk_buff **skb,
        if (!info) {
                if (net_ratelimit())
                        printk(KERN_ERR "OOM queueing packet %p\n",
-                              *skb);
+                              skb);
                read_unlock(&queue_handler_lock);
-               kfree_skb(*skb);
+               kfree_skb(skb);
                return 1;
        }
 
@@ -130,15 +130,15 @@ int nf_queue(struct sk_buff **skb,
        if (outdev) dev_hold(outdev);
 
 #ifdef CONFIG_BRIDGE_NETFILTER
-       if ((*skb)->nf_bridge) {
-               physindev = (*skb)->nf_bridge->physindev;
+       if (skb->nf_bridge) {
+               physindev = skb->nf_bridge->physindev;
                if (physindev) dev_hold(physindev);
-               physoutdev = (*skb)->nf_bridge->physoutdev;
+               physoutdev = skb->nf_bridge->physoutdev;
                if (physoutdev) dev_hold(physoutdev);
        }
 #endif
-       afinfo->saveroute(*skb, info);
-       status = queue_handler[pf]->outfn(*skb, info, queuenum,
+       afinfo->saveroute(skb, info);
+       status = queue_handler[pf]->outfn(skb, info, queuenum,
                                          queue_handler[pf]->data);
 
        read_unlock(&queue_handler_lock);
@@ -153,7 +153,7 @@ int nf_queue(struct sk_buff **skb,
 #endif
                module_put(info->elem->owner);
                kfree(info);
-               kfree_skb(*skb);
+               kfree_skb(skb);
 
                return 1;
        }
@@ -161,6 +161,46 @@ int nf_queue(struct sk_buff **skb,
        return 1;
 }
 
+int nf_queue(struct sk_buff *skb,
+            struct list_head *elem,
+            int pf, unsigned int hook,
+            struct net_device *indev,
+            struct net_device *outdev,
+            int (*okfn)(struct sk_buff *),
+            unsigned int queuenum)
+{
+       struct sk_buff *segs;
+
+       if (!skb_is_gso(skb))
+               return __nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
+                                 queuenum);
+
+       switch (pf) {
+       case AF_INET:
+               skb->protocol = htons(ETH_P_IP);
+               break;
+       case AF_INET6:
+               skb->protocol = htons(ETH_P_IPV6);
+               break;
+       }
+
+       segs = skb_gso_segment(skb, 0);
+       kfree_skb(skb);
+       if (unlikely(IS_ERR(segs)))
+               return 1;
+
+       do {
+               struct sk_buff *nskb = segs->next;
+
+               segs->next = NULL;
+               if (!__nf_queue(segs, elem, pf, hook, indev, outdev, okfn,
+                               queuenum))
+                       kfree_skb(segs);
+               segs = nskb;
+       } while (segs);
+       return 1;
+}
+
 void nf_reinject(struct sk_buff *skb, struct nf_info *info,
                 unsigned int verdict)
 {
@@ -224,9 +264,9 @@ void nf_reinject(struct sk_buff *skb, struct nf_info *info,
        case NF_STOLEN:
                break;
        case NF_QUEUE:
-               if (!nf_queue(&skb, elem, info->pf, info->hook, 
-                             info->indev, info->outdev, info->okfn,
-                             verdict >> NF_VERDICT_BITS))
+               if (!__nf_queue(skb, elem, info->pf, info->hook,
+                               info->indev, info->outdev, info->okfn,
+                               verdict >> NF_VERDICT_BITS))
                        goto next_hook;
                break;
        default: