| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * net/sched/ematch.c		Extended Match API | 
 | 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:	Thomas Graf <tgraf@suug.ch> | 
 | 10 |  * | 
 | 11 |  * ========================================================================== | 
 | 12 |  * | 
 | 13 |  * An extended match (ematch) is a small classification tool not worth | 
 | 14 |  * writing a full classifier for. Ematches can be interconnected to form | 
 | 15 |  * a logic expression and get attached to classifiers to extend their | 
 | 16 |  * functionatlity. | 
 | 17 |  * | 
 | 18 |  * The userspace part transforms the logic expressions into an array | 
 | 19 |  * consisting of multiple sequences of interconnected ematches separated | 
 | 20 |  * by markers. Precedence is implemented by a special ematch kind | 
 | 21 |  * referencing a sequence beyond the marker of the current sequence | 
 | 22 |  * causing the current position in the sequence to be pushed onto a stack | 
 | 23 |  * to allow the current position to be overwritten by the position referenced | 
 | 24 |  * in the special ematch. Matching continues in the new sequence until a | 
 | 25 |  * marker is reached causing the position to be restored from the stack. | 
 | 26 |  * | 
 | 27 |  * Example: | 
 | 28 |  *          A AND (B1 OR B2) AND C AND D | 
 | 29 |  * | 
 | 30 |  *              ------->-PUSH------- | 
 | 31 |  *    -->--    /         -->--      \   -->-- | 
 | 32 |  *   /     \  /         /     \      \ /     \ | 
 | 33 |  * +-------+-------+-------+-------+-------+--------+ | 
 | 34 |  * | A AND | B AND | C AND | D END | B1 OR | B2 END | | 
 | 35 |  * +-------+-------+-------+-------+-------+--------+ | 
 | 36 |  *                    \                      / | 
 | 37 |  *                     --------<-POP--------- | 
 | 38 |  * | 
 | 39 |  * where B is a virtual ematch referencing to sequence starting with B1. | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 40 |  * | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 41 |  * ========================================================================== | 
 | 42 |  * | 
 | 43 |  * How to write an ematch in 60 seconds | 
 | 44 |  * ------------------------------------ | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 45 |  * | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 46 |  *   1) Provide a matcher function: | 
 | 47 |  *      static int my_match(struct sk_buff *skb, struct tcf_ematch *m, | 
 | 48 |  *                          struct tcf_pkt_info *info) | 
 | 49 |  *      { | 
 | 50 |  *      	struct mydata *d = (struct mydata *) m->data; | 
 | 51 |  * | 
 | 52 |  *      	if (...matching goes here...) | 
 | 53 |  *      		return 1; | 
 | 54 |  *      	else | 
 | 55 |  *      		return 0; | 
 | 56 |  *      } | 
 | 57 |  * | 
 | 58 |  *   2) Fill out a struct tcf_ematch_ops: | 
 | 59 |  *      static struct tcf_ematch_ops my_ops = { | 
 | 60 |  *      	.kind = unique id, | 
 | 61 |  *      	.datalen = sizeof(struct mydata), | 
 | 62 |  *      	.match = my_match, | 
 | 63 |  *      	.owner = THIS_MODULE, | 
 | 64 |  *      }; | 
 | 65 |  * | 
 | 66 |  *   3) Register/Unregister your ematch: | 
 | 67 |  *      static int __init init_my_ematch(void) | 
 | 68 |  *      { | 
 | 69 |  *      	return tcf_em_register(&my_ops); | 
 | 70 |  *      } | 
 | 71 |  * | 
 | 72 |  *      static void __exit exit_my_ematch(void) | 
 | 73 |  *      { | 
 | 74 |  *      	return tcf_em_unregister(&my_ops); | 
 | 75 |  *      } | 
 | 76 |  * | 
 | 77 |  *      module_init(init_my_ematch); | 
 | 78 |  *      module_exit(exit_my_ematch); | 
 | 79 |  * | 
 | 80 |  *   4) By now you should have two more seconds left, barely enough to | 
 | 81 |  *      open up a beer to watch the compilation going. | 
 | 82 |  */ | 
 | 83 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 | #include <linux/module.h> | 
 | 85 | #include <linux/types.h> | 
 | 86 | #include <linux/kernel.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 87 | #include <linux/errno.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 88 | #include <linux/rtnetlink.h> | 
 | 89 | #include <linux/skbuff.h> | 
 | 90 | #include <net/pkt_cls.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 91 |  | 
 | 92 | static LIST_HEAD(ematch_ops); | 
 | 93 | static DEFINE_RWLOCK(ematch_mod_lock); | 
 | 94 |  | 
 | 95 | static inline struct tcf_ematch_ops * tcf_em_lookup(u16 kind) | 
 | 96 | { | 
 | 97 | 	struct tcf_ematch_ops *e = NULL; | 
 | 98 |  | 
 | 99 | 	read_lock(&ematch_mod_lock); | 
 | 100 | 	list_for_each_entry(e, &ematch_ops, link) { | 
 | 101 | 		if (kind == e->kind) { | 
 | 102 | 			if (!try_module_get(e->owner)) | 
 | 103 | 				e = NULL; | 
 | 104 | 			read_unlock(&ematch_mod_lock); | 
 | 105 | 			return e; | 
 | 106 | 		} | 
 | 107 | 	} | 
 | 108 | 	read_unlock(&ematch_mod_lock); | 
 | 109 |  | 
 | 110 | 	return NULL; | 
 | 111 | } | 
 | 112 |  | 
 | 113 | /** | 
 | 114 |  * tcf_em_register - register an extended match | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 115 |  * | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 116 |  * @ops: ematch operations lookup table | 
 | 117 |  * | 
 | 118 |  * This function must be called by ematches to announce their presence. | 
 | 119 |  * The given @ops must have kind set to a unique identifier and the | 
 | 120 |  * callback match() must be implemented. All other callbacks are optional | 
 | 121 |  * and a fallback implementation is used instead. | 
 | 122 |  * | 
 | 123 |  * Returns -EEXISTS if an ematch of the same kind has already registered. | 
 | 124 |  */ | 
 | 125 | int tcf_em_register(struct tcf_ematch_ops *ops) | 
 | 126 | { | 
 | 127 | 	int err = -EEXIST; | 
 | 128 | 	struct tcf_ematch_ops *e; | 
 | 129 |  | 
 | 130 | 	if (ops->match == NULL) | 
 | 131 | 		return -EINVAL; | 
 | 132 |  | 
 | 133 | 	write_lock(&ematch_mod_lock); | 
 | 134 | 	list_for_each_entry(e, &ematch_ops, link) | 
 | 135 | 		if (ops->kind == e->kind) | 
 | 136 | 			goto errout; | 
 | 137 |  | 
 | 138 | 	list_add_tail(&ops->link, &ematch_ops); | 
 | 139 | 	err = 0; | 
 | 140 | errout: | 
 | 141 | 	write_unlock(&ematch_mod_lock); | 
 | 142 | 	return err; | 
 | 143 | } | 
| Patrick McHardy | 62e3ba1 | 2008-01-22 22:10:23 -0800 | [diff] [blame] | 144 | EXPORT_SYMBOL(tcf_em_register); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 145 |  | 
 | 146 | /** | 
 | 147 |  * tcf_em_unregister - unregster and extended match | 
 | 148 |  * | 
 | 149 |  * @ops: ematch operations lookup table | 
 | 150 |  * | 
 | 151 |  * This function must be called by ematches to announce their disappearance | 
 | 152 |  * for examples when the module gets unloaded. The @ops parameter must be | 
 | 153 |  * the same as the one used for registration. | 
 | 154 |  * | 
 | 155 |  * Returns -ENOENT if no matching ematch was found. | 
 | 156 |  */ | 
 | 157 | int tcf_em_unregister(struct tcf_ematch_ops *ops) | 
 | 158 | { | 
 | 159 | 	int err = 0; | 
 | 160 | 	struct tcf_ematch_ops *e; | 
 | 161 |  | 
 | 162 | 	write_lock(&ematch_mod_lock); | 
 | 163 | 	list_for_each_entry(e, &ematch_ops, link) { | 
 | 164 | 		if (e == ops) { | 
 | 165 | 			list_del(&e->link); | 
 | 166 | 			goto out; | 
 | 167 | 		} | 
 | 168 | 	} | 
 | 169 |  | 
 | 170 | 	err = -ENOENT; | 
 | 171 | out: | 
 | 172 | 	write_unlock(&ematch_mod_lock); | 
 | 173 | 	return err; | 
 | 174 | } | 
| Patrick McHardy | 62e3ba1 | 2008-01-22 22:10:23 -0800 | [diff] [blame] | 175 | EXPORT_SYMBOL(tcf_em_unregister); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 176 |  | 
 | 177 | static inline struct tcf_ematch * tcf_em_get_match(struct tcf_ematch_tree *tree, | 
 | 178 | 						   int index) | 
 | 179 | { | 
 | 180 | 	return &tree->matches[index]; | 
 | 181 | } | 
 | 182 |  | 
 | 183 |  | 
 | 184 | static int tcf_em_validate(struct tcf_proto *tp, | 
 | 185 | 			   struct tcf_ematch_tree_hdr *tree_hdr, | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 186 | 			   struct tcf_ematch *em, struct nlattr *nla, int idx) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 187 | { | 
 | 188 | 	int err = -EINVAL; | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 189 | 	struct tcf_ematch_hdr *em_hdr = nla_data(nla); | 
 | 190 | 	int data_len = nla_len(nla) - sizeof(*em_hdr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 191 | 	void *data = (void *) em_hdr + sizeof(*em_hdr); | 
 | 192 |  | 
 | 193 | 	if (!TCF_EM_REL_VALID(em_hdr->flags)) | 
 | 194 | 		goto errout; | 
 | 195 |  | 
 | 196 | 	if (em_hdr->kind == TCF_EM_CONTAINER) { | 
 | 197 | 		/* Special ematch called "container", carries an index | 
 | 198 | 		 * referencing an external ematch sequence. */ | 
 | 199 | 		u32 ref; | 
 | 200 |  | 
 | 201 | 		if (data_len < sizeof(ref)) | 
 | 202 | 			goto errout; | 
 | 203 | 		ref = *(u32 *) data; | 
 | 204 |  | 
 | 205 | 		if (ref >= tree_hdr->nmatches) | 
 | 206 | 			goto errout; | 
 | 207 |  | 
 | 208 | 		/* We do not allow backward jumps to avoid loops and jumps | 
 | 209 | 		 * to our own position are of course illegal. */ | 
 | 210 | 		if (ref <= idx) | 
 | 211 | 			goto errout; | 
 | 212 |  | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 213 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 214 | 		em->data = ref; | 
 | 215 | 	} else { | 
 | 216 | 		/* Note: This lookup will increase the module refcnt | 
 | 217 | 		 * of the ematch module referenced. In case of a failure, | 
 | 218 | 		 * a destroy function is called by the underlying layer | 
 | 219 | 		 * which automatically releases the reference again, therefore | 
 | 220 | 		 * the module MUST not be given back under any circumstances | 
 | 221 | 		 * here. Be aware, the destroy function assumes that the | 
 | 222 | 		 * module is held if the ops field is non zero. */ | 
 | 223 | 		em->ops = tcf_em_lookup(em_hdr->kind); | 
 | 224 |  | 
 | 225 | 		if (em->ops == NULL) { | 
 | 226 | 			err = -ENOENT; | 
| Johannes Berg | 95a5afc | 2008-10-16 15:24:51 -0700 | [diff] [blame] | 227 | #ifdef CONFIG_MODULES | 
| Patrick McHardy | db3d99c | 2007-07-11 19:46:26 -0700 | [diff] [blame] | 228 | 			__rtnl_unlock(); | 
 | 229 | 			request_module("ematch-kind-%u", em_hdr->kind); | 
 | 230 | 			rtnl_lock(); | 
 | 231 | 			em->ops = tcf_em_lookup(em_hdr->kind); | 
 | 232 | 			if (em->ops) { | 
 | 233 | 				/* We dropped the RTNL mutex in order to | 
 | 234 | 				 * perform the module load. Tell the caller | 
 | 235 | 				 * to replay the request. */ | 
 | 236 | 				module_put(em->ops->owner); | 
 | 237 | 				err = -EAGAIN; | 
 | 238 | 			} | 
 | 239 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 240 | 			goto errout; | 
 | 241 | 		} | 
 | 242 |  | 
 | 243 | 		/* ematch module provides expected length of data, so we | 
 | 244 | 		 * can do a basic sanity check. */ | 
 | 245 | 		if (em->ops->datalen && data_len < em->ops->datalen) | 
 | 246 | 			goto errout; | 
 | 247 |  | 
 | 248 | 		if (em->ops->change) { | 
 | 249 | 			err = em->ops->change(tp, data, data_len, em); | 
 | 250 | 			if (err < 0) | 
 | 251 | 				goto errout; | 
 | 252 | 		} else if (data_len > 0) { | 
 | 253 | 			/* ematch module doesn't provide an own change | 
 | 254 | 			 * procedure and expects us to allocate and copy | 
 | 255 | 			 * the ematch data. | 
 | 256 | 			 * | 
 | 257 | 			 * TCF_EM_SIMPLE may be specified stating that the | 
 | 258 | 			 * data only consists of a u32 integer and the module | 
 | 259 | 			 * does not expected a memory reference but rather | 
 | 260 | 			 * the value carried. */ | 
 | 261 | 			if (em_hdr->flags & TCF_EM_SIMPLE) { | 
 | 262 | 				if (data_len < sizeof(u32)) | 
 | 263 | 					goto errout; | 
 | 264 | 				em->data = *(u32 *) data; | 
 | 265 | 			} else { | 
| Arnaldo Carvalho de Melo | c7b1b24 | 2006-11-21 01:19:40 -0200 | [diff] [blame] | 266 | 				void *v = kmemdup(data, data_len, GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 267 | 				if (v == NULL) { | 
 | 268 | 					err = -ENOBUFS; | 
 | 269 | 					goto errout; | 
 | 270 | 				} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 271 | 				em->data = (unsigned long) v; | 
 | 272 | 			} | 
 | 273 | 		} | 
 | 274 | 	} | 
 | 275 |  | 
 | 276 | 	em->matchid = em_hdr->matchid; | 
 | 277 | 	em->flags = em_hdr->flags; | 
 | 278 | 	em->datalen = data_len; | 
 | 279 |  | 
 | 280 | 	err = 0; | 
 | 281 | errout: | 
 | 282 | 	return err; | 
 | 283 | } | 
 | 284 |  | 
| Patrick McHardy | 7a9c1bd | 2008-01-23 20:36:45 -0800 | [diff] [blame] | 285 | static const struct nla_policy em_policy[TCA_EMATCH_TREE_MAX + 1] = { | 
 | 286 | 	[TCA_EMATCH_TREE_HDR]	= { .len = sizeof(struct tcf_ematch_tree_hdr) }, | 
 | 287 | 	[TCA_EMATCH_TREE_LIST]	= { .type = NLA_NESTED }, | 
 | 288 | }; | 
 | 289 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 290 | /** | 
 | 291 |  * tcf_em_tree_validate - validate ematch config TLV and build ematch tree | 
 | 292 |  * | 
 | 293 |  * @tp: classifier kind handle | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 294 |  * @nla: ematch tree configuration TLV | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 295 |  * @tree: destination ematch tree variable to store the resulting | 
 | 296 |  *        ematch tree. | 
 | 297 |  * | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 298 |  * This function validates the given configuration TLV @nla and builds an | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 299 |  * ematch tree in @tree. The resulting tree must later be copied into | 
 | 300 |  * the private classifier data using tcf_em_tree_change(). You MUST NOT | 
 | 301 |  * provide the ematch tree variable of the private classifier data directly, | 
 | 302 |  * the changes would not be locked properly. | 
 | 303 |  * | 
 | 304 |  * Returns a negative error code if the configuration TLV contains errors. | 
 | 305 |  */ | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 306 | int tcf_em_tree_validate(struct tcf_proto *tp, struct nlattr *nla, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 307 | 			 struct tcf_ematch_tree *tree) | 
 | 308 | { | 
| Patrick McHardy | cee6372 | 2008-01-23 20:33:32 -0800 | [diff] [blame] | 309 | 	int idx, list_len, matches_len, err; | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 310 | 	struct nlattr *tb[TCA_EMATCH_TREE_MAX + 1]; | 
 | 311 | 	struct nlattr *rt_match, *rt_hdr, *rt_list; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 312 | 	struct tcf_ematch_tree_hdr *tree_hdr; | 
 | 313 | 	struct tcf_ematch *em; | 
 | 314 |  | 
| Stephen Hemminger | 268bcca | 2008-02-09 03:47:19 -0800 | [diff] [blame] | 315 | 	memset(tree, 0, sizeof(*tree)); | 
 | 316 | 	if (!nla) | 
| Thomas Graf | b541ca2 | 2005-11-08 09:39:17 -0800 | [diff] [blame] | 317 | 		return 0; | 
| Thomas Graf | b541ca2 | 2005-11-08 09:39:17 -0800 | [diff] [blame] | 318 |  | 
| Patrick McHardy | 7a9c1bd | 2008-01-23 20:36:45 -0800 | [diff] [blame] | 319 | 	err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, nla, em_policy); | 
| Patrick McHardy | cee6372 | 2008-01-23 20:33:32 -0800 | [diff] [blame] | 320 | 	if (err < 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 321 | 		goto errout; | 
 | 322 |  | 
| Patrick McHardy | cee6372 | 2008-01-23 20:33:32 -0800 | [diff] [blame] | 323 | 	err = -EINVAL; | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 324 | 	rt_hdr = tb[TCA_EMATCH_TREE_HDR]; | 
 | 325 | 	rt_list = tb[TCA_EMATCH_TREE_LIST]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 326 |  | 
 | 327 | 	if (rt_hdr == NULL || rt_list == NULL) | 
 | 328 | 		goto errout; | 
 | 329 |  | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 330 | 	tree_hdr = nla_data(rt_hdr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 331 | 	memcpy(&tree->hdr, tree_hdr, sizeof(*tree_hdr)); | 
 | 332 |  | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 333 | 	rt_match = nla_data(rt_list); | 
 | 334 | 	list_len = nla_len(rt_list); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 335 | 	matches_len = tree_hdr->nmatches * sizeof(*em); | 
 | 336 |  | 
| Panagiotis Issaris | 0da974f | 2006-07-21 14:51:30 -0700 | [diff] [blame] | 337 | 	tree->matches = kzalloc(matches_len, GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 338 | 	if (tree->matches == NULL) | 
 | 339 | 		goto errout; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 340 |  | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 341 | 	/* We do not use nla_parse_nested here because the maximum | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 342 | 	 * number of attributes is unknown. This saves us the allocation | 
 | 343 | 	 * for a tb buffer which would serve no purpose at all. | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 344 | 	 * | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 345 | 	 * The array of rt attributes is parsed in the order as they are | 
 | 346 | 	 * provided, their type must be incremental from 1 to n. Even | 
 | 347 | 	 * if it does not serve any real purpose, a failure of sticking | 
 | 348 | 	 * to this policy will result in parsing failure. */ | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 349 | 	for (idx = 0; nla_ok(rt_match, list_len); idx++) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 350 | 		err = -EINVAL; | 
 | 351 |  | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 352 | 		if (rt_match->nla_type != (idx + 1)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 353 | 			goto errout_abort; | 
 | 354 |  | 
 | 355 | 		if (idx >= tree_hdr->nmatches) | 
 | 356 | 			goto errout_abort; | 
 | 357 |  | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 358 | 		if (nla_len(rt_match) < sizeof(struct tcf_ematch_hdr)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 359 | 			goto errout_abort; | 
 | 360 |  | 
 | 361 | 		em = tcf_em_get_match(tree, idx); | 
 | 362 |  | 
 | 363 | 		err = tcf_em_validate(tp, tree_hdr, em, rt_match, idx); | 
 | 364 | 		if (err < 0) | 
 | 365 | 			goto errout_abort; | 
 | 366 |  | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 367 | 		rt_match = nla_next(rt_match, &list_len); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 368 | 	} | 
 | 369 |  | 
 | 370 | 	/* Check if the number of matches provided by userspace actually | 
 | 371 | 	 * complies with the array of matches. The number was used for | 
 | 372 | 	 * the validation of references and a mismatch could lead to | 
 | 373 | 	 * undefined references during the matching process. */ | 
 | 374 | 	if (idx != tree_hdr->nmatches) { | 
 | 375 | 		err = -EINVAL; | 
 | 376 | 		goto errout_abort; | 
 | 377 | 	} | 
 | 378 |  | 
 | 379 | 	err = 0; | 
 | 380 | errout: | 
 | 381 | 	return err; | 
 | 382 |  | 
 | 383 | errout_abort: | 
 | 384 | 	tcf_em_tree_destroy(tp, tree); | 
 | 385 | 	return err; | 
 | 386 | } | 
| Patrick McHardy | 62e3ba1 | 2008-01-22 22:10:23 -0800 | [diff] [blame] | 387 | EXPORT_SYMBOL(tcf_em_tree_validate); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 388 |  | 
 | 389 | /** | 
 | 390 |  * tcf_em_tree_destroy - destroy an ematch tree | 
 | 391 |  * | 
 | 392 |  * @tp: classifier kind handle | 
 | 393 |  * @tree: ematch tree to be deleted | 
 | 394 |  * | 
 | 395 |  * This functions destroys an ematch tree previously created by | 
 | 396 |  * tcf_em_tree_validate()/tcf_em_tree_change(). You must ensure that | 
 | 397 |  * the ematch tree is not in use before calling this function. | 
 | 398 |  */ | 
 | 399 | void tcf_em_tree_destroy(struct tcf_proto *tp, struct tcf_ematch_tree *tree) | 
 | 400 | { | 
 | 401 | 	int i; | 
 | 402 |  | 
 | 403 | 	if (tree->matches == NULL) | 
 | 404 | 		return; | 
 | 405 |  | 
 | 406 | 	for (i = 0; i < tree->hdr.nmatches; i++) { | 
 | 407 | 		struct tcf_ematch *em = tcf_em_get_match(tree, i); | 
 | 408 |  | 
 | 409 | 		if (em->ops) { | 
 | 410 | 			if (em->ops->destroy) | 
 | 411 | 				em->ops->destroy(tp, em); | 
| Stephen Hemminger | 954415e | 2008-02-09 23:26:53 -0800 | [diff] [blame] | 412 | 			else if (!tcf_em_is_simple(em)) | 
| David S. Miller | 30ddb15 | 2008-02-10 03:48:15 -0800 | [diff] [blame] | 413 | 				kfree((void *) em->data); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 414 | 			module_put(em->ops->owner); | 
 | 415 | 		} | 
 | 416 | 	} | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 417 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 418 | 	tree->hdr.nmatches = 0; | 
 | 419 | 	kfree(tree->matches); | 
| Stephen Hemminger | 954415e | 2008-02-09 23:26:53 -0800 | [diff] [blame] | 420 | 	tree->matches = NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 421 | } | 
| Patrick McHardy | 62e3ba1 | 2008-01-22 22:10:23 -0800 | [diff] [blame] | 422 | EXPORT_SYMBOL(tcf_em_tree_destroy); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 423 |  | 
 | 424 | /** | 
 | 425 |  * tcf_em_tree_dump - dump ematch tree into a rtnl message | 
 | 426 |  * | 
 | 427 |  * @skb: skb holding the rtnl message | 
 | 428 |  * @t: ematch tree to be dumped | 
 | 429 |  * @tlv: TLV type to be used to encapsulate the tree | 
 | 430 |  * | 
 | 431 |  * This function dumps a ematch tree into a rtnl message. It is valid to | 
 | 432 |  * call this function while the ematch tree is in use. | 
 | 433 |  * | 
 | 434 |  * Returns -1 if the skb tailroom is insufficient. | 
 | 435 |  */ | 
 | 436 | int tcf_em_tree_dump(struct sk_buff *skb, struct tcf_ematch_tree *tree, int tlv) | 
 | 437 | { | 
 | 438 | 	int i; | 
| Arnaldo Carvalho de Melo | 27a884d | 2007-04-19 20:29:13 -0700 | [diff] [blame] | 439 | 	u8 *tail; | 
| Patrick McHardy | 4b3550ef | 2008-01-23 20:34:11 -0800 | [diff] [blame] | 440 | 	struct nlattr *top_start; | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 441 | 	struct nlattr *list_start; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 442 |  | 
| Patrick McHardy | 4b3550ef | 2008-01-23 20:34:11 -0800 | [diff] [blame] | 443 | 	top_start = nla_nest_start(skb, tlv); | 
 | 444 | 	if (top_start == NULL) | 
 | 445 | 		goto nla_put_failure; | 
 | 446 |  | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 447 | 	NLA_PUT(skb, TCA_EMATCH_TREE_HDR, sizeof(tree->hdr), &tree->hdr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 448 |  | 
| Patrick McHardy | 4b3550ef | 2008-01-23 20:34:11 -0800 | [diff] [blame] | 449 | 	list_start = nla_nest_start(skb, TCA_EMATCH_TREE_LIST); | 
 | 450 | 	if (list_start == NULL) | 
 | 451 | 		goto nla_put_failure; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 452 |  | 
| Arnaldo Carvalho de Melo | 27a884d | 2007-04-19 20:29:13 -0700 | [diff] [blame] | 453 | 	tail = skb_tail_pointer(skb); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 454 | 	for (i = 0; i < tree->hdr.nmatches; i++) { | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 455 | 		struct nlattr *match_start = (struct nlattr *)tail; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 456 | 		struct tcf_ematch *em = tcf_em_get_match(tree, i); | 
 | 457 | 		struct tcf_ematch_hdr em_hdr = { | 
 | 458 | 			.kind = em->ops ? em->ops->kind : TCF_EM_CONTAINER, | 
 | 459 | 			.matchid = em->matchid, | 
 | 460 | 			.flags = em->flags | 
 | 461 | 		}; | 
 | 462 |  | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 463 | 		NLA_PUT(skb, i+1, sizeof(em_hdr), &em_hdr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 464 |  | 
 | 465 | 		if (em->ops && em->ops->dump) { | 
 | 466 | 			if (em->ops->dump(skb, em) < 0) | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 467 | 				goto nla_put_failure; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 468 | 		} else if (tcf_em_is_container(em) || tcf_em_is_simple(em)) { | 
 | 469 | 			u32 u = em->data; | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 470 | 			nla_put_nohdr(skb, sizeof(u), &u); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 471 | 		} else if (em->datalen > 0) | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 472 | 			nla_put_nohdr(skb, em->datalen, (void *) em->data); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 473 |  | 
| Arnaldo Carvalho de Melo | 27a884d | 2007-04-19 20:29:13 -0700 | [diff] [blame] | 474 | 		tail = skb_tail_pointer(skb); | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 475 | 		match_start->nla_len = tail - (u8 *)match_start; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 476 | 	} | 
 | 477 |  | 
| Patrick McHardy | 4b3550ef | 2008-01-23 20:34:11 -0800 | [diff] [blame] | 478 | 	nla_nest_end(skb, list_start); | 
 | 479 | 	nla_nest_end(skb, top_start); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 480 |  | 
 | 481 | 	return 0; | 
 | 482 |  | 
| Patrick McHardy | add93b6 | 2008-01-22 22:11:33 -0800 | [diff] [blame] | 483 | nla_put_failure: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 484 | 	return -1; | 
 | 485 | } | 
| Patrick McHardy | 62e3ba1 | 2008-01-22 22:10:23 -0800 | [diff] [blame] | 486 | EXPORT_SYMBOL(tcf_em_tree_dump); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 487 |  | 
 | 488 | static inline int tcf_em_match(struct sk_buff *skb, struct tcf_ematch *em, | 
 | 489 | 			       struct tcf_pkt_info *info) | 
 | 490 | { | 
 | 491 | 	int r = em->ops->match(skb, em, info); | 
 | 492 | 	return tcf_em_is_inverted(em) ? !r : r; | 
 | 493 | } | 
 | 494 |  | 
 | 495 | /* Do not use this function directly, use tcf_em_tree_match instead */ | 
 | 496 | int __tcf_em_tree_match(struct sk_buff *skb, struct tcf_ematch_tree *tree, | 
 | 497 | 			struct tcf_pkt_info *info) | 
 | 498 | { | 
 | 499 | 	int stackp = 0, match_idx = 0, res = 0; | 
 | 500 | 	struct tcf_ematch *cur_match; | 
 | 501 | 	int stack[CONFIG_NET_EMATCH_STACK]; | 
 | 502 |  | 
 | 503 | proceed: | 
 | 504 | 	while (match_idx < tree->hdr.nmatches) { | 
 | 505 | 		cur_match = tcf_em_get_match(tree, match_idx); | 
 | 506 |  | 
 | 507 | 		if (tcf_em_is_container(cur_match)) { | 
 | 508 | 			if (unlikely(stackp >= CONFIG_NET_EMATCH_STACK)) | 
 | 509 | 				goto stack_overflow; | 
 | 510 |  | 
 | 511 | 			stack[stackp++] = match_idx; | 
 | 512 | 			match_idx = cur_match->data; | 
 | 513 | 			goto proceed; | 
 | 514 | 		} | 
 | 515 |  | 
 | 516 | 		res = tcf_em_match(skb, cur_match, info); | 
 | 517 |  | 
 | 518 | 		if (tcf_em_early_end(cur_match, res)) | 
 | 519 | 			break; | 
 | 520 |  | 
 | 521 | 		match_idx++; | 
 | 522 | 	} | 
 | 523 |  | 
 | 524 | pop_stack: | 
 | 525 | 	if (stackp > 0) { | 
 | 526 | 		match_idx = stack[--stackp]; | 
 | 527 | 		cur_match = tcf_em_get_match(tree, match_idx); | 
 | 528 |  | 
 | 529 | 		if (tcf_em_early_end(cur_match, res)) | 
 | 530 | 			goto pop_stack; | 
 | 531 | 		else { | 
 | 532 | 			match_idx++; | 
 | 533 | 			goto proceed; | 
 | 534 | 		} | 
 | 535 | 	} | 
 | 536 |  | 
 | 537 | 	return res; | 
 | 538 |  | 
 | 539 | stack_overflow: | 
 | 540 | 	if (net_ratelimit()) | 
 | 541 | 		printk("Local stack overflow, increase NET_EMATCH_STACK\n"); | 
 | 542 | 	return -1; | 
 | 543 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 544 | EXPORT_SYMBOL(__tcf_em_tree_match); |