Blame view
net/sched/cls_route.c
14.4 KB
2874c5fd2 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4c Linux-2.6.12-rc2 |
2 3 4 |
/* * net/sched/cls_route.c ROUTE4 classifier. * |
1da177e4c Linux-2.6.12-rc2 |
5 6 7 8 |
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> */ #include <linux/module.h> |
5a0e3ad6a include cleanup: ... |
9 |
#include <linux/slab.h> |
1da177e4c Linux-2.6.12-rc2 |
10 11 |
#include <linux/types.h> #include <linux/kernel.h> |
1da177e4c Linux-2.6.12-rc2 |
12 |
#include <linux/string.h> |
1da177e4c Linux-2.6.12-rc2 |
13 |
#include <linux/errno.h> |
1da177e4c Linux-2.6.12-rc2 |
14 |
#include <linux/skbuff.h> |
0ba480538 [NET_SCHED]: Remo... |
15 16 17 |
#include <net/dst.h> #include <net/route.h> #include <net/netlink.h> |
1da177e4c Linux-2.6.12-rc2 |
18 19 20 21 |
#include <net/act_api.h> #include <net/pkt_cls.h> /* |
cc7ec456f net_sched: cleanups |
22 23 24 25 26 |
* 1. For now we assume that route tags < 256. * It allows to use direct table lookups, instead of hash tables. * 2. For now we assume that "from TAG" and "fromdev DEV" statements * are mutually exclusive. * 3. "to TAG from ANY" has higher priority, than "to ANY from XXX" |
1da177e4c Linux-2.6.12-rc2 |
27 |
*/ |
cc7ec456f net_sched: cleanups |
28 |
struct route4_fastmap { |
1109c0054 net: sched: RCU c... |
29 30 31 |
struct route4_filter *filter; u32 id; int iif; |
1da177e4c Linux-2.6.12-rc2 |
32 |
}; |
cc7ec456f net_sched: cleanups |
33 |
struct route4_head { |
1109c0054 net: sched: RCU c... |
34 35 36 |
struct route4_fastmap fastmap[16]; struct route4_bucket __rcu *table[256 + 1]; struct rcu_head rcu; |
1da177e4c Linux-2.6.12-rc2 |
37 |
}; |
cc7ec456f net_sched: cleanups |
38 |
struct route4_bucket { |
1da177e4c Linux-2.6.12-rc2 |
39 |
/* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */ |
1109c0054 net: sched: RCU c... |
40 41 |
struct route4_filter __rcu *ht[16 + 16 + 1]; struct rcu_head rcu; |
1da177e4c Linux-2.6.12-rc2 |
42 |
}; |
cc7ec456f net_sched: cleanups |
43 |
struct route4_filter { |
1109c0054 net: sched: RCU c... |
44 |
struct route4_filter __rcu *next; |
1da177e4c Linux-2.6.12-rc2 |
45 46 47 48 49 50 51 |
u32 id; int iif; struct tcf_result res; struct tcf_exts exts; u32 handle; struct route4_bucket *bkt; |
1109c0054 net: sched: RCU c... |
52 |
struct tcf_proto *tp; |
aaa908ffb net_sched: switch... |
53 |
struct rcu_work rwork; |
1da177e4c Linux-2.6.12-rc2 |
54 |
}; |
cc7ec456f net_sched: cleanups |
55 |
#define ROUTE4_FAILURE ((struct route4_filter *)(-1L)) |
1da177e4c Linux-2.6.12-rc2 |
56 |
|
cc7ec456f net_sched: cleanups |
57 |
static inline int route4_fastmap_hash(u32 id, int iif) |
1da177e4c Linux-2.6.12-rc2 |
58 |
{ |
cc7ec456f net_sched: cleanups |
59 |
return id & 0xF; |
1da177e4c Linux-2.6.12-rc2 |
60 |
} |
1109c0054 net: sched: RCU c... |
61 |
static DEFINE_SPINLOCK(fastmap_lock); |
cc7ec456f net_sched: cleanups |
62 |
static void |
1109c0054 net: sched: RCU c... |
63 |
route4_reset_fastmap(struct route4_head *head) |
1da177e4c Linux-2.6.12-rc2 |
64 |
{ |
1109c0054 net: sched: RCU c... |
65 |
spin_lock_bh(&fastmap_lock); |
1da177e4c Linux-2.6.12-rc2 |
66 |
memset(head->fastmap, 0, sizeof(head->fastmap)); |
1109c0054 net: sched: RCU c... |
67 |
spin_unlock_bh(&fastmap_lock); |
1da177e4c Linux-2.6.12-rc2 |
68 |
} |
cc7ec456f net_sched: cleanups |
69 |
static void |
1da177e4c Linux-2.6.12-rc2 |
70 71 72 73 |
route4_set_fastmap(struct route4_head *head, u32 id, int iif, struct route4_filter *f) { int h = route4_fastmap_hash(id, iif); |
cc7ec456f net_sched: cleanups |
74 |
|
1109c0054 net: sched: RCU c... |
75 76 |
/* fastmap updates must look atomic to aling id, iff, filter */ spin_lock_bh(&fastmap_lock); |
1da177e4c Linux-2.6.12-rc2 |
77 78 79 |
head->fastmap[h].id = id; head->fastmap[h].iif = iif; head->fastmap[h].filter = f; |
1109c0054 net: sched: RCU c... |
80 |
spin_unlock_bh(&fastmap_lock); |
1da177e4c Linux-2.6.12-rc2 |
81 |
} |
cc7ec456f net_sched: cleanups |
82 |
static inline int route4_hash_to(u32 id) |
1da177e4c Linux-2.6.12-rc2 |
83 |
{ |
cc7ec456f net_sched: cleanups |
84 |
return id & 0xFF; |
1da177e4c Linux-2.6.12-rc2 |
85 |
} |
cc7ec456f net_sched: cleanups |
86 |
static inline int route4_hash_from(u32 id) |
1da177e4c Linux-2.6.12-rc2 |
87 |
{ |
cc7ec456f net_sched: cleanups |
88 |
return (id >> 16) & 0xF; |
1da177e4c Linux-2.6.12-rc2 |
89 |
} |
cc7ec456f net_sched: cleanups |
90 |
static inline int route4_hash_iif(int iif) |
1da177e4c Linux-2.6.12-rc2 |
91 |
{ |
cc7ec456f net_sched: cleanups |
92 |
return 16 + ((iif >> 16) & 0xF); |
1da177e4c Linux-2.6.12-rc2 |
93 |
} |
cc7ec456f net_sched: cleanups |
94 |
static inline int route4_hash_wild(void) |
1da177e4c Linux-2.6.12-rc2 |
95 96 97 98 99 100 101 |
{ return 32; } #define ROUTE4_APPLY_RESULT() \ { \ *res = f->res; \ |
6fc6d06e5 net: sched: remov... |
102 |
if (tcf_exts_has_actions(&f->exts)) { \ |
1da177e4c Linux-2.6.12-rc2 |
103 104 105 106 107 108 109 110 111 112 |
int r = tcf_exts_exec(skb, &f->exts, res); \ if (r < 0) { \ dont_cache = 1; \ continue; \ } \ return r; \ } else if (!dont_cache) \ route4_set_fastmap(head, id, iif, f); \ return 0; \ } |
dc7f9f6e8 net: sched: const... |
113 |
static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp, |
1da177e4c Linux-2.6.12-rc2 |
114 115 |
struct tcf_result *res) { |
1109c0054 net: sched: RCU c... |
116 |
struct route4_head *head = rcu_dereference_bh(tp->root); |
1da177e4c Linux-2.6.12-rc2 |
117 118 119 120 121 |
struct dst_entry *dst; struct route4_bucket *b; struct route4_filter *f; u32 id, h; int iif, dont_cache = 0; |
cc7ec456f net_sched: cleanups |
122 123 |
dst = skb_dst(skb); if (!dst) |
1da177e4c Linux-2.6.12-rc2 |
124 125 126 |
goto failure; id = dst->tclassid; |
1da177e4c Linux-2.6.12-rc2 |
127 |
|
92101b3b2 ipv4: Prepare for... |
128 |
iif = inet_iif(skb); |
1da177e4c Linux-2.6.12-rc2 |
129 130 |
h = route4_fastmap_hash(id, iif); |
1109c0054 net: sched: RCU c... |
131 132 |
spin_lock(&fastmap_lock); |
1da177e4c Linux-2.6.12-rc2 |
133 134 135 |
if (id == head->fastmap[h].id && iif == head->fastmap[h].iif && (f = head->fastmap[h].filter) != NULL) { |
1109c0054 net: sched: RCU c... |
136 137 |
if (f == ROUTE4_FAILURE) { spin_unlock(&fastmap_lock); |
1da177e4c Linux-2.6.12-rc2 |
138 |
goto failure; |
1109c0054 net: sched: RCU c... |
139 |
} |
1da177e4c Linux-2.6.12-rc2 |
140 141 |
*res = f->res; |
1109c0054 net: sched: RCU c... |
142 |
spin_unlock(&fastmap_lock); |
1da177e4c Linux-2.6.12-rc2 |
143 144 |
return 0; } |
1109c0054 net: sched: RCU c... |
145 |
spin_unlock(&fastmap_lock); |
1da177e4c Linux-2.6.12-rc2 |
146 147 148 149 |
h = route4_hash_to(id); restart: |
1109c0054 net: sched: RCU c... |
150 |
b = rcu_dereference_bh(head->table[h]); |
cc7ec456f net_sched: cleanups |
151 |
if (b) { |
1109c0054 net: sched: RCU c... |
152 153 154 |
for (f = rcu_dereference_bh(b->ht[route4_hash_from(id)]); f; f = rcu_dereference_bh(f->next)) |
1da177e4c Linux-2.6.12-rc2 |
155 156 |
if (f->id == id) ROUTE4_APPLY_RESULT(); |
1109c0054 net: sched: RCU c... |
157 158 159 |
for (f = rcu_dereference_bh(b->ht[route4_hash_iif(iif)]); f; f = rcu_dereference_bh(f->next)) |
1da177e4c Linux-2.6.12-rc2 |
160 161 |
if (f->iif == iif) ROUTE4_APPLY_RESULT(); |
1109c0054 net: sched: RCU c... |
162 163 164 |
for (f = rcu_dereference_bh(b->ht[route4_hash_wild()]); f; f = rcu_dereference_bh(f->next)) |
1da177e4c Linux-2.6.12-rc2 |
165 |
ROUTE4_APPLY_RESULT(); |
1da177e4c Linux-2.6.12-rc2 |
166 167 168 169 170 171 172 173 174 175 176 |
} if (h < 256) { h = 256; id &= ~0xFFFF; goto restart; } if (!dont_cache) route4_set_fastmap(head, id, iif, ROUTE4_FAILURE); failure: return -1; |
1da177e4c Linux-2.6.12-rc2 |
177 178 179 180 |
} static inline u32 to_hash(u32 id) { |
cc7ec456f net_sched: cleanups |
181 182 183 |
u32 h = id & 0xFF; if (id & 0x8000) |
1da177e4c Linux-2.6.12-rc2 |
184 185 186 187 188 189 190 191 192 193 194 195 |
h += 256; return h; } static inline u32 from_hash(u32 id) { id &= 0xFFFF; if (id == 0xFFFF) return 32; if (!(id & 0x8000)) { if (id > 255) return 256; |
cc7ec456f net_sched: cleanups |
196 |
return id & 0xF; |
1da177e4c Linux-2.6.12-rc2 |
197 |
} |
cc7ec456f net_sched: cleanups |
198 |
return 16 + (id & 0xF); |
1da177e4c Linux-2.6.12-rc2 |
199 |
} |
8113c0956 net_sched: use vo... |
200 |
static void *route4_get(struct tcf_proto *tp, u32 handle) |
1da177e4c Linux-2.6.12-rc2 |
201 |
{ |
1109c0054 net: sched: RCU c... |
202 |
struct route4_head *head = rtnl_dereference(tp->root); |
1da177e4c Linux-2.6.12-rc2 |
203 204 |
struct route4_bucket *b; struct route4_filter *f; |
cc7ec456f net_sched: cleanups |
205 |
unsigned int h1, h2; |
1da177e4c Linux-2.6.12-rc2 |
206 |
|
1da177e4c Linux-2.6.12-rc2 |
207 208 |
h1 = to_hash(handle); if (h1 > 256) |
8113c0956 net_sched: use vo... |
209 |
return NULL; |
1da177e4c Linux-2.6.12-rc2 |
210 |
|
cc7ec456f net_sched: cleanups |
211 |
h2 = from_hash(handle >> 16); |
1da177e4c Linux-2.6.12-rc2 |
212 |
if (h2 > 32) |
8113c0956 net_sched: use vo... |
213 |
return NULL; |
1da177e4c Linux-2.6.12-rc2 |
214 |
|
1109c0054 net: sched: RCU c... |
215 |
b = rtnl_dereference(head->table[h1]); |
cc7ec456f net_sched: cleanups |
216 |
if (b) { |
1109c0054 net: sched: RCU c... |
217 218 219 |
for (f = rtnl_dereference(b->ht[h2]); f; f = rtnl_dereference(f->next)) |
1da177e4c Linux-2.6.12-rc2 |
220 |
if (f->handle == handle) |
8113c0956 net_sched: use vo... |
221 |
return f; |
1da177e4c Linux-2.6.12-rc2 |
222 |
} |
8113c0956 net_sched: use vo... |
223 |
return NULL; |
1da177e4c Linux-2.6.12-rc2 |
224 |
} |
1da177e4c Linux-2.6.12-rc2 |
225 226 |
static int route4_init(struct tcf_proto *tp) { |
a05c2d112 net_sched: move t... |
227 228 229 230 231 232 233 |
struct route4_head *head; head = kzalloc(sizeof(struct route4_head), GFP_KERNEL); if (head == NULL) return -ENOBUFS; rcu_assign_pointer(tp->root, head); |
1da177e4c Linux-2.6.12-rc2 |
234 235 |
return 0; } |
3fd51de5e cls_route: use tc... |
236 237 238 239 240 241 |
static void __route4_delete_filter(struct route4_filter *f) { tcf_exts_destroy(&f->exts); tcf_exts_put_net(&f->exts); kfree(f); } |
c2f3f31d4 net_sched: use tc... |
242 |
static void route4_delete_filter_work(struct work_struct *work) |
1da177e4c Linux-2.6.12-rc2 |
243 |
{ |
aaa908ffb net_sched: switch... |
244 245 246 |
struct route4_filter *f = container_of(to_rcu_work(work), struct route4_filter, rwork); |
c2f3f31d4 net_sched: use tc... |
247 |
rtnl_lock(); |
3fd51de5e cls_route: use tc... |
248 |
__route4_delete_filter(f); |
c2f3f31d4 net_sched: use tc... |
249 250 |
rtnl_unlock(); } |
aaa908ffb net_sched: switch... |
251 |
static void route4_queue_work(struct route4_filter *f) |
c2f3f31d4 net_sched: use tc... |
252 |
{ |
aaa908ffb net_sched: switch... |
253 |
tcf_queue_work(&f->rwork, route4_delete_filter_work); |
1da177e4c Linux-2.6.12-rc2 |
254 |
} |
12db03b65 net: sched: exten... |
255 256 |
static void route4_destroy(struct tcf_proto *tp, bool rtnl_held, struct netlink_ext_ack *extack) |
1da177e4c Linux-2.6.12-rc2 |
257 |
{ |
1109c0054 net: sched: RCU c... |
258 |
struct route4_head *head = rtnl_dereference(tp->root); |
1da177e4c Linux-2.6.12-rc2 |
259 260 261 |
int h1, h2; if (head == NULL) |
763dbf632 net_sched: move t... |
262 |
return; |
1da177e4c Linux-2.6.12-rc2 |
263 |
|
cc7ec456f net_sched: cleanups |
264 |
for (h1 = 0; h1 <= 256; h1++) { |
1da177e4c Linux-2.6.12-rc2 |
265 |
struct route4_bucket *b; |
1109c0054 net: sched: RCU c... |
266 |
b = rtnl_dereference(head->table[h1]); |
cc7ec456f net_sched: cleanups |
267 268 |
if (b) { for (h2 = 0; h2 <= 32; h2++) { |
1da177e4c Linux-2.6.12-rc2 |
269 |
struct route4_filter *f; |
1109c0054 net: sched: RCU c... |
270 271 272 273 274 |
while ((f = rtnl_dereference(b->ht[h2])) != NULL) { struct route4_filter *next; next = rtnl_dereference(f->next); RCU_INIT_POINTER(b->ht[h2], next); |
18cdb37eb net: sched: do no... |
275 |
tcf_unbind_filter(tp, &f->res); |
3fd51de5e cls_route: use tc... |
276 |
if (tcf_exts_get_net(&f->exts)) |
aaa908ffb net_sched: switch... |
277 |
route4_queue_work(f); |
3fd51de5e cls_route: use tc... |
278 279 |
else __route4_delete_filter(f); |
1da177e4c Linux-2.6.12-rc2 |
280 281 |
} } |
1109c0054 net: sched: RCU c... |
282 283 |
RCU_INIT_POINTER(head->table[h1], NULL); kfree_rcu(b, rcu); |
1da177e4c Linux-2.6.12-rc2 |
284 285 |
} } |
1109c0054 net: sched: RCU c... |
286 |
kfree_rcu(head, rcu); |
1da177e4c Linux-2.6.12-rc2 |
287 |
} |
571acf210 net: sched: cls: ... |
288 |
static int route4_delete(struct tcf_proto *tp, void *arg, bool *last, |
12db03b65 net: sched: exten... |
289 |
bool rtnl_held, struct netlink_ext_ack *extack) |
1da177e4c Linux-2.6.12-rc2 |
290 |
{ |
1109c0054 net: sched: RCU c... |
291 |
struct route4_head *head = rtnl_dereference(tp->root); |
8113c0956 net_sched: use vo... |
292 |
struct route4_filter *f = arg; |
1109c0054 net: sched: RCU c... |
293 294 |
struct route4_filter __rcu **fp; struct route4_filter *nf; |
1da177e4c Linux-2.6.12-rc2 |
295 |
struct route4_bucket *b; |
1109c0054 net: sched: RCU c... |
296 |
unsigned int h = 0; |
763dbf632 net_sched: move t... |
297 |
int i, h1; |
1da177e4c Linux-2.6.12-rc2 |
298 299 300 301 302 303 |
if (!head || !f) return -EINVAL; h = f->handle; b = f->bkt; |
1109c0054 net: sched: RCU c... |
304 305 306 307 308 309 |
fp = &b->ht[from_hash(h >> 16)]; for (nf = rtnl_dereference(*fp); nf; fp = &nf->next, nf = rtnl_dereference(*fp)) { if (nf == f) { /* unlink it */ RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); |
1da177e4c Linux-2.6.12-rc2 |
310 |
|
1109c0054 net: sched: RCU c... |
311 312 313 314 315 |
/* Remove any fastmap lookups that might ref filter * notice we unlink'd the filter so we can't get it * back in the fastmap. */ route4_reset_fastmap(head); |
1da177e4c Linux-2.6.12-rc2 |
316 |
|
1109c0054 net: sched: RCU c... |
317 |
/* Delete it */ |
18cdb37eb net: sched: do no... |
318 |
tcf_unbind_filter(tp, &f->res); |
3fd51de5e cls_route: use tc... |
319 |
tcf_exts_get_net(&f->exts); |
aaa908ffb net_sched: switch... |
320 |
tcf_queue_work(&f->rwork, route4_delete_filter_work); |
1da177e4c Linux-2.6.12-rc2 |
321 |
|
1109c0054 net: sched: RCU c... |
322 323 324 325 326 327 |
/* Strip RTNL protected tree */ for (i = 0; i <= 32; i++) { struct route4_filter *rt; rt = rtnl_dereference(b->ht[i]); if (rt) |
763dbf632 net_sched: move t... |
328 |
goto out; |
1109c0054 net: sched: RCU c... |
329 |
} |
1da177e4c Linux-2.6.12-rc2 |
330 331 |
/* OK, session has no flows */ |
1109c0054 net: sched: RCU c... |
332 333 |
RCU_INIT_POINTER(head->table[to_hash(h)], NULL); kfree_rcu(b, rcu); |
763dbf632 net_sched: move t... |
334 335 336 |
break; } } |
1da177e4c Linux-2.6.12-rc2 |
337 |
|
763dbf632 net_sched: move t... |
338 339 340 341 342 343 |
out: *last = true; for (h1 = 0; h1 <= 256; h1++) { if (rcu_access_pointer(head->table[h1])) { *last = false; break; |
1da177e4c Linux-2.6.12-rc2 |
344 345 |
} } |
763dbf632 net_sched: move t... |
346 |
|
1da177e4c Linux-2.6.12-rc2 |
347 348 |
return 0; } |
6fa8c0144 [NET_SCHED]: Use ... |
349 350 351 352 353 354 |
static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = { [TCA_ROUTE4_CLASSID] = { .type = NLA_U32 }, [TCA_ROUTE4_TO] = { .type = NLA_U32 }, [TCA_ROUTE4_FROM] = { .type = NLA_U32 }, [TCA_ROUTE4_IIF] = { .type = NLA_U32 }, }; |
c1b52739e pkt_sched: namesp... |
355 356 357 |
static int route4_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, struct route4_filter *f, u32 handle, struct route4_head *head, |
2f7ef2f87 sched, cls: check... |
358 |
struct nlattr **tb, struct nlattr *est, int new, |
50a561900 net: sched: cls: ... |
359 |
bool ovr, struct netlink_ext_ack *extack) |
1da177e4c Linux-2.6.12-rc2 |
360 |
{ |
1da177e4c Linux-2.6.12-rc2 |
361 362 363 364 |
u32 id = 0, to = 0, nhandle = 0x8000; struct route4_filter *fp; unsigned int h1; struct route4_bucket *b; |
b9a24bb76 net_sched: proper... |
365 |
int err; |
1da177e4c Linux-2.6.12-rc2 |
366 |
|
ec6743a10 net: sched: track... |
367 |
err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, true, extack); |
1da177e4c Linux-2.6.12-rc2 |
368 369 |
if (err < 0) return err; |
add93b610 [NET_SCHED]: Conv... |
370 |
if (tb[TCA_ROUTE4_TO]) { |
1da177e4c Linux-2.6.12-rc2 |
371 |
if (new && handle & 0x8000) |
8c98d571b net: sched: cls_r... |
372 |
return -EINVAL; |
1587bac49 [NET_SCHED]: Use ... |
373 |
to = nla_get_u32(tb[TCA_ROUTE4_TO]); |
1da177e4c Linux-2.6.12-rc2 |
374 |
if (to > 0xFF) |
8c98d571b net: sched: cls_r... |
375 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
376 377 |
nhandle = to; } |
add93b610 [NET_SCHED]: Conv... |
378 379 |
if (tb[TCA_ROUTE4_FROM]) { if (tb[TCA_ROUTE4_IIF]) |
8c98d571b net: sched: cls_r... |
380 |
return -EINVAL; |
1587bac49 [NET_SCHED]: Use ... |
381 |
id = nla_get_u32(tb[TCA_ROUTE4_FROM]); |
1da177e4c Linux-2.6.12-rc2 |
382 |
if (id > 0xFF) |
8c98d571b net: sched: cls_r... |
383 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
384 |
nhandle |= id << 16; |
add93b610 [NET_SCHED]: Conv... |
385 |
} else if (tb[TCA_ROUTE4_IIF]) { |
1587bac49 [NET_SCHED]: Use ... |
386 |
id = nla_get_u32(tb[TCA_ROUTE4_IIF]); |
1da177e4c Linux-2.6.12-rc2 |
387 |
if (id > 0x7FFF) |
8c98d571b net: sched: cls_r... |
388 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
389 390 391 392 393 394 395 |
nhandle |= (id | 0x8000) << 16; } else nhandle |= 0xFFFF << 16; if (handle && new) { nhandle |= handle & 0x7F00; if (nhandle != handle) |
8c98d571b net: sched: cls_r... |
396 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
397 398 399 |
} h1 = to_hash(nhandle); |
1109c0054 net: sched: RCU c... |
400 |
b = rtnl_dereference(head->table[h1]); |
cc7ec456f net_sched: cleanups |
401 |
if (!b) { |
0da974f4f [NET]: Conversion... |
402 |
b = kzalloc(sizeof(struct route4_bucket), GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
403 |
if (b == NULL) |
8c98d571b net: sched: cls_r... |
404 |
return -ENOBUFS; |
1da177e4c Linux-2.6.12-rc2 |
405 |
|
1109c0054 net: sched: RCU c... |
406 |
rcu_assign_pointer(head->table[h1], b); |
1da177e4c Linux-2.6.12-rc2 |
407 408 |
} else { unsigned int h2 = from_hash(nhandle >> 16); |
cc7ec456f net_sched: cleanups |
409 |
|
1109c0054 net: sched: RCU c... |
410 411 412 |
for (fp = rtnl_dereference(b->ht[h2]); fp; fp = rtnl_dereference(fp->next)) |
1da177e4c Linux-2.6.12-rc2 |
413 |
if (fp->handle == f->handle) |
8c98d571b net: sched: cls_r... |
414 |
return -EEXIST; |
1da177e4c Linux-2.6.12-rc2 |
415 |
} |
add93b610 [NET_SCHED]: Conv... |
416 |
if (tb[TCA_ROUTE4_TO]) |
1da177e4c Linux-2.6.12-rc2 |
417 |
f->id = to; |
add93b610 [NET_SCHED]: Conv... |
418 |
if (tb[TCA_ROUTE4_FROM]) |
1da177e4c Linux-2.6.12-rc2 |
419 |
f->id = to | id<<16; |
add93b610 [NET_SCHED]: Conv... |
420 |
else if (tb[TCA_ROUTE4_IIF]) |
1da177e4c Linux-2.6.12-rc2 |
421 422 423 424 |
f->iif = id; f->handle = nhandle; f->bkt = b; |
1109c0054 net: sched: RCU c... |
425 |
f->tp = tp; |
1da177e4c Linux-2.6.12-rc2 |
426 |
|
add93b610 [NET_SCHED]: Conv... |
427 |
if (tb[TCA_ROUTE4_CLASSID]) { |
1587bac49 [NET_SCHED]: Use ... |
428 |
f->res.classid = nla_get_u32(tb[TCA_ROUTE4_CLASSID]); |
1da177e4c Linux-2.6.12-rc2 |
429 430 |
tcf_bind_filter(tp, &f->res, base); } |
1da177e4c Linux-2.6.12-rc2 |
431 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
432 |
} |
c1b52739e pkt_sched: namesp... |
433 |
static int route4_change(struct net *net, struct sk_buff *in_skb, |
5a7a5555a net sched: stylis... |
434 |
struct tcf_proto *tp, unsigned long base, u32 handle, |
7306db38a net: sched: cls: ... |
435 |
struct nlattr **tca, void **arg, bool ovr, |
12db03b65 net: sched: exten... |
436 |
bool rtnl_held, struct netlink_ext_ack *extack) |
1da177e4c Linux-2.6.12-rc2 |
437 |
{ |
1109c0054 net: sched: RCU c... |
438 439 440 |
struct route4_head *head = rtnl_dereference(tp->root); struct route4_filter __rcu **fp; struct route4_filter *fold, *f1, *pfp, *f = NULL; |
1da177e4c Linux-2.6.12-rc2 |
441 |
struct route4_bucket *b; |
add93b610 [NET_SCHED]: Conv... |
442 443 |
struct nlattr *opt = tca[TCA_OPTIONS]; struct nlattr *tb[TCA_ROUTE4_MAX + 1]; |
1da177e4c Linux-2.6.12-rc2 |
444 |
unsigned int h, th; |
1da177e4c Linux-2.6.12-rc2 |
445 |
int err; |
1109c0054 net: sched: RCU c... |
446 |
bool new = true; |
1da177e4c Linux-2.6.12-rc2 |
447 448 449 |
if (opt == NULL) return handle ? -EINVAL : 0; |
8cb081746 netlink: make val... |
450 451 |
err = nla_parse_nested_deprecated(tb, TCA_ROUTE4_MAX, opt, route4_policy, NULL); |
cee63723b [NET_SCHED]: Prop... |
452 453 |
if (err < 0) return err; |
1da177e4c Linux-2.6.12-rc2 |
454 |
|
8113c0956 net_sched: use vo... |
455 |
fold = *arg; |
1109c0054 net: sched: RCU c... |
456 |
if (fold && handle && fold->handle != handle) |
1da177e4c Linux-2.6.12-rc2 |
457 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
458 |
err = -ENOBUFS; |
0da974f4f [NET]: Conversion... |
459 |
f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL); |
1109c0054 net: sched: RCU c... |
460 |
if (!f) |
1da177e4c Linux-2.6.12-rc2 |
461 |
goto errout; |
1da177e4c Linux-2.6.12-rc2 |
462 |
|
14215108a net_sched: initia... |
463 |
err = tcf_exts_init(&f->exts, net, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE); |
b9a24bb76 net_sched: proper... |
464 465 |
if (err < 0) goto errout; |
1109c0054 net: sched: RCU c... |
466 467 468 469 470 471 472 473 474 475 |
if (fold) { f->id = fold->id; f->iif = fold->iif; f->res = fold->res; f->handle = fold->handle; f->tp = fold->tp; f->bkt = fold->bkt; new = false; } |
c1b52739e pkt_sched: namesp... |
476 |
err = route4_set_parms(net, tp, base, f, handle, head, tb, |
50a561900 net: sched: cls: ... |
477 |
tca[TCA_RATE], new, ovr, extack); |
1da177e4c Linux-2.6.12-rc2 |
478 479 |
if (err < 0) goto errout; |
1da177e4c Linux-2.6.12-rc2 |
480 |
h = from_hash(f->handle >> 16); |
1109c0054 net: sched: RCU c... |
481 482 483 484 |
fp = &f->bkt->ht[h]; for (pfp = rtnl_dereference(*fp); (f1 = rtnl_dereference(*fp)) != NULL; fp = &f1->next) |
1da177e4c Linux-2.6.12-rc2 |
485 486 |
if (f->handle < f1->handle) break; |
f36fe1c49 net: sched: intro... |
487 |
tcf_block_netif_keep_dst(tp->chain->block); |
1109c0054 net: sched: RCU c... |
488 489 |
rcu_assign_pointer(f->next, f1); rcu_assign_pointer(*fp, f); |
1da177e4c Linux-2.6.12-rc2 |
490 |
|
1109c0054 net: sched: RCU c... |
491 492 493 494 |
if (fold && fold->handle && f->handle != fold->handle) { th = to_hash(fold->handle); h = from_hash(fold->handle >> 16); b = rtnl_dereference(head->table[th]); |
cc7ec456f net_sched: cleanups |
495 |
if (b) { |
1109c0054 net: sched: RCU c... |
496 497 498 499 |
fp = &b->ht[h]; for (pfp = rtnl_dereference(*fp); pfp; fp = &pfp->next, pfp = rtnl_dereference(*fp)) { if (pfp == f) { |
1da177e4c Linux-2.6.12-rc2 |
500 501 502 503 504 505 |
*fp = f->next; break; } } } } |
1da177e4c Linux-2.6.12-rc2 |
506 |
|
1109c0054 net: sched: RCU c... |
507 |
route4_reset_fastmap(head); |
8113c0956 net_sched: use vo... |
508 |
*arg = f; |
18cdb37eb net: sched: do no... |
509 510 |
if (fold) { tcf_unbind_filter(tp, &fold->res); |
3fd51de5e cls_route: use tc... |
511 |
tcf_exts_get_net(&fold->exts); |
aaa908ffb net_sched: switch... |
512 |
tcf_queue_work(&fold->rwork, route4_delete_filter_work); |
18cdb37eb net: sched: do no... |
513 |
} |
1da177e4c Linux-2.6.12-rc2 |
514 515 516 |
return 0; errout: |
21641c2e1 net_sched: check ... |
517 518 |
if (f) tcf_exts_destroy(&f->exts); |
a51482bde [NET]: kfree cleanup |
519 |
kfree(f); |
1da177e4c Linux-2.6.12-rc2 |
520 521 |
return err; } |
12db03b65 net: sched: exten... |
522 523 |
static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg, bool rtnl_held) |
1da177e4c Linux-2.6.12-rc2 |
524 |
{ |
1109c0054 net: sched: RCU c... |
525 |
struct route4_head *head = rtnl_dereference(tp->root); |
cc7ec456f net_sched: cleanups |
526 |
unsigned int h, h1; |
1da177e4c Linux-2.6.12-rc2 |
527 |
|
3027ff41f net: sched: route... |
528 |
if (head == NULL || arg->stop) |
1da177e4c Linux-2.6.12-rc2 |
529 530 531 |
return; for (h = 0; h <= 256; h++) { |
1109c0054 net: sched: RCU c... |
532 |
struct route4_bucket *b = rtnl_dereference(head->table[h]); |
1da177e4c Linux-2.6.12-rc2 |
533 534 535 536 |
if (b) { for (h1 = 0; h1 <= 32; h1++) { struct route4_filter *f; |
1109c0054 net: sched: RCU c... |
537 538 539 |
for (f = rtnl_dereference(b->ht[h1]); f; f = rtnl_dereference(f->next)) { |
1da177e4c Linux-2.6.12-rc2 |
540 541 542 543 |
if (arg->count < arg->skip) { arg->count++; continue; } |
8113c0956 net_sched: use vo... |
544 |
if (arg->fn(tp, f, arg) < 0) { |
1da177e4c Linux-2.6.12-rc2 |
545 546 547 548 549 550 551 552 553 |
arg->stop = 1; return; } arg->count++; } } } } } |
8113c0956 net_sched: use vo... |
554 |
static int route4_dump(struct net *net, struct tcf_proto *tp, void *fh, |
12db03b65 net: sched: exten... |
555 |
struct sk_buff *skb, struct tcmsg *t, bool rtnl_held) |
1da177e4c Linux-2.6.12-rc2 |
556 |
{ |
8113c0956 net_sched: use vo... |
557 |
struct route4_filter *f = fh; |
4b3550ef5 [NET_SCHED]: Use ... |
558 |
struct nlattr *nest; |
1da177e4c Linux-2.6.12-rc2 |
559 560 561 562 563 564 |
u32 id; if (f == NULL) return skb->len; t->tcm_handle = f->handle; |
ae0be8de9 netlink: make nla... |
565 |
nest = nla_nest_start_noflag(skb, TCA_OPTIONS); |
4b3550ef5 [NET_SCHED]: Use ... |
566 567 |
if (nest == NULL) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
568 |
|
cc7ec456f net_sched: cleanups |
569 570 |
if (!(f->handle & 0x8000)) { id = f->id & 0xFF; |
1b34ec43c pkt_sched: Stop u... |
571 572 |
if (nla_put_u32(skb, TCA_ROUTE4_TO, id)) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
573 |
} |
cc7ec456f net_sched: cleanups |
574 |
if (f->handle & 0x80000000) { |
1b34ec43c pkt_sched: Stop u... |
575 576 577 |
if ((f->handle >> 16) != 0xFFFF && nla_put_u32(skb, TCA_ROUTE4_IIF, f->iif)) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
578 |
} else { |
cc7ec456f net_sched: cleanups |
579 |
id = f->id >> 16; |
1b34ec43c pkt_sched: Stop u... |
580 581 |
if (nla_put_u32(skb, TCA_ROUTE4_FROM, id)) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
582 |
} |
1b34ec43c pkt_sched: Stop u... |
583 584 585 |
if (f->res.classid && nla_put_u32(skb, TCA_ROUTE4_CLASSID, f->res.classid)) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
586 |
|
5da57f422 net_sched: cls: r... |
587 |
if (tcf_exts_dump(skb, &f->exts) < 0) |
add93b610 [NET_SCHED]: Conv... |
588 |
goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
589 |
|
4b3550ef5 [NET_SCHED]: Use ... |
590 |
nla_nest_end(skb, nest); |
1da177e4c Linux-2.6.12-rc2 |
591 |
|
5da57f422 net_sched: cls: r... |
592 |
if (tcf_exts_dump_stats(skb, &f->exts) < 0) |
add93b610 [NET_SCHED]: Conv... |
593 |
goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
594 595 |
return skb->len; |
add93b610 [NET_SCHED]: Conv... |
596 |
nla_put_failure: |
6ea3b446b net: sched: cls: ... |
597 |
nla_nest_cancel(skb, nest); |
1da177e4c Linux-2.6.12-rc2 |
598 599 |
return -1; } |
55ec468d3 net_sched: fix op... |
600 601 |
static void route4_bind_class(void *fh, u32 classid, unsigned long cl, void *q, unsigned long base) |
07d79fc7d net_sched: add re... |
602 603 |
{ struct route4_filter *f = fh; |
55ec468d3 net_sched: fix op... |
604 605 606 607 608 609 |
if (f && f->res.classid == classid) { if (cl) __tcf_bind_filter(q, &f->res, base); else __tcf_unbind_filter(q, &f->res); } |
07d79fc7d net_sched: add re... |
610 |
} |
2eb9d75c7 [NET_SCHED]: mark... |
611 |
static struct tcf_proto_ops cls_route4_ops __read_mostly = { |
1da177e4c Linux-2.6.12-rc2 |
612 613 614 615 616 |
.kind = "route", .classify = route4_classify, .init = route4_init, .destroy = route4_destroy, .get = route4_get, |
1da177e4c Linux-2.6.12-rc2 |
617 618 619 620 |
.change = route4_change, .delete = route4_delete, .walk = route4_walk, .dump = route4_dump, |
07d79fc7d net_sched: add re... |
621 |
.bind_class = route4_bind_class, |
1da177e4c Linux-2.6.12-rc2 |
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 |
.owner = THIS_MODULE, }; static int __init init_route4(void) { return register_tcf_proto_ops(&cls_route4_ops); } static void __exit exit_route4(void) { unregister_tcf_proto_ops(&cls_route4_ops); } module_init(init_route4) module_exit(exit_route4) MODULE_LICENSE("GPL"); |