[NETFILTER]: nf_queue: handle GSO packets

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>
This commit is contained in:
Patrick McHardy 2006-08-05 00:58:52 -07:00 committed by David S. Miller
parent 4cf411de49
commit 394f545db6
3 changed files with 62 additions and 22 deletions

View file

@ -182,7 +182,7 @@ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb,
ret = -EPERM; ret = -EPERM;
} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
NFDEBUG("nf_hook: Verdict = QUEUE.\n"); 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)) verdict >> NF_VERDICT_BITS))
goto next_hook; goto next_hook;
} }

View file

@ -23,7 +23,7 @@ extern unsigned int nf_iterate(struct list_head *head,
int hook_thresh); int hook_thresh);
/* nf_queue.c */ /* nf_queue.c */
extern int nf_queue(struct sk_buff **skb, extern int nf_queue(struct sk_buff *skb,
struct list_head *elem, struct list_head *elem,
int pf, unsigned int hook, int pf, unsigned int hook,
struct net_device *indev, struct net_device *indev,

View file

@ -74,13 +74,13 @@ EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers);
* Any packet that leaves via this function must come back * Any packet that leaves via this function must come back
* through nf_reinject(). * through nf_reinject().
*/ */
int nf_queue(struct sk_buff **skb, static int __nf_queue(struct sk_buff *skb,
struct list_head *elem, struct list_head *elem,
int pf, unsigned int hook, int pf, unsigned int hook,
struct net_device *indev, struct net_device *indev,
struct net_device *outdev, struct net_device *outdev,
int (*okfn)(struct sk_buff *), int (*okfn)(struct sk_buff *),
unsigned int queuenum) unsigned int queuenum)
{ {
int status; int status;
struct nf_info *info; struct nf_info *info;
@ -94,14 +94,14 @@ int nf_queue(struct sk_buff **skb,
read_lock(&queue_handler_lock); read_lock(&queue_handler_lock);
if (!queue_handler[pf]) { if (!queue_handler[pf]) {
read_unlock(&queue_handler_lock); read_unlock(&queue_handler_lock);
kfree_skb(*skb); kfree_skb(skb);
return 1; return 1;
} }
afinfo = nf_get_afinfo(pf); afinfo = nf_get_afinfo(pf);
if (!afinfo) { if (!afinfo) {
read_unlock(&queue_handler_lock); read_unlock(&queue_handler_lock);
kfree_skb(*skb); kfree_skb(skb);
return 1; return 1;
} }
@ -109,9 +109,9 @@ int nf_queue(struct sk_buff **skb,
if (!info) { if (!info) {
if (net_ratelimit()) if (net_ratelimit())
printk(KERN_ERR "OOM queueing packet %p\n", printk(KERN_ERR "OOM queueing packet %p\n",
*skb); skb);
read_unlock(&queue_handler_lock); read_unlock(&queue_handler_lock);
kfree_skb(*skb); kfree_skb(skb);
return 1; return 1;
} }
@ -130,15 +130,15 @@ int nf_queue(struct sk_buff **skb,
if (outdev) dev_hold(outdev); if (outdev) dev_hold(outdev);
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER
if ((*skb)->nf_bridge) { if (skb->nf_bridge) {
physindev = (*skb)->nf_bridge->physindev; physindev = skb->nf_bridge->physindev;
if (physindev) dev_hold(physindev); if (physindev) dev_hold(physindev);
physoutdev = (*skb)->nf_bridge->physoutdev; physoutdev = skb->nf_bridge->physoutdev;
if (physoutdev) dev_hold(physoutdev); if (physoutdev) dev_hold(physoutdev);
} }
#endif #endif
afinfo->saveroute(*skb, info); afinfo->saveroute(skb, info);
status = queue_handler[pf]->outfn(*skb, info, queuenum, status = queue_handler[pf]->outfn(skb, info, queuenum,
queue_handler[pf]->data); queue_handler[pf]->data);
read_unlock(&queue_handler_lock); read_unlock(&queue_handler_lock);
@ -153,7 +153,7 @@ int nf_queue(struct sk_buff **skb,
#endif #endif
module_put(info->elem->owner); module_put(info->elem->owner);
kfree(info); kfree(info);
kfree_skb(*skb); kfree_skb(skb);
return 1; return 1;
} }
@ -161,6 +161,46 @@ int nf_queue(struct sk_buff **skb,
return 1; 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, void nf_reinject(struct sk_buff *skb, struct nf_info *info,
unsigned int verdict) unsigned int verdict)
{ {
@ -224,9 +264,9 @@ void nf_reinject(struct sk_buff *skb, struct nf_info *info,
case NF_STOLEN: case NF_STOLEN:
break; break;
case NF_QUEUE: case NF_QUEUE:
if (!nf_queue(&skb, elem, info->pf, info->hook, if (!__nf_queue(skb, elem, info->pf, info->hook,
info->indev, info->outdev, info->okfn, info->indev, info->outdev, info->okfn,
verdict >> NF_VERDICT_BITS)) verdict >> NF_VERDICT_BITS))
goto next_hook; goto next_hook;
break; break;
default: default: