xref: /openbmc/linux/net/sched/ematch.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * net/sched/ematch.c		Extended Match API
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Authors:	Thomas Graf <tgraf@suug.ch>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * ==========================================================================
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * An extended match (ematch) is a small classification tool not worth
101da177e4SLinus Torvalds  * writing a full classifier for. Ematches can be interconnected to form
111da177e4SLinus Torvalds  * a logic expression and get attached to classifiers to extend their
121da177e4SLinus Torvalds  * functionatlity.
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  * The userspace part transforms the logic expressions into an array
151da177e4SLinus Torvalds  * consisting of multiple sequences of interconnected ematches separated
161da177e4SLinus Torvalds  * by markers. Precedence is implemented by a special ematch kind
171da177e4SLinus Torvalds  * referencing a sequence beyond the marker of the current sequence
181da177e4SLinus Torvalds  * causing the current position in the sequence to be pushed onto a stack
191da177e4SLinus Torvalds  * to allow the current position to be overwritten by the position referenced
201da177e4SLinus Torvalds  * in the special ematch. Matching continues in the new sequence until a
211da177e4SLinus Torvalds  * marker is reached causing the position to be restored from the stack.
221da177e4SLinus Torvalds  *
231da177e4SLinus Torvalds  * Example:
241da177e4SLinus Torvalds  *          A AND (B1 OR B2) AND C AND D
251da177e4SLinus Torvalds  *
261da177e4SLinus Torvalds  *              ------->-PUSH-------
271da177e4SLinus Torvalds  *    -->--    /         -->--      \   -->--
281da177e4SLinus Torvalds  *   /     \  /         /     \      \ /     \
291da177e4SLinus Torvalds  * +-------+-------+-------+-------+-------+--------+
301da177e4SLinus Torvalds  * | A AND | B AND | C AND | D END | B1 OR | B2 END |
311da177e4SLinus Torvalds  * +-------+-------+-------+-------+-------+--------+
321da177e4SLinus Torvalds  *                    \                      /
331da177e4SLinus Torvalds  *                     --------<-POP---------
341da177e4SLinus Torvalds  *
351da177e4SLinus Torvalds  * where B is a virtual ematch referencing to sequence starting with B1.
361da177e4SLinus Torvalds  *
371da177e4SLinus Torvalds  * ==========================================================================
381da177e4SLinus Torvalds  *
391da177e4SLinus Torvalds  * How to write an ematch in 60 seconds
401da177e4SLinus Torvalds  * ------------------------------------
411da177e4SLinus Torvalds  *
421da177e4SLinus Torvalds  *   1) Provide a matcher function:
431da177e4SLinus Torvalds  *      static int my_match(struct sk_buff *skb, struct tcf_ematch *m,
441da177e4SLinus Torvalds  *                          struct tcf_pkt_info *info)
451da177e4SLinus Torvalds  *      {
461da177e4SLinus Torvalds  *      	struct mydata *d = (struct mydata *) m->data;
471da177e4SLinus Torvalds  *
481da177e4SLinus Torvalds  *      	if (...matching goes here...)
491da177e4SLinus Torvalds  *      		return 1;
501da177e4SLinus Torvalds  *      	else
511da177e4SLinus Torvalds  *      		return 0;
521da177e4SLinus Torvalds  *      }
531da177e4SLinus Torvalds  *
541da177e4SLinus Torvalds  *   2) Fill out a struct tcf_ematch_ops:
551da177e4SLinus Torvalds  *      static struct tcf_ematch_ops my_ops = {
561da177e4SLinus Torvalds  *      	.kind = unique id,
571da177e4SLinus Torvalds  *      	.datalen = sizeof(struct mydata),
581da177e4SLinus Torvalds  *      	.match = my_match,
591da177e4SLinus Torvalds  *      	.owner = THIS_MODULE,
601da177e4SLinus Torvalds  *      };
611da177e4SLinus Torvalds  *
621da177e4SLinus Torvalds  *   3) Register/Unregister your ematch:
631da177e4SLinus Torvalds  *      static int __init init_my_ematch(void)
641da177e4SLinus Torvalds  *      {
651da177e4SLinus Torvalds  *      	return tcf_em_register(&my_ops);
661da177e4SLinus Torvalds  *      }
671da177e4SLinus Torvalds  *
681da177e4SLinus Torvalds  *      static void __exit exit_my_ematch(void)
691da177e4SLinus Torvalds  *      {
704d24b52aSAlexey Dobriyan  *      	tcf_em_unregister(&my_ops);
711da177e4SLinus Torvalds  *      }
721da177e4SLinus Torvalds  *
731da177e4SLinus Torvalds  *      module_init(init_my_ematch);
741da177e4SLinus Torvalds  *      module_exit(exit_my_ematch);
751da177e4SLinus Torvalds  *
761da177e4SLinus Torvalds  *   4) By now you should have two more seconds left, barely enough to
771da177e4SLinus Torvalds  *      open up a beer to watch the compilation going.
781da177e4SLinus Torvalds  */
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds #include <linux/module.h>
815a0e3ad6STejun Heo #include <linux/slab.h>
821da177e4SLinus Torvalds #include <linux/types.h>
831da177e4SLinus Torvalds #include <linux/kernel.h>
841da177e4SLinus Torvalds #include <linux/errno.h>
851da177e4SLinus Torvalds #include <linux/rtnetlink.h>
861da177e4SLinus Torvalds #include <linux/skbuff.h>
871da177e4SLinus Torvalds #include <net/pkt_cls.h>
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds static LIST_HEAD(ematch_ops);
901da177e4SLinus Torvalds static DEFINE_RWLOCK(ematch_mod_lock);
911da177e4SLinus Torvalds 
tcf_em_lookup(u16 kind)92cc7ec456SEric Dumazet static struct tcf_ematch_ops *tcf_em_lookup(u16 kind)
931da177e4SLinus Torvalds {
941da177e4SLinus Torvalds 	struct tcf_ematch_ops *e = NULL;
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds 	read_lock(&ematch_mod_lock);
971da177e4SLinus Torvalds 	list_for_each_entry(e, &ematch_ops, link) {
981da177e4SLinus Torvalds 		if (kind == e->kind) {
991da177e4SLinus Torvalds 			if (!try_module_get(e->owner))
1001da177e4SLinus Torvalds 				e = NULL;
1011da177e4SLinus Torvalds 			read_unlock(&ematch_mod_lock);
1021da177e4SLinus Torvalds 			return e;
1031da177e4SLinus Torvalds 		}
1041da177e4SLinus Torvalds 	}
1051da177e4SLinus Torvalds 	read_unlock(&ematch_mod_lock);
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 	return NULL;
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds /**
1111da177e4SLinus Torvalds  * tcf_em_register - register an extended match
1121da177e4SLinus Torvalds  *
1131da177e4SLinus Torvalds  * @ops: ematch operations lookup table
1141da177e4SLinus Torvalds  *
1151da177e4SLinus Torvalds  * This function must be called by ematches to announce their presence.
1161da177e4SLinus Torvalds  * The given @ops must have kind set to a unique identifier and the
1171da177e4SLinus Torvalds  * callback match() must be implemented. All other callbacks are optional
1181da177e4SLinus Torvalds  * and a fallback implementation is used instead.
1191da177e4SLinus Torvalds  *
1201da177e4SLinus Torvalds  * Returns -EEXISTS if an ematch of the same kind has already registered.
1211da177e4SLinus Torvalds  */
tcf_em_register(struct tcf_ematch_ops * ops)1221da177e4SLinus Torvalds int tcf_em_register(struct tcf_ematch_ops *ops)
1231da177e4SLinus Torvalds {
1241da177e4SLinus Torvalds 	int err = -EEXIST;
1251da177e4SLinus Torvalds 	struct tcf_ematch_ops *e;
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 	if (ops->match == NULL)
1281da177e4SLinus Torvalds 		return -EINVAL;
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	write_lock(&ematch_mod_lock);
1311da177e4SLinus Torvalds 	list_for_each_entry(e, &ematch_ops, link)
1321da177e4SLinus Torvalds 		if (ops->kind == e->kind)
1331da177e4SLinus Torvalds 			goto errout;
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	list_add_tail(&ops->link, &ematch_ops);
1361da177e4SLinus Torvalds 	err = 0;
1371da177e4SLinus Torvalds errout:
1381da177e4SLinus Torvalds 	write_unlock(&ematch_mod_lock);
1391da177e4SLinus Torvalds 	return err;
1401da177e4SLinus Torvalds }
14162e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_em_register);
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds /**
14437f2ad2bSZheng Yongjun  * tcf_em_unregister - unregister and extended match
1451da177e4SLinus Torvalds  *
1461da177e4SLinus Torvalds  * @ops: ematch operations lookup table
1471da177e4SLinus Torvalds  *
1481da177e4SLinus Torvalds  * This function must be called by ematches to announce their disappearance
1491da177e4SLinus Torvalds  * for examples when the module gets unloaded. The @ops parameter must be
1501da177e4SLinus Torvalds  * the same as the one used for registration.
1511da177e4SLinus Torvalds  *
1521da177e4SLinus Torvalds  * Returns -ENOENT if no matching ematch was found.
1531da177e4SLinus Torvalds  */
tcf_em_unregister(struct tcf_ematch_ops * ops)1544d24b52aSAlexey Dobriyan void tcf_em_unregister(struct tcf_ematch_ops *ops)
1551da177e4SLinus Torvalds {
1561da177e4SLinus Torvalds 	write_lock(&ematch_mod_lock);
1574d24b52aSAlexey Dobriyan 	list_del(&ops->link);
1581da177e4SLinus Torvalds 	write_unlock(&ematch_mod_lock);
1591da177e4SLinus Torvalds }
16062e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_em_unregister);
1611da177e4SLinus Torvalds 
tcf_em_get_match(struct tcf_ematch_tree * tree,int index)1621da177e4SLinus Torvalds static inline struct tcf_ematch *tcf_em_get_match(struct tcf_ematch_tree *tree,
1631da177e4SLinus Torvalds 						  int index)
1641da177e4SLinus Torvalds {
1651da177e4SLinus Torvalds 	return &tree->matches[index];
1661da177e4SLinus Torvalds }
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 
tcf_em_validate(struct tcf_proto * tp,struct tcf_ematch_tree_hdr * tree_hdr,struct tcf_ematch * em,struct nlattr * nla,int idx)1691da177e4SLinus Torvalds static int tcf_em_validate(struct tcf_proto *tp,
1701da177e4SLinus Torvalds 			   struct tcf_ematch_tree_hdr *tree_hdr,
171add93b61SPatrick McHardy 			   struct tcf_ematch *em, struct nlattr *nla, int idx)
1721da177e4SLinus Torvalds {
1731da177e4SLinus Torvalds 	int err = -EINVAL;
174add93b61SPatrick McHardy 	struct tcf_ematch_hdr *em_hdr = nla_data(nla);
175add93b61SPatrick McHardy 	int data_len = nla_len(nla) - sizeof(*em_hdr);
1761da177e4SLinus Torvalds 	void *data = (void *) em_hdr + sizeof(*em_hdr);
177c1954561SJiri Pirko 	struct net *net = tp->chain->block->net;
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	if (!TCF_EM_REL_VALID(em_hdr->flags))
1801da177e4SLinus Torvalds 		goto errout;
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 	if (em_hdr->kind == TCF_EM_CONTAINER) {
1831da177e4SLinus Torvalds 		/* Special ematch called "container", carries an index
184cc7ec456SEric Dumazet 		 * referencing an external ematch sequence.
185cc7ec456SEric Dumazet 		 */
1861da177e4SLinus Torvalds 		u32 ref;
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 		if (data_len < sizeof(ref))
1891da177e4SLinus Torvalds 			goto errout;
1901da177e4SLinus Torvalds 		ref = *(u32 *) data;
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds 		if (ref >= tree_hdr->nmatches)
1931da177e4SLinus Torvalds 			goto errout;
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds 		/* We do not allow backward jumps to avoid loops and jumps
196cc7ec456SEric Dumazet 		 * to our own position are of course illegal.
197cc7ec456SEric Dumazet 		 */
1981da177e4SLinus Torvalds 		if (ref <= idx)
1991da177e4SLinus Torvalds 			goto errout;
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 		em->data = ref;
2031da177e4SLinus Torvalds 	} else {
2041da177e4SLinus Torvalds 		/* Note: This lookup will increase the module refcnt
2051da177e4SLinus Torvalds 		 * of the ematch module referenced. In case of a failure,
2061da177e4SLinus Torvalds 		 * a destroy function is called by the underlying layer
2071da177e4SLinus Torvalds 		 * which automatically releases the reference again, therefore
2081da177e4SLinus Torvalds 		 * the module MUST not be given back under any circumstances
2091da177e4SLinus Torvalds 		 * here. Be aware, the destroy function assumes that the
210cc7ec456SEric Dumazet 		 * module is held if the ops field is non zero.
211cc7ec456SEric Dumazet 		 */
2121da177e4SLinus Torvalds 		em->ops = tcf_em_lookup(em_hdr->kind);
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds 		if (em->ops == NULL) {
2151da177e4SLinus Torvalds 			err = -ENOENT;
21695a5afcaSJohannes Berg #ifdef CONFIG_MODULES
217db3d99c0SPatrick McHardy 			__rtnl_unlock();
218db3d99c0SPatrick McHardy 			request_module("ematch-kind-%u", em_hdr->kind);
219db3d99c0SPatrick McHardy 			rtnl_lock();
220db3d99c0SPatrick McHardy 			em->ops = tcf_em_lookup(em_hdr->kind);
221db3d99c0SPatrick McHardy 			if (em->ops) {
222db3d99c0SPatrick McHardy 				/* We dropped the RTNL mutex in order to
223db3d99c0SPatrick McHardy 				 * perform the module load. Tell the caller
224cc7ec456SEric Dumazet 				 * to replay the request.
225cc7ec456SEric Dumazet 				 */
226db3d99c0SPatrick McHardy 				module_put(em->ops->owner);
22734eea79eSIgnacy Gawędzki 				em->ops = NULL;
228db3d99c0SPatrick McHardy 				err = -EAGAIN;
229db3d99c0SPatrick McHardy 			}
230db3d99c0SPatrick McHardy #endif
2311da177e4SLinus Torvalds 			goto errout;
2321da177e4SLinus Torvalds 		}
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 		/* ematch module provides expected length of data, so we
235cc7ec456SEric Dumazet 		 * can do a basic sanity check.
236cc7ec456SEric Dumazet 		 */
2371da177e4SLinus Torvalds 		if (em->ops->datalen && data_len < em->ops->datalen)
2381da177e4SLinus Torvalds 			goto errout;
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 		if (em->ops->change) {
24155cd9f67SEric Dumazet 			err = -EINVAL;
24255cd9f67SEric Dumazet 			if (em_hdr->flags & TCF_EM_SIMPLE)
24355cd9f67SEric Dumazet 				goto errout;
24482a470f1SJohn Fastabend 			err = em->ops->change(net, data, data_len, em);
2451da177e4SLinus Torvalds 			if (err < 0)
2461da177e4SLinus Torvalds 				goto errout;
2471da177e4SLinus Torvalds 		} else if (data_len > 0) {
2481da177e4SLinus Torvalds 			/* ematch module doesn't provide an own change
2491da177e4SLinus Torvalds 			 * procedure and expects us to allocate and copy
2501da177e4SLinus Torvalds 			 * the ematch data.
2511da177e4SLinus Torvalds 			 *
2521da177e4SLinus Torvalds 			 * TCF_EM_SIMPLE may be specified stating that the
2531da177e4SLinus Torvalds 			 * data only consists of a u32 integer and the module
2541da177e4SLinus Torvalds 			 * does not expected a memory reference but rather
255cc7ec456SEric Dumazet 			 * the value carried.
256cc7ec456SEric Dumazet 			 */
2571da177e4SLinus Torvalds 			if (em_hdr->flags & TCF_EM_SIMPLE) {
258*9cd3fd20SCong Wang 				if (em->ops->datalen > 0)
259*9cd3fd20SCong Wang 					goto errout;
2601da177e4SLinus Torvalds 				if (data_len < sizeof(u32))
2611da177e4SLinus Torvalds 					goto errout;
2621da177e4SLinus Torvalds 				em->data = *(u32 *) data;
2631da177e4SLinus Torvalds 			} else {
264c7b1b249SArnaldo Carvalho de Melo 				void *v = kmemdup(data, data_len, GFP_KERNEL);
2651da177e4SLinus Torvalds 				if (v == NULL) {
2661da177e4SLinus Torvalds 					err = -ENOBUFS;
2671da177e4SLinus Torvalds 					goto errout;
2681da177e4SLinus Torvalds 				}
2691da177e4SLinus Torvalds 				em->data = (unsigned long) v;
2701da177e4SLinus Torvalds 			}
27161678d28SCong Wang 			em->datalen = data_len;
2721da177e4SLinus Torvalds 		}
2731da177e4SLinus Torvalds 	}
2741da177e4SLinus Torvalds 
2751da177e4SLinus Torvalds 	em->matchid = em_hdr->matchid;
2761da177e4SLinus Torvalds 	em->flags = em_hdr->flags;
27782a470f1SJohn Fastabend 	em->net = net;
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds 	err = 0;
2801da177e4SLinus Torvalds errout:
2811da177e4SLinus Torvalds 	return err;
2821da177e4SLinus Torvalds }
2831da177e4SLinus Torvalds 
2847a9c1bd4SPatrick McHardy static const struct nla_policy em_policy[TCA_EMATCH_TREE_MAX + 1] = {
2857a9c1bd4SPatrick McHardy 	[TCA_EMATCH_TREE_HDR]	= { .len = sizeof(struct tcf_ematch_tree_hdr) },
2867a9c1bd4SPatrick McHardy 	[TCA_EMATCH_TREE_LIST]	= { .type = NLA_NESTED },
2877a9c1bd4SPatrick McHardy };
2887a9c1bd4SPatrick McHardy 
2891da177e4SLinus Torvalds /**
2901da177e4SLinus Torvalds  * tcf_em_tree_validate - validate ematch config TLV and build ematch tree
2911da177e4SLinus Torvalds  *
2921da177e4SLinus Torvalds  * @tp: classifier kind handle
293add93b61SPatrick McHardy  * @nla: ematch tree configuration TLV
2941da177e4SLinus Torvalds  * @tree: destination ematch tree variable to store the resulting
2951da177e4SLinus Torvalds  *        ematch tree.
2961da177e4SLinus Torvalds  *
297add93b61SPatrick McHardy  * This function validates the given configuration TLV @nla and builds an
2981da177e4SLinus Torvalds  * ematch tree in @tree. The resulting tree must later be copied into
2991da177e4SLinus Torvalds  * the private classifier data using tcf_em_tree_change(). You MUST NOT
3001da177e4SLinus Torvalds  * provide the ematch tree variable of the private classifier data directly,
3011da177e4SLinus Torvalds  * the changes would not be locked properly.
3021da177e4SLinus Torvalds  *
3031da177e4SLinus Torvalds  * Returns a negative error code if the configuration TLV contains errors.
3041da177e4SLinus Torvalds  */
tcf_em_tree_validate(struct tcf_proto * tp,struct nlattr * nla,struct tcf_ematch_tree * tree)305add93b61SPatrick McHardy int tcf_em_tree_validate(struct tcf_proto *tp, struct nlattr *nla,
3061da177e4SLinus Torvalds 			 struct tcf_ematch_tree *tree)
3071da177e4SLinus Torvalds {
308cee63723SPatrick McHardy 	int idx, list_len, matches_len, err;
309add93b61SPatrick McHardy 	struct nlattr *tb[TCA_EMATCH_TREE_MAX + 1];
310add93b61SPatrick McHardy 	struct nlattr *rt_match, *rt_hdr, *rt_list;
3111da177e4SLinus Torvalds 	struct tcf_ematch_tree_hdr *tree_hdr;
3121da177e4SLinus Torvalds 	struct tcf_ematch *em;
3131da177e4SLinus Torvalds 
314b541ca2cSThomas Graf 	memset(tree, 0, sizeof(*tree));
315268bcca1SStephen Hemminger 	if (!nla)
316b541ca2cSThomas Graf 		return 0;
317b541ca2cSThomas Graf 
3188cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, TCA_EMATCH_TREE_MAX, nla,
3198cb08174SJohannes Berg 					  em_policy, NULL);
320cee63723SPatrick McHardy 	if (err < 0)
3211da177e4SLinus Torvalds 		goto errout;
3221da177e4SLinus Torvalds 
323cee63723SPatrick McHardy 	err = -EINVAL;
324add93b61SPatrick McHardy 	rt_hdr = tb[TCA_EMATCH_TREE_HDR];
325add93b61SPatrick McHardy 	rt_list = tb[TCA_EMATCH_TREE_LIST];
3261da177e4SLinus Torvalds 
3271da177e4SLinus Torvalds 	if (rt_hdr == NULL || rt_list == NULL)
3281da177e4SLinus Torvalds 		goto errout;
3291da177e4SLinus Torvalds 
330add93b61SPatrick McHardy 	tree_hdr = nla_data(rt_hdr);
3311da177e4SLinus Torvalds 	memcpy(&tree->hdr, tree_hdr, sizeof(*tree_hdr));
3321da177e4SLinus Torvalds 
333add93b61SPatrick McHardy 	rt_match = nla_data(rt_list);
334add93b61SPatrick McHardy 	list_len = nla_len(rt_list);
3351da177e4SLinus Torvalds 	matches_len = tree_hdr->nmatches * sizeof(*em);
3361da177e4SLinus Torvalds 
3370da974f4SPanagiotis Issaris 	tree->matches = kzalloc(matches_len, GFP_KERNEL);
3381da177e4SLinus Torvalds 	if (tree->matches == NULL)
3391da177e4SLinus Torvalds 		goto errout;
3401da177e4SLinus Torvalds 
341add93b61SPatrick McHardy 	/* We do not use nla_parse_nested here because the maximum
3421da177e4SLinus Torvalds 	 * number of attributes is unknown. This saves us the allocation
3431da177e4SLinus Torvalds 	 * for a tb buffer which would serve no purpose at all.
3441da177e4SLinus Torvalds 	 *
3451da177e4SLinus Torvalds 	 * The array of rt attributes is parsed in the order as they are
3461da177e4SLinus Torvalds 	 * provided, their type must be incremental from 1 to n. Even
3471da177e4SLinus Torvalds 	 * if it does not serve any real purpose, a failure of sticking
348cc7ec456SEric Dumazet 	 * to this policy will result in parsing failure.
349cc7ec456SEric Dumazet 	 */
350add93b61SPatrick McHardy 	for (idx = 0; nla_ok(rt_match, list_len); idx++) {
3511da177e4SLinus Torvalds 		err = -EINVAL;
3521da177e4SLinus Torvalds 
353add93b61SPatrick McHardy 		if (rt_match->nla_type != (idx + 1))
3541da177e4SLinus Torvalds 			goto errout_abort;
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds 		if (idx >= tree_hdr->nmatches)
3571da177e4SLinus Torvalds 			goto errout_abort;
3581da177e4SLinus Torvalds 
359add93b61SPatrick McHardy 		if (nla_len(rt_match) < sizeof(struct tcf_ematch_hdr))
3601da177e4SLinus Torvalds 			goto errout_abort;
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 		em = tcf_em_get_match(tree, idx);
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds 		err = tcf_em_validate(tp, tree_hdr, em, rt_match, idx);
3651da177e4SLinus Torvalds 		if (err < 0)
3661da177e4SLinus Torvalds 			goto errout_abort;
3671da177e4SLinus Torvalds 
368add93b61SPatrick McHardy 		rt_match = nla_next(rt_match, &list_len);
3691da177e4SLinus Torvalds 	}
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 	/* Check if the number of matches provided by userspace actually
3721da177e4SLinus Torvalds 	 * complies with the array of matches. The number was used for
3731da177e4SLinus Torvalds 	 * the validation of references and a mismatch could lead to
374cc7ec456SEric Dumazet 	 * undefined references during the matching process.
375cc7ec456SEric Dumazet 	 */
3761da177e4SLinus Torvalds 	if (idx != tree_hdr->nmatches) {
3771da177e4SLinus Torvalds 		err = -EINVAL;
3781da177e4SLinus Torvalds 		goto errout_abort;
3791da177e4SLinus Torvalds 	}
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds 	err = 0;
3821da177e4SLinus Torvalds errout:
3831da177e4SLinus Torvalds 	return err;
3841da177e4SLinus Torvalds 
3851da177e4SLinus Torvalds errout_abort:
38682a470f1SJohn Fastabend 	tcf_em_tree_destroy(tree);
3871da177e4SLinus Torvalds 	return err;
3881da177e4SLinus Torvalds }
38962e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_em_tree_validate);
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds /**
3921da177e4SLinus Torvalds  * tcf_em_tree_destroy - destroy an ematch tree
3931da177e4SLinus Torvalds  *
3941da177e4SLinus Torvalds  * @tree: ematch tree to be deleted
3951da177e4SLinus Torvalds  *
3961da177e4SLinus Torvalds  * This functions destroys an ematch tree previously created by
3971da177e4SLinus Torvalds  * tcf_em_tree_validate()/tcf_em_tree_change(). You must ensure that
3981da177e4SLinus Torvalds  * the ematch tree is not in use before calling this function.
3991da177e4SLinus Torvalds  */
tcf_em_tree_destroy(struct tcf_ematch_tree * tree)40082a470f1SJohn Fastabend void tcf_em_tree_destroy(struct tcf_ematch_tree *tree)
4011da177e4SLinus Torvalds {
4021da177e4SLinus Torvalds 	int i;
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds 	if (tree->matches == NULL)
4051da177e4SLinus Torvalds 		return;
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	for (i = 0; i < tree->hdr.nmatches; i++) {
4081da177e4SLinus Torvalds 		struct tcf_ematch *em = tcf_em_get_match(tree, i);
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds 		if (em->ops) {
4111da177e4SLinus Torvalds 			if (em->ops->destroy)
41282a470f1SJohn Fastabend 				em->ops->destroy(em);
413954415e3SStephen Hemminger 			else if (!tcf_em_is_simple(em))
41430ddb159SDavid S. Miller 				kfree((void *) em->data);
4151da177e4SLinus Torvalds 			module_put(em->ops->owner);
4161da177e4SLinus Torvalds 		}
4171da177e4SLinus Torvalds 	}
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	tree->hdr.nmatches = 0;
4201da177e4SLinus Torvalds 	kfree(tree->matches);
421954415e3SStephen Hemminger 	tree->matches = NULL;
4221da177e4SLinus Torvalds }
42362e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_em_tree_destroy);
4241da177e4SLinus Torvalds 
4251da177e4SLinus Torvalds /**
4261da177e4SLinus Torvalds  * tcf_em_tree_dump - dump ematch tree into a rtnl message
4271da177e4SLinus Torvalds  *
4281da177e4SLinus Torvalds  * @skb: skb holding the rtnl message
42990ac5d03SAndrew Lunn  * @tree: ematch tree to be dumped
4301da177e4SLinus Torvalds  * @tlv: TLV type to be used to encapsulate the tree
4311da177e4SLinus Torvalds  *
4321da177e4SLinus Torvalds  * This function dumps a ematch tree into a rtnl message. It is valid to
4331da177e4SLinus Torvalds  * call this function while the ematch tree is in use.
4341da177e4SLinus Torvalds  *
4351da177e4SLinus Torvalds  * Returns -1 if the skb tailroom is insufficient.
4361da177e4SLinus Torvalds  */
tcf_em_tree_dump(struct sk_buff * skb,struct tcf_ematch_tree * tree,int tlv)4371da177e4SLinus Torvalds int tcf_em_tree_dump(struct sk_buff *skb, struct tcf_ematch_tree *tree, int tlv)
4381da177e4SLinus Torvalds {
4391da177e4SLinus Torvalds 	int i;
44027a884dcSArnaldo Carvalho de Melo 	u8 *tail;
4414b3550efSPatrick McHardy 	struct nlattr *top_start;
442add93b61SPatrick McHardy 	struct nlattr *list_start;
4431da177e4SLinus Torvalds 
444ae0be8deSMichal Kubecek 	top_start = nla_nest_start_noflag(skb, tlv);
4454b3550efSPatrick McHardy 	if (top_start == NULL)
4464b3550efSPatrick McHardy 		goto nla_put_failure;
4474b3550efSPatrick McHardy 
4481b34ec43SDavid S. Miller 	if (nla_put(skb, TCA_EMATCH_TREE_HDR, sizeof(tree->hdr), &tree->hdr))
4491b34ec43SDavid S. Miller 		goto nla_put_failure;
4501da177e4SLinus Torvalds 
451ae0be8deSMichal Kubecek 	list_start = nla_nest_start_noflag(skb, TCA_EMATCH_TREE_LIST);
4524b3550efSPatrick McHardy 	if (list_start == NULL)
4534b3550efSPatrick McHardy 		goto nla_put_failure;
4541da177e4SLinus Torvalds 
45527a884dcSArnaldo Carvalho de Melo 	tail = skb_tail_pointer(skb);
4561da177e4SLinus Torvalds 	for (i = 0; i < tree->hdr.nmatches; i++) {
457add93b61SPatrick McHardy 		struct nlattr *match_start = (struct nlattr *)tail;
4581da177e4SLinus Torvalds 		struct tcf_ematch *em = tcf_em_get_match(tree, i);
4591da177e4SLinus Torvalds 		struct tcf_ematch_hdr em_hdr = {
4601da177e4SLinus Torvalds 			.kind = em->ops ? em->ops->kind : TCF_EM_CONTAINER,
4611da177e4SLinus Torvalds 			.matchid = em->matchid,
4621da177e4SLinus Torvalds 			.flags = em->flags
4631da177e4SLinus Torvalds 		};
4641da177e4SLinus Torvalds 
4651b34ec43SDavid S. Miller 		if (nla_put(skb, i + 1, sizeof(em_hdr), &em_hdr))
4661b34ec43SDavid S. Miller 			goto nla_put_failure;
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds 		if (em->ops && em->ops->dump) {
4691da177e4SLinus Torvalds 			if (em->ops->dump(skb, em) < 0)
470add93b61SPatrick McHardy 				goto nla_put_failure;
4711da177e4SLinus Torvalds 		} else if (tcf_em_is_container(em) || tcf_em_is_simple(em)) {
4721da177e4SLinus Torvalds 			u32 u = em->data;
473add93b61SPatrick McHardy 			nla_put_nohdr(skb, sizeof(u), &u);
4741da177e4SLinus Torvalds 		} else if (em->datalen > 0)
475add93b61SPatrick McHardy 			nla_put_nohdr(skb, em->datalen, (void *) em->data);
4761da177e4SLinus Torvalds 
47727a884dcSArnaldo Carvalho de Melo 		tail = skb_tail_pointer(skb);
478add93b61SPatrick McHardy 		match_start->nla_len = tail - (u8 *)match_start;
4791da177e4SLinus Torvalds 	}
4801da177e4SLinus Torvalds 
4814b3550efSPatrick McHardy 	nla_nest_end(skb, list_start);
4824b3550efSPatrick McHardy 	nla_nest_end(skb, top_start);
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds 	return 0;
4851da177e4SLinus Torvalds 
486add93b61SPatrick McHardy nla_put_failure:
4871da177e4SLinus Torvalds 	return -1;
4881da177e4SLinus Torvalds }
48962e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_em_tree_dump);
4901da177e4SLinus Torvalds 
tcf_em_match(struct sk_buff * skb,struct tcf_ematch * em,struct tcf_pkt_info * info)4911da177e4SLinus Torvalds static inline int tcf_em_match(struct sk_buff *skb, struct tcf_ematch *em,
4921da177e4SLinus Torvalds 			       struct tcf_pkt_info *info)
4931da177e4SLinus Torvalds {
4941da177e4SLinus Torvalds 	int r = em->ops->match(skb, em, info);
495cc7ec456SEric Dumazet 
4961da177e4SLinus Torvalds 	return tcf_em_is_inverted(em) ? !r : r;
4971da177e4SLinus Torvalds }
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds /* Do not use this function directly, use tcf_em_tree_match instead */
__tcf_em_tree_match(struct sk_buff * skb,struct tcf_ematch_tree * tree,struct tcf_pkt_info * info)5001da177e4SLinus Torvalds int __tcf_em_tree_match(struct sk_buff *skb, struct tcf_ematch_tree *tree,
5011da177e4SLinus Torvalds 			struct tcf_pkt_info *info)
5021da177e4SLinus Torvalds {
5031da177e4SLinus Torvalds 	int stackp = 0, match_idx = 0, res = 0;
5041da177e4SLinus Torvalds 	struct tcf_ematch *cur_match;
5051da177e4SLinus Torvalds 	int stack[CONFIG_NET_EMATCH_STACK];
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds proceed:
5081da177e4SLinus Torvalds 	while (match_idx < tree->hdr.nmatches) {
5091da177e4SLinus Torvalds 		cur_match = tcf_em_get_match(tree, match_idx);
5101da177e4SLinus Torvalds 
5111da177e4SLinus Torvalds 		if (tcf_em_is_container(cur_match)) {
5121da177e4SLinus Torvalds 			if (unlikely(stackp >= CONFIG_NET_EMATCH_STACK))
5131da177e4SLinus Torvalds 				goto stack_overflow;
5141da177e4SLinus Torvalds 
5151da177e4SLinus Torvalds 			stack[stackp++] = match_idx;
5161da177e4SLinus Torvalds 			match_idx = cur_match->data;
5171da177e4SLinus Torvalds 			goto proceed;
5181da177e4SLinus Torvalds 		}
5191da177e4SLinus Torvalds 
5201da177e4SLinus Torvalds 		res = tcf_em_match(skb, cur_match, info);
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds 		if (tcf_em_early_end(cur_match, res))
5231da177e4SLinus Torvalds 			break;
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 		match_idx++;
5261da177e4SLinus Torvalds 	}
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds pop_stack:
5291da177e4SLinus Torvalds 	if (stackp > 0) {
5301da177e4SLinus Torvalds 		match_idx = stack[--stackp];
5311da177e4SLinus Torvalds 		cur_match = tcf_em_get_match(tree, match_idx);
5321da177e4SLinus Torvalds 
53317c9c823SIgnacy Gawędzki 		if (tcf_em_is_inverted(cur_match))
53417c9c823SIgnacy Gawędzki 			res = !res;
53534a419d4SIgnacy Gawędzki 
53634a419d4SIgnacy Gawędzki 		if (tcf_em_early_end(cur_match, res)) {
5371da177e4SLinus Torvalds 			goto pop_stack;
53817c9c823SIgnacy Gawędzki 		} else {
5391da177e4SLinus Torvalds 			match_idx++;
5401da177e4SLinus Torvalds 			goto proceed;
5411da177e4SLinus Torvalds 		}
5421da177e4SLinus Torvalds 	}
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	return res;
5451da177e4SLinus Torvalds 
5461da177e4SLinus Torvalds stack_overflow:
547e87cc472SJoe Perches 	net_warn_ratelimited("tc ematch: local stack overflow, increase NET_EMATCH_STACK\n");
5481da177e4SLinus Torvalds 	return -1;
5491da177e4SLinus Torvalds }
5501da177e4SLinus Torvalds EXPORT_SYMBOL(__tcf_em_tree_match);
551