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