diff options
-rw-r--r-- | include/linux/bpf.h | 7 | ||||
-rw-r--r-- | include/uapi/linux/bpf.h | 12 | ||||
-rw-r--r-- | kernel/bpf/syscall.c | 27 | ||||
-rw-r--r-- | net/Makefile | 2 | ||||
-rw-r--r-- | net/bpf/Makefile | 1 | ||||
-rw-r--r-- | net/bpf/test_run.c | 172 | ||||
-rw-r--r-- | net/core/filter.c | 5 |
7 files changed, 223 insertions, 3 deletions
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 2ae39a3e9ead..bbb513da5075 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h | |||
@@ -169,6 +169,8 @@ struct bpf_verifier_ops { | |||
169 | const struct bpf_insn *src, | 169 | const struct bpf_insn *src, |
170 | struct bpf_insn *dst, | 170 | struct bpf_insn *dst, |
171 | struct bpf_prog *prog); | 171 | struct bpf_prog *prog); |
172 | int (*test_run)(struct bpf_prog *prog, const union bpf_attr *kattr, | ||
173 | union bpf_attr __user *uattr); | ||
172 | }; | 174 | }; |
173 | 175 | ||
174 | struct bpf_prog_type_list { | 176 | struct bpf_prog_type_list { |
@@ -233,6 +235,11 @@ typedef unsigned long (*bpf_ctx_copy_t)(void *dst, const void *src, | |||
233 | u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size, | 235 | u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size, |
234 | void *ctx, u64 ctx_size, bpf_ctx_copy_t ctx_copy); | 236 | void *ctx, u64 ctx_size, bpf_ctx_copy_t ctx_copy); |
235 | 237 | ||
238 | int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, | ||
239 | union bpf_attr __user *uattr); | ||
240 | int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, | ||
241 | union bpf_attr __user *uattr); | ||
242 | |||
236 | #ifdef CONFIG_BPF_SYSCALL | 243 | #ifdef CONFIG_BPF_SYSCALL |
237 | DECLARE_PER_CPU(int, bpf_prog_active); | 244 | DECLARE_PER_CPU(int, bpf_prog_active); |
238 | 245 | ||
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 28317a04c34d..a1d95386f562 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h | |||
@@ -81,6 +81,7 @@ enum bpf_cmd { | |||
81 | BPF_OBJ_GET, | 81 | BPF_OBJ_GET, |
82 | BPF_PROG_ATTACH, | 82 | BPF_PROG_ATTACH, |
83 | BPF_PROG_DETACH, | 83 | BPF_PROG_DETACH, |
84 | BPF_PROG_TEST_RUN, | ||
84 | }; | 85 | }; |
85 | 86 | ||
86 | enum bpf_map_type { | 87 | enum bpf_map_type { |
@@ -189,6 +190,17 @@ union bpf_attr { | |||
189 | __u32 attach_type; | 190 | __u32 attach_type; |
190 | __u32 attach_flags; | 191 | __u32 attach_flags; |
191 | }; | 192 | }; |
193 | |||
194 | struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */ | ||
195 | __u32 prog_fd; | ||
196 | __u32 retval; | ||
197 | __u32 data_size_in; | ||
198 | __u32 data_size_out; | ||
199 | __aligned_u64 data_in; | ||
200 | __aligned_u64 data_out; | ||
201 | __u32 repeat; | ||
202 | __u32 duration; | ||
203 | } test; | ||
192 | } __attribute__((aligned(8))); | 204 | } __attribute__((aligned(8))); |
193 | 205 | ||
194 | /* BPF helper function descriptions: | 206 | /* BPF helper function descriptions: |
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index c35ebfe6d84d..ab0cf4c43690 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c | |||
@@ -973,6 +973,28 @@ static int bpf_prog_detach(const union bpf_attr *attr) | |||
973 | } | 973 | } |
974 | #endif /* CONFIG_CGROUP_BPF */ | 974 | #endif /* CONFIG_CGROUP_BPF */ |
975 | 975 | ||
976 | #define BPF_PROG_TEST_RUN_LAST_FIELD test.duration | ||
977 | |||
978 | static int bpf_prog_test_run(const union bpf_attr *attr, | ||
979 | union bpf_attr __user *uattr) | ||
980 | { | ||
981 | struct bpf_prog *prog; | ||
982 | int ret = -ENOTSUPP; | ||
983 | |||
984 | if (CHECK_ATTR(BPF_PROG_TEST_RUN)) | ||
985 | return -EINVAL; | ||
986 | |||
987 | prog = bpf_prog_get(attr->test.prog_fd); | ||
988 | if (IS_ERR(prog)) | ||
989 | return PTR_ERR(prog); | ||
990 | |||
991 | if (prog->aux->ops->test_run) | ||
992 | ret = prog->aux->ops->test_run(prog, attr, uattr); | ||
993 | |||
994 | bpf_prog_put(prog); | ||
995 | return ret; | ||
996 | } | ||
997 | |||
976 | SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) | 998 | SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) |
977 | { | 999 | { |
978 | union bpf_attr attr = {}; | 1000 | union bpf_attr attr = {}; |
@@ -1039,7 +1061,6 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz | |||
1039 | case BPF_OBJ_GET: | 1061 | case BPF_OBJ_GET: |
1040 | err = bpf_obj_get(&attr); | 1062 | err = bpf_obj_get(&attr); |
1041 | break; | 1063 | break; |
1042 | |||
1043 | #ifdef CONFIG_CGROUP_BPF | 1064 | #ifdef CONFIG_CGROUP_BPF |
1044 | case BPF_PROG_ATTACH: | 1065 | case BPF_PROG_ATTACH: |
1045 | err = bpf_prog_attach(&attr); | 1066 | err = bpf_prog_attach(&attr); |
@@ -1048,7 +1069,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz | |||
1048 | err = bpf_prog_detach(&attr); | 1069 | err = bpf_prog_detach(&attr); |
1049 | break; | 1070 | break; |
1050 | #endif | 1071 | #endif |
1051 | 1072 | case BPF_PROG_TEST_RUN: | |
1073 | err = bpf_prog_test_run(&attr, uattr); | ||
1074 | break; | ||
1052 | default: | 1075 | default: |
1053 | err = -EINVAL; | 1076 | err = -EINVAL; |
1054 | break; | 1077 | break; |
diff --git a/net/Makefile b/net/Makefile index 9b681550e3a3..9086ffbb5085 100644 --- a/net/Makefile +++ b/net/Makefile | |||
@@ -12,7 +12,7 @@ obj-$(CONFIG_NET) += $(tmp-y) | |||
12 | 12 | ||
13 | # LLC has to be linked before the files in net/802/ | 13 | # LLC has to be linked before the files in net/802/ |
14 | obj-$(CONFIG_LLC) += llc/ | 14 | obj-$(CONFIG_LLC) += llc/ |
15 | obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ | 15 | obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ bpf/ |
16 | obj-$(CONFIG_NETFILTER) += netfilter/ | 16 | obj-$(CONFIG_NETFILTER) += netfilter/ |
17 | obj-$(CONFIG_INET) += ipv4/ | 17 | obj-$(CONFIG_INET) += ipv4/ |
18 | obj-$(CONFIG_XFRM) += xfrm/ | 18 | obj-$(CONFIG_XFRM) += xfrm/ |
diff --git a/net/bpf/Makefile b/net/bpf/Makefile new file mode 100644 index 000000000000..27b2992a0692 --- /dev/null +++ b/net/bpf/Makefile | |||
@@ -0,0 +1 @@ | |||
obj-y := test_run.o | |||
diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c new file mode 100644 index 000000000000..8a6d0a37c30c --- /dev/null +++ b/net/bpf/test_run.c | |||
@@ -0,0 +1,172 @@ | |||
1 | /* Copyright (c) 2017 Facebook | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or | ||
4 | * modify it under the terms of version 2 of the GNU General Public | ||
5 | * License as published by the Free Software Foundation. | ||
6 | */ | ||
7 | #include <linux/bpf.h> | ||
8 | #include <linux/slab.h> | ||
9 | #include <linux/vmalloc.h> | ||
10 | #include <linux/etherdevice.h> | ||
11 | #include <linux/filter.h> | ||
12 | #include <linux/sched/signal.h> | ||
13 | |||
14 | static __always_inline u32 bpf_test_run_one(struct bpf_prog *prog, void *ctx) | ||
15 | { | ||
16 | u32 ret; | ||
17 | |||
18 | preempt_disable(); | ||
19 | rcu_read_lock(); | ||
20 | ret = BPF_PROG_RUN(prog, ctx); | ||
21 | rcu_read_unlock(); | ||
22 | preempt_enable(); | ||
23 | |||
24 | return ret; | ||
25 | } | ||
26 | |||
27 | static u32 bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *time) | ||
28 | { | ||
29 | u64 time_start, time_spent = 0; | ||
30 | u32 ret = 0, i; | ||
31 | |||
32 | if (!repeat) | ||
33 | repeat = 1; | ||
34 | time_start = ktime_get_ns(); | ||
35 | for (i = 0; i < repeat; i++) { | ||
36 | ret = bpf_test_run_one(prog, ctx); | ||
37 | if (need_resched()) { | ||
38 | if (signal_pending(current)) | ||
39 | break; | ||
40 | time_spent += ktime_get_ns() - time_start; | ||
41 | cond_resched(); | ||
42 | time_start = ktime_get_ns(); | ||
43 | } | ||
44 | } | ||
45 | time_spent += ktime_get_ns() - time_start; | ||
46 | do_div(time_spent, repeat); | ||
47 | *time = time_spent > U32_MAX ? U32_MAX : (u32)time_spent; | ||
48 | |||
49 | return ret; | ||
50 | } | ||
51 | |||
52 | static int bpf_test_finish(union bpf_attr __user *uattr, const void *data, | ||
53 | u32 size, u32 retval, u32 duration) | ||
54 | { | ||
55 | void __user *data_out = u64_to_user_ptr(uattr->test.data_out); | ||
56 | int err = -EFAULT; | ||
57 | |||
58 | if (data_out && copy_to_user(data_out, data, size)) | ||
59 | goto out; | ||
60 | if (copy_to_user(&uattr->test.data_size_out, &size, sizeof(size))) | ||
61 | goto out; | ||
62 | if (copy_to_user(&uattr->test.retval, &retval, sizeof(retval))) | ||
63 | goto out; | ||
64 | if (copy_to_user(&uattr->test.duration, &duration, sizeof(duration))) | ||
65 | goto out; | ||
66 | err = 0; | ||
67 | out: | ||
68 | return err; | ||
69 | } | ||
70 | |||
71 | static void *bpf_test_init(const union bpf_attr *kattr, u32 size, | ||
72 | u32 headroom, u32 tailroom) | ||
73 | { | ||
74 | void __user *data_in = u64_to_user_ptr(kattr->test.data_in); | ||
75 | void *data; | ||
76 | |||
77 | if (size < ETH_HLEN || size > PAGE_SIZE - headroom - tailroom) | ||
78 | return ERR_PTR(-EINVAL); | ||
79 | |||
80 | data = kzalloc(size + headroom + tailroom, GFP_USER); | ||
81 | if (!data) | ||
82 | return ERR_PTR(-ENOMEM); | ||
83 | |||
84 | if (copy_from_user(data + headroom, data_in, size)) { | ||
85 | kfree(data); | ||
86 | return ERR_PTR(-EFAULT); | ||
87 | } | ||
88 | return data; | ||
89 | } | ||
90 | |||
91 | int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, | ||
92 | union bpf_attr __user *uattr) | ||
93 | { | ||
94 | bool is_l2 = false, is_direct_pkt_access = false; | ||
95 | u32 size = kattr->test.data_size_in; | ||
96 | u32 repeat = kattr->test.repeat; | ||
97 | u32 retval, duration; | ||
98 | struct sk_buff *skb; | ||
99 | void *data; | ||
100 | int ret; | ||
101 | |||
102 | data = bpf_test_init(kattr, size, NET_SKB_PAD, | ||
103 | SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); | ||
104 | if (IS_ERR(data)) | ||
105 | return PTR_ERR(data); | ||
106 | |||
107 | switch (prog->type) { | ||
108 | case BPF_PROG_TYPE_SCHED_CLS: | ||
109 | case BPF_PROG_TYPE_SCHED_ACT: | ||
110 | is_l2 = true; | ||
111 | /* fall through */ | ||
112 | case BPF_PROG_TYPE_LWT_IN: | ||
113 | case BPF_PROG_TYPE_LWT_OUT: | ||
114 | case BPF_PROG_TYPE_LWT_XMIT: | ||
115 | is_direct_pkt_access = true; | ||
116 | break; | ||
117 | default: | ||
118 | break; | ||
119 | } | ||
120 | |||
121 | skb = build_skb(data, 0); | ||
122 | if (!skb) { | ||
123 | kfree(data); | ||
124 | return -ENOMEM; | ||
125 | } | ||
126 | |||
127 | skb_reserve(skb, NET_SKB_PAD); | ||
128 | __skb_put(skb, size); | ||
129 | skb->protocol = eth_type_trans(skb, current->nsproxy->net_ns->loopback_dev); | ||
130 | skb_reset_network_header(skb); | ||
131 | |||
132 | if (is_l2) | ||
133 | __skb_push(skb, ETH_HLEN); | ||
134 | if (is_direct_pkt_access) | ||
135 | bpf_compute_data_end(skb); | ||
136 | retval = bpf_test_run(prog, skb, repeat, &duration); | ||
137 | if (!is_l2) | ||
138 | __skb_push(skb, ETH_HLEN); | ||
139 | size = skb->len; | ||
140 | /* bpf program can never convert linear skb to non-linear */ | ||
141 | if (WARN_ON_ONCE(skb_is_nonlinear(skb))) | ||
142 | size = skb_headlen(skb); | ||
143 | ret = bpf_test_finish(uattr, skb->data, size, retval, duration); | ||
144 | kfree_skb(skb); | ||
145 | return ret; | ||
146 | } | ||
147 | |||
148 | int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, | ||
149 | union bpf_attr __user *uattr) | ||
150 | { | ||
151 | u32 size = kattr->test.data_size_in; | ||
152 | u32 repeat = kattr->test.repeat; | ||
153 | struct xdp_buff xdp = {}; | ||
154 | u32 retval, duration; | ||
155 | void *data; | ||
156 | int ret; | ||
157 | |||
158 | data = bpf_test_init(kattr, size, XDP_PACKET_HEADROOM, 0); | ||
159 | if (IS_ERR(data)) | ||
160 | return PTR_ERR(data); | ||
161 | |||
162 | xdp.data_hard_start = data; | ||
163 | xdp.data = data + XDP_PACKET_HEADROOM; | ||
164 | xdp.data_end = xdp.data + size; | ||
165 | |||
166 | retval = bpf_test_run(prog, &xdp, repeat, &duration); | ||
167 | if (xdp.data != data + XDP_PACKET_HEADROOM) | ||
168 | size = xdp.data_end - xdp.data; | ||
169 | ret = bpf_test_finish(uattr, xdp.data, size, retval, duration); | ||
170 | kfree(data); | ||
171 | return ret; | ||
172 | } | ||
diff --git a/net/core/filter.c b/net/core/filter.c index dfb9f61a2fd5..15e9a81ffebe 100644 --- a/net/core/filter.c +++ b/net/core/filter.c | |||
@@ -3309,24 +3309,28 @@ static const struct bpf_verifier_ops tc_cls_act_ops = { | |||
3309 | .is_valid_access = tc_cls_act_is_valid_access, | 3309 | .is_valid_access = tc_cls_act_is_valid_access, |
3310 | .convert_ctx_access = tc_cls_act_convert_ctx_access, | 3310 | .convert_ctx_access = tc_cls_act_convert_ctx_access, |
3311 | .gen_prologue = tc_cls_act_prologue, | 3311 | .gen_prologue = tc_cls_act_prologue, |
3312 | .test_run = bpf_prog_test_run_skb, | ||
3312 | }; | 3313 | }; |
3313 | 3314 | ||
3314 | static const struct bpf_verifier_ops xdp_ops = { | 3315 | static const struct bpf_verifier_ops xdp_ops = { |
3315 | .get_func_proto = xdp_func_proto, | 3316 | .get_func_proto = xdp_func_proto, |
3316 | .is_valid_access = xdp_is_valid_access, | 3317 | .is_valid_access = xdp_is_valid_access, |
3317 | .convert_ctx_access = xdp_convert_ctx_access, | 3318 | .convert_ctx_access = xdp_convert_ctx_access, |
3319 | .test_run = bpf_prog_test_run_xdp, | ||
3318 | }; | 3320 | }; |
3319 | 3321 | ||
3320 | static const struct bpf_verifier_ops cg_skb_ops = { | 3322 | static const struct bpf_verifier_ops cg_skb_ops = { |
3321 | .get_func_proto = cg_skb_func_proto, | 3323 | .get_func_proto = cg_skb_func_proto, |
3322 | .is_valid_access = sk_filter_is_valid_access, | 3324 | .is_valid_access = sk_filter_is_valid_access, |
3323 | .convert_ctx_access = bpf_convert_ctx_access, | 3325 | .convert_ctx_access = bpf_convert_ctx_access, |
3326 | .test_run = bpf_prog_test_run_skb, | ||
3324 | }; | 3327 | }; |
3325 | 3328 | ||
3326 | static const struct bpf_verifier_ops lwt_inout_ops = { | 3329 | static const struct bpf_verifier_ops lwt_inout_ops = { |
3327 | .get_func_proto = lwt_inout_func_proto, | 3330 | .get_func_proto = lwt_inout_func_proto, |
3328 | .is_valid_access = lwt_is_valid_access, | 3331 | .is_valid_access = lwt_is_valid_access, |
3329 | .convert_ctx_access = bpf_convert_ctx_access, | 3332 | .convert_ctx_access = bpf_convert_ctx_access, |
3333 | .test_run = bpf_prog_test_run_skb, | ||
3330 | }; | 3334 | }; |
3331 | 3335 | ||
3332 | static const struct bpf_verifier_ops lwt_xmit_ops = { | 3336 | static const struct bpf_verifier_ops lwt_xmit_ops = { |
@@ -3334,6 +3338,7 @@ static const struct bpf_verifier_ops lwt_xmit_ops = { | |||
3334 | .is_valid_access = lwt_is_valid_access, | 3338 | .is_valid_access = lwt_is_valid_access, |
3335 | .convert_ctx_access = bpf_convert_ctx_access, | 3339 | .convert_ctx_access = bpf_convert_ctx_access, |
3336 | .gen_prologue = tc_cls_act_prologue, | 3340 | .gen_prologue = tc_cls_act_prologue, |
3341 | .test_run = bpf_prog_test_run_skb, | ||
3337 | }; | 3342 | }; |
3338 | 3343 | ||
3339 | static const struct bpf_verifier_ops cg_sock_ops = { | 3344 | static const struct bpf_verifier_ops cg_sock_ops = { |