Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/sched/cls_fw.c
15112 views
1
/*
2
* net/sched/cls_fw.c Classifier mapping ipchains' fwmark to traffic class.
3
*
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License
6
* as published by the Free Software Foundation; either version
7
* 2 of the License, or (at your option) any later version.
8
*
9
* Authors: Alexey Kuznetsov, <[email protected]>
10
*
11
* Changes:
12
* Karlis Peisenieks <[email protected]> : 990415 : fw_walk off by one
13
* Karlis Peisenieks <[email protected]> : 990415 : fw_delete killed all the filter (and kernel).
14
* Alex <[email protected]> : 2004xxyy: Added Action extension
15
*
16
* JHS: We should remove the CONFIG_NET_CLS_IND from here
17
* eventually when the meta match extension is made available
18
*
19
*/
20
21
#include <linux/module.h>
22
#include <linux/slab.h>
23
#include <linux/types.h>
24
#include <linux/kernel.h>
25
#include <linux/string.h>
26
#include <linux/errno.h>
27
#include <linux/skbuff.h>
28
#include <net/netlink.h>
29
#include <net/act_api.h>
30
#include <net/pkt_cls.h>
31
32
#define HTSIZE (PAGE_SIZE/sizeof(struct fw_filter *))
33
34
struct fw_head {
35
struct fw_filter *ht[HTSIZE];
36
u32 mask;
37
};
38
39
struct fw_filter {
40
struct fw_filter *next;
41
u32 id;
42
struct tcf_result res;
43
#ifdef CONFIG_NET_CLS_IND
44
char indev[IFNAMSIZ];
45
#endif /* CONFIG_NET_CLS_IND */
46
struct tcf_exts exts;
47
};
48
49
static const struct tcf_ext_map fw_ext_map = {
50
.action = TCA_FW_ACT,
51
.police = TCA_FW_POLICE
52
};
53
54
static inline int fw_hash(u32 handle)
55
{
56
if (HTSIZE == 4096)
57
return ((handle >> 24) & 0xFFF) ^
58
((handle >> 12) & 0xFFF) ^
59
(handle & 0xFFF);
60
else if (HTSIZE == 2048)
61
return ((handle >> 22) & 0x7FF) ^
62
((handle >> 11) & 0x7FF) ^
63
(handle & 0x7FF);
64
else if (HTSIZE == 1024)
65
return ((handle >> 20) & 0x3FF) ^
66
((handle >> 10) & 0x3FF) ^
67
(handle & 0x3FF);
68
else if (HTSIZE == 512)
69
return (handle >> 27) ^
70
((handle >> 18) & 0x1FF) ^
71
((handle >> 9) & 0x1FF) ^
72
(handle & 0x1FF);
73
else if (HTSIZE == 256) {
74
u8 *t = (u8 *) &handle;
75
return t[0] ^ t[1] ^ t[2] ^ t[3];
76
} else
77
return handle & (HTSIZE - 1);
78
}
79
80
static int fw_classify(struct sk_buff *skb, struct tcf_proto *tp,
81
struct tcf_result *res)
82
{
83
struct fw_head *head = (struct fw_head *)tp->root;
84
struct fw_filter *f;
85
int r;
86
u32 id = skb->mark;
87
88
if (head != NULL) {
89
id &= head->mask;
90
for (f = head->ht[fw_hash(id)]; f; f = f->next) {
91
if (f->id == id) {
92
*res = f->res;
93
#ifdef CONFIG_NET_CLS_IND
94
if (!tcf_match_indev(skb, f->indev))
95
continue;
96
#endif /* CONFIG_NET_CLS_IND */
97
r = tcf_exts_exec(skb, &f->exts, res);
98
if (r < 0)
99
continue;
100
101
return r;
102
}
103
}
104
} else {
105
/* old method */
106
if (id && (TC_H_MAJ(id) == 0 ||
107
!(TC_H_MAJ(id ^ tp->q->handle)))) {
108
res->classid = id;
109
res->class = 0;
110
return 0;
111
}
112
}
113
114
return -1;
115
}
116
117
static unsigned long fw_get(struct tcf_proto *tp, u32 handle)
118
{
119
struct fw_head *head = (struct fw_head *)tp->root;
120
struct fw_filter *f;
121
122
if (head == NULL)
123
return 0;
124
125
for (f = head->ht[fw_hash(handle)]; f; f = f->next) {
126
if (f->id == handle)
127
return (unsigned long)f;
128
}
129
return 0;
130
}
131
132
static void fw_put(struct tcf_proto *tp, unsigned long f)
133
{
134
}
135
136
static int fw_init(struct tcf_proto *tp)
137
{
138
return 0;
139
}
140
141
static void fw_delete_filter(struct tcf_proto *tp, struct fw_filter *f)
142
{
143
tcf_unbind_filter(tp, &f->res);
144
tcf_exts_destroy(tp, &f->exts);
145
kfree(f);
146
}
147
148
static void fw_destroy(struct tcf_proto *tp)
149
{
150
struct fw_head *head = tp->root;
151
struct fw_filter *f;
152
int h;
153
154
if (head == NULL)
155
return;
156
157
for (h = 0; h < HTSIZE; h++) {
158
while ((f = head->ht[h]) != NULL) {
159
head->ht[h] = f->next;
160
fw_delete_filter(tp, f);
161
}
162
}
163
kfree(head);
164
}
165
166
static int fw_delete(struct tcf_proto *tp, unsigned long arg)
167
{
168
struct fw_head *head = (struct fw_head *)tp->root;
169
struct fw_filter *f = (struct fw_filter *)arg;
170
struct fw_filter **fp;
171
172
if (head == NULL || f == NULL)
173
goto out;
174
175
for (fp = &head->ht[fw_hash(f->id)]; *fp; fp = &(*fp)->next) {
176
if (*fp == f) {
177
tcf_tree_lock(tp);
178
*fp = f->next;
179
tcf_tree_unlock(tp);
180
fw_delete_filter(tp, f);
181
return 0;
182
}
183
}
184
out:
185
return -EINVAL;
186
}
187
188
static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = {
189
[TCA_FW_CLASSID] = { .type = NLA_U32 },
190
[TCA_FW_INDEV] = { .type = NLA_STRING, .len = IFNAMSIZ },
191
[TCA_FW_MASK] = { .type = NLA_U32 },
192
};
193
194
static int
195
fw_change_attrs(struct tcf_proto *tp, struct fw_filter *f,
196
struct nlattr **tb, struct nlattr **tca, unsigned long base)
197
{
198
struct fw_head *head = (struct fw_head *)tp->root;
199
struct tcf_exts e;
200
u32 mask;
201
int err;
202
203
err = tcf_exts_validate(tp, tb, tca[TCA_RATE], &e, &fw_ext_map);
204
if (err < 0)
205
return err;
206
207
err = -EINVAL;
208
if (tb[TCA_FW_CLASSID]) {
209
f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]);
210
tcf_bind_filter(tp, &f->res, base);
211
}
212
213
#ifdef CONFIG_NET_CLS_IND
214
if (tb[TCA_FW_INDEV]) {
215
err = tcf_change_indev(tp, f->indev, tb[TCA_FW_INDEV]);
216
if (err < 0)
217
goto errout;
218
}
219
#endif /* CONFIG_NET_CLS_IND */
220
221
if (tb[TCA_FW_MASK]) {
222
mask = nla_get_u32(tb[TCA_FW_MASK]);
223
if (mask != head->mask)
224
goto errout;
225
} else if (head->mask != 0xFFFFFFFF)
226
goto errout;
227
228
tcf_exts_change(tp, &f->exts, &e);
229
230
return 0;
231
errout:
232
tcf_exts_destroy(tp, &e);
233
return err;
234
}
235
236
static int fw_change(struct tcf_proto *tp, unsigned long base,
237
u32 handle,
238
struct nlattr **tca,
239
unsigned long *arg)
240
{
241
struct fw_head *head = (struct fw_head *)tp->root;
242
struct fw_filter *f = (struct fw_filter *) *arg;
243
struct nlattr *opt = tca[TCA_OPTIONS];
244
struct nlattr *tb[TCA_FW_MAX + 1];
245
int err;
246
247
if (!opt)
248
return handle ? -EINVAL : 0;
249
250
err = nla_parse_nested(tb, TCA_FW_MAX, opt, fw_policy);
251
if (err < 0)
252
return err;
253
254
if (f != NULL) {
255
if (f->id != handle && handle)
256
return -EINVAL;
257
return fw_change_attrs(tp, f, tb, tca, base);
258
}
259
260
if (!handle)
261
return -EINVAL;
262
263
if (head == NULL) {
264
u32 mask = 0xFFFFFFFF;
265
if (tb[TCA_FW_MASK])
266
mask = nla_get_u32(tb[TCA_FW_MASK]);
267
268
head = kzalloc(sizeof(struct fw_head), GFP_KERNEL);
269
if (head == NULL)
270
return -ENOBUFS;
271
head->mask = mask;
272
273
tcf_tree_lock(tp);
274
tp->root = head;
275
tcf_tree_unlock(tp);
276
}
277
278
f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL);
279
if (f == NULL)
280
return -ENOBUFS;
281
282
f->id = handle;
283
284
err = fw_change_attrs(tp, f, tb, tca, base);
285
if (err < 0)
286
goto errout;
287
288
f->next = head->ht[fw_hash(handle)];
289
tcf_tree_lock(tp);
290
head->ht[fw_hash(handle)] = f;
291
tcf_tree_unlock(tp);
292
293
*arg = (unsigned long)f;
294
return 0;
295
296
errout:
297
kfree(f);
298
return err;
299
}
300
301
static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
302
{
303
struct fw_head *head = (struct fw_head *)tp->root;
304
int h;
305
306
if (head == NULL)
307
arg->stop = 1;
308
309
if (arg->stop)
310
return;
311
312
for (h = 0; h < HTSIZE; h++) {
313
struct fw_filter *f;
314
315
for (f = head->ht[h]; f; f = f->next) {
316
if (arg->count < arg->skip) {
317
arg->count++;
318
continue;
319
}
320
if (arg->fn(tp, (unsigned long)f, arg) < 0) {
321
arg->stop = 1;
322
return;
323
}
324
arg->count++;
325
}
326
}
327
}
328
329
static int fw_dump(struct tcf_proto *tp, unsigned long fh,
330
struct sk_buff *skb, struct tcmsg *t)
331
{
332
struct fw_head *head = (struct fw_head *)tp->root;
333
struct fw_filter *f = (struct fw_filter *)fh;
334
unsigned char *b = skb_tail_pointer(skb);
335
struct nlattr *nest;
336
337
if (f == NULL)
338
return skb->len;
339
340
t->tcm_handle = f->id;
341
342
if (!f->res.classid && !tcf_exts_is_available(&f->exts))
343
return skb->len;
344
345
nest = nla_nest_start(skb, TCA_OPTIONS);
346
if (nest == NULL)
347
goto nla_put_failure;
348
349
if (f->res.classid)
350
NLA_PUT_U32(skb, TCA_FW_CLASSID, f->res.classid);
351
#ifdef CONFIG_NET_CLS_IND
352
if (strlen(f->indev))
353
NLA_PUT_STRING(skb, TCA_FW_INDEV, f->indev);
354
#endif /* CONFIG_NET_CLS_IND */
355
if (head->mask != 0xFFFFFFFF)
356
NLA_PUT_U32(skb, TCA_FW_MASK, head->mask);
357
358
if (tcf_exts_dump(skb, &f->exts, &fw_ext_map) < 0)
359
goto nla_put_failure;
360
361
nla_nest_end(skb, nest);
362
363
if (tcf_exts_dump_stats(skb, &f->exts, &fw_ext_map) < 0)
364
goto nla_put_failure;
365
366
return skb->len;
367
368
nla_put_failure:
369
nlmsg_trim(skb, b);
370
return -1;
371
}
372
373
static struct tcf_proto_ops cls_fw_ops __read_mostly = {
374
.kind = "fw",
375
.classify = fw_classify,
376
.init = fw_init,
377
.destroy = fw_destroy,
378
.get = fw_get,
379
.put = fw_put,
380
.change = fw_change,
381
.delete = fw_delete,
382
.walk = fw_walk,
383
.dump = fw_dump,
384
.owner = THIS_MODULE,
385
};
386
387
static int __init init_fw(void)
388
{
389
return register_tcf_proto_ops(&cls_fw_ops);
390
}
391
392
static void __exit exit_fw(void)
393
{
394
unregister_tcf_proto_ops(&cls_fw_ops);
395
}
396
397
module_init(init_fw)
398
module_exit(exit_fw)
399
MODULE_LICENSE("GPL");
400
401