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