diff options
Diffstat (limited to '')
-rw-r--r-- | src/ruleset.c | 719 |
1 files changed, 719 insertions, 0 deletions
diff --git a/src/ruleset.c b/src/ruleset.c new file mode 100644 index 0000000..185aa48 --- /dev/null +++ b/src/ruleset.c @@ -0,0 +1,719 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * (C) 2013 by Arturo Borrero Gonzalez <arturo@debian.org> + * (C) 2013 by Alvaro Neira Ayuso <alvaroneay@gmail.com> + * + * 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. + * + * This code has been sponsored by Sophos Astaro <http://www.sophos.com> + */ + +#include <errno.h> + +#include "internal.h" +#include <stdlib.h> + +#include <libmnl/libmnl.h> +#include <libnftnl/ruleset.h> +#include <libnftnl/table.h> +#include <libnftnl/chain.h> +#include <libnftnl/set.h> +#include <libnftnl/rule.h> + +struct nftnl_ruleset { + struct nftnl_table_list *table_list; + struct nftnl_chain_list *chain_list; + struct nftnl_set_list *set_list; + struct nftnl_rule_list *rule_list; + + uint16_t flags; +}; + +struct nftnl_parse_ctx { + enum nftnl_cmd_type cmd; + enum nftnl_ruleset_type type; + union { + struct nftnl_table *table; + struct nftnl_chain *chain; + struct nftnl_rule *rule; + struct nftnl_set *set; + struct nftnl_set_elem *set_elem; + }; + void *data; + + /* These fields below are not exposed to the user */ + uint32_t format; + uint32_t set_id; + struct nftnl_set_list *set_list; + + int (*cb)(const struct nftnl_parse_ctx *ctx); + uint16_t flags; +}; + +EXPORT_SYMBOL(nftnl_ruleset_alloc); +struct nftnl_ruleset *nftnl_ruleset_alloc(void) +{ + return calloc(1, sizeof(struct nftnl_ruleset)); +} + +EXPORT_SYMBOL(nftnl_ruleset_free); +void nftnl_ruleset_free(const struct nftnl_ruleset *r) +{ + if (r->flags & (1 << NFTNL_RULESET_TABLELIST)) + nftnl_table_list_free(r->table_list); + if (r->flags & (1 << NFTNL_RULESET_CHAINLIST)) + nftnl_chain_list_free(r->chain_list); + if (r->flags & (1 << NFTNL_RULESET_SETLIST)) + nftnl_set_list_free(r->set_list); + if (r->flags & (1 << NFTNL_RULESET_RULELIST)) + nftnl_rule_list_free(r->rule_list); + xfree(r); +} + +EXPORT_SYMBOL(nftnl_ruleset_is_set); +bool nftnl_ruleset_is_set(const struct nftnl_ruleset *r, uint16_t attr) +{ + return r->flags & (1 << attr); +} + +EXPORT_SYMBOL(nftnl_ruleset_unset); +void nftnl_ruleset_unset(struct nftnl_ruleset *r, uint16_t attr) +{ + if (!(r->flags & (1 << attr))) + return; + + switch (attr) { + case NFTNL_RULESET_TABLELIST: + nftnl_table_list_free(r->table_list); + break; + case NFTNL_RULESET_CHAINLIST: + nftnl_chain_list_free(r->chain_list); + break; + case NFTNL_RULESET_SETLIST: + nftnl_set_list_free(r->set_list); + break; + case NFTNL_RULESET_RULELIST: + nftnl_rule_list_free(r->rule_list); + break; + } + r->flags &= ~(1 << attr); +} + +EXPORT_SYMBOL(nftnl_ruleset_set); +void nftnl_ruleset_set(struct nftnl_ruleset *r, uint16_t attr, void *data) +{ + switch (attr) { + case NFTNL_RULESET_TABLELIST: + nftnl_ruleset_unset(r, NFTNL_RULESET_TABLELIST); + r->table_list = data; + break; + case NFTNL_RULESET_CHAINLIST: + nftnl_ruleset_unset(r, NFTNL_RULESET_CHAINLIST); + r->chain_list = data; + break; + case NFTNL_RULESET_SETLIST: + nftnl_ruleset_unset(r, NFTNL_RULESET_SETLIST); + r->set_list = data; + break; + case NFTNL_RULESET_RULELIST: + nftnl_ruleset_unset(r, NFTNL_RULESET_RULELIST); + r->rule_list = data; + break; + default: + return; + } + r->flags |= (1 << attr); +} + +EXPORT_SYMBOL(nftnl_ruleset_get); +void *nftnl_ruleset_get(const struct nftnl_ruleset *r, uint16_t attr) +{ + if (!(r->flags & (1 << attr))) + return NULL; + + switch (attr) { + case NFTNL_RULESET_TABLELIST: + return r->table_list; + case NFTNL_RULESET_CHAINLIST: + return r->chain_list; + case NFTNL_RULESET_SETLIST: + return r->set_list; + case NFTNL_RULESET_RULELIST: + return r->rule_list; + default: + return NULL; + } +} + +EXPORT_SYMBOL(nftnl_ruleset_ctx_free); +void nftnl_ruleset_ctx_free(const struct nftnl_parse_ctx *ctx) +{ + switch (ctx->type) { + case NFTNL_RULESET_TABLE: + nftnl_table_free(ctx->table); + break; + case NFTNL_RULESET_CHAIN: + nftnl_chain_free(ctx->chain); + break; + case NFTNL_RULESET_RULE: + nftnl_rule_free(ctx->rule); + break; + case NFTNL_RULESET_SET: + case NFTNL_RULESET_SET_ELEMS: + nftnl_set_free(ctx->set); + break; + case NFTNL_RULESET_RULESET: + case NFTNL_RULESET_UNSPEC: + break; + } +} + +EXPORT_SYMBOL(nftnl_ruleset_ctx_is_set); +bool nftnl_ruleset_ctx_is_set(const struct nftnl_parse_ctx *ctx, uint16_t attr) +{ + return ctx->flags & (1 << attr); +} + +EXPORT_SYMBOL(nftnl_ruleset_ctx_get); +void *nftnl_ruleset_ctx_get(const struct nftnl_parse_ctx *ctx, uint16_t attr) +{ + if (!(ctx->flags & (1 << attr))) + return NULL; + + switch (attr) { + case NFTNL_RULESET_CTX_CMD: + return (void *)&ctx->cmd; + case NFTNL_RULESET_CTX_TYPE: + return (void *)&ctx->type; + case NFTNL_RULESET_CTX_TABLE: + return ctx->table; + case NFTNL_RULESET_CTX_CHAIN: + return ctx->chain; + case NFTNL_RULESET_CTX_RULE: + return ctx->rule; + case NFTNL_RULESET_CTX_SET: + return ctx->set; + case NFTNL_RULESET_CTX_DATA: + return ctx->data; + default: + return NULL; + } +} + +EXPORT_SYMBOL(nftnl_ruleset_ctx_get_u32); +uint32_t nftnl_ruleset_ctx_get_u32(const struct nftnl_parse_ctx *ctx, uint16_t attr) +{ + const void *ret = nftnl_ruleset_ctx_get(ctx, attr); + return ret == NULL ? 0 : *((uint32_t *)ret); +} + + +EXPORT_SYMBOL(nftnl_ruleset_parse_file_cb); +int nftnl_ruleset_parse_file_cb(enum nftnl_parse_type type, FILE *fp, + struct nftnl_parse_err *err, void *data, + int (*cb)(const struct nftnl_parse_ctx *ctx)) +{ + errno = EOPNOTSUPP; + return -1; +} + +EXPORT_SYMBOL(nftnl_ruleset_parse_buffer_cb); +int nftnl_ruleset_parse_buffer_cb(enum nftnl_parse_type type, const char *buffer, + struct nftnl_parse_err *err, void *data, + int (*cb)(const struct nftnl_parse_ctx *ctx)) +{ + errno = EOPNOTSUPP; + return -1; +} + +static int nftnl_ruleset_cb(const struct nftnl_parse_ctx *ctx) +{ + struct nftnl_ruleset *r = ctx->data; + + if (ctx->cmd != NFTNL_CMD_ADD) + return -1; + + switch (ctx->type) { + case NFTNL_RULESET_TABLE: + if (r->table_list == NULL) { + r->table_list = nftnl_table_list_alloc(); + if (r->table_list == NULL) + return -1; + + nftnl_ruleset_set(r, NFTNL_RULESET_TABLELIST, + r->table_list); + } + nftnl_table_list_add_tail(ctx->table, r->table_list); + break; + case NFTNL_RULESET_CHAIN: + if (r->chain_list == NULL) { + r->chain_list = nftnl_chain_list_alloc(); + if (r->chain_list == NULL) + return -1; + + nftnl_ruleset_set(r, NFTNL_RULESET_CHAINLIST, + r->chain_list); + } + nftnl_chain_list_add_tail(ctx->chain, r->chain_list); + break; + case NFTNL_RULESET_SET: + if (r->set_list == NULL) { + r->set_list = nftnl_set_list_alloc(); + if (r->set_list == NULL) + return -1; + + nftnl_ruleset_set(r, NFTNL_RULESET_SETLIST, + r->set_list); + } + nftnl_set_list_add_tail(ctx->set, r->set_list); + break; + case NFTNL_RULESET_RULE: + if (r->rule_list == NULL) { + r->rule_list = nftnl_rule_list_alloc(); + if (r->rule_list == NULL) + return -1; + + nftnl_ruleset_set(r, NFTNL_RULESET_RULELIST, + r->rule_list); + } + nftnl_rule_list_add_tail(ctx->rule, r->rule_list); + break; + case NFTNL_RULESET_RULESET: + break; + default: + return -1; + } + + return 0; +} + +EXPORT_SYMBOL(nftnl_ruleset_parse); +int nftnl_ruleset_parse(struct nftnl_ruleset *r, enum nftnl_parse_type type, + const char *data, struct nftnl_parse_err *err) +{ + errno = EOPNOTSUPP; + return -1; +} + +EXPORT_SYMBOL(nftnl_ruleset_parse_file); +int nftnl_ruleset_parse_file(struct nftnl_ruleset *rs, enum nftnl_parse_type type, + FILE *fp, struct nftnl_parse_err *err) +{ + return nftnl_ruleset_parse_file_cb(type, fp, err, rs, nftnl_ruleset_cb); +} + +static int +nftnl_ruleset_snprintf_table(char *buf, size_t remain, + const struct nftnl_ruleset *rs, uint32_t type, + uint32_t flags) +{ + struct nftnl_table *t; + struct nftnl_table_list_iter *ti; + const char *sep = ""; + int ret, offset = 0; + + ti = nftnl_table_list_iter_create(rs->table_list); + if (ti == NULL) + return 0; + + t = nftnl_table_list_iter_next(ti); + while (t != NULL) { + ret = snprintf(buf + offset, remain, "%s", sep); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_table_snprintf(buf + offset, remain, t, type, flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + t = nftnl_table_list_iter_next(ti); + sep = "\n"; + } + nftnl_table_list_iter_destroy(ti); + + return offset; +} + +static int +nftnl_ruleset_snprintf_chain(char *buf, size_t remain, + const struct nftnl_ruleset *rs, uint32_t type, + uint32_t flags) +{ + struct nftnl_chain *c; + struct nftnl_chain_list_iter *ci; + const char *sep = ""; + int ret, offset = 0; + + ci = nftnl_chain_list_iter_create(rs->chain_list); + if (ci == NULL) + return 0; + + c = nftnl_chain_list_iter_next(ci); + while (c != NULL) { + ret = snprintf(buf + offset, remain, "%s", sep); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_chain_snprintf(buf + offset, remain, c, type, flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + c = nftnl_chain_list_iter_next(ci); + sep = "\n"; + } + nftnl_chain_list_iter_destroy(ci); + + return offset; +} + +static int +nftnl_ruleset_snprintf_set(char *buf, size_t remain, + const struct nftnl_ruleset *rs, uint32_t type, + uint32_t flags) +{ + struct nftnl_set *s; + struct nftnl_set_list_iter *si; + const char *sep = ""; + int ret, offset = 0; + + si = nftnl_set_list_iter_create(rs->set_list); + if (si == NULL) + return 0; + + s = nftnl_set_list_iter_next(si); + while (s != NULL) { + ret = snprintf(buf + offset, remain, "%s", sep); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_set_snprintf(buf + offset, remain, s, type, flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + s = nftnl_set_list_iter_next(si); + sep = "\n"; + } + nftnl_set_list_iter_destroy(si); + + return offset; +} + +static int +nftnl_ruleset_snprintf_rule(char *buf, size_t remain, + const struct nftnl_ruleset *rs, uint32_t type, + uint32_t flags) +{ + struct nftnl_rule *r; + struct nftnl_rule_list_iter *ri; + const char *sep = ""; + int ret, offset = 0; + + ri = nftnl_rule_list_iter_create(rs->rule_list); + if (ri == NULL) + return 0; + + r = nftnl_rule_list_iter_next(ri); + while (r != NULL) { + ret = snprintf(buf + offset, remain, "%s", sep); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_rule_snprintf(buf + offset, remain, r, type, flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + r = nftnl_rule_list_iter_next(ri); + sep = "\n"; + } + nftnl_rule_list_iter_destroy(ri); + + return offset; +} + +static int +nftnl_ruleset_do_snprintf(char *buf, size_t remain, + const struct nftnl_ruleset *rs, + uint32_t cmd, uint32_t type, uint32_t flags) +{ + uint32_t inner_flags = flags; + const char *sep = ""; + int ret, offset = 0; + + /* dont pass events flags to child calls of _snprintf() */ + inner_flags &= ~NFTNL_OF_EVENT_ANY; + + if (nftnl_ruleset_is_set(rs, NFTNL_RULESET_TABLELIST) && + (!nftnl_table_list_is_empty(rs->table_list))) { + ret = nftnl_ruleset_snprintf_table(buf + offset, remain, rs, + type, inner_flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + if (ret > 0) + sep = "\n"; + } + + if (nftnl_ruleset_is_set(rs, NFTNL_RULESET_CHAINLIST) && + (!nftnl_chain_list_is_empty(rs->chain_list))) { + ret = snprintf(buf + offset, remain, "%s", sep); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_ruleset_snprintf_chain(buf + offset, remain, rs, + type, inner_flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + if (ret > 0) + sep = "\n"; + } + + if (nftnl_ruleset_is_set(rs, NFTNL_RULESET_SETLIST) && + (!nftnl_set_list_is_empty(rs->set_list))) { + ret = snprintf(buf + offset, remain, "%s", sep); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_ruleset_snprintf_set(buf + offset, remain, rs, + type, inner_flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + if (ret > 0) + sep = "\n"; + } + + if (nftnl_ruleset_is_set(rs, NFTNL_RULESET_RULELIST) && + (!nftnl_rule_list_is_empty(rs->rule_list))) { + ret = snprintf(buf + offset, remain, "%s", sep); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_ruleset_snprintf_rule(buf + offset, remain, rs, + type, inner_flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +EXPORT_SYMBOL(nftnl_ruleset_snprintf); +int nftnl_ruleset_snprintf(char *buf, size_t size, const struct nftnl_ruleset *r, + uint32_t type, uint32_t flags) +{ + if (size) + buf[0] = '\0'; + + if (type != NFTNL_OUTPUT_DEFAULT) { + errno = EOPNOTSUPP; + return -1; + } + return nftnl_ruleset_do_snprintf(buf, size, r, nftnl_flag2cmd(flags), + type, flags); +} + +static int nftnl_ruleset_fprintf_tables(FILE *fp, const struct nftnl_ruleset *rs, + uint32_t type, uint32_t flags) +{ + int len = 0, ret = 0; + struct nftnl_table *t; + struct nftnl_table_list_iter *ti; + const char *sep = ""; + + ti = nftnl_table_list_iter_create(rs->table_list); + if (ti == NULL) + return -1; + + t = nftnl_table_list_iter_next(ti); + while (t != NULL) { + ret = fprintf(fp, "%s", sep); + if (ret < 0) + goto err; + + len += ret; + + ret = nftnl_table_fprintf(fp, t, type, flags); + if (ret < 0) + goto err; + + len += ret; + + t = nftnl_table_list_iter_next(ti); + sep = "\n"; + + } + nftnl_table_list_iter_destroy(ti); + + return len; +err: + nftnl_table_list_iter_destroy(ti); + return -1; +} + +static int nftnl_ruleset_fprintf_chains(FILE *fp, const struct nftnl_ruleset *rs, + uint32_t type, uint32_t flags) +{ + int len = 0, ret = 0; + struct nftnl_chain *o; + struct nftnl_chain_list_iter *i; + const char *sep = ""; + + i = nftnl_chain_list_iter_create(rs->chain_list); + if (i == NULL) + return -1; + + o = nftnl_chain_list_iter_next(i); + while (o != NULL) { + ret = fprintf(fp, "%s", sep); + if (ret < 0) + goto err; + + len += ret; + + ret = nftnl_chain_fprintf(fp, o, type, flags); + if (ret < 0) + goto err; + + len += ret; + + o = nftnl_chain_list_iter_next(i); + sep = "\n"; + } + nftnl_chain_list_iter_destroy(i); + + return len; +err: + nftnl_chain_list_iter_destroy(i); + return -1; +} + +static int nftnl_ruleset_fprintf_sets(FILE *fp, const struct nftnl_ruleset *rs, + uint32_t type, uint32_t flags) +{ + int len = 0, ret = 0; + struct nftnl_set *o; + struct nftnl_set_list_iter *i; + const char *sep = ""; + + i = nftnl_set_list_iter_create(rs->set_list); + if (i == NULL) + return -1; + + o = nftnl_set_list_iter_next(i); + while (o != NULL) { + ret = fprintf(fp, "%s", sep); + if (ret < 0) + goto err; + + len += ret; + + ret = nftnl_set_fprintf(fp, o, type, flags); + if (ret < 0) + goto err; + + len += ret; + + o = nftnl_set_list_iter_next(i); + sep = "\n"; + } + nftnl_set_list_iter_destroy(i); + + return len; +err: + nftnl_set_list_iter_destroy(i); + return -1; +} + +static int nftnl_ruleset_fprintf_rules(FILE *fp, const struct nftnl_ruleset *rs, + uint32_t type, uint32_t flags) +{ + int len = 0, ret = 0; + struct nftnl_rule *o; + struct nftnl_rule_list_iter *i; + const char *sep = ""; + + i = nftnl_rule_list_iter_create(rs->rule_list); + if (i == NULL) + return -1; + + o = nftnl_rule_list_iter_next(i); + while (o != NULL) { + ret = fprintf(fp, "%s", sep); + if (ret < 0) + goto err; + + len += ret; + + ret = nftnl_rule_fprintf(fp, o, type, flags); + if (ret < 0) + goto err; + + len += ret; + + o = nftnl_rule_list_iter_next(i); + sep = "\n"; + } + nftnl_rule_list_iter_destroy(i); + + return len; +err: + nftnl_rule_list_iter_destroy(i); + return -1; +} + +#define NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len) \ + if (ret < 0) \ + return -1; \ + len += ret; + +static int nftnl_ruleset_cmd_fprintf(FILE *fp, const struct nftnl_ruleset *rs, + uint32_t cmd, uint32_t type, uint32_t flags) +{ + int len = 0, ret = 0; + uint32_t inner_flags = flags; + const char *sep = ""; + + /* dont pass events flags to child calls of _snprintf() */ + inner_flags &= ~NFTNL_OF_EVENT_ANY; + + if ((nftnl_ruleset_is_set(rs, NFTNL_RULESET_TABLELIST)) && + (!nftnl_table_list_is_empty(rs->table_list))) { + ret = nftnl_ruleset_fprintf_tables(fp, rs, type, inner_flags); + NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len); + + if (ret > 0) + sep = "\n"; + } + + if ((nftnl_ruleset_is_set(rs, NFTNL_RULESET_CHAINLIST)) && + (!nftnl_chain_list_is_empty(rs->chain_list))) { + ret = fprintf(fp, "%s", sep); + NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len); + + ret = nftnl_ruleset_fprintf_chains(fp, rs, type, inner_flags); + NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len); + + if (ret > 0) + sep = "\n"; + } + + if ((nftnl_ruleset_is_set(rs, NFTNL_RULESET_SETLIST)) && + (!nftnl_set_list_is_empty(rs->set_list))) { + ret = fprintf(fp, "%s", sep); + NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len); + + ret = nftnl_ruleset_fprintf_sets(fp, rs, type, inner_flags); + NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len); + + if (ret > 0) + sep = "\n"; + } + + if ((nftnl_ruleset_is_set(rs, NFTNL_RULESET_RULELIST)) && + (!nftnl_rule_list_is_empty(rs->rule_list))) { + ret = fprintf(fp, "%s", sep); + NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len); + + ret = nftnl_ruleset_fprintf_rules(fp, rs, type, inner_flags); + NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len); + } + + return len; +} + +EXPORT_SYMBOL(nftnl_ruleset_fprintf); +int nftnl_ruleset_fprintf(FILE *fp, const struct nftnl_ruleset *rs, uint32_t type, + uint32_t flags) +{ + return nftnl_ruleset_cmd_fprintf(fp, rs, nftnl_flag2cmd(flags), type, + flags); +} |