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