netfilter: nft_fwd_netdev: use recursion counter in neigh egress path
nft_fwd_neigh can be used in egress chains (NF_NETDEV_EGRESS). When the
forwarding rule targets the same device or two devices forward to each
other, neigh_xmit() triggers dev_queue_xmit() which re-enters
nf_hook_egress(), causing infinite recursion and stack overflow.
Move the nf_get_nf_dup_skb_recursion() accessor and NF_RECURSION_LIMIT
to the shared header nf_dup_netdev.h as a static inline, so that
nft_fwd_netdev can use the recursion counter directly without exported
function call overhead. Guard neigh_xmit() with the same recursion
limit already used in nf_do_netdev_egress().
[ Updated to cache the nf_get_nf_dup_skb_recursion pointer. --pablo ]
Fixes: f87b9464d1 ("netfilter: nft_fwd_netdev: Support egress hook")
Reported-by: Xiang Mei <xmei5@asu.edu>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
master
parent
0a0b35f0bf
commit
1d47b55b36
|
|
@ -3,10 +3,23 @@
|
|||
#define _NF_DUP_NETDEV_H_
|
||||
|
||||
#include <net/netfilter/nf_tables.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif);
|
||||
void nf_fwd_netdev_egress(const struct nft_pktinfo *pkt, int oif);
|
||||
|
||||
#define NF_RECURSION_LIMIT 2
|
||||
|
||||
static inline u8 *nf_get_nf_dup_skb_recursion(void)
|
||||
{
|
||||
#ifndef CONFIG_PREEMPT_RT
|
||||
return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion);
|
||||
#else
|
||||
return ¤t->net_xmit.nf_dup_skb_recursion;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct nft_offload_ctx;
|
||||
struct nft_flow_rule;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,22 +13,6 @@
|
|||
#include <net/netfilter/nf_tables_offload.h>
|
||||
#include <net/netfilter/nf_dup_netdev.h>
|
||||
|
||||
#define NF_RECURSION_LIMIT 2
|
||||
|
||||
#ifndef CONFIG_PREEMPT_RT
|
||||
static u8 *nf_get_nf_dup_skb_recursion(void)
|
||||
{
|
||||
return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion);
|
||||
}
|
||||
#else
|
||||
|
||||
static u8 *nf_get_nf_dup_skb_recursion(void)
|
||||
{
|
||||
return ¤t->net_xmit.nf_dup_skb_recursion;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev,
|
||||
enum nf_dev_hooks hook)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
|||
struct nft_regs *regs,
|
||||
const struct nft_pktinfo *pkt)
|
||||
{
|
||||
u8 *nf_dup_skb_recursion = nf_get_nf_dup_skb_recursion();
|
||||
struct nft_fwd_neigh *priv = nft_expr_priv(expr);
|
||||
void *addr = ®s->data[priv->sreg_addr];
|
||||
int oif = regs->data[priv->sreg_dev];
|
||||
|
|
@ -153,6 +154,11 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (*nf_dup_skb_recursion > NF_RECURSION_LIMIT) {
|
||||
verdict = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev = dev_get_by_index_rcu(nft_net(pkt), oif);
|
||||
if (dev == NULL) {
|
||||
verdict = NF_DROP;
|
||||
|
|
@ -170,7 +176,9 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
|||
|
||||
skb->dev = dev;
|
||||
skb_clear_tstamp(skb);
|
||||
(*nf_dup_skb_recursion)++;
|
||||
neigh_xmit(neigh_table, dev, addr, skb);
|
||||
(*nf_dup_skb_recursion)--;
|
||||
out:
|
||||
regs->verdict.code = verdict;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue