• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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  */
rtnl_ematch_register(struct rtnl_ematch_ops * ops)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  */
rtnl_ematch_unregister(struct rtnl_ematch_ops * ops)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  */
rtnl_ematch_lookup_ops(int kind)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  */
rtnl_ematch_lookup_ops_name(const char * name)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 
rtnl_ematch_alloc(struct rtnl_ematch_ops * ops)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  */
rtnl_ematch_add_child(struct rtnl_ematch * parent,struct rtnl_ematch * child)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  */
rtnl_ematch_unlink(struct rtnl_ematch * ematch)143 void rtnl_ematch_unlink(struct rtnl_ematch *ematch)
144 {
145 	nl_list_del(&ematch->e_list);
146 }
147 
rtnl_ematch_free(struct rtnl_ematch * ematch)148 void rtnl_ematch_free(struct rtnl_ematch *ematch)
149 {
150 	if (!ematch)
151 		return;
152 
153 	free(ematch);
154 }
155 
rtnl_ematch_set_flags(struct rtnl_ematch * ematch,uint16_t flags)156 void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags)
157 {
158 	ematch->e_flags |= flags;
159 }
160 
rtnl_ematch_unset_flags(struct rtnl_ematch * ematch,uint16_t flags)161 void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags)
162 {
163 	ematch->e_flags &= ~flags;
164 }
165 
rtnl_ematch_get_flags(struct rtnl_ematch * ematch)166 uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch)
167 {
168 	return ematch->e_flags;
169 }
170 
rtnl_ematch_data(struct rtnl_ematch * ematch)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 
rtnl_ematch_tree_alloc(uint16_t progid)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 
free_ematch_list(struct nl_list_head * head)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 
rtnl_ematch_tree_free(struct rtnl_ematch_tree * tree)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 
rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree * tree,struct rtnl_ematch * ematch)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 
container_ref(struct rtnl_ematch * ematch)221 static inline uint32_t container_ref(struct rtnl_ematch *ematch)
222 {
223 	return *((uint32_t *) rtnl_ematch_data(ematch));
224 }
225 
link_tree(struct rtnl_ematch * index[],int nmatches,int pos,struct nl_list_head * root)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  */
rtnl_ematch_parse(struct nlattr * attr,struct rtnl_ematch_tree ** result)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 
dump_ematch_sequence(struct nl_list_head * head,struct nl_dump_params * p)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 
rtnl_ematch_tree_dump(struct rtnl_ematch_tree * tree,struct nl_dump_params * p)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