libnl 2.0
|
00001 /* 00002 * lib/route/cls/ematch.c Extended Matches 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Lesser General Public 00006 * License as published by the Free Software Foundation version 2.1 00007 * of the License. 00008 * 00009 * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup cls 00014 * @defgroup ematch Extended Match 00015 * 00016 * @{ 00017 */ 00018 00019 #include <netlink-local.h> 00020 #include <netlink-tc.h> 00021 #include <netlink/netlink.h> 00022 #include <netlink/route/classifier.h> 00023 #include <netlink/route/classifier-modules.h> 00024 #include <netlink/route/cls/ematch.h> 00025 00026 /** 00027 * @name Module Registration 00028 * @{ 00029 */ 00030 00031 static NL_LIST_HEAD(ematch_ops_list); 00032 00033 /** 00034 * Register ematch module 00035 * @arg ops Module operations. 00036 * 00037 * @return 0 on success or a negative error code. 00038 */ 00039 int rtnl_ematch_register(struct rtnl_ematch_ops *ops) 00040 { 00041 if (rtnl_ematch_lookup_ops(ops->eo_kind)) 00042 return -NLE_EXIST; 00043 00044 nl_list_add_tail(&ops->eo_list, &ematch_ops_list); 00045 00046 return 0; 00047 } 00048 00049 /** 00050 * Unregister ematch module 00051 * @arg ops Module operations. 00052 * 00053 * @return 0 on success or a negative error code. 00054 */ 00055 int rtnl_ematch_unregister(struct rtnl_ematch_ops *ops) 00056 { 00057 struct rtnl_ematch_ops *o; 00058 00059 nl_list_for_each_entry(o, &ematch_ops_list, eo_list) { 00060 if (ops->eo_kind == o->eo_kind) { 00061 nl_list_del(&o->eo_list); 00062 return 0; 00063 } 00064 } 00065 00066 return -NLE_OBJ_NOTFOUND; 00067 } 00068 00069 /** 00070 * Lookup ematch module by kind 00071 * @arg kind Module kind. 00072 * 00073 * @return Module operations or NULL if not found. 00074 */ 00075 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind) 00076 { 00077 struct rtnl_ematch_ops *ops; 00078 00079 nl_list_for_each_entry(ops, &ematch_ops_list, eo_list) 00080 if (ops->eo_kind == kind) 00081 return ops; 00082 00083 return NULL; 00084 } 00085 00086 /** 00087 * Lookup ematch module by name 00088 * @arg name Name of ematch module. 00089 * 00090 * @return Module operations or NULL if not fuond. 00091 */ 00092 struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_name(const char *name) 00093 { 00094 struct rtnl_ematch_ops *ops; 00095 00096 nl_list_for_each_entry(ops, &ematch_ops_list, eo_list) 00097 if (!strcasecmp(ops->eo_name, name)) 00098 return ops; 00099 00100 return NULL; 00101 } 00102 00103 /** @} */ 00104 00105 /** 00106 * @name Match 00107 */ 00108 00109 struct rtnl_ematch *rtnl_ematch_alloc(struct rtnl_ematch_ops *ops) 00110 { 00111 struct rtnl_ematch *e; 00112 size_t len = sizeof(*e) + (ops ? ops->eo_datalen : 0); 00113 00114 if (!(e = calloc(1, len))) 00115 return NULL; 00116 00117 NL_INIT_LIST_HEAD(&e->e_list); 00118 NL_INIT_LIST_HEAD(&e->e_childs); 00119 00120 if (ops) { 00121 e->e_ops = ops; 00122 e->e_kind = ops->eo_kind; 00123 } 00124 00125 return e; 00126 } 00127 00128 /** 00129 * Add ematch to the end of the parent's list of children. 00130 * @arg parent Parent ematch. 00131 * @arg child Ematch to be added as new child of parent. 00132 */ 00133 void rtnl_ematch_add_child(struct rtnl_ematch *parent, 00134 struct rtnl_ematch *child) 00135 { 00136 nl_list_add_tail(&child->e_list, &parent->e_childs); 00137 } 00138 00139 /** 00140 * Remove ematch from the list it is linked to. 00141 * @arg ematch Ematch to be unlinked. 00142 */ 00143 void rtnl_ematch_unlink(struct rtnl_ematch *ematch) 00144 { 00145 nl_list_del(&ematch->e_list); 00146 } 00147 00148 void rtnl_ematch_free(struct rtnl_ematch *ematch) 00149 { 00150 if (!ematch) 00151 return; 00152 00153 free(ematch); 00154 } 00155 00156 void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags) 00157 { 00158 ematch->e_flags |= flags; 00159 } 00160 00161 void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags) 00162 { 00163 ematch->e_flags &= ~flags; 00164 } 00165 00166 uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch) 00167 { 00168 return ematch->e_flags; 00169 } 00170 00171 void *rtnl_ematch_data(struct rtnl_ematch *ematch) 00172 { 00173 return ematch->e_data; 00174 } 00175 00176 /** @} */ 00177 00178 /** 00179 * @name Tree 00180 */ 00181 00182 struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid) 00183 { 00184 struct rtnl_ematch_tree *tree; 00185 00186 if (!(tree = calloc(1, sizeof(*tree)))) 00187 return NULL; 00188 00189 NL_INIT_LIST_HEAD(&tree->et_list); 00190 tree->et_progid = progid; 00191 00192 return tree; 00193 } 00194 00195 static void free_ematch_list(struct nl_list_head *head) 00196 { 00197 struct rtnl_ematch *pos, *next; 00198 00199 nl_list_for_each_entry_safe(pos, next, head, e_list) { 00200 if (!nl_list_empty(&pos->e_childs)) 00201 free_ematch_list(&pos->e_childs); 00202 rtnl_ematch_free(pos); 00203 } 00204 } 00205 00206 void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree) 00207 { 00208 if (!tree) 00209 return; 00210 00211 free_ematch_list(&tree->et_list); 00212 free(tree); 00213 } 00214 00215 void rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *tree, 00216 struct rtnl_ematch *ematch) 00217 { 00218 nl_list_add_tail(&ematch->e_list, &tree->et_list); 00219 } 00220 00221 static inline uint32_t container_ref(struct rtnl_ematch *ematch) 00222 { 00223 return *((uint32_t *) rtnl_ematch_data(ematch)); 00224 } 00225 00226 static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos, 00227 struct nl_list_head *root) 00228 { 00229 struct rtnl_ematch *ematch; 00230 int i; 00231 00232 for (i = pos; i < nmatches; i++) { 00233 ematch = index[i]; 00234 00235 nl_list_add_tail(&ematch->e_list, root); 00236 00237 if (ematch->e_kind == TCF_EM_CONTAINER) 00238 link_tree(index, nmatches, container_ref(ematch), 00239 &ematch->e_childs); 00240 00241 if (!(ematch->e_flags & TCF_EM_REL_MASK)) 00242 return 0; 00243 } 00244 00245 /* Last entry in chain can't possibly have no relation */ 00246 return -NLE_INVAL; 00247 } 00248 00249 static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = { 00250 [TCA_EMATCH_TREE_HDR] = { .minlen=sizeof(struct tcf_ematch_tree_hdr) }, 00251 [TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED }, 00252 }; 00253 00254 /** 00255 * Parse ematch netlink attributes 00256 * 00257 * @return 0 on success or a negative error code. 00258 */ 00259 int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result) 00260 { 00261 struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1]; 00262 struct tcf_ematch_tree_hdr *thdr; 00263 struct rtnl_ematch_tree *tree; 00264 struct rtnl_ematch **index; 00265 int nmatches = 0, err, remaining; 00266 00267 err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy); 00268 if (err < 0) 00269 return err; 00270 00271 if (!tb[TCA_EMATCH_TREE_HDR]) 00272 return -NLE_MISSING_ATTR; 00273 00274 thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]); 00275 00276 /* Ignore empty trees */ 00277 if (thdr->nmatches == 0) 00278 return 0; 00279 00280 if (!tb[TCA_EMATCH_TREE_LIST]) 00281 return -NLE_MISSING_ATTR; 00282 00283 if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) / 00284 nla_total_size(sizeof(struct tcf_ematch_hdr)))) 00285 return -NLE_INVAL; 00286 00287 if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *)))) 00288 return -NLE_NOMEM; 00289 00290 if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) { 00291 err = -NLE_NOMEM; 00292 goto errout; 00293 } 00294 00295 nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) { 00296 struct rtnl_ematch_ops *ops; 00297 struct tcf_ematch_hdr *hdr; 00298 struct rtnl_ematch *ematch; 00299 void *data; 00300 size_t len; 00301 00302 if (nla_len(a) < sizeof(*hdr)) { 00303 err = -NLE_INVAL; 00304 goto errout; 00305 } 00306 00307 if (nmatches >= thdr->nmatches) { 00308 err = -NLE_RANGE; 00309 goto errout; 00310 } 00311 00312 hdr = nla_data(a); 00313 data = nla_data(a) + NLA_ALIGN(sizeof(*hdr)); 00314 len = nla_len(a) - NLA_ALIGN(sizeof(*hdr)); 00315 00316 ops = rtnl_ematch_lookup_ops(hdr->kind); 00317 if (ops && ops->eo_datalen && len < ops->eo_datalen) { 00318 err = -NLE_INVAL; 00319 goto errout; 00320 } 00321 00322 if (!(ematch = rtnl_ematch_alloc(ops))) { 00323 err = -NLE_NOMEM; 00324 goto errout; 00325 } 00326 00327 ematch->e_id = hdr->matchid; 00328 ematch->e_kind = hdr->kind; 00329 ematch->e_flags = hdr->flags; 00330 00331 if (ops && (err = ops->eo_parse(ematch, data, len)) < 0) 00332 goto errout; 00333 00334 if (hdr->kind == TCF_EM_CONTAINER && 00335 container_ref(ematch) >= thdr->nmatches) { 00336 err = -NLE_INVAL; 00337 goto errout; 00338 } 00339 00340 index[nmatches++] = ematch; 00341 } 00342 00343 if (nmatches != thdr->nmatches) { 00344 err = -NLE_INVAL; 00345 goto errout; 00346 } 00347 00348 err = link_tree(index, nmatches, 0, &tree->et_list); 00349 if (err < 0) 00350 goto errout; 00351 00352 free(index); 00353 *result = tree; 00354 00355 return 0; 00356 00357 errout: 00358 rtnl_ematch_tree_free(tree); 00359 free(index); 00360 return err; 00361 } 00362 00363 static void dump_ematch_sequence(struct nl_list_head *head, 00364 struct nl_dump_params *p) 00365 { 00366 struct rtnl_ematch *match; 00367 00368 nl_list_for_each_entry(match, head, e_list) { 00369 if (match->e_flags & TCF_EM_INVERT) 00370 nl_dump(p, "NOT "); 00371 00372 if (match->e_kind == TCF_EM_CONTAINER) { 00373 nl_dump(p, "("); 00374 dump_ematch_sequence(&match->e_childs, p); 00375 nl_dump(p, ")"); 00376 } else if (!match->e_ops) { 00377 nl_dump(p, "[unknown ematch %d]", match->e_kind); 00378 } else { 00379 nl_dump(p, "%s(", match->e_ops->eo_name); 00380 00381 if (match->e_ops->eo_dump) 00382 match->e_ops->eo_dump(match, p); 00383 00384 nl_dump(p, ")"); 00385 } 00386 00387 switch (match->e_flags & TCF_EM_REL_MASK) { 00388 case TCF_EM_REL_AND: 00389 nl_dump(p, " AND "); 00390 break; 00391 case TCF_EM_REL_OR: 00392 nl_dump(p, " OR "); 00393 break; 00394 default: 00395 /* end of first level ematch sequence */ 00396 return; 00397 } 00398 } 00399 } 00400 00401 void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree, 00402 struct nl_dump_params *p) 00403 { 00404 dump_ematch_sequence(&tree->et_list, p); 00405 nl_dump(p, "\n"); 00406 } 00407 00408 /** @} */ 00409 00410 /** @} */