Skip to content

Commit

Permalink
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf
Browse files Browse the repository at this point in the history
Florian Westphal says:

====================
netfilter: bug fixes for net

1. Fix IP address check in irc DCC conntrack helper, this should check
   the opposite direction rather than the destination address of the
   packets' direction, from David Leadbeater.

2. bridge netfilter needs to drop dst references, from Harsh Modi.
   This was fine back in the day the code was originally written,
   but nowadays various tunnels can pre-set metadata dsts on packets.

3. Remove nf_conntrack_helper sysctl and the modparam toggle, users
   need to explicitily assign the helpers to use via nftables or
   iptables.  Conntrack helpers, by design, may be used to add dynamic
   port redirections to internal machines, so its necessary to restrict
   which hosts/peers are allowed to use them.
   It was discovered that improper checking in the irc DCC helper makes
   it possible to trigger the 'please do dynamic port forward'
   from outside by embedding a 'DCC' in a PING request; if the client
   echos that back a expectation/port forward gets added.
   The auto-assign-for-everything mechanism has been in "please don't do this"
   territory since 2012.  From Pablo.

4. Fix a memory leak in the netdev hook error unwind path, also from Pablo.

* git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: nf_conntrack_irc: Fix forged IP logic
  netfilter: nf_tables: clean up hook list when offload flags check fails
  netfilter: br_netfilter: Drop dst references before setting.
  netfilter: remove nf_conntrack_helper sysctl and modparam toggles
====================

Link: https://lore.kernel.org/r/20220901071238.3044-1-fw@strlen.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed Sep 3, 2022
2 parents e7506d3 + 0efe125 commit 05a5474
Show file tree
Hide file tree
Showing 12 changed files with 46 additions and 110 deletions.
2 changes: 0 additions & 2 deletions include/net/netfilter/nf_conntrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ struct nf_conntrack_net {
/* only used when new connection is allocated: */
atomic_t count;
unsigned int expect_count;
u8 sysctl_auto_assign_helper;
bool auto_assign_helper_warned;

/* only used from work queues, configuration plane, and so on: */
unsigned int users4;
Expand Down
1 change: 0 additions & 1 deletion include/net/netns/conntrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ struct netns_ct {
u8 sysctl_log_invalid; /* Log invalid packets */
u8 sysctl_events;
u8 sysctl_acct;
u8 sysctl_auto_assign_helper;
u8 sysctl_tstamp;
u8 sysctl_checksum;

Expand Down
2 changes: 2 additions & 0 deletions net/bridge/br_netfilter_hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_
/* - Bridged-and-DNAT'ed traffic doesn't
* require ip_forwarding. */
if (rt->dst.dev == dev) {
skb_dst_drop(skb);
skb_dst_set(skb, &rt->dst);
goto bridged_dnat;
}
Expand Down Expand Up @@ -413,6 +414,7 @@ static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_
kfree_skb(skb);
return 0;
}
skb_dst_drop(skb);
skb_dst_set_noref(skb, &rt->dst);
}

Expand Down
1 change: 1 addition & 0 deletions net/bridge/br_netfilter_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ static int br_nf_pre_routing_finish_ipv6(struct net *net, struct sock *sk, struc
kfree_skb(skb);
return 0;
}
skb_dst_drop(skb);
skb_dst_set_noref(skb, &rt->dst);
}

Expand Down
7 changes: 1 addition & 6 deletions net/netfilter/nf_conntrack_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1782,7 +1782,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
}
spin_unlock_bh(&nf_conntrack_expect_lock);
}
if (!exp)
if (!exp && tmpl)
__nf_ct_try_assign_helper(ct, tmpl, GFP_ATOMIC);

/* Other CPU might have obtained a pointer to this object before it was
Expand Down Expand Up @@ -2068,10 +2068,6 @@ void nf_conntrack_alter_reply(struct nf_conn *ct,
ct->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply;
if (ct->master || (help && !hlist_empty(&help->expectations)))
return;

rcu_read_lock();
__nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC);
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply);

Expand Down Expand Up @@ -2797,7 +2793,6 @@ int nf_conntrack_init_net(struct net *net)
nf_conntrack_acct_pernet_init(net);
nf_conntrack_tstamp_pernet_init(net);
nf_conntrack_ecache_pernet_init(net);
nf_conntrack_helper_pernet_init(net);
nf_conntrack_proto_pernet_init(net);

return 0;
Expand Down
80 changes: 10 additions & 70 deletions net/netfilter/nf_conntrack_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ unsigned int nf_ct_helper_hsize __read_mostly;
EXPORT_SYMBOL_GPL(nf_ct_helper_hsize);
static unsigned int nf_ct_helper_count __read_mostly;

static bool nf_ct_auto_assign_helper __read_mostly = false;
module_param_named(nf_conntrack_helper, nf_ct_auto_assign_helper, bool, 0644);
MODULE_PARM_DESC(nf_conntrack_helper,
"Enable automatic conntrack helper assignment (default 0)");

static DEFINE_MUTEX(nf_ct_nat_helpers_mutex);
static struct list_head nf_ct_nat_helpers __read_mostly;

Expand All @@ -51,24 +46,6 @@ static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
(__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
}

static struct nf_conntrack_helper *
__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
{
struct nf_conntrack_helper *helper;
struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
unsigned int h;

if (!nf_ct_helper_count)
return NULL;

h = helper_hash(tuple);
hlist_for_each_entry_rcu(helper, &nf_ct_helper_hash[h], hnode) {
if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
return helper;
}
return NULL;
}

struct nf_conntrack_helper *
__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
{
Expand Down Expand Up @@ -209,33 +186,11 @@ nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp)
}
EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);

static struct nf_conntrack_helper *
nf_ct_lookup_helper(struct nf_conn *ct, struct net *net)
{
struct nf_conntrack_net *cnet = nf_ct_pernet(net);

if (!cnet->sysctl_auto_assign_helper) {
if (cnet->auto_assign_helper_warned)
return NULL;
if (!__nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple))
return NULL;
pr_info("nf_conntrack: default automatic helper assignment "
"has been turned off for security reasons and CT-based "
"firewall rule not found. Use the iptables CT target "
"to attach helpers instead.\n");
cnet->auto_assign_helper_warned = true;
return NULL;
}

return __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
}

int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
gfp_t flags)
{
struct nf_conntrack_helper *helper = NULL;
struct nf_conn_help *help;
struct net *net = nf_ct_net(ct);

/* We already got a helper explicitly attached. The function
* nf_conntrack_alter_reply - in case NAT is in use - asks for looking
Expand All @@ -246,23 +201,21 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
if (test_bit(IPS_HELPER_BIT, &ct->status))
return 0;

if (tmpl != NULL) {
help = nfct_help(tmpl);
if (help != NULL) {
helper = rcu_dereference(help->helper);
set_bit(IPS_HELPER_BIT, &ct->status);
}
if (WARN_ON_ONCE(!tmpl))
return 0;

help = nfct_help(tmpl);
if (help != NULL) {
helper = rcu_dereference(help->helper);
set_bit(IPS_HELPER_BIT, &ct->status);
}

help = nfct_help(ct);

if (helper == NULL) {
helper = nf_ct_lookup_helper(ct, net);
if (helper == NULL) {
if (help)
RCU_INIT_POINTER(help->helper, NULL);
return 0;
}
if (help)
RCU_INIT_POINTER(help->helper, NULL);
return 0;
}

if (help == NULL) {
Expand Down Expand Up @@ -545,19 +498,6 @@ void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat)
}
EXPORT_SYMBOL_GPL(nf_nat_helper_unregister);

void nf_ct_set_auto_assign_helper_warned(struct net *net)
{
nf_ct_pernet(net)->auto_assign_helper_warned = true;
}
EXPORT_SYMBOL_GPL(nf_ct_set_auto_assign_helper_warned);

void nf_conntrack_helper_pernet_init(struct net *net)
{
struct nf_conntrack_net *cnet = nf_ct_pernet(net);

cnet->sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
}

int nf_conntrack_helper_init(void)
{
nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
Expand Down
5 changes: 3 additions & 2 deletions net/netfilter/nf_conntrack_irc.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,9 @@ static int help(struct sk_buff *skb, unsigned int protoff,

/* dcc_ip can be the internal OR external (NAT'ed) IP */
tuple = &ct->tuplehash[dir].tuple;
if (tuple->src.u3.ip != dcc_ip &&
tuple->dst.u3.ip != dcc_ip) {
if ((tuple->src.u3.ip != dcc_ip &&
ct->tuplehash[!dir].tuple.dst.u3.ip != dcc_ip) ||
dcc_port == 0) {
net_warn_ratelimited("Forged DCC command from %pI4: %pI4:%u\n",
&tuple->src.u3.ip,
&dcc_ip, dcc_port);
Expand Down
5 changes: 0 additions & 5 deletions net/netfilter/nf_conntrack_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -2298,11 +2298,6 @@ ctnetlink_create_conntrack(struct net *net,
ct->status |= IPS_HELPER;
RCU_INIT_POINTER(help->helper, helper);
}
} else {
/* try an implicit helper assignation */
err = __nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC);
if (err < 0)
goto err2;
}

err = ctnetlink_setup_nat(ct, cda);
Expand Down
10 changes: 0 additions & 10 deletions net/netfilter/nf_conntrack_standalone.c
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,6 @@ enum nf_ct_sysctl_index {
NF_SYSCTL_CT_LOG_INVALID,
NF_SYSCTL_CT_EXPECT_MAX,
NF_SYSCTL_CT_ACCT,
NF_SYSCTL_CT_HELPER,
#ifdef CONFIG_NF_CONNTRACK_EVENTS
NF_SYSCTL_CT_EVENTS,
#endif
Expand Down Expand Up @@ -680,14 +679,6 @@ static struct ctl_table nf_ct_sysctl_table[] = {
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE,
},
[NF_SYSCTL_CT_HELPER] = {
.procname = "nf_conntrack_helper",
.maxlen = sizeof(u8),
.mode = 0644,
.proc_handler = proc_dou8vec_minmax,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE,
},
#ifdef CONFIG_NF_CONNTRACK_EVENTS
[NF_SYSCTL_CT_EVENTS] = {
.procname = "nf_conntrack_events",
Expand Down Expand Up @@ -1100,7 +1091,6 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net)
table[NF_SYSCTL_CT_CHECKSUM].data = &net->ct.sysctl_checksum;
table[NF_SYSCTL_CT_LOG_INVALID].data = &net->ct.sysctl_log_invalid;
table[NF_SYSCTL_CT_ACCT].data = &net->ct.sysctl_acct;
table[NF_SYSCTL_CT_HELPER].data = &cnet->sysctl_auto_assign_helper;
#ifdef CONFIG_NF_CONNTRACK_EVENTS
table[NF_SYSCTL_CT_EVENTS].data = &net->ct.sysctl_events;
#endif
Expand Down
4 changes: 3 additions & 1 deletion net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -2166,8 +2166,10 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
chain->flags |= NFT_CHAIN_BASE | flags;
basechain->policy = NF_ACCEPT;
if (chain->flags & NFT_CHAIN_HW_OFFLOAD &&
!nft_chain_offload_support(basechain))
!nft_chain_offload_support(basechain)) {
list_splice_init(&basechain->hook_list, &hook->list);
return -EOPNOTSUPP;
}

flow_block_init(&basechain->flow_block);

Expand Down
3 changes: 0 additions & 3 deletions net/netfilter/nft_ct.c
Original file line number Diff line number Diff line change
Expand Up @@ -1089,9 +1089,6 @@ static int nft_ct_helper_obj_init(const struct nft_ctx *ctx,
if (err < 0)
goto err_put_helper;

/* Avoid the bogus warning, helper will be assigned after CT init */
nf_ct_set_auto_assign_helper_warned(ctx->net);

return 0;

err_put_helper:
Expand Down
36 changes: 26 additions & 10 deletions tools/testing/selftests/netfilter/nft_conntrack_helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -102,26 +102,42 @@ check_for_helper()

ip netns exec ${netns} conntrack -L -f $family -p tcp --dport $port 2> /dev/null |grep -q 'helper=ftp'
if [ $? -ne 0 ] ; then
echo "FAIL: ${netns} did not show attached helper $message" 1>&2
ret=1
if [ $autoassign -eq 0 ] ;then
echo "FAIL: ${netns} did not show attached helper $message" 1>&2
ret=1
else
echo "PASS: ${netns} did not show attached helper $message" 1>&2
fi
else
if [ $autoassign -eq 0 ] ;then
echo "PASS: ${netns} connection on port $port has ftp helper attached" 1>&2
else
echo "FAIL: ${netns} connection on port $port has ftp helper attached" 1>&2
ret=1
fi
fi

echo "PASS: ${netns} connection on port $port has ftp helper attached" 1>&2
return 0
}

test_helper()
{
local port=$1
local msg=$2
local autoassign=$2

if [ $autoassign -eq 0 ] ;then
msg="set via ruleset"
else
msg="auto-assign"
fi

sleep 3 | ip netns exec ${ns2} nc -w 2 -l -p $port > /dev/null &

sleep 1 | ip netns exec ${ns1} nc -w 2 10.0.1.2 $port > /dev/null &
sleep 1

check_for_helper "$ns1" "ip $msg" $port
check_for_helper "$ns2" "ip $msg" $port
check_for_helper "$ns1" "ip $msg" $port $autoassign
check_for_helper "$ns2" "ip $msg" $port $autoassign

wait

Expand Down Expand Up @@ -173,9 +189,9 @@ if [ $? -ne 0 ];then
fi
fi

test_helper 2121 "set via ruleset"
ip netns exec ${ns1} sysctl -q 'net.netfilter.nf_conntrack_helper=1'
ip netns exec ${ns2} sysctl -q 'net.netfilter.nf_conntrack_helper=1'
test_helper 21 "auto-assign"
test_helper 2121 0
ip netns exec ${ns1} sysctl -qe 'net.netfilter.nf_conntrack_helper=1'
ip netns exec ${ns2} sysctl -qe 'net.netfilter.nf_conntrack_helper=1'
test_helper 21 1

exit $ret

0 comments on commit 05a5474

Please sign in to comment.