xref: /openbmc/linux/net/sched/act_bpf.c (revision 034f90b3)
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 
24 static 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, filter_res;
29 
30 	spin_lock(&b->tcf_lock);
31 
32 	b->tcf_tm.lastuse = jiffies;
33 	bstats_update(&b->tcf_bstats, skb);
34 
35 	filter_res = BPF_PROG_RUN(b->filter, skb);
36 
37 	/* A BPF program may overwrite the default action opcode.
38 	 * Similarly as in cls_bpf, if filter_res == -1 we use the
39 	 * default action specified from tc.
40 	 *
41 	 * In case a different well-known TC_ACT opcode has been
42 	 * returned, it will overwrite the default one.
43 	 *
44 	 * For everything else that is unkown, TC_ACT_UNSPEC is
45 	 * returned.
46 	 */
47 	switch (filter_res) {
48 	case TC_ACT_PIPE:
49 	case TC_ACT_RECLASSIFY:
50 	case TC_ACT_OK:
51 		action = filter_res;
52 		break;
53 	case TC_ACT_SHOT:
54 		action = filter_res;
55 		b->tcf_qstats.drops++;
56 		break;
57 	case TC_ACT_UNSPEC:
58 		action = b->tcf_action;
59 		break;
60 	default:
61 		action = TC_ACT_UNSPEC;
62 		break;
63 	}
64 
65 	spin_unlock(&b->tcf_lock);
66 	return action;
67 }
68 
69 static int tcf_bpf_dump(struct sk_buff *skb, struct tc_action *a,
70 			int bind, int ref)
71 {
72 	unsigned char *tp = skb_tail_pointer(skb);
73 	struct tcf_bpf *b = a->priv;
74 	struct tc_act_bpf opt = {
75 		.index    = b->tcf_index,
76 		.refcnt   = b->tcf_refcnt - ref,
77 		.bindcnt  = b->tcf_bindcnt - bind,
78 		.action   = b->tcf_action,
79 	};
80 	struct tcf_t t;
81 	struct nlattr *nla;
82 
83 	if (nla_put(skb, TCA_ACT_BPF_PARMS, sizeof(opt), &opt))
84 		goto nla_put_failure;
85 
86 	if (nla_put_u16(skb, TCA_ACT_BPF_OPS_LEN, b->bpf_num_ops))
87 		goto nla_put_failure;
88 
89 	nla = nla_reserve(skb, TCA_ACT_BPF_OPS, b->bpf_num_ops *
90 			  sizeof(struct sock_filter));
91 	if (!nla)
92 		goto nla_put_failure;
93 
94 	memcpy(nla_data(nla), b->bpf_ops, nla_len(nla));
95 
96 	t.install = jiffies_to_clock_t(jiffies - b->tcf_tm.install);
97 	t.lastuse = jiffies_to_clock_t(jiffies - b->tcf_tm.lastuse);
98 	t.expires = jiffies_to_clock_t(b->tcf_tm.expires);
99 	if (nla_put(skb, TCA_ACT_BPF_TM, sizeof(t), &t))
100 		goto nla_put_failure;
101 	return skb->len;
102 
103 nla_put_failure:
104 	nlmsg_trim(skb, tp);
105 	return -1;
106 }
107 
108 static const struct nla_policy act_bpf_policy[TCA_ACT_BPF_MAX + 1] = {
109 	[TCA_ACT_BPF_PARMS]	= { .len = sizeof(struct tc_act_bpf) },
110 	[TCA_ACT_BPF_OPS_LEN]	= { .type = NLA_U16 },
111 	[TCA_ACT_BPF_OPS]	= { .type = NLA_BINARY,
112 				    .len = sizeof(struct sock_filter) * BPF_MAXINSNS },
113 };
114 
115 static int tcf_bpf_init(struct net *net, struct nlattr *nla,
116 			struct nlattr *est, struct tc_action *a,
117 			int ovr, int bind)
118 {
119 	struct nlattr *tb[TCA_ACT_BPF_MAX + 1];
120 	struct tc_act_bpf *parm;
121 	struct tcf_bpf *b;
122 	u16 bpf_size, bpf_num_ops;
123 	struct sock_filter *bpf_ops;
124 	struct sock_fprog_kern tmp;
125 	struct bpf_prog *fp;
126 	int ret;
127 
128 	if (!nla)
129 		return -EINVAL;
130 
131 	ret = nla_parse_nested(tb, TCA_ACT_BPF_MAX, nla, act_bpf_policy);
132 	if (ret < 0)
133 		return ret;
134 
135 	if (!tb[TCA_ACT_BPF_PARMS] ||
136 	    !tb[TCA_ACT_BPF_OPS_LEN] || !tb[TCA_ACT_BPF_OPS])
137 		return -EINVAL;
138 	parm = nla_data(tb[TCA_ACT_BPF_PARMS]);
139 
140 	bpf_num_ops = nla_get_u16(tb[TCA_ACT_BPF_OPS_LEN]);
141 	if (bpf_num_ops	> BPF_MAXINSNS || bpf_num_ops == 0)
142 		return -EINVAL;
143 
144 	bpf_size = bpf_num_ops * sizeof(*bpf_ops);
145 	if (bpf_size != nla_len(tb[TCA_ACT_BPF_OPS]))
146 		return -EINVAL;
147 
148 	bpf_ops = kzalloc(bpf_size, GFP_KERNEL);
149 	if (!bpf_ops)
150 		return -ENOMEM;
151 
152 	memcpy(bpf_ops, nla_data(tb[TCA_ACT_BPF_OPS]), bpf_size);
153 
154 	tmp.len = bpf_num_ops;
155 	tmp.filter = bpf_ops;
156 
157 	ret = bpf_prog_create(&fp, &tmp);
158 	if (ret)
159 		goto free_bpf_ops;
160 
161 	if (!tcf_hash_check(parm->index, a, bind)) {
162 		ret = tcf_hash_create(parm->index, est, a, sizeof(*b), bind);
163 		if (ret)
164 			goto destroy_fp;
165 
166 		ret = ACT_P_CREATED;
167 	} else {
168 		if (bind)
169 			goto destroy_fp;
170 		tcf_hash_release(a, bind);
171 		if (!ovr) {
172 			ret = -EEXIST;
173 			goto destroy_fp;
174 		}
175 	}
176 
177 	b = to_bpf(a);
178 	spin_lock_bh(&b->tcf_lock);
179 	b->tcf_action = parm->action;
180 	b->bpf_num_ops = bpf_num_ops;
181 	b->bpf_ops = bpf_ops;
182 	b->filter = fp;
183 	spin_unlock_bh(&b->tcf_lock);
184 
185 	if (ret == ACT_P_CREATED)
186 		tcf_hash_insert(a);
187 	return ret;
188 
189 destroy_fp:
190 	bpf_prog_destroy(fp);
191 free_bpf_ops:
192 	kfree(bpf_ops);
193 	return ret;
194 }
195 
196 static void tcf_bpf_cleanup(struct tc_action *a, int bind)
197 {
198 	struct tcf_bpf *b = a->priv;
199 
200 	bpf_prog_destroy(b->filter);
201 }
202 
203 static struct tc_action_ops act_bpf_ops = {
204 	.kind =		"bpf",
205 	.type =		TCA_ACT_BPF,
206 	.owner =	THIS_MODULE,
207 	.act =		tcf_bpf,
208 	.dump =		tcf_bpf_dump,
209 	.cleanup =	tcf_bpf_cleanup,
210 	.init =		tcf_bpf_init,
211 };
212 
213 static int __init bpf_init_module(void)
214 {
215 	return tcf_register_action(&act_bpf_ops, BPF_TAB_MASK);
216 }
217 
218 static void __exit bpf_cleanup_module(void)
219 {
220 	tcf_unregister_action(&act_bpf_ops);
221 }
222 
223 module_init(bpf_init_module);
224 module_exit(bpf_cleanup_module);
225 
226 MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>");
227 MODULE_DESCRIPTION("TC BPF based action");
228 MODULE_LICENSE("GPL v2");
229