Blame view
net/sched/cls_fw.c
9.18 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* * net/sched/cls_fw.c Classifier mapping ipchains' fwmark to traffic class. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * * Changes: * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_walk off by one * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_delete killed all the filter (and kernel). * Alex <alex@pilotsoft.com> : 2004xxyy: Added Action extension * * JHS: We should remove the CONFIG_NET_CLS_IND from here * eventually when the meta match extension is made available * */ |
1da177e4c Linux-2.6.12-rc2 |
20 |
#include <linux/module.h> |
5a0e3ad6a include cleanup: ... |
21 |
#include <linux/slab.h> |
1da177e4c Linux-2.6.12-rc2 |
22 23 |
#include <linux/types.h> #include <linux/kernel.h> |
1da177e4c Linux-2.6.12-rc2 |
24 |
#include <linux/string.h> |
1da177e4c Linux-2.6.12-rc2 |
25 |
#include <linux/errno.h> |
1da177e4c Linux-2.6.12-rc2 |
26 |
#include <linux/skbuff.h> |
0ba480538 [NET_SCHED]: Remo... |
27 |
#include <net/netlink.h> |
1da177e4c Linux-2.6.12-rc2 |
28 29 |
#include <net/act_api.h> #include <net/pkt_cls.h> |
d37d8ac17 net: sched: use n... |
30 |
#define HTSIZE 256 |
c5c13fafd [PKT_SCHED]: impr... |
31 |
|
cc7ec456f net_sched: cleanups |
32 |
struct fw_head { |
d37d8ac17 net: sched: use n... |
33 |
u32 mask; |
e35a8ee59 net: sched: fw us... |
34 35 |
struct fw_filter __rcu *ht[HTSIZE]; struct rcu_head rcu; |
1da177e4c Linux-2.6.12-rc2 |
36 |
}; |
cc7ec456f net_sched: cleanups |
37 |
struct fw_filter { |
e35a8ee59 net: sched: fw us... |
38 |
struct fw_filter __rcu *next; |
1da177e4c Linux-2.6.12-rc2 |
39 40 41 |
u32 id; struct tcf_result res; #ifdef CONFIG_NET_CLS_IND |
2519a602c net_sched: optimi... |
42 |
int ifindex; |
1da177e4c Linux-2.6.12-rc2 |
43 44 |
#endif /* CONFIG_NET_CLS_IND */ struct tcf_exts exts; |
e35a8ee59 net: sched: fw us... |
45 46 |
struct tcf_proto *tp; struct rcu_head rcu; |
1da177e4c Linux-2.6.12-rc2 |
47 |
}; |
d37d8ac17 net: sched: use n... |
48 |
static u32 fw_hash(u32 handle) |
1da177e4c Linux-2.6.12-rc2 |
49 |
{ |
d37d8ac17 net: sched: use n... |
50 51 52 |
handle ^= (handle >> 16); handle ^= (handle >> 8); return handle % HTSIZE; |
1da177e4c Linux-2.6.12-rc2 |
53 |
} |
dc7f9f6e8 net: sched: const... |
54 |
static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, |
1da177e4c Linux-2.6.12-rc2 |
55 56 |
struct tcf_result *res) { |
e35a8ee59 net: sched: fw us... |
57 |
struct fw_head *head = rcu_dereference_bh(tp->root); |
1da177e4c Linux-2.6.12-rc2 |
58 59 |
struct fw_filter *f; int r; |
5c804bfdc [NET_SCHED]: cls_... |
60 |
u32 id = skb->mark; |
1da177e4c Linux-2.6.12-rc2 |
61 62 |
if (head != NULL) { |
5c804bfdc [NET_SCHED]: cls_... |
63 |
id &= head->mask; |
e35a8ee59 net: sched: fw us... |
64 65 66 |
for (f = rcu_dereference_bh(head->ht[fw_hash(id)]); f; f = rcu_dereference_bh(f->next)) { |
1da177e4c Linux-2.6.12-rc2 |
67 68 69 |
if (f->id == id) { *res = f->res; #ifdef CONFIG_NET_CLS_IND |
2519a602c net_sched: optimi... |
70 |
if (!tcf_match_indev(skb, f->ifindex)) |
1da177e4c Linux-2.6.12-rc2 |
71 72 73 74 75 76 77 78 79 80 81 |
continue; #endif /* CONFIG_NET_CLS_IND */ r = tcf_exts_exec(skb, &f->exts, res); if (r < 0) continue; return r; } } } else { /* old method */ |
cc7ec456f net_sched: cleanups |
82 83 |
if (id && (TC_H_MAJ(id) == 0 || !(TC_H_MAJ(id ^ tp->q->handle)))) { |
1da177e4c Linux-2.6.12-rc2 |
84 85 86 87 88 89 90 91 92 93 94 |
res->classid = id; res->class = 0; return 0; } } return -1; } static unsigned long fw_get(struct tcf_proto *tp, u32 handle) { |
e35a8ee59 net: sched: fw us... |
95 |
struct fw_head *head = rtnl_dereference(tp->root); |
1da177e4c Linux-2.6.12-rc2 |
96 97 98 99 |
struct fw_filter *f; if (head == NULL) return 0; |
e35a8ee59 net: sched: fw us... |
100 101 |
f = rtnl_dereference(head->ht[fw_hash(handle)]); for (; f; f = rtnl_dereference(f->next)) { |
1da177e4c Linux-2.6.12-rc2 |
102 103 104 105 106 |
if (f->id == handle) return (unsigned long)f; } return 0; } |
1da177e4c Linux-2.6.12-rc2 |
107 108 109 110 |
static int fw_init(struct tcf_proto *tp) { return 0; } |
e35a8ee59 net: sched: fw us... |
111 |
static void fw_delete_filter(struct rcu_head *head) |
1da177e4c Linux-2.6.12-rc2 |
112 |
{ |
e35a8ee59 net: sched: fw us... |
113 |
struct fw_filter *f = container_of(head, struct fw_filter, rcu); |
e35a8ee59 net: sched: fw us... |
114 |
|
18d0264f6 net_sched: remove... |
115 |
tcf_exts_destroy(&f->exts); |
1da177e4c Linux-2.6.12-rc2 |
116 117 118 119 120 |
kfree(f); } static void fw_destroy(struct tcf_proto *tp) { |
e35a8ee59 net: sched: fw us... |
121 |
struct fw_head *head = rtnl_dereference(tp->root); |
1da177e4c Linux-2.6.12-rc2 |
122 123 124 125 126 |
struct fw_filter *f; int h; if (head == NULL) return; |
cc7ec456f net_sched: cleanups |
127 |
for (h = 0; h < HTSIZE; h++) { |
e35a8ee59 net: sched: fw us... |
128 129 130 |
while ((f = rtnl_dereference(head->ht[h])) != NULL) { RCU_INIT_POINTER(head->ht[h], rtnl_dereference(f->next)); |
18cdb37eb net: sched: do no... |
131 |
tcf_unbind_filter(tp, &f->res); |
e35a8ee59 net: sched: fw us... |
132 |
call_rcu(&f->rcu, fw_delete_filter); |
1da177e4c Linux-2.6.12-rc2 |
133 134 |
} } |
e35a8ee59 net: sched: fw us... |
135 136 |
RCU_INIT_POINTER(tp->root, NULL); kfree_rcu(head, rcu); |
1da177e4c Linux-2.6.12-rc2 |
137 138 139 140 |
} static int fw_delete(struct tcf_proto *tp, unsigned long arg) { |
e35a8ee59 net: sched: fw us... |
141 |
struct fw_head *head = rtnl_dereference(tp->root); |
cc7ec456f net_sched: cleanups |
142 |
struct fw_filter *f = (struct fw_filter *)arg; |
e35a8ee59 net: sched: fw us... |
143 144 |
struct fw_filter __rcu **fp; struct fw_filter *pfp; |
1da177e4c Linux-2.6.12-rc2 |
145 146 147 |
if (head == NULL || f == NULL) goto out; |
e35a8ee59 net: sched: fw us... |
148 149 150 151 152 153 |
fp = &head->ht[fw_hash(f->id)]; for (pfp = rtnl_dereference(*fp); pfp; fp = &pfp->next, pfp = rtnl_dereference(*fp)) { if (pfp == f) { RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); |
18cdb37eb net: sched: do no... |
154 |
tcf_unbind_filter(tp, &f->res); |
e35a8ee59 net: sched: fw us... |
155 |
call_rcu(&f->rcu, fw_delete_filter); |
1da177e4c Linux-2.6.12-rc2 |
156 157 158 159 160 161 |
return 0; } } out: return -EINVAL; } |
6fa8c0144 [NET_SCHED]: Use ... |
162 163 164 165 166 |
static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = { [TCA_FW_CLASSID] = { .type = NLA_U32 }, [TCA_FW_INDEV] = { .type = NLA_STRING, .len = IFNAMSIZ }, [TCA_FW_MASK] = { .type = NLA_U32 }, }; |
1da177e4c Linux-2.6.12-rc2 |
167 |
static int |
c1b52739e pkt_sched: namesp... |
168 |
fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f, |
2f7ef2f87 sched, cls: check... |
169 |
struct nlattr **tb, struct nlattr **tca, unsigned long base, bool ovr) |
1da177e4c Linux-2.6.12-rc2 |
170 |
{ |
e35a8ee59 net: sched: fw us... |
171 |
struct fw_head *head = rtnl_dereference(tp->root); |
1da177e4c Linux-2.6.12-rc2 |
172 |
struct tcf_exts e; |
b4e9b520c [NET_SCHED]: Add ... |
173 |
u32 mask; |
1da177e4c Linux-2.6.12-rc2 |
174 |
int err; |
5da57f422 net_sched: cls: r... |
175 |
tcf_exts_init(&e, TCA_FW_ACT, TCA_FW_POLICE); |
2f7ef2f87 sched, cls: check... |
176 |
err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); |
1da177e4c Linux-2.6.12-rc2 |
177 178 |
if (err < 0) return err; |
add93b610 [NET_SCHED]: Conv... |
179 |
if (tb[TCA_FW_CLASSID]) { |
1587bac49 [NET_SCHED]: Use ... |
180 |
f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]); |
1da177e4c Linux-2.6.12-rc2 |
181 182 183 184 |
tcf_bind_filter(tp, &f->res, base); } #ifdef CONFIG_NET_CLS_IND |
add93b610 [NET_SCHED]: Conv... |
185 |
if (tb[TCA_FW_INDEV]) { |
2519a602c net_sched: optimi... |
186 187 |
int ret; ret = tcf_change_indev(net, tb[TCA_FW_INDEV]); |
722e47d79 net_sched: fix er... |
188 189 |
if (ret < 0) { err = ret; |
1da177e4c Linux-2.6.12-rc2 |
190 |
goto errout; |
722e47d79 net_sched: fix er... |
191 |
} |
2519a602c net_sched: optimi... |
192 |
f->ifindex = ret; |
1da177e4c Linux-2.6.12-rc2 |
193 194 |
} #endif /* CONFIG_NET_CLS_IND */ |
cb95ec626 pkt_sched: fix er... |
195 |
err = -EINVAL; |
add93b610 [NET_SCHED]: Conv... |
196 |
if (tb[TCA_FW_MASK]) { |
1587bac49 [NET_SCHED]: Use ... |
197 |
mask = nla_get_u32(tb[TCA_FW_MASK]); |
b4e9b520c [NET_SCHED]: Add ... |
198 199 200 201 |
if (mask != head->mask) goto errout; } else if (head->mask != 0xFFFFFFFF) goto errout; |
1da177e4c Linux-2.6.12-rc2 |
202 203 204 205 |
tcf_exts_change(tp, &f->exts, &e); return 0; errout: |
18d0264f6 net_sched: remove... |
206 |
tcf_exts_destroy(&e); |
1da177e4c Linux-2.6.12-rc2 |
207 208 |
return err; } |
c1b52739e pkt_sched: namesp... |
209 |
static int fw_change(struct net *net, struct sk_buff *in_skb, |
af4c6641f net sched: Pass t... |
210 |
struct tcf_proto *tp, unsigned long base, |
1da177e4c Linux-2.6.12-rc2 |
211 |
u32 handle, |
add93b610 [NET_SCHED]: Conv... |
212 |
struct nlattr **tca, |
2f7ef2f87 sched, cls: check... |
213 |
unsigned long *arg, bool ovr) |
1da177e4c Linux-2.6.12-rc2 |
214 |
{ |
e35a8ee59 net: sched: fw us... |
215 |
struct fw_head *head = rtnl_dereference(tp->root); |
1da177e4c Linux-2.6.12-rc2 |
216 |
struct fw_filter *f = (struct fw_filter *) *arg; |
add93b610 [NET_SCHED]: Conv... |
217 218 |
struct nlattr *opt = tca[TCA_OPTIONS]; struct nlattr *tb[TCA_FW_MAX + 1]; |
1da177e4c Linux-2.6.12-rc2 |
219 220 221 222 |
int err; if (!opt) return handle ? -EINVAL : 0; |
6fa8c0144 [NET_SCHED]: Use ... |
223 |
err = nla_parse_nested(tb, TCA_FW_MAX, opt, fw_policy); |
cee63723b [NET_SCHED]: Prop... |
224 225 |
if (err < 0) return err; |
1da177e4c Linux-2.6.12-rc2 |
226 |
|
e35a8ee59 net: sched: fw us... |
227 228 229 |
if (f) { struct fw_filter *pfp, *fnew; struct fw_filter __rcu **fp; |
1da177e4c Linux-2.6.12-rc2 |
230 231 |
if (f->id != handle && handle) return -EINVAL; |
e35a8ee59 net: sched: fw us... |
232 233 234 235 236 237 238 239 240 241 242 |
fnew = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); if (!fnew) return -ENOBUFS; fnew->id = f->id; fnew->res = f->res; #ifdef CONFIG_NET_CLS_IND fnew->ifindex = f->ifindex; #endif /* CONFIG_NET_CLS_IND */ fnew->tp = f->tp; |
e1f93eb06 net: sched: cls_f... |
243 |
tcf_exts_init(&fnew->exts, TCA_FW_ACT, TCA_FW_POLICE); |
e35a8ee59 net: sched: fw us... |
244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
err = fw_change_attrs(net, tp, fnew, tb, tca, base, ovr); if (err < 0) { kfree(fnew); return err; } fp = &head->ht[fw_hash(fnew->id)]; for (pfp = rtnl_dereference(*fp); pfp; fp = &pfp->next, pfp = rtnl_dereference(*fp)) if (pfp == f) break; RCU_INIT_POINTER(fnew->next, rtnl_dereference(pfp->next)); rcu_assign_pointer(*fp, fnew); |
18cdb37eb net: sched: do no... |
258 |
tcf_unbind_filter(tp, &f->res); |
e35a8ee59 net: sched: fw us... |
259 260 261 262 |
call_rcu(&f->rcu, fw_delete_filter); *arg = (unsigned long)fnew; return err; |
1da177e4c Linux-2.6.12-rc2 |
263 264 265 266 267 268 |
} if (!handle) return -EINVAL; if (head == NULL) { |
b4e9b520c [NET_SCHED]: Add ... |
269 |
u32 mask = 0xFFFFFFFF; |
6fa8c0144 [NET_SCHED]: Use ... |
270 |
if (tb[TCA_FW_MASK]) |
1587bac49 [NET_SCHED]: Use ... |
271 |
mask = nla_get_u32(tb[TCA_FW_MASK]); |
b4e9b520c [NET_SCHED]: Add ... |
272 |
|
0da974f4f [NET]: Conversion... |
273 |
head = kzalloc(sizeof(struct fw_head), GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
274 275 |
if (head == NULL) return -ENOBUFS; |
b4e9b520c [NET_SCHED]: Add ... |
276 |
head->mask = mask; |
1da177e4c Linux-2.6.12-rc2 |
277 |
|
e35a8ee59 net: sched: fw us... |
278 |
rcu_assign_pointer(tp->root, head); |
1da177e4c Linux-2.6.12-rc2 |
279 |
} |
0da974f4f [NET]: Conversion... |
280 |
f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
281 282 |
if (f == NULL) return -ENOBUFS; |
1da177e4c Linux-2.6.12-rc2 |
283 |
|
5da57f422 net_sched: cls: r... |
284 |
tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE); |
1da177e4c Linux-2.6.12-rc2 |
285 |
f->id = handle; |
e35a8ee59 net: sched: fw us... |
286 |
f->tp = tp; |
1da177e4c Linux-2.6.12-rc2 |
287 |
|
2f7ef2f87 sched, cls: check... |
288 |
err = fw_change_attrs(net, tp, f, tb, tca, base, ovr); |
1da177e4c Linux-2.6.12-rc2 |
289 290 |
if (err < 0) goto errout; |
e35a8ee59 net: sched: fw us... |
291 292 |
RCU_INIT_POINTER(f->next, head->ht[fw_hash(handle)]); rcu_assign_pointer(head->ht[fw_hash(handle)], f); |
1da177e4c Linux-2.6.12-rc2 |
293 294 295 296 297 |
*arg = (unsigned long)f; return 0; errout: |
a51482bde [NET]: kfree cleanup |
298 |
kfree(f); |
1da177e4c Linux-2.6.12-rc2 |
299 300 301 302 303 |
return err; } static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) { |
e35a8ee59 net: sched: fw us... |
304 |
struct fw_head *head = rtnl_dereference(tp->root); |
1da177e4c Linux-2.6.12-rc2 |
305 306 307 308 309 310 311 |
int h; if (head == NULL) arg->stop = 1; if (arg->stop) return; |
c5c13fafd [PKT_SCHED]: impr... |
312 |
for (h = 0; h < HTSIZE; h++) { |
1da177e4c Linux-2.6.12-rc2 |
313 |
struct fw_filter *f; |
e35a8ee59 net: sched: fw us... |
314 315 |
for (f = rtnl_dereference(head->ht[h]); f; f = rtnl_dereference(f->next)) { |
1da177e4c Linux-2.6.12-rc2 |
316 317 318 319 320 321 322 323 324 325 326 327 |
if (arg->count < arg->skip) { arg->count++; continue; } if (arg->fn(tp, (unsigned long)f, arg) < 0) { arg->stop = 1; return; } arg->count++; } } } |
832d1d5bf net_sched: add st... |
328 |
static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, |
1da177e4c Linux-2.6.12-rc2 |
329 330 |
struct sk_buff *skb, struct tcmsg *t) { |
e35a8ee59 net: sched: fw us... |
331 |
struct fw_head *head = rtnl_dereference(tp->root); |
cc7ec456f net_sched: cleanups |
332 |
struct fw_filter *f = (struct fw_filter *)fh; |
4b3550ef5 [NET_SCHED]: Use ... |
333 |
struct nlattr *nest; |
1da177e4c Linux-2.6.12-rc2 |
334 335 336 337 338 339 340 341 |
if (f == NULL) return skb->len; t->tcm_handle = f->id; if (!f->res.classid && !tcf_exts_is_available(&f->exts)) return skb->len; |
4b3550ef5 [NET_SCHED]: Use ... |
342 343 344 |
nest = nla_nest_start(skb, TCA_OPTIONS); if (nest == NULL) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
345 |
|
1b34ec43c pkt_sched: Stop u... |
346 347 348 |
if (f->res.classid && nla_put_u32(skb, TCA_FW_CLASSID, f->res.classid)) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
349 |
#ifdef CONFIG_NET_CLS_IND |
2519a602c net_sched: optimi... |
350 351 352 353 354 355 |
if (f->ifindex) { struct net_device *dev; dev = __dev_get_by_index(net, f->ifindex); if (dev && nla_put_string(skb, TCA_FW_INDEV, dev->name)) goto nla_put_failure; } |
1da177e4c Linux-2.6.12-rc2 |
356 |
#endif /* CONFIG_NET_CLS_IND */ |
1b34ec43c pkt_sched: Stop u... |
357 358 359 |
if (head->mask != 0xFFFFFFFF && nla_put_u32(skb, TCA_FW_MASK, head->mask)) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
360 |
|
5da57f422 net_sched: cls: r... |
361 |
if (tcf_exts_dump(skb, &f->exts) < 0) |
add93b610 [NET_SCHED]: Conv... |
362 |
goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
363 |
|
4b3550ef5 [NET_SCHED]: Use ... |
364 |
nla_nest_end(skb, nest); |
1da177e4c Linux-2.6.12-rc2 |
365 |
|
5da57f422 net_sched: cls: r... |
366 |
if (tcf_exts_dump_stats(skb, &f->exts) < 0) |
add93b610 [NET_SCHED]: Conv... |
367 |
goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
368 369 |
return skb->len; |
add93b610 [NET_SCHED]: Conv... |
370 |
nla_put_failure: |
6ea3b446b net: sched: cls: ... |
371 |
nla_nest_cancel(skb, nest); |
1da177e4c Linux-2.6.12-rc2 |
372 373 |
return -1; } |
2eb9d75c7 [NET_SCHED]: mark... |
374 |
static struct tcf_proto_ops cls_fw_ops __read_mostly = { |
1da177e4c Linux-2.6.12-rc2 |
375 376 377 378 379 |
.kind = "fw", .classify = fw_classify, .init = fw_init, .destroy = fw_destroy, .get = fw_get, |
1da177e4c Linux-2.6.12-rc2 |
380 381 382 383 384 385 386 387 388 389 390 |
.change = fw_change, .delete = fw_delete, .walk = fw_walk, .dump = fw_dump, .owner = THIS_MODULE, }; static int __init init_fw(void) { return register_tcf_proto_ops(&cls_fw_ops); } |
10297b993 [NET] SCHED: Fix ... |
391 |
static void __exit exit_fw(void) |
1da177e4c Linux-2.6.12-rc2 |
392 393 394 395 396 397 398 |
{ unregister_tcf_proto_ops(&cls_fw_ops); } module_init(init_fw) module_exit(exit_fw) MODULE_LICENSE("GPL"); |