Skip to content

Commit 2cf399b

Browse files
Florian WestphalKernel Patches Daemon
Florian Westphal
authored and
Kernel Patches Daemon
committed
bpf: add test_run support for netfilter program type
also extend prog_tests with a small retval test: values other than accept or drop (0, 1) will cause issues. NF_QUEUE could be implemented later if we can guarantee that attachment of such programs can be rejected if they get attached to a pf/hook that doesn't support async reinjection. NF_STOLEN could be implemented via trusted helpers that can guarantee that the skb will eventually be free'd. $ ./test_progs --allow=verifier_netfilter_retcode #278/1 verifier_netfilter_retcode/bpf_exit with invalid return code. test1:OK #278/2 verifier_netfilter_retcode/bpf_exit with valid return code. test2:OK #278/3 verifier_netfilter_retcode/bpf_exit with valid return code. test3:OK #278/4 verifier_netfilter_retcode/bpf_exit with invalid return code. test4:OK #278 verifier_netfilter_retcode:OK Signed-off-by: Florian Westphal <fw@strlen.de>
1 parent 8860cbc commit 2cf399b

File tree

5 files changed

+195
-0
lines changed

5 files changed

+195
-0
lines changed

include/linux/bpf.h

+3
Original file line numberDiff line numberDiff line change
@@ -2264,6 +2264,9 @@ int bpf_prog_test_run_raw_tp(struct bpf_prog *prog,
22642264
int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog,
22652265
const union bpf_attr *kattr,
22662266
union bpf_attr __user *uattr);
2267+
int bpf_prog_test_run_nf(struct bpf_prog *prog,
2268+
const union bpf_attr *kattr,
2269+
union bpf_attr __user *uattr);
22672270
bool btf_ctx_access(int off, int size, enum bpf_access_type type,
22682271
const struct bpf_prog *prog,
22692272
struct bpf_insn_access_aux *info);

net/bpf/test_run.c

+140
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
#include <linux/error-injection.h>
2020
#include <linux/smp.h>
2121
#include <linux/sock_diag.h>
22+
#include <linux/netfilter.h>
2223
#include <net/xdp.h>
24+
#include <net/netfilter/nf_bpf_link.h>
2325

2426
#define CREATE_TRACE_POINTS
2527
#include <trace/events/bpf_test_run.h>
@@ -1691,6 +1693,144 @@ int bpf_prog_test_run_syscall(struct bpf_prog *prog,
16911693
return err;
16921694
}
16931695

1696+
static int verify_and_copy_hook_state(struct nf_hook_state *state,
1697+
const struct nf_hook_state *user,
1698+
struct net_device *dev)
1699+
{
1700+
if (user->in || user->out)
1701+
return -EINVAL;
1702+
1703+
if (user->net || user->sk || user->okfn)
1704+
return -EINVAL;
1705+
1706+
switch (user->pf) {
1707+
case NFPROTO_IPV4:
1708+
case NFPROTO_IPV6:
1709+
switch (state->hook) {
1710+
case NF_INET_PRE_ROUTING:
1711+
state->in = dev;
1712+
break;
1713+
case NF_INET_LOCAL_IN:
1714+
state->in = dev;
1715+
break;
1716+
case NF_INET_FORWARD:
1717+
state->in = dev;
1718+
state->out = dev;
1719+
break;
1720+
case NF_INET_LOCAL_OUT:
1721+
state->out = dev;
1722+
break;
1723+
case NF_INET_POST_ROUTING:
1724+
state->out = dev;
1725+
break;
1726+
}
1727+
1728+
break;
1729+
default:
1730+
return -EINVAL;
1731+
}
1732+
1733+
state->pf = user->pf;
1734+
state->hook = user->hook;
1735+
1736+
return 0;
1737+
}
1738+
1739+
int bpf_prog_test_run_nf(struct bpf_prog *prog,
1740+
const union bpf_attr *kattr,
1741+
union bpf_attr __user *uattr)
1742+
{
1743+
struct net *net = current->nsproxy->net_ns;
1744+
struct net_device *dev = net->loopback_dev;
1745+
struct nf_hook_state *user_ctx, hook_state = {
1746+
.pf = NFPROTO_IPV4,
1747+
.hook = NF_INET_PRE_ROUTING,
1748+
};
1749+
u32 size = kattr->test.data_size_in;
1750+
u32 repeat = kattr->test.repeat;
1751+
struct bpf_nf_ctx ctx = {
1752+
.state = &hook_state,
1753+
};
1754+
struct sk_buff *skb = NULL;
1755+
u32 retval, duration;
1756+
void *data;
1757+
int ret;
1758+
1759+
if (kattr->test.flags || kattr->test.cpu || kattr->test.batch_size)
1760+
return -EINVAL;
1761+
1762+
if (size < ETH_HLEN + sizeof(struct iphdr))
1763+
return -EINVAL;
1764+
1765+
data = bpf_test_init(kattr, kattr->test.data_size_in, size,
1766+
NET_SKB_PAD + NET_IP_ALIGN,
1767+
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
1768+
if (IS_ERR(data))
1769+
return PTR_ERR(data);
1770+
1771+
if (!repeat)
1772+
repeat = 1;
1773+
1774+
user_ctx = bpf_ctx_init(kattr, sizeof(struct nf_hook_state));
1775+
if (IS_ERR(user_ctx)) {
1776+
kfree(data);
1777+
return PTR_ERR(user_ctx);
1778+
}
1779+
1780+
if (user_ctx) {
1781+
ret = verify_and_copy_hook_state(&hook_state, user_ctx, dev);
1782+
if (ret)
1783+
goto out;
1784+
}
1785+
1786+
skb = slab_build_skb(data);
1787+
if (!skb) {
1788+
ret = -ENOMEM;
1789+
goto out;
1790+
}
1791+
1792+
data = NULL; /* data released via kfree_skb */
1793+
1794+
skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
1795+
__skb_put(skb, size);
1796+
1797+
skb->protocol = eth_type_trans(skb, dev);
1798+
1799+
skb_reset_network_header(skb);
1800+
1801+
ret = -EINVAL;
1802+
1803+
switch (skb->protocol) {
1804+
case htons(ETH_P_IP):
1805+
if (hook_state.pf == NFPROTO_IPV4)
1806+
break;
1807+
goto out;
1808+
case htons(ETH_P_IPV6):
1809+
if (size < ETH_HLEN + sizeof(struct ipv6hdr))
1810+
goto out;
1811+
if (hook_state.pf == NFPROTO_IPV6)
1812+
break;
1813+
goto out;
1814+
default:
1815+
ret = -EPROTO;
1816+
goto out;
1817+
}
1818+
1819+
ctx.skb = skb;
1820+
1821+
ret = bpf_test_run(prog, &ctx, repeat, &retval, &duration, false);
1822+
if (ret)
1823+
goto out;
1824+
1825+
ret = bpf_test_finish(kattr, uattr, NULL, NULL, 0, retval, duration);
1826+
1827+
out:
1828+
kfree(user_ctx);
1829+
kfree_skb(skb);
1830+
kfree(data);
1831+
return ret;
1832+
}
1833+
16941834
static const struct btf_kfunc_id_set bpf_prog_test_kfunc_set = {
16951835
.owner = THIS_MODULE,
16961836
.set = &test_sk_check_kfunc_ids,

net/netfilter/nf_bpf_link.c

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ int bpf_nf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
167167
}
168168

169169
const struct bpf_prog_ops netfilter_prog_ops = {
170+
.test_run = bpf_prog_test_run_nf,
170171
};
171172

172173
static bool nf_ptr_to_btf_id(struct bpf_insn_access_aux *info, const char *name)

tools/testing/selftests/bpf/prog_tests/verifier.c

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "verifier_map_ret_val.skel.h"
3030
#include "verifier_masking.skel.h"
3131
#include "verifier_meta_access.skel.h"
32+
#include "verifier_netfilter_retcode.skel.h"
3233
#include "verifier_raw_stack.skel.h"
3334
#include "verifier_raw_tp_writable.skel.h"
3435
#include "verifier_reg_equal.skel.h"
@@ -94,6 +95,7 @@ void test_verifier_map_ptr(void) { RUN(verifier_map_ptr); }
9495
void test_verifier_map_ret_val(void) { RUN(verifier_map_ret_val); }
9596
void test_verifier_masking(void) { RUN(verifier_masking); }
9697
void test_verifier_meta_access(void) { RUN(verifier_meta_access); }
98+
void test_verifier_netfilter_retcode(void) { RUN(verifier_netfilter_retcode); }
9799
void test_verifier_raw_stack(void) { RUN(verifier_raw_stack); }
98100
void test_verifier_raw_tp_writable(void) { RUN(verifier_raw_tp_writable); }
99101
void test_verifier_reg_equal(void) { RUN(verifier_reg_equal); }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <linux/bpf.h>
4+
#include <bpf/bpf_helpers.h>
5+
#include "bpf_misc.h"
6+
7+
SEC("netfilter")
8+
__description("bpf_exit with invalid return code. test1")
9+
__failure __msg("R0 is not a known value")
10+
__naked void with_invalid_return_code_test1(void)
11+
{
12+
asm volatile (" \
13+
r0 = *(u64*)(r1 + 0); \
14+
exit; \
15+
" ::: __clobber_all);
16+
}
17+
18+
SEC("netfilter")
19+
__description("bpf_exit with valid return code. test2")
20+
__success
21+
__naked void with_valid_return_code_test2(void)
22+
{
23+
asm volatile (" \
24+
r0 = 0; \
25+
exit; \
26+
" ::: __clobber_all);
27+
}
28+
29+
SEC("netfilter")
30+
__description("bpf_exit with valid return code. test3")
31+
__success
32+
__naked void with_valid_return_code_test3(void)
33+
{
34+
asm volatile (" \
35+
r0 = 1; \
36+
exit; \
37+
" ::: __clobber_all);
38+
}
39+
40+
SEC("netfilter")
41+
__description("bpf_exit with invalid return code. test4")
42+
__failure __msg("R0 has value (0x2; 0x0)")
43+
__naked void with_invalid_return_code_test4(void)
44+
{
45+
asm volatile (" \
46+
r0 = 2; \
47+
exit; \
48+
" ::: __clobber_all);
49+
}

0 commit comments

Comments
 (0)