Commit 9270bbe2 authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for net:

1) Fix incorrect enum type definition in nfnetlink_cthelper UAPI,
   from Dmitry V. Levin.

2) Remove extra space in deprecated automatic helper assignment
   notice, from Klemen Košir.

3) Drop early socket demux socket after NAT mangling, from
   Florian Westphal. Add a test to exercise this bug.

4) Fix bogus invalid packet report in the conntrack TCP tracker,
   also from Florian.

5) Fix access to xt[NFPROTO_UNSPEC] list with no mutex
   in target/match_revfn(), from Vasily Averin.

6) Disallow updates on the table ownership flag.

7) Fix double hook unregistration of tables with owner.

8) Remove bogus check on the table owner in __nft_release_tables().
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a4dcfbc4 bd1777b3
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
#define NFCT_HELPER_STATUS_DISABLED 0 #define NFCT_HELPER_STATUS_DISABLED 0
#define NFCT_HELPER_STATUS_ENABLED 1 #define NFCT_HELPER_STATUS_ENABLED 1
enum nfnl_acct_msg_types { enum nfnl_cthelper_msg_types {
NFNL_MSG_CTHELPER_NEW, NFNL_MSG_CTHELPER_NEW,
NFNL_MSG_CTHELPER_GET, NFNL_MSG_CTHELPER_GET,
NFNL_MSG_CTHELPER_DEL, NFNL_MSG_CTHELPER_DEL,
......
...@@ -219,7 +219,7 @@ nf_ct_lookup_helper(struct nf_conn *ct, struct net *net) ...@@ -219,7 +219,7 @@ nf_ct_lookup_helper(struct nf_conn *ct, struct net *net)
return NULL; return NULL;
pr_info("nf_conntrack: default automatic helper assignment " pr_info("nf_conntrack: default automatic helper assignment "
"has been turned off for security reasons and CT-based " "has been turned off for security reasons and CT-based "
" firewall rule not found. Use the iptables CT target " "firewall rule not found. Use the iptables CT target "
"to attach helpers instead.\n"); "to attach helpers instead.\n");
net->ct.auto_assign_helper_warned = 1; net->ct.auto_assign_helper_warned = 1;
return NULL; return NULL;
...@@ -228,7 +228,6 @@ nf_ct_lookup_helper(struct nf_conn *ct, struct net *net) ...@@ -228,7 +228,6 @@ nf_ct_lookup_helper(struct nf_conn *ct, struct net *net)
return __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); 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, int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
gfp_t flags) gfp_t flags)
{ {
......
...@@ -982,8 +982,10 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, ...@@ -982,8 +982,10 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
IP_CT_EXP_CHALLENGE_ACK; IP_CT_EXP_CHALLENGE_ACK;
} }
spin_unlock_bh(&ct->lock); spin_unlock_bh(&ct->lock);
nf_ct_l4proto_log_invalid(skb, ct, "invalid packet ignored in " nf_ct_l4proto_log_invalid(skb, ct,
"state %s ", tcp_conntrack_names[old_state]); "packet (index %d) in dir %d ignored, state %s",
index, dir,
tcp_conntrack_names[old_state]);
return NF_ACCEPT; return NF_ACCEPT;
case TCP_CONNTRACK_MAX: case TCP_CONNTRACK_MAX:
/* Special case for SYN proxy: when the SYN to the server or /* Special case for SYN proxy: when the SYN to the server or
......
...@@ -646,8 +646,8 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb, ...@@ -646,8 +646,8 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
} }
static unsigned int static unsigned int
nf_nat_ipv4_in(void *priv, struct sk_buff *skb, nf_nat_ipv4_pre_routing(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state) const struct nf_hook_state *state)
{ {
unsigned int ret; unsigned int ret;
__be32 daddr = ip_hdr(skb)->daddr; __be32 daddr = ip_hdr(skb)->daddr;
...@@ -659,6 +659,23 @@ nf_nat_ipv4_in(void *priv, struct sk_buff *skb, ...@@ -659,6 +659,23 @@ nf_nat_ipv4_in(void *priv, struct sk_buff *skb,
return ret; return ret;
} }
static unsigned int
nf_nat_ipv4_local_in(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
__be32 saddr = ip_hdr(skb)->saddr;
struct sock *sk = skb->sk;
unsigned int ret;
ret = nf_nat_ipv4_fn(priv, skb, state);
if (ret == NF_ACCEPT && sk && saddr != ip_hdr(skb)->saddr &&
!inet_sk_transparent(sk))
skb_orphan(skb); /* TCP edemux obtained wrong socket */
return ret;
}
static unsigned int static unsigned int
nf_nat_ipv4_out(void *priv, struct sk_buff *skb, nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state) const struct nf_hook_state *state)
...@@ -736,7 +753,7 @@ nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb, ...@@ -736,7 +753,7 @@ nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb,
static const struct nf_hook_ops nf_nat_ipv4_ops[] = { static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
/* Before packet filtering, change destination */ /* Before packet filtering, change destination */
{ {
.hook = nf_nat_ipv4_in, .hook = nf_nat_ipv4_pre_routing,
.pf = NFPROTO_IPV4, .pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING, .hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_NAT_DST, .priority = NF_IP_PRI_NAT_DST,
...@@ -757,7 +774,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = { ...@@ -757,7 +774,7 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
}, },
/* After packet filtering, change source */ /* After packet filtering, change source */
{ {
.hook = nf_nat_ipv4_fn, .hook = nf_nat_ipv4_local_in,
.pf = NFPROTO_IPV4, .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN, .hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_NAT_SRC, .priority = NF_IP_PRI_NAT_SRC,
......
...@@ -916,6 +916,12 @@ static int nf_tables_updtable(struct nft_ctx *ctx) ...@@ -916,6 +916,12 @@ static int nf_tables_updtable(struct nft_ctx *ctx)
if (flags == ctx->table->flags) if (flags == ctx->table->flags)
return 0; return 0;
if ((nft_table_has_owner(ctx->table) &&
!(flags & NFT_TABLE_F_OWNER)) ||
(!nft_table_has_owner(ctx->table) &&
flags & NFT_TABLE_F_OWNER))
return -EOPNOTSUPP;
trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE, trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
sizeof(struct nft_trans_table)); sizeof(struct nft_trans_table));
if (trans == NULL) if (trans == NULL)
...@@ -9022,8 +9028,12 @@ static void __nft_release_hooks(struct net *net) ...@@ -9022,8 +9028,12 @@ static void __nft_release_hooks(struct net *net)
{ {
struct nft_table *table; struct nft_table *table;
list_for_each_entry(table, &net->nft.tables, list) list_for_each_entry(table, &net->nft.tables, list) {
if (nft_table_has_owner(table))
continue;
__nft_release_hook(net, table); __nft_release_hook(net, table);
}
} }
static void __nft_release_table(struct net *net, struct nft_table *table) static void __nft_release_table(struct net *net, struct nft_table *table)
...@@ -9073,13 +9083,12 @@ static void __nft_release_table(struct net *net, struct nft_table *table) ...@@ -9073,13 +9083,12 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
nf_tables_table_destroy(&ctx); nf_tables_table_destroy(&ctx);
} }
static void __nft_release_tables(struct net *net, u32 nlpid) static void __nft_release_tables(struct net *net)
{ {
struct nft_table *table, *nt; struct nft_table *table, *nt;
list_for_each_entry_safe(table, nt, &net->nft.tables, list) { list_for_each_entry_safe(table, nt, &net->nft.tables, list) {
if (nft_table_has_owner(table) && if (nft_table_has_owner(table))
nlpid != table->nlpid)
continue; continue;
__nft_release_table(net, table); __nft_release_table(net, table);
...@@ -9145,7 +9154,7 @@ static void __net_exit nf_tables_exit_net(struct net *net) ...@@ -9145,7 +9154,7 @@ static void __net_exit nf_tables_exit_net(struct net *net)
mutex_lock(&net->nft.commit_mutex); mutex_lock(&net->nft.commit_mutex);
if (!list_empty(&net->nft.commit_list)) if (!list_empty(&net->nft.commit_list))
__nf_tables_abort(net, NFNL_ABORT_NONE); __nf_tables_abort(net, NFNL_ABORT_NONE);
__nft_release_tables(net, 0); __nft_release_tables(net);
mutex_unlock(&net->nft.commit_mutex); mutex_unlock(&net->nft.commit_mutex);
WARN_ON_ONCE(!list_empty(&net->nft.tables)); WARN_ON_ONCE(!list_empty(&net->nft.tables));
WARN_ON_ONCE(!list_empty(&net->nft.module_list)); WARN_ON_ONCE(!list_empty(&net->nft.module_list));
......
...@@ -330,6 +330,7 @@ static int match_revfn(u8 af, const char *name, u8 revision, int *bestp) ...@@ -330,6 +330,7 @@ static int match_revfn(u8 af, const char *name, u8 revision, int *bestp)
const struct xt_match *m; const struct xt_match *m;
int have_rev = 0; int have_rev = 0;
mutex_lock(&xt[af].mutex);
list_for_each_entry(m, &xt[af].match, list) { list_for_each_entry(m, &xt[af].match, list) {
if (strcmp(m->name, name) == 0) { if (strcmp(m->name, name) == 0) {
if (m->revision > *bestp) if (m->revision > *bestp)
...@@ -338,6 +339,7 @@ static int match_revfn(u8 af, const char *name, u8 revision, int *bestp) ...@@ -338,6 +339,7 @@ static int match_revfn(u8 af, const char *name, u8 revision, int *bestp)
have_rev = 1; have_rev = 1;
} }
} }
mutex_unlock(&xt[af].mutex);
if (af != NFPROTO_UNSPEC && !have_rev) if (af != NFPROTO_UNSPEC && !have_rev)
return match_revfn(NFPROTO_UNSPEC, name, revision, bestp); return match_revfn(NFPROTO_UNSPEC, name, revision, bestp);
...@@ -350,6 +352,7 @@ static int target_revfn(u8 af, const char *name, u8 revision, int *bestp) ...@@ -350,6 +352,7 @@ static int target_revfn(u8 af, const char *name, u8 revision, int *bestp)
const struct xt_target *t; const struct xt_target *t;
int have_rev = 0; int have_rev = 0;
mutex_lock(&xt[af].mutex);
list_for_each_entry(t, &xt[af].target, list) { list_for_each_entry(t, &xt[af].target, list) {
if (strcmp(t->name, name) == 0) { if (strcmp(t->name, name) == 0) {
if (t->revision > *bestp) if (t->revision > *bestp)
...@@ -358,6 +361,7 @@ static int target_revfn(u8 af, const char *name, u8 revision, int *bestp) ...@@ -358,6 +361,7 @@ static int target_revfn(u8 af, const char *name, u8 revision, int *bestp)
have_rev = 1; have_rev = 1;
} }
} }
mutex_unlock(&xt[af].mutex);
if (af != NFPROTO_UNSPEC && !have_rev) if (af != NFPROTO_UNSPEC && !have_rev)
return target_revfn(NFPROTO_UNSPEC, name, revision, bestp); return target_revfn(NFPROTO_UNSPEC, name, revision, bestp);
...@@ -371,12 +375,10 @@ int xt_find_revision(u8 af, const char *name, u8 revision, int target, ...@@ -371,12 +375,10 @@ int xt_find_revision(u8 af, const char *name, u8 revision, int target,
{ {
int have_rev, best = -1; int have_rev, best = -1;
mutex_lock(&xt[af].mutex);
if (target == 1) if (target == 1)
have_rev = target_revfn(af, name, revision, &best); have_rev = target_revfn(af, name, revision, &best);
else else
have_rev = match_revfn(af, name, revision, &best); have_rev = match_revfn(af, name, revision, &best);
mutex_unlock(&xt[af].mutex);
/* Nothing at all? Return 0 to try loading module. */ /* Nothing at all? Return 0 to try loading module. */
if (best == -1) { if (best == -1) {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
TEST_PROGS := nft_trans_stress.sh nft_nat.sh bridge_brouter.sh \ TEST_PROGS := nft_trans_stress.sh nft_nat.sh bridge_brouter.sh \
conntrack_icmp_related.sh nft_flowtable.sh ipvs.sh \ conntrack_icmp_related.sh nft_flowtable.sh ipvs.sh \
nft_concat_range.sh nft_conntrack_helper.sh \ nft_concat_range.sh nft_conntrack_helper.sh \
nft_queue.sh nft_meta.sh \ nft_queue.sh nft_meta.sh nf_nat_edemux.sh \
ipip-conntrack-mtu.sh ipip-conntrack-mtu.sh
LDLIBS = -lmnl LDLIBS = -lmnl
......
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Test NAT source port clash resolution
#
# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4
ret=0
sfx=$(mktemp -u "XXXXXXXX")
ns1="ns1-$sfx"
ns2="ns2-$sfx"
cleanup()
{
ip netns del $ns1
ip netns del $ns2
}
iperf3 -v > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: Could not run test without iperf3"
exit $ksft_skip
fi
iptables --version > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: Could not run test without iptables"
exit $ksft_skip
fi
ip -Version > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: Could not run test without ip tool"
exit $ksft_skip
fi
ip netns add "$ns1"
if [ $? -ne 0 ];then
echo "SKIP: Could not create net namespace $ns1"
exit $ksft_skip
fi
trap cleanup EXIT
ip netns add $ns2
# Connect the namespaces using a veth pair
ip link add name veth2 type veth peer name veth1
ip link set netns $ns1 dev veth1
ip link set netns $ns2 dev veth2
ip netns exec $ns1 ip link set up dev lo
ip netns exec $ns1 ip link set up dev veth1
ip netns exec $ns1 ip addr add 192.168.1.1/24 dev veth1
ip netns exec $ns2 ip link set up dev lo
ip netns exec $ns2 ip link set up dev veth2
ip netns exec $ns2 ip addr add 192.168.1.2/24 dev veth2
# Create a server in one namespace
ip netns exec $ns1 iperf3 -s > /dev/null 2>&1 &
iperfs=$!
# Restrict source port to just one so we don't have to exhaust
# all others.
ip netns exec $ns2 sysctl -q net.ipv4.ip_local_port_range="10000 10000"
# add a virtual IP using DNAT
ip netns exec $ns2 iptables -t nat -A OUTPUT -d 10.96.0.1/32 -p tcp --dport 443 -j DNAT --to-destination 192.168.1.1:5201
# ... and route it to the other namespace
ip netns exec $ns2 ip route add 10.96.0.1 via 192.168.1.1
sleep 1
# add a persistent connection from the other namespace
ip netns exec $ns2 nc -q 10 -w 10 192.168.1.1 5201 > /dev/null &
sleep 1
# ip daddr:dport will be rewritten to 192.168.1.1 5201
# NAT must reallocate source port 10000 because
# 192.168.1.2:10000 -> 192.168.1.1:5201 is already in use
echo test | ip netns exec $ns2 nc -w 3 -q 3 10.96.0.1 443 >/dev/null
ret=$?
kill $iperfs
# Check nc can connect to 10.96.0.1:443 (aka 192.168.1.1:5201).
if [ $ret -eq 0 ]; then
echo "PASS: nc can connect via NAT'd address"
else
echo "FAIL: nc cannot connect via NAT'd address"
exit 1
fi
exit 0
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment