/* * Copyright (c) 2014, 2015 Patrick McHardy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include "internal.h" #include #include #include #include #include #include #include #include #include #include "data_reg.h" #include "expr_ops.h" struct nftnl_expr_dynset { enum nft_registers sreg_key; enum nft_registers sreg_data; enum nft_dynset_ops op; uint64_t timeout; struct list_head expr_list; char *set_name; uint32_t set_id; uint32_t dynset_flags; }; static int nftnl_expr_dynset_set(struct nftnl_expr *e, uint16_t type, const void *data, uint32_t data_len) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); struct nftnl_expr *expr, *next; switch (type) { case NFTNL_EXPR_DYNSET_SREG_KEY: memcpy(&dynset->sreg_key, data, sizeof(dynset->sreg_key)); break; case NFTNL_EXPR_DYNSET_SREG_DATA: memcpy(&dynset->sreg_data, data, sizeof(dynset->sreg_data)); break; case NFTNL_EXPR_DYNSET_OP: memcpy(&dynset->op, data, sizeof(dynset->op)); break; case NFTNL_EXPR_DYNSET_TIMEOUT: memcpy(&dynset->timeout, data, sizeof(dynset->timeout)); break; case NFTNL_EXPR_DYNSET_SET_NAME: dynset->set_name = strdup((const char *)data); if (!dynset->set_name) return -1; break; case NFTNL_EXPR_DYNSET_SET_ID: memcpy(&dynset->set_id, data, sizeof(dynset->set_id)); break; case NFTNL_EXPR_DYNSET_EXPR: list_for_each_entry_safe(expr, next, &dynset->expr_list, head) nftnl_expr_free(expr); expr = (void *)data; list_add(&expr->head, &dynset->expr_list); break; case NFTNL_EXPR_DYNSET_FLAGS: memcpy(&dynset->dynset_flags, data, sizeof(dynset->dynset_flags)); break; default: return -1; } return 0; } static const void * nftnl_expr_dynset_get(const struct nftnl_expr *e, uint16_t type, uint32_t *data_len) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); struct nftnl_expr *expr; switch (type) { case NFTNL_EXPR_DYNSET_SREG_KEY: *data_len = sizeof(dynset->sreg_key); return &dynset->sreg_key; case NFTNL_EXPR_DYNSET_SREG_DATA: *data_len = sizeof(dynset->sreg_data); return &dynset->sreg_data; case NFTNL_EXPR_DYNSET_OP: *data_len = sizeof(dynset->op); return &dynset->op; case NFTNL_EXPR_DYNSET_TIMEOUT: *data_len = sizeof(dynset->timeout); return &dynset->timeout; case NFTNL_EXPR_DYNSET_SET_NAME: *data_len = strlen(dynset->set_name) + 1; return dynset->set_name; case NFTNL_EXPR_DYNSET_SET_ID: *data_len = sizeof(dynset->set_id); return &dynset->set_id; case NFTNL_EXPR_DYNSET_EXPR: list_for_each_entry(expr, &dynset->expr_list, head) break; return expr; case NFTNL_EXPR_DYNSET_FLAGS: *data_len = sizeof(dynset->dynset_flags); return &dynset->dynset_flags; } return NULL; } static int nftnl_expr_dynset_cb(const struct nlattr *attr, void *data) { const struct nlattr **tb = data; int type = mnl_attr_get_type(attr); if (mnl_attr_type_valid(attr, NFTA_SET_MAX) < 0) return MNL_CB_OK; switch (type) { case NFTA_DYNSET_SREG_KEY: case NFTA_DYNSET_SREG_DATA: case NFTA_DYNSET_SET_ID: case NFTA_DYNSET_OP: case NFTA_DYNSET_FLAGS: if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) abi_breakage(); break; case NFTA_DYNSET_TIMEOUT: if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) abi_breakage(); break; case NFTA_DYNSET_SET_NAME: if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) abi_breakage(); break; case NFTA_DYNSET_EXPR: if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) abi_breakage(); break; } tb[type] = attr; return MNL_CB_OK; } static void nftnl_expr_dynset_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); struct nlattr *nest; int num_exprs = 0; if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_KEY)) mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_KEY, htonl(dynset->sreg_key)); if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA)) mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_DATA, htonl(dynset->sreg_data)); if (e->flags & (1 << NFTNL_EXPR_DYNSET_OP)) mnl_attr_put_u32(nlh, NFTA_DYNSET_OP, htonl(dynset->op)); if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT)) mnl_attr_put_u64(nlh, NFTA_DYNSET_TIMEOUT, htobe64(dynset->timeout)); if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_NAME)) mnl_attr_put_strz(nlh, NFTA_DYNSET_SET_NAME, dynset->set_name); if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_ID)) mnl_attr_put_u32(nlh, NFTA_DYNSET_SET_ID, htonl(dynset->set_id)); if (!list_empty(&dynset->expr_list)) { struct nftnl_expr *expr; list_for_each_entry(expr, &dynset->expr_list, head) num_exprs++; if (num_exprs == 1) { nest = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPR); list_for_each_entry(expr, &dynset->expr_list, head) nftnl_expr_build_payload(nlh, expr); mnl_attr_nest_end(nlh, nest); } else if (num_exprs > 1) { struct nlattr *nest1, *nest2; nest1 = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPRESSIONS); list_for_each_entry(expr, &dynset->expr_list, head) { nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM); nftnl_expr_build_payload(nlh, expr); mnl_attr_nest_end(nlh, nest2); } mnl_attr_nest_end(nlh, nest1); } } if (e->flags & (1 << NFTNL_EXPR_DYNSET_FLAGS)) mnl_attr_put_u32(nlh, NFTA_DYNSET_FLAGS, htonl(dynset->dynset_flags)); } EXPORT_SYMBOL(nftnl_expr_add_expr); void nftnl_expr_add_expr(struct nftnl_expr *e, uint32_t type, struct nftnl_expr *expr) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); list_add_tail(&expr->head, &dynset->expr_list); } EXPORT_SYMBOL(nftnl_expr_expr_foreach); int nftnl_expr_expr_foreach(const struct nftnl_expr *e, int (*cb)(struct nftnl_expr *e, void *data), void *data) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); struct nftnl_expr *cur, *tmp; int ret; list_for_each_entry_safe(cur, tmp, &dynset->expr_list, head) { ret = cb(cur, data); if (ret < 0) return ret; } return 0; } static int nftnl_expr_dynset_parse(struct nftnl_expr *e, struct nlattr *attr) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); struct nlattr *tb[NFTA_SET_MAX+1] = {}; struct nftnl_expr *expr, *next; int ret = 0; if (mnl_attr_parse_nested(attr, nftnl_expr_dynset_cb, tb) < 0) return -1; if (tb[NFTA_DYNSET_SREG_KEY]) { dynset->sreg_key = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_KEY])); e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_KEY); } if (tb[NFTA_DYNSET_SREG_DATA]) { dynset->sreg_data = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_DATA])); e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_DATA); } if (tb[NFTA_DYNSET_OP]) { dynset->op = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_OP])); e->flags |= (1 << NFTNL_EXPR_DYNSET_OP); } if (tb[NFTA_DYNSET_TIMEOUT]) { dynset->timeout = be64toh(mnl_attr_get_u64(tb[NFTA_DYNSET_TIMEOUT])); e->flags |= (1 << NFTNL_EXPR_DYNSET_TIMEOUT); } if (tb[NFTA_DYNSET_SET_NAME]) { dynset->set_name = strdup(mnl_attr_get_str(tb[NFTA_DYNSET_SET_NAME])); if (!dynset->set_name) return -1; e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_NAME); } if (tb[NFTA_DYNSET_SET_ID]) { dynset->set_id = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SET_ID])); e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_ID); } if (tb[NFTA_DYNSET_EXPR]) { expr = nftnl_expr_parse(tb[NFTA_DYNSET_EXPR]); if (expr == NULL) return -1; list_add(&expr->head, &dynset->expr_list); e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPR); } else if (tb[NFTA_DYNSET_EXPRESSIONS]) { struct nlattr *attr2; mnl_attr_for_each_nested(attr2, tb[NFTA_DYNSET_EXPRESSIONS]) { if (mnl_attr_get_type(attr2) != NFTA_LIST_ELEM) goto out_dynset_expr; expr = nftnl_expr_parse(attr2); if (!expr) goto out_dynset_expr; list_add_tail(&expr->head, &dynset->expr_list); } e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPRESSIONS); } if (tb[NFTA_DYNSET_FLAGS]) { dynset->dynset_flags = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_FLAGS])); e->flags |= (1 << NFTNL_EXPR_DYNSET_FLAGS); } return ret; out_dynset_expr: list_for_each_entry_safe(expr, next, &dynset->expr_list, head) nftnl_expr_free(expr); return -1; } static const char *op2str_array[] = { [NFT_DYNSET_OP_ADD] = "add", [NFT_DYNSET_OP_UPDATE] = "update", [NFT_DYNSET_OP_DELETE] = "delete", }; static const char *op2str(enum nft_dynset_ops op) { if (op > NFT_DYNSET_OP_DELETE) return "unknown"; return op2str_array[op]; } static int nftnl_expr_dynset_snprintf_default(char *buf, size_t size, const struct nftnl_expr *e) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); struct nftnl_expr *expr; int remain = size, offset = 0, ret; ret = snprintf(buf, remain, "%s reg_key %u set %s ", op2str(dynset->op), dynset->sreg_key, dynset->set_name); SNPRINTF_BUFFER_SIZE(ret, remain, offset); if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA)) { ret = snprintf(buf + offset, remain, "sreg_data %u ", dynset->sreg_data); SNPRINTF_BUFFER_SIZE(ret, remain, offset); } if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT)) { ret = snprintf(buf + offset, remain, "timeout %"PRIu64"ms ", dynset->timeout); SNPRINTF_BUFFER_SIZE(ret, remain, offset); } list_for_each_entry(expr, &dynset->expr_list, head) { ret = snprintf(buf + offset, remain, "expr [ %s ", expr->ops->name); SNPRINTF_BUFFER_SIZE(ret, remain, offset); ret = nftnl_expr_snprintf(buf + offset, remain, expr, NFTNL_OUTPUT_DEFAULT, NFTNL_OF_EVENT_ANY); SNPRINTF_BUFFER_SIZE(ret, remain, offset); ret = snprintf(buf + offset, remain, "] "); SNPRINTF_BUFFER_SIZE(ret, remain, offset); } return offset; } static int nftnl_expr_dynset_snprintf(char *buf, size_t size, uint32_t type, uint32_t flags, const struct nftnl_expr *e) { switch (type) { case NFTNL_OUTPUT_DEFAULT: return nftnl_expr_dynset_snprintf_default(buf, size, e); case NFTNL_OUTPUT_XML: case NFTNL_OUTPUT_JSON: default: break; } return -1; } static void nftnl_expr_dynset_init(const struct nftnl_expr *e) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); INIT_LIST_HEAD(&dynset->expr_list); } static void nftnl_expr_dynset_free(const struct nftnl_expr *e) { struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); struct nftnl_expr *expr, *next; xfree(dynset->set_name); list_for_each_entry_safe(expr, next, &dynset->expr_list, head) nftnl_expr_free(expr); } struct expr_ops expr_ops_dynset = { .name = "dynset", .alloc_len = sizeof(struct nftnl_expr_dynset), .max_attr = NFTA_DYNSET_MAX, .init = nftnl_expr_dynset_init, .free = nftnl_expr_dynset_free, .set = nftnl_expr_dynset_set, .get = nftnl_expr_dynset_get, .parse = nftnl_expr_dynset_parse, .build = nftnl_expr_dynset_build, .snprintf = nftnl_expr_dynset_snprintf, };