aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/tc_act/tc_bpf.h25
-rw-r--r--include/uapi/linux/tc_act/Kbuild1
-rw-r--r--include/uapi/linux/tc_act/tc_bpf.h31
-rw-r--r--net/sched/Kconfig12
-rw-r--r--net/sched/Makefile1
-rw-r--r--net/sched/act_bpf.c205
6 files changed, 275 insertions, 0 deletions
diff --git a/include/net/tc_act/tc_bpf.h b/include/net/tc_act/tc_bpf.h
new file mode 100644
index 000000000000..86a070ffc930
--- /dev/null
+++ b/include/net/tc_act/tc_bpf.h
@@ -0,0 +1,25 @@
1/*
2 * Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10#ifndef __NET_TC_BPF_H
11#define __NET_TC_BPF_H
12
13#include <linux/filter.h>
14#include <net/act_api.h>
15
16struct tcf_bpf {
17 struct tcf_common common;
18 struct bpf_prog *filter;
19 struct sock_filter *bpf_ops;
20 u16 bpf_num_ops;
21};
22#define to_bpf(a) \
23 container_of(a->priv, struct tcf_bpf, common)
24
25#endif /* __NET_TC_BPF_H */
diff --git a/include/uapi/linux/tc_act/Kbuild b/include/uapi/linux/tc_act/Kbuild
index b057da2b87a4..19d5219b0b99 100644
--- a/include/uapi/linux/tc_act/Kbuild
+++ b/include/uapi/linux/tc_act/Kbuild
@@ -8,3 +8,4 @@ header-y += tc_nat.h
8header-y += tc_pedit.h 8header-y += tc_pedit.h
9header-y += tc_skbedit.h 9header-y += tc_skbedit.h
10header-y += tc_vlan.h 10header-y += tc_vlan.h
11header-y += tc_bpf.h
diff --git a/include/uapi/linux/tc_act/tc_bpf.h b/include/uapi/linux/tc_act/tc_bpf.h
new file mode 100644
index 000000000000..5288bd77e63b
--- /dev/null
+++ b/include/uapi/linux/tc_act/tc_bpf.h
@@ -0,0 +1,31 @@
1/*
2 * Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10#ifndef __LINUX_TC_BPF_H
11#define __LINUX_TC_BPF_H
12
13#include <linux/pkt_cls.h>
14
15#define TCA_ACT_BPF 13
16
17struct tc_act_bpf {
18 tc_gen;
19};
20
21enum {
22 TCA_ACT_BPF_UNSPEC,
23 TCA_ACT_BPF_TM,
24 TCA_ACT_BPF_PARMS,
25 TCA_ACT_BPF_OPS_LEN,
26 TCA_ACT_BPF_OPS,
27 __TCA_ACT_BPF_MAX,
28};
29#define TCA_ACT_BPF_MAX (__TCA_ACT_BPF_MAX - 1)
30
31#endif
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index c54c9d9d1ffb..466943551581 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -698,6 +698,18 @@ config NET_ACT_VLAN
698 To compile this code as a module, choose M here: the 698 To compile this code as a module, choose M here: the
699 module will be called act_vlan. 699 module will be called act_vlan.
700 700
701config NET_ACT_BPF
702 tristate "BPF based action"
703 depends on NET_CLS_ACT
704 ---help---
705 Say Y here to execute BPF code on packets. The BPF code will decide
706 if the packet should be dropped or not.
707
708 If unsure, say N.
709
710 To compile this code as a module, choose M here: the
711 module will be called act_bpf.
712
701config NET_CLS_IND 713config NET_CLS_IND
702 bool "Incoming device classification" 714 bool "Incoming device classification"
703 depends on NET_CLS_U32 || NET_CLS_FW 715 depends on NET_CLS_U32 || NET_CLS_FW
diff --git a/net/sched/Makefile b/net/sched/Makefile
index 679f24ae7f93..7ca2b4e76312 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_NET_ACT_SIMP) += act_simple.o
17obj-$(CONFIG_NET_ACT_SKBEDIT) += act_skbedit.o 17obj-$(CONFIG_NET_ACT_SKBEDIT) += act_skbedit.o
18obj-$(CONFIG_NET_ACT_CSUM) += act_csum.o 18obj-$(CONFIG_NET_ACT_CSUM) += act_csum.o
19obj-$(CONFIG_NET_ACT_VLAN) += act_vlan.o 19obj-$(CONFIG_NET_ACT_VLAN) += act_vlan.o
20obj-$(CONFIG_NET_ACT_BPF) += act_bpf.o
20obj-$(CONFIG_NET_SCH_FIFO) += sch_fifo.o 21obj-$(CONFIG_NET_SCH_FIFO) += sch_fifo.o
21obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o 22obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o
22obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o 23obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o
diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
new file mode 100644
index 000000000000..1bd257e473a9
--- /dev/null
+++ b/net/sched/act_bpf.c
@@ -0,0 +1,205 @@
1/*
2 * Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10#include <linux/module.h>
11#include <linux/init.h>
12#include <linux/kernel.h>
13#include <linux/skbuff.h>
14#include <linux/rtnetlink.h>
15#include <linux/filter.h>
16#include <net/netlink.h>
17#include <net/pkt_sched.h>
18
19#include <linux/tc_act/tc_bpf.h>
20#include <net/tc_act/tc_bpf.h>
21
22#define BPF_TAB_MASK 15
23
24static int tcf_bpf(struct sk_buff *skb, const struct tc_action *a,
25 struct tcf_result *res)
26{
27 struct tcf_bpf *b = a->priv;
28 int action;
29 int filter_res;
30
31 spin_lock(&b->tcf_lock);
32 b->tcf_tm.lastuse = jiffies;
33 bstats_update(&b->tcf_bstats, skb);
34 action = b->tcf_action;
35
36 filter_res = BPF_PROG_RUN(b->filter, skb);
37 if (filter_res == 0) {
38 /* Return code 0 from the BPF program
39 * is being interpreted as a drop here.
40 */
41 action = TC_ACT_SHOT;
42 b->tcf_qstats.drops++;
43 }
44
45 spin_unlock(&b->tcf_lock);
46 return action;
47}
48
49static int tcf_bpf_dump(struct sk_buff *skb, struct tc_action *a,
50 int bind, int ref)
51{
52 unsigned char *tp = skb_tail_pointer(skb);
53 struct tcf_bpf *b = a->priv;
54 struct tc_act_bpf opt = {
55 .index = b->tcf_index,
56 .refcnt = b->tcf_refcnt - ref,
57 .bindcnt = b->tcf_bindcnt - bind,
58 .action = b->tcf_action,
59 };
60 struct tcf_t t;
61 struct nlattr *nla;
62
63 if (nla_put(skb, TCA_ACT_BPF_PARMS, sizeof(opt), &opt))
64 goto nla_put_failure;
65
66 if (nla_put_u16(skb, TCA_ACT_BPF_OPS_LEN, b->bpf_num_ops))
67 goto nla_put_failure;
68
69 nla = nla_reserve(skb, TCA_ACT_BPF_OPS, b->bpf_num_ops *
70 sizeof(struct sock_filter));
71 if (!nla)
72 goto nla_put_failure;
73
74 memcpy(nla_data(nla), b->bpf_ops, nla_len(nla));
75
76 t.install = jiffies_to_clock_t(jiffies - b->tcf_tm.install);
77 t.lastuse = jiffies_to_clock_t(jiffies - b->tcf_tm.lastuse);
78 t.expires = jiffies_to_clock_t(b->tcf_tm.expires);
79 if (nla_put(skb, TCA_ACT_BPF_TM, sizeof(t), &t))
80 goto nla_put_failure;
81 return skb->len;
82
83nla_put_failure:
84 nlmsg_trim(skb, tp);
85 return -1;
86}
87
88static const struct nla_policy act_bpf_policy[TCA_ACT_BPF_MAX + 1] = {
89 [TCA_ACT_BPF_PARMS] = { .len = sizeof(struct tc_act_bpf) },
90 [TCA_ACT_BPF_OPS_LEN] = { .type = NLA_U16 },
91 [TCA_ACT_BPF_OPS] = { .type = NLA_BINARY,
92 .len = sizeof(struct sock_filter) * BPF_MAXINSNS },
93};
94
95static int tcf_bpf_init(struct net *net, struct nlattr *nla,
96 struct nlattr *est, struct tc_action *a,
97 int ovr, int bind)
98{
99 struct nlattr *tb[TCA_ACT_BPF_MAX + 1];
100 struct tc_act_bpf *parm;
101 struct tcf_bpf *b;
102 u16 bpf_size, bpf_num_ops;
103 struct sock_filter *bpf_ops;
104 struct sock_fprog_kern tmp;
105 struct bpf_prog *fp;
106 int ret;
107
108 if (!nla)
109 return -EINVAL;
110
111 ret = nla_parse_nested(tb, TCA_ACT_BPF_MAX, nla, act_bpf_policy);
112 if (ret < 0)
113 return ret;
114
115 if (!tb[TCA_ACT_BPF_PARMS] ||
116 !tb[TCA_ACT_BPF_OPS_LEN] || !tb[TCA_ACT_BPF_OPS])
117 return -EINVAL;
118 parm = nla_data(tb[TCA_ACT_BPF_PARMS]);
119
120 bpf_num_ops = nla_get_u16(tb[TCA_ACT_BPF_OPS_LEN]);
121 if (bpf_num_ops > BPF_MAXINSNS || bpf_num_ops == 0)
122 return -EINVAL;
123
124 bpf_size = bpf_num_ops * sizeof(*bpf_ops);
125 bpf_ops = kzalloc(bpf_size, GFP_KERNEL);
126 if (!bpf_ops)
127 return -ENOMEM;
128
129 memcpy(bpf_ops, nla_data(tb[TCA_ACT_BPF_OPS]), bpf_size);
130
131 tmp.len = bpf_num_ops;
132 tmp.filter = bpf_ops;
133
134 ret = bpf_prog_create(&fp, &tmp);
135 if (ret)
136 goto free_bpf_ops;
137
138 if (!tcf_hash_check(parm->index, a, bind)) {
139 ret = tcf_hash_create(parm->index, est, a, sizeof(*b), bind);
140 if (ret)
141 goto destroy_fp;
142
143 ret = ACT_P_CREATED;
144 } else {
145 if (bind)
146 goto destroy_fp;
147 tcf_hash_release(a, bind);
148 if (!ovr) {
149 ret = -EEXIST;
150 goto destroy_fp;
151 }
152 }
153
154 b = to_bpf(a);
155 spin_lock_bh(&b->tcf_lock);
156 b->tcf_action = parm->action;
157 b->bpf_num_ops = bpf_num_ops;
158 b->bpf_ops = bpf_ops;
159 b->filter = fp;
160 spin_unlock_bh(&b->tcf_lock);
161
162 if (ret == ACT_P_CREATED)
163 tcf_hash_insert(a);
164 return ret;
165
166destroy_fp:
167 bpf_prog_destroy(fp);
168free_bpf_ops:
169 kfree(bpf_ops);
170 return ret;
171}
172
173static void tcf_bpf_cleanup(struct tc_action *a, int bind)
174{
175 struct tcf_bpf *b = a->priv;
176
177 bpf_prog_destroy(b->filter);
178}
179
180static struct tc_action_ops act_bpf_ops = {
181 .kind = "bpf",
182 .type = TCA_ACT_BPF,
183 .owner = THIS_MODULE,
184 .act = tcf_bpf,
185 .dump = tcf_bpf_dump,
186 .cleanup = tcf_bpf_cleanup,
187 .init = tcf_bpf_init,
188};
189
190static int __init bpf_init_module(void)
191{
192 return tcf_register_action(&act_bpf_ops, BPF_TAB_MASK);
193}
194
195static void __exit bpf_cleanup_module(void)
196{
197 tcf_unregister_action(&act_bpf_ops);
198}
199
200module_init(bpf_init_module);
201module_exit(bpf_cleanup_module);
202
203MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>");
204MODULE_DESCRIPTION("TC BPF based action");
205MODULE_LICENSE("GPL v2");