Home | History | Annotate | Download | only in cls
      1 /*
      2  * lib/route/cls/ematch.c	Extended Matches
      3  *
      4  *	This library is free software; you can redistribute it and/or
      5  *	modify it under the terms of the GNU Lesser General Public
      6  *	License as published by the Free Software Foundation version 2.1
      7  *	of the License.
      8  *
      9  * Copyright (c) 2008-2009 Thomas Graf <tgraf (at) suug.ch>
     10  */
     11 
     12 /**
     13  * @ingroup cls
     14  * @defgroup ematch Extended Match
     15  *
     16  * @{
     17  */
     18 
     19 #include <netlink-local.h>
     20 #include <netlink-tc.h>
     21 #include <netlink/netlink.h>
     22 #include <netlink/route/classifier.h>
     23 #include <netlink/route/classifier-modules.h>
     24 #include <netlink/route/cls/ematch.h>
     25 
     26 /**
     27  * @name Module Registration
     28  * @{
     29  */
     30 
     31 static NL_LIST_HEAD(ematch_ops_list);
     32 
     33 /**
     34  * Register ematch module
     35  * @arg ops		Module operations.
     36  *
     37  * @return 0 on success or a negative error code.
     38  */
     39 int rtnl_ematch_register(struct rtnl_ematch_ops *ops)
     40 {
     41 	if (rtnl_ematch_lookup_ops(ops->eo_kind))
     42 		return -NLE_EXIST;
     43 
     44 	nl_list_add_tail(&ops->eo_list, &ematch_ops_list);
     45 
     46 	return 0;
     47 }
     48 
     49 /**
     50  * Unregister ematch module
     51  * @arg ops		Module operations.
     52  *
     53  * @return 0 on success or a negative error code.
     54  */
     55 int rtnl_ematch_unregister(struct rtnl_ematch_ops *ops)
     56 {
     57 	struct rtnl_ematch_ops *o;
     58 
     59 	nl_list_for_each_entry(o, &ematch_ops_list, eo_list) {
     60 		if (ops->eo_kind == o->eo_kind) {
     61 			nl_list_del(&o->eo_list);
     62 			return 0;
     63 		}
     64 	}
     65 
     66 	return -NLE_OBJ_NOTFOUND;
     67 }
     68 
     69 /**
     70  * Lookup ematch module by kind
     71  * @arg kind		Module kind.
     72  *
     73  * @return Module operations or NULL if not found.
     74  */
     75 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind)
     76 {
     77 	struct rtnl_ematch_ops *ops;
     78 
     79 	nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
     80 		if (ops->eo_kind == kind)
     81 			return ops;
     82 
     83 	return NULL;
     84 }
     85 
     86 /**
     87  * Lookup ematch module by name
     88  * @arg name		Name of ematch module.
     89  *
     90  * @return Module operations or NULL if not fuond.
     91  */
     92 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_name(const char *name)
     93 {
     94 	struct rtnl_ematch_ops *ops;
     95 
     96 	nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
     97 		if (!strcasecmp(ops->eo_name, name))
     98 			return ops;
     99 
    100 	return NULL;
    101 }
    102 
    103 /** @} */
    104 
    105 /**
    106  * @name Match
    107  */
    108 
    109 struct rtnl_ematch *rtnl_ematch_alloc(struct rtnl_ematch_ops *ops)
    110 {
    111 	struct rtnl_ematch *e;
    112 	size_t len = sizeof(*e) + (ops ? ops->eo_datalen : 0);
    113 
    114 	if (!(e = calloc(1, len)))
    115 		return NULL;
    116 
    117 	NL_INIT_LIST_HEAD(&e->e_list);
    118 	NL_INIT_LIST_HEAD(&e->e_childs);
    119 
    120 	if (ops) {
    121 		e->e_ops = ops;
    122 		e->e_kind = ops->eo_kind;
    123 	}
    124 
    125 	return e;
    126 }
    127 
    128 /**
    129  * Add ematch to the end of the parent's list of children.
    130  * @arg parent		Parent ematch.
    131  * @arg child		Ematch to be added as new child of parent.
    132  */
    133 void rtnl_ematch_add_child(struct rtnl_ematch *parent,
    134 			   struct rtnl_ematch *child)
    135 {
    136 	nl_list_add_tail(&child->e_list, &parent->e_childs);
    137 }
    138 
    139 /**
    140  * Remove ematch from the list it is linked to.
    141  * @arg ematch		Ematch to be unlinked.
    142  */
    143 void rtnl_ematch_unlink(struct rtnl_ematch *ematch)
    144 {
    145 	nl_list_del(&ematch->e_list);
    146 }
    147 
    148 void rtnl_ematch_free(struct rtnl_ematch *ematch)
    149 {
    150 	if (!ematch)
    151 		return;
    152 
    153 	free(ematch);
    154 }
    155 
    156 void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags)
    157 {
    158 	ematch->e_flags |= flags;
    159 }
    160 
    161 void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags)
    162 {
    163 	ematch->e_flags &= ~flags;
    164 }
    165 
    166 uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch)
    167 {
    168 	return ematch->e_flags;
    169 }
    170 
    171 void *rtnl_ematch_data(struct rtnl_ematch *ematch)
    172 {
    173 	return ematch->e_data;
    174 }
    175 
    176 /** @} */
    177 
    178 /**
    179  * @name Tree
    180  */
    181 
    182 struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid)
    183 {
    184 	struct rtnl_ematch_tree *tree;
    185 
    186 	if (!(tree = calloc(1, sizeof(*tree))))
    187 		return NULL;
    188 
    189 	NL_INIT_LIST_HEAD(&tree->et_list);
    190 	tree->et_progid = progid;
    191 
    192 	return tree;
    193 }
    194 
    195 static void free_ematch_list(struct nl_list_head *head)
    196 {
    197 	struct rtnl_ematch *pos, *next;
    198 
    199 	nl_list_for_each_entry_safe(pos, next, head, e_list) {
    200 		if (!nl_list_empty(&pos->e_childs))
    201 			free_ematch_list(&pos->e_childs);
    202 		rtnl_ematch_free(pos);
    203 	}
    204 }
    205 
    206 void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree)
    207 {
    208 	if (!tree)
    209 		return;
    210 
    211 	free_ematch_list(&tree->et_list);
    212 	free(tree);
    213 }
    214 
    215 void rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *tree,
    216 			       struct rtnl_ematch *ematch)
    217 {
    218 	nl_list_add_tail(&ematch->e_list, &tree->et_list);
    219 }
    220 
    221 static inline uint32_t container_ref(struct rtnl_ematch *ematch)
    222 {
    223 	return *((uint32_t *) rtnl_ematch_data(ematch));
    224 }
    225 
    226 static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos,
    227 		     struct nl_list_head *root)
    228 {
    229 	struct rtnl_ematch *ematch;
    230 	int i;
    231 
    232 	for (i = pos; i < nmatches; i++) {
    233 		ematch = index[i];
    234 
    235 		nl_list_add_tail(&ematch->e_list, root);
    236 
    237 		if (ematch->e_kind == TCF_EM_CONTAINER)
    238 			link_tree(index, nmatches, container_ref(ematch),
    239 				  &ematch->e_childs);
    240 
    241 		if (!(ematch->e_flags & TCF_EM_REL_MASK))
    242 			return 0;
    243 	}
    244 
    245 	/* Last entry in chain can't possibly have no relation */
    246 	return -NLE_INVAL;
    247 }
    248 
    249 static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = {
    250 	[TCA_EMATCH_TREE_HDR]  = { .minlen=sizeof(struct tcf_ematch_tree_hdr) },
    251 	[TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED },
    252 };
    253 
    254 /**
    255  * Parse ematch netlink attributes
    256  *
    257  * @return 0 on success or a negative error code.
    258  */
    259 int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result)
    260 {
    261 	struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1];
    262 	struct tcf_ematch_tree_hdr *thdr;
    263 	struct rtnl_ematch_tree *tree;
    264 	struct rtnl_ematch **index;
    265 	int nmatches = 0, err, remaining;
    266 
    267 	err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy);
    268 	if (err < 0)
    269 		return err;
    270 
    271 	if (!tb[TCA_EMATCH_TREE_HDR])
    272 		return -NLE_MISSING_ATTR;
    273 
    274 	thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]);
    275 
    276 	/* Ignore empty trees */
    277 	if (thdr->nmatches == 0)
    278 		return 0;
    279 
    280 	if (!tb[TCA_EMATCH_TREE_LIST])
    281 		return -NLE_MISSING_ATTR;
    282 
    283 	if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) /
    284 			      nla_total_size(sizeof(struct tcf_ematch_hdr))))
    285 		return -NLE_INVAL;
    286 
    287 	if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *))))
    288 		return -NLE_NOMEM;
    289 
    290 	if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) {
    291 		err = -NLE_NOMEM;
    292 		goto errout;
    293 	}
    294 
    295 	nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) {
    296 		struct rtnl_ematch_ops *ops;
    297 		struct tcf_ematch_hdr *hdr;
    298 		struct rtnl_ematch *ematch;
    299 		void *data;
    300 		size_t len;
    301 
    302 		if (nla_len(a) < sizeof(*hdr)) {
    303 			err = -NLE_INVAL;
    304 			goto errout;
    305 		}
    306 
    307 		if (nmatches >= thdr->nmatches) {
    308 			err = -NLE_RANGE;
    309 			goto errout;
    310 		}
    311 
    312 		hdr = nla_data(a);
    313 		data = nla_data(a) + NLA_ALIGN(sizeof(*hdr));
    314 		len = nla_len(a) - NLA_ALIGN(sizeof(*hdr));
    315 
    316 		ops = rtnl_ematch_lookup_ops(hdr->kind);
    317 		if (ops && ops->eo_datalen && len < ops->eo_datalen) {
    318 			err = -NLE_INVAL;
    319 			goto errout;
    320 		}
    321 
    322 		if (!(ematch = rtnl_ematch_alloc(ops))) {
    323 			err = -NLE_NOMEM;
    324 			goto errout;
    325 		}
    326 
    327 		ematch->e_id = hdr->matchid;
    328 		ematch->e_kind = hdr->kind;
    329 		ematch->e_flags = hdr->flags;
    330 
    331 		if (ops && (err = ops->eo_parse(ematch, data, len)) < 0)
    332 			goto errout;
    333 
    334 		if (hdr->kind == TCF_EM_CONTAINER &&
    335 		    container_ref(ematch) >= thdr->nmatches) {
    336 			err = -NLE_INVAL;
    337 			goto errout;
    338 		}
    339 
    340 		index[nmatches++] = ematch;
    341 	}
    342 
    343 	if (nmatches != thdr->nmatches) {
    344 		err = -NLE_INVAL;
    345 		goto errout;
    346 	}
    347 
    348 	err = link_tree(index, nmatches, 0, &tree->et_list);
    349 	if (err < 0)
    350 		goto errout;
    351 
    352 	free(index);
    353 	*result = tree;
    354 
    355 	return 0;
    356 
    357 errout:
    358 	rtnl_ematch_tree_free(tree);
    359 	free(index);
    360 	return err;
    361 }
    362 
    363 static void dump_ematch_sequence(struct nl_list_head *head,
    364 				 struct nl_dump_params *p)
    365 {
    366 	struct rtnl_ematch *match;
    367 
    368 	nl_list_for_each_entry(match, head, e_list) {
    369 		if (match->e_flags & TCF_EM_INVERT)
    370 			nl_dump(p, "NOT ");
    371 
    372 		if (match->e_kind == TCF_EM_CONTAINER) {
    373 			nl_dump(p, "(");
    374 			dump_ematch_sequence(&match->e_childs, p);
    375 			nl_dump(p, ")");
    376 		} else if (!match->e_ops) {
    377 			nl_dump(p, "[unknown ematch %d]", match->e_kind);
    378 		} else {
    379 			nl_dump(p, "%s(", match->e_ops->eo_name);
    380 
    381 			if (match->e_ops->eo_dump)
    382 				match->e_ops->eo_dump(match, p);
    383 
    384 			nl_dump(p, ")");
    385 		}
    386 
    387 		switch (match->e_flags & TCF_EM_REL_MASK) {
    388 		case TCF_EM_REL_AND:
    389 			nl_dump(p, " AND ");
    390 			break;
    391 		case TCF_EM_REL_OR:
    392 			nl_dump(p, " OR ");
    393 			break;
    394 		default:
    395 			/* end of first level ematch sequence */
    396 			return;
    397 		}
    398 	}
    399 }
    400 
    401 void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree,
    402 			   struct nl_dump_params *p)
    403 {
    404 	dump_ematch_sequence(&tree->et_list, p);
    405 	nl_dump(p, "\n");
    406 }
    407 
    408 /** @} */
    409 
    410 /** @} */
    411