Skip to content

Commit

Permalink
Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf
Browse files Browse the repository at this point in the history
Pablo Neira Ayuso says:

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

The following patchset contains Netfilter fixes for net:

1) Fix a crash when stateful expression with its own gc callback
   is used in a set definition.

2) Skip IPv6 packets from any link-local address in IPv6 fib expression.
   Add a selftest for this scenario, from Florian Westphal.
====================

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed Jun 10, 2021
2 parents 0280f42 + 12f36e9 commit 22488e4
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 47 deletions.
22 changes: 18 additions & 4 deletions net/ipv6/netfilter/nft_fib_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,17 @@ void nft_fib6_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
}
EXPORT_SYMBOL_GPL(nft_fib6_eval_type);

static bool nft_fib_v6_skip_icmpv6(const struct sk_buff *skb, u8 next, const struct ipv6hdr *iph)
{
if (likely(next != IPPROTO_ICMPV6))
return false;

if (ipv6_addr_type(&iph->saddr) != IPV6_ADDR_ANY)
return false;

return ipv6_addr_type(&iph->daddr) & IPV6_ADDR_LINKLOCAL;
}

void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
Expand Down Expand Up @@ -163,10 +174,13 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,

lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif, iph);

if (nft_hook(pkt) == NF_INET_PRE_ROUTING &&
nft_fib_is_loopback(pkt->skb, nft_in(pkt))) {
nft_fib_store_result(dest, priv, nft_in(pkt));
return;
if (nft_hook(pkt) == NF_INET_PRE_ROUTING ||
nft_hook(pkt) == NF_INET_INGRESS) {
if (nft_fib_is_loopback(pkt->skb, nft_in(pkt)) ||
nft_fib_v6_skip_icmpv6(pkt->skb, pkt->tprot, iph)) {
nft_fib_store_result(dest, priv, nft_in(pkt));
return;
}
}

*dest = 0;
Expand Down
85 changes: 43 additions & 42 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -4364,13 +4364,45 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
err = nf_tables_set_alloc_name(&ctx, set, name);
kfree(name);
if (err < 0)
goto err_set_alloc_name;
goto err_set_name;

udata = NULL;
if (udlen) {
udata = set->data + size;
nla_memcpy(udata, nla[NFTA_SET_USERDATA], udlen);
}

INIT_LIST_HEAD(&set->bindings);
INIT_LIST_HEAD(&set->catchall_list);
set->table = table;
write_pnet(&set->net, net);
set->ops = ops;
set->ktype = ktype;
set->klen = desc.klen;
set->dtype = dtype;
set->objtype = objtype;
set->dlen = desc.dlen;
set->flags = flags;
set->size = desc.size;
set->policy = policy;
set->udlen = udlen;
set->udata = udata;
set->timeout = timeout;
set->gc_int = gc_int;

set->field_count = desc.field_count;
for (i = 0; i < desc.field_count; i++)
set->field_len[i] = desc.field_len[i];

err = ops->init(set, &desc, nla);
if (err < 0)
goto err_set_init;

if (nla[NFTA_SET_EXPR]) {
expr = nft_set_elem_expr_alloc(&ctx, set, nla[NFTA_SET_EXPR]);
if (IS_ERR(expr)) {
err = PTR_ERR(expr);
goto err_set_alloc_name;
goto err_set_expr_alloc;
}
set->exprs[0] = expr;
set->num_exprs++;
Expand All @@ -4381,75 +4413,44 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,

if (!(flags & NFT_SET_EXPR)) {
err = -EINVAL;
goto err_set_alloc_name;
goto err_set_expr_alloc;
}
i = 0;
nla_for_each_nested(tmp, nla[NFTA_SET_EXPRESSIONS], left) {
if (i == NFT_SET_EXPR_MAX) {
err = -E2BIG;
goto err_set_init;
goto err_set_expr_alloc;
}
if (nla_type(tmp) != NFTA_LIST_ELEM) {
err = -EINVAL;
goto err_set_init;
goto err_set_expr_alloc;
}
expr = nft_set_elem_expr_alloc(&ctx, set, tmp);
if (IS_ERR(expr)) {
err = PTR_ERR(expr);
goto err_set_init;
goto err_set_expr_alloc;
}
set->exprs[i++] = expr;
set->num_exprs++;
}
}

udata = NULL;
if (udlen) {
udata = set->data + size;
nla_memcpy(udata, nla[NFTA_SET_USERDATA], udlen);
}

INIT_LIST_HEAD(&set->bindings);
INIT_LIST_HEAD(&set->catchall_list);
set->table = table;
write_pnet(&set->net, net);
set->ops = ops;
set->ktype = ktype;
set->klen = desc.klen;
set->dtype = dtype;
set->objtype = objtype;
set->dlen = desc.dlen;
set->flags = flags;
set->size = desc.size;
set->policy = policy;
set->udlen = udlen;
set->udata = udata;
set->timeout = timeout;
set->gc_int = gc_int;
set->handle = nf_tables_alloc_handle(table);

set->field_count = desc.field_count;
for (i = 0; i < desc.field_count; i++)
set->field_len[i] = desc.field_len[i];

err = ops->init(set, &desc, nla);
if (err < 0)
goto err_set_init;

err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
if (err < 0)
goto err_set_trans;
goto err_set_expr_alloc;

list_add_tail_rcu(&set->list, &table->sets);
table->use++;
return 0;

err_set_trans:
ops->destroy(set);
err_set_init:
err_set_expr_alloc:
for (i = 0; i < set->num_exprs; i++)
nft_expr_destroy(&ctx, set->exprs[i]);
err_set_alloc_name:

ops->destroy(set);
err_set_init:
kfree(set->name);
err_set_name:
kvfree(set);
Expand Down
2 changes: 1 addition & 1 deletion tools/testing/selftests/netfilter/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for netfilter selftests

TEST_PROGS := nft_trans_stress.sh nft_nat.sh bridge_brouter.sh \
TEST_PROGS := nft_trans_stress.sh nft_fib.sh nft_nat.sh bridge_brouter.sh \
conntrack_icmp_related.sh nft_flowtable.sh ipvs.sh \
nft_concat_range.sh nft_conntrack_helper.sh \
nft_queue.sh nft_meta.sh nf_nat_edemux.sh \
Expand Down
221 changes: 221 additions & 0 deletions tools/testing/selftests/netfilter/nft_fib.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
#!/bin/bash
#
# This tests the fib expression.
#
# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4
ret=0

sfx=$(mktemp -u "XXXXXXXX")
ns1="ns1-$sfx"
ns2="ns2-$sfx"
nsrouter="nsrouter-$sfx"
timeout=4

log_netns=$(sysctl -n net.netfilter.nf_log_all_netns)

cleanup()
{
ip netns del ${ns1}
ip netns del ${ns2}
ip netns del ${nsrouter}

[ $log_netns -eq 0 ] && sysctl -q net.netfilter.nf_log_all_netns=$log_netns
}

nft --version > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: Could not run test without nft tool"
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 ${nsrouter}
if [ $? -ne 0 ];then
echo "SKIP: Could not create net namespace"
exit $ksft_skip
fi

trap cleanup EXIT

dmesg | grep -q ' nft_rpfilter: '
if [ $? -eq 0 ]; then
dmesg -c | grep ' nft_rpfilter: '
echo "WARN: a previous test run has failed" 1>&2
fi

sysctl -q net.netfilter.nf_log_all_netns=1
ip netns add ${ns1}
ip netns add ${ns2}

load_ruleset() {
local netns=$1

ip netns exec ${netns} nft -f /dev/stdin <<EOF
table inet filter {
chain prerouting {
type filter hook prerouting priority 0; policy accept;
fib saddr . iif oif missing counter log prefix "$netns nft_rpfilter: " drop
}
}
EOF
}

load_ruleset_count() {
local netns=$1

ip netns exec ${netns} nft -f /dev/stdin <<EOF
table inet filter {
chain prerouting {
type filter hook prerouting priority 0; policy accept;
ip daddr 1.1.1.1 fib saddr . iif oif missing counter drop
ip6 daddr 1c3::c01d fib saddr . iif oif missing counter drop
}
}
EOF
}

check_drops() {
dmesg | grep -q ' nft_rpfilter: '
if [ $? -eq 0 ]; then
dmesg | grep ' nft_rpfilter: '
echo "FAIL: rpfilter did drop packets"
return 1
fi

return 0
}

check_fib_counter() {
local want=$1
local ns=$2
local address=$3

line=$(ip netns exec ${ns} nft list table inet filter | grep 'fib saddr . iif' | grep $address | grep "packets $want" )
ret=$?

if [ $ret -ne 0 ];then
echo "Netns $ns fib counter doesn't match expected packet count of $want for $address" 1>&2
ip netns exec ${ns} nft list table inet filter
return 1
fi

if [ $want -gt 0 ]; then
echo "PASS: fib expression did drop packets for $address"
fi

return 0
}

load_ruleset ${nsrouter}
load_ruleset ${ns1}
load_ruleset ${ns2}

ip link add veth0 netns ${nsrouter} type veth peer name eth0 netns ${ns1} > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: No virtual ethernet pair device support in kernel"
exit $ksft_skip
fi
ip link add veth1 netns ${nsrouter} type veth peer name eth0 netns ${ns2}

ip -net ${nsrouter} link set lo up
ip -net ${nsrouter} link set veth0 up
ip -net ${nsrouter} addr add 10.0.1.1/24 dev veth0
ip -net ${nsrouter} addr add dead:1::1/64 dev veth0

ip -net ${nsrouter} link set veth1 up
ip -net ${nsrouter} addr add 10.0.2.1/24 dev veth1
ip -net ${nsrouter} addr add dead:2::1/64 dev veth1

ip -net ${ns1} link set lo up
ip -net ${ns1} link set eth0 up

ip -net ${ns2} link set lo up
ip -net ${ns2} link set eth0 up

ip -net ${ns1} addr add 10.0.1.99/24 dev eth0
ip -net ${ns1} addr add dead:1::99/64 dev eth0
ip -net ${ns1} route add default via 10.0.1.1
ip -net ${ns1} route add default via dead:1::1

ip -net ${ns2} addr add 10.0.2.99/24 dev eth0
ip -net ${ns2} addr add dead:2::99/64 dev eth0
ip -net ${ns2} route add default via 10.0.2.1
ip -net ${ns2} route add default via dead:2::1

test_ping() {
local daddr4=$1
local daddr6=$2

ip netns exec ${ns1} ping -c 1 -q $daddr4 > /dev/null
ret=$?
if [ $ret -ne 0 ];then
check_drops
echo "FAIL: ${ns1} cannot reach $daddr4, ret $ret" 1>&2
return 1
fi

ip netns exec ${ns1} ping -c 3 -q $daddr6 > /dev/null
ret=$?
if [ $ret -ne 0 ];then
check_drops
echo "FAIL: ${ns1} cannot reach $daddr6, ret $ret" 1>&2
return 1
fi

return 0
}

ip netns exec ${nsrouter} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null

sleep 3

test_ping 10.0.2.1 dead:2::1 || exit 1
check_drops || exit 1

test_ping 10.0.2.99 dead:2::99 || exit 1
check_drops || exit 1

echo "PASS: fib expression did not cause unwanted packet drops"

ip netns exec ${nsrouter} nft flush table inet filter

ip -net ${ns1} route del default
ip -net ${ns1} -6 route del default

ip -net ${ns1} addr del 10.0.1.99/24 dev eth0
ip -net ${ns1} addr del dead:1::99/64 dev eth0

ip -net ${ns1} addr add 10.0.2.99/24 dev eth0
ip -net ${ns1} addr add dead:2::99/64 dev eth0

ip -net ${ns1} route add default via 10.0.2.1
ip -net ${ns1} -6 route add default via dead:2::1

ip -net ${nsrouter} addr add dead:2::1/64 dev veth0

# switch to ruleset that doesn't log, this time
# its expected that this does drop the packets.
load_ruleset_count ${nsrouter}

# ns1 has a default route, but nsrouter does not.
# must not check return value, ping to 1.1.1.1 will
# fail.
check_fib_counter 0 ${nsrouter} 1.1.1.1 || exit 1
check_fib_counter 0 ${nsrouter} 1c3::c01d || exit 1

ip netns exec ${ns1} ping -c 1 -W 1 -q 1.1.1.1 > /dev/null
check_fib_counter 1 ${nsrouter} 1.1.1.1 || exit 1

sleep 2
ip netns exec ${ns1} ping -c 3 -q 1c3::c01d > /dev/null
check_fib_counter 3 ${nsrouter} 1c3::c01d || exit 1

exit 0

0 comments on commit 22488e4

Please sign in to comment.