Skip to content

Commit d23b8ad

Browse files
jpirkodavem330
authored andcommitted
tc: add BPF based action
This action provides a possibility to exec custom BPF code. Signed-off-by: Jiri Pirko <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 02dba43 commit d23b8ad

File tree

6 files changed

+275
-0
lines changed

6 files changed

+275
-0
lines changed

include/net/tc_act/tc_bpf.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2015 Jiri Pirko <[email protected]>
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+
16+
struct 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 */

include/uapi/linux/tc_act/Kbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ header-y += tc_nat.h
88
header-y += tc_pedit.h
99
header-y += tc_skbedit.h
1010
header-y += tc_vlan.h
11+
header-y += tc_bpf.h

include/uapi/linux/tc_act/tc_bpf.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (c) 2015 Jiri Pirko <[email protected]>
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+
17+
struct tc_act_bpf {
18+
tc_gen;
19+
};
20+
21+
enum {
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

net/sched/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,18 @@ config NET_ACT_VLAN
698698
To compile this code as a module, choose M here: the
699699
module will be called act_vlan.
700700

701+
config 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+
701713
config NET_CLS_IND
702714
bool "Incoming device classification"
703715
depends on NET_CLS_U32 || NET_CLS_FW

net/sched/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ obj-$(CONFIG_NET_ACT_SIMP) += act_simple.o
1717
obj-$(CONFIG_NET_ACT_SKBEDIT) += act_skbedit.o
1818
obj-$(CONFIG_NET_ACT_CSUM) += act_csum.o
1919
obj-$(CONFIG_NET_ACT_VLAN) += act_vlan.o
20+
obj-$(CONFIG_NET_ACT_BPF) += act_bpf.o
2021
obj-$(CONFIG_NET_SCH_FIFO) += sch_fifo.o
2122
obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o
2223
obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o

net/sched/act_bpf.c

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
* Copyright (c) 2015 Jiri Pirko <[email protected]>
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;
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+
49+
static 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+
83+
nla_put_failure:
84+
nlmsg_trim(skb, tp);
85+
return -1;
86+
}
87+
88+
static 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+
95+
static 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+
166+
destroy_fp:
167+
bpf_prog_destroy(fp);
168+
free_bpf_ops:
169+
kfree(bpf_ops);
170+
return ret;
171+
}
172+
173+
static 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+
180+
static 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+
190+
static int __init bpf_init_module(void)
191+
{
192+
return tcf_register_action(&act_bpf_ops, BPF_TAB_MASK);
193+
}
194+
195+
static void __exit bpf_cleanup_module(void)
196+
{
197+
tcf_unregister_action(&act_bpf_ops);
198+
}
199+
200+
module_init(bpf_init_module);
201+
module_exit(bpf_cleanup_module);
202+
203+
MODULE_AUTHOR("Jiri Pirko <[email protected]>");
204+
MODULE_DESCRIPTION("TC BPF based action");
205+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)