[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:
parent
4cf411de49
commit
394f545db6
3 changed files with 62 additions and 22 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue