static void cgroup_free_data(struct rtnl_tc *tc, void *data) { struct rtnl_cgroup *c = data; if (!c) return; rtnl_ematch_tree_free(c->cg_ematch); }
void rtnl_cgroup_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree) { struct rtnl_cgroup *c = rtnl_cls_data(cls); if (c->cg_ematch) { rtnl_ematch_tree_free(c->cg_ematch); c->cg_mask &= ~CGROUP_ATTR_EMATCH; } c->cg_ematch = tree; if (tree) c->cg_mask |= CGROUP_ATTR_EMATCH; }
static int fill_ematch_sequence(struct nl_msg *msg, struct nl_list_head *list) { struct rtnl_ematch *e; nl_list_for_each_entry(e, list, e_list) { struct tcf_ematch_hdr match = { .matchid = e->e_id, .kind = e->e_kind, .flags = e->e_flags, }; struct nlattr *attr; int err = 0; if (!(attr = nla_nest_start(msg, e->e_index + 1))) return -NLE_NOMEM; if (nlmsg_append(msg, &match, sizeof(match), 0) < 0) return -NLE_NOMEM; if (e->e_ops->eo_fill) err = e->e_ops->eo_fill(e, msg); else if (e->e_flags & TCF_EM_SIMPLE) err = nlmsg_append(msg, e->e_data, 4, 0); else if (e->e_datalen > 0) err = nlmsg_append(msg, e->e_data, e->e_datalen, 0); NL_DBG(3, "msg %p: added ematch [%d] id=%d kind=%d flags=%d\n", msg, e->e_index, match.matchid, match.kind, match.flags); if (err < 0) return -NLE_NOMEM; nla_nest_end(msg, attr); } nl_list_for_each_entry(e, list, e_list) { if (e->e_kind == TCF_EM_CONTAINER && fill_ematch_sequence(msg, &e->e_childs) < 0) return -NLE_NOMEM; } return 0; } int rtnl_ematch_fill_attr(struct nl_msg *msg, int attrid, struct rtnl_ematch_tree *tree) { struct tcf_ematch_tree_hdr thdr = { .progid = tree->et_progid, }; struct nlattr *list, *topattr; int err, index = 0; /* Assign index number to each ematch to allow for references * to be made while constructing the sequence of matches. */ err = update_container_index(&tree->et_list, &index); if (err < 0) return err; if (!(topattr = nla_nest_start(msg, attrid))) goto nla_put_failure; thdr.nmatches = index; NLA_PUT(msg, TCA_EMATCH_TREE_HDR, sizeof(thdr), &thdr); if (!(list = nla_nest_start(msg, TCA_EMATCH_TREE_LIST))) goto nla_put_failure; if (fill_ematch_sequence(msg, &tree->et_list) < 0) goto nla_put_failure; nla_nest_end(msg, list); nla_nest_end(msg, topattr); return 0; nla_put_failure: return -NLE_NOMEM; } /** @} */ extern int ematch_parse(void *, char **, struct nl_list_head *); int rtnl_ematch_parse_expr(const char *expr, char **errp, struct rtnl_ematch_tree **result) { struct rtnl_ematch_tree *tree; YY_BUFFER_STATE buf = NULL; yyscan_t scanner = NULL; int err; NL_DBG(2, "Parsing ematch expression \"%s\"\n", expr); if (!(tree = rtnl_ematch_tree_alloc(RTNL_EMATCH_PROGID))) return -NLE_FAILURE; if ((err = ematch_lex_init(&scanner)) < 0) { err = -NLE_FAILURE; goto errout; } buf = ematch__scan_string(expr, scanner); if ((err = ematch_parse(scanner, errp, &tree->et_list)) != 0) { ematch__delete_buffer(buf, scanner); err = -NLE_PARSE_ERR; goto errout; } ematch_lex_destroy(scanner); *result = tree; return 0; errout: if (scanner) ematch_lex_destroy(scanner); rtnl_ematch_tree_free(tree); return err; } static const char *layer_txt[] = { [TCF_LAYER_LINK] = "eth", [TCF_LAYER_NETWORK] = "ip", [TCF_LAYER_TRANSPORT] = "tcp", }; char *rtnl_ematch_offset2txt(uint8_t layer, uint16_t offset, char *buf, size_t len) { snprintf(buf, len, "%s+%u", (layer <= TCF_LAYER_MAX) ? layer_txt[layer] : "?", offset); return buf; } static const char *operand_txt[] = { [TCF_EM_OPND_EQ] = "=", [TCF_EM_OPND_LT] = "<", [TCF_EM_OPND_GT] = ">", }; char *rtnl_ematch_opnd2txt(uint8_t opnd, char *buf, size_t len) { snprintf(buf, len, "%s", opnd < ARRAY_SIZE(operand_txt) ? operand_txt[opnd] : "?"); return buf; }
/** * Parse ematch netlink attributes * * @return 0 on success or a negative error code. */ int rtnl_ematch_parse_attr(struct nlattr *attr, struct rtnl_ematch_tree **result) { struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1]; struct tcf_ematch_tree_hdr *thdr; struct rtnl_ematch_tree *tree; struct rtnl_ematch **index; int nmatches = 0, err, remaining; NL_DBG(2, "Parsing attribute %p as ematch tree\n", attr); err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy); if (err < 0) return err; if (!tb[TCA_EMATCH_TREE_HDR]) return -NLE_MISSING_ATTR; thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]); /* Ignore empty trees */ if (thdr->nmatches == 0) { NL_DBG(2, "Ignoring empty ematch configuration\n"); return 0; } if (!tb[TCA_EMATCH_TREE_LIST]) return -NLE_MISSING_ATTR; NL_DBG(2, "ematch tree found with nmatches=%u, progid=%u\n", thdr->nmatches, thdr->progid); /* * Do some basic sanity checking since we will allocate * index[thdr->nmatches]. Calculate how many ematch headers fit into * the provided data and make sure nmatches does not exceed it. */ if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) / nla_total_size(sizeof(struct tcf_ematch_hdr)))) return -NLE_INVAL; if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *)))) return -NLE_NOMEM; if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) { err = -NLE_NOMEM; goto errout; } nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) { struct rtnl_ematch_ops *ops; struct tcf_ematch_hdr *hdr; struct rtnl_ematch *ematch; void *data; size_t len; NL_DBG(3, "parsing ematch attribute %d, len=%u\n", nmatches+1, nla_len(a)); if (nla_len(a) < sizeof(*hdr)) { err = -NLE_INVAL; goto errout; } /* Quit as soon as we've parsed more matches than expected */ if (nmatches >= thdr->nmatches) { err = -NLE_RANGE; goto errout; } hdr = nla_data(a); data = nla_data(a) + NLA_ALIGN(sizeof(*hdr)); len = nla_len(a) - NLA_ALIGN(sizeof(*hdr)); NL_DBG(3, "ematch attribute matchid=%u, kind=%u, flags=%u\n", hdr->matchid, hdr->kind, hdr->flags); /* * Container matches contain a reference to another sequence * of matches. Ensure that the reference is within boundries. */ if (hdr->kind == TCF_EM_CONTAINER && *((uint32_t *) data) >= thdr->nmatches) { err = -NLE_INVAL; goto errout; } if (!(ematch = rtnl_ematch_alloc())) { err = -NLE_NOMEM; goto errout; } ematch->e_id = hdr->matchid; ematch->e_kind = hdr->kind; ematch->e_flags = hdr->flags; if ((ops = rtnl_ematch_lookup_ops(hdr->kind))) { if (ops->eo_minlen && len < ops->eo_minlen) { rtnl_ematch_free(ematch); err = -NLE_INVAL; goto errout; } rtnl_ematch_set_ops(ematch, ops); if (ops->eo_parse && (err = ops->eo_parse(ematch, data, len)) < 0) { rtnl_ematch_free(ematch); goto errout; } } NL_DBG(3, "index[%d] = %p\n", nmatches, ematch); index[nmatches++] = ematch; } if (nmatches != thdr->nmatches) { err = -NLE_INVAL; goto errout; } err = link_tree(index, nmatches, 0, &tree->et_list); if (err < 0) goto errout; free(index); *result = tree; return 0; errout: rtnl_ematch_tree_free(tree); free(index); return err; }
static void cgroup_free_data(struct rtnl_cls *cls) { struct rtnl_cgroup *c = rtnl_cls_data(cls); rtnl_ematch_tree_free(c->cg_ematch); }