diff options
-rw-r--r-- | include/net/sch_generic.h | 2 | ||||
-rw-r--r-- | include/uapi/linux/bpf.h | 1 | ||||
-rw-r--r-- | include/uapi/linux/pkt_cls.h | 3 | ||||
-rw-r--r-- | net/core/filter.c | 14 | ||||
-rw-r--r-- | net/sched/cls_bpf.c | 60 |
5 files changed, 68 insertions, 12 deletions
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 444faa89a55f..da61febb9091 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h | |||
@@ -251,7 +251,7 @@ struct tcf_proto { | |||
251 | struct qdisc_skb_cb { | 251 | struct qdisc_skb_cb { |
252 | unsigned int pkt_len; | 252 | unsigned int pkt_len; |
253 | u16 slave_dev_queue_mapping; | 253 | u16 slave_dev_queue_mapping; |
254 | u16 _pad; | 254 | u16 tc_classid; |
255 | #define QDISC_CB_PRIV_LEN 20 | 255 | #define QDISC_CB_PRIV_LEN 20 |
256 | unsigned char data[QDISC_CB_PRIV_LEN]; | 256 | unsigned char data[QDISC_CB_PRIV_LEN]; |
257 | }; | 257 | }; |
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 92a48e2d5461..2fbd1c71fa3b 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h | |||
@@ -293,6 +293,7 @@ struct __sk_buff { | |||
293 | __u32 tc_index; | 293 | __u32 tc_index; |
294 | __u32 cb[5]; | 294 | __u32 cb[5]; |
295 | __u32 hash; | 295 | __u32 hash; |
296 | __u32 tc_classid; | ||
296 | }; | 297 | }; |
297 | 298 | ||
298 | struct bpf_tunnel_key { | 299 | struct bpf_tunnel_key { |
diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 4f0d1bc3647d..0a262a83f9d4 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h | |||
@@ -373,6 +373,8 @@ enum { | |||
373 | 373 | ||
374 | /* BPF classifier */ | 374 | /* BPF classifier */ |
375 | 375 | ||
376 | #define TCA_BPF_FLAG_ACT_DIRECT (1 << 0) | ||
377 | |||
376 | enum { | 378 | enum { |
377 | TCA_BPF_UNSPEC, | 379 | TCA_BPF_UNSPEC, |
378 | TCA_BPF_ACT, | 380 | TCA_BPF_ACT, |
@@ -382,6 +384,7 @@ enum { | |||
382 | TCA_BPF_OPS, | 384 | TCA_BPF_OPS, |
383 | TCA_BPF_FD, | 385 | TCA_BPF_FD, |
384 | TCA_BPF_NAME, | 386 | TCA_BPF_NAME, |
387 | TCA_BPF_FLAGS, | ||
385 | __TCA_BPF_MAX, | 388 | __TCA_BPF_MAX, |
386 | }; | 389 | }; |
387 | 390 | ||
diff --git a/net/core/filter.c b/net/core/filter.c index 13079f03902e..971d6ba89758 100644 --- a/net/core/filter.c +++ b/net/core/filter.c | |||
@@ -1632,6 +1632,9 @@ static bool __is_valid_access(int off, int size, enum bpf_access_type type) | |||
1632 | static bool sk_filter_is_valid_access(int off, int size, | 1632 | static bool sk_filter_is_valid_access(int off, int size, |
1633 | enum bpf_access_type type) | 1633 | enum bpf_access_type type) |
1634 | { | 1634 | { |
1635 | if (off == offsetof(struct __sk_buff, tc_classid)) | ||
1636 | return false; | ||
1637 | |||
1635 | if (type == BPF_WRITE) { | 1638 | if (type == BPF_WRITE) { |
1636 | switch (off) { | 1639 | switch (off) { |
1637 | case offsetof(struct __sk_buff, cb[0]) ... | 1640 | case offsetof(struct __sk_buff, cb[0]) ... |
@@ -1648,6 +1651,9 @@ static bool sk_filter_is_valid_access(int off, int size, | |||
1648 | static bool tc_cls_act_is_valid_access(int off, int size, | 1651 | static bool tc_cls_act_is_valid_access(int off, int size, |
1649 | enum bpf_access_type type) | 1652 | enum bpf_access_type type) |
1650 | { | 1653 | { |
1654 | if (off == offsetof(struct __sk_buff, tc_classid)) | ||
1655 | return type == BPF_WRITE ? true : false; | ||
1656 | |||
1651 | if (type == BPF_WRITE) { | 1657 | if (type == BPF_WRITE) { |
1652 | switch (off) { | 1658 | switch (off) { |
1653 | case offsetof(struct __sk_buff, mark): | 1659 | case offsetof(struct __sk_buff, mark): |
@@ -1760,6 +1766,14 @@ static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg, | |||
1760 | *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, ctx_off); | 1766 | *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, ctx_off); |
1761 | break; | 1767 | break; |
1762 | 1768 | ||
1769 | case offsetof(struct __sk_buff, tc_classid): | ||
1770 | ctx_off -= offsetof(struct __sk_buff, tc_classid); | ||
1771 | ctx_off += offsetof(struct sk_buff, cb); | ||
1772 | ctx_off += offsetof(struct qdisc_skb_cb, tc_classid); | ||
1773 | WARN_ON(type != BPF_WRITE); | ||
1774 | *insn++ = BPF_STX_MEM(BPF_H, dst_reg, src_reg, ctx_off); | ||
1775 | break; | ||
1776 | |||
1763 | case offsetof(struct __sk_buff, tc_index): | 1777 | case offsetof(struct __sk_buff, tc_index): |
1764 | #ifdef CONFIG_NET_SCHED | 1778 | #ifdef CONFIG_NET_SCHED |
1765 | BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tc_index) != 2); | 1779 | BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tc_index) != 2); |
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index e5168f8b9640..77b0ef148256 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c | |||
@@ -38,6 +38,7 @@ struct cls_bpf_prog { | |||
38 | struct bpf_prog *filter; | 38 | struct bpf_prog *filter; |
39 | struct list_head link; | 39 | struct list_head link; |
40 | struct tcf_result res; | 40 | struct tcf_result res; |
41 | bool exts_integrated; | ||
41 | struct tcf_exts exts; | 42 | struct tcf_exts exts; |
42 | u32 handle; | 43 | u32 handle; |
43 | union { | 44 | union { |
@@ -52,6 +53,7 @@ struct cls_bpf_prog { | |||
52 | 53 | ||
53 | static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = { | 54 | static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = { |
54 | [TCA_BPF_CLASSID] = { .type = NLA_U32 }, | 55 | [TCA_BPF_CLASSID] = { .type = NLA_U32 }, |
56 | [TCA_BPF_FLAGS] = { .type = NLA_U32 }, | ||
55 | [TCA_BPF_FD] = { .type = NLA_U32 }, | 57 | [TCA_BPF_FD] = { .type = NLA_U32 }, |
56 | [TCA_BPF_NAME] = { .type = NLA_NUL_STRING, .len = CLS_BPF_NAME_LEN }, | 58 | [TCA_BPF_NAME] = { .type = NLA_NUL_STRING, .len = CLS_BPF_NAME_LEN }, |
57 | [TCA_BPF_OPS_LEN] = { .type = NLA_U16 }, | 59 | [TCA_BPF_OPS_LEN] = { .type = NLA_U16 }, |
@@ -59,6 +61,22 @@ static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = { | |||
59 | .len = sizeof(struct sock_filter) * BPF_MAXINSNS }, | 61 | .len = sizeof(struct sock_filter) * BPF_MAXINSNS }, |
60 | }; | 62 | }; |
61 | 63 | ||
64 | static int cls_bpf_exec_opcode(int code) | ||
65 | { | ||
66 | switch (code) { | ||
67 | case TC_ACT_OK: | ||
68 | case TC_ACT_RECLASSIFY: | ||
69 | case TC_ACT_SHOT: | ||
70 | case TC_ACT_PIPE: | ||
71 | case TC_ACT_STOLEN: | ||
72 | case TC_ACT_QUEUED: | ||
73 | case TC_ACT_UNSPEC: | ||
74 | return code; | ||
75 | default: | ||
76 | return TC_ACT_UNSPEC; | ||
77 | } | ||
78 | } | ||
79 | |||
62 | static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, | 80 | static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, |
63 | struct tcf_result *res) | 81 | struct tcf_result *res) |
64 | { | 82 | { |
@@ -79,6 +97,8 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, | |||
79 | list_for_each_entry_rcu(prog, &head->plist, link) { | 97 | list_for_each_entry_rcu(prog, &head->plist, link) { |
80 | int filter_res; | 98 | int filter_res; |
81 | 99 | ||
100 | qdisc_skb_cb(skb)->tc_classid = prog->res.classid; | ||
101 | |||
82 | if (at_ingress) { | 102 | if (at_ingress) { |
83 | /* It is safe to push/pull even if skb_shared() */ | 103 | /* It is safe to push/pull even if skb_shared() */ |
84 | __skb_push(skb, skb->mac_len); | 104 | __skb_push(skb, skb->mac_len); |
@@ -88,6 +108,16 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, | |||
88 | filter_res = BPF_PROG_RUN(prog->filter, skb); | 108 | filter_res = BPF_PROG_RUN(prog->filter, skb); |
89 | } | 109 | } |
90 | 110 | ||
111 | if (prog->exts_integrated) { | ||
112 | res->class = prog->res.class; | ||
113 | res->classid = qdisc_skb_cb(skb)->tc_classid; | ||
114 | |||
115 | ret = cls_bpf_exec_opcode(filter_res); | ||
116 | if (ret == TC_ACT_UNSPEC) | ||
117 | continue; | ||
118 | break; | ||
119 | } | ||
120 | |||
91 | if (filter_res == 0) | 121 | if (filter_res == 0) |
92 | continue; | 122 | continue; |
93 | 123 | ||
@@ -195,8 +225,7 @@ static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle) | |||
195 | return ret; | 225 | return ret; |
196 | } | 226 | } |
197 | 227 | ||
198 | static int cls_bpf_prog_from_ops(struct nlattr **tb, | 228 | static int cls_bpf_prog_from_ops(struct nlattr **tb, struct cls_bpf_prog *prog) |
199 | struct cls_bpf_prog *prog, u32 classid) | ||
200 | { | 229 | { |
201 | struct sock_filter *bpf_ops; | 230 | struct sock_filter *bpf_ops; |
202 | struct sock_fprog_kern fprog_tmp; | 231 | struct sock_fprog_kern fprog_tmp; |
@@ -230,15 +259,13 @@ static int cls_bpf_prog_from_ops(struct nlattr **tb, | |||
230 | prog->bpf_ops = bpf_ops; | 259 | prog->bpf_ops = bpf_ops; |
231 | prog->bpf_num_ops = bpf_num_ops; | 260 | prog->bpf_num_ops = bpf_num_ops; |
232 | prog->bpf_name = NULL; | 261 | prog->bpf_name = NULL; |
233 | |||
234 | prog->filter = fp; | 262 | prog->filter = fp; |
235 | prog->res.classid = classid; | ||
236 | 263 | ||
237 | return 0; | 264 | return 0; |
238 | } | 265 | } |
239 | 266 | ||
240 | static int cls_bpf_prog_from_efd(struct nlattr **tb, | 267 | static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, |
241 | struct cls_bpf_prog *prog, u32 classid) | 268 | const struct tcf_proto *tp) |
242 | { | 269 | { |
243 | struct bpf_prog *fp; | 270 | struct bpf_prog *fp; |
244 | char *name = NULL; | 271 | char *name = NULL; |
@@ -268,9 +295,7 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, | |||
268 | prog->bpf_ops = NULL; | 295 | prog->bpf_ops = NULL; |
269 | prog->bpf_fd = bpf_fd; | 296 | prog->bpf_fd = bpf_fd; |
270 | prog->bpf_name = name; | 297 | prog->bpf_name = name; |
271 | |||
272 | prog->filter = fp; | 298 | prog->filter = fp; |
273 | prog->res.classid = classid; | ||
274 | 299 | ||
275 | return 0; | 300 | return 0; |
276 | } | 301 | } |
@@ -280,8 +305,8 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, | |||
280 | unsigned long base, struct nlattr **tb, | 305 | unsigned long base, struct nlattr **tb, |
281 | struct nlattr *est, bool ovr) | 306 | struct nlattr *est, bool ovr) |
282 | { | 307 | { |
308 | bool is_bpf, is_ebpf, have_exts = false; | ||
283 | struct tcf_exts exts; | 309 | struct tcf_exts exts; |
284 | bool is_bpf, is_ebpf; | ||
285 | u32 classid; | 310 | u32 classid; |
286 | int ret; | 311 | int ret; |
287 | 312 | ||
@@ -298,9 +323,22 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, | |||
298 | return ret; | 323 | return ret; |
299 | 324 | ||
300 | classid = nla_get_u32(tb[TCA_BPF_CLASSID]); | 325 | classid = nla_get_u32(tb[TCA_BPF_CLASSID]); |
326 | if (tb[TCA_BPF_FLAGS]) { | ||
327 | u32 bpf_flags = nla_get_u32(tb[TCA_BPF_FLAGS]); | ||
328 | |||
329 | if (bpf_flags & ~TCA_BPF_FLAG_ACT_DIRECT) { | ||
330 | tcf_exts_destroy(&exts); | ||
331 | return -EINVAL; | ||
332 | } | ||
333 | |||
334 | have_exts = bpf_flags & TCA_BPF_FLAG_ACT_DIRECT; | ||
335 | } | ||
336 | |||
337 | prog->res.classid = classid; | ||
338 | prog->exts_integrated = have_exts; | ||
301 | 339 | ||
302 | ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog, classid) : | 340 | ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog) : |
303 | cls_bpf_prog_from_efd(tb, prog, classid); | 341 | cls_bpf_prog_from_efd(tb, prog, tp); |
304 | if (ret < 0) { | 342 | if (ret < 0) { |
305 | tcf_exts_destroy(&exts); | 343 | tcf_exts_destroy(&exts); |
306 | return ret; | 344 | return ret; |