diff options
Diffstat (limited to 'src/expr')
40 files changed, 7973 insertions, 0 deletions
diff --git a/src/expr/bitwise.c b/src/expr/bitwise.c new file mode 100644 index 0000000..2d27233 --- /dev/null +++ b/src/expr/bitwise.c @@ -0,0 +1,286 @@ +/* + * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> /* for memcpy */ +#include <arpa/inet.h> +#include <errno.h> +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_bitwise { + enum nft_registers sreg; + enum nft_registers dreg; + enum nft_bitwise_ops op; + unsigned int len; + union nftnl_data_reg mask; + union nftnl_data_reg xor; + union nftnl_data_reg data; +}; + +static int +nftnl_expr_bitwise_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_BITWISE_SREG: + memcpy(&bitwise->sreg, data, sizeof(bitwise->sreg)); + break; + case NFTNL_EXPR_BITWISE_DREG: + memcpy(&bitwise->dreg, data, sizeof(bitwise->dreg)); + break; + case NFTNL_EXPR_BITWISE_OP: + memcpy(&bitwise->op, data, sizeof(bitwise->op)); + break; + case NFTNL_EXPR_BITWISE_LEN: + memcpy(&bitwise->len, data, sizeof(bitwise->len)); + break; + case NFTNL_EXPR_BITWISE_MASK: + memcpy(&bitwise->mask.val, data, data_len); + bitwise->mask.len = data_len; + break; + case NFTNL_EXPR_BITWISE_XOR: + memcpy(&bitwise->xor.val, data, data_len); + bitwise->xor.len = data_len; + break; + case NFTNL_EXPR_BITWISE_DATA: + memcpy(&bitwise->data.val, data, data_len); + bitwise->data.len = data_len; + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_bitwise_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_BITWISE_SREG: + *data_len = sizeof(bitwise->sreg); + return &bitwise->sreg; + case NFTNL_EXPR_BITWISE_DREG: + *data_len = sizeof(bitwise->dreg); + return &bitwise->dreg; + case NFTNL_EXPR_BITWISE_OP: + *data_len = sizeof(bitwise->op); + return &bitwise->op; + case NFTNL_EXPR_BITWISE_LEN: + *data_len = sizeof(bitwise->len); + return &bitwise->len; + case NFTNL_EXPR_BITWISE_MASK: + *data_len = bitwise->mask.len; + return &bitwise->mask.val; + case NFTNL_EXPR_BITWISE_XOR: + *data_len = bitwise->xor.len; + return &bitwise->xor.val; + case NFTNL_EXPR_BITWISE_DATA: + *data_len = bitwise->data.len; + return &bitwise->data.val; + } + return NULL; +} + +static int nftnl_expr_bitwise_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_BITWISE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_BITWISE_SREG: + case NFTA_BITWISE_DREG: + case NFTA_BITWISE_OP: + case NFTA_BITWISE_LEN: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_BITWISE_MASK: + case NFTA_BITWISE_XOR: + case NFTA_BITWISE_DATA: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_bitwise_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_BITWISE_SREG)) + mnl_attr_put_u32(nlh, NFTA_BITWISE_SREG, htonl(bitwise->sreg)); + if (e->flags & (1 << NFTNL_EXPR_BITWISE_DREG)) + mnl_attr_put_u32(nlh, NFTA_BITWISE_DREG, htonl(bitwise->dreg)); + if (e->flags & (1 << NFTNL_EXPR_BITWISE_OP)) + mnl_attr_put_u32(nlh, NFTA_BITWISE_OP, htonl(bitwise->op)); + if (e->flags & (1 << NFTNL_EXPR_BITWISE_LEN)) + mnl_attr_put_u32(nlh, NFTA_BITWISE_LEN, htonl(bitwise->len)); + if (e->flags & (1 << NFTNL_EXPR_BITWISE_MASK)) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_BITWISE_MASK); + mnl_attr_put(nlh, NFTA_DATA_VALUE, bitwise->mask.len, + bitwise->mask.val); + mnl_attr_nest_end(nlh, nest); + } + if (e->flags & (1 << NFTNL_EXPR_BITWISE_XOR)) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_BITWISE_XOR); + mnl_attr_put(nlh, NFTA_DATA_VALUE, bitwise->xor.len, + bitwise->xor.val); + mnl_attr_nest_end(nlh, nest); + } + if (e->flags & (1 << NFTNL_EXPR_BITWISE_DATA)) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_BITWISE_DATA); + mnl_attr_put(nlh, NFTA_DATA_VALUE, bitwise->data.len, + bitwise->data.val); + mnl_attr_nest_end(nlh, nest); + } +} + +static int +nftnl_expr_bitwise_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e); + struct nlattr *tb[NFTA_BITWISE_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_bitwise_cb, tb) < 0) + return -1; + + if (tb[NFTA_BITWISE_SREG]) { + bitwise->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_SREG])); + e->flags |= (1 << NFTNL_EXPR_BITWISE_SREG); + } + if (tb[NFTA_BITWISE_DREG]) { + bitwise->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_DREG])); + e->flags |= (1 << NFTNL_EXPR_BITWISE_DREG); + } + if (tb[NFTA_BITWISE_OP]) { + bitwise->op = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_OP])); + e->flags |= (1 << NFTNL_EXPR_BITWISE_OP); + } + if (tb[NFTA_BITWISE_LEN]) { + bitwise->len = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_LEN])); + e->flags |= (1 << NFTNL_EXPR_BITWISE_LEN); + } + if (tb[NFTA_BITWISE_MASK]) { + ret = nftnl_parse_data(&bitwise->mask, tb[NFTA_BITWISE_MASK], NULL); + e->flags |= (1 << NFTA_BITWISE_MASK); + } + if (tb[NFTA_BITWISE_XOR]) { + ret = nftnl_parse_data(&bitwise->xor, tb[NFTA_BITWISE_XOR], NULL); + e->flags |= (1 << NFTA_BITWISE_XOR); + } + if (tb[NFTA_BITWISE_DATA]) { + ret = nftnl_parse_data(&bitwise->data, tb[NFTA_BITWISE_DATA], NULL); + e->flags |= (1 << NFTNL_EXPR_BITWISE_DATA); + } + + return ret; +} + +static int +nftnl_expr_bitwise_snprintf_bool(char *buf, size_t remain, + const struct nftnl_expr_bitwise *bitwise) +{ + int offset = 0, ret; + + ret = snprintf(buf, remain, "reg %u = ( reg %u & ", + bitwise->dreg, bitwise->sreg); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->mask, + 0, DATA_VALUE); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = snprintf(buf + offset, remain, ") ^ "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->xor, + 0, DATA_VALUE); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + return offset; +} + +static int +nftnl_expr_bitwise_snprintf_shift(char *buf, size_t remain, const char *op, + const struct nftnl_expr_bitwise *bitwise) +{ int offset = 0, ret; + + ret = snprintf(buf, remain, "reg %u = ( reg %u %s ", + bitwise->dreg, bitwise->sreg, op); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->data, + 0, DATA_VALUE); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = snprintf(buf + offset, remain, ") "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + return offset; +} + +static int +nftnl_expr_bitwise_snprintf(char *buf, size_t size, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e); + int err = -1; + + switch (bitwise->op) { + case NFT_BITWISE_BOOL: + err = nftnl_expr_bitwise_snprintf_bool(buf, size, bitwise); + break; + case NFT_BITWISE_LSHIFT: + err = nftnl_expr_bitwise_snprintf_shift(buf, size, "<<", bitwise); + break; + case NFT_BITWISE_RSHIFT: + err = nftnl_expr_bitwise_snprintf_shift(buf, size, ">>", bitwise); + break; + } + + return err; +} + +struct expr_ops expr_ops_bitwise = { + .name = "bitwise", + .alloc_len = sizeof(struct nftnl_expr_bitwise), + .max_attr = NFTA_BITWISE_MAX, + .set = nftnl_expr_bitwise_set, + .get = nftnl_expr_bitwise_get, + .parse = nftnl_expr_bitwise_parse, + .build = nftnl_expr_bitwise_build, + .output = nftnl_expr_bitwise_snprintf, +}; diff --git a/src/expr/byteorder.c b/src/expr/byteorder.c new file mode 100644 index 0000000..89ed0a8 --- /dev/null +++ b/src/expr/byteorder.c @@ -0,0 +1,224 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> /* for memcpy */ +#include <arpa/inet.h> +#include <errno.h> +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_byteorder { + enum nft_registers sreg; + enum nft_registers dreg; + enum nft_byteorder_ops op; + unsigned int len; + unsigned int size; +}; + +static int +nftnl_expr_byteorder_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_BYTEORDER_SREG: + memcpy(&byteorder->sreg, data, sizeof(byteorder->sreg)); + break; + case NFTNL_EXPR_BYTEORDER_DREG: + memcpy(&byteorder->dreg, data, sizeof(byteorder->dreg)); + break; + case NFTNL_EXPR_BYTEORDER_OP: + memcpy(&byteorder->op, data, sizeof(byteorder->op)); + break; + case NFTNL_EXPR_BYTEORDER_LEN: + memcpy(&byteorder->len, data, sizeof(byteorder->len)); + break; + case NFTNL_EXPR_BYTEORDER_SIZE: + memcpy(&byteorder->size, data, sizeof(byteorder->size)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_byteorder_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_BYTEORDER_SREG: + *data_len = sizeof(byteorder->sreg); + return &byteorder->sreg; + case NFTNL_EXPR_BYTEORDER_DREG: + *data_len = sizeof(byteorder->dreg); + return &byteorder->dreg; + case NFTNL_EXPR_BYTEORDER_OP: + *data_len = sizeof(byteorder->op); + return &byteorder->op; + case NFTNL_EXPR_BYTEORDER_LEN: + *data_len = sizeof(byteorder->len); + return &byteorder->len; + case NFTNL_EXPR_BYTEORDER_SIZE: + *data_len = sizeof(byteorder->size); + return &byteorder->size; + } + return NULL; +} + +static int nftnl_expr_byteorder_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_BYTEORDER_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_BYTEORDER_SREG: + case NFTA_BYTEORDER_DREG: + case NFTA_BYTEORDER_OP: + case NFTA_BYTEORDER_LEN: + case NFTA_BYTEORDER_SIZE: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_byteorder_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_SREG)) { + mnl_attr_put_u32(nlh, NFTA_BYTEORDER_SREG, + htonl(byteorder->sreg)); + } + if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_DREG)) { + mnl_attr_put_u32(nlh, NFTA_BYTEORDER_DREG, + htonl(byteorder->dreg)); + } + if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_OP)) { + mnl_attr_put_u32(nlh, NFTA_BYTEORDER_OP, + htonl(byteorder->op)); + } + if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_LEN)) { + mnl_attr_put_u32(nlh, NFTA_BYTEORDER_LEN, + htonl(byteorder->len)); + } + if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_SIZE)) { + mnl_attr_put_u32(nlh, NFTA_BYTEORDER_SIZE, + htonl(byteorder->size)); + } +} + +static int +nftnl_expr_byteorder_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e); + struct nlattr *tb[NFTA_BYTEORDER_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_byteorder_cb, tb) < 0) + return -1; + + if (tb[NFTA_BYTEORDER_SREG]) { + byteorder->sreg = + ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_SREG])); + e->flags |= (1 << NFTNL_EXPR_BYTEORDER_SREG); + } + if (tb[NFTA_BYTEORDER_DREG]) { + byteorder->dreg = + ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_DREG])); + e->flags |= (1 << NFTNL_EXPR_BYTEORDER_DREG); + } + if (tb[NFTA_BYTEORDER_OP]) { + byteorder->op = + ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_OP])); + e->flags |= (1 << NFTNL_EXPR_BYTEORDER_OP); + } + if (tb[NFTA_BYTEORDER_LEN]) { + byteorder->len = + ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_LEN])); + e->flags |= (1 << NFTNL_EXPR_BYTEORDER_LEN); + } + if (tb[NFTA_BYTEORDER_SIZE]) { + byteorder->size = + ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_SIZE])); + e->flags |= (1 << NFTNL_EXPR_BYTEORDER_SIZE); + } + + return ret; +} + +static const char *expr_byteorder_str[] = { + [NFT_BYTEORDER_HTON] = "hton", + [NFT_BYTEORDER_NTOH] = "ntoh", +}; + +static const char *bo2str(uint32_t type) +{ + if (type > NFT_BYTEORDER_HTON) + return "unknown"; + + return expr_byteorder_str[type]; +} + +static inline int nftnl_str2ntoh(const char *op) +{ + if (strcmp(op, "ntoh") == 0) + return NFT_BYTEORDER_NTOH; + else if (strcmp(op, "hton") == 0) + return NFT_BYTEORDER_HTON; + else { + errno = EINVAL; + return -1; + } +} + +static int +nftnl_expr_byteorder_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e); + int offset = 0, ret; + + ret = snprintf(buf, remain, "reg %u = %s(reg %u, %u, %u) ", + byteorder->dreg, bo2str(byteorder->op), + byteorder->sreg, byteorder->size, byteorder->len); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + return offset; +} + +struct expr_ops expr_ops_byteorder = { + .name = "byteorder", + .alloc_len = sizeof(struct nftnl_expr_byteorder), + .max_attr = NFTA_BYTEORDER_MAX, + .set = nftnl_expr_byteorder_set, + .get = nftnl_expr_byteorder_get, + .parse = nftnl_expr_byteorder_parse, + .build = nftnl_expr_byteorder_build, + .output = nftnl_expr_byteorder_snprintf, +}; diff --git a/src/expr/cmp.c b/src/expr/cmp.c new file mode 100644 index 0000000..f9d15bb --- /dev/null +++ b/src/expr/cmp.c @@ -0,0 +1,206 @@ +/* + * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_cmp { + union nftnl_data_reg data; + enum nft_registers sreg; + enum nft_cmp_ops op; +}; + +static int +nftnl_expr_cmp_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_cmp *cmp = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_CMP_SREG: + memcpy(&cmp->sreg, data, sizeof(cmp->sreg)); + break; + case NFTNL_EXPR_CMP_OP: + memcpy(&cmp->op, data, sizeof(cmp->op)); + break; + case NFTNL_EXPR_CMP_DATA: + memcpy(&cmp->data.val, data, data_len); + cmp->data.len = data_len; + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_cmp_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_cmp *cmp = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_CMP_SREG: + *data_len = sizeof(cmp->sreg); + return &cmp->sreg; + case NFTNL_EXPR_CMP_OP: + *data_len = sizeof(cmp->op); + return &cmp->op; + case NFTNL_EXPR_CMP_DATA: + *data_len = cmp->data.len; + return &cmp->data.val; + } + return NULL; +} + +static int nftnl_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) + abi_breakage(); + break; + case NFTA_CMP_DATA: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_cmp_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_cmp *cmp = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_CMP_SREG)) + mnl_attr_put_u32(nlh, NFTA_CMP_SREG, htonl(cmp->sreg)); + if (e->flags & (1 << NFTNL_EXPR_CMP_OP)) + mnl_attr_put_u32(nlh, NFTA_CMP_OP, htonl(cmp->op)); + if (e->flags & (1 << NFTNL_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 +nftnl_expr_cmp_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_cmp *cmp = nftnl_expr_data(e); + struct nlattr *tb[NFTA_CMP_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_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 = nftnl_parse_data(&cmp->data, tb[NFTA_CMP_DATA], NULL); + e->flags |= (1 << NFTA_CMP_DATA); + } + + return ret; +} + +static const 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 const char *cmp2str(uint32_t op) +{ + if (op > NFT_CMP_GTE) + return "unknown"; + + return expr_cmp_str[op]; +} + +static inline int nftnl_str2cmp(const char *op) +{ + if (strcmp(op, "eq") == 0) + return NFT_CMP_EQ; + else if (strcmp(op, "neq") == 0) + return NFT_CMP_NEQ; + else if (strcmp(op, "lt") == 0) + return NFT_CMP_LT; + else if (strcmp(op, "lte") == 0) + return NFT_CMP_LTE; + else if (strcmp(op, "gt") == 0) + return NFT_CMP_GT; + else if (strcmp(op, "gte") == 0) + return NFT_CMP_GTE; + else { + errno = EINVAL; + return -1; + } +} + +static int +nftnl_expr_cmp_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_cmp *cmp = nftnl_expr_data(e); + int offset = 0, ret; + + ret = snprintf(buf, remain, "%s reg %u ", + cmp2str(cmp->op), cmp->sreg); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_data_reg_snprintf(buf + offset, remain, &cmp->data, + 0, DATA_VALUE); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + return offset; +} + +struct expr_ops expr_ops_cmp = { + .name = "cmp", + .alloc_len = sizeof(struct nftnl_expr_cmp), + .max_attr = NFTA_CMP_MAX, + .set = nftnl_expr_cmp_set, + .get = nftnl_expr_cmp_get, + .parse = nftnl_expr_cmp_parse, + .build = nftnl_expr_cmp_build, + .output = nftnl_expr_cmp_snprintf, +}; diff --git a/src/expr/connlimit.c b/src/expr/connlimit.c new file mode 100644 index 0000000..549417b --- /dev/null +++ b/src/expr/connlimit.c @@ -0,0 +1,139 @@ +/* + * (C) 2018 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <stdio.h> +#include <stdint.h> +#include <arpa/inet.h> +#include <errno.h> +#include <inttypes.h> + +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_connlimit { + uint32_t count; + uint32_t flags; +}; + +static int +nftnl_expr_connlimit_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_CONNLIMIT_COUNT: + memcpy(&connlimit->count, data, sizeof(connlimit->count)); + break; + case NFTNL_EXPR_CONNLIMIT_FLAGS: + memcpy(&connlimit->flags, data, sizeof(connlimit->flags)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_connlimit_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_CONNLIMIT_COUNT: + *data_len = sizeof(connlimit->count); + return &connlimit->count; + case NFTNL_EXPR_CONNLIMIT_FLAGS: + *data_len = sizeof(connlimit->flags); + return &connlimit->flags; + } + return NULL; +} + +static int nftnl_expr_connlimit_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_CONNLIMIT_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_CONNLIMIT_COUNT: + case NFTA_CONNLIMIT_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_connlimit_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_CONNLIMIT_COUNT)) + mnl_attr_put_u32(nlh, NFTA_CONNLIMIT_COUNT, + htonl(connlimit->count)); + if (e->flags & (1 << NFTNL_EXPR_CONNLIMIT_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_CONNLIMIT_FLAGS, + htonl(connlimit->flags)); +} + +static int +nftnl_expr_connlimit_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e); + struct nlattr *tb[NFTA_CONNLIMIT_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_connlimit_cb, tb) < 0) + return -1; + + if (tb[NFTA_CONNLIMIT_COUNT]) { + connlimit->count = + ntohl(mnl_attr_get_u32(tb[NFTA_CONNLIMIT_COUNT])); + e->flags |= (1 << NFTNL_EXPR_CONNLIMIT_COUNT); + } + if (tb[NFTA_CONNLIMIT_FLAGS]) { + connlimit->flags = + ntohl(mnl_attr_get_u32(tb[NFTA_CONNLIMIT_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_CONNLIMIT_FLAGS); + } + + return 0; +} + +static int nftnl_expr_connlimit_snprintf(char *buf, size_t len, + uint32_t flags, + const struct nftnl_expr *e) +{ + struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e); + + return snprintf(buf, len, "count %u flags %x ", + connlimit->count, connlimit->flags); +} + +struct expr_ops expr_ops_connlimit = { + .name = "connlimit", + .alloc_len = sizeof(struct nftnl_expr_connlimit), + .max_attr = NFTA_CONNLIMIT_MAX, + .set = nftnl_expr_connlimit_set, + .get = nftnl_expr_connlimit_get, + .parse = nftnl_expr_connlimit_parse, + .build = nftnl_expr_connlimit_build, + .output = nftnl_expr_connlimit_snprintf, +}; diff --git a/src/expr/counter.c b/src/expr/counter.c new file mode 100644 index 0000000..d139a5f --- /dev/null +++ b/src/expr/counter.c @@ -0,0 +1,137 @@ +/* + * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 <stdio.h> +#include <stdint.h> +#include <arpa/inet.h> +#include <errno.h> +#include <inttypes.h> + +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_counter { + uint64_t pkts; + uint64_t bytes; +}; + +static int +nftnl_expr_counter_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_counter *ctr = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_CTR_BYTES: + memcpy(&ctr->bytes, data, sizeof(ctr->bytes)); + break; + case NFTNL_EXPR_CTR_PACKETS: + memcpy(&ctr->pkts, data, sizeof(ctr->pkts)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_counter_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_counter *ctr = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_CTR_BYTES: + *data_len = sizeof(ctr->bytes); + return &ctr->bytes; + case NFTNL_EXPR_CTR_PACKETS: + *data_len = sizeof(ctr->pkts); + return &ctr->pkts; + } + return NULL; +} + +static int nftnl_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) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_counter_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_counter *ctr = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_CTR_BYTES)) + mnl_attr_put_u64(nlh, NFTA_COUNTER_BYTES, htobe64(ctr->bytes)); + if (e->flags & (1 << NFTNL_EXPR_CTR_PACKETS)) + mnl_attr_put_u64(nlh, NFTA_COUNTER_PACKETS, htobe64(ctr->pkts)); +} + +static int +nftnl_expr_counter_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_counter *ctr = nftnl_expr_data(e); + struct nlattr *tb[NFTA_COUNTER_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_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 << NFTNL_EXPR_CTR_BYTES); + } + if (tb[NFTA_COUNTER_PACKETS]) { + ctr->pkts = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_PACKETS])); + e->flags |= (1 << NFTNL_EXPR_CTR_PACKETS); + } + + return 0; +} + +static int nftnl_expr_counter_snprintf(char *buf, size_t len, + uint32_t flags, + const struct nftnl_expr *e) +{ + struct nftnl_expr_counter *ctr = nftnl_expr_data(e); + + return snprintf(buf, len, "pkts %"PRIu64" bytes %"PRIu64" ", + ctr->pkts, ctr->bytes); +} + +struct expr_ops expr_ops_counter = { + .name = "counter", + .alloc_len = sizeof(struct nftnl_expr_counter), + .max_attr = NFTA_COUNTER_MAX, + .set = nftnl_expr_counter_set, + .get = nftnl_expr_counter_get, + .parse = nftnl_expr_counter_parse, + .build = nftnl_expr_counter_build, + .output = nftnl_expr_counter_snprintf, +}; diff --git a/src/expr/ct.c b/src/expr/ct.c new file mode 100644 index 0000000..f4a2aea --- /dev/null +++ b/src/expr/ct.c @@ -0,0 +1,262 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 <stdio.h> +#include <string.h> +#include <stdint.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_ct { + enum nft_ct_keys key; + enum nft_registers dreg; + enum nft_registers sreg; + uint8_t dir; +}; + +#define IP_CT_DIR_ORIGINAL 0 +#define IP_CT_DIR_REPLY 1 + +static int +nftnl_expr_ct_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_ct *ct = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_CT_KEY: + memcpy(&ct->key, data, sizeof(ct->key)); + break; + case NFTNL_EXPR_CT_DIR: + memcpy(&ct->dir, data, sizeof(ct->dir)); + break; + case NFTNL_EXPR_CT_DREG: + memcpy(&ct->dreg, data, sizeof(ct->dreg)); + break; + case NFTNL_EXPR_CT_SREG: + memcpy(&ct->sreg, data, sizeof(ct->sreg)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_ct_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_ct *ct = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_CT_KEY: + *data_len = sizeof(ct->key); + return &ct->key; + case NFTNL_EXPR_CT_DIR: + *data_len = sizeof(ct->dir); + return &ct->dir; + case NFTNL_EXPR_CT_DREG: + *data_len = sizeof(ct->dreg); + return &ct->dreg; + case NFTNL_EXPR_CT_SREG: + *data_len = sizeof(ct->sreg); + return &ct->sreg; + } + return NULL; +} + +static int nftnl_expr_ct_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_CT_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_CT_KEY: + case NFTA_CT_DREG: + case NFTA_CT_SREG: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_CT_DIRECTION: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_ct_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_ct *ct = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_CT_KEY)) + mnl_attr_put_u32(nlh, NFTA_CT_KEY, htonl(ct->key)); + if (e->flags & (1 << NFTNL_EXPR_CT_DREG)) + mnl_attr_put_u32(nlh, NFTA_CT_DREG, htonl(ct->dreg)); + if (e->flags & (1 << NFTNL_EXPR_CT_DIR)) + mnl_attr_put_u8(nlh, NFTA_CT_DIRECTION, ct->dir); + if (e->flags & (1 << NFTNL_EXPR_CT_SREG)) + mnl_attr_put_u32(nlh, NFTA_CT_SREG, htonl(ct->sreg)); +} + +static int +nftnl_expr_ct_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_ct *ct = nftnl_expr_data(e); + struct nlattr *tb[NFTA_CT_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_ct_cb, tb) < 0) + return -1; + + if (tb[NFTA_CT_KEY]) { + ct->key = ntohl(mnl_attr_get_u32(tb[NFTA_CT_KEY])); + e->flags |= (1 << NFTNL_EXPR_CT_KEY); + } + if (tb[NFTA_CT_DREG]) { + ct->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_CT_DREG])); + e->flags |= (1 << NFTNL_EXPR_CT_DREG); + } + if (tb[NFTA_CT_SREG]) { + ct->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_CT_SREG])); + e->flags |= (1 << NFTNL_EXPR_CT_SREG); + } + if (tb[NFTA_CT_DIRECTION]) { + ct->dir = mnl_attr_get_u8(tb[NFTA_CT_DIRECTION]); + e->flags |= (1 << NFTNL_EXPR_CT_DIR); + } + + return 0; +} + +static const char *ctkey2str_array[NFT_CT_MAX + 1] = { + [NFT_CT_STATE] = "state", + [NFT_CT_DIRECTION] = "direction", + [NFT_CT_STATUS] = "status", + [NFT_CT_MARK] = "mark", + [NFT_CT_SECMARK] = "secmark", + [NFT_CT_EXPIRATION] = "expiration", + [NFT_CT_HELPER] = "helper", + [NFT_CT_L3PROTOCOL] = "l3protocol", + [NFT_CT_PROTOCOL] = "protocol", + [NFT_CT_SRC] = "src", + [NFT_CT_DST] = "dst", + [NFT_CT_PROTO_SRC] = "proto_src", + [NFT_CT_PROTO_DST] = "proto_dst", + [NFT_CT_LABELS] = "label", + [NFT_CT_PKTS] = "packets", + [NFT_CT_BYTES] = "bytes", + [NFT_CT_AVGPKT] = "avgpkt", + [NFT_CT_ZONE] = "zone", + [NFT_CT_EVENTMASK] = "event", + [NFT_CT_SRC_IP] = "src_ip", + [NFT_CT_DST_IP] = "dst_ip", + [NFT_CT_SRC_IP6] = "src_ip6", + [NFT_CT_DST_IP6] = "dst_ip6", + [NFT_CT_ID] = "id", +}; + +static const char *ctkey2str(uint32_t ctkey) +{ + if (ctkey >= NFT_CT_MAX) + return "unknown"; + + return ctkey2str_array[ctkey]; +} + +static inline int str2ctkey(const char *ctkey) +{ + int i; + + for (i = 0; i < NFT_CT_MAX; i++) { + if (strcmp(ctkey2str_array[i], ctkey) == 0) + return i; + } + + return -1; +} + +static const char *ctdir2str(uint8_t ctdir) +{ + switch (ctdir) { + case IP_CT_DIR_ORIGINAL: + return "original"; + case IP_CT_DIR_REPLY: + return "reply"; + default: + return "unknown"; + } +} + +static inline int str2ctdir(const char *str, uint8_t *ctdir) +{ + if (strcmp(str, "original") == 0) { + *ctdir = IP_CT_DIR_ORIGINAL; + return 0; + } + + if (strcmp(str, "reply") == 0) { + *ctdir = IP_CT_DIR_REPLY; + return 0; + } + + return -1; +} + +static int +nftnl_expr_ct_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_ct *ct = nftnl_expr_data(e); + int ret, offset = 0; + + if (e->flags & (1 << NFTNL_EXPR_CT_SREG)) { + ret = snprintf(buf, remain, "set %s with reg %u ", + ctkey2str(ct->key), ct->sreg); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_CT_DREG)) { + ret = snprintf(buf, remain, "load %s => reg %u ", + ctkey2str(ct->key), ct->dreg); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (nftnl_expr_is_set(e, NFTNL_EXPR_CT_DIR)) { + ret = snprintf(buf + offset, remain, ", dir %s ", + ctdir2str(ct->dir)); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +struct expr_ops expr_ops_ct = { + .name = "ct", + .alloc_len = sizeof(struct nftnl_expr_ct), + .max_attr = NFTA_CT_MAX, + .set = nftnl_expr_ct_set, + .get = nftnl_expr_ct_get, + .parse = nftnl_expr_ct_parse, + .build = nftnl_expr_ct_build, + .output = nftnl_expr_ct_snprintf, +}; diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c new file mode 100644 index 0000000..2633a77 --- /dev/null +++ b/src/expr/data_reg.c @@ -0,0 +1,219 @@ +/* + * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 <stdio.h> +#include <stdint.h> +#include <string.h> +#include <limits.h> +#include <arpa/inet.h> +#include <errno.h> +#include <netinet/in.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> +#include "internal.h" + +static int +nftnl_data_reg_value_snprintf_default(char *buf, size_t remain, + const union nftnl_data_reg *reg, + uint32_t flags) +{ + const char *pfx = flags & DATA_F_NOPFX ? "" : "0x"; + int offset = 0, ret, i; + + + + for (i = 0; i < div_round_up(reg->len, sizeof(uint32_t)); i++) { + ret = snprintf(buf + offset, remain, + "%s%.8x ", pfx, reg->val[i]); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +static int +nftnl_data_reg_verdict_snprintf_def(char *buf, size_t size, + const union nftnl_data_reg *reg, + uint32_t flags) +{ + int remain = size, offset = 0, ret = 0; + + ret = snprintf(buf, size, "%s ", nftnl_verdict2str(reg->verdict)); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + if (reg->chain != NULL) { + ret = snprintf(buf + offset, remain, "-> %s ", reg->chain); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +int nftnl_data_reg_snprintf(char *buf, size_t size, + const union nftnl_data_reg *reg, + uint32_t flags, int reg_type) +{ + switch(reg_type) { + case DATA_VALUE: + return nftnl_data_reg_value_snprintf_default(buf, size, + reg, flags); + case DATA_VERDICT: + case DATA_CHAIN: + return nftnl_data_reg_verdict_snprintf_def(buf, size, + reg, flags); + default: + return -1; + } +} + +static int nftnl_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) + abi_breakage(); + break; + case NFTA_DATA_VERDICT: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + abi_breakage(); + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int nftnl_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: + case NFTA_VERDICT_CHAIN_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_VERDICT_CHAIN: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + abi_breakage(); + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nftnl_parse_verdict(union nftnl_data_reg *data, const struct nlattr *attr, int *type) +{ + struct nlattr *tb[NFTA_VERDICT_MAX+1]; + + if (mnl_attr_parse_nested(attr, nftnl_verdict_parse_cb, tb) < 0) + 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 (!data->chain) + return -1; + + if (type) + *type = DATA_CHAIN; + break; + default: + return -1; + } + + return 0; +} + +static int +__nftnl_parse_data(union nftnl_data_reg *data, const struct nlattr *attr) +{ + void *orig = mnl_attr_get_payload(attr); + uint32_t data_len = mnl_attr_get_payload_len(attr); + + if (data_len == 0) + return -1; + + if (data_len > sizeof(data->val)) + return -1; + + memcpy(data->val, orig, data_len); + data->len = data_len; + + return 0; +} + +int nftnl_parse_data(union nftnl_data_reg *data, struct nlattr *attr, int *type) +{ + struct nlattr *tb[NFTA_DATA_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_data_parse_cb, tb) < 0) + return -1; + + if (tb[NFTA_DATA_VALUE]) { + if (type) + *type = DATA_VALUE; + + ret = __nftnl_parse_data(data, tb[NFTA_DATA_VALUE]); + if (ret < 0) + return ret; + } + if (tb[NFTA_DATA_VERDICT]) + ret = nftnl_parse_verdict(data, tb[NFTA_DATA_VERDICT], type); + + return ret; +} + +void nftnl_free_verdict(const union nftnl_data_reg *data) +{ + switch(data->verdict) { + case NFT_JUMP: + case NFT_GOTO: + xfree(data->chain); + break; + default: + break; + } +} diff --git a/src/expr/dup.c b/src/expr/dup.c new file mode 100644 index 0000000..a239ff3 --- /dev/null +++ b/src/expr/dup.c @@ -0,0 +1,142 @@ +/* + * (C) 2015 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include "internal.h" +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> +#include "expr_ops.h" +#include "data_reg.h" + +struct nftnl_expr_dup { + enum nft_registers sreg_addr; + enum nft_registers sreg_dev; +}; + +static int nftnl_expr_dup_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_dup *dup = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_DUP_SREG_ADDR: + memcpy(&dup->sreg_addr, data, sizeof(dup->sreg_addr)); + break; + case NFTNL_EXPR_DUP_SREG_DEV: + memcpy(&dup->sreg_dev, data, sizeof(dup->sreg_dev)); + break; + default: + return -1; + } + return 0; +} + +static const void *nftnl_expr_dup_get(const struct nftnl_expr *e, + uint16_t type, uint32_t *data_len) +{ + struct nftnl_expr_dup *dup = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_DUP_SREG_ADDR: + *data_len = sizeof(dup->sreg_addr); + return &dup->sreg_addr; + case NFTNL_EXPR_DUP_SREG_DEV: + *data_len = sizeof(dup->sreg_dev); + return &dup->sreg_dev; + } + return NULL; +} + +static int nftnl_expr_dup_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_DUP_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_DUP_SREG_ADDR: + case NFTA_DUP_SREG_DEV: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void nftnl_expr_dup_build(struct nlmsghdr *nlh, + const struct nftnl_expr *e) +{ + struct nftnl_expr_dup *dup = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_ADDR)) + mnl_attr_put_u32(nlh, NFTA_DUP_SREG_ADDR, htonl(dup->sreg_addr)); + if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_DEV)) + mnl_attr_put_u32(nlh, NFTA_DUP_SREG_DEV, htonl(dup->sreg_dev)); +} + +static int nftnl_expr_dup_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_dup *dup = nftnl_expr_data(e); + struct nlattr *tb[NFTA_DUP_MAX + 1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_dup_cb, tb) < 0) + return -1; + + if (tb[NFTA_DUP_SREG_ADDR]) { + dup->sreg_addr = ntohl(mnl_attr_get_u32(tb[NFTA_DUP_SREG_ADDR])); + e->flags |= (1 << NFTNL_EXPR_DUP_SREG_ADDR); + } + if (tb[NFTA_DUP_SREG_DEV]) { + dup->sreg_dev = ntohl(mnl_attr_get_u32(tb[NFTA_DUP_SREG_DEV])); + e->flags |= (1 << NFTNL_EXPR_DUP_SREG_DEV); + } + + return ret; +} + +static int nftnl_expr_dup_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_dup *dup = nftnl_expr_data(e); + int offset = 0, ret; + + if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_ADDR)) { + ret = snprintf(buf + offset, remain, "sreg_addr %u ", dup->sreg_addr); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_DEV)) { + ret = snprintf(buf + offset, remain, "sreg_dev %u ", dup->sreg_dev); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +struct expr_ops expr_ops_dup = { + .name = "dup", + .alloc_len = sizeof(struct nftnl_expr_dup), + .max_attr = NFTA_DUP_MAX, + .set = nftnl_expr_dup_set, + .get = nftnl_expr_dup_get, + .parse = nftnl_expr_dup_parse, + .build = nftnl_expr_dup_build, + .output = nftnl_expr_dup_snprintf, +}; diff --git a/src/expr/dynset.c b/src/expr/dynset.c new file mode 100644 index 0000000..5bcf1c6 --- /dev/null +++ b/src/expr/dynset.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2014, 2015 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <arpa/inet.h> +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/rule.h> +#include <libnftnl/expr.h> +#include "data_reg.h" +#include "expr_ops.h" + +struct nftnl_expr_dynset { + enum nft_registers sreg_key; + enum nft_registers sreg_data; + enum nft_dynset_ops op; + uint64_t timeout; + struct list_head expr_list; + char *set_name; + uint32_t set_id; + uint32_t dynset_flags; +}; + +static int +nftnl_expr_dynset_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + struct nftnl_expr *expr, *next; + + switch (type) { + case NFTNL_EXPR_DYNSET_SREG_KEY: + memcpy(&dynset->sreg_key, data, sizeof(dynset->sreg_key)); + break; + case NFTNL_EXPR_DYNSET_SREG_DATA: + memcpy(&dynset->sreg_data, data, sizeof(dynset->sreg_data)); + break; + case NFTNL_EXPR_DYNSET_OP: + memcpy(&dynset->op, data, sizeof(dynset->op)); + break; + case NFTNL_EXPR_DYNSET_TIMEOUT: + memcpy(&dynset->timeout, data, sizeof(dynset->timeout)); + break; + case NFTNL_EXPR_DYNSET_SET_NAME: + dynset->set_name = strdup((const char *)data); + if (!dynset->set_name) + return -1; + break; + case NFTNL_EXPR_DYNSET_SET_ID: + memcpy(&dynset->set_id, data, sizeof(dynset->set_id)); + break; + case NFTNL_EXPR_DYNSET_EXPR: + list_for_each_entry_safe(expr, next, &dynset->expr_list, head) + nftnl_expr_free(expr); + + expr = (void *)data; + list_add(&expr->head, &dynset->expr_list); + break; + case NFTNL_EXPR_DYNSET_FLAGS: + memcpy(&dynset->dynset_flags, data, sizeof(dynset->dynset_flags)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_dynset_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + struct nftnl_expr *expr; + + switch (type) { + case NFTNL_EXPR_DYNSET_SREG_KEY: + *data_len = sizeof(dynset->sreg_key); + return &dynset->sreg_key; + case NFTNL_EXPR_DYNSET_SREG_DATA: + *data_len = sizeof(dynset->sreg_data); + return &dynset->sreg_data; + case NFTNL_EXPR_DYNSET_OP: + *data_len = sizeof(dynset->op); + return &dynset->op; + case NFTNL_EXPR_DYNSET_TIMEOUT: + *data_len = sizeof(dynset->timeout); + return &dynset->timeout; + case NFTNL_EXPR_DYNSET_SET_NAME: + *data_len = strlen(dynset->set_name) + 1; + return dynset->set_name; + case NFTNL_EXPR_DYNSET_SET_ID: + *data_len = sizeof(dynset->set_id); + return &dynset->set_id; + case NFTNL_EXPR_DYNSET_EXPR: + list_for_each_entry(expr, &dynset->expr_list, head) + break; + return expr; + case NFTNL_EXPR_DYNSET_FLAGS: + *data_len = sizeof(dynset->dynset_flags); + return &dynset->dynset_flags; + } + return NULL; +} + +static int nftnl_expr_dynset_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_SET_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_DYNSET_SREG_KEY: + case NFTA_DYNSET_SREG_DATA: + case NFTA_DYNSET_SET_ID: + case NFTA_DYNSET_OP: + case NFTA_DYNSET_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_DYNSET_TIMEOUT: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + abi_breakage(); + break; + case NFTA_DYNSET_SET_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + abi_breakage(); + break; + case NFTA_DYNSET_EXPR: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_dynset_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + struct nlattr *nest; + int num_exprs = 0; + + if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_KEY)) + mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_KEY, htonl(dynset->sreg_key)); + if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA)) + mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_DATA, htonl(dynset->sreg_data)); + if (e->flags & (1 << NFTNL_EXPR_DYNSET_OP)) + mnl_attr_put_u32(nlh, NFTA_DYNSET_OP, htonl(dynset->op)); + if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT)) + mnl_attr_put_u64(nlh, NFTA_DYNSET_TIMEOUT, htobe64(dynset->timeout)); + if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_NAME)) + mnl_attr_put_strz(nlh, NFTA_DYNSET_SET_NAME, dynset->set_name); + if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_ID)) + mnl_attr_put_u32(nlh, NFTA_DYNSET_SET_ID, htonl(dynset->set_id)); + if (!list_empty(&dynset->expr_list)) { + struct nftnl_expr *expr; + + list_for_each_entry(expr, &dynset->expr_list, head) + num_exprs++; + + if (num_exprs == 1) { + nest = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPR); + list_for_each_entry(expr, &dynset->expr_list, head) + nftnl_expr_build_payload(nlh, expr); + mnl_attr_nest_end(nlh, nest); + } else if (num_exprs > 1) { + struct nlattr *nest1, *nest2; + + nest1 = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPRESSIONS); + list_for_each_entry(expr, &dynset->expr_list, head) { + nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM); + nftnl_expr_build_payload(nlh, expr); + mnl_attr_nest_end(nlh, nest2); + } + mnl_attr_nest_end(nlh, nest1); + } + } + if (e->flags & (1 << NFTNL_EXPR_DYNSET_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_DYNSET_FLAGS, + htonl(dynset->dynset_flags)); +} + +EXPORT_SYMBOL(nftnl_expr_add_expr); +void nftnl_expr_add_expr(struct nftnl_expr *e, uint32_t type, + struct nftnl_expr *expr) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + + list_add_tail(&expr->head, &dynset->expr_list); +} + +EXPORT_SYMBOL(nftnl_expr_expr_foreach); +int nftnl_expr_expr_foreach(const struct nftnl_expr *e, + int (*cb)(struct nftnl_expr *e, void *data), + void *data) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + struct nftnl_expr *cur, *tmp; + int ret; + + list_for_each_entry_safe(cur, tmp, &dynset->expr_list, head) { + ret = cb(cur, data); + if (ret < 0) + return ret; + } + return 0; +} + +static int +nftnl_expr_dynset_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + struct nlattr *tb[NFTA_SET_MAX+1] = {}; + struct nftnl_expr *expr, *next; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_dynset_cb, tb) < 0) + return -1; + + if (tb[NFTA_DYNSET_SREG_KEY]) { + dynset->sreg_key = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_KEY])); + e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_KEY); + } + if (tb[NFTA_DYNSET_SREG_DATA]) { + dynset->sreg_data = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_DATA])); + e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_DATA); + } + if (tb[NFTA_DYNSET_OP]) { + dynset->op = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_OP])); + e->flags |= (1 << NFTNL_EXPR_DYNSET_OP); + } + if (tb[NFTA_DYNSET_TIMEOUT]) { + dynset->timeout = be64toh(mnl_attr_get_u64(tb[NFTA_DYNSET_TIMEOUT])); + e->flags |= (1 << NFTNL_EXPR_DYNSET_TIMEOUT); + } + if (tb[NFTA_DYNSET_SET_NAME]) { + dynset->set_name = + strdup(mnl_attr_get_str(tb[NFTA_DYNSET_SET_NAME])); + if (!dynset->set_name) + return -1; + e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_NAME); + } + if (tb[NFTA_DYNSET_SET_ID]) { + dynset->set_id = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SET_ID])); + e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_ID); + } + if (tb[NFTA_DYNSET_EXPR]) { + expr = nftnl_expr_parse(tb[NFTA_DYNSET_EXPR]); + if (expr == NULL) + return -1; + + list_add(&expr->head, &dynset->expr_list); + e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPR); + } else if (tb[NFTA_DYNSET_EXPRESSIONS]) { + struct nlattr *attr2; + + mnl_attr_for_each_nested(attr2, tb[NFTA_DYNSET_EXPRESSIONS]) { + if (mnl_attr_get_type(attr2) != NFTA_LIST_ELEM) + goto out_dynset_expr; + + expr = nftnl_expr_parse(attr2); + if (!expr) + goto out_dynset_expr; + + list_add_tail(&expr->head, &dynset->expr_list); + } + e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPRESSIONS); + } + if (tb[NFTA_DYNSET_FLAGS]) { + dynset->dynset_flags = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_DYNSET_FLAGS); + } + + return ret; +out_dynset_expr: + list_for_each_entry_safe(expr, next, &dynset->expr_list, head) + nftnl_expr_free(expr); + + return -1; +} + +static const char *op2str_array[] = { + [NFT_DYNSET_OP_ADD] = "add", + [NFT_DYNSET_OP_UPDATE] = "update", + [NFT_DYNSET_OP_DELETE] = "delete", +}; + +static const char *op2str(enum nft_dynset_ops op) +{ + if (op > NFT_DYNSET_OP_DELETE) + return "unknown"; + return op2str_array[op]; +} + +static int +nftnl_expr_dynset_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + struct nftnl_expr *expr; + int offset = 0, ret; + + ret = snprintf(buf, remain, "%s reg_key %u set %s ", + op2str(dynset->op), dynset->sreg_key, dynset->set_name); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA)) { + ret = snprintf(buf + offset, remain, "sreg_data %u ", + dynset->sreg_data); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT)) { + ret = snprintf(buf + offset, remain, "timeout %"PRIu64"ms ", + dynset->timeout); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + list_for_each_entry(expr, &dynset->expr_list, head) { + ret = snprintf(buf + offset, remain, "expr [ %s ", + expr->ops->name); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_expr_snprintf(buf + offset, remain, expr, + NFTNL_OUTPUT_DEFAULT, + NFTNL_OF_EVENT_ANY); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = snprintf(buf + offset, remain, "] "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +static void nftnl_expr_dynset_init(const struct nftnl_expr *e) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + + INIT_LIST_HEAD(&dynset->expr_list); +} + +static void nftnl_expr_dynset_free(const struct nftnl_expr *e) +{ + struct nftnl_expr_dynset *dynset = nftnl_expr_data(e); + struct nftnl_expr *expr, *next; + + xfree(dynset->set_name); + list_for_each_entry_safe(expr, next, &dynset->expr_list, head) + nftnl_expr_free(expr); +} + +struct expr_ops expr_ops_dynset = { + .name = "dynset", + .alloc_len = sizeof(struct nftnl_expr_dynset), + .max_attr = NFTA_DYNSET_MAX, + .init = nftnl_expr_dynset_init, + .free = nftnl_expr_dynset_free, + .set = nftnl_expr_dynset_set, + .get = nftnl_expr_dynset_get, + .parse = nftnl_expr_dynset_parse, + .build = nftnl_expr_dynset_build, + .output = nftnl_expr_dynset_snprintf, +}; diff --git a/src/expr/exthdr.c b/src/expr/exthdr.c new file mode 100644 index 0000000..739c7ff --- /dev/null +++ b/src/expr/exthdr.c @@ -0,0 +1,271 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 "internal.h" + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <limits.h> +#include <arpa/inet.h> +#include <errno.h> +#include <libmnl/libmnl.h> + +#include <linux/netfilter/nf_tables.h> + +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +#ifndef IPPROTO_MH +#define IPPROTO_MH 135 +#endif + +struct nftnl_expr_exthdr { + enum nft_registers dreg; + enum nft_registers sreg; + uint32_t offset; + uint32_t len; + uint8_t type; + uint32_t op; + uint32_t flags; +}; + +static int +nftnl_expr_exthdr_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_EXTHDR_DREG: + memcpy(&exthdr->dreg, data, sizeof(exthdr->dreg)); + break; + case NFTNL_EXPR_EXTHDR_TYPE: + memcpy(&exthdr->type, data, sizeof(exthdr->type)); + break; + case NFTNL_EXPR_EXTHDR_OFFSET: + memcpy(&exthdr->offset, data, sizeof(exthdr->offset)); + break; + case NFTNL_EXPR_EXTHDR_LEN: + memcpy(&exthdr->len, data, sizeof(exthdr->len)); + break; + case NFTNL_EXPR_EXTHDR_OP: + memcpy(&exthdr->op, data, sizeof(exthdr->op)); + break; + case NFTNL_EXPR_EXTHDR_FLAGS: + memcpy(&exthdr->flags, data, sizeof(exthdr->flags)); + break; + case NFTNL_EXPR_EXTHDR_SREG: + memcpy(&exthdr->sreg, data, sizeof(exthdr->sreg)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_exthdr_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_EXTHDR_DREG: + *data_len = sizeof(exthdr->dreg); + return &exthdr->dreg; + case NFTNL_EXPR_EXTHDR_TYPE: + *data_len = sizeof(exthdr->type); + return &exthdr->type; + case NFTNL_EXPR_EXTHDR_OFFSET: + *data_len = sizeof(exthdr->offset); + return &exthdr->offset; + case NFTNL_EXPR_EXTHDR_LEN: + *data_len = sizeof(exthdr->len); + return &exthdr->len; + case NFTNL_EXPR_EXTHDR_OP: + *data_len = sizeof(exthdr->op); + return &exthdr->op; + case NFTNL_EXPR_EXTHDR_FLAGS: + *data_len = sizeof(exthdr->flags); + return &exthdr->flags; + case NFTNL_EXPR_EXTHDR_SREG: + *data_len = sizeof(exthdr->sreg); + return &exthdr->sreg; + } + return NULL; +} + +static int nftnl_expr_exthdr_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_EXTHDR_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_EXTHDR_TYPE: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + abi_breakage(); + break; + case NFTA_EXTHDR_DREG: + case NFTA_EXTHDR_SREG: + case NFTA_EXTHDR_OFFSET: + case NFTA_EXTHDR_LEN: + case NFTA_EXTHDR_OP: + case NFTA_EXTHDR_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_exthdr_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG)) + mnl_attr_put_u32(nlh, NFTA_EXTHDR_DREG, htonl(exthdr->dreg)); + if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG)) + mnl_attr_put_u32(nlh, NFTA_EXTHDR_SREG, htonl(exthdr->sreg)); + if (e->flags & (1 << NFTNL_EXPR_EXTHDR_TYPE)) + mnl_attr_put_u8(nlh, NFTA_EXTHDR_TYPE, exthdr->type); + if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OFFSET)) + mnl_attr_put_u32(nlh, NFTA_EXTHDR_OFFSET, htonl(exthdr->offset)); + if (e->flags & (1 << NFTNL_EXPR_EXTHDR_LEN)) + mnl_attr_put_u32(nlh, NFTA_EXTHDR_LEN, htonl(exthdr->len)); + if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OP)) + mnl_attr_put_u32(nlh, NFTA_EXTHDR_OP, htonl(exthdr->op)); + if (e->flags & (1 << NFTNL_EXPR_EXTHDR_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_EXTHDR_FLAGS, htonl(exthdr->flags)); +} + +static int +nftnl_expr_exthdr_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e); + struct nlattr *tb[NFTA_EXTHDR_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_exthdr_cb, tb) < 0) + return -1; + + if (tb[NFTA_EXTHDR_DREG]) { + exthdr->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_DREG])); + e->flags |= (1 << NFTNL_EXPR_EXTHDR_DREG); + } + if (tb[NFTA_EXTHDR_SREG]) { + exthdr->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_SREG])); + e->flags |= (1 << NFTNL_EXPR_EXTHDR_SREG); + } + if (tb[NFTA_EXTHDR_TYPE]) { + exthdr->type = mnl_attr_get_u8(tb[NFTA_EXTHDR_TYPE]); + e->flags |= (1 << NFTNL_EXPR_EXTHDR_TYPE); + } + if (tb[NFTA_EXTHDR_OFFSET]) { + exthdr->offset = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OFFSET])); + e->flags |= (1 << NFTNL_EXPR_EXTHDR_OFFSET); + } + if (tb[NFTA_EXTHDR_LEN]) { + exthdr->len = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_LEN])); + e->flags |= (1 << NFTNL_EXPR_EXTHDR_LEN); + } + if (tb[NFTA_EXTHDR_OP]) { + exthdr->op = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OP])); + e->flags |= (1 << NFTNL_EXPR_EXTHDR_OP); + } + if (tb[NFTA_EXTHDR_FLAGS]) { + exthdr->flags = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_EXTHDR_FLAGS); + } + + return 0; +} + +static const char *op2str(uint8_t op) +{ + switch (op) { + case NFT_EXTHDR_OP_TCPOPT: + return " tcpopt"; + case NFT_EXTHDR_OP_IPV6: + return " ipv6"; + case NFT_EXTHDR_OP_IPV4: + return " ipv4"; + default: + return ""; + } +} + +static inline int str2exthdr_op(const char* str) +{ + if (!strcmp(str, "tcpopt")) + return NFT_EXTHDR_OP_TCPOPT; + if (!strcmp(str, "ipv4")) + return NFT_EXTHDR_OP_IPV4; + + /* if str == "ipv6" or anything else */ + return NFT_EXTHDR_OP_IPV6; +} + +static inline int str2exthdr_type(const char *str) +{ + if (strcmp(str, "hopopts") == 0) + return IPPROTO_HOPOPTS; + else if (strcmp(str, "routing") == 0) + return IPPROTO_ROUTING; + else if (strcmp(str, "fragment") == 0) + return IPPROTO_FRAGMENT; + else if (strcmp(str, "dstopts") == 0) + return IPPROTO_DSTOPTS; + else if (strcmp(str, "mh") == 0) + return IPPROTO_MH; + + return -1; +} + +static int +nftnl_expr_exthdr_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG)) + return snprintf(buf, len, "load%s %ub @ %u + %u%s => reg %u ", + op2str(exthdr->op), exthdr->len, exthdr->type, + exthdr->offset, + exthdr->flags & NFT_EXTHDR_F_PRESENT ? " present" : "", + exthdr->dreg); + else if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG)) + return snprintf(buf, len, "write%s reg %u => %ub @ %u + %u ", + op2str(exthdr->op), exthdr->sreg, exthdr->len, exthdr->type, + exthdr->offset); + else if (exthdr->op == NFT_EXTHDR_OP_TCPOPT && exthdr->len == 0) + return snprintf(buf, len, "reset tcpopt %u ", exthdr->type); + else + return snprintf(buf, len, "op %u len %u type %u offset %u ", + exthdr->op, exthdr->len, exthdr->type, exthdr->offset); + +} + +struct expr_ops expr_ops_exthdr = { + .name = "exthdr", + .alloc_len = sizeof(struct nftnl_expr_exthdr), + .max_attr = NFTA_EXTHDR_MAX, + .set = nftnl_expr_exthdr_set, + .get = nftnl_expr_exthdr_get, + .parse = nftnl_expr_exthdr_parse, + .build = nftnl_expr_exthdr_build, + .output = nftnl_expr_exthdr_snprintf, +}; diff --git a/src/expr/fib.c b/src/expr/fib.c new file mode 100644 index 0000000..957f929 --- /dev/null +++ b/src/expr/fib.c @@ -0,0 +1,202 @@ +/* + * (C) 2016 Red Hat GmbH + * Author: Florian Westphal <fw@strlen.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <stdio.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_fib { + uint32_t flags; + uint32_t result; + enum nft_registers dreg; +}; + +static int +nftnl_expr_fib_set(struct nftnl_expr *e, uint16_t result, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_fib *fib = nftnl_expr_data(e); + + switch (result) { + case NFTNL_EXPR_FIB_RESULT: + memcpy(&fib->result, data, sizeof(fib->result)); + break; + case NFTNL_EXPR_FIB_DREG: + memcpy(&fib->dreg, data, sizeof(fib->dreg)); + break; + case NFTNL_EXPR_FIB_FLAGS: + memcpy(&fib->flags, data, sizeof(fib->flags)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_fib_get(const struct nftnl_expr *e, uint16_t result, + uint32_t *data_len) +{ + struct nftnl_expr_fib *fib = nftnl_expr_data(e); + + switch (result) { + case NFTNL_EXPR_FIB_RESULT: + *data_len = sizeof(fib->result); + return &fib->result; + case NFTNL_EXPR_FIB_DREG: + *data_len = sizeof(fib->dreg); + return &fib->dreg; + case NFTNL_EXPR_FIB_FLAGS: + *data_len = sizeof(fib->flags); + return &fib->flags; + } + return NULL; +} + +static int nftnl_expr_fib_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_FIB_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_FIB_RESULT: + case NFTA_FIB_DREG: + case NFTA_FIB_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_fib_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_fib *fib = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_FIB_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_FIB_FLAGS, htonl(fib->flags)); + if (e->flags & (1 << NFTNL_EXPR_FIB_RESULT)) + mnl_attr_put_u32(nlh, NFTA_FIB_RESULT, htonl(fib->result)); + if (e->flags & (1 << NFTNL_EXPR_FIB_DREG)) + mnl_attr_put_u32(nlh, NFTA_FIB_DREG, htonl(fib->dreg)); +} + +static int +nftnl_expr_fib_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_fib *fib = nftnl_expr_data(e); + struct nlattr *tb[NFTA_FIB_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_fib_cb, tb) < 0) + return -1; + + if (tb[NFTA_FIB_RESULT]) { + fib->result = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_RESULT])); + e->flags |= (1 << NFTNL_EXPR_FIB_RESULT); + } + if (tb[NFTA_FIB_DREG]) { + fib->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_DREG])); + e->flags |= (1 << NFTNL_EXPR_FIB_DREG); + } + if (tb[NFTA_FIB_FLAGS]) { + fib->flags = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_FIB_FLAGS); + } + return ret; +} + +static const char *fib_type[NFT_FIB_RESULT_MAX + 1] = { + [NFT_FIB_RESULT_OIF] = "oif", + [NFT_FIB_RESULT_OIFNAME] = "oifname", + [NFT_FIB_RESULT_ADDRTYPE] = "type", +}; + +static const char *fib_type_str(enum nft_fib_result r) +{ + if (r <= NFT_FIB_RESULT_MAX) + return fib_type[r]; + + return "unknown"; +} + +static int +nftnl_expr_fib_snprintf(char *buf, size_t remain, + uint32_t printflags, const struct nftnl_expr *e) +{ + struct nftnl_expr_fib *fib = nftnl_expr_data(e); + uint32_t flags = fib->flags & ~NFTA_FIB_F_PRESENT; + uint32_t present_flag = fib->flags & NFTA_FIB_F_PRESENT; + int offset = 0, ret, i; + static const struct { + int bit; + const char *name; + } tab[] = { + { NFTA_FIB_F_SADDR, "saddr" }, + { NFTA_FIB_F_DADDR, "daddr" }, + { NFTA_FIB_F_MARK, "mark" }, + { NFTA_FIB_F_IIF, "iif" }, + { NFTA_FIB_F_OIF, "oif" }, + }; + + for (i = 0; i < (sizeof(tab) / sizeof(tab[0])); i++) { + if (flags & tab[i].bit) { + ret = snprintf(buf + offset, remain, "%s ", + tab[i].name); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + flags &= ~tab[i].bit; + if (flags) { + ret = snprintf(buf + offset, remain, ". "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + } + } + + if (flags) { + ret = snprintf(buf + offset, remain, "unknown 0x%" PRIx32, + flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + ret = snprintf(buf + offset, remain, "%s%s => reg %d ", + fib_type_str(fib->result), + present_flag ? " present" : "", + fib->dreg); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + return offset; +} + +struct expr_ops expr_ops_fib = { + .name = "fib", + .alloc_len = sizeof(struct nftnl_expr_fib), + .max_attr = NFTA_FIB_MAX, + .set = nftnl_expr_fib_set, + .get = nftnl_expr_fib_get, + .parse = nftnl_expr_fib_parse, + .build = nftnl_expr_fib_build, + .output = nftnl_expr_fib_snprintf, +}; diff --git a/src/expr/flow_offload.c b/src/expr/flow_offload.c new file mode 100644 index 0000000..4fc0563 --- /dev/null +++ b/src/expr/flow_offload.c @@ -0,0 +1,124 @@ +#include "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> /* for memcpy */ +#include <arpa/inet.h> +#include <errno.h> +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/rule.h> +#include <libnftnl/expr.h> + +struct nftnl_expr_flow { + char *table_name; +}; + +static int nftnl_expr_flow_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_flow *flow = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_FLOW_TABLE_NAME: + flow->table_name = strdup((const char *)data); + if (!flow->table_name) + return -1; + break; + default: + return -1; + } + return 0; +} + +static const void *nftnl_expr_flow_get(const struct nftnl_expr *e, + uint16_t type, uint32_t *data_len) +{ + struct nftnl_expr_flow *flow = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_FLOW_TABLE_NAME: + *data_len = strlen(flow->table_name) + 1; + return flow->table_name; + } + return NULL; +} + +static int nftnl_expr_flow_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_FLOW_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_FLOW_TABLE_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void nftnl_expr_flow_build(struct nlmsghdr *nlh, + const struct nftnl_expr *e) +{ + struct nftnl_expr_flow *flow = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_FLOW_TABLE_NAME)) + mnl_attr_put_strz(nlh, NFTA_FLOW_TABLE_NAME, flow->table_name); +} + +static int nftnl_expr_flow_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_flow *flow = nftnl_expr_data(e); + struct nlattr *tb[NFTA_FLOW_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_flow_cb, tb) < 0) + return -1; + + if (tb[NFTA_FLOW_TABLE_NAME]) { + flow->table_name = + strdup(mnl_attr_get_str(tb[NFTA_FLOW_TABLE_NAME])); + if (!flow->table_name) + return -1; + e->flags |= (1 << NFTNL_EXPR_FLOW_TABLE_NAME); + } + + return ret; +} + +static int nftnl_expr_flow_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_flow *l = nftnl_expr_data(e); + int offset = 0, ret; + + ret = snprintf(buf, remain, "flowtable %s ", l->table_name); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + return offset; +} + +static void nftnl_expr_flow_free(const struct nftnl_expr *e) +{ + struct nftnl_expr_flow *flow = nftnl_expr_data(e); + + xfree(flow->table_name); +} + +struct expr_ops expr_ops_flow = { + .name = "flow_offload", + .alloc_len = sizeof(struct nftnl_expr_flow), + .max_attr = NFTA_FLOW_MAX, + .free = nftnl_expr_flow_free, + .set = nftnl_expr_flow_set, + .get = nftnl_expr_flow_get, + .parse = nftnl_expr_flow_parse, + .build = nftnl_expr_flow_build, + .output = nftnl_expr_flow_snprintf, +}; diff --git a/src/expr/fwd.c b/src/expr/fwd.c new file mode 100644 index 0000000..51f6612 --- /dev/null +++ b/src/expr/fwd.c @@ -0,0 +1,162 @@ +/* + * (C) 2015 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include "internal.h" +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> +#include "expr_ops.h" +#include "data_reg.h" + +struct nftnl_expr_fwd { + enum nft_registers sreg_dev; + enum nft_registers sreg_addr; + uint32_t nfproto; +}; + +static int nftnl_expr_fwd_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_fwd *fwd = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_FWD_SREG_DEV: + memcpy(&fwd->sreg_dev, data, sizeof(fwd->sreg_dev)); + break; + case NFTNL_EXPR_FWD_SREG_ADDR: + memcpy(&fwd->sreg_addr, data, sizeof(fwd->sreg_addr)); + break; + case NFTNL_EXPR_FWD_NFPROTO: + memcpy(&fwd->nfproto, data, sizeof(fwd->nfproto)); + break; + default: + return -1; + } + return 0; +} + +static const void *nftnl_expr_fwd_get(const struct nftnl_expr *e, + uint16_t type, uint32_t *data_len) +{ + struct nftnl_expr_fwd *fwd = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_FWD_SREG_DEV: + *data_len = sizeof(fwd->sreg_dev); + return &fwd->sreg_dev; + case NFTNL_EXPR_FWD_SREG_ADDR: + *data_len = sizeof(fwd->sreg_addr); + return &fwd->sreg_addr; + case NFTNL_EXPR_FWD_NFPROTO: + *data_len = sizeof(fwd->nfproto); + return &fwd->nfproto; + } + return NULL; +} + +static int nftnl_expr_fwd_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_FWD_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_FWD_SREG_DEV: + case NFTA_FWD_SREG_ADDR: + case NFTA_FWD_NFPROTO: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void nftnl_expr_fwd_build(struct nlmsghdr *nlh, + const struct nftnl_expr *e) +{ + struct nftnl_expr_fwd *fwd = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_DEV)) + mnl_attr_put_u32(nlh, NFTA_FWD_SREG_DEV, htonl(fwd->sreg_dev)); + if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_ADDR)) + mnl_attr_put_u32(nlh, NFTA_FWD_SREG_ADDR, htonl(fwd->sreg_addr)); + if (e->flags & (1 << NFTNL_EXPR_FWD_NFPROTO)) + mnl_attr_put_u32(nlh, NFTA_FWD_NFPROTO, htonl(fwd->nfproto)); +} + +static int nftnl_expr_fwd_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_fwd *fwd = nftnl_expr_data(e); + struct nlattr *tb[NFTA_FWD_MAX + 1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_fwd_cb, tb) < 0) + return -1; + + if (tb[NFTA_FWD_SREG_DEV]) { + fwd->sreg_dev = ntohl(mnl_attr_get_u32(tb[NFTA_FWD_SREG_DEV])); + e->flags |= (1 << NFTNL_EXPR_FWD_SREG_DEV); + } + if (tb[NFTA_FWD_SREG_ADDR]) { + fwd->sreg_addr = ntohl(mnl_attr_get_u32(tb[NFTA_FWD_SREG_ADDR])); + e->flags |= (1 << NFTNL_EXPR_FWD_SREG_ADDR); + } + if (tb[NFTA_FWD_NFPROTO]) { + fwd->nfproto = ntohl(mnl_attr_get_u32(tb[NFTA_FWD_NFPROTO])); + e->flags |= (1 << NFTNL_EXPR_FWD_NFPROTO); + } + + return ret; +} + +static int nftnl_expr_fwd_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_fwd *fwd = nftnl_expr_data(e); + int offset = 0, ret; + + if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_DEV)) { + ret = snprintf(buf + offset, remain, "sreg_dev %u ", + fwd->sreg_dev); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_ADDR)) { + ret = snprintf(buf + offset, remain, "sreg_addr %u ", + fwd->sreg_addr); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (e->flags & (1 << NFTNL_EXPR_FWD_NFPROTO)) { + ret = snprintf(buf + offset, remain, "nfproto %u ", + fwd->nfproto); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +struct expr_ops expr_ops_fwd = { + .name = "fwd", + .alloc_len = sizeof(struct nftnl_expr_fwd), + .max_attr = NFTA_FWD_MAX, + .set = nftnl_expr_fwd_set, + .get = nftnl_expr_fwd_get, + .parse = nftnl_expr_fwd_parse, + .build = nftnl_expr_fwd_build, + .output = nftnl_expr_fwd_snprintf, +}; diff --git a/src/expr/hash.c b/src/expr/hash.c new file mode 100644 index 0000000..6e2dd19 --- /dev/null +++ b/src/expr/hash.c @@ -0,0 +1,230 @@ +/* + * (C) 2016 by Laura Garcia <nevola@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. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_hash { + enum nft_hash_types type; + enum nft_registers sreg; + enum nft_registers dreg; + unsigned int len; + unsigned int modulus; + unsigned int seed; + unsigned int offset; +}; + +static int +nftnl_expr_hash_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_hash *hash = nftnl_expr_data(e); + switch (type) { + case NFTNL_EXPR_HASH_SREG: + memcpy(&hash->sreg, data, sizeof(hash->sreg)); + break; + case NFTNL_EXPR_HASH_DREG: + memcpy(&hash->dreg, data, sizeof(hash->dreg)); + break; + case NFTNL_EXPR_HASH_LEN: + memcpy(&hash->len, data, sizeof(hash->len)); + break; + case NFTNL_EXPR_HASH_MODULUS: + memcpy(&hash->modulus, data, sizeof(hash->modulus)); + break; + case NFTNL_EXPR_HASH_SEED: + memcpy(&hash->seed, data, sizeof(hash->seed)); + break; + case NFTNL_EXPR_HASH_OFFSET: + memcpy(&hash->offset, data, sizeof(hash->offset)); + break; + case NFTNL_EXPR_HASH_TYPE: + memcpy(&hash->type, data, sizeof(hash->type)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_hash_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_hash *hash = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_HASH_SREG: + *data_len = sizeof(hash->sreg); + return &hash->sreg; + case NFTNL_EXPR_HASH_DREG: + *data_len = sizeof(hash->dreg); + return &hash->dreg; + case NFTNL_EXPR_HASH_LEN: + *data_len = sizeof(hash->len); + return &hash->len; + case NFTNL_EXPR_HASH_MODULUS: + *data_len = sizeof(hash->modulus); + return &hash->modulus; + case NFTNL_EXPR_HASH_SEED: + *data_len = sizeof(hash->seed); + return &hash->seed; + case NFTNL_EXPR_HASH_OFFSET: + *data_len = sizeof(hash->offset); + return &hash->offset; + case NFTNL_EXPR_HASH_TYPE: + *data_len = sizeof(hash->type); + return &hash->type; + } + return NULL; +} + +static int nftnl_expr_hash_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_HASH_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_HASH_SREG: + case NFTA_HASH_DREG: + case NFTA_HASH_LEN: + case NFTA_HASH_MODULUS: + case NFTA_HASH_SEED: + case NFTA_HASH_OFFSET: + case NFTA_HASH_TYPE: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_hash_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_hash *hash = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_HASH_SREG)) + mnl_attr_put_u32(nlh, NFTA_HASH_SREG, htonl(hash->sreg)); + if (e->flags & (1 << NFTNL_EXPR_HASH_DREG)) + mnl_attr_put_u32(nlh, NFTA_HASH_DREG, htonl(hash->dreg)); + if (e->flags & (1 << NFTNL_EXPR_HASH_LEN)) + mnl_attr_put_u32(nlh, NFTA_HASH_LEN, htonl(hash->len)); + if (e->flags & (1 << NFTNL_EXPR_HASH_MODULUS)) + mnl_attr_put_u32(nlh, NFTA_HASH_MODULUS, htonl(hash->modulus)); + if (e->flags & (1 << NFTNL_EXPR_HASH_SEED)) + mnl_attr_put_u32(nlh, NFTA_HASH_SEED, htonl(hash->seed)); + if (e->flags & (1 << NFTNL_EXPR_HASH_OFFSET)) + mnl_attr_put_u32(nlh, NFTA_HASH_OFFSET, htonl(hash->offset)); + if (e->flags & (1 << NFTNL_EXPR_HASH_TYPE)) + mnl_attr_put_u32(nlh, NFTA_HASH_TYPE, htonl(hash->type)); +} + +static int +nftnl_expr_hash_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_hash *hash = nftnl_expr_data(e); + struct nlattr *tb[NFTA_HASH_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_hash_cb, tb) < 0) + return -1; + + if (tb[NFTA_HASH_SREG]) { + hash->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_SREG])); + e->flags |= (1 << NFTNL_EXPR_HASH_SREG); + } + if (tb[NFTA_HASH_DREG]) { + hash->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_DREG])); + e->flags |= (1 << NFTNL_EXPR_HASH_DREG); + } + if (tb[NFTA_HASH_LEN]) { + hash->len = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_LEN])); + e->flags |= (1 << NFTNL_EXPR_HASH_LEN); + } + if (tb[NFTA_HASH_MODULUS]) { + hash->modulus = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_MODULUS])); + e->flags |= (1 << NFTNL_EXPR_HASH_MODULUS); + } + if (tb[NFTA_HASH_SEED]) { + hash->seed = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_SEED])); + e->flags |= (1 << NFTNL_EXPR_HASH_SEED); + } + if (tb[NFTA_HASH_OFFSET]) { + hash->offset = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_OFFSET])); + e->flags |= (1 << NFTNL_EXPR_HASH_OFFSET); + } + if (tb[NFTA_HASH_TYPE]) { + hash->type = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_TYPE])); + e->flags |= (1 << NFTNL_EXPR_HASH_TYPE); + } + + return ret; +} + +static int +nftnl_expr_hash_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_hash *hash = nftnl_expr_data(e); + int offset = 0, ret; + + switch (hash->type) { + case NFT_HASH_SYM: + ret = + snprintf(buf, remain, "reg %u = symhash() %% mod %u ", + hash->dreg, + hash->modulus); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + break; + case NFT_HASH_JENKINS: + default: + ret = + snprintf(buf, remain, + "reg %u = jhash(reg %u, %u, 0x%x) %% mod %u ", + hash->dreg, hash->sreg, hash->len, hash->seed, + hash->modulus); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + break; + } + + if (hash->offset) { + ret = snprintf(buf + offset, remain, "offset %u ", + hash->offset); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +struct expr_ops expr_ops_hash = { + .name = "hash", + .alloc_len = sizeof(struct nftnl_expr_hash), + .max_attr = NFTA_HASH_MAX, + .set = nftnl_expr_hash_set, + .get = nftnl_expr_hash_get, + .parse = nftnl_expr_hash_parse, + .build = nftnl_expr_hash_build, + .output = nftnl_expr_hash_snprintf, +}; diff --git a/src/expr/immediate.c b/src/expr/immediate.c new file mode 100644 index 0000000..5d477a8 --- /dev/null +++ b/src/expr/immediate.c @@ -0,0 +1,233 @@ +/* + * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include "internal.h" +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_immediate { + union nftnl_data_reg data; + enum nft_registers dreg; +}; + +static int +nftnl_expr_immediate_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_immediate *imm = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_IMM_DREG: + memcpy(&imm->dreg, data, sizeof(imm->dreg)); + break; + case NFTNL_EXPR_IMM_DATA: + memcpy(&imm->data.val, data, data_len); + imm->data.len = data_len; + break; + case NFTNL_EXPR_IMM_VERDICT: + memcpy(&imm->data.verdict, data, sizeof(imm->data.verdict)); + break; + case NFTNL_EXPR_IMM_CHAIN: + if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN)) + xfree(imm->data.chain); + + imm->data.chain = strdup(data); + if (!imm->data.chain) + return -1; + break; + case NFTNL_EXPR_IMM_CHAIN_ID: + memcpy(&imm->data.chain_id, data, sizeof(uint32_t)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_immediate_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_immediate *imm = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_IMM_DREG: + *data_len = sizeof(imm->dreg); + return &imm->dreg; + case NFTNL_EXPR_IMM_DATA: + *data_len = imm->data.len; + return &imm->data.val; + case NFTNL_EXPR_IMM_VERDICT: + *data_len = sizeof(imm->data.verdict); + return &imm->data.verdict; + case NFTNL_EXPR_IMM_CHAIN: + *data_len = strlen(imm->data.chain)+1; + return imm->data.chain; + case NFTNL_EXPR_IMM_CHAIN_ID: + *data_len = sizeof(imm->data.chain_id); + return &imm->data.chain_id; + } + return NULL; +} + +static int nftnl_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) + abi_breakage(); + break; + case NFTA_IMMEDIATE_DATA: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_immediate_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_immediate *imm = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_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 << NFTNL_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 << NFTNL_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 << NFTNL_EXPR_IMM_CHAIN)) + mnl_attr_put_strz(nlh, NFTA_VERDICT_CHAIN, imm->data.chain); + if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN_ID)) { + mnl_attr_put_u32(nlh, NFTA_VERDICT_CHAIN_ID, + htonl(imm->data.chain_id)); + } + + mnl_attr_nest_end(nlh, nest1); + mnl_attr_nest_end(nlh, nest2); + } +} + +static int +nftnl_expr_immediate_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_immediate *imm = nftnl_expr_data(e); + struct nlattr *tb[NFTA_IMMEDIATE_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_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 << NFTNL_EXPR_IMM_DREG); + } + if (tb[NFTA_IMMEDIATE_DATA]) { + int type; + + ret = nftnl_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 << NFTNL_EXPR_IMM_DATA); + break; + case DATA_VERDICT: + /* NF_ACCEPT, NF_DROP, NF_QUEUE and NFTNL_RETURN case */ + e->flags |= (1 << NFTNL_EXPR_IMM_VERDICT); + break; + case DATA_CHAIN: + /* NFTNL_GOTO and NFTNL_JUMP case */ + e->flags |= (1 << NFTNL_EXPR_IMM_VERDICT) | + (1 << NFTNL_EXPR_IMM_CHAIN); + break; + } + } + + return ret; +} + +static int +nftnl_expr_immediate_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_immediate *imm = nftnl_expr_data(e); + int offset = 0, ret; + + ret = snprintf(buf, remain, "reg %u ", imm->dreg); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + if (e->flags & (1 << NFTNL_EXPR_IMM_DATA)) { + ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data, + flags, DATA_VALUE); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + } else if (e->flags & (1 << NFTNL_EXPR_IMM_VERDICT)) { + ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data, + flags, DATA_VERDICT); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + } else if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN)) { + ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data, + flags, DATA_CHAIN); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +static void nftnl_expr_immediate_free(const struct nftnl_expr *e) +{ + struct nftnl_expr_immediate *imm = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_IMM_VERDICT)) + nftnl_free_verdict(&imm->data); +} + +struct expr_ops expr_ops_immediate = { + .name = "immediate", + .alloc_len = sizeof(struct nftnl_expr_immediate), + .max_attr = NFTA_IMMEDIATE_MAX, + .free = nftnl_expr_immediate_free, + .set = nftnl_expr_immediate_set, + .get = nftnl_expr_immediate_get, + .parse = nftnl_expr_immediate_parse, + .build = nftnl_expr_immediate_build, + .output = nftnl_expr_immediate_snprintf, +}; diff --git a/src/expr/inner.c b/src/expr/inner.c new file mode 100644 index 0000000..7daae4f --- /dev/null +++ b/src/expr/inner.c @@ -0,0 +1,214 @@ +/* + * (C) 2012-2022 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <limits.h> +#include <arpa/inet.h> +#include <errno.h> +#include <libmnl/libmnl.h> + +#include <linux/netfilter/nf_tables.h> + +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_inner { + uint32_t type; + uint32_t flags; + uint32_t hdrsize; + struct nftnl_expr *expr; +}; + +static void nftnl_expr_inner_free(const struct nftnl_expr *e) +{ + struct nftnl_expr_inner *inner = nftnl_expr_data(e); + + if (inner->expr) + nftnl_expr_free(inner->expr); +} + +static int +nftnl_expr_inner_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_inner *inner = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_INNER_TYPE: + memcpy(&inner->type, data, sizeof(inner->type)); + break; + case NFTNL_EXPR_INNER_FLAGS: + memcpy(&inner->flags, data, sizeof(inner->flags)); + break; + case NFTNL_EXPR_INNER_HDRSIZE: + memcpy(&inner->hdrsize, data, sizeof(inner->hdrsize)); + break; + case NFTNL_EXPR_INNER_EXPR: + if (inner->expr) + nftnl_expr_free(inner->expr); + + inner->expr = (void *)data; + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_inner_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_inner *inner = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_INNER_FLAGS: + *data_len = sizeof(inner->flags); + return &inner->flags; + case NFTNL_EXPR_INNER_TYPE: + *data_len = sizeof(inner->type); + return &inner->type; + case NFTNL_EXPR_INNER_HDRSIZE: + *data_len = sizeof(inner->hdrsize); + return &inner->hdrsize; + case NFTNL_EXPR_INNER_EXPR: + return inner->expr; + } + return NULL; +} + +static void +nftnl_expr_inner_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_inner *inner = nftnl_expr_data(e); + struct nlattr *nest; + + mnl_attr_put_u32(nlh, NFTA_INNER_NUM, htonl(0)); + if (e->flags & (1 << NFTNL_EXPR_INNER_TYPE)) + mnl_attr_put_u32(nlh, NFTA_INNER_TYPE, htonl(inner->type)); + if (e->flags & (1 << NFTNL_EXPR_INNER_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_INNER_FLAGS, htonl(inner->flags)); + if (e->flags & (1 << NFTNL_EXPR_INNER_HDRSIZE)) + mnl_attr_put_u32(nlh, NFTA_INNER_HDRSIZE, htonl(inner->hdrsize)); + if (e->flags & (1 << NFTNL_EXPR_INNER_EXPR)) { + nest = mnl_attr_nest_start(nlh, NFTA_INNER_EXPR); + nftnl_expr_build_payload(nlh, inner->expr); + mnl_attr_nest_end(nlh, nest); + } +} + +static int nftnl_inner_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_INNER_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_INNER_NUM: + case NFTA_INNER_TYPE: + case NFTA_INNER_HDRSIZE: + case NFTA_INNER_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_INNER_EXPR: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + + return MNL_CB_OK; +} + +static int +nftnl_expr_inner_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_inner *inner = nftnl_expr_data(e); + struct nlattr *tb[NFTA_INNER_MAX + 1] = {}; + struct nftnl_expr *expr; + int err; + + err = mnl_attr_parse_nested(attr, nftnl_inner_parse_cb, tb); + if (err < 0) + return err; + + if (tb[NFTA_INNER_HDRSIZE]) { + inner->hdrsize = + ntohl(mnl_attr_get_u32(tb[NFTA_INNER_HDRSIZE])); + e->flags |= (1 << NFTNL_EXPR_INNER_HDRSIZE); + } + if (tb[NFTA_INNER_FLAGS]) { + inner->flags = + ntohl(mnl_attr_get_u32(tb[NFTA_INNER_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_INNER_FLAGS); + } + if (tb[NFTA_INNER_TYPE]) { + inner->type = + ntohl(mnl_attr_get_u32(tb[NFTA_INNER_TYPE])); + e->flags |= (1 << NFTNL_EXPR_INNER_TYPE); + } + if (tb[NFTA_INNER_EXPR]) { + expr = nftnl_expr_parse(tb[NFTA_INNER_EXPR]); + if (!expr) + return -1; + + if (inner->expr) + nftnl_expr_free(inner->expr); + + inner->expr = expr; + e->flags |= (1 << NFTNL_EXPR_INNER_EXPR); + } + + return 0; +} + +static int +nftnl_expr_inner_snprintf(char *buf, size_t remain, uint32_t flags, + const struct nftnl_expr *e) +{ + struct nftnl_expr_inner *inner = nftnl_expr_data(e); + uint32_t offset = 0; + int ret; + + ret = snprintf(buf, remain, "type %u hdrsize %u flags %x [", + inner->type, inner->hdrsize, inner->flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = snprintf(buf + offset, remain, " %s ", inner->expr->ops->name); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_expr_snprintf(buf + offset, remain, inner->expr, + NFTNL_OUTPUT_DEFAULT, 0); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = snprintf(buf + offset, remain, "] "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + return offset; +} + +struct expr_ops expr_ops_inner = { + .name = "inner", + .alloc_len = sizeof(struct nftnl_expr_inner), + .max_attr = NFTA_INNER_MAX, + .free = nftnl_expr_inner_free, + .set = nftnl_expr_inner_set, + .get = nftnl_expr_inner_get, + .parse = nftnl_expr_inner_parse, + .build = nftnl_expr_inner_build, + .output = nftnl_expr_inner_snprintf, +}; diff --git a/src/expr/last.c b/src/expr/last.c new file mode 100644 index 0000000..641b713 --- /dev/null +++ b/src/expr/last.c @@ -0,0 +1,138 @@ +/* + * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <stdio.h> +#include <stdint.h> +#include <arpa/inet.h> +#include <errno.h> +#include <inttypes.h> + +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_last { + uint64_t msecs; + uint32_t set; +}; + +static int nftnl_expr_last_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_last *last = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_LAST_MSECS: + memcpy(&last->msecs, data, sizeof(last->msecs)); + break; + case NFTNL_EXPR_LAST_SET: + memcpy(&last->set, data, sizeof(last->set)); + break; + default: + return -1; + } + return 0; +} + +static const void *nftnl_expr_last_get(const struct nftnl_expr *e, + uint16_t type, uint32_t *data_len) +{ + struct nftnl_expr_last *last = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_LAST_MSECS: + *data_len = sizeof(last->msecs); + return &last->msecs; + case NFTNL_EXPR_LAST_SET: + *data_len = sizeof(last->set); + return &last->set; + } + return NULL; +} + +static int nftnl_expr_last_cb(const struct nlattr *attr, void *data) +{ + int type = mnl_attr_get_type(attr); + const struct nlattr **tb = data; + + if (mnl_attr_type_valid(attr, NFTA_LAST_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_LAST_MSECS: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + abi_breakage(); + break; + case NFTA_LAST_SET: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_last_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_last *last = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_LAST_MSECS)) + mnl_attr_put_u64(nlh, NFTA_LAST_MSECS, htobe64(last->msecs)); + if (e->flags & (1 << NFTNL_EXPR_LAST_SET)) + mnl_attr_put_u32(nlh, NFTA_LAST_SET, htonl(last->set)); +} + +static int +nftnl_expr_last_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_last *last = nftnl_expr_data(e); + struct nlattr *tb[NFTA_LAST_MAX + 1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_last_cb, tb) < 0) + return -1; + + if (tb[NFTA_LAST_MSECS]) { + last->msecs = be64toh(mnl_attr_get_u64(tb[NFTA_LAST_MSECS])); + e->flags |= (1 << NFTNL_EXPR_LAST_MSECS); + } + if (tb[NFTA_LAST_SET]) { + last->set = ntohl(mnl_attr_get_u32(tb[NFTA_LAST_SET])); + e->flags |= (1 << NFTNL_EXPR_LAST_SET); + } + + return 0; +} + +static int nftnl_expr_last_snprintf(char *buf, size_t len, + uint32_t flags, + const struct nftnl_expr *e) +{ + struct nftnl_expr_last *last = nftnl_expr_data(e); + + if (!last->set) + return snprintf(buf, len, "never "); + + return snprintf(buf, len, "%"PRIu64" ", last->msecs); +} + +struct expr_ops expr_ops_last = { + .name = "last", + .alloc_len = sizeof(struct nftnl_expr_last), + .max_attr = NFTA_LAST_MAX, + .set = nftnl_expr_last_set, + .get = nftnl_expr_last_get, + .parse = nftnl_expr_last_parse, + .build = nftnl_expr_last_build, + .output = nftnl_expr_last_snprintf, +}; diff --git a/src/expr/limit.c b/src/expr/limit.c new file mode 100644 index 0000000..1870e0e --- /dev/null +++ b/src/expr/limit.c @@ -0,0 +1,206 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 <stdio.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_limit { + uint64_t rate; + uint64_t unit; + uint32_t burst; + enum nft_limit_type type; + uint32_t flags; +}; + +static int +nftnl_expr_limit_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_limit *limit = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_LIMIT_RATE: + memcpy(&limit->rate, data, sizeof(limit->rate)); + break; + case NFTNL_EXPR_LIMIT_UNIT: + memcpy(&limit->unit, data, sizeof(limit->unit)); + break; + case NFTNL_EXPR_LIMIT_BURST: + memcpy(&limit->burst, data, sizeof(limit->burst)); + break; + case NFTNL_EXPR_LIMIT_TYPE: + memcpy(&limit->type, data, sizeof(limit->type)); + break; + case NFTNL_EXPR_LIMIT_FLAGS: + memcpy(&limit->flags, data, sizeof(limit->flags)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_limit_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_limit *limit = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_LIMIT_RATE: + *data_len = sizeof(uint64_t); + return &limit->rate; + case NFTNL_EXPR_LIMIT_UNIT: + *data_len = sizeof(uint64_t); + return &limit->unit; + case NFTNL_EXPR_LIMIT_BURST: + *data_len = sizeof(uint32_t); + return &limit->burst; + case NFTNL_EXPR_LIMIT_TYPE: + *data_len = sizeof(uint32_t); + return &limit->type; + case NFTNL_EXPR_LIMIT_FLAGS: + *data_len = sizeof(uint32_t); + return &limit->flags; + } + return NULL; +} + +static int nftnl_expr_limit_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_LIMIT_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_LIMIT_RATE: + case NFTA_LIMIT_UNIT: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + abi_breakage(); + break; + case NFTA_LIMIT_BURST: + case NFTA_LIMIT_TYPE: + case NFTA_LIMIT_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_limit_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_limit *limit = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_LIMIT_RATE)) + mnl_attr_put_u64(nlh, NFTA_LIMIT_RATE, htobe64(limit->rate)); + if (e->flags & (1 << NFTNL_EXPR_LIMIT_UNIT)) + mnl_attr_put_u64(nlh, NFTA_LIMIT_UNIT, htobe64(limit->unit)); + if (e->flags & (1 << NFTNL_EXPR_LIMIT_BURST)) + mnl_attr_put_u32(nlh, NFTA_LIMIT_BURST, htonl(limit->burst)); + if (e->flags & (1 << NFTNL_EXPR_LIMIT_TYPE)) + mnl_attr_put_u32(nlh, NFTA_LIMIT_TYPE, htonl(limit->type)); + if (e->flags & (1 << NFTNL_EXPR_LIMIT_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_LIMIT_FLAGS, htonl(limit->flags)); +} + +static int +nftnl_expr_limit_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_limit *limit = nftnl_expr_data(e); + struct nlattr *tb[NFTA_LIMIT_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_limit_cb, tb) < 0) + return -1; + + if (tb[NFTA_LIMIT_RATE]) { + limit->rate = be64toh(mnl_attr_get_u64(tb[NFTA_LIMIT_RATE])); + e->flags |= (1 << NFTNL_EXPR_LIMIT_RATE); + } + if (tb[NFTA_LIMIT_UNIT]) { + limit->unit = be64toh(mnl_attr_get_u64(tb[NFTA_LIMIT_UNIT])); + e->flags |= (1 << NFTNL_EXPR_LIMIT_UNIT); + } + if (tb[NFTA_LIMIT_BURST]) { + limit->burst = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_BURST])); + e->flags |= (1 << NFTNL_EXPR_LIMIT_BURST); + } + if (tb[NFTA_LIMIT_TYPE]) { + limit->type = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_TYPE])); + e->flags |= (1 << NFTNL_EXPR_LIMIT_TYPE); + } + if (tb[NFTA_LIMIT_FLAGS]) { + limit->flags = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_LIMIT_FLAGS); + } + + return 0; +} + +static const char *get_unit(uint64_t u) +{ + switch (u) { + case 1: return "second"; + case 60: return "minute"; + case 60 * 60: return "hour"; + case 60 * 60 * 24: return "day"; + case 60 * 60 * 24 * 7: return "week"; + } + return "error"; +} + +static const char *limit_to_type(enum nft_limit_type type) +{ + switch (type) { + default: + case NFT_LIMIT_PKTS: + return "packets"; + case NFT_LIMIT_PKT_BYTES: + return "bytes"; + } +} + +static int +nftnl_expr_limit_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_limit *limit = nftnl_expr_data(e); + + return snprintf(buf, len, "rate %"PRIu64"/%s burst %u type %s flags 0x%x ", + limit->rate, get_unit(limit->unit), limit->burst, + limit_to_type(limit->type), limit->flags); +} + +struct expr_ops expr_ops_limit = { + .name = "limit", + .alloc_len = sizeof(struct nftnl_expr_limit), + .max_attr = NFTA_LIMIT_MAX, + .set = nftnl_expr_limit_set, + .get = nftnl_expr_limit_get, + .parse = nftnl_expr_limit_parse, + .build = nftnl_expr_limit_build, + .output = nftnl_expr_limit_snprintf, +}; diff --git a/src/expr/log.c b/src/expr/log.c new file mode 100644 index 0000000..180d839 --- /dev/null +++ b/src/expr/log.c @@ -0,0 +1,257 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_log.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_log { + uint32_t snaplen; + uint16_t group; + uint16_t qthreshold; + uint32_t level; + uint32_t flags; + const char *prefix; +}; + +static int nftnl_expr_log_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_log *log = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_LOG_PREFIX: + if (log->flags & (1 << NFTNL_EXPR_LOG_PREFIX)) + xfree(log->prefix); + + log->prefix = strdup(data); + if (!log->prefix) + return -1; + break; + case NFTNL_EXPR_LOG_GROUP: + memcpy(&log->group, data, sizeof(log->group)); + break; + case NFTNL_EXPR_LOG_SNAPLEN: + memcpy(&log->snaplen, data, sizeof(log->snaplen)); + break; + case NFTNL_EXPR_LOG_QTHRESHOLD: + memcpy(&log->qthreshold, data, sizeof(log->qthreshold)); + break; + case NFTNL_EXPR_LOG_LEVEL: + memcpy(&log->level, data, sizeof(log->level)); + break; + case NFTNL_EXPR_LOG_FLAGS: + memcpy(&log->flags, data, sizeof(log->flags)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_log_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_log *log = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_LOG_PREFIX: + *data_len = strlen(log->prefix)+1; + return log->prefix; + case NFTNL_EXPR_LOG_GROUP: + *data_len = sizeof(log->group); + return &log->group; + case NFTNL_EXPR_LOG_SNAPLEN: + *data_len = sizeof(log->snaplen); + return &log->snaplen; + case NFTNL_EXPR_LOG_QTHRESHOLD: + *data_len = sizeof(log->qthreshold); + return &log->qthreshold; + case NFTNL_EXPR_LOG_LEVEL: + *data_len = sizeof(log->level); + return &log->level; + case NFTNL_EXPR_LOG_FLAGS: + *data_len = sizeof(log->flags); + return &log->flags; + } + return NULL; +} + +static int nftnl_expr_log_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_LOG_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_LOG_PREFIX: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + abi_breakage(); + break; + case NFTA_LOG_GROUP: + case NFTA_LOG_QTHRESHOLD: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + abi_breakage(); + break; + case NFTA_LOG_SNAPLEN: + case NFTA_LOG_LEVEL: + case NFTA_LOG_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_log_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_log *log = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_LOG_PREFIX)) + mnl_attr_put_strz(nlh, NFTA_LOG_PREFIX, log->prefix); + if (e->flags & (1 << NFTNL_EXPR_LOG_GROUP)) + mnl_attr_put_u16(nlh, NFTA_LOG_GROUP, htons(log->group)); + if (e->flags & (1 << NFTNL_EXPR_LOG_SNAPLEN)) + mnl_attr_put_u32(nlh, NFTA_LOG_SNAPLEN, htonl(log->snaplen)); + if (e->flags & (1 << NFTNL_EXPR_LOG_QTHRESHOLD)) + mnl_attr_put_u16(nlh, NFTA_LOG_QTHRESHOLD, htons(log->qthreshold)); + if (e->flags & (1 << NFTNL_EXPR_LOG_LEVEL)) + mnl_attr_put_u32(nlh, NFTA_LOG_LEVEL, htonl(log->level)); + if (e->flags & (1 << NFTNL_EXPR_LOG_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_LOG_FLAGS, htonl(log->flags)); +} + +static int +nftnl_expr_log_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_log *log = nftnl_expr_data(e); + struct nlattr *tb[NFTA_LOG_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_log_cb, tb) < 0) + return -1; + + if (tb[NFTA_LOG_PREFIX]) { + if (log->prefix) + xfree(log->prefix); + + log->prefix = strdup(mnl_attr_get_str(tb[NFTA_LOG_PREFIX])); + if (!log->prefix) + return -1; + e->flags |= (1 << NFTNL_EXPR_LOG_PREFIX); + } + if (tb[NFTA_LOG_GROUP]) { + log->group = ntohs(mnl_attr_get_u16(tb[NFTA_LOG_GROUP])); + e->flags |= (1 << NFTNL_EXPR_LOG_GROUP); + } + if (tb[NFTA_LOG_SNAPLEN]) { + log->snaplen = ntohl(mnl_attr_get_u32(tb[NFTA_LOG_SNAPLEN])); + e->flags |= (1 << NFTNL_EXPR_LOG_SNAPLEN); + } + if (tb[NFTA_LOG_QTHRESHOLD]) { + log->qthreshold = ntohs(mnl_attr_get_u16(tb[NFTA_LOG_QTHRESHOLD])); + e->flags |= (1 << NFTNL_EXPR_LOG_QTHRESHOLD); + } + if (tb[NFTA_LOG_LEVEL]) { + log->level = ntohl(mnl_attr_get_u32(tb[NFTA_LOG_LEVEL])); + e->flags |= (1 << NFTNL_EXPR_LOG_LEVEL); + } + if (tb[NFTA_LOG_FLAGS]) { + log->flags = ntohl(mnl_attr_get_u32(tb[NFTA_LOG_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_LOG_FLAGS); + } + + return 0; +} + +static int +nftnl_expr_log_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_log *log = nftnl_expr_data(e); + int ret, offset = 0; + + if (e->flags & (1 << NFTNL_EXPR_LOG_PREFIX)) { + ret = snprintf(buf, remain, "prefix %s ", log->prefix); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_LOG_GROUP)) { + ret = snprintf(buf + offset, remain, + "group %u snaplen %u qthreshold %u ", + log->group, log->snaplen, log->qthreshold); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } else { + if (e->flags & (1 << NFTNL_EXPR_LOG_LEVEL)) { + ret = snprintf(buf + offset, remain, "level %u ", + log->level); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (e->flags & (1 << NFTNL_EXPR_LOG_FLAGS)) { + if (log->flags & NF_LOG_TCPSEQ) { + ret = snprintf(buf + offset, remain, "tcpseq "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (log->flags & NF_LOG_TCPOPT) { + ret = snprintf(buf + offset, remain, "tcpopt "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (log->flags & NF_LOG_IPOPT) { + ret = snprintf(buf + offset, remain, "ipopt "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (log->flags & NF_LOG_UID) { + ret = snprintf(buf + offset, remain, "uid "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (log->flags & NF_LOG_MACDECODE) { + ret = snprintf(buf + offset, remain, + "macdecode "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + } + } + + return offset; +} + +static void nftnl_expr_log_free(const struct nftnl_expr *e) +{ + struct nftnl_expr_log *log = nftnl_expr_data(e); + + xfree(log->prefix); +} + +struct expr_ops expr_ops_log = { + .name = "log", + .alloc_len = sizeof(struct nftnl_expr_log), + .max_attr = NFTA_LOG_MAX, + .free = nftnl_expr_log_free, + .set = nftnl_expr_log_set, + .get = nftnl_expr_log_get, + .parse = nftnl_expr_log_parse, + .build = nftnl_expr_log_build, + .output = nftnl_expr_log_snprintf, +}; diff --git a/src/expr/lookup.c b/src/expr/lookup.c new file mode 100644 index 0000000..a06c338 --- /dev/null +++ b/src/expr/lookup.c @@ -0,0 +1,210 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> /* for memcpy */ +#include <arpa/inet.h> +#include <errno.h> +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/rule.h> +#include <libnftnl/expr.h> + +struct nftnl_expr_lookup { + enum nft_registers sreg; + enum nft_registers dreg; + char *set_name; + uint32_t set_id; + uint32_t flags; +}; + +static int +nftnl_expr_lookup_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_lookup *lookup = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_LOOKUP_SREG: + memcpy(&lookup->sreg, data, sizeof(lookup->sreg)); + break; + case NFTNL_EXPR_LOOKUP_DREG: + memcpy(&lookup->dreg, data, sizeof(lookup->dreg)); + break; + case NFTNL_EXPR_LOOKUP_SET: + lookup->set_name = strdup((const char *)data); + if (!lookup->set_name) + return -1; + break; + case NFTNL_EXPR_LOOKUP_SET_ID: + memcpy(&lookup->set_id, data, sizeof(lookup->set_id)); + break; + case NFTNL_EXPR_LOOKUP_FLAGS: + memcpy(&lookup->flags, data, sizeof(lookup->flags)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_lookup_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_lookup *lookup = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_LOOKUP_SREG: + *data_len = sizeof(lookup->sreg); + return &lookup->sreg; + case NFTNL_EXPR_LOOKUP_DREG: + *data_len = sizeof(lookup->dreg); + return &lookup->dreg; + case NFTNL_EXPR_LOOKUP_SET: + *data_len = strlen(lookup->set_name) + 1; + return lookup->set_name; + case NFTNL_EXPR_LOOKUP_SET_ID: + *data_len = sizeof(lookup->set_id); + return &lookup->set_id; + case NFTNL_EXPR_LOOKUP_FLAGS: + *data_len = sizeof(lookup->flags); + return &lookup->flags; + } + return NULL; +} + +static int nftnl_expr_lookup_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_LOOKUP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_LOOKUP_SREG: + case NFTA_LOOKUP_DREG: + case NFTA_LOOKUP_SET_ID: + case NFTA_LOOKUP_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_LOOKUP_SET: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_lookup_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_lookup *lookup = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_LOOKUP_SREG)) + mnl_attr_put_u32(nlh, NFTA_LOOKUP_SREG, htonl(lookup->sreg)); + if (e->flags & (1 << NFTNL_EXPR_LOOKUP_DREG)) + mnl_attr_put_u32(nlh, NFTA_LOOKUP_DREG, htonl(lookup->dreg)); + if (e->flags & (1 << NFTNL_EXPR_LOOKUP_SET)) + mnl_attr_put_strz(nlh, NFTA_LOOKUP_SET, lookup->set_name); + if (e->flags & (1 << NFTNL_EXPR_LOOKUP_SET_ID)) + mnl_attr_put_u32(nlh, NFTA_LOOKUP_SET_ID, + htonl(lookup->set_id)); + if (e->flags & (1 << NFTNL_EXPR_LOOKUP_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_LOOKUP_FLAGS, htonl(lookup->flags)); +} + +static int +nftnl_expr_lookup_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_lookup *lookup = nftnl_expr_data(e); + struct nlattr *tb[NFTA_LOOKUP_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_lookup_cb, tb) < 0) + return -1; + + if (tb[NFTA_LOOKUP_SREG]) { + lookup->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_SREG])); + e->flags |= (1 << NFTNL_EXPR_LOOKUP_SREG); + } + if (tb[NFTA_LOOKUP_DREG]) { + lookup->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_DREG])); + e->flags |= (1 << NFTNL_EXPR_LOOKUP_DREG); + } + if (tb[NFTA_LOOKUP_SET]) { + lookup->set_name = + strdup(mnl_attr_get_str(tb[NFTA_LOOKUP_SET])); + if (!lookup->set_name) + return -1; + e->flags |= (1 << NFTNL_EXPR_LOOKUP_SET); + } + if (tb[NFTA_LOOKUP_SET_ID]) { + lookup->set_id = + ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_SET_ID])); + e->flags |= (1 << NFTNL_EXPR_LOOKUP_SET_ID); + } + if (tb[NFTA_LOOKUP_FLAGS]) { + lookup->flags = ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_LOOKUP_FLAGS); + } + + return ret; +} + +static int +nftnl_expr_lookup_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_lookup *l = nftnl_expr_data(e); + int offset = 0, ret; + + ret = snprintf(buf, remain, "reg %u set %s ", l->sreg, l->set_name); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + if (e->flags & (1 << NFTNL_EXPR_LOOKUP_DREG)) { + ret = snprintf(buf + offset, remain, "dreg %u ", l->dreg); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_LOOKUP_FLAGS)) { + ret = snprintf(buf + offset, remain, "0x%x ", l->flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +static void nftnl_expr_lookup_free(const struct nftnl_expr *e) +{ + struct nftnl_expr_lookup *lookup = nftnl_expr_data(e); + + xfree(lookup->set_name); +} + +struct expr_ops expr_ops_lookup = { + .name = "lookup", + .alloc_len = sizeof(struct nftnl_expr_lookup), + .max_attr = NFTA_LOOKUP_MAX, + .free = nftnl_expr_lookup_free, + .set = nftnl_expr_lookup_set, + .get = nftnl_expr_lookup_get, + .parse = nftnl_expr_lookup_parse, + .build = nftnl_expr_lookup_build, + .output = nftnl_expr_lookup_snprintf, +}; diff --git a/src/expr/masq.c b/src/expr/masq.c new file mode 100644 index 0000000..e6e528d --- /dev/null +++ b/src/expr/masq.c @@ -0,0 +1,167 @@ +/* + * (C) 2014 by Arturo Borrero Gonzalez <arturo@debian.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <stdio.h> +#include <stdint.h> +#include <arpa/inet.h> +#include <errno.h> +#include <inttypes.h> + +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_masq { + uint32_t flags; + enum nft_registers sreg_proto_min; + enum nft_registers sreg_proto_max; +}; + +static int +nftnl_expr_masq_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_masq *masq = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_MASQ_FLAGS: + memcpy(&masq->flags, data, sizeof(masq->flags)); + break; + case NFTNL_EXPR_MASQ_REG_PROTO_MIN: + memcpy(&masq->sreg_proto_min, data, sizeof(masq->sreg_proto_min)); + break; + case NFTNL_EXPR_MASQ_REG_PROTO_MAX: + memcpy(&masq->sreg_proto_max, data, sizeof(masq->sreg_proto_max)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_masq_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_masq *masq = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_MASQ_FLAGS: + *data_len = sizeof(masq->flags); + return &masq->flags; + case NFTNL_EXPR_MASQ_REG_PROTO_MIN: + *data_len = sizeof(masq->sreg_proto_min); + return &masq->sreg_proto_min; + case NFTNL_EXPR_MASQ_REG_PROTO_MAX: + *data_len = sizeof(masq->sreg_proto_max); + return &masq->sreg_proto_max; + } + return NULL; +} + +static int nftnl_expr_masq_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_MASQ_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_MASQ_REG_PROTO_MIN: + case NFTA_MASQ_REG_PROTO_MAX: + case NFTA_MASQ_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_masq_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_masq *masq = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_MASQ_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_MASQ_FLAGS, htobe32(masq->flags)); + if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MIN)) + mnl_attr_put_u32(nlh, NFTA_MASQ_REG_PROTO_MIN, + htobe32(masq->sreg_proto_min)); + if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MAX)) + mnl_attr_put_u32(nlh, NFTA_MASQ_REG_PROTO_MAX, + htobe32(masq->sreg_proto_max)); +} + +static int +nftnl_expr_masq_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_masq *masq = nftnl_expr_data(e); + struct nlattr *tb[NFTA_MASQ_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_masq_cb, tb) < 0) + return -1; + + if (tb[NFTA_MASQ_FLAGS]) { + masq->flags = be32toh(mnl_attr_get_u32(tb[NFTA_MASQ_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_MASQ_FLAGS); + } + if (tb[NFTA_MASQ_REG_PROTO_MIN]) { + masq->sreg_proto_min = + be32toh(mnl_attr_get_u32(tb[NFTA_MASQ_REG_PROTO_MIN])); + e->flags |= (1 << NFTNL_EXPR_MASQ_REG_PROTO_MIN); + } + if (tb[NFTA_MASQ_REG_PROTO_MAX]) { + masq->sreg_proto_max = + be32toh(mnl_attr_get_u32(tb[NFTA_MASQ_REG_PROTO_MAX])); + e->flags |= (1 << NFTNL_EXPR_MASQ_REG_PROTO_MAX); + } + + return 0; +} + +static int nftnl_expr_masq_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_masq *masq = nftnl_expr_data(e); + int offset = 0, ret = 0; + + if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MIN)) { + ret = snprintf(buf + offset, remain, "proto_min reg %u ", + masq->sreg_proto_min); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MAX)) { + ret = snprintf(buf + offset, remain, "proto_max reg %u ", + masq->sreg_proto_max); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (e->flags & (1 << NFTNL_EXPR_MASQ_FLAGS)) { + ret = snprintf(buf + offset, remain, "flags 0x%x ", masq->flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +struct expr_ops expr_ops_masq = { + .name = "masq", + .alloc_len = sizeof(struct nftnl_expr_masq), + .max_attr = NFTA_MASQ_MAX, + .set = nftnl_expr_masq_set, + .get = nftnl_expr_masq_get, + .parse = nftnl_expr_masq_parse, + .build = nftnl_expr_masq_build, + .output = nftnl_expr_masq_snprintf, +}; diff --git a/src/expr/match.c b/src/expr/match.c new file mode 100644 index 0000000..f472add --- /dev/null +++ b/src/expr/match.c @@ -0,0 +1,193 @@ +/* + * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> /* for memcpy */ +#include <arpa/inet.h> +#include <errno.h> +#include <libmnl/libmnl.h> + +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_tables_compat.h> + +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +/* From include/linux/netfilter/x_tables.h */ +#define XT_EXTENSION_MAXNAMELEN 29 + +struct nftnl_expr_match { + char name[XT_EXTENSION_MAXNAMELEN]; + uint32_t rev; + uint32_t data_len; + const void *data; +}; + +static int +nftnl_expr_match_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_match *mt = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_MT_NAME: + snprintf(mt->name, sizeof(mt->name), "%.*s", data_len, + (const char *)data); + break; + case NFTNL_EXPR_MT_REV: + memcpy(&mt->rev, data, sizeof(mt->rev)); + break; + case NFTNL_EXPR_MT_INFO: + if (e->flags & (1 << NFTNL_EXPR_MT_INFO)) + xfree(mt->data); + + mt->data = data; + mt->data_len = data_len; + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_match_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_match *mt = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_MT_NAME: + *data_len = sizeof(mt->name); + return mt->name; + case NFTNL_EXPR_MT_REV: + *data_len = sizeof(mt->rev); + return &mt->rev; + case NFTNL_EXPR_MT_INFO: + *data_len = mt->data_len; + return mt->data; + } + return NULL; +} + +static int nftnl_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) + abi_breakage(); + break; + case NFTA_MATCH_REV: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_MATCH_INFO: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_match_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_match *mt = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_MT_NAME)) + mnl_attr_put_strz(nlh, NFTA_MATCH_NAME, mt->name); + if (e->flags & (1 << NFTNL_EXPR_MT_REV)) + mnl_attr_put_u32(nlh, NFTA_MATCH_REV, htonl(mt->rev)); + if (e->flags & (1 << NFTNL_EXPR_MT_INFO)) + mnl_attr_put(nlh, NFTA_MATCH_INFO, mt->data_len, mt->data); +} + +static int nftnl_expr_match_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_match *match = nftnl_expr_data(e); + struct nlattr *tb[NFTA_MATCH_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_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 << NFTNL_EXPR_MT_NAME); + } + + if (tb[NFTA_MATCH_REV]) { + match->rev = ntohl(mnl_attr_get_u32(tb[NFTA_MATCH_REV])); + e->flags |= (1 << NFTNL_EXPR_MT_REV); + } + + if (tb[NFTA_MATCH_INFO]) { + uint32_t len = mnl_attr_get_payload_len(tb[NFTA_MATCH_INFO]); + void *match_data; + + if (e->flags & (1 << NFTNL_EXPR_MT_INFO)) + xfree(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 << NFTNL_EXPR_MT_INFO); + } + + return 0; +} + +static int +nftnl_expr_match_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_match *match = nftnl_expr_data(e); + + return snprintf(buf, len, "name %s rev %u ", match->name, match->rev); +} + +static void nftnl_expr_match_free(const struct nftnl_expr *e) +{ + struct nftnl_expr_match *match = nftnl_expr_data(e); + + xfree(match->data); +} + +struct expr_ops expr_ops_match = { + .name = "match", + .alloc_len = sizeof(struct nftnl_expr_match), + .max_attr = NFTA_MATCH_MAX, + .free = nftnl_expr_match_free, + .set = nftnl_expr_match_set, + .get = nftnl_expr_match_get, + .parse = nftnl_expr_match_parse, + .build = nftnl_expr_match_build, + .output = nftnl_expr_match_snprintf, +}; diff --git a/src/expr/meta.c b/src/expr/meta.c new file mode 100644 index 0000000..183f441 --- /dev/null +++ b/src/expr/meta.c @@ -0,0 +1,221 @@ +/* + * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +#ifndef NFT_META_MAX +#define NFT_META_MAX (NFT_META_BRI_BROUTE + 1) +#endif + +struct nftnl_expr_meta { + enum nft_meta_keys key; + enum nft_registers dreg; + enum nft_registers sreg; +}; + +static int +nftnl_expr_meta_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_meta *meta = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_META_KEY: + memcpy(&meta->key, data, sizeof(meta->key)); + break; + case NFTNL_EXPR_META_DREG: + memcpy(&meta->dreg, data, sizeof(meta->dreg)); + break; + case NFTNL_EXPR_META_SREG: + memcpy(&meta->sreg, data, sizeof(meta->sreg)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_meta_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_meta *meta = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_META_KEY: + *data_len = sizeof(meta->key); + return &meta->key; + case NFTNL_EXPR_META_DREG: + *data_len = sizeof(meta->dreg); + return &meta->dreg; + case NFTNL_EXPR_META_SREG: + *data_len = sizeof(meta->sreg); + return &meta->sreg; + } + return NULL; +} + +static int nftnl_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: + case NFTA_META_SREG: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_meta_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_meta *meta = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_META_KEY)) + mnl_attr_put_u32(nlh, NFTA_META_KEY, htonl(meta->key)); + if (e->flags & (1 << NFTNL_EXPR_META_DREG)) + mnl_attr_put_u32(nlh, NFTA_META_DREG, htonl(meta->dreg)); + if (e->flags & (1 << NFTNL_EXPR_META_SREG)) + mnl_attr_put_u32(nlh, NFTA_META_SREG, htonl(meta->sreg)); +} + +static int +nftnl_expr_meta_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_meta *meta = nftnl_expr_data(e); + struct nlattr *tb[NFTA_META_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_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 << NFTNL_EXPR_META_KEY); + } + if (tb[NFTA_META_DREG]) { + meta->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_META_DREG])); + e->flags |= (1 << NFTNL_EXPR_META_DREG); + } + if (tb[NFTA_META_SREG]) { + meta->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_META_SREG])); + e->flags |= (1 << NFTNL_EXPR_META_SREG); + } + + return 0; +} + +static const char *meta_key2str_array[NFT_META_MAX] = { + [NFT_META_LEN] = "len", + [NFT_META_PROTOCOL] = "protocol", + [NFT_META_NFPROTO] = "nfproto", + [NFT_META_L4PROTO] = "l4proto", + [NFT_META_PRIORITY] = "priority", + [NFT_META_MARK] = "mark", + [NFT_META_IIF] = "iif", + [NFT_META_OIF] = "oif", + [NFT_META_IIFNAME] = "iifname", + [NFT_META_OIFNAME] = "oifname", + [NFT_META_IIFTYPE] = "iiftype", + [NFT_META_OIFTYPE] = "oiftype", + [NFT_META_SKUID] = "skuid", + [NFT_META_SKGID] = "skgid", + [NFT_META_NFTRACE] = "nftrace", + [NFT_META_RTCLASSID] = "rtclassid", + [NFT_META_SECMARK] = "secmark", + [NFT_META_BRI_IIFNAME] = "bri_iifname", + [NFT_META_BRI_OIFNAME] = "bri_oifname", + [NFT_META_PKTTYPE] = "pkttype", + [NFT_META_CPU] = "cpu", + [NFT_META_IIFGROUP] = "iifgroup", + [NFT_META_OIFGROUP] = "oifgroup", + [NFT_META_CGROUP] = "cgroup", + [NFT_META_PRANDOM] = "prandom", + [NFT_META_SECPATH] = "secpath", + [NFT_META_IIFKIND] = "iifkind", + [NFT_META_OIFKIND] = "oifkind", + [NFT_META_BRI_IIFPVID] = "bri_iifpvid", + [NFT_META_BRI_IIFVPROTO] = "bri_iifvproto", + [NFT_META_TIME_NS] = "time", + [NFT_META_TIME_DAY] = "day", + [NFT_META_TIME_HOUR] = "hour", + [NFT_META_SDIF] = "sdif", + [NFT_META_SDIFNAME] = "sdifname", + [NFT_META_BRI_BROUTE] = "broute", +}; + +static const char *meta_key2str(uint8_t key) +{ + if (key < NFT_META_MAX) + return meta_key2str_array[key]; + + return "unknown"; +} + +static inline int str2meta_key(const char *str) +{ + int i; + + for (i = 0; i < NFT_META_MAX; i++) { + if (strcmp(str, meta_key2str_array[i]) == 0) + return i; + } + + errno = EINVAL; + return -1; +} + +static int +nftnl_expr_meta_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_meta *meta = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_META_SREG)) { + return snprintf(buf, len, "set %s with reg %u ", + meta_key2str(meta->key), meta->sreg); + } + if (e->flags & (1 << NFTNL_EXPR_META_DREG)) { + return snprintf(buf, len, "load %s => reg %u ", + meta_key2str(meta->key), meta->dreg); + } + return 0; +} + +struct expr_ops expr_ops_meta = { + .name = "meta", + .alloc_len = sizeof(struct nftnl_expr_meta), + .max_attr = NFTA_META_MAX, + .set = nftnl_expr_meta_set, + .get = nftnl_expr_meta_get, + .parse = nftnl_expr_meta_parse, + .build = nftnl_expr_meta_build, + .output = nftnl_expr_meta_snprintf, +}; diff --git a/src/expr/nat.c b/src/expr/nat.c new file mode 100644 index 0000000..ca727be --- /dev/null +++ b/src/expr/nat.c @@ -0,0 +1,278 @@ +/* + * (C) 2012-2014 Pablo Neira Ayuso <pablo@netfilter.org> + * (C) 2012 Intel Corporation + * + * 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. + * + * Authors: + * Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com> + */ + +#include "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <limits.h> +#include <string.h> +#include <errno.h> +#include <arpa/inet.h> +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_nat { + enum nft_registers sreg_addr_min; + enum nft_registers sreg_addr_max; + enum nft_registers sreg_proto_min; + enum nft_registers sreg_proto_max; + int family; + enum nft_nat_types type; + uint32_t flags; +}; + +static int +nftnl_expr_nat_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_nat *nat = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_NAT_TYPE: + memcpy(&nat->type, data, sizeof(nat->type)); + break; + case NFTNL_EXPR_NAT_FAMILY: + memcpy(&nat->family, data, sizeof(nat->family)); + break; + case NFTNL_EXPR_NAT_REG_ADDR_MIN: + memcpy(&nat->sreg_addr_min, data, sizeof(nat->sreg_addr_min)); + break; + case NFTNL_EXPR_NAT_REG_ADDR_MAX: + memcpy(&nat->sreg_addr_max, data, sizeof(nat->sreg_addr_max)); + break; + case NFTNL_EXPR_NAT_REG_PROTO_MIN: + memcpy(&nat->sreg_proto_min, data, sizeof(nat->sreg_proto_min)); + break; + case NFTNL_EXPR_NAT_REG_PROTO_MAX: + memcpy(&nat->sreg_proto_max, data, sizeof(nat->sreg_proto_max)); + break; + case NFTNL_EXPR_NAT_FLAGS: + memcpy(&nat->flags, data, sizeof(nat->flags)); + break; + default: + return -1; + } + + return 0; +} + +static const void * +nftnl_expr_nat_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_nat *nat = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_NAT_TYPE: + *data_len = sizeof(nat->type); + return &nat->type; + case NFTNL_EXPR_NAT_FAMILY: + *data_len = sizeof(nat->family); + return &nat->family; + case NFTNL_EXPR_NAT_REG_ADDR_MIN: + *data_len = sizeof(nat->sreg_addr_min); + return &nat->sreg_addr_min; + case NFTNL_EXPR_NAT_REG_ADDR_MAX: + *data_len = sizeof(nat->sreg_addr_max); + return &nat->sreg_addr_max; + case NFTNL_EXPR_NAT_REG_PROTO_MIN: + *data_len = sizeof(nat->sreg_proto_min); + return &nat->sreg_proto_min; + case NFTNL_EXPR_NAT_REG_PROTO_MAX: + *data_len = sizeof(nat->sreg_proto_max); + return &nat->sreg_proto_max; + case NFTNL_EXPR_NAT_FLAGS: + *data_len = sizeof(nat->flags); + return &nat->flags; + } + return NULL; +} + +static int nftnl_expr_nat_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_NAT_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_NAT_TYPE: + case NFTA_NAT_FAMILY: + case NFTA_NAT_REG_ADDR_MIN: + case NFTA_NAT_REG_ADDR_MAX: + case NFTA_NAT_REG_PROTO_MIN: + case NFTA_NAT_REG_PROTO_MAX: + case NFTA_NAT_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nftnl_expr_nat_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_nat *nat = nftnl_expr_data(e); + struct nlattr *tb[NFTA_NAT_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_nat_cb, tb) < 0) + return -1; + + if (tb[NFTA_NAT_TYPE]) { + nat->type = ntohl(mnl_attr_get_u32(tb[NFTA_NAT_TYPE])); + e->flags |= (1 << NFTNL_EXPR_NAT_TYPE); + } + if (tb[NFTA_NAT_FAMILY]) { + nat->family = ntohl(mnl_attr_get_u32(tb[NFTA_NAT_FAMILY])); + e->flags |= (1 << NFTNL_EXPR_NAT_FAMILY); + } + if (tb[NFTA_NAT_REG_ADDR_MIN]) { + nat->sreg_addr_min = + ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_ADDR_MIN])); + e->flags |= (1 << NFTNL_EXPR_NAT_REG_ADDR_MIN); + } + if (tb[NFTA_NAT_REG_ADDR_MAX]) { + nat->sreg_addr_max = + ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_ADDR_MAX])); + e->flags |= (1 << NFTNL_EXPR_NAT_REG_ADDR_MAX); + } + if (tb[NFTA_NAT_REG_PROTO_MIN]) { + nat->sreg_proto_min = + ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_PROTO_MIN])); + e->flags |= (1 << NFTNL_EXPR_NAT_REG_PROTO_MIN); + } + if (tb[NFTA_NAT_REG_PROTO_MAX]) { + nat->sreg_proto_max = + ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_PROTO_MAX])); + e->flags |= (1 << NFTNL_EXPR_NAT_REG_PROTO_MAX); + } + if (tb[NFTA_NAT_FLAGS]) { + nat->flags = ntohl(mnl_attr_get_u32(tb[NFTA_NAT_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_NAT_FLAGS); + } + + return 0; +} + +static void +nftnl_expr_nat_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_nat *nat = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_NAT_TYPE)) + mnl_attr_put_u32(nlh, NFTA_NAT_TYPE, htonl(nat->type)); + if (e->flags & (1 << NFTNL_EXPR_NAT_FAMILY)) + mnl_attr_put_u32(nlh, NFTA_NAT_FAMILY, htonl(nat->family)); + if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MIN)) + mnl_attr_put_u32(nlh, NFTA_NAT_REG_ADDR_MIN, + htonl(nat->sreg_addr_min)); + if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MAX)) + mnl_attr_put_u32(nlh, NFTA_NAT_REG_ADDR_MAX, + htonl(nat->sreg_addr_max)); + if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MIN)) + mnl_attr_put_u32(nlh, NFTA_NAT_REG_PROTO_MIN, + htonl(nat->sreg_proto_min)); + if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MAX)) + mnl_attr_put_u32(nlh, NFTA_NAT_REG_PROTO_MAX, + htonl(nat->sreg_proto_max)); + if (e->flags & (1 << NFTNL_EXPR_NAT_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_NAT_FLAGS, htonl(nat->flags)); +} + +static inline const char *nat2str(uint16_t nat) +{ + switch (nat) { + case NFT_NAT_SNAT: + return "snat"; + case NFT_NAT_DNAT: + return "dnat"; + default: + return "unknown"; + } +} + +static inline int nftnl_str2nat(const char *nat) +{ + if (strcmp(nat, "snat") == 0) + return NFT_NAT_SNAT; + else if (strcmp(nat, "dnat") == 0) + return NFT_NAT_DNAT; + else { + errno = EINVAL; + return -1; + } +} + +static int +nftnl_expr_nat_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_nat *nat = nftnl_expr_data(e); + int offset = 0, ret = 0; + + ret = snprintf(buf, remain, "%s ", nat2str(nat->type)); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = snprintf(buf + offset, remain, "%s ", + nftnl_family2str(nat->family)); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MIN)) { + ret = snprintf(buf + offset, remain, + "addr_min reg %u ", nat->sreg_addr_min); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MAX)) { + ret = snprintf(buf + offset, remain, + "addr_max reg %u ", nat->sreg_addr_max); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MIN)) { + ret = snprintf(buf + offset, remain, + "proto_min reg %u ", nat->sreg_proto_min); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MAX)) { + ret = snprintf(buf + offset, remain, + "proto_max reg %u ", nat->sreg_proto_max); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_NAT_FLAGS)) { + ret = snprintf(buf + offset, remain, "flags 0x%x ", nat->flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +struct expr_ops expr_ops_nat = { + .name = "nat", + .alloc_len = sizeof(struct nftnl_expr_nat), + .max_attr = NFTA_NAT_MAX, + .set = nftnl_expr_nat_set, + .get = nftnl_expr_nat_get, + .parse = nftnl_expr_nat_parse, + .build = nftnl_expr_nat_build, + .output = nftnl_expr_nat_snprintf, +}; diff --git a/src/expr/numgen.c b/src/expr/numgen.c new file mode 100644 index 0000000..d4020a6 --- /dev/null +++ b/src/expr/numgen.c @@ -0,0 +1,184 @@ +/* + * (C) 2016 by Laura Garcia <nevola@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. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_ng { + enum nft_registers dreg; + unsigned int modulus; + enum nft_ng_types type; + unsigned int offset; +}; + +static int +nftnl_expr_ng_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_ng *ng = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_NG_DREG: + memcpy(&ng->dreg, data, sizeof(ng->dreg)); + break; + case NFTNL_EXPR_NG_MODULUS: + memcpy(&ng->modulus, data, sizeof(ng->modulus)); + break; + case NFTNL_EXPR_NG_TYPE: + memcpy(&ng->type, data, sizeof(ng->type)); + break; + case NFTNL_EXPR_NG_OFFSET: + memcpy(&ng->offset, data, sizeof(ng->offset)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_ng_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_ng *ng = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_NG_DREG: + *data_len = sizeof(ng->dreg); + return &ng->dreg; + case NFTNL_EXPR_NG_MODULUS: + *data_len = sizeof(ng->modulus); + return &ng->modulus; + case NFTNL_EXPR_NG_TYPE: + *data_len = sizeof(ng->type); + return &ng->type; + case NFTNL_EXPR_NG_OFFSET: + *data_len = sizeof(ng->offset); + return &ng->offset; + } + return NULL; +} + +static int nftnl_expr_ng_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_NG_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_NG_DREG: + case NFTA_NG_MODULUS: + case NFTA_NG_TYPE: + case NFTA_NG_OFFSET: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_ng_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_ng *ng = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_NG_DREG)) + mnl_attr_put_u32(nlh, NFTA_NG_DREG, htonl(ng->dreg)); + if (e->flags & (1 << NFTNL_EXPR_NG_MODULUS)) + mnl_attr_put_u32(nlh, NFTA_NG_MODULUS, htonl(ng->modulus)); + if (e->flags & (1 << NFTNL_EXPR_NG_TYPE)) + mnl_attr_put_u32(nlh, NFTA_NG_TYPE, htonl(ng->type)); + if (e->flags & (1 << NFTNL_EXPR_NG_OFFSET)) + mnl_attr_put_u32(nlh, NFTA_NG_OFFSET, htonl(ng->offset)); +} + +static int +nftnl_expr_ng_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_ng *ng = nftnl_expr_data(e); + struct nlattr *tb[NFTA_NG_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_ng_cb, tb) < 0) + return -1; + + if (tb[NFTA_NG_DREG]) { + ng->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_NG_DREG])); + e->flags |= (1 << NFTNL_EXPR_NG_DREG); + } + if (tb[NFTA_NG_MODULUS]) { + ng->modulus = ntohl(mnl_attr_get_u32(tb[NFTA_NG_MODULUS])); + e->flags |= (1 << NFTNL_EXPR_NG_MODULUS); + } + if (tb[NFTA_NG_TYPE]) { + ng->type = ntohl(mnl_attr_get_u32(tb[NFTA_NG_TYPE])); + e->flags |= (1 << NFTNL_EXPR_NG_TYPE); + } + if (tb[NFTA_NG_OFFSET]) { + ng->offset = ntohl(mnl_attr_get_u32(tb[NFTA_NG_OFFSET])); + e->flags |= (1 << NFTNL_EXPR_NG_OFFSET); + } + + return ret; +} + +static int +nftnl_expr_ng_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_ng *ng = nftnl_expr_data(e); + int offset = 0, ret; + + switch (ng->type) { + case NFT_NG_INCREMENTAL: + ret = snprintf(buf, remain, "reg %u = inc mod %u ", + ng->dreg, ng->modulus); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + break; + case NFT_NG_RANDOM: + ret = snprintf(buf, remain, "reg %u = random mod %u ", + ng->dreg, ng->modulus); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + break; + default: + return 0; + } + + if (ng->offset) { + ret = snprintf(buf + offset, remain, "offset %u ", ng->offset); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +struct expr_ops expr_ops_ng = { + .name = "numgen", + .alloc_len = sizeof(struct nftnl_expr_ng), + .max_attr = NFTA_NG_MAX, + .set = nftnl_expr_ng_set, + .get = nftnl_expr_ng_get, + .parse = nftnl_expr_ng_parse, + .build = nftnl_expr_ng_build, + .output = nftnl_expr_ng_snprintf, +}; diff --git a/src/expr/objref.c b/src/expr/objref.c new file mode 100644 index 0000000..ad0688f --- /dev/null +++ b/src/expr/objref.c @@ -0,0 +1,209 @@ +/* + * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <stdio.h> +#include <stdint.h> +#include <arpa/inet.h> +#include <errno.h> +#include <inttypes.h> + +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_objref { + struct { + uint32_t type; + const char *name; + } imm; + struct { + uint32_t sreg; + const char *name; + uint32_t id; + } set; +}; + +static int nftnl_expr_objref_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_objref *objref = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_OBJREF_IMM_TYPE: + memcpy(&objref->imm.type, data, sizeof(objref->imm.type)); + break; + case NFTNL_EXPR_OBJREF_IMM_NAME: + objref->imm.name = strdup(data); + if (!objref->imm.name) + return -1; + break; + case NFTNL_EXPR_OBJREF_SET_SREG: + memcpy(&objref->set.sreg, data, sizeof(objref->set.sreg)); + break; + case NFTNL_EXPR_OBJREF_SET_NAME: + objref->set.name = strdup(data); + if (!objref->set.name) + return -1; + break; + case NFTNL_EXPR_OBJREF_SET_ID: + memcpy(&objref->set.id, data, sizeof(objref->set.id)); + break; + default: + return -1; + } + return 0; +} + +static const void *nftnl_expr_objref_get(const struct nftnl_expr *e, + uint16_t type, uint32_t *data_len) +{ + struct nftnl_expr_objref *objref = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_OBJREF_IMM_TYPE: + *data_len = sizeof(objref->imm.type); + return &objref->imm.type; + case NFTNL_EXPR_OBJREF_IMM_NAME: + *data_len = strlen(objref->imm.name) + 1; + return objref->imm.name; + case NFTNL_EXPR_OBJREF_SET_SREG: + *data_len = sizeof(objref->set.sreg); + return &objref->set.sreg; + case NFTNL_EXPR_OBJREF_SET_NAME: + *data_len = strlen(objref->set.name) + 1; + return objref->set.name; + case NFTNL_EXPR_OBJREF_SET_ID: + *data_len = sizeof(objref->set.id); + return &objref->set.id; + } + return NULL; +} + +static int nftnl_expr_objref_cb(const struct nlattr *attr, void *data) +{ + int type = mnl_attr_get_type(attr); + const struct nlattr **tb = data; + + if (mnl_attr_type_valid(attr, NFTA_OBJREF_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_OBJREF_IMM_TYPE: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_OBJREF_IMM_NAME: + case NFTA_OBJREF_SET_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + abi_breakage(); + break; + case NFTA_OBJREF_SET_SREG: + case NFTA_OBJREF_SET_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void nftnl_expr_objref_build(struct nlmsghdr *nlh, + const struct nftnl_expr *e) +{ + struct nftnl_expr_objref *objref = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_OBJREF_IMM_TYPE)) + mnl_attr_put_u32(nlh, NFTA_OBJREF_IMM_TYPE, + htonl(objref->imm.type)); + if (e->flags & (1 << NFTNL_EXPR_OBJREF_IMM_NAME)) + mnl_attr_put_str(nlh, NFTA_OBJREF_IMM_NAME, objref->imm.name); + if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_SREG)) + mnl_attr_put_u32(nlh, NFTA_OBJREF_SET_SREG, + htonl(objref->set.sreg)); + if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_NAME)) + mnl_attr_put_str(nlh, NFTA_OBJREF_SET_NAME, objref->set.name); + if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_ID)) + mnl_attr_put_u32(nlh, NFTA_OBJREF_SET_ID, + htonl(objref->set.id)); +} + +static int nftnl_expr_objref_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_objref *objref = nftnl_expr_data(e); + struct nlattr *tb[NFTA_OBJREF_MAX + 1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_objref_cb, tb) < 0) + return -1; + + if (tb[NFTA_OBJREF_IMM_TYPE]) { + objref->imm.type = + ntohl(mnl_attr_get_u32(tb[NFTA_OBJREF_IMM_TYPE])); + e->flags |= (1 << NFTNL_EXPR_OBJREF_IMM_TYPE); + } + if (tb[NFTA_OBJREF_IMM_NAME]) { + objref->imm.name = + strdup(mnl_attr_get_str(tb[NFTA_OBJREF_IMM_NAME])); + e->flags |= (1 << NFTNL_EXPR_OBJREF_IMM_NAME); + } + if (tb[NFTA_OBJREF_SET_SREG]) { + objref->set.sreg = + ntohl(mnl_attr_get_u32(tb[NFTA_OBJREF_SET_SREG])); + e->flags |= (1 << NFTNL_EXPR_OBJREF_SET_SREG); + } + if (tb[NFTA_OBJREF_SET_NAME]) { + objref->set.name = + strdup(mnl_attr_get_str(tb[NFTA_OBJREF_SET_NAME])); + e->flags |= (1 << NFTNL_EXPR_OBJREF_SET_NAME); + } + if (tb[NFTA_OBJREF_SET_ID]) { + objref->set.id = + ntohl(mnl_attr_get_u32(tb[NFTA_OBJREF_SET_ID])); + e->flags |= (1 << NFTNL_EXPR_OBJREF_SET_ID); + } + + return 0; +} + +static int nftnl_expr_objref_snprintf(char *buf, size_t len, + uint32_t flags, + const struct nftnl_expr *e) +{ + struct nftnl_expr_objref *objref = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_SREG)) + return snprintf(buf, len, "sreg %u set %s ", + objref->set.sreg, objref->set.name); + else + return snprintf(buf, len, "type %u name %s ", + objref->imm.type, objref->imm.name); +} + +static void nftnl_expr_objref_free(const struct nftnl_expr *e) +{ + struct nftnl_expr_objref *objref = nftnl_expr_data(e); + + xfree(objref->imm.name); + xfree(objref->set.name); +} + +struct expr_ops expr_ops_objref = { + .name = "objref", + .alloc_len = sizeof(struct nftnl_expr_objref), + .max_attr = NFTA_OBJREF_MAX, + .free = nftnl_expr_objref_free, + .set = nftnl_expr_objref_set, + .get = nftnl_expr_objref_get, + .parse = nftnl_expr_objref_parse, + .build = nftnl_expr_objref_build, + .output = nftnl_expr_objref_snprintf, +}; diff --git a/src/expr/osf.c b/src/expr/osf.c new file mode 100644 index 0000000..f15a722 --- /dev/null +++ b/src/expr/osf.c @@ -0,0 +1,151 @@ +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +#define OSF_GENRE_SIZE 32 + +struct nftnl_expr_osf { + enum nft_registers dreg; + uint8_t ttl; + uint32_t flags; +}; + +static int nftnl_expr_osf_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_osf *osf = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_OSF_DREG: + memcpy(&osf->dreg, data, sizeof(osf->dreg)); + break; + case NFTNL_EXPR_OSF_TTL: + memcpy(&osf->ttl, data, sizeof(osf->ttl)); + break; + case NFTNL_EXPR_OSF_FLAGS: + memcpy(&osf->flags, data, sizeof(osf->flags)); + break; + } + return 0; +} + +static const void * +nftnl_expr_osf_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_osf *osf = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_OSF_DREG: + *data_len = sizeof(osf->dreg); + return &osf->dreg; + case NFTNL_EXPR_OSF_TTL: + *data_len = sizeof(osf->ttl); + return &osf->ttl; + case NFTNL_EXPR_OSF_FLAGS: + *data_len = sizeof(osf->flags); + return &osf->flags; + } + return NULL; +} + +static int nftnl_expr_osf_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_OSF_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTNL_EXPR_OSF_DREG: + case NFTNL_EXPR_OSF_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + + case NFTNL_EXPR_OSF_TTL: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + abi_breakage(); + break; + + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_osf_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_osf *osf = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_OSF_DREG)) + mnl_attr_put_u32(nlh, NFTNL_EXPR_OSF_DREG, htonl(osf->dreg)); + if (e->flags & (1 << NFTNL_EXPR_OSF_TTL)) + mnl_attr_put_u8(nlh, NFTNL_EXPR_OSF_TTL, osf->ttl); + if (e->flags & (1 << NFTNL_EXPR_OSF_FLAGS)) + if (osf->flags) + mnl_attr_put_u32(nlh, NFTNL_EXPR_OSF_FLAGS, htonl(osf->flags)); +} + +static int +nftnl_expr_osf_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_osf *osf = nftnl_expr_data(e); + struct nlattr *tb[NFTA_OSF_MAX + 1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_osf_cb, tb) < 0) + return -1; + + if (tb[NFTA_OSF_DREG]) { + osf->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_OSF_DREG])); + e->flags |= (1 << NFTNL_EXPR_OSF_DREG); + } + + if (tb[NFTA_OSF_TTL]) { + osf->ttl = mnl_attr_get_u8(tb[NFTA_OSF_TTL]); + e->flags |= (1 << NFTNL_EXPR_OSF_TTL); + } + + if (tb[NFTA_OSF_FLAGS]) { + osf->flags = ntohl(mnl_attr_get_u32(tb[NFTA_OSF_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_OSF_FLAGS); + } + + return 0; +} + +static int +nftnl_expr_osf_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_osf *osf = nftnl_expr_data(e); + int ret, offset = 0; + + if (e->flags & (1 << NFTNL_EXPR_OSF_DREG)) { + ret = snprintf(buf, len, "dreg %u ", osf->dreg); + SNPRINTF_BUFFER_SIZE(ret, len, offset); + } + + return offset; +} + +struct expr_ops expr_ops_osf = { + .name = "osf", + .alloc_len = sizeof(struct nftnl_expr_osf), + .max_attr = NFTA_OSF_MAX, + .set = nftnl_expr_osf_set, + .get = nftnl_expr_osf_get, + .parse = nftnl_expr_osf_parse, + .build = nftnl_expr_osf_build, + .output = nftnl_expr_osf_snprintf, +}; diff --git a/src/expr/payload.c b/src/expr/payload.c new file mode 100644 index 0000000..c633e33 --- /dev/null +++ b/src/expr/payload.c @@ -0,0 +1,250 @@ +/* + * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <limits.h> +#include <arpa/inet.h> +#include <errno.h> +#include <libmnl/libmnl.h> + +#include <linux/netfilter/nf_tables.h> + +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_payload { + enum nft_registers sreg; + enum nft_registers dreg; + enum nft_payload_bases base; + uint32_t offset; + uint32_t len; + uint32_t csum_type; + uint32_t csum_offset; + uint32_t csum_flags; +}; + +static int +nftnl_expr_payload_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_payload *payload = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_PAYLOAD_SREG: + memcpy(&payload->sreg, data, sizeof(payload->sreg)); + break; + case NFTNL_EXPR_PAYLOAD_DREG: + memcpy(&payload->dreg, data, sizeof(payload->dreg)); + break; + case NFTNL_EXPR_PAYLOAD_BASE: + memcpy(&payload->base, data, sizeof(payload->base)); + break; + case NFTNL_EXPR_PAYLOAD_OFFSET: + memcpy(&payload->offset, data, sizeof(payload->offset)); + break; + case NFTNL_EXPR_PAYLOAD_LEN: + memcpy(&payload->len, data, sizeof(payload->len)); + break; + case NFTNL_EXPR_PAYLOAD_CSUM_TYPE: + memcpy(&payload->csum_type, data, sizeof(payload->csum_type)); + break; + case NFTNL_EXPR_PAYLOAD_CSUM_OFFSET: + memcpy(&payload->csum_offset, data, sizeof(payload->csum_offset)); + break; + case NFTNL_EXPR_PAYLOAD_FLAGS: + memcpy(&payload->csum_flags, data, sizeof(payload->csum_flags)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_payload_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_payload *payload = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_PAYLOAD_SREG: + *data_len = sizeof(payload->sreg); + return &payload->sreg; + case NFTNL_EXPR_PAYLOAD_DREG: + *data_len = sizeof(payload->dreg); + return &payload->dreg; + case NFTNL_EXPR_PAYLOAD_BASE: + *data_len = sizeof(payload->base); + return &payload->base; + case NFTNL_EXPR_PAYLOAD_OFFSET: + *data_len = sizeof(payload->offset); + return &payload->offset; + case NFTNL_EXPR_PAYLOAD_LEN: + *data_len = sizeof(payload->len); + return &payload->len; + case NFTNL_EXPR_PAYLOAD_CSUM_TYPE: + *data_len = sizeof(payload->csum_type); + return &payload->csum_type; + case NFTNL_EXPR_PAYLOAD_CSUM_OFFSET: + *data_len = sizeof(payload->csum_offset); + return &payload->csum_offset; + case NFTNL_EXPR_PAYLOAD_FLAGS: + *data_len = sizeof(payload->csum_flags); + return &payload->csum_flags; + } + return NULL; +} + +static int nftnl_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_SREG: + case NFTA_PAYLOAD_DREG: + case NFTA_PAYLOAD_BASE: + case NFTA_PAYLOAD_OFFSET: + case NFTA_PAYLOAD_LEN: + case NFTA_PAYLOAD_CSUM_TYPE: + case NFTA_PAYLOAD_CSUM_OFFSET: + case NFTA_PAYLOAD_CSUM_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_payload_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_payload *payload = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_SREG)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_SREG, htonl(payload->sreg)); + if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_DREG)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_DREG, htonl(payload->dreg)); + if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_BASE)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_BASE, htonl(payload->base)); + if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_OFFSET)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_OFFSET, htonl(payload->offset)); + if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_LEN)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_LEN, htonl(payload->len)); + if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_CSUM_TYPE)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_CSUM_TYPE, + htonl(payload->csum_type)); + if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_CSUM_OFFSET)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_CSUM_OFFSET, + htonl(payload->csum_offset)); + if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_CSUM_FLAGS, + htonl(payload->csum_flags)); +} + +static int +nftnl_expr_payload_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_payload *payload = nftnl_expr_data(e); + struct nlattr *tb[NFTA_PAYLOAD_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_payload_cb, tb) < 0) + return -1; + + if (tb[NFTA_PAYLOAD_SREG]) { + payload->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_SREG])); + e->flags |= (1 << NFTNL_EXPR_PAYLOAD_SREG); + } + if (tb[NFTA_PAYLOAD_DREG]) { + payload->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_DREG])); + e->flags |= (1 << NFTNL_EXPR_PAYLOAD_DREG); + } + if (tb[NFTA_PAYLOAD_BASE]) { + payload->base = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_BASE])); + e->flags |= (1 << NFTNL_EXPR_PAYLOAD_BASE); + } + if (tb[NFTA_PAYLOAD_OFFSET]) { + payload->offset = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_OFFSET])); + e->flags |= (1 << NFTNL_EXPR_PAYLOAD_OFFSET); + } + if (tb[NFTA_PAYLOAD_LEN]) { + payload->len = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_LEN])); + e->flags |= (1 << NFTNL_EXPR_PAYLOAD_LEN); + } + if (tb[NFTA_PAYLOAD_CSUM_TYPE]) { + payload->csum_type = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_CSUM_TYPE])); + e->flags |= (1 << NFTNL_EXPR_PAYLOAD_CSUM_TYPE); + } + if (tb[NFTA_PAYLOAD_CSUM_OFFSET]) { + payload->csum_offset = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_CSUM_OFFSET])); + e->flags |= (1 << NFTNL_EXPR_PAYLOAD_CSUM_OFFSET); + } + if (tb[NFTA_PAYLOAD_CSUM_FLAGS]) { + payload->csum_flags = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_CSUM_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_PAYLOAD_FLAGS); + } + return 0; +} + +static const char *base2str_array[NFT_PAYLOAD_TUN_HEADER + 1] = { + [NFT_PAYLOAD_LL_HEADER] = "link", + [NFT_PAYLOAD_NETWORK_HEADER] = "network", + [NFT_PAYLOAD_TRANSPORT_HEADER] = "transport", + [NFT_PAYLOAD_INNER_HEADER] = "inner", + [NFT_PAYLOAD_TUN_HEADER] = "tunnel", +}; + +static const char *base2str(enum nft_payload_bases base) +{ + if (base > NFT_PAYLOAD_INNER_HEADER) + return "unknown"; + + return base2str_array[base]; +} + +static int +nftnl_expr_payload_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_payload *payload = nftnl_expr_data(e); + + if (payload->sreg) + return snprintf(buf, len, "write reg %u => %ub @ %s header + %u csum_type %u csum_off %u csum_flags 0x%x ", + payload->sreg, + payload->len, base2str(payload->base), + payload->offset, payload->csum_type, + payload->csum_offset, + payload->csum_flags); + else + return snprintf(buf, len, "load %ub @ %s header + %u => reg %u ", + payload->len, base2str(payload->base), + payload->offset, payload->dreg); +} + +struct expr_ops expr_ops_payload = { + .name = "payload", + .alloc_len = sizeof(struct nftnl_expr_payload), + .max_attr = NFTA_PAYLOAD_MAX, + .set = nftnl_expr_payload_set, + .get = nftnl_expr_payload_get, + .parse = nftnl_expr_payload_parse, + .build = nftnl_expr_payload_build, + .output = nftnl_expr_payload_snprintf, +}; diff --git a/src/expr/queue.c b/src/expr/queue.c new file mode 100644 index 0000000..de287f2 --- /dev/null +++ b/src/expr/queue.c @@ -0,0 +1,197 @@ +/* + * (C) 2013 by Eric Leblond <eric@regit.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_queue { + enum nft_registers sreg_qnum; + uint16_t queuenum; + uint16_t queues_total; + uint16_t flags; +}; + +static int nftnl_expr_queue_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_queue *queue = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_QUEUE_NUM: + memcpy(&queue->queuenum, data, sizeof(queue->queuenum)); + break; + case NFTNL_EXPR_QUEUE_TOTAL: + memcpy(&queue->queues_total, data, sizeof(queue->queues_total)); + break; + case NFTNL_EXPR_QUEUE_FLAGS: + memcpy(&queue->flags, data, sizeof(queue->flags)); + break; + case NFTNL_EXPR_QUEUE_SREG_QNUM: + memcpy(&queue->sreg_qnum, data, sizeof(queue->sreg_qnum)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_queue_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_queue *queue = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_QUEUE_NUM: + *data_len = sizeof(queue->queuenum); + return &queue->queuenum; + case NFTNL_EXPR_QUEUE_TOTAL: + *data_len = sizeof(queue->queues_total); + return &queue->queues_total; + case NFTNL_EXPR_QUEUE_FLAGS: + *data_len = sizeof(queue->flags); + return &queue->flags; + case NFTNL_EXPR_QUEUE_SREG_QNUM: + *data_len = sizeof(queue->sreg_qnum); + return &queue->sreg_qnum; + } + return NULL; +} + +static int nftnl_expr_queue_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_QUEUE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_QUEUE_NUM: + case NFTA_QUEUE_TOTAL: + case NFTA_QUEUE_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + abi_breakage(); + break; + case NFTA_QUEUE_SREG_QNUM: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_queue_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_queue *queue = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_QUEUE_NUM)) + mnl_attr_put_u16(nlh, NFTA_QUEUE_NUM, htons(queue->queuenum)); + if (e->flags & (1 << NFTNL_EXPR_QUEUE_TOTAL)) + mnl_attr_put_u16(nlh, NFTA_QUEUE_TOTAL, htons(queue->queues_total)); + if (e->flags & (1 << NFTNL_EXPR_QUEUE_FLAGS)) + mnl_attr_put_u16(nlh, NFTA_QUEUE_FLAGS, htons(queue->flags)); + if (e->flags & (1 << NFTNL_EXPR_QUEUE_SREG_QNUM)) + mnl_attr_put_u32(nlh, NFTA_QUEUE_SREG_QNUM, htonl(queue->sreg_qnum)); +} + +static int +nftnl_expr_queue_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_queue *queue = nftnl_expr_data(e); + struct nlattr *tb[NFTA_QUEUE_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_queue_cb, tb) < 0) + return -1; + + if (tb[NFTA_QUEUE_NUM]) { + queue->queuenum = ntohs(mnl_attr_get_u16(tb[NFTA_QUEUE_NUM])); + e->flags |= (1 << NFTNL_EXPR_QUEUE_NUM); + } + if (tb[NFTA_QUEUE_TOTAL]) { + queue->queues_total = ntohs(mnl_attr_get_u16(tb[NFTA_QUEUE_TOTAL])); + e->flags |= (1 << NFTNL_EXPR_QUEUE_TOTAL); + } + if (tb[NFTA_QUEUE_FLAGS]) { + queue->flags = ntohs(mnl_attr_get_u16(tb[NFTA_QUEUE_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_QUEUE_FLAGS); + } + if (tb[NFTA_QUEUE_SREG_QNUM]) { + queue->sreg_qnum = ntohl(mnl_attr_get_u32(tb[NFTA_QUEUE_SREG_QNUM])); + e->flags |= (1 << NFTNL_EXPR_QUEUE_SREG_QNUM); + } + + return 0; +} + +static int +nftnl_expr_queue_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_queue *queue = nftnl_expr_data(e); + uint16_t total_queues; + int ret, offset = 0; + + if (e->flags & (1 << NFTNL_EXPR_QUEUE_NUM)) { + total_queues = queue->queuenum + queue->queues_total - 1; + + ret = snprintf(buf + offset, remain, "num %u", queue->queuenum); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + if (queue->queues_total && total_queues != queue->queuenum) { + ret = snprintf(buf + offset, remain, "-%u", total_queues); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + ret = snprintf(buf + offset, remain, " "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_QUEUE_SREG_QNUM)) { + ret = snprintf(buf + offset, remain, "sreg_qnum %u ", + queue->sreg_qnum); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_QUEUE_FLAGS)) { + if (queue->flags & (NFT_QUEUE_FLAG_BYPASS)) { + ret = snprintf(buf + offset, remain, "bypass "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (queue->flags & (NFT_QUEUE_FLAG_CPU_FANOUT)) { + ret = snprintf(buf + offset, remain, "fanout "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + } + return offset; +} + +struct expr_ops expr_ops_queue = { + .name = "queue", + .alloc_len = sizeof(struct nftnl_expr_queue), + .max_attr = NFTA_QUEUE_MAX, + .set = nftnl_expr_queue_set, + .get = nftnl_expr_queue_get, + .parse = nftnl_expr_queue_parse, + .build = nftnl_expr_queue_build, + .output = nftnl_expr_queue_snprintf, +}; diff --git a/src/expr/quota.c b/src/expr/quota.c new file mode 100644 index 0000000..835729c --- /dev/null +++ b/src/expr/quota.c @@ -0,0 +1,151 @@ +/* + * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <stdio.h> +#include <stdint.h> +#include <arpa/inet.h> +#include <errno.h> +#include <inttypes.h> + +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_quota { + uint64_t bytes; + uint64_t consumed; + uint32_t flags; +}; + +static int nftnl_expr_quota_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_quota *quota = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_QUOTA_BYTES: + memcpy("a->bytes, data, sizeof(quota->bytes)); + break; + case NFTNL_EXPR_QUOTA_CONSUMED: + memcpy("a->consumed, data, sizeof(quota->consumed)); + break; + case NFTNL_EXPR_QUOTA_FLAGS: + memcpy("a->flags, data, sizeof(quota->flags)); + break; + default: + return -1; + } + return 0; +} + +static const void *nftnl_expr_quota_get(const struct nftnl_expr *e, + uint16_t type, uint32_t *data_len) +{ + struct nftnl_expr_quota *quota = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_QUOTA_BYTES: + *data_len = sizeof(quota->bytes); + return "a->bytes; + case NFTNL_EXPR_QUOTA_CONSUMED: + *data_len = sizeof(quota->consumed); + return "a->consumed; + case NFTNL_EXPR_QUOTA_FLAGS: + *data_len = sizeof(quota->flags); + return "a->flags; + } + return NULL; +} + +static int nftnl_expr_quota_cb(const struct nlattr *attr, void *data) +{ + int type = mnl_attr_get_type(attr); + const struct nlattr **tb = data; + + if (mnl_attr_type_valid(attr, NFTA_QUOTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_QUOTA_BYTES: + case NFTA_QUOTA_CONSUMED: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + abi_breakage(); + break; + case NFTA_QUOTA_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_quota_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_quota *quota = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_QUOTA_BYTES)) + mnl_attr_put_u64(nlh, NFTA_QUOTA_BYTES, htobe64(quota->bytes)); + if (e->flags & (1 << NFTNL_EXPR_QUOTA_CONSUMED)) + mnl_attr_put_u64(nlh, NFTA_QUOTA_CONSUMED, htobe64(quota->consumed)); + if (e->flags & (1 << NFTNL_EXPR_QUOTA_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_QUOTA_FLAGS, htonl(quota->flags)); +} + +static int +nftnl_expr_quota_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_quota *quota = nftnl_expr_data(e); + struct nlattr *tb[NFTA_QUOTA_MAX + 1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_quota_cb, tb) < 0) + return -1; + + if (tb[NFTA_QUOTA_BYTES]) { + quota->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_QUOTA_BYTES])); + e->flags |= (1 << NFTNL_EXPR_QUOTA_BYTES); + } + if (tb[NFTA_QUOTA_CONSUMED]) { + quota->consumed = be64toh(mnl_attr_get_u64(tb[NFTA_QUOTA_CONSUMED])); + e->flags |= (1 << NFTNL_EXPR_QUOTA_CONSUMED); + } + if (tb[NFTA_QUOTA_FLAGS]) { + quota->flags = ntohl(mnl_attr_get_u32(tb[NFTA_QUOTA_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_QUOTA_FLAGS); + } + + return 0; +} + +static int nftnl_expr_quota_snprintf(char *buf, size_t len, + uint32_t flags, + const struct nftnl_expr *e) +{ + struct nftnl_expr_quota *quota = nftnl_expr_data(e); + + return snprintf(buf, len, + "bytes %"PRIu64" consumed %"PRIu64" flags %u ", + quota->bytes, quota->consumed, quota->flags); +} + +struct expr_ops expr_ops_quota = { + .name = "quota", + .alloc_len = sizeof(struct nftnl_expr_quota), + .max_attr = NFTA_QUOTA_MAX, + .set = nftnl_expr_quota_set, + .get = nftnl_expr_quota_get, + .parse = nftnl_expr_quota_parse, + .build = nftnl_expr_quota_build, + .output = nftnl_expr_quota_snprintf, +}; diff --git a/src/expr/range.c b/src/expr/range.c new file mode 100644 index 0000000..473add8 --- /dev/null +++ b/src/expr/range.c @@ -0,0 +1,217 @@ +/* + * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_range { + union nftnl_data_reg data_from; + union nftnl_data_reg data_to; + enum nft_registers sreg; + enum nft_range_ops op; +}; + +static int nftnl_expr_range_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_range *range = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_RANGE_SREG: + memcpy(&range->sreg, data, sizeof(range->sreg)); + break; + case NFTNL_EXPR_RANGE_OP: + memcpy(&range->op, data, sizeof(range->op)); + break; + case NFTNL_EXPR_RANGE_FROM_DATA: + memcpy(&range->data_from.val, data, data_len); + range->data_from.len = data_len; + break; + case NFTNL_EXPR_RANGE_TO_DATA: + memcpy(&range->data_to.val, data, data_len); + range->data_to.len = data_len; + break; + default: + return -1; + } + return 0; +} + +static const void *nftnl_expr_range_get(const struct nftnl_expr *e, + uint16_t type, uint32_t *data_len) +{ + struct nftnl_expr_range *range = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_RANGE_SREG: + *data_len = sizeof(range->sreg); + return &range->sreg; + case NFTNL_EXPR_RANGE_OP: + *data_len = sizeof(range->op); + return &range->op; + case NFTNL_EXPR_RANGE_FROM_DATA: + *data_len = range->data_from.len; + return &range->data_from.val; + case NFTNL_EXPR_RANGE_TO_DATA: + *data_len = range->data_to.len; + return &range->data_to.val; + } + return NULL; +} + +static int nftnl_expr_range_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_RANGE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_RANGE_SREG: + case NFTA_RANGE_OP: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_RANGE_FROM_DATA: + case NFTA_RANGE_TO_DATA: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_range_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_range *range = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_RANGE_SREG)) + mnl_attr_put_u32(nlh, NFTA_RANGE_SREG, htonl(range->sreg)); + if (e->flags & (1 << NFTNL_EXPR_RANGE_OP)) + mnl_attr_put_u32(nlh, NFTA_RANGE_OP, htonl(range->op)); + if (e->flags & (1 << NFTNL_EXPR_RANGE_FROM_DATA)) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_RANGE_FROM_DATA); + mnl_attr_put(nlh, NFTA_DATA_VALUE, range->data_from.len, + range->data_from.val); + mnl_attr_nest_end(nlh, nest); + } + if (e->flags & (1 << NFTNL_EXPR_RANGE_TO_DATA)) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_RANGE_TO_DATA); + mnl_attr_put(nlh, NFTA_DATA_VALUE, range->data_to.len, + range->data_to.val); + mnl_attr_nest_end(nlh, nest); + } +} + +static int +nftnl_expr_range_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_range *range = nftnl_expr_data(e); + struct nlattr *tb[NFTA_RANGE_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nftnl_expr_range_cb, tb) < 0) + return -1; + + if (tb[NFTA_RANGE_SREG]) { + range->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_RANGE_SREG])); + e->flags |= (1 << NFTA_RANGE_SREG); + } + if (tb[NFTA_RANGE_OP]) { + range->op = ntohl(mnl_attr_get_u32(tb[NFTA_RANGE_OP])); + e->flags |= (1 << NFTA_RANGE_OP); + } + if (tb[NFTA_RANGE_FROM_DATA]) { + ret = nftnl_parse_data(&range->data_from, + tb[NFTA_RANGE_FROM_DATA], NULL); + e->flags |= (1 << NFTA_RANGE_FROM_DATA); + } + if (tb[NFTA_RANGE_TO_DATA]) { + ret = nftnl_parse_data(&range->data_to, + tb[NFTA_RANGE_TO_DATA], NULL); + e->flags |= (1 << NFTA_RANGE_TO_DATA); + } + + return ret; +} + +static const char *expr_range_str[] = { + [NFT_RANGE_EQ] = "eq", + [NFT_RANGE_NEQ] = "neq", +}; + +static const char *range2str(uint32_t op) +{ + if (op > NFT_RANGE_NEQ) + return "unknown"; + + return expr_range_str[op]; +} + +static inline int nftnl_str2range(const char *op) +{ + if (strcmp(op, "eq") == 0) + return NFT_RANGE_EQ; + else if (strcmp(op, "neq") == 0) + return NFT_RANGE_NEQ; + else { + errno = EINVAL; + return -1; + } +} + +static int nftnl_expr_range_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_range *range = nftnl_expr_data(e); + int offset = 0, ret; + + ret = snprintf(buf, remain, "%s reg %u ", + range2str(range->op), range->sreg); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_data_reg_snprintf(buf + offset, remain, &range->data_from, + 0, DATA_VALUE); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_data_reg_snprintf(buf + offset, remain, &range->data_to, + 0, DATA_VALUE); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + return offset; +} + +struct expr_ops expr_ops_range = { + .name = "range", + .alloc_len = sizeof(struct nftnl_expr_range), + .max_attr = NFTA_RANGE_MAX, + .set = nftnl_expr_range_set, + .get = nftnl_expr_range_get, + .parse = nftnl_expr_range_parse, + .build = nftnl_expr_range_build, + .output = nftnl_expr_range_snprintf, +}; diff --git a/src/expr/redir.c b/src/expr/redir.c new file mode 100644 index 0000000..87c2acc --- /dev/null +++ b/src/expr/redir.c @@ -0,0 +1,171 @@ +/* + * (C) 2014 by Arturo Borrero Gonzalez <arturo@debian.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <stdio.h> +#include <stdint.h> +#include <arpa/inet.h> +#include <errno.h> +#include <inttypes.h> + +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_redir { + enum nft_registers sreg_proto_min; + enum nft_registers sreg_proto_max; + uint32_t flags; +}; + +static int +nftnl_expr_redir_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_redir *redir = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_REDIR_REG_PROTO_MIN: + memcpy(&redir->sreg_proto_min, data, sizeof(redir->sreg_proto_min)); + break; + case NFTNL_EXPR_REDIR_REG_PROTO_MAX: + memcpy(&redir->sreg_proto_max, data, sizeof(redir->sreg_proto_max)); + break; + case NFTNL_EXPR_REDIR_FLAGS: + memcpy(&redir->flags, data, sizeof(redir->flags)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_redir_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_redir *redir = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_REDIR_REG_PROTO_MIN: + *data_len = sizeof(redir->sreg_proto_min); + return &redir->sreg_proto_min; + case NFTNL_EXPR_REDIR_REG_PROTO_MAX: + *data_len = sizeof(redir->sreg_proto_max); + return &redir->sreg_proto_max; + case NFTNL_EXPR_REDIR_FLAGS: + *data_len = sizeof(redir->flags); + return &redir->flags; + } + return NULL; +} + +static int nftnl_expr_redir_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_REDIR_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_REDIR_REG_PROTO_MIN: + case NFTA_REDIR_REG_PROTO_MAX: + case NFTA_REDIR_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_redir_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_redir *redir = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_REDIR_REG_PROTO_MIN)) + mnl_attr_put_u32(nlh, NFTA_REDIR_REG_PROTO_MIN, + htobe32(redir->sreg_proto_min)); + if (e->flags & (1 << NFTNL_EXPR_REDIR_REG_PROTO_MAX)) + mnl_attr_put_u32(nlh, NFTA_REDIR_REG_PROTO_MAX, + htobe32(redir->sreg_proto_max)); + if (e->flags & (1 << NFTNL_EXPR_REDIR_FLAGS)) + mnl_attr_put_u32(nlh, NFTA_REDIR_FLAGS, htobe32(redir->flags)); +} + +static int +nftnl_expr_redir_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_redir *redir = nftnl_expr_data(e); + struct nlattr *tb[NFTA_REDIR_MAX + 1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_redir_cb, tb) < 0) + return -1; + + if (tb[NFTA_REDIR_REG_PROTO_MIN]) { + redir->sreg_proto_min = + ntohl(mnl_attr_get_u32(tb[NFTA_REDIR_REG_PROTO_MIN])); + e->flags |= (1 << NFTNL_EXPR_REDIR_REG_PROTO_MIN); + } + if (tb[NFTA_REDIR_REG_PROTO_MAX]) { + redir->sreg_proto_max = + ntohl(mnl_attr_get_u32(tb[NFTA_REDIR_REG_PROTO_MAX])); + e->flags |= (1 << NFTNL_EXPR_REDIR_REG_PROTO_MAX); + } + if (tb[NFTA_REDIR_FLAGS]) { + redir->flags = be32toh(mnl_attr_get_u32(tb[NFTA_REDIR_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_REDIR_FLAGS); + } + + return 0; +} + +static int +nftnl_expr_redir_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + int ret, offset = 0; + struct nftnl_expr_redir *redir = nftnl_expr_data(e); + + if (nftnl_expr_is_set(e, NFTNL_EXPR_REDIR_REG_PROTO_MIN)) { + ret = snprintf(buf + offset, remain, "proto_min reg %u ", + redir->sreg_proto_min); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (nftnl_expr_is_set(e, NFTNL_EXPR_REDIR_REG_PROTO_MAX)) { + ret = snprintf(buf + offset, remain, "proto_max reg %u ", + redir->sreg_proto_max); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (nftnl_expr_is_set(e, NFTNL_EXPR_REDIR_FLAGS)) { + ret = snprintf(buf + offset, remain, "flags 0x%x ", + redir->flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +struct expr_ops expr_ops_redir = { + .name = "redir", + .alloc_len = sizeof(struct nftnl_expr_redir), + .max_attr = NFTA_REDIR_MAX, + .set = nftnl_expr_redir_set, + .get = nftnl_expr_redir_get, + .parse = nftnl_expr_redir_parse, + .build = nftnl_expr_redir_build, + .output = nftnl_expr_redir_snprintf, +}; diff --git a/src/expr/reject.c b/src/expr/reject.c new file mode 100644 index 0000000..c7c9441 --- /dev/null +++ b/src/expr/reject.c @@ -0,0 +1,138 @@ +/* + * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_reject { + uint32_t type; + uint8_t icmp_code; +}; + +static int nftnl_expr_reject_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_reject *reject = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_REJECT_TYPE: + memcpy(&reject->type, data, sizeof(reject->type)); + break; + case NFTNL_EXPR_REJECT_CODE: + memcpy(&reject->icmp_code, data, sizeof(reject->icmp_code)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_reject_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_reject *reject = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_REJECT_TYPE: + *data_len = sizeof(reject->type); + return &reject->type; + case NFTNL_EXPR_REJECT_CODE: + *data_len = sizeof(reject->icmp_code); + return &reject->icmp_code; + } + return NULL; +} + +static int nftnl_expr_reject_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_REJECT_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_REJECT_TYPE: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_REJECT_ICMP_CODE: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_reject_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_reject *reject = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_REJECT_TYPE)) + mnl_attr_put_u32(nlh, NFTA_REJECT_TYPE, htonl(reject->type)); + if (e->flags & (1 << NFTNL_EXPR_REJECT_CODE)) + mnl_attr_put_u8(nlh, NFTA_REJECT_ICMP_CODE, reject->icmp_code); +} + +static int +nftnl_expr_reject_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_reject *reject = nftnl_expr_data(e); + struct nlattr *tb[NFTA_REJECT_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_reject_cb, tb) < 0) + return -1; + + if (tb[NFTA_REJECT_TYPE]) { + reject->type = ntohl(mnl_attr_get_u32(tb[NFTA_REJECT_TYPE])); + e->flags |= (1 << NFTNL_EXPR_REJECT_TYPE); + } + if (tb[NFTA_REJECT_ICMP_CODE]) { + reject->icmp_code = mnl_attr_get_u8(tb[NFTA_REJECT_ICMP_CODE]); + e->flags |= (1 << NFTNL_EXPR_REJECT_CODE); + } + + return 0; +} + +static int +nftnl_expr_reject_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_reject *reject = nftnl_expr_data(e); + + return snprintf(buf, len, "type %u code %u ", + reject->type, reject->icmp_code); +} + +struct expr_ops expr_ops_reject = { + .name = "reject", + .alloc_len = sizeof(struct nftnl_expr_reject), + .max_attr = NFTA_REJECT_MAX, + .set = nftnl_expr_reject_set, + .get = nftnl_expr_reject_get, + .parse = nftnl_expr_reject_parse, + .build = nftnl_expr_reject_build, + .output = nftnl_expr_reject_snprintf, +}; diff --git a/src/expr/rt.c b/src/expr/rt.c new file mode 100644 index 0000000..695a658 --- /dev/null +++ b/src/expr/rt.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016 Anders K. Pedersen <akp@cohaesio.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. + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_rt { + enum nft_rt_keys key; + enum nft_registers dreg; +}; + +static int +nftnl_expr_rt_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_rt *rt = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_RT_KEY: + memcpy(&rt->key, data, sizeof(rt->key)); + break; + case NFTNL_EXPR_RT_DREG: + memcpy(&rt->dreg, data, sizeof(rt->dreg)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_rt_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_rt *rt = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_RT_KEY: + *data_len = sizeof(rt->key); + return &rt->key; + case NFTNL_EXPR_RT_DREG: + *data_len = sizeof(rt->dreg); + return &rt->dreg; + } + return NULL; +} + +static int nftnl_expr_rt_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_RT_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_RT_KEY: + case NFTA_RT_DREG: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_rt_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_rt *rt = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_RT_KEY)) + mnl_attr_put_u32(nlh, NFTA_RT_KEY, htonl(rt->key)); + if (e->flags & (1 << NFTNL_EXPR_RT_DREG)) + mnl_attr_put_u32(nlh, NFTA_RT_DREG, htonl(rt->dreg)); +} + +static int +nftnl_expr_rt_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_rt *rt = nftnl_expr_data(e); + struct nlattr *tb[NFTA_RT_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_rt_cb, tb) < 0) + return -1; + + if (tb[NFTA_RT_KEY]) { + rt->key = ntohl(mnl_attr_get_u32(tb[NFTA_RT_KEY])); + e->flags |= (1 << NFTNL_EXPR_RT_KEY); + } + if (tb[NFTA_RT_DREG]) { + rt->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_RT_DREG])); + e->flags |= (1 << NFTNL_EXPR_RT_DREG); + } + + return 0; +} + +static const char *rt_key2str_array[NFT_RT_MAX + 1] = { + [NFT_RT_CLASSID] = "classid", + [NFT_RT_NEXTHOP4] = "nexthop4", + [NFT_RT_NEXTHOP6] = "nexthop6", + [NFT_RT_TCPMSS] = "tcpmss", + [NFT_RT_XFRM] = "ipsec", +}; + +static const char *rt_key2str(uint8_t key) +{ + if (key <= NFT_RT_MAX) + return rt_key2str_array[key]; + + return "unknown"; +} + +static inline int str2rt_key(const char *str) +{ + int i; + + for (i = 0; i < NFT_RT_MAX; i++) { + if (strcmp(str, rt_key2str_array[i]) == 0) + return i; + } + + errno = EINVAL; + return -1; +} + +static int +nftnl_expr_rt_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_rt *rt = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_RT_DREG)) { + return snprintf(buf, len, "load %s => reg %u ", + rt_key2str(rt->key), rt->dreg); + } + return 0; +} + +struct expr_ops expr_ops_rt = { + .name = "rt", + .alloc_len = sizeof(struct nftnl_expr_rt), + .max_attr = NFTA_RT_MAX, + .set = nftnl_expr_rt_set, + .get = nftnl_expr_rt_get, + .parse = nftnl_expr_rt_parse, + .build = nftnl_expr_rt_build, + .output = nftnl_expr_rt_snprintf, +}; diff --git a/src/expr/socket.c b/src/expr/socket.c new file mode 100644 index 0000000..83045c0 --- /dev/null +++ b/src/expr/socket.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2018 Máté Eckl <ecklm94@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. + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_socket { + enum nft_socket_keys key; + enum nft_registers dreg; + uint32_t level; +}; + +static int +nftnl_expr_socket_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_socket *socket = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_SOCKET_KEY: + memcpy(&socket->key, data, sizeof(socket->key)); + break; + case NFTNL_EXPR_SOCKET_DREG: + memcpy(&socket->dreg, data, sizeof(socket->dreg)); + break; + case NFTNL_EXPR_SOCKET_LEVEL: + memcpy(&socket->level, data, sizeof(socket->level)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_socket_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_socket *socket = nftnl_expr_data(e); + + switch (type) { + case NFTNL_EXPR_SOCKET_KEY: + *data_len = sizeof(socket->key); + return &socket->key; + case NFTNL_EXPR_SOCKET_DREG: + *data_len = sizeof(socket->dreg); + return &socket->dreg; + case NFTNL_EXPR_SOCKET_LEVEL: + *data_len = sizeof(socket->level); + return &socket->level; + } + return NULL; +} + +static int nftnl_expr_socket_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_SOCKET_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_SOCKET_KEY: + case NFTA_SOCKET_DREG: + case NFTA_SOCKET_LEVEL: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_socket_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_socket *socket = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_SOCKET_KEY)) + mnl_attr_put_u32(nlh, NFTA_SOCKET_KEY, htonl(socket->key)); + if (e->flags & (1 << NFTNL_EXPR_SOCKET_DREG)) + mnl_attr_put_u32(nlh, NFTA_SOCKET_DREG, htonl(socket->dreg)); + if (e->flags & (1 << NFTNL_EXPR_SOCKET_LEVEL)) + mnl_attr_put_u32(nlh, NFTA_SOCKET_LEVEL, htonl(socket->level)); +} + +static int +nftnl_expr_socket_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_socket *socket = nftnl_expr_data(e); + struct nlattr *tb[NFTA_SOCKET_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_socket_cb, tb) < 0) + return -1; + + if (tb[NFTA_SOCKET_KEY]) { + socket->key = ntohl(mnl_attr_get_u32(tb[NFTA_SOCKET_KEY])); + e->flags |= (1 << NFTNL_EXPR_SOCKET_KEY); + } + if (tb[NFTA_SOCKET_DREG]) { + socket->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_SOCKET_DREG])); + e->flags |= (1 << NFTNL_EXPR_SOCKET_DREG); + } + if (tb[NFTA_SOCKET_LEVEL]) { + socket->level = ntohl(mnl_attr_get_u32(tb[NFTA_SOCKET_LEVEL])); + e->flags |= (1 << NFTNL_EXPR_SOCKET_LEVEL); + } + + return 0; +} + +static const char *socket_key2str_array[NFT_SOCKET_MAX + 1] = { + [NFT_SOCKET_TRANSPARENT] = "transparent", + [NFT_SOCKET_MARK] = "mark", + [NFT_SOCKET_WILDCARD] = "wildcard", + [NFT_SOCKET_CGROUPV2] = "cgroupv2", +}; + +static const char *socket_key2str(uint8_t key) +{ + if (key < NFT_SOCKET_MAX + 1) + return socket_key2str_array[key]; + + return "unknown"; +} + +static int +nftnl_expr_socket_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_socket *socket = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_SOCKET_DREG)) { + return snprintf(buf, len, "load %s => reg %u ", + socket_key2str(socket->key), socket->dreg); + } + if (e->flags & (1 << NFTNL_EXPR_SOCKET_LEVEL)) + return snprintf(buf, len, "level %u ", socket->level); + + return 0; +} + +struct expr_ops expr_ops_socket = { + .name = "socket", + .alloc_len = sizeof(struct nftnl_expr_socket), + .max_attr = NFTA_SOCKET_MAX, + .set = nftnl_expr_socket_set, + .get = nftnl_expr_socket_get, + .parse = nftnl_expr_socket_parse, + .build = nftnl_expr_socket_build, + .output = nftnl_expr_socket_snprintf, +}; diff --git a/src/expr/synproxy.c b/src/expr/synproxy.c new file mode 100644 index 0000000..47fcaef --- /dev/null +++ b/src/expr/synproxy.c @@ -0,0 +1,156 @@ +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_synproxy { + uint16_t mss; + uint8_t wscale; + uint32_t flags; +}; + +static int nftnl_expr_synproxy_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_SYNPROXY_MSS: + memcpy(&synproxy->mss, data, sizeof(synproxy->mss)); + break; + case NFTNL_EXPR_SYNPROXY_WSCALE: + memcpy(&synproxy->wscale, data, sizeof(synproxy->wscale)); + break; + case NFTNL_EXPR_SYNPROXY_FLAGS: + memcpy(&synproxy->flags, data, sizeof(synproxy->flags)); + break; + } + return 0; +} + +static const void * +nftnl_expr_synproxy_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_SYNPROXY_MSS: + *data_len = sizeof(synproxy->mss); + return &synproxy->mss; + case NFTNL_EXPR_SYNPROXY_WSCALE: + *data_len = sizeof(synproxy->wscale); + return &synproxy->wscale; + case NFTNL_EXPR_SYNPROXY_FLAGS: + *data_len = sizeof(synproxy->flags); + return &synproxy->flags; + } + return NULL; +} + +static int nftnl_expr_synproxy_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_SYNPROXY_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTNL_EXPR_SYNPROXY_MSS: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + abi_breakage(); + break; + + case NFTNL_EXPR_SYNPROXY_WSCALE: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + abi_breakage(); + break; + + case NFTNL_EXPR_SYNPROXY_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_synproxy_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_MSS)) + mnl_attr_put_u16(nlh, NFTNL_EXPR_SYNPROXY_MSS, + htons(synproxy->mss)); + if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_WSCALE)) + mnl_attr_put_u8(nlh, NFTNL_EXPR_SYNPROXY_WSCALE, + synproxy->wscale); + if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_FLAGS)) + mnl_attr_put_u32(nlh, NFTNL_EXPR_SYNPROXY_FLAGS, + htonl(synproxy->flags)); +} + +static int +nftnl_expr_synproxy_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e); + struct nlattr *tb[NFTA_SYNPROXY_MAX + 1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_synproxy_cb, tb) < 0) + return -1; + + if (tb[NFTA_SYNPROXY_MSS]) { + synproxy->mss = ntohs(mnl_attr_get_u16(tb[NFTA_SYNPROXY_MSS])); + e->flags |= (1 << NFTNL_EXPR_SYNPROXY_MSS); + } + + if (tb[NFTA_SYNPROXY_WSCALE]) { + synproxy->wscale = mnl_attr_get_u8(tb[NFTA_SYNPROXY_WSCALE]); + e->flags |= (1 << NFTNL_EXPR_SYNPROXY_WSCALE); + } + + if (tb[NFTA_SYNPROXY_FLAGS]) { + synproxy->flags = ntohl(mnl_attr_get_u32(tb[NFTA_SYNPROXY_FLAGS])); + e->flags |= (1 << NFTNL_EXPR_SYNPROXY_FLAGS); + } + + return 0; +} + +static int +nftnl_expr_synproxy_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e); + int ret, offset = 0; + + if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_MSS) && + e->flags & (1 << NFTNL_EXPR_SYNPROXY_WSCALE)) { + ret = snprintf(buf, len, "mss %u wscale %u ", synproxy->mss, + synproxy->wscale); + SNPRINTF_BUFFER_SIZE(ret, len, offset); + } + + return offset; +} + +struct expr_ops expr_ops_synproxy = { + .name = "synproxy", + .alloc_len = sizeof(struct nftnl_expr_synproxy), + .max_attr = NFTA_SYNPROXY_MAX, + .set = nftnl_expr_synproxy_set, + .get = nftnl_expr_synproxy_get, + .parse = nftnl_expr_synproxy_parse, + .build = nftnl_expr_synproxy_build, + .output = nftnl_expr_synproxy_snprintf, +}; diff --git a/src/expr/target.c b/src/expr/target.c new file mode 100644 index 0000000..2a3fe8a --- /dev/null +++ b/src/expr/target.c @@ -0,0 +1,193 @@ +/* + * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> /* for memcpy */ +#include <arpa/inet.h> +#include <errno.h> +#include <libmnl/libmnl.h> + +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_tables_compat.h> + +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +/* From include/linux/netfilter/x_tables.h */ +#define XT_EXTENSION_MAXNAMELEN 29 + +struct nftnl_expr_target { + char name[XT_EXTENSION_MAXNAMELEN]; + uint32_t rev; + uint32_t data_len; + const void *data; +}; + +static int +nftnl_expr_target_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_target *tg = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_TG_NAME: + snprintf(tg->name, sizeof(tg->name), "%.*s", data_len, + (const char *) data); + break; + case NFTNL_EXPR_TG_REV: + memcpy(&tg->rev, data, sizeof(tg->rev)); + break; + case NFTNL_EXPR_TG_INFO: + if (e->flags & (1 << NFTNL_EXPR_TG_INFO)) + xfree(tg->data); + + tg->data = data; + tg->data_len = data_len; + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_target_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_target *tg = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_TG_NAME: + *data_len = sizeof(tg->name); + return tg->name; + case NFTNL_EXPR_TG_REV: + *data_len = sizeof(tg->rev); + return &tg->rev; + case NFTNL_EXPR_TG_INFO: + *data_len = tg->data_len; + return tg->data; + } + return NULL; +} + +static int nftnl_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) + abi_breakage(); + break; + case NFTA_TARGET_REV: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_TARGET_INFO: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_target_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_target *tg = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_TG_NAME)) + mnl_attr_put_strz(nlh, NFTA_TARGET_NAME, tg->name); + if (e->flags & (1 << NFTNL_EXPR_TG_REV)) + mnl_attr_put_u32(nlh, NFTA_TARGET_REV, htonl(tg->rev)); + if (e->flags & (1 << NFTNL_EXPR_TG_INFO)) + mnl_attr_put(nlh, NFTA_TARGET_INFO, tg->data_len, tg->data); +} + +static int nftnl_expr_target_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_target *target = nftnl_expr_data(e); + struct nlattr *tb[NFTA_TARGET_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_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 << NFTNL_EXPR_TG_NAME); + } + + if (tb[NFTA_TARGET_REV]) { + target->rev = ntohl(mnl_attr_get_u32(tb[NFTA_TARGET_REV])); + e->flags |= (1 << NFTNL_EXPR_TG_REV); + } + + if (tb[NFTA_TARGET_INFO]) { + uint32_t len = mnl_attr_get_payload_len(tb[NFTA_TARGET_INFO]); + void *target_data; + + if (target->data) + xfree(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 << NFTNL_EXPR_TG_INFO); + } + + return 0; +} + +static int +nftnl_expr_target_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_target *target = nftnl_expr_data(e); + + return snprintf(buf, len, "name %s rev %u ", target->name, target->rev); +} + +static void nftnl_expr_target_free(const struct nftnl_expr *e) +{ + struct nftnl_expr_target *target = nftnl_expr_data(e); + + xfree(target->data); +} + +struct expr_ops expr_ops_target = { + .name = "target", + .alloc_len = sizeof(struct nftnl_expr_target), + .max_attr = NFTA_TARGET_MAX, + .free = nftnl_expr_target_free, + .set = nftnl_expr_target_set, + .get = nftnl_expr_target_get, + .parse = nftnl_expr_target_parse, + .build = nftnl_expr_target_build, + .output = nftnl_expr_target_snprintf, +}; diff --git a/src/expr/tproxy.c b/src/expr/tproxy.c new file mode 100644 index 0000000..bd5ffbf --- /dev/null +++ b/src/expr/tproxy.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2018 Máté Eckl <ecklm94@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. + */ + +#include "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <limits.h> +#include <string.h> +#include <errno.h> +#include <arpa/inet.h> +#include <libmnl/libmnl.h> +#include <linux/netfilter/nf_tables.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_tproxy { + enum nft_registers sreg_addr; + enum nft_registers sreg_port; + int family; +}; + +static int +nftnl_expr_tproxy_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_TPROXY_FAMILY: + memcpy(&tproxy->family, data, sizeof(tproxy->family)); + break; + case NFTNL_EXPR_TPROXY_REG_ADDR: + memcpy(&tproxy->sreg_addr, data, sizeof(tproxy->sreg_addr)); + break; + case NFTNL_EXPR_TPROXY_REG_PORT: + memcpy(&tproxy->sreg_port, data, sizeof(tproxy->sreg_port)); + break; + default: + return -1; + } + + return 0; +} + +static const void * +nftnl_expr_tproxy_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_TPROXY_FAMILY: + *data_len = sizeof(tproxy->family); + return &tproxy->family; + case NFTNL_EXPR_TPROXY_REG_ADDR: + *data_len = sizeof(tproxy->sreg_addr); + return &tproxy->sreg_addr; + case NFTNL_EXPR_TPROXY_REG_PORT: + *data_len = sizeof(tproxy->sreg_port); + return &tproxy->sreg_port; + } + return NULL; +} + +static int nftnl_expr_tproxy_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_TPROXY_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_TPROXY_FAMILY: + case NFTA_TPROXY_REG_ADDR: + case NFTA_TPROXY_REG_PORT: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nftnl_expr_tproxy_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e); + struct nlattr *tb[NFTA_TPROXY_MAX + 1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_tproxy_cb, tb) < 0) + return -1; + + if (tb[NFTA_TPROXY_FAMILY]) { + tproxy->family = ntohl(mnl_attr_get_u32(tb[NFTA_TPROXY_FAMILY])); + e->flags |= (1 << NFTNL_EXPR_TPROXY_FAMILY); + } + if (tb[NFTA_TPROXY_REG_ADDR]) { + tproxy->sreg_addr = + ntohl(mnl_attr_get_u32(tb[NFTA_TPROXY_REG_ADDR])); + e->flags |= (1 << NFTNL_EXPR_TPROXY_REG_ADDR); + } + if (tb[NFTA_TPROXY_REG_PORT]) { + tproxy->sreg_port = + ntohl(mnl_attr_get_u32(tb[NFTA_TPROXY_REG_PORT])); + e->flags |= (1 << NFTNL_EXPR_TPROXY_REG_PORT); + } + + return 0; +} + +static void +nftnl_expr_tproxy_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_TPROXY_FAMILY)) + mnl_attr_put_u32(nlh, NFTA_TPROXY_FAMILY, htonl(tproxy->family)); + + if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_ADDR)) + mnl_attr_put_u32(nlh, NFTA_TPROXY_REG_ADDR, + htonl(tproxy->sreg_addr)); + + if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_PORT)) + mnl_attr_put_u32(nlh, NFTA_TPROXY_REG_PORT, + htonl(tproxy->sreg_port)); +} + +static int +nftnl_expr_tproxy_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e); + int offset = 0, ret = 0; + + if (tproxy->family != NFTA_TPROXY_UNSPEC) { + ret = snprintf(buf + offset, remain, "%s ", + nftnl_family2str(tproxy->family)); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_ADDR)) { + ret = snprintf(buf + offset, remain, + "addr reg %u ", tproxy->sreg_addr); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_PORT)) { + ret = snprintf(buf + offset, remain, + "port reg %u ", tproxy->sreg_port); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + return offset; +} + +struct expr_ops expr_ops_tproxy = { + .name = "tproxy", + .alloc_len = sizeof(struct nftnl_expr_tproxy), + .max_attr = NFTA_TPROXY_MAX, + .set = nftnl_expr_tproxy_set, + .get = nftnl_expr_tproxy_get, + .parse = nftnl_expr_tproxy_parse, + .build = nftnl_expr_tproxy_build, + .output = nftnl_expr_tproxy_snprintf, +}; diff --git a/src/expr/tunnel.c b/src/expr/tunnel.c new file mode 100644 index 0000000..a00f620 --- /dev/null +++ b/src/expr/tunnel.c @@ -0,0 +1,149 @@ +/* + * (C) 2018 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_tunnel { + enum nft_tunnel_keys key; + enum nft_registers dreg; +}; + +static int nftnl_expr_tunnel_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_TUNNEL_KEY: + memcpy(&tunnel->key, data, sizeof(tunnel->key)); + break; + case NFTNL_EXPR_TUNNEL_DREG: + memcpy(&tunnel->dreg, data, sizeof(tunnel->dreg)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_tunnel_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_TUNNEL_KEY: + *data_len = sizeof(tunnel->key); + return &tunnel->key; + case NFTNL_EXPR_TUNNEL_DREG: + *data_len = sizeof(tunnel->dreg); + return &tunnel->dreg; + } + return NULL; +} + +static int nftnl_expr_tunnel_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_TUNNEL_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_TUNNEL_KEY: + case NFTA_TUNNEL_DREG: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_tunnel_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_TUNNEL_KEY)) + mnl_attr_put_u32(nlh, NFTA_TUNNEL_KEY, htonl(tunnel->key)); + if (e->flags & (1 << NFTNL_EXPR_TUNNEL_DREG)) + mnl_attr_put_u32(nlh, NFTA_TUNNEL_DREG, htonl(tunnel->dreg)); +} + +static int +nftnl_expr_tunnel_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e); + struct nlattr *tb[NFTA_TUNNEL_MAX + 1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_tunnel_cb, tb) < 0) + return -1; + + if (tb[NFTA_TUNNEL_KEY]) { + tunnel->key = ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_KEY])); + e->flags |= (1 << NFTNL_EXPR_TUNNEL_KEY); + } + if (tb[NFTA_TUNNEL_DREG]) { + tunnel->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_DREG])); + e->flags |= (1 << NFTNL_EXPR_TUNNEL_DREG); + } + + return 0; +} + +static const char *tunnel_key2str_array[NFT_TUNNEL_MAX + 1] = { + [NFT_TUNNEL_PATH] = "path", + [NFT_TUNNEL_ID] = "id", +}; + +static const char *tunnel_key2str(uint8_t key) +{ + if (key <= NFT_TUNNEL_MAX) + return tunnel_key2str_array[key]; + + return "unknown"; +} + +static int +nftnl_expr_tunnel_snprintf(char *buf, size_t len, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_TUNNEL_DREG)) { + return snprintf(buf, len, "load %s => reg %u ", + tunnel_key2str(tunnel->key), tunnel->dreg); + } + return 0; +} + +struct expr_ops expr_ops_tunnel = { + .name = "tunnel", + .alloc_len = sizeof(struct nftnl_expr_tunnel), + .max_attr = NFTA_TUNNEL_MAX, + .set = nftnl_expr_tunnel_set, + .get = nftnl_expr_tunnel_get, + .parse = nftnl_expr_tunnel_parse, + .build = nftnl_expr_tunnel_build, + .output = nftnl_expr_tunnel_snprintf, +}; diff --git a/src/expr/xfrm.c b/src/expr/xfrm.c new file mode 100644 index 0000000..2db00d5 --- /dev/null +++ b/src/expr/xfrm.c @@ -0,0 +1,200 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/xfrm.h> + +#include "internal.h" +#include <libmnl/libmnl.h> +#include <libnftnl/expr.h> +#include <libnftnl/rule.h> + +struct nftnl_expr_xfrm { + enum nft_registers dreg; + enum nft_xfrm_keys key; + uint32_t spnum; + uint8_t dir; +}; + +static int +nftnl_expr_xfrm_set(struct nftnl_expr *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_expr_xfrm *x = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_XFRM_KEY: + memcpy(&x->key, data, sizeof(x->key)); + break; + case NFTNL_EXPR_XFRM_DIR: + memcpy(&x->dir, data, sizeof(x->dir)); + break; + case NFTNL_EXPR_XFRM_SPNUM: + memcpy(&x->spnum, data, sizeof(x->spnum)); + break; + case NFTNL_EXPR_XFRM_DREG: + memcpy(&x->dreg, data, sizeof(x->dreg)); + break; + default: + return -1; + } + return 0; +} + +static const void * +nftnl_expr_xfrm_get(const struct nftnl_expr *e, uint16_t type, + uint32_t *data_len) +{ + struct nftnl_expr_xfrm *x = nftnl_expr_data(e); + + switch(type) { + case NFTNL_EXPR_XFRM_KEY: + *data_len = sizeof(x->key); + return &x->key; + case NFTNL_EXPR_XFRM_DIR: + *data_len = sizeof(x->dir); + return &x->dir; + case NFTNL_EXPR_XFRM_SPNUM: + *data_len = sizeof(x->spnum); + return &x->spnum; + case NFTNL_EXPR_XFRM_DREG: + *data_len = sizeof(x->dreg); + return &x->dreg; + } + return NULL; +} + +static int nftnl_expr_xfrm_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_XFRM_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_XFRM_DREG: + case NFTA_XFRM_KEY: + case NFTA_XFRM_SPNUM: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_XFRM_DIR: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_expr_xfrm_build(struct nlmsghdr *nlh, const struct nftnl_expr *e) +{ + struct nftnl_expr_xfrm *x = nftnl_expr_data(e); + + if (e->flags & (1 << NFTNL_EXPR_XFRM_KEY)) + mnl_attr_put_u32(nlh, NFTA_XFRM_KEY, htonl(x->key)); + if (e->flags & (1 << NFTNL_EXPR_XFRM_DIR)) + mnl_attr_put_u8(nlh, NFTA_XFRM_DIR, x->dir); + if (e->flags & (1 << NFTNL_EXPR_XFRM_SPNUM)) + mnl_attr_put_u32(nlh, NFTA_XFRM_SPNUM, htonl(x->spnum)); + if (e->flags & (1 << NFTNL_EXPR_XFRM_DREG)) + mnl_attr_put_u32(nlh, NFTA_XFRM_DREG, htonl(x->dreg)); +} + +static int +nftnl_expr_xfrm_parse(struct nftnl_expr *e, struct nlattr *attr) +{ + struct nftnl_expr_xfrm *x = nftnl_expr_data(e); + struct nlattr *tb[NFTA_XFRM_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_expr_xfrm_cb, tb) < 0) + return -1; + + if (tb[NFTA_XFRM_KEY]) { + x->key = ntohl(mnl_attr_get_u32(tb[NFTA_XFRM_KEY])); + e->flags |= (1 << NFTNL_EXPR_XFRM_KEY); + } + if (tb[NFTA_XFRM_DIR]) { + x->dir = mnl_attr_get_u8(tb[NFTA_XFRM_DIR]); + e->flags |= (1 << NFTNL_EXPR_XFRM_DIR); + } + if (tb[NFTA_XFRM_SPNUM]) { + x->spnum = ntohl(mnl_attr_get_u32(tb[NFTA_XFRM_SPNUM])); + e->flags |= (1 << NFTNL_EXPR_XFRM_SPNUM); + } + if (tb[NFTA_XFRM_DREG]) { + x->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_XFRM_DREG])); + e->flags |= (1 << NFTNL_EXPR_XFRM_DREG); + } + return 0; +} + +static const char *xfrmkey2str_array[] = { + [NFT_XFRM_KEY_DADDR_IP4] = "daddr4", + [NFT_XFRM_KEY_SADDR_IP4] = "saddr4", + [NFT_XFRM_KEY_DADDR_IP6] = "daddr6", + [NFT_XFRM_KEY_SADDR_IP6] = "saddr6", + [NFT_XFRM_KEY_REQID] = "reqid", + [NFT_XFRM_KEY_SPI] = "spi", +}; + +static const char *xfrmkey2str(uint32_t key) +{ + if (key >= sizeof(xfrmkey2str_array) / sizeof(xfrmkey2str_array[0])) + return "unknown"; + + return xfrmkey2str_array[key]; +} + +static const char *xfrmdir2str_array[] = { + [XFRM_POLICY_IN] = "in", + [XFRM_POLICY_OUT] = "out", +}; + +static const char *xfrmdir2str(uint8_t dir) +{ + if (dir >= sizeof(xfrmdir2str_array) / sizeof(xfrmdir2str_array[0])) + return "unknown"; + + return xfrmdir2str_array[dir]; +} + +static int +nftnl_expr_xfrm_snprintf(char *buf, size_t remain, + uint32_t flags, const struct nftnl_expr *e) +{ + struct nftnl_expr_xfrm *x = nftnl_expr_data(e); + int ret, offset = 0; + + if (e->flags & (1 << NFTNL_EXPR_XFRM_DREG)) { + ret = snprintf(buf, remain, "load %s %u %s => reg %u ", + xfrmdir2str(x->dir), + x->spnum, + xfrmkey2str(x->key), x->dreg); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + return offset; +} + +struct expr_ops expr_ops_xfrm = { + .name = "xfrm", + .alloc_len = sizeof(struct nftnl_expr_xfrm), + .max_attr = NFTA_XFRM_MAX, + .set = nftnl_expr_xfrm_set, + .get = nftnl_expr_xfrm_get, + .parse = nftnl_expr_xfrm_parse, + .build = nftnl_expr_xfrm_build, + .output = nftnl_expr_xfrm_snprintf, +}; |