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, <kuznet@ms2.inr.ac.ru> 10 * 11 * Changes: 12 * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_walk off by one 13 * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_delete killed all the filter (and kernel). 14 * Alex <alex@pilotsoft.com> : 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/types.h> 23 #include <linux/kernel.h> 24 #include <linux/string.h> 25 #include <linux/errno.h> 26 #include <linux/skbuff.h> 27 #include <net/netlink.h> 28 #include <net/act_api.h> 29 #include <net/pkt_cls.h> 30 31 #define HTSIZE (PAGE_SIZE/sizeof(struct fw_filter *)) 32 33 struct fw_head 34 { 35 struct fw_filter *ht[HTSIZE]; 36 u32 mask; 37 }; 38 39 struct fw_filter 40 { 41 struct fw_filter *next; 42 u32 id; 43 struct tcf_result res; 44 #ifdef CONFIG_NET_CLS_IND 45 char indev[IFNAMSIZ]; 46 #endif /* CONFIG_NET_CLS_IND */ 47 struct tcf_exts exts; 48 }; 49 50 static const struct tcf_ext_map fw_ext_map = { 51 .action = TCA_FW_ACT, 52 .police = TCA_FW_POLICE 53 }; 54 55 static __inline__ int fw_hash(u32 handle) 56 { 57 if (HTSIZE == 4096) 58 return ((handle >> 24) & 0xFFF) ^ 59 ((handle >> 12) & 0xFFF) ^ 60 (handle & 0xFFF); 61 else if (HTSIZE == 2048) 62 return ((handle >> 22) & 0x7FF) ^ 63 ((handle >> 11) & 0x7FF) ^ 64 (handle & 0x7FF); 65 else if (HTSIZE == 1024) 66 return ((handle >> 20) & 0x3FF) ^ 67 ((handle >> 10) & 0x3FF) ^ 68 (handle & 0x3FF); 69 else if (HTSIZE == 512) 70 return (handle >> 27) ^ 71 ((handle >> 18) & 0x1FF) ^ 72 ((handle >> 9) & 0x1FF) ^ 73 (handle & 0x1FF); 74 else if (HTSIZE == 256) { 75 u8 *t = (u8 *) &handle; 76 return t[0] ^ t[1] ^ t[2] ^ t[3]; 77 } else 78 return handle & (HTSIZE - 1); 79 } 80 81 static int fw_classify(struct sk_buff *skb, struct tcf_proto *tp, 82 struct tcf_result *res) 83 { 84 struct fw_head *head = (struct fw_head*)tp->root; 85 struct fw_filter *f; 86 int r; 87 u32 id = skb->mark; 88 89 if (head != NULL) { 90 id &= head->mask; 91 for (f=head->ht[fw_hash(id)]; f; f=f->next) { 92 if (f->id == id) { 93 *res = f->res; 94 #ifdef CONFIG_NET_CLS_IND 95 if (!tcf_match_indev(skb, f->indev)) 96 continue; 97 #endif /* CONFIG_NET_CLS_IND */ 98 r = tcf_exts_exec(skb, &f->exts, res); 99 if (r < 0) 100 continue; 101 102 return r; 103 } 104 } 105 } else { 106 /* old method */ 107 if (id && (TC_H_MAJ(id) == 0 || !(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 inline void 142 fw_delete_filter(struct tcf_proto *tp, struct fw_filter *f) 143 { 144 tcf_unbind_filter(tp, &f->res); 145 tcf_exts_destroy(tp, &f->exts); 146 kfree(f); 147 } 148 149 static void fw_destroy(struct tcf_proto *tp) 150 { 151 struct fw_head *head = (struct fw_head*)xchg(&tp->root, NULL); 152 struct fw_filter *f; 153 int h; 154 155 if (head == NULL) 156 return; 157 158 for (h=0; h<HTSIZE; h++) { 159 while ((f=head->ht[h]) != NULL) { 160 head->ht[h] = f->next; 161 fw_delete_filter(tp, f); 162 } 163 } 164 kfree(head); 165 } 166 167 static int fw_delete(struct tcf_proto *tp, unsigned long arg) 168 { 169 struct fw_head *head = (struct fw_head*)tp->root; 170 struct fw_filter *f = (struct fw_filter*)arg; 171 struct fw_filter **fp; 172 173 if (head == NULL || f == NULL) 174 goto out; 175 176 for (fp=&head->ht[fw_hash(f->id)]; *fp; fp = &(*fp)->next) { 177 if (*fp == f) { 178 tcf_tree_lock(tp); 179 *fp = f->next; 180 tcf_tree_unlock(tp); 181 fw_delete_filter(tp, f); 182 return 0; 183 } 184 } 185 out: 186 return -EINVAL; 187 } 188 189 static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = { 190 [TCA_FW_CLASSID] = { .type = NLA_U32 }, 191 [TCA_FW_INDEV] = { .type = NLA_STRING, .len = IFNAMSIZ }, 192 [TCA_FW_MASK] = { .type = NLA_U32 }, 193 }; 194 195 static int 196 fw_change_attrs(struct tcf_proto *tp, struct fw_filter *f, 197 struct nlattr **tb, struct nlattr **tca, unsigned long base) 198 { 199 struct fw_head *head = (struct fw_head *)tp->root; 200 struct tcf_exts e; 201 u32 mask; 202 int err; 203 204 err = tcf_exts_validate(tp, tb, tca[TCA_RATE], &e, &fw_ext_map); 205 if (err < 0) 206 return err; 207 208 err = -EINVAL; 209 if (tb[TCA_FW_CLASSID]) { 210 f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]); 211 tcf_bind_filter(tp, &f->res, base); 212 } 213 214 #ifdef CONFIG_NET_CLS_IND 215 if (tb[TCA_FW_INDEV]) { 216 err = tcf_change_indev(tp, f->indev, tb[TCA_FW_INDEV]); 217 if (err < 0) 218 goto errout; 219 } 220 #endif /* CONFIG_NET_CLS_IND */ 221 222 if (tb[TCA_FW_MASK]) { 223 mask = nla_get_u32(tb[TCA_FW_MASK]); 224 if (mask != head->mask) 225 goto errout; 226 } else if (head->mask != 0xFFFFFFFF) 227 goto errout; 228 229 tcf_exts_change(tp, &f->exts, &e); 230 231 return 0; 232 errout: 233 tcf_exts_destroy(tp, &e); 234 return err; 235 } 236 237 static int fw_change(struct tcf_proto *tp, unsigned long base, 238 u32 handle, 239 struct nlattr **tca, 240 unsigned long *arg) 241 { 242 struct fw_head *head = (struct fw_head*)tp->root; 243 struct fw_filter *f = (struct fw_filter *) *arg; 244 struct nlattr *opt = tca[TCA_OPTIONS]; 245 struct nlattr *tb[TCA_FW_MAX + 1]; 246 int err; 247 248 if (!opt) 249 return handle ? -EINVAL : 0; 250 251 err = nla_parse_nested(tb, TCA_FW_MAX, opt, fw_policy); 252 if (err < 0) 253 return err; 254 255 if (f != NULL) { 256 if (f->id != handle && handle) 257 return -EINVAL; 258 return fw_change_attrs(tp, f, tb, tca, base); 259 } 260 261 if (!handle) 262 return -EINVAL; 263 264 if (head == NULL) { 265 u32 mask = 0xFFFFFFFF; 266 if (tb[TCA_FW_MASK]) 267 mask = nla_get_u32(tb[TCA_FW_MASK]); 268 269 head = kzalloc(sizeof(struct fw_head), GFP_KERNEL); 270 if (head == NULL) 271 return -ENOBUFS; 272 head->mask = mask; 273 274 tcf_tree_lock(tp); 275 tp->root = head; 276 tcf_tree_unlock(tp); 277 } 278 279 f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); 280 if (f == NULL) 281 return -ENOBUFS; 282 283 f->id = handle; 284 285 err = fw_change_attrs(tp, f, tb, tca, base); 286 if (err < 0) 287 goto errout; 288 289 f->next = head->ht[fw_hash(handle)]; 290 tcf_tree_lock(tp); 291 head->ht[fw_hash(handle)] = f; 292 tcf_tree_unlock(tp); 293 294 *arg = (unsigned long)f; 295 return 0; 296 297 errout: 298 kfree(f); 299 return err; 300 } 301 302 static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) 303 { 304 struct fw_head *head = (struct fw_head*)tp->root; 305 int h; 306 307 if (head == NULL) 308 arg->stop = 1; 309 310 if (arg->stop) 311 return; 312 313 for (h = 0; h < HTSIZE; h++) { 314 struct fw_filter *f; 315 316 for (f = head->ht[h]; f; f = f->next) { 317 if (arg->count < arg->skip) { 318 arg->count++; 319 continue; 320 } 321 if (arg->fn(tp, (unsigned long)f, arg) < 0) { 322 arg->stop = 1; 323 return; 324 } 325 arg->count++; 326 } 327 } 328 } 329 330 static int fw_dump(struct tcf_proto *tp, unsigned long fh, 331 struct sk_buff *skb, struct tcmsg *t) 332 { 333 struct fw_head *head = (struct fw_head *)tp->root; 334 struct fw_filter *f = (struct fw_filter*)fh; 335 unsigned char *b = skb_tail_pointer(skb); 336 struct nlattr *nest; 337 338 if (f == NULL) 339 return skb->len; 340 341 t->tcm_handle = f->id; 342 343 if (!f->res.classid && !tcf_exts_is_available(&f->exts)) 344 return skb->len; 345 346 nest = nla_nest_start(skb, TCA_OPTIONS); 347 if (nest == NULL) 348 goto nla_put_failure; 349 350 if (f->res.classid) 351 NLA_PUT_U32(skb, TCA_FW_CLASSID, f->res.classid); 352 #ifdef CONFIG_NET_CLS_IND 353 if (strlen(f->indev)) 354 NLA_PUT_STRING(skb, TCA_FW_INDEV, f->indev); 355 #endif /* CONFIG_NET_CLS_IND */ 356 if (head->mask != 0xFFFFFFFF) 357 NLA_PUT_U32(skb, TCA_FW_MASK, head->mask); 358 359 if (tcf_exts_dump(skb, &f->exts, &fw_ext_map) < 0) 360 goto nla_put_failure; 361 362 nla_nest_end(skb, nest); 363 364 if (tcf_exts_dump_stats(skb, &f->exts, &fw_ext_map) < 0) 365 goto nla_put_failure; 366 367 return skb->len; 368 369 nla_put_failure: 370 nlmsg_trim(skb, b); 371 return -1; 372 } 373 374 static struct tcf_proto_ops cls_fw_ops __read_mostly = { 375 .kind = "fw", 376 .classify = fw_classify, 377 .init = fw_init, 378 .destroy = fw_destroy, 379 .get = fw_get, 380 .put = fw_put, 381 .change = fw_change, 382 .delete = fw_delete, 383 .walk = fw_walk, 384 .dump = fw_dump, 385 .owner = THIS_MODULE, 386 }; 387 388 static int __init init_fw(void) 389 { 390 return register_tcf_proto_ops(&cls_fw_ops); 391 } 392 393 static void __exit exit_fw(void) 394 { 395 unregister_tcf_proto_ops(&cls_fw_ops); 396 } 397 398 module_init(init_fw) 399 module_exit(exit_fw) 400 MODULE_LICENSE("GPL"); 401