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; }
int parse_ematch(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) { begin_argc = ematch_argc = *argc_p; begin_argv = ematch_argv = *argv_p; if (ematch_parse()) { int err = em_parse_error(EINVAL, NULL, NULL, NULL, "Parse error"); free_ematch_err(); return err; } free_ematch_err(); /* undo look ahead by parser */ ematch_argc++; ematch_argv--; if (ematch_root) { struct rtattr *tail, *tail_list; struct tcf_ematch_tree_hdr hdr = { .nmatches = flatten_tree(ematch_root, ematch_root), .progid = TCF_EM_PROG_TC }; tail = NLMSG_TAIL(n); addattr_l(n, MAX_MSG, tca_id, NULL, 0); addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_HDR, &hdr, sizeof(hdr)); tail_list = NLMSG_TAIL(n); addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_LIST, NULL, 0); if (parse_tree(n, ematch_root) < 0) return -1; tail_list->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail_list; tail->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail; } *argc_p = ematch_argc; *argv_p = ematch_argv; return 0; } static int print_ematch_seq(FILE *fd, struct rtattr **tb, int start, int prefix) { int n, i = start; struct tcf_ematch_hdr *hdr; int dlen; void *data; for (;;) { if (tb[i] == NULL) return -1; dlen = RTA_PAYLOAD(tb[i]) - sizeof(*hdr); data = (void *) RTA_DATA(tb[i]) + sizeof(*hdr); if (dlen < 0) return -1; hdr = RTA_DATA(tb[i]); if (hdr->flags & TCF_EM_INVERT) fprintf(fd, "NOT "); if (hdr->kind == 0) { __u32 ref; if (dlen < sizeof(__u32)) return -1; ref = *(__u32 *) data; fprintf(fd, "(\n"); for (n = 0; n <= prefix; n++) fprintf(fd, " "); if (print_ematch_seq(fd, tb, ref + 1, prefix + 1) < 0) return -1; for (n = 0; n < prefix; n++) fprintf(fd, " "); fprintf(fd, ") "); } else { struct ematch_util *e; e = get_ematch_kind_num(hdr->kind); if (e == NULL) fprintf(fd, "[unknown ematch %d]\n", hdr->kind); else { fprintf(fd, "%s(", e->kind); if (e->print_eopt(fd, hdr, data, dlen) < 0) return -1; fprintf(fd, ")\n"); } if (hdr->flags & TCF_EM_REL_MASK) for (n = 0; n < prefix; n++) fprintf(fd, " "); } switch (hdr->flags & TCF_EM_REL_MASK) { case TCF_EM_REL_AND: fprintf(fd, "AND "); break; case TCF_EM_REL_OR: fprintf(fd, "OR "); break; default: return 0; } i++; } return 0; }