Skip to content

Commit 7e961f8

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 <[email protected]>
1 parent 56103cc commit 7e961f8

File tree

5 files changed

+195
-0
lines changed

5 files changed

+195
-0
lines changed

include/linux/bpf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2234,6 +2234,9 @@ int bpf_prog_test_run_raw_tp(struct bpf_prog *prog,
22342234
int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog,
22352235
const union bpf_attr *kattr,
22362236
union bpf_attr __user *uattr);
2237+
int bpf_prog_test_run_nf(struct bpf_prog *prog,
2238+
const union bpf_attr *kattr,
2239+
union bpf_attr __user *uattr);
22372240
bool btf_ctx_access(int off, int size, enum bpf_access_type type,
22382241
const struct bpf_prog *prog,
22392242
struct bpf_insn_access_aux *info);

net/bpf/test_run.c

Lines changed: 140 additions & 0 deletions
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>
@@ -1696,6 +1698,144 @@ int bpf_prog_test_run_syscall(struct bpf_prog *prog,
16961698
return err;
16971699
}
16981700

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

net/netfilter/nf_bpf_link.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ int bpf_nf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
128128
}
129129

130130
const struct bpf_prog_ops netfilter_prog_ops = {
131+
.test_run = bpf_prog_test_run_nf,
131132
};
132133

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

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

Lines changed: 2 additions & 0 deletions
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_ringbuf.skel.h"
@@ -93,6 +94,7 @@ void test_verifier_map_ptr(void) { RUN(verifier_map_ptr); }
9394
void test_verifier_map_ret_val(void) { RUN(verifier_map_ret_val); }
9495
void test_verifier_masking(void) { RUN(verifier_masking); }
9596
void test_verifier_meta_access(void) { RUN(verifier_meta_access); }
97+
void test_verifier_netfilter_retcode(void) { RUN(verifier_netfilter_retcode); }
9698
void test_verifier_raw_stack(void) { RUN(verifier_raw_stack); }
9799
void test_verifier_raw_tp_writable(void) { RUN(verifier_raw_tp_writable); }
98100
void test_verifier_ringbuf(void) { RUN(verifier_ringbuf); }
Lines changed: 49 additions & 0 deletions
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)