From 00c179b4c9f9f9394cc6f79e5f2a68410631fd72 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 3 Aug 2012 17:31:48 +0200 Subject: initial version of libnftables It adds support for table, chain and rule handling. This also includes expression handling for each rule. Signed-off-by: Pablo Neira Ayuso --- src/Makefile.am | 21 +++ src/chain.c | 484 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/expr.c | 189 ++++++++++++++++++++ src/expr/cmp.c | 194 +++++++++++++++++++++ src/expr/counter.c | 145 +++++++++++++++ src/expr/data_reg.c | 158 +++++++++++++++++ src/expr/data_reg.h | 23 +++ src/expr/immediate.c | 215 +++++++++++++++++++++++ src/expr/match.c | 203 +++++++++++++++++++++ src/expr/meta.c | 144 +++++++++++++++ src/expr/payload.c | 185 ++++++++++++++++++++ src/expr/target.c | 203 +++++++++++++++++++++ src/expr_ops.c | 36 ++++ src/expr_ops.h | 24 +++ src/internal.h | 32 ++++ src/libnftables.map | 80 +++++++++ src/rule.c | 452 +++++++++++++++++++++++++++++++++++++++++++++++ src/table.c | 212 ++++++++++++++++++++++ 18 files changed, 3000 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/chain.c create mode 100644 src/expr.c create mode 100644 src/expr/cmp.c create mode 100644 src/expr/counter.c create mode 100644 src/expr/data_reg.c create mode 100644 src/expr/data_reg.h create mode 100644 src/expr/immediate.c create mode 100644 src/expr/match.c create mode 100644 src/expr/meta.c create mode 100644 src/expr/payload.c create mode 100644 src/expr/target.c create mode 100644 src/expr_ops.c create mode 100644 src/expr_ops.h create mode 100644 src/internal.h create mode 100644 src/libnftables.map create mode 100644 src/rule.c create mode 100644 src/table.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..b1c1044 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,21 @@ +include $(top_srcdir)/Make_global.am +lib_LTLIBRARIES = libnftables.la + +libnftables_la_LIBADD = ${LIBMNL_LIBS} +libnftables_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnftables.map \ + -version-info $(LIBVERSION) +libnftables_la_SOURCES = table.c \ + chain.c \ + rule.c \ + expr.c \ + expr_ops.c \ + expr/cmp.c \ + expr/counter.c \ + expr/data_reg.c \ + expr/immediate.c \ + expr/match.c \ + expr/meta.c \ + expr/payload.c \ + expr/target.c \ + libnftables.map \ + internal.h diff --git a/src/chain.c b/src/chain.c new file mode 100644 index 0000000..fd95a64 --- /dev/null +++ b/src/chain.c @@ -0,0 +1,484 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct nft_chain { + struct list_head head; + + char *name; + char *table; + uint8_t family; + uint32_t policy; + uint32_t hooknum; + int32_t prio; + uint32_t use; + uint64_t packets; + uint64_t bytes; + uint32_t flags; +}; + +struct nft_chain *nft_chain_alloc(void) +{ + return calloc(1, sizeof(struct nft_chain)); +} +EXPORT_SYMBOL(nft_chain_alloc); + +void nft_chain_free(struct nft_chain *c) +{ + if (c->name != NULL) + free(c->name); + if (c->table != NULL) + free(c->table); + + free(c); +} +EXPORT_SYMBOL(nft_chain_free); + +void nft_chain_attr_set(struct nft_chain *c, uint16_t attr, void *data) +{ + switch(attr) { + case NFT_CHAIN_ATTR_NAME: + if (c->name) + free(c->name); + + c->name = strdup(data); + break; + case NFT_CHAIN_ATTR_TABLE: + if (c->table) + free(c->table); + + c->table = strdup(data); + break; + case NFT_CHAIN_ATTR_HOOKNUM: + memcpy(&c->hooknum, data, sizeof(c->hooknum)); + break; + case NFT_CHAIN_ATTR_PRIO: + memcpy(&c->prio, data, sizeof(c->prio)); + break; + case NFT_CHAIN_ATTR_POLICY: + c->policy = *((uint32_t *)data); + break; + case NFT_CHAIN_ATTR_USE: + /* cannot be set, ignore it */ + return; + case NFT_CHAIN_ATTR_BYTES: + c->bytes = *((uint64_t *)data); + break; + case NFT_CHAIN_ATTR_PACKETS: + c->bytes = *((uint64_t *)data); + break; + default: + return; + } + c->flags |= (1 << attr); +} +EXPORT_SYMBOL(nft_chain_attr_set); + +void nft_chain_attr_set_u32(struct nft_chain *c, uint16_t attr, uint32_t data) +{ + nft_chain_attr_set(c, attr, &data); +} +EXPORT_SYMBOL(nft_chain_attr_set_u32); + +void nft_chain_attr_set_s32(struct nft_chain *c, uint16_t attr, int32_t data) +{ + nft_chain_attr_set(c, attr, &data); +} +EXPORT_SYMBOL(nft_chain_attr_set_s32); + +void nft_chain_attr_set_u64(struct nft_chain *c, uint16_t attr, uint64_t data) +{ + nft_chain_attr_set(c, attr, &data); +} +EXPORT_SYMBOL(nft_chain_attr_set_u64); + +void *nft_chain_attr_get(struct nft_chain *c, uint16_t attr) +{ + switch(attr) { + case NFT_CHAIN_ATTR_NAME: + if (c->flags & (1 << NFT_CHAIN_ATTR_NAME)) + return c->name; + else + return NULL; + break; + case NFT_CHAIN_ATTR_TABLE: + if (c->flags & (1 << NFT_CHAIN_ATTR_TABLE)) + return c->table; + else + return NULL; + case NFT_CHAIN_ATTR_HOOKNUM: + if (c->flags & (1 << NFT_CHAIN_ATTR_HOOKNUM)) + return &c->hooknum; + else + return NULL; + break; + case NFT_CHAIN_ATTR_PRIO: + if (c->flags & (1 << NFT_CHAIN_ATTR_PRIO)) + return &c->prio; + else + return NULL; + break; + case NFT_CHAIN_ATTR_POLICY: + if (c->flags & (1 << NFT_CHAIN_ATTR_POLICY)) + return &c->policy; + else + return NULL; + break; + case NFT_CHAIN_ATTR_USE: + if (c->flags & (1 << NFT_CHAIN_ATTR_USE)) + return &c->use; + else + return NULL; + break; + case NFT_CHAIN_ATTR_BYTES: + if (c->flags & (1 << NFT_CHAIN_ATTR_BYTES)) + return &c->bytes; + else + return NULL; + break; + case NFT_CHAIN_ATTR_PACKETS: + if (c->flags & (1 << NFT_CHAIN_ATTR_PACKETS)) + return &c->packets; + else + return NULL; + break; + default: + return NULL; + } +} +EXPORT_SYMBOL(nft_chain_attr_get); + +const char *nft_chain_attr_get_str(struct nft_chain *c, uint16_t attr) +{ + return nft_chain_attr_get(c, attr); +} +EXPORT_SYMBOL(nft_chain_attr_get_str); + +uint32_t nft_chain_attr_get_u32(struct nft_chain *c, uint16_t attr) +{ + uint32_t *val = nft_chain_attr_get(c, attr); + return val ? *val : 0; +} +EXPORT_SYMBOL(nft_chain_attr_get_u32); + +int32_t nft_chain_attr_get_s32(struct nft_chain *c, uint16_t attr) +{ + int32_t *val = nft_chain_attr_get(c, attr); + return val ? *val : 0; +} +EXPORT_SYMBOL(nft_chain_attr_get_s32); + +uint64_t nft_chain_attr_get_u64(struct nft_chain *c, uint16_t attr) +{ + uint64_t *val = nft_chain_attr_get(c, attr); + return val ? *val : 0; +} +EXPORT_SYMBOL(nft_chain_attr_get_u64); + +struct nlmsghdr * +nft_chain_nlmsg_build_hdr(char *buf, uint16_t cmd, uint16_t family, + uint16_t type, uint32_t seq) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | cmd; + nlh->nlmsg_flags = NLM_F_REQUEST | type; + nlh->nlmsg_seq = seq; + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); + nfh->nfgen_family = family; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + + return nlh; +} +EXPORT_SYMBOL(nft_chain_nlmsg_build_hdr); + +void nft_chain_nlmsg_build_payload(struct nlmsghdr *nlh, const struct nft_chain *c) +{ + if (c->flags & (1 << NFT_CHAIN_ATTR_TABLE)) + mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, c->table); + if (c->flags & (1 << NFT_CHAIN_ATTR_NAME)) + mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, c->name); + if ((c->flags & (1 << NFT_CHAIN_ATTR_HOOKNUM)) && + (c->flags & (1 << NFT_CHAIN_ATTR_PRIO))) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK); + mnl_attr_put_u32(nlh, NFTA_HOOK_HOOKNUM, htonl(c->hooknum)); + mnl_attr_put_u32(nlh, NFTA_HOOK_PRIORITY, htonl(c->prio)); + mnl_attr_nest_end(nlh, nest); + } + if (c->flags & (1 << NFT_CHAIN_ATTR_POLICY)) + mnl_attr_put_u32(nlh, NFTA_CHAIN_POLICY, htonl(c->policy)); + if ((c->flags & (1 << NFT_CHAIN_ATTR_PACKETS)) && + (c->flags & (1 << NFT_CHAIN_ATTR_BYTES))) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_COUNTERS); + mnl_attr_put_u64(nlh, NFTA_COUNTER_PACKETS, be64toh(c->packets)); + mnl_attr_put_u64(nlh, NFTA_COUNTER_BYTES, be64toh(c->bytes)); + mnl_attr_nest_end(nlh, nest); + } +} +EXPORT_SYMBOL(nft_chain_nlmsg_build_payload); + +static int nft_chain_parse_attr_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_CHAIN_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_CHAIN_NAME: + case NFTA_CHAIN_TABLE: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_CHAIN_HOOK: + case NFTA_CHAIN_COUNTERS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_CHAIN_POLICY: + case NFTA_CHAIN_USE: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int nft_chain_parse_counters_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_COUNTER_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_COUNTER_BYTES: + case NFTA_COUNTER_PACKETS: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int nft_chain_parse_counters(struct nlattr *attr, struct nft_chain *c) +{ + struct nlattr *tb[NFTA_COUNTER_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_chain_parse_counters_cb, tb) < 0) + return -1; + + if (tb[NFTA_COUNTER_PACKETS]) { + c->packets = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_PACKETS])); + c->flags |= (1 << NFT_CHAIN_ATTR_PACKETS); + } + if (tb[NFTA_COUNTER_BYTES]) { + c->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_BYTES])); + c->flags |= (1 << NFT_CHAIN_ATTR_BYTES); + } + + return 0; +} +static int nft_chain_parse_hook_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_HOOK_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_HOOK_HOOKNUM: + case NFTA_HOOK_PRIORITY: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int nft_chain_parse_hook(struct nlattr *attr, struct nft_chain *c) +{ + struct nlattr *tb[NFTA_HOOK_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_chain_parse_hook_cb, tb) < 0) + return -1; + + if (tb[NFTA_HOOK_HOOKNUM]) { + c->hooknum = ntohl(mnl_attr_get_u32(tb[NFTA_HOOK_HOOKNUM])); + c->flags |= (1 << NFT_CHAIN_ATTR_HOOKNUM); + } + if (tb[NFTA_HOOK_PRIORITY]) { + c->prio = ntohl(mnl_attr_get_u32(tb[NFTA_HOOK_PRIORITY])); + c->flags |= (1 << NFT_CHAIN_ATTR_PRIO); + } + + return 0; +} + +int nft_chain_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_chain *c) +{ + struct nlattr *tb[NFTA_CHAIN_MAX+1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + int ret = 0; + + mnl_attr_parse(nlh, sizeof(*nfg), nft_chain_parse_attr_cb, tb); + if (tb[NFTA_CHAIN_NAME]) { + c->name = strdup(mnl_attr_get_str(tb[NFTA_CHAIN_NAME])); + c->flags |= (1 << NFT_CHAIN_ATTR_NAME); + } + if (tb[NFTA_CHAIN_TABLE]) { + c->table = strdup(mnl_attr_get_str(tb[NFTA_CHAIN_TABLE])); + c->flags |= (1 << NFT_CHAIN_ATTR_TABLE); + } + if (tb[NFTA_CHAIN_HOOK]) + ret = nft_chain_parse_hook(tb[NFTA_CHAIN_HOOK], c); + if (tb[NFTA_CHAIN_POLICY]) { + c->policy = ntohl(mnl_attr_get_u32(tb[NFTA_CHAIN_POLICY])); + c->flags |= (1 << NFT_CHAIN_ATTR_POLICY); + } + if (tb[NFTA_CHAIN_USE]) { + c->use = ntohl(mnl_attr_get_u32(tb[NFTA_CHAIN_USE])); + c->flags |= (1 << NFT_CHAIN_ATTR_USE); + } + if (tb[NFTA_CHAIN_COUNTERS]) + ret = nft_chain_parse_counters(tb[NFTA_CHAIN_COUNTERS], c); + + c->family = nfg->nfgen_family; + + return ret; +} +EXPORT_SYMBOL(nft_chain_nlmsg_parse); + +int nft_chain_snprintf(char *buf, size_t size, struct nft_chain *c, + uint32_t type, uint32_t flags) +{ + return snprintf(buf, size, "family=%u table=%s chain=%s " + "hook=%u prio=%d policy=%d use=%d " + "packets=%lu bytes=%lu\n", + c->family, c->table, c->name, c->hooknum, + c->prio, c->policy, c->use, c->packets, c->bytes); +} +EXPORT_SYMBOL(nft_chain_snprintf); + +struct nft_chain_list { + struct list_head list; +}; + +struct nft_chain_list *nft_chain_list_alloc(void) +{ + struct nft_chain_list *list; + + list = calloc(1, sizeof(struct nft_chain_list)); + if (list == NULL) + return NULL; + + INIT_LIST_HEAD(&list->list); + + return list; +} +EXPORT_SYMBOL(nft_chain_list_alloc); + +void nft_chain_list_free(struct nft_chain_list *list) +{ + struct nft_chain *r, *tmp; + + list_for_each_entry_safe(r, tmp, &list->list, head) { + list_del(&r->head); + nft_chain_free(r); + } + free(list); +} +EXPORT_SYMBOL(nft_chain_list_free); + +void nft_chain_list_add(struct nft_chain *r, struct nft_chain_list *list) +{ + list_add_tail(&r->head, &list->list); +} +EXPORT_SYMBOL(nft_chain_list_add); + +struct nft_chain_list_iter { + struct nft_chain_list *list; + struct nft_chain *cur; +}; + +struct nft_chain_list_iter *nft_chain_list_iter_create(struct nft_chain_list *l) +{ + struct nft_chain_list_iter *iter; + + iter = calloc(1, sizeof(struct nft_chain_list_iter)); + if (iter == NULL) + return NULL; + + iter->list = l; + iter->cur = list_entry(l->list.next, struct nft_chain, head); + + return iter; +} +EXPORT_SYMBOL(nft_chain_list_iter_create); + +struct nft_chain *nft_chain_list_iter_next(struct nft_chain_list_iter *iter) +{ + struct nft_chain *r = iter->cur; + + /* get next chain, if any */ + iter->cur = list_entry(iter->cur->head.next, struct nft_chain, head); + if (&iter->cur->head == iter->list->list.next) + return NULL; + + return r; +} +EXPORT_SYMBOL(nft_chain_list_iter_next); + +void nft_chain_list_iter_destroy(struct nft_chain_list_iter *iter) +{ + free(iter); +} +EXPORT_SYMBOL(nft_chain_list_iter_destroy); diff --git a/src/expr.c b/src/expr.c new file mode 100644 index 0000000..0b06aed --- /dev/null +++ b/src/expr.c @@ -0,0 +1,189 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ +#include "internal.h" +#include "expr_ops.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "linux_list.h" + +struct nft_rule_expr *nft_rule_expr_alloc(const char *name) +{ + struct nft_rule_expr *expr; + struct expr_ops *ops; + + ops = nft_expr_ops_lookup(name); + if (ops == NULL) + return NULL; + + expr = calloc(1, sizeof(struct nft_rule_expr) + ops->alloc_len); + if (expr == NULL) + return NULL; + + /* Manually set expression name attribute */ + expr->flags |= (1 << NFT_RULE_EXPR_ATTR_NAME); + expr->ops = ops; + + return expr; +} +EXPORT_SYMBOL(nft_rule_expr_alloc); + +void nft_rule_expr_free(struct nft_rule_expr *expr) +{ + free(expr); +} +EXPORT_SYMBOL(nft_rule_expr_free); + +void +nft_rule_expr_set(struct nft_rule_expr *expr, uint16_t type, + const void *data, size_t data_len) +{ + switch(type) { + case NFT_RULE_EXPR_ATTR_NAME: /* cannot be modified */ + return; + default: + if (expr->ops->set(expr, type, data, data_len) < 0) + return; + } + expr->flags |= (1 << type); +} +EXPORT_SYMBOL(nft_rule_expr_set); + +void +nft_rule_expr_set_u8(struct nft_rule_expr *expr, uint16_t type, uint8_t data) +{ + nft_rule_expr_set(expr, type, &data, sizeof(uint8_t)); +} +EXPORT_SYMBOL(nft_rule_expr_set_u8); + +void +nft_rule_expr_set_u32(struct nft_rule_expr *expr, uint16_t type, uint32_t data) +{ + nft_rule_expr_set(expr, type, &data, sizeof(uint32_t)); +} +EXPORT_SYMBOL(nft_rule_expr_set_u32); + +void +nft_rule_expr_set_u64(struct nft_rule_expr *expr, uint16_t type, uint64_t data) +{ + nft_rule_expr_set(expr, type, &data, sizeof(uint64_t)); +} +EXPORT_SYMBOL(nft_rule_expr_set_u64); + +void +nft_rule_expr_set_str(struct nft_rule_expr *expr, uint16_t type, char *str) +{ + nft_rule_expr_set(expr, type, str, strlen(str)+1); +} +EXPORT_SYMBOL(nft_rule_expr_set_str); + +const void *nft_rule_expr_get(struct nft_rule_expr *expr, uint16_t type, size_t *data_len) +{ + const void *ret = NULL; + + switch(type) { + case NFT_RULE_EXPR_ATTR_NAME: + if (!(expr->flags & (1 << NFT_RULE_EXPR_ATTR_NAME))) + return NULL; + + ret = expr->ops->name; + break; + default: + ret = expr->ops->get(expr, type, data_len); + break; + } + + return ret; +} +EXPORT_SYMBOL(nft_rule_expr_get); + +uint8_t nft_rule_expr_get_u8(struct nft_rule_expr *expr, uint16_t type) +{ + const void *data; + size_t data_len; + + data = nft_rule_expr_get(expr, type, &data_len); + if (data == NULL) + return 0; + + if (data_len != sizeof(uint8_t)) + return 0; + + return *((uint8_t *)data); +} +EXPORT_SYMBOL(nft_rule_expr_get_u8); + +uint32_t nft_rule_expr_get_u32(struct nft_rule_expr *expr, uint16_t type) +{ + const void *data; + size_t data_len; + + data = nft_rule_expr_get(expr, type, &data_len); + if (data == NULL) + return 0; + + if (data_len != sizeof(uint32_t)) + return 0; + + return *((uint32_t *)data); +} +EXPORT_SYMBOL(nft_rule_expr_get_u32); + +uint64_t nft_rule_expr_get_u64(struct nft_rule_expr *expr, uint16_t type) +{ + const void *data; + size_t data_len; + + data = nft_rule_expr_get(expr, type, &data_len); + if (data == NULL) + return 0; + + if (data_len != sizeof(uint64_t)) + return 0; + + return *((uint64_t *)data); +} +EXPORT_SYMBOL(nft_rule_expr_get_u64); + +const char *nft_rule_expr_get_str(struct nft_rule_expr *expr, uint16_t type) +{ + size_t data_len; + + return (const char *)nft_rule_expr_get(expr, type, &data_len); +} +EXPORT_SYMBOL(nft_rule_expr_get_str); + +void +nft_rule_expr_build_payload(struct nlmsghdr *nlh, struct nft_rule_expr *expr) +{ + struct nlattr *nest1, *nest2; + + nest1 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM); + mnl_attr_put_strz(nlh, NFTA_EXPR_NAME, expr->ops->name); + + nest2 = mnl_attr_nest_start(nlh, NFTA_EXPR_DATA); + expr->ops->build(nlh, expr); + mnl_attr_nest_end(nlh, nest2); + + mnl_attr_nest_end(nlh, nest1); +} +EXPORT_SYMBOL(nft_rule_expr_build_payload); diff --git a/src/expr/cmp.c b/src/expr/cmp.c new file mode 100644 index 0000000..cabaddd --- /dev/null +++ b/src/expr/cmp.c @@ -0,0 +1,194 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ + +#include "internal.h" + +#include +#include +#include +#include + +#include +#include +#include +#include "expr_ops.h" +#include "data_reg.h" + +struct nft_expr_cmp { + union nft_data_reg data; + uint8_t sreg; /* enum nft_registers */ + uint8_t op; /* enum nft_cmp_ops */ +}; + +static int +nft_rule_expr_cmp_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data; + + switch(type) { + case NFT_EXPR_CMP_SREG: + cmp->sreg = *((uint32_t *)data); + break; + case NFT_EXPR_CMP_OP: + cmp->op = *((uint32_t *)data); + break; + case NFT_EXPR_CMP_DATA: + memcpy(&cmp->data.val, data, data_len); + cmp->data.len = data_len; + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_cmp_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len) +{ + struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data; + + switch(type) { + case NFT_EXPR_CMP_SREG: + if (e->flags & (1 << NFT_EXPR_CMP_SREG)) { + *data_len = sizeof(cmp->sreg); + return &cmp->sreg; + } else + return NULL; + break; + case NFT_EXPR_CMP_OP: + if (e->flags & (1 << NFT_EXPR_CMP_OP)) { + *data_len = sizeof(cmp->op); + return &cmp->op; + } else + return NULL; + break; + case NFT_EXPR_CMP_DATA: + if (e->flags & (1 << NFT_EXPR_CMP_DATA)) { + *data_len = cmp->data.len; + return &cmp->data.val; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_cmp_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_CMP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_CMP_SREG: + case NFTA_CMP_OP: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_CMP_DATA: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_cmp_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data; + + if (e->flags & (1 << NFT_EXPR_CMP_SREG)) + mnl_attr_put_u32(nlh, NFTA_CMP_SREG, htonl(cmp->sreg)); + if (e->flags & (1 << NFT_EXPR_CMP_OP)) + mnl_attr_put_u32(nlh, NFTA_CMP_OP, htonl(cmp->op)); + if (e->flags & (1 << NFT_EXPR_CMP_DATA)) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_CMP_DATA); + mnl_attr_put(nlh, NFTA_DATA_VALUE, cmp->data.len, cmp->data.val); + mnl_attr_nest_end(nlh, nest); + } +} + +static int +nft_rule_expr_cmp_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data; + struct nlattr *tb[NFTA_CMP_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_cmp_cb, tb) < 0) + return -1; + + if (tb[NFTA_CMP_SREG]) { + cmp->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_CMP_SREG])); + e->flags |= (1 << NFTA_CMP_SREG); + } + if (tb[NFTA_CMP_OP]) { + cmp->op = ntohl(mnl_attr_get_u32(tb[NFTA_CMP_OP])); + e->flags |= (1 << NFTA_CMP_OP); + } + if (tb[NFTA_CMP_DATA]) { + ret = nft_parse_data(&cmp->data, tb[NFTA_CMP_DATA], NULL); + e->flags |= (1 << NFTA_CMP_DATA); + } + + return ret; +} + +static char *expr_cmp_str[] = { + [NFT_CMP_EQ] = "eq", + [NFT_CMP_NEQ] = "neq", + [NFT_CMP_LT] = "lt", + [NFT_CMP_LTE] = "lte", + [NFT_CMP_GT] = "gt", + [NFT_CMP_GTE] = "gte", +}; + +static int +nft_rule_expr_cmp_snprintf(char *buf, size_t size, struct nft_rule_expr *e) +{ + struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data; + int len = size, offset = 0, ret, i; + + ret = snprintf(buf, len, "sreg=%u op=%s data=", + cmp->sreg, expr_cmp_str[cmp->op]); + SNPRINTF_BUFFER_SIZE(ret, size, len, offset); + + for (i=0; idata.len/sizeof(uint32_t); i++) { + ret = snprintf(buf+offset, len, "%.8x ", cmp->data.val[i]); + SNPRINTF_BUFFER_SIZE(ret, size, len, offset); + } + return offset; +} + +struct expr_ops expr_ops_cmp = { + .name = "cmp", + .alloc_len = sizeof(struct nft_expr_cmp), + .max_attr = NFTA_CMP_MAX, + .set = nft_rule_expr_cmp_set, + .get = nft_rule_expr_cmp_get, + .parse = nft_rule_expr_cmp_parse, + .build = nft_rule_expr_cmp_build, + .snprintf = nft_rule_expr_cmp_snprintf, +}; diff --git a/src/expr/counter.c b/src/expr/counter.c new file mode 100644 index 0000000..26b7d6f --- /dev/null +++ b/src/expr/counter.c @@ -0,0 +1,145 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ + +#include +#include +#include + +#include + +#include "internal.h" +#include +#include +#include "expr_ops.h" + +struct nft_expr_counter { + uint64_t pkts; + uint64_t bytes; +}; + +static int +nft_rule_expr_counter_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data; + + switch(type) { + case NFT_EXPR_CTR_BYTES: + ctr->bytes = *((uint64_t *)data); + break; + case NFT_EXPR_CTR_PACKETS: + ctr->pkts = *((uint64_t *)data); + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_counter_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len) +{ + struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data; + + switch(type) { + case NFT_EXPR_CTR_BYTES: + if (e->flags & (1 << NFT_EXPR_CTR_BYTES)) { + *data_len = sizeof(ctr->bytes); + return &ctr->bytes; + } else + return NULL; + break; + case NFT_EXPR_CTR_PACKETS: + if (e->flags & (1 << NFT_EXPR_CTR_PACKETS)) { + *data_len = sizeof(ctr->pkts); + return &ctr->pkts; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_counter_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_COUNTER_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_COUNTER_BYTES: + case NFTA_COUNTER_PACKETS: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_counter_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data; + + if (e->flags & (1 << NFT_EXPR_CTR_BYTES)) + mnl_attr_put_u64(nlh, NFTA_COUNTER_BYTES, htobe64(ctr->bytes)); + if (e->flags & (1 << NFT_EXPR_CTR_PACKETS)) + mnl_attr_put_u64(nlh, NFTA_COUNTER_PACKETS, htobe64(ctr->pkts)); +} + +static int +nft_rule_expr_counter_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data; + struct nlattr *tb[NFTA_COUNTER_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_counter_cb, tb) < 0) + return -1; + + if (tb[NFTA_COUNTER_BYTES]) { + ctr->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_BYTES])); + e->flags |= (1 << NFT_EXPR_CTR_BYTES); + } + if (tb[NFTA_COUNTER_PACKETS]) { + ctr->pkts = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_PACKETS])); + e->flags |= (1 << NFT_EXPR_CTR_PACKETS); + } + + return 0; +} + +static int +nft_rule_expr_counter_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data; + + return snprintf(buf, len, "pkts=%lu bytes=%lu ", + ctr->pkts, ctr->bytes); +} + +struct expr_ops expr_ops_counter = { + .name = "counter", + .alloc_len = sizeof(struct nft_expr_counter), + .max_attr = NFTA_COUNTER_MAX, + .set = nft_rule_expr_counter_set, + .get = nft_rule_expr_counter_get, + .parse = nft_rule_expr_counter_parse, + .build = nft_rule_expr_counter_build, + .snprintf = nft_rule_expr_counter_snprintf, +}; diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c new file mode 100644 index 0000000..5b14695 --- /dev/null +++ b/src/expr/data_reg.c @@ -0,0 +1,158 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include "expr_ops.h" +#include "data_reg.h" +#include "internal.h" + +static int nft_data_parse_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_DATA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_DATA_VALUE: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_DATA_VERDICT: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int nft_verdict_parse_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_VERDICT_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_VERDICT_CODE: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_VERDICT_CHAIN: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nft_parse_verdict(union nft_data_reg *data, const struct nlattr *attr, int *type) +{ + struct nlattr *tb[NFTA_VERDICT_MAX+1]; + + if (mnl_attr_parse_nested(attr, nft_verdict_parse_cb, tb) < 0) { + perror("mnl_attr_parse_nested"); + return -1; + } + + if (!tb[NFTA_VERDICT_CODE]) + return -1; + + data->verdict = ntohl(mnl_attr_get_u32(tb[NFTA_VERDICT_CODE])); + + switch(data->verdict) { + case NF_ACCEPT: + case NF_DROP: + case NF_QUEUE: + case NFT_CONTINUE: + case NFT_BREAK: + case NFT_RETURN: + if (type) + *type = DATA_VERDICT; + data->len = sizeof(data->verdict); + break; + case NFT_JUMP: + case NFT_GOTO: + if (!tb[NFTA_VERDICT_CHAIN]) + return -1; + + data->chain = strdup(mnl_attr_get_str(tb[NFTA_VERDICT_CHAIN])); + if (type) + *type = DATA_CHAIN; + break; + default: + return -1; + } + + return 0; +} + +static int +__nft_parse_data(union nft_data_reg *data, const struct nlattr *attr) +{ + void *orig = mnl_attr_get_payload(attr); + size_t data_len = mnl_attr_get_payload_len(attr); + + if (data_len == 0) + return -1; + + if (data_len > sizeof(uint32_t) * 4) + return -1; + + memcpy(data->val, orig, data_len); + data->len = data_len; + + return 0; +} + +int nft_parse_data(union nft_data_reg *data, struct nlattr *attr, int *type) +{ + struct nlattr *tb[NFTA_DATA_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nft_data_parse_cb, tb) < 0) { + perror("mnl_attr_parse_nested"); + return -1; + } + if (tb[NFTA_DATA_VALUE]) { + if (type) + *type = DATA_VALUE; + + ret = __nft_parse_data(data, tb[NFTA_DATA_VALUE]); + if (ret < 0) + return ret; + } + if (tb[NFTA_DATA_VERDICT]) + ret = nft_parse_verdict(data, tb[NFTA_DATA_VERDICT], type); + + return ret; +} diff --git a/src/expr/data_reg.h b/src/expr/data_reg.h new file mode 100644 index 0000000..00eab63 --- /dev/null +++ b/src/expr/data_reg.h @@ -0,0 +1,23 @@ +#ifndef _DATA_H_ +#define _DATA_H_ + +enum { + DATA_VALUE, + DATA_VERDICT, + DATA_CHAIN, +}; + +union nft_data_reg { + struct { + uint32_t val[4]; + size_t len; + }; + struct { + int verdict; + char *chain; + }; +}; + +int nft_parse_data(union nft_data_reg *data, struct nlattr *attr, int *type); + +#endif diff --git a/src/expr/immediate.c b/src/expr/immediate.c new file mode 100644 index 0000000..68c27ec --- /dev/null +++ b/src/expr/immediate.c @@ -0,0 +1,215 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ + +#include +#include +#include +#include + +#include "internal.h" +#include +#include +#include +#include "expr_ops.h" +#include "data_reg.h" + +struct nft_expr_immediate { + union nft_data_reg data; + enum nft_registers dreg; +}; + +static int +nft_rule_expr_immediate_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data; + + switch(type) { + case NFT_EXPR_IMM_DREG: + imm->dreg = *((uint32_t *)data); + break; + case NFT_EXPR_IMM_DATA: + memcpy(&imm->data.val, data, data_len); + imm->data.len = data_len; + break; + case NFT_EXPR_IMM_VERDICT: + imm->data.verdict = *((uint32_t *)data); + break; + case NFT_EXPR_IMM_CHAIN: + if (imm->data.chain) + free(imm->data.chain); + + imm->data.chain = strdup(data); + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_immediate_get(struct nft_rule_expr *e, uint16_t type, + size_t *data_len) +{ + struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data; + + switch(type) { + case NFT_EXPR_IMM_DREG: + if (e->flags & (1 << NFT_EXPR_IMM_DREG)) { + *data_len = sizeof(imm->dreg); + return &imm->dreg; + } else + return NULL; + break; + case NFT_EXPR_IMM_DATA: + if (e->flags & (1 << NFT_EXPR_IMM_DATA)) { + *data_len = imm->data.len; + return &imm->data.val; + } else + return NULL; + break; + case NFT_EXPR_IMM_VERDICT: + if (e->flags & (1 << NFT_EXPR_IMM_VERDICT)) { + *data_len = sizeof(imm->data.verdict); + return &imm->data.verdict; + } else + return NULL; + break; + case NFT_EXPR_IMM_CHAIN: + if (e->flags & (1 << NFT_EXPR_IMM_CHAIN)) { + *data_len = strlen(imm->data.chain)+1; + return imm->data.chain; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_immediate_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_IMMEDIATE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_IMMEDIATE_DREG: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_IMMEDIATE_DATA: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_immediate_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data; + + if (e->flags & (1 << NFT_EXPR_IMM_DREG)) + mnl_attr_put_u32(nlh, NFTA_IMMEDIATE_DREG, htonl(imm->dreg)); + + /* Sane configurations allows you to set ONLY one of these two below */ + if (e->flags & (1 << NFT_EXPR_IMM_DATA)) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_IMMEDIATE_DATA); + mnl_attr_put(nlh, NFTA_DATA_VALUE, imm->data.len, imm->data.val); + mnl_attr_nest_end(nlh, nest); + + } else if (e->flags & (1 << NFT_EXPR_IMM_VERDICT)) { + struct nlattr *nest1, *nest2; + + nest1 = mnl_attr_nest_start(nlh, NFTA_IMMEDIATE_DATA); + nest2 = mnl_attr_nest_start(nlh, NFTA_DATA_VERDICT); + mnl_attr_put_u32(nlh, NFTA_VERDICT_CODE, htonl(imm->data.verdict)); + if (e->flags & (1 << NFT_EXPR_IMM_CHAIN)) + mnl_attr_put_strz(nlh, NFTA_VERDICT_CHAIN, imm->data.chain); + + mnl_attr_nest_end(nlh, nest1); + mnl_attr_nest_end(nlh, nest2); + } +} + +static int +nft_rule_expr_immediate_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data; + struct nlattr *tb[NFTA_IMMEDIATE_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_immediate_cb, tb) < 0) + return -1; + + if (tb[NFTA_IMMEDIATE_DREG]) { + imm->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_IMMEDIATE_DREG])); + e->flags |= (1 << NFT_EXPR_IMM_DREG); + } + if (tb[NFTA_IMMEDIATE_DATA]) { + int type; + + ret = nft_parse_data(&imm->data, tb[NFTA_IMMEDIATE_DATA], &type); + if (ret < 0) + return ret; + + switch(type) { + case DATA_VALUE: + /* real immediate data to be loaded to destination */ + e->flags |= (1 << NFT_EXPR_IMM_DATA); + break; + case DATA_VERDICT: + /* NF_ACCEPT, NF_DROP, NF_QUEUE and NFT_RETURN case */ + e->flags |= (1 << NFT_EXPR_IMM_VERDICT); + break; + case DATA_CHAIN: + /* NFT_GOTO and NFT_JUMP case */ + e->flags |= (1 << NFT_EXPR_IMM_VERDICT) | + (1 << NFT_EXPR_IMM_CHAIN); + break; + } + } + + return ret; +} + +static int +nft_rule_expr_immediate_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data; + + return snprintf(buf, len, "dreg=%u data=%u ", + imm->dreg, imm->data.val[0]); +} + +struct expr_ops expr_ops_immediate = { + .name = "immediate", + .alloc_len = sizeof(struct nft_expr_immediate), + .max_attr = NFTA_IMMEDIATE_MAX, + .set = nft_rule_expr_immediate_set, + .get = nft_rule_expr_immediate_get, + .parse = nft_rule_expr_immediate_parse, + .build = nft_rule_expr_immediate_build, + .snprintf = nft_rule_expr_immediate_snprintf, +}; diff --git a/src/expr/match.c b/src/expr/match.c new file mode 100644 index 0000000..a9e2dee --- /dev/null +++ b/src/expr/match.c @@ -0,0 +1,203 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ + +#include "internal.h" + +#include +#include +#include /* for memcpy */ +#include + +#include + +#include +#include + +#include + +#include "expr_ops.h" + +struct nft_expr_match { + char name[XT_EXTENSION_MAXNAMELEN]; + uint32_t rev; + uint32_t data_len; + const void *data; +}; + +static int +nft_rule_expr_match_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_match *mt = (struct nft_expr_match *)e->data; + + switch(type) { + case NFT_EXPR_MT_NAME: + memcpy(mt->name, data, XT_EXTENSION_MAXNAMELEN); + mt->name[XT_EXTENSION_MAXNAMELEN-1] = '\0'; + break; + case NFT_EXPR_MT_REV: + mt->rev = *((uint32_t *)data); + break; + case NFT_EXPR_MT_INFO: + if (mt->data) + free((void *)mt->data); + + mt->data = data; + mt->data_len = data_len; + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_match_get(struct nft_rule_expr *e, uint16_t type, + size_t *data_len) +{ + struct nft_expr_match *mt = (struct nft_expr_match *)e->data; + + switch(type) { + case NFT_EXPR_MT_NAME: + if (e->flags & (1 << NFT_EXPR_MT_NAME)) { + *data_len = sizeof(mt->name); + return mt->name; + } else + return NULL; + break; + case NFT_EXPR_MT_REV: + if (e->flags & (1 << NFT_EXPR_MT_REV)) { + *data_len = sizeof(mt->rev); + return &mt->rev; + } else + return NULL; + break; + case NFT_EXPR_MT_INFO: + if (e->flags & (1 << NFT_EXPR_MT_INFO)) { + *data_len = mt->data_len; + return mt->data; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_match_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_MATCH_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_MATCH_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_MATCH_REV: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_MATCH_INFO: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_match_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_match *mt = (struct nft_expr_match *)e->data; + + if (e->flags & (1 << NFT_EXPR_MT_NAME)) + mnl_attr_put_strz(nlh, NFTA_MATCH_NAME, mt->name); + if (e->flags & (1 << NFT_EXPR_MT_REV)) + mnl_attr_put_u32(nlh, NFTA_MATCH_REV, htonl(mt->rev)); + if (e->flags & (1 << NFT_EXPR_MT_INFO)) + mnl_attr_put(nlh, NFTA_MATCH_INFO, XT_ALIGN(mt->data_len), mt->data); +} + +static int nft_rule_expr_match_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_match *match = (struct nft_expr_match *)e->data; + struct nlattr *tb[NFTA_MATCH_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_match_cb, tb) < 0) + return -1; + + if (tb[NFTA_MATCH_NAME]) { + snprintf(match->name, XT_EXTENSION_MAXNAMELEN, "%s", + mnl_attr_get_str(tb[NFTA_MATCH_NAME])); + + match->name[XT_EXTENSION_MAXNAMELEN-1] = '\0'; + e->flags |= (1 << NFTA_MATCH_NAME); + } + + if (tb[NFTA_MATCH_REV]) { + match->rev = ntohl(mnl_attr_get_u32(tb[NFTA_MATCH_REV])); + e->flags |= (1 << NFTA_MATCH_REV); + } + + if (tb[NFTA_MATCH_INFO]) { + uint32_t len = mnl_attr_get_payload_len(tb[NFTA_MATCH_INFO]); + void *match_data; + + if (match->data) + free((void *) match->data); + + match_data = calloc(1, len); + if (match_data == NULL) + return -1; + + memcpy(match_data, mnl_attr_get_payload(tb[NFTA_MATCH_INFO]), len); + + match->data = match_data; + match->data_len = len; + + e->flags |= (1 << NFTA_MATCH_INFO); + } + + return 0; +} + +static int +nft_rule_expr_match_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_match *match = (struct nft_expr_match *)e->data; + + return snprintf(buf, len, "name=%s rev=%u ", + match->name, match->rev); +} + +struct expr_ops expr_ops_match = { + .name = "match", + .alloc_len = sizeof(struct nft_expr_match), + .max_attr = NFTA_MATCH_MAX, + .set = nft_rule_expr_match_set, + .get = nft_rule_expr_match_get, + .parse = nft_rule_expr_match_parse, + .build = nft_rule_expr_match_build, + .snprintf = nft_rule_expr_match_snprintf, +}; diff --git a/src/expr/meta.c b/src/expr/meta.c new file mode 100644 index 0000000..0251e43 --- /dev/null +++ b/src/expr/meta.c @@ -0,0 +1,144 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ + +#include +#include +#include + +#include + +#include "internal.h" +#include +#include +#include "expr_ops.h" + +struct nft_expr_meta { + uint8_t key; /* enum nft_meta_keys */ + uint8_t dreg; /* enum nft_registers */ +}; + +static int +nft_rule_expr_meta_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data; + + switch(type) { + case NFT_EXPR_META_KEY: + meta->key = *((uint32_t *)data); + break; + case NFT_EXPR_META_DREG: + meta->dreg = *((uint32_t *)data); + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_meta_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len) +{ + struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data; + + switch(type) { + case NFT_EXPR_META_KEY: + if (e->flags & (1 << NFT_EXPR_META_KEY)) { + *data_len = sizeof(meta->key); + return &meta->key; + } else + return NULL; + break; + case NFT_EXPR_META_DREG: + if (e->flags & (1 << NFT_EXPR_META_DREG)) { + *data_len = sizeof(meta->dreg); + return &meta->dreg; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_meta_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_META_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_META_KEY: + case NFTA_META_DREG: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_meta_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data; + + if (e->flags & (1 << NFT_EXPR_META_KEY)) + mnl_attr_put_u32(nlh, NFTA_META_KEY, htonl(meta->key)); + if (e->flags & (1 << NFT_EXPR_META_DREG)) + mnl_attr_put_u32(nlh, NFTA_META_DREG, htonl(meta->dreg)); +} + +static int +nft_rule_expr_meta_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data; + struct nlattr *tb[NFTA_META_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_meta_cb, tb) < 0) + return -1; + + if (tb[NFTA_META_KEY]) { + meta->key = ntohl(mnl_attr_get_u32(tb[NFTA_META_KEY])); + e->flags |= (1 << NFT_EXPR_META_KEY); + } + if (tb[NFTA_META_DREG]) { + meta->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_META_DREG])); + e->flags |= (1 << NFT_EXPR_META_DREG); + } + + return 0; +} + +static int +nft_rule_expr_meta_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data; + + return snprintf(buf, len, "dreg=%u key=%u ", meta->dreg, meta->key); +} + +struct expr_ops expr_ops_meta = { + .name = "meta", + .alloc_len = sizeof(struct nft_expr_meta), + .max_attr = NFTA_META_MAX, + .set = nft_rule_expr_meta_set, + .get = nft_rule_expr_meta_get, + .parse = nft_rule_expr_meta_parse, + .build = nft_rule_expr_meta_build, + .snprintf = nft_rule_expr_meta_snprintf, +}; diff --git a/src/expr/payload.c b/src/expr/payload.c new file mode 100644 index 0000000..746a127 --- /dev/null +++ b/src/expr/payload.c @@ -0,0 +1,185 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ + +#include "internal.h" + +#include +#include +#include + +#include + +#include + +#include + +#include "expr_ops.h" + +struct nft_expr_payload { + enum nft_registers dreg; + enum nft_payload_bases base; + unsigned int offset; + unsigned int len; +}; + +static int +nft_rule_expr_payload_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data; + + switch(type) { + case NFT_EXPR_PAYLOAD_DREG: + payload->dreg = *((uint32_t *)data); + break; + case NFT_EXPR_PAYLOAD_BASE: + payload->base = *((uint32_t *)data); + break; + case NFT_EXPR_PAYLOAD_OFFSET: + payload->offset = *((unsigned int *)data); + break; + case NFT_EXPR_PAYLOAD_LEN: + payload->len = *((unsigned int *)data); + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_payload_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len) +{ + struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data; + + switch(type) { + case NFT_EXPR_PAYLOAD_DREG: + if (e->flags & (1 << NFT_EXPR_PAYLOAD_DREG)) { + *data_len = sizeof(payload->dreg); + return &payload->dreg; + } else + return NULL; + break; + case NFT_EXPR_PAYLOAD_BASE: + if (e->flags & (1 << NFT_EXPR_PAYLOAD_BASE)) { + *data_len = sizeof(payload->base); + return &payload->base; + } else + return NULL; + break; + case NFT_EXPR_PAYLOAD_OFFSET: + if (e->flags & (1 << NFT_EXPR_PAYLOAD_OFFSET)) { + *data_len = sizeof(payload->offset); + return &payload->offset; + } else + return NULL; + break; + case NFT_EXPR_PAYLOAD_LEN: + if (e->flags & (1 << NFT_EXPR_PAYLOAD_LEN)) { + *data_len = sizeof(payload->len); + return &payload->len; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_payload_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_PAYLOAD_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_PAYLOAD_DREG: + case NFTA_PAYLOAD_BASE: + case NFTA_PAYLOAD_OFFSET: + case NFTA_PAYLOAD_LEN: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_payload_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data; + + if (e->flags & (1 << NFT_EXPR_PAYLOAD_DREG)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_DREG, htonl(payload->dreg)); + if (e->flags & (1 << NFT_EXPR_PAYLOAD_BASE)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_BASE, htonl(payload->base)); + if (e->flags & (1 << NFT_EXPR_PAYLOAD_OFFSET)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_OFFSET, htonl(payload->offset)); + if (e->flags & (1 << NFT_EXPR_PAYLOAD_LEN)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_LEN, htonl(payload->len)); +} + +static int +nft_rule_expr_payload_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data; + struct nlattr *tb[NFTA_PAYLOAD_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_payload_cb, tb) < 0) + return -1; + + if (tb[NFTA_PAYLOAD_DREG]) { + payload->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_DREG])); + e->flags |= (1 << NFT_EXPR_PAYLOAD_DREG); + } + if (tb[NFTA_PAYLOAD_BASE]) { + payload->base = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_BASE])); + e->flags |= (1 << NFT_EXPR_PAYLOAD_BASE); + } + if (tb[NFTA_PAYLOAD_OFFSET]) { + payload->offset = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_OFFSET])); + e->flags |= (1 << NFT_EXPR_PAYLOAD_OFFSET); + } + if (tb[NFTA_PAYLOAD_LEN]) { + payload->len = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_LEN])); + e->flags |= (1 << NFT_EXPR_PAYLOAD_LEN); + } + + return 0; +} + +static int +nft_rule_expr_payload_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data; + + return snprintf(buf, len, "dreg=%u base=%u offset=%u len=%u ", + payload->dreg, payload->base, + payload->offset, payload->len); +} + +struct expr_ops expr_ops_payload = { + .name = "payload", + .alloc_len = sizeof(struct nft_expr_payload), + .max_attr = NFTA_PAYLOAD_MAX, + .set = nft_rule_expr_payload_set, + .get = nft_rule_expr_payload_get, + .parse = nft_rule_expr_payload_parse, + .build = nft_rule_expr_payload_build, + .snprintf = nft_rule_expr_payload_snprintf, +}; diff --git a/src/expr/target.c b/src/expr/target.c new file mode 100644 index 0000000..fccf64b --- /dev/null +++ b/src/expr/target.c @@ -0,0 +1,203 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ + +#include "internal.h" + +#include +#include +#include /* for memcpy */ +#include + +#include + +#include +#include + +#include + +#include "expr_ops.h" + +struct nft_expr_target { + char name[XT_EXTENSION_MAXNAMELEN]; + uint32_t rev; + uint32_t data_len; + const void *data; +}; + +static int +nft_rule_expr_target_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_target *tg = (struct nft_expr_target *)e->data; + + switch(type) { + case NFT_EXPR_TG_NAME: + memcpy(tg->name, data, XT_EXTENSION_MAXNAMELEN); + tg->name[XT_EXTENSION_MAXNAMELEN-1] = '\0'; + break; + case NFT_EXPR_TG_REV: + tg->rev = *((uint32_t *)data); + break; + case NFT_EXPR_TG_INFO: + if (tg->data) + free((void *)tg->data); + + tg->data = data; + tg->data_len = data_len; + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_target_get(struct nft_rule_expr *e, uint16_t type, + size_t *data_len) +{ + struct nft_expr_target *tg = (struct nft_expr_target *)e->data; + + switch(type) { + case NFT_EXPR_TG_NAME: + if (e->flags & (1 << NFT_EXPR_TG_NAME)) { + *data_len = sizeof(tg->name); + return tg->name; + } else + return NULL; + break; + case NFT_EXPR_TG_REV: + if (e->flags & (1 << NFT_EXPR_TG_REV)) { + *data_len = sizeof(tg->rev); + return &tg->rev; + } else + return NULL; + break; + case NFT_EXPR_TG_INFO: + if (e->flags & (1 << NFT_EXPR_TG_INFO)) { + *data_len = tg->data_len; + return tg->data; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_target_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_TARGET_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_TARGET_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_TARGET_REV: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_TARGET_INFO: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_target_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_target *tg = (struct nft_expr_target *)e->data; + + if (e->flags & (1 << NFT_EXPR_TG_NAME)) + mnl_attr_put_strz(nlh, NFTA_TARGET_NAME, tg->name); + if (e->flags & (1 << NFT_EXPR_TG_REV)) + mnl_attr_put_u32(nlh, NFTA_TARGET_REV, htonl(tg->rev)); + if (e->flags & (1 << NFT_EXPR_TG_INFO)) + mnl_attr_put(nlh, NFTA_TARGET_INFO, XT_ALIGN(tg->data_len), tg->data); +} + +static int nft_rule_expr_target_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_target *target = (struct nft_expr_target *)e->data; + struct nlattr *tb[NFTA_TARGET_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_target_cb, tb) < 0) + return -1; + + if (tb[NFTA_TARGET_NAME]) { + snprintf(target->name, XT_EXTENSION_MAXNAMELEN, "%s", + mnl_attr_get_str(tb[NFTA_TARGET_NAME])); + + target->name[XT_EXTENSION_MAXNAMELEN-1] = '\0'; + e->flags |= (1 << NFT_EXPR_TG_NAME); + } + + if (tb[NFTA_TARGET_REV]) { + target->rev = ntohl(mnl_attr_get_u32(tb[NFTA_TARGET_REV])); + e->flags |= (1 << NFT_EXPR_TG_REV); + } + + if (tb[NFTA_TARGET_INFO]) { + uint32_t len = mnl_attr_get_len(tb[NFTA_TARGET_INFO]); + void *target_data; + + if (target->data) + free((void *) target->data); + + target_data = calloc(1, len); + if (target_data == NULL) + return -1; + + memcpy(target_data, mnl_attr_get_payload(tb[NFTA_TARGET_INFO]), len); + + target->data = target_data; + target->data_len = len; + + e->flags |= (1 << NFT_EXPR_TG_INFO); + } + + return 0; +} + +static int +nft_rule_expr_target_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_target *target = (struct nft_expr_target *)e->data; + + return snprintf(buf, len, "name=%s rev=%u ", + target->name, target->rev); +} + +struct expr_ops expr_ops_target = { + .name = "target", + .alloc_len = sizeof(struct nft_expr_target), + .max_attr = NFTA_TARGET_MAX, + .set = nft_rule_expr_target_set, + .get = nft_rule_expr_target_get, + .parse = nft_rule_expr_target_parse, + .build = nft_rule_expr_target_build, + .snprintf = nft_rule_expr_target_snprintf, +}; diff --git a/src/expr_ops.c b/src/expr_ops.c new file mode 100644 index 0000000..2b21d96 --- /dev/null +++ b/src/expr_ops.c @@ -0,0 +1,36 @@ +#include + +#include "expr_ops.h" + +extern struct expr_ops expr_ops_cmp; +extern struct expr_ops expr_ops_counter; +extern struct expr_ops expr_ops_immediate; +extern struct expr_ops expr_ops_match; +extern struct expr_ops expr_ops_meta; +extern struct expr_ops expr_ops_payload; +extern struct expr_ops expr_ops_target; + +struct expr_ops *expr_ops[] = { + &expr_ops_cmp, + &expr_ops_counter, + &expr_ops_immediate, + &expr_ops_match, + &expr_ops_meta, + &expr_ops_payload, + &expr_ops_target, + NULL, +}; + +struct expr_ops *nft_expr_ops_lookup(const char *name) +{ + int i = 0; + + while (expr_ops[i] != NULL) { + if (strcmp(expr_ops[i]->name, name) == 0) + return expr_ops[i]; + + i++; + } + + return NULL; +} diff --git a/src/expr_ops.h b/src/expr_ops.h new file mode 100644 index 0000000..2322958 --- /dev/null +++ b/src/expr_ops.h @@ -0,0 +1,24 @@ +#ifndef _EXPR_OPS_H_ +#define _EXPR_OPS_H_ + +#include +#include + +struct nlattr; +struct nlmsghdr; +struct nft_rule_expr; + +struct expr_ops { + char *name; + size_t alloc_len; + int max_attr; + int (*set)(struct nft_rule_expr *e, uint16_t type, const void *data, size_t data_len); + const void *(*get)(struct nft_rule_expr *e, uint16_t type, size_t *data_len); + int (*parse)(struct nft_rule_expr *e, struct nlattr *attr); + void (*build)(struct nlmsghdr *nlh, struct nft_rule_expr *e); + int (*snprintf)(char *buf, size_t len, struct nft_rule_expr *e); +}; + +struct expr_ops *nft_expr_ops_lookup(const char *name); + +#endif diff --git a/src/internal.h b/src/internal.h new file mode 100644 index 0000000..5ba7c76 --- /dev/null +++ b/src/internal.h @@ -0,0 +1,32 @@ +#ifndef INTERNAL_H +#define INTERNAL_H 1 + +#include "config.h" +#ifdef HAVE_VISIBILITY_HIDDEN +# define __visible __attribute__((visibility("default"))) +# define EXPORT_SYMBOL(x) typeof(x) (x) __visible +#else +# define EXPORT_SYMBOL +#endif + +#include "linux_list.h" + +#include + +struct expr_ops; + +struct nft_rule_expr { + struct list_head head; + uint32_t flags; + struct expr_ops *ops; + uint8_t data[]; +}; + +#define SNPRINTF_BUFFER_SIZE(ret, size, len, offset) \ + size += ret; \ + if (ret > len) \ + ret = len; \ + offset += ret; \ + len -= ret; + +#endif diff --git a/src/libnftables.map b/src/libnftables.map new file mode 100644 index 0000000..4024764 --- /dev/null +++ b/src/libnftables.map @@ -0,0 +1,80 @@ +LIBNFTABLES_1.0 { +global: + nft_table_alloc; + nft_table_free; + nft_table_attr_set; + nft_table_attr_get; + nft_table_snprintf; + nft_table_nlmsg_build_hdr; + nft_table_nlmsg_build_payload; + nft_table_nlmsg_parse; + nft_table_list_alloc; + nft_table_list_free; + nft_table_list_add; + nft_table_list_iter_create; + nft_table_list_iter_next; + nft_table_list_iter_destroy; + + nft_chain_alloc; + nft_chain_free; + nft_chain_attr_set; + nft_chain_attr_set_u32; + nft_chain_attr_set_s32; + nft_chain_attr_set_u64; + nft_chain_attr_get; + nft_chain_attr_get_u32; + nft_chain_attr_get_s32; + nft_chain_attr_get_u64; + nft_chain_attr_get_str; + nft_chain_snprintf; + nft_chain_nlmsg_build_hdr; + nft_chain_nlmsg_build_payload; + nft_chain_nlmsg_parse; + nft_chain_list_alloc; + nft_chain_list_free; + nft_chain_list_add; + nft_chain_list_iter_create; + nft_chain_list_iter_next; + nft_chain_list_iter_destroy; + + nft_rule_alloc; + nft_rule_free; + nft_rule_attr_set; + nft_rule_attr_set_u16; + nft_rule_attr_set_str; + nft_rule_attr_get; + nft_rule_attr_get_u16; + nft_rule_attr_get_str; + nft_rule_snprintf; + nft_rule_nlmsg_build_hdr; + nft_rule_nlmsg_build_payload; + nft_rule_nlmsg_parse; + nft_rule_add_expr; + + nft_rule_expr_iter_create; + nft_rule_expr_iter_next; + nft_rule_expr_iter_destroy; + + nft_rule_expr_alloc; + nft_rule_expr_set; + nft_rule_expr_set_u8; + nft_rule_expr_set_u32; + nft_rule_expr_set_u64; + nft_rule_expr_set_str; + nft_rule_expr_get; + nft_rule_expr_get_u8; + nft_rule_expr_get_u32; + nft_rule_expr_get_u64; + nft_rule_expr_get_str; + nft_rule_expr_free; + + nft_rule_list_alloc; + nft_rule_list_free; + nft_rule_list_add; + nft_rule_list_iter_create; + nft_rule_list_iter_cur; + nft_rule_list_iter_next; + nft_rule_list_iter_destroy; + +local: *; +}; diff --git a/src/rule.c b/src/rule.c new file mode 100644 index 0000000..b0f7bb3 --- /dev/null +++ b/src/rule.c @@ -0,0 +1,452 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "linux_list.h" +#include "expr_ops.h" + +struct nft_rule { + struct list_head head; + + uint32_t flags; + char *table; + char *chain; + uint8_t family; + uint16_t handle; + + struct list_head expr_list; +}; + +struct nft_rule *nft_rule_alloc(void) +{ + struct nft_rule *r; + + r = calloc(1, sizeof(struct nft_rule)); + if (r == NULL) + return NULL; + + INIT_LIST_HEAD(&r->expr_list); + + return r; +} +EXPORT_SYMBOL(nft_rule_alloc); + +void nft_rule_free(struct nft_rule *r) +{ + if (r->table != NULL) + free(r->table); + if (r->chain != NULL) + free(r->chain); + + free(r); +} +EXPORT_SYMBOL(nft_rule_free); + +void nft_rule_attr_set(struct nft_rule *r, uint16_t attr, void *data) +{ + switch(attr) { + case NFT_RULE_ATTR_TABLE: + if (r->table) + free(r->table); + + r->table = strdup(data); + break; + case NFT_RULE_ATTR_CHAIN: + if (r->chain) + free(r->chain); + + r->chain = strdup(data); + break; + case NFT_RULE_ATTR_HANDLE: + r->handle = *((uint16_t *)data); + break; + default: + return; + } + r->flags |= (1 << attr); +} +EXPORT_SYMBOL(nft_rule_attr_set); + +void nft_rule_attr_set_u16(struct nft_rule *r, uint16_t attr, uint16_t val) +{ + nft_rule_attr_set(r, attr, &val); +} +EXPORT_SYMBOL(nft_rule_attr_set_u16); + +void nft_rule_attr_set_str(struct nft_rule *r, uint16_t attr, char *str) +{ + nft_rule_attr_set(r, attr, str); +} +EXPORT_SYMBOL(nft_rule_attr_set_str); + +void *nft_rule_attr_get(struct nft_rule *r, uint16_t attr) +{ + switch(attr) { + case NFT_RULE_ATTR_TABLE: + if (r->flags & (1 << NFT_RULE_ATTR_TABLE)) + return r->table; + else + return NULL; + break; + case NFT_RULE_ATTR_CHAIN: + if (r->flags & (1 << NFT_RULE_ATTR_CHAIN)) + return r->chain; + else + return NULL; + case NFT_RULE_ATTR_HANDLE: + if (r->flags & (1 << NFT_RULE_ATTR_HANDLE)) + return &r->handle; + else + return NULL; + break; + default: + return NULL; + } +} +EXPORT_SYMBOL(nft_rule_attr_get); + +const char *nft_rule_attr_get_str(struct nft_rule *r, uint16_t attr) +{ + return nft_rule_attr_get(r, attr); +} +EXPORT_SYMBOL(nft_rule_attr_get_str); + +uint16_t nft_rule_attr_get_u16(struct nft_rule *r, uint16_t attr) +{ + uint16_t val = *((uint32_t *)nft_rule_attr_get(r, attr)); + return val; +} +EXPORT_SYMBOL(nft_rule_attr_get_u16); + +struct nlmsghdr * +nft_rule_nlmsg_build_hdr(char *buf, uint16_t cmd, uint16_t family, + uint16_t type, uint32_t seq) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | cmd; + nlh->nlmsg_flags = NLM_F_REQUEST | type; + nlh->nlmsg_seq = seq; + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); + nfh->nfgen_family = family; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + + return nlh; +} +EXPORT_SYMBOL(nft_rule_nlmsg_build_hdr); + +void nft_rule_nlmsg_build_payload(struct nlmsghdr *nlh, struct nft_rule *r) +{ + struct nft_rule_expr *expr; + struct nlattr *nest; + + if (r->flags & (1 << NFT_RULE_ATTR_TABLE)) + mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, r->table); + if (r->flags & (1 << NFT_RULE_ATTR_CHAIN)) + mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, r->chain); + if (r->flags & (1 << NFT_RULE_ATTR_HANDLE)) + mnl_attr_put_u16(nlh, NFTA_RULE_HANDLE, htons(r->handle)); + + nest = mnl_attr_nest_start(nlh, NFTA_RULE_EXPRESSIONS); + list_for_each_entry(expr, &r->expr_list, head) { + nft_rule_expr_build_payload(nlh, expr); + } + mnl_attr_nest_end(nlh, nest); +} +EXPORT_SYMBOL(nft_rule_nlmsg_build_payload); + +void nft_rule_add_expr(struct nft_rule *r, struct nft_rule_expr *expr) +{ + list_add_tail(&expr->head, &r->expr_list); +} +EXPORT_SYMBOL(nft_rule_add_expr); + +static int nft_rule_parse_attr_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_RULE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_RULE_TABLE: + case NFTA_RULE_CHAIN: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_RULE_HANDLE: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int nft_rule_parse_expr_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_EXPR_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_EXPR_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_EXPR_DATA: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int nft_rule_parse_expr2(struct nlattr *attr, struct nft_rule *r) +{ + struct nlattr *tb[NFTA_EXPR_MAX+1] = {}; + struct nft_rule_expr *expr; + + if (mnl_attr_parse_nested(attr, nft_rule_parse_expr_cb, tb) < 0) + return -1; + + expr = nft_rule_expr_alloc(mnl_attr_get_str(tb[NFTA_EXPR_NAME])); + if (expr == NULL) + return -1; + + if (tb[NFTA_EXPR_DATA]) { + if (expr->ops->parse(expr, tb[NFTA_EXPR_DATA]) < 0) { + free(expr); + return -1; + } + } + list_add_tail(&expr->head, &r->expr_list); + + return 0; +} + +static int nft_rule_parse_expr(struct nlattr *nest, struct nft_rule *r) +{ + struct nlattr *attr; + + mnl_attr_for_each_nested(attr, nest) { + if (mnl_attr_get_type(attr) != NFTA_LIST_ELEM) + return -1; + + nft_rule_parse_expr2(attr, r); + } + return 0; +} + +int nft_rule_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_rule *r) +{ + struct nlattr *tb[NFTA_RULE_MAX+1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + int ret = 0; + + mnl_attr_parse(nlh, sizeof(*nfg), nft_rule_parse_attr_cb, tb); + if (tb[NFTA_RULE_TABLE]) { + r->table = strdup(mnl_attr_get_str(tb[NFTA_RULE_TABLE])); + r->flags |= (1 << NFT_RULE_ATTR_TABLE); + } + if (tb[NFTA_RULE_CHAIN]) { + r->chain = strdup(mnl_attr_get_str(tb[NFTA_RULE_CHAIN])); + r->flags |= (1 << NFT_RULE_ATTR_CHAIN); + } + if (tb[NFTA_RULE_HANDLE]) { + r->handle = ntohs(mnl_attr_get_u16(tb[NFTA_RULE_HANDLE])); + r->flags |= (1 << NFT_RULE_ATTR_HANDLE); + } + if (tb[NFTA_RULE_EXPRESSIONS]) + ret = nft_rule_parse_expr(tb[NFTA_RULE_EXPRESSIONS], r); + + r->family = nfg->nfgen_family; + + return ret; +} +EXPORT_SYMBOL(nft_rule_nlmsg_parse); + +int nft_rule_snprintf(char *buf, size_t size, struct nft_rule *r, + uint32_t type, uint32_t flags) +{ + int ret; + struct nft_rule_expr *expr; + int len = size, offset = 0; + + ret = snprintf(buf, size, "family=%u table=%s chain=%s handle=%u ", + r->family, r->table, r->chain, r->handle); + SNPRINTF_BUFFER_SIZE(ret, size, len, offset); + + list_for_each_entry(expr, &r->expr_list, head) { + ret = snprintf(buf+offset, len, "%s ", expr->ops->name); + SNPRINTF_BUFFER_SIZE(ret, size, len, offset); + + ret = expr->ops->snprintf(buf+offset, len, expr); + SNPRINTF_BUFFER_SIZE(ret, size, len, offset); + } + ret = snprintf(buf+offset-1, len, "\n"); + SNPRINTF_BUFFER_SIZE(ret, size, len, offset); + + return ret; +} +EXPORT_SYMBOL(nft_rule_snprintf); + +struct nft_rule_expr_iter { + struct nft_rule *r; + struct nft_rule_expr *cur; +}; + +struct nft_rule_expr_iter *nft_rule_expr_iter_create(struct nft_rule *r) +{ + struct nft_rule_expr_iter *iter; + + iter = calloc(1, sizeof(struct nft_rule_expr_iter)); + if (iter == NULL) + return NULL; + + iter->r = r; + iter->cur = list_entry(r->expr_list.next, struct nft_rule_expr, head); + + return iter; +} +EXPORT_SYMBOL(nft_rule_expr_iter_create); + +struct nft_rule_expr *nft_rule_expr_iter_next(struct nft_rule_expr_iter *iter) +{ + struct nft_rule_expr *expr = iter->cur; + + /* get next expression, if any */ + iter->cur = list_entry(iter->cur->head.next, struct nft_rule_expr, head); + if (&iter->cur->head == iter->r->expr_list.next) + return NULL; + + return expr; +} +EXPORT_SYMBOL(nft_rule_expr_iter_next); + +void nft_rule_expr_iter_destroy(struct nft_rule_expr_iter *iter) +{ + free(iter); +} +EXPORT_SYMBOL(nft_rule_expr_iter_destroy); + +struct nft_rule_list { + struct list_head list; +}; + +struct nft_rule_list *nft_rule_list_alloc(void) +{ + struct nft_rule_list *list; + + list = calloc(1, sizeof(struct nft_rule_list)); + if (list == NULL) + return NULL; + + INIT_LIST_HEAD(&list->list); + + return list; +} +EXPORT_SYMBOL(nft_rule_list_alloc); + +void nft_rule_list_free(struct nft_rule_list *list) +{ + struct nft_rule *r, *tmp; + + list_for_each_entry_safe(r, tmp, &list->list, head) { + list_del(&r->head); + nft_rule_free(r); + } + free(list); +} +EXPORT_SYMBOL(nft_rule_list_free); + +void nft_rule_list_add(struct nft_rule *r, struct nft_rule_list *list) +{ + list_add_tail(&r->head, &list->list); +} +EXPORT_SYMBOL(nft_rule_list_add); + +struct nft_rule_list_iter { + struct nft_rule_list *list; + struct nft_rule *cur; +}; + +struct nft_rule_list_iter *nft_rule_list_iter_create(struct nft_rule_list *l) +{ + struct nft_rule_list_iter *iter; + + iter = calloc(1, sizeof(struct nft_rule_list_iter)); + if (iter == NULL) + return NULL; + + iter->list = l; + iter->cur = list_entry(l->list.next, struct nft_rule, head); + + return iter; +} +EXPORT_SYMBOL(nft_rule_list_iter_create); + +struct nft_rule *nft_rule_list_iter_cur(struct nft_rule_list_iter *iter) +{ + return iter->cur; +} +EXPORT_SYMBOL(nft_rule_list_iter_cur); + +struct nft_rule *nft_rule_list_iter_next(struct nft_rule_list_iter *iter) +{ + struct nft_rule *r = iter->cur; + + /* get next rule, if any */ + iter->cur = list_entry(iter->cur->head.next, struct nft_rule, head); + if (&iter->cur->head == iter->list->list.next) + return NULL; + + return r; +} +EXPORT_SYMBOL(nft_rule_list_iter_next); + +void nft_rule_list_iter_destroy(struct nft_rule_list_iter *iter) +{ + free(iter); +} +EXPORT_SYMBOL(nft_rule_list_iter_destroy); diff --git a/src/table.c b/src/table.c new file mode 100644 index 0000000..040bf9a --- /dev/null +++ b/src/table.c @@ -0,0 +1,212 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * 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 + */ +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct nft_table { + struct list_head head; + + char *name; + uint8_t family; + uint32_t flags; /* FIXME missing */ +}; + +struct nft_table *nft_table_alloc(void) +{ + return calloc(1, sizeof(struct nft_table)); +} +EXPORT_SYMBOL(nft_table_alloc); + +void nft_table_free(struct nft_table *t) +{ + if (t->name != NULL) + free(t->name); + + free(t); +} +EXPORT_SYMBOL(nft_table_free); + +void nft_table_attr_set(struct nft_table *t, uint16_t attr, void *data) +{ + switch(attr) { + case NFT_TABLE_ATTR_NAME: + if (t->name) + free(t->name); + + t->name = strdup(data); + break; + } +} +EXPORT_SYMBOL(nft_table_attr_set); + +const void *nft_table_attr_get(struct nft_table *t, uint16_t attr) +{ + switch(attr) { + case NFT_TABLE_ATTR_NAME: + return t->name; + } + return NULL; +} +EXPORT_SYMBOL(nft_table_attr_get); + +struct nlmsghdr * +nft_table_nlmsg_build_hdr(char *buf, uint16_t cmd, uint16_t family, + uint16_t type, uint32_t seq) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | cmd; + nlh->nlmsg_flags = NLM_F_REQUEST | type; + nlh->nlmsg_seq = seq; + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); + nfh->nfgen_family = family; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + + return nlh; +} +EXPORT_SYMBOL(nft_table_nlmsg_build_hdr); + +void nft_table_nlmsg_build_payload(struct nlmsghdr *nlh, const struct nft_table *t) +{ + if (t->name != NULL) + mnl_attr_put_strz(nlh, NFTA_TABLE_NAME, t->name); +} +EXPORT_SYMBOL(nft_table_nlmsg_build_payload); + +static int nft_table_parse_attr_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_TABLE_MAX) < 0) + return MNL_CB_OK; + + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +int nft_table_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_table *t) +{ + struct nlattr *tb[NFTA_TABLE_MAX+1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*nfg), nft_table_parse_attr_cb, tb); + if (tb[NFTA_TABLE_NAME]) + t->name = strdup(mnl_attr_get_str(tb[NFTA_TABLE_NAME])); + + t->family = nfg->nfgen_family; + + return 0; +} +EXPORT_SYMBOL(nft_table_nlmsg_parse); + +int nft_table_snprintf(char *buf, size_t size, struct nft_table *t, + uint32_t type, uint32_t flags) +{ + return snprintf(buf, size, "table=%s family=%u\n", t->name, t->family); +} +EXPORT_SYMBOL(nft_table_snprintf); + +struct nft_table_list { + struct list_head list; +}; + +struct nft_table_list *nft_table_list_alloc(void) +{ + struct nft_table_list *list; + + list = calloc(1, sizeof(struct nft_table_list)); + if (list == NULL) + return NULL; + + INIT_LIST_HEAD(&list->list); + + return list; +} +EXPORT_SYMBOL(nft_table_list_alloc); + +void nft_table_list_free(struct nft_table_list *list) +{ + struct nft_table *r, *tmp; + + list_for_each_entry_safe(r, tmp, &list->list, head) { + list_del(&r->head); + nft_table_free(r); + } + free(list); +} +EXPORT_SYMBOL(nft_table_list_free); + +void nft_table_list_add(struct nft_table *r, struct nft_table_list *list) +{ + list_add_tail(&r->head, &list->list); +} +EXPORT_SYMBOL(nft_table_list_add); + +struct nft_table_list_iter { + struct nft_table_list *list; + struct nft_table *cur; +}; + +struct nft_table_list_iter *nft_table_list_iter_create(struct nft_table_list *l) +{ + struct nft_table_list_iter *iter; + + iter = calloc(1, sizeof(struct nft_table_list_iter)); + if (iter == NULL) + return NULL; + + iter->list = l; + iter->cur = list_entry(l->list.next, struct nft_table, head); + + return iter; +} +EXPORT_SYMBOL(nft_table_list_iter_create); + +struct nft_table *nft_table_list_iter_next(struct nft_table_list_iter *iter) +{ + struct nft_table *r = iter->cur; + + /* get next table, if any */ + iter->cur = list_entry(iter->cur->head.next, struct nft_table, head); + if (&iter->cur->head == iter->list->list.next) + return NULL; + + return r; +} +EXPORT_SYMBOL(nft_table_list_iter_next); + +void nft_table_list_iter_destroy(struct nft_table_list_iter *iter) +{ + free(iter); +} +EXPORT_SYMBOL(nft_table_list_iter_destroy); -- cgit v1.2.3