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