From 00c179b4c9f9f9394cc6f79e5f2a68410631fd72 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 3 Aug 2012 17:31:48 +0200 Subject: initial version of libnftables It adds support for table, chain and rule handling. This also includes expression handling for each rule. Signed-off-by: Pablo Neira Ayuso --- src/expr/cmp.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++ src/expr/counter.c | 145 ++++++++++++++++++++++++++++++++++ src/expr/data_reg.c | 158 +++++++++++++++++++++++++++++++++++++ src/expr/data_reg.h | 23 ++++++ src/expr/immediate.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/expr/match.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++ src/expr/meta.c | 144 ++++++++++++++++++++++++++++++++++ src/expr/payload.c | 185 ++++++++++++++++++++++++++++++++++++++++++++ src/expr/target.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 1470 insertions(+) create mode 100644 src/expr/cmp.c create mode 100644 src/expr/counter.c create mode 100644 src/expr/data_reg.c create mode 100644 src/expr/data_reg.h create mode 100644 src/expr/immediate.c create mode 100644 src/expr/match.c create mode 100644 src/expr/meta.c create mode 100644 src/expr/payload.c create mode 100644 src/expr/target.c (limited to 'src/expr') diff --git a/src/expr/cmp.c b/src/expr/cmp.c new file mode 100644 index 0000000..cabaddd --- /dev/null +++ b/src/expr/cmp.c @@ -0,0 +1,194 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include "internal.h" + +#include +#include +#include +#include + +#include +#include +#include +#include "expr_ops.h" +#include "data_reg.h" + +struct nft_expr_cmp { + union nft_data_reg data; + uint8_t sreg; /* enum nft_registers */ + uint8_t op; /* enum nft_cmp_ops */ +}; + +static int +nft_rule_expr_cmp_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data; + + switch(type) { + case NFT_EXPR_CMP_SREG: + cmp->sreg = *((uint32_t *)data); + break; + case NFT_EXPR_CMP_OP: + cmp->op = *((uint32_t *)data); + break; + case NFT_EXPR_CMP_DATA: + memcpy(&cmp->data.val, data, data_len); + cmp->data.len = data_len; + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_cmp_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len) +{ + struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data; + + switch(type) { + case NFT_EXPR_CMP_SREG: + if (e->flags & (1 << NFT_EXPR_CMP_SREG)) { + *data_len = sizeof(cmp->sreg); + return &cmp->sreg; + } else + return NULL; + break; + case NFT_EXPR_CMP_OP: + if (e->flags & (1 << NFT_EXPR_CMP_OP)) { + *data_len = sizeof(cmp->op); + return &cmp->op; + } else + return NULL; + break; + case NFT_EXPR_CMP_DATA: + if (e->flags & (1 << NFT_EXPR_CMP_DATA)) { + *data_len = cmp->data.len; + return &cmp->data.val; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_cmp_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_CMP_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_CMP_SREG: + case NFTA_CMP_OP: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_CMP_DATA: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_cmp_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data; + + if (e->flags & (1 << NFT_EXPR_CMP_SREG)) + mnl_attr_put_u32(nlh, NFTA_CMP_SREG, htonl(cmp->sreg)); + if (e->flags & (1 << NFT_EXPR_CMP_OP)) + mnl_attr_put_u32(nlh, NFTA_CMP_OP, htonl(cmp->op)); + if (e->flags & (1 << NFT_EXPR_CMP_DATA)) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_CMP_DATA); + mnl_attr_put(nlh, NFTA_DATA_VALUE, cmp->data.len, cmp->data.val); + mnl_attr_nest_end(nlh, nest); + } +} + +static int +nft_rule_expr_cmp_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data; + struct nlattr *tb[NFTA_CMP_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_cmp_cb, tb) < 0) + return -1; + + if (tb[NFTA_CMP_SREG]) { + cmp->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_CMP_SREG])); + e->flags |= (1 << NFTA_CMP_SREG); + } + if (tb[NFTA_CMP_OP]) { + cmp->op = ntohl(mnl_attr_get_u32(tb[NFTA_CMP_OP])); + e->flags |= (1 << NFTA_CMP_OP); + } + if (tb[NFTA_CMP_DATA]) { + ret = nft_parse_data(&cmp->data, tb[NFTA_CMP_DATA], NULL); + e->flags |= (1 << NFTA_CMP_DATA); + } + + return ret; +} + +static char *expr_cmp_str[] = { + [NFT_CMP_EQ] = "eq", + [NFT_CMP_NEQ] = "neq", + [NFT_CMP_LT] = "lt", + [NFT_CMP_LTE] = "lte", + [NFT_CMP_GT] = "gt", + [NFT_CMP_GTE] = "gte", +}; + +static int +nft_rule_expr_cmp_snprintf(char *buf, size_t size, struct nft_rule_expr *e) +{ + struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data; + int len = size, offset = 0, ret, i; + + ret = snprintf(buf, len, "sreg=%u op=%s data=", + cmp->sreg, expr_cmp_str[cmp->op]); + SNPRINTF_BUFFER_SIZE(ret, size, len, offset); + + for (i=0; idata.len/sizeof(uint32_t); i++) { + ret = snprintf(buf+offset, len, "%.8x ", cmp->data.val[i]); + SNPRINTF_BUFFER_SIZE(ret, size, len, offset); + } + return offset; +} + +struct expr_ops expr_ops_cmp = { + .name = "cmp", + .alloc_len = sizeof(struct nft_expr_cmp), + .max_attr = NFTA_CMP_MAX, + .set = nft_rule_expr_cmp_set, + .get = nft_rule_expr_cmp_get, + .parse = nft_rule_expr_cmp_parse, + .build = nft_rule_expr_cmp_build, + .snprintf = nft_rule_expr_cmp_snprintf, +}; diff --git a/src/expr/counter.c b/src/expr/counter.c new file mode 100644 index 0000000..26b7d6f --- /dev/null +++ b/src/expr/counter.c @@ -0,0 +1,145 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include +#include +#include + +#include + +#include "internal.h" +#include +#include +#include "expr_ops.h" + +struct nft_expr_counter { + uint64_t pkts; + uint64_t bytes; +}; + +static int +nft_rule_expr_counter_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data; + + switch(type) { + case NFT_EXPR_CTR_BYTES: + ctr->bytes = *((uint64_t *)data); + break; + case NFT_EXPR_CTR_PACKETS: + ctr->pkts = *((uint64_t *)data); + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_counter_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len) +{ + struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data; + + switch(type) { + case NFT_EXPR_CTR_BYTES: + if (e->flags & (1 << NFT_EXPR_CTR_BYTES)) { + *data_len = sizeof(ctr->bytes); + return &ctr->bytes; + } else + return NULL; + break; + case NFT_EXPR_CTR_PACKETS: + if (e->flags & (1 << NFT_EXPR_CTR_PACKETS)) { + *data_len = sizeof(ctr->pkts); + return &ctr->pkts; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_counter_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_COUNTER_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_COUNTER_BYTES: + case NFTA_COUNTER_PACKETS: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_counter_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data; + + if (e->flags & (1 << NFT_EXPR_CTR_BYTES)) + mnl_attr_put_u64(nlh, NFTA_COUNTER_BYTES, htobe64(ctr->bytes)); + if (e->flags & (1 << NFT_EXPR_CTR_PACKETS)) + mnl_attr_put_u64(nlh, NFTA_COUNTER_PACKETS, htobe64(ctr->pkts)); +} + +static int +nft_rule_expr_counter_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data; + struct nlattr *tb[NFTA_COUNTER_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_counter_cb, tb) < 0) + return -1; + + if (tb[NFTA_COUNTER_BYTES]) { + ctr->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_BYTES])); + e->flags |= (1 << NFT_EXPR_CTR_BYTES); + } + if (tb[NFTA_COUNTER_PACKETS]) { + ctr->pkts = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_PACKETS])); + e->flags |= (1 << NFT_EXPR_CTR_PACKETS); + } + + return 0; +} + +static int +nft_rule_expr_counter_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data; + + return snprintf(buf, len, "pkts=%lu bytes=%lu ", + ctr->pkts, ctr->bytes); +} + +struct expr_ops expr_ops_counter = { + .name = "counter", + .alloc_len = sizeof(struct nft_expr_counter), + .max_attr = NFTA_COUNTER_MAX, + .set = nft_rule_expr_counter_set, + .get = nft_rule_expr_counter_get, + .parse = nft_rule_expr_counter_parse, + .build = nft_rule_expr_counter_build, + .snprintf = nft_rule_expr_counter_snprintf, +}; diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c new file mode 100644 index 0000000..5b14695 --- /dev/null +++ b/src/expr/data_reg.c @@ -0,0 +1,158 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include "expr_ops.h" +#include "data_reg.h" +#include "internal.h" + +static int nft_data_parse_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_DATA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_DATA_VALUE: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_DATA_VERDICT: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int nft_verdict_parse_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_VERDICT_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_VERDICT_CODE: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_VERDICT_CHAIN: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +nft_parse_verdict(union nft_data_reg *data, const struct nlattr *attr, int *type) +{ + struct nlattr *tb[NFTA_VERDICT_MAX+1]; + + if (mnl_attr_parse_nested(attr, nft_verdict_parse_cb, tb) < 0) { + perror("mnl_attr_parse_nested"); + return -1; + } + + if (!tb[NFTA_VERDICT_CODE]) + return -1; + + data->verdict = ntohl(mnl_attr_get_u32(tb[NFTA_VERDICT_CODE])); + + switch(data->verdict) { + case NF_ACCEPT: + case NF_DROP: + case NF_QUEUE: + case NFT_CONTINUE: + case NFT_BREAK: + case NFT_RETURN: + if (type) + *type = DATA_VERDICT; + data->len = sizeof(data->verdict); + break; + case NFT_JUMP: + case NFT_GOTO: + if (!tb[NFTA_VERDICT_CHAIN]) + return -1; + + data->chain = strdup(mnl_attr_get_str(tb[NFTA_VERDICT_CHAIN])); + if (type) + *type = DATA_CHAIN; + break; + default: + return -1; + } + + return 0; +} + +static int +__nft_parse_data(union nft_data_reg *data, const struct nlattr *attr) +{ + void *orig = mnl_attr_get_payload(attr); + size_t data_len = mnl_attr_get_payload_len(attr); + + if (data_len == 0) + return -1; + + if (data_len > sizeof(uint32_t) * 4) + return -1; + + memcpy(data->val, orig, data_len); + data->len = data_len; + + return 0; +} + +int nft_parse_data(union nft_data_reg *data, struct nlattr *attr, int *type) +{ + struct nlattr *tb[NFTA_DATA_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nft_data_parse_cb, tb) < 0) { + perror("mnl_attr_parse_nested"); + return -1; + } + if (tb[NFTA_DATA_VALUE]) { + if (type) + *type = DATA_VALUE; + + ret = __nft_parse_data(data, tb[NFTA_DATA_VALUE]); + if (ret < 0) + return ret; + } + if (tb[NFTA_DATA_VERDICT]) + ret = nft_parse_verdict(data, tb[NFTA_DATA_VERDICT], type); + + return ret; +} diff --git a/src/expr/data_reg.h b/src/expr/data_reg.h new file mode 100644 index 0000000..00eab63 --- /dev/null +++ b/src/expr/data_reg.h @@ -0,0 +1,23 @@ +#ifndef _DATA_H_ +#define _DATA_H_ + +enum { + DATA_VALUE, + DATA_VERDICT, + DATA_CHAIN, +}; + +union nft_data_reg { + struct { + uint32_t val[4]; + size_t len; + }; + struct { + int verdict; + char *chain; + }; +}; + +int nft_parse_data(union nft_data_reg *data, struct nlattr *attr, int *type); + +#endif diff --git a/src/expr/immediate.c b/src/expr/immediate.c new file mode 100644 index 0000000..68c27ec --- /dev/null +++ b/src/expr/immediate.c @@ -0,0 +1,215 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include +#include +#include +#include + +#include "internal.h" +#include +#include +#include +#include "expr_ops.h" +#include "data_reg.h" + +struct nft_expr_immediate { + union nft_data_reg data; + enum nft_registers dreg; +}; + +static int +nft_rule_expr_immediate_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data; + + switch(type) { + case NFT_EXPR_IMM_DREG: + imm->dreg = *((uint32_t *)data); + break; + case NFT_EXPR_IMM_DATA: + memcpy(&imm->data.val, data, data_len); + imm->data.len = data_len; + break; + case NFT_EXPR_IMM_VERDICT: + imm->data.verdict = *((uint32_t *)data); + break; + case NFT_EXPR_IMM_CHAIN: + if (imm->data.chain) + free(imm->data.chain); + + imm->data.chain = strdup(data); + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_immediate_get(struct nft_rule_expr *e, uint16_t type, + size_t *data_len) +{ + struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data; + + switch(type) { + case NFT_EXPR_IMM_DREG: + if (e->flags & (1 << NFT_EXPR_IMM_DREG)) { + *data_len = sizeof(imm->dreg); + return &imm->dreg; + } else + return NULL; + break; + case NFT_EXPR_IMM_DATA: + if (e->flags & (1 << NFT_EXPR_IMM_DATA)) { + *data_len = imm->data.len; + return &imm->data.val; + } else + return NULL; + break; + case NFT_EXPR_IMM_VERDICT: + if (e->flags & (1 << NFT_EXPR_IMM_VERDICT)) { + *data_len = sizeof(imm->data.verdict); + return &imm->data.verdict; + } else + return NULL; + break; + case NFT_EXPR_IMM_CHAIN: + if (e->flags & (1 << NFT_EXPR_IMM_CHAIN)) { + *data_len = strlen(imm->data.chain)+1; + return imm->data.chain; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_immediate_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_IMMEDIATE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_IMMEDIATE_DREG: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_IMMEDIATE_DATA: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_immediate_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data; + + if (e->flags & (1 << NFT_EXPR_IMM_DREG)) + mnl_attr_put_u32(nlh, NFTA_IMMEDIATE_DREG, htonl(imm->dreg)); + + /* Sane configurations allows you to set ONLY one of these two below */ + if (e->flags & (1 << NFT_EXPR_IMM_DATA)) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_IMMEDIATE_DATA); + mnl_attr_put(nlh, NFTA_DATA_VALUE, imm->data.len, imm->data.val); + mnl_attr_nest_end(nlh, nest); + + } else if (e->flags & (1 << NFT_EXPR_IMM_VERDICT)) { + struct nlattr *nest1, *nest2; + + nest1 = mnl_attr_nest_start(nlh, NFTA_IMMEDIATE_DATA); + nest2 = mnl_attr_nest_start(nlh, NFTA_DATA_VERDICT); + mnl_attr_put_u32(nlh, NFTA_VERDICT_CODE, htonl(imm->data.verdict)); + if (e->flags & (1 << NFT_EXPR_IMM_CHAIN)) + mnl_attr_put_strz(nlh, NFTA_VERDICT_CHAIN, imm->data.chain); + + mnl_attr_nest_end(nlh, nest1); + mnl_attr_nest_end(nlh, nest2); + } +} + +static int +nft_rule_expr_immediate_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data; + struct nlattr *tb[NFTA_IMMEDIATE_MAX+1] = {}; + int ret = 0; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_immediate_cb, tb) < 0) + return -1; + + if (tb[NFTA_IMMEDIATE_DREG]) { + imm->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_IMMEDIATE_DREG])); + e->flags |= (1 << NFT_EXPR_IMM_DREG); + } + if (tb[NFTA_IMMEDIATE_DATA]) { + int type; + + ret = nft_parse_data(&imm->data, tb[NFTA_IMMEDIATE_DATA], &type); + if (ret < 0) + return ret; + + switch(type) { + case DATA_VALUE: + /* real immediate data to be loaded to destination */ + e->flags |= (1 << NFT_EXPR_IMM_DATA); + break; + case DATA_VERDICT: + /* NF_ACCEPT, NF_DROP, NF_QUEUE and NFT_RETURN case */ + e->flags |= (1 << NFT_EXPR_IMM_VERDICT); + break; + case DATA_CHAIN: + /* NFT_GOTO and NFT_JUMP case */ + e->flags |= (1 << NFT_EXPR_IMM_VERDICT) | + (1 << NFT_EXPR_IMM_CHAIN); + break; + } + } + + return ret; +} + +static int +nft_rule_expr_immediate_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data; + + return snprintf(buf, len, "dreg=%u data=%u ", + imm->dreg, imm->data.val[0]); +} + +struct expr_ops expr_ops_immediate = { + .name = "immediate", + .alloc_len = sizeof(struct nft_expr_immediate), + .max_attr = NFTA_IMMEDIATE_MAX, + .set = nft_rule_expr_immediate_set, + .get = nft_rule_expr_immediate_get, + .parse = nft_rule_expr_immediate_parse, + .build = nft_rule_expr_immediate_build, + .snprintf = nft_rule_expr_immediate_snprintf, +}; diff --git a/src/expr/match.c b/src/expr/match.c new file mode 100644 index 0000000..a9e2dee --- /dev/null +++ b/src/expr/match.c @@ -0,0 +1,203 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include "internal.h" + +#include +#include +#include /* for memcpy */ +#include + +#include + +#include +#include + +#include + +#include "expr_ops.h" + +struct nft_expr_match { + char name[XT_EXTENSION_MAXNAMELEN]; + uint32_t rev; + uint32_t data_len; + const void *data; +}; + +static int +nft_rule_expr_match_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_match *mt = (struct nft_expr_match *)e->data; + + switch(type) { + case NFT_EXPR_MT_NAME: + memcpy(mt->name, data, XT_EXTENSION_MAXNAMELEN); + mt->name[XT_EXTENSION_MAXNAMELEN-1] = '\0'; + break; + case NFT_EXPR_MT_REV: + mt->rev = *((uint32_t *)data); + break; + case NFT_EXPR_MT_INFO: + if (mt->data) + free((void *)mt->data); + + mt->data = data; + mt->data_len = data_len; + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_match_get(struct nft_rule_expr *e, uint16_t type, + size_t *data_len) +{ + struct nft_expr_match *mt = (struct nft_expr_match *)e->data; + + switch(type) { + case NFT_EXPR_MT_NAME: + if (e->flags & (1 << NFT_EXPR_MT_NAME)) { + *data_len = sizeof(mt->name); + return mt->name; + } else + return NULL; + break; + case NFT_EXPR_MT_REV: + if (e->flags & (1 << NFT_EXPR_MT_REV)) { + *data_len = sizeof(mt->rev); + return &mt->rev; + } else + return NULL; + break; + case NFT_EXPR_MT_INFO: + if (e->flags & (1 << NFT_EXPR_MT_INFO)) { + *data_len = mt->data_len; + return mt->data; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_match_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_MATCH_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_MATCH_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_MATCH_REV: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_MATCH_INFO: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_match_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_match *mt = (struct nft_expr_match *)e->data; + + if (e->flags & (1 << NFT_EXPR_MT_NAME)) + mnl_attr_put_strz(nlh, NFTA_MATCH_NAME, mt->name); + if (e->flags & (1 << NFT_EXPR_MT_REV)) + mnl_attr_put_u32(nlh, NFTA_MATCH_REV, htonl(mt->rev)); + if (e->flags & (1 << NFT_EXPR_MT_INFO)) + mnl_attr_put(nlh, NFTA_MATCH_INFO, XT_ALIGN(mt->data_len), mt->data); +} + +static int nft_rule_expr_match_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_match *match = (struct nft_expr_match *)e->data; + struct nlattr *tb[NFTA_MATCH_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_match_cb, tb) < 0) + return -1; + + if (tb[NFTA_MATCH_NAME]) { + snprintf(match->name, XT_EXTENSION_MAXNAMELEN, "%s", + mnl_attr_get_str(tb[NFTA_MATCH_NAME])); + + match->name[XT_EXTENSION_MAXNAMELEN-1] = '\0'; + e->flags |= (1 << NFTA_MATCH_NAME); + } + + if (tb[NFTA_MATCH_REV]) { + match->rev = ntohl(mnl_attr_get_u32(tb[NFTA_MATCH_REV])); + e->flags |= (1 << NFTA_MATCH_REV); + } + + if (tb[NFTA_MATCH_INFO]) { + uint32_t len = mnl_attr_get_payload_len(tb[NFTA_MATCH_INFO]); + void *match_data; + + if (match->data) + free((void *) match->data); + + match_data = calloc(1, len); + if (match_data == NULL) + return -1; + + memcpy(match_data, mnl_attr_get_payload(tb[NFTA_MATCH_INFO]), len); + + match->data = match_data; + match->data_len = len; + + e->flags |= (1 << NFTA_MATCH_INFO); + } + + return 0; +} + +static int +nft_rule_expr_match_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_match *match = (struct nft_expr_match *)e->data; + + return snprintf(buf, len, "name=%s rev=%u ", + match->name, match->rev); +} + +struct expr_ops expr_ops_match = { + .name = "match", + .alloc_len = sizeof(struct nft_expr_match), + .max_attr = NFTA_MATCH_MAX, + .set = nft_rule_expr_match_set, + .get = nft_rule_expr_match_get, + .parse = nft_rule_expr_match_parse, + .build = nft_rule_expr_match_build, + .snprintf = nft_rule_expr_match_snprintf, +}; diff --git a/src/expr/meta.c b/src/expr/meta.c new file mode 100644 index 0000000..0251e43 --- /dev/null +++ b/src/expr/meta.c @@ -0,0 +1,144 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include +#include +#include + +#include + +#include "internal.h" +#include +#include +#include "expr_ops.h" + +struct nft_expr_meta { + uint8_t key; /* enum nft_meta_keys */ + uint8_t dreg; /* enum nft_registers */ +}; + +static int +nft_rule_expr_meta_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data; + + switch(type) { + case NFT_EXPR_META_KEY: + meta->key = *((uint32_t *)data); + break; + case NFT_EXPR_META_DREG: + meta->dreg = *((uint32_t *)data); + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_meta_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len) +{ + struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data; + + switch(type) { + case NFT_EXPR_META_KEY: + if (e->flags & (1 << NFT_EXPR_META_KEY)) { + *data_len = sizeof(meta->key); + return &meta->key; + } else + return NULL; + break; + case NFT_EXPR_META_DREG: + if (e->flags & (1 << NFT_EXPR_META_DREG)) { + *data_len = sizeof(meta->dreg); + return &meta->dreg; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_meta_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_META_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_META_KEY: + case NFTA_META_DREG: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_meta_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data; + + if (e->flags & (1 << NFT_EXPR_META_KEY)) + mnl_attr_put_u32(nlh, NFTA_META_KEY, htonl(meta->key)); + if (e->flags & (1 << NFT_EXPR_META_DREG)) + mnl_attr_put_u32(nlh, NFTA_META_DREG, htonl(meta->dreg)); +} + +static int +nft_rule_expr_meta_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data; + struct nlattr *tb[NFTA_META_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_meta_cb, tb) < 0) + return -1; + + if (tb[NFTA_META_KEY]) { + meta->key = ntohl(mnl_attr_get_u32(tb[NFTA_META_KEY])); + e->flags |= (1 << NFT_EXPR_META_KEY); + } + if (tb[NFTA_META_DREG]) { + meta->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_META_DREG])); + e->flags |= (1 << NFT_EXPR_META_DREG); + } + + return 0; +} + +static int +nft_rule_expr_meta_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data; + + return snprintf(buf, len, "dreg=%u key=%u ", meta->dreg, meta->key); +} + +struct expr_ops expr_ops_meta = { + .name = "meta", + .alloc_len = sizeof(struct nft_expr_meta), + .max_attr = NFTA_META_MAX, + .set = nft_rule_expr_meta_set, + .get = nft_rule_expr_meta_get, + .parse = nft_rule_expr_meta_parse, + .build = nft_rule_expr_meta_build, + .snprintf = nft_rule_expr_meta_snprintf, +}; diff --git a/src/expr/payload.c b/src/expr/payload.c new file mode 100644 index 0000000..746a127 --- /dev/null +++ b/src/expr/payload.c @@ -0,0 +1,185 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include "internal.h" + +#include +#include +#include + +#include + +#include + +#include + +#include "expr_ops.h" + +struct nft_expr_payload { + enum nft_registers dreg; + enum nft_payload_bases base; + unsigned int offset; + unsigned int len; +}; + +static int +nft_rule_expr_payload_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data; + + switch(type) { + case NFT_EXPR_PAYLOAD_DREG: + payload->dreg = *((uint32_t *)data); + break; + case NFT_EXPR_PAYLOAD_BASE: + payload->base = *((uint32_t *)data); + break; + case NFT_EXPR_PAYLOAD_OFFSET: + payload->offset = *((unsigned int *)data); + break; + case NFT_EXPR_PAYLOAD_LEN: + payload->len = *((unsigned int *)data); + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_payload_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len) +{ + struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data; + + switch(type) { + case NFT_EXPR_PAYLOAD_DREG: + if (e->flags & (1 << NFT_EXPR_PAYLOAD_DREG)) { + *data_len = sizeof(payload->dreg); + return &payload->dreg; + } else + return NULL; + break; + case NFT_EXPR_PAYLOAD_BASE: + if (e->flags & (1 << NFT_EXPR_PAYLOAD_BASE)) { + *data_len = sizeof(payload->base); + return &payload->base; + } else + return NULL; + break; + case NFT_EXPR_PAYLOAD_OFFSET: + if (e->flags & (1 << NFT_EXPR_PAYLOAD_OFFSET)) { + *data_len = sizeof(payload->offset); + return &payload->offset; + } else + return NULL; + break; + case NFT_EXPR_PAYLOAD_LEN: + if (e->flags & (1 << NFT_EXPR_PAYLOAD_LEN)) { + *data_len = sizeof(payload->len); + return &payload->len; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_payload_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_PAYLOAD_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_PAYLOAD_DREG: + case NFTA_PAYLOAD_BASE: + case NFTA_PAYLOAD_OFFSET: + case NFTA_PAYLOAD_LEN: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_payload_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data; + + if (e->flags & (1 << NFT_EXPR_PAYLOAD_DREG)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_DREG, htonl(payload->dreg)); + if (e->flags & (1 << NFT_EXPR_PAYLOAD_BASE)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_BASE, htonl(payload->base)); + if (e->flags & (1 << NFT_EXPR_PAYLOAD_OFFSET)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_OFFSET, htonl(payload->offset)); + if (e->flags & (1 << NFT_EXPR_PAYLOAD_LEN)) + mnl_attr_put_u32(nlh, NFTA_PAYLOAD_LEN, htonl(payload->len)); +} + +static int +nft_rule_expr_payload_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data; + struct nlattr *tb[NFTA_PAYLOAD_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_payload_cb, tb) < 0) + return -1; + + if (tb[NFTA_PAYLOAD_DREG]) { + payload->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_DREG])); + e->flags |= (1 << NFT_EXPR_PAYLOAD_DREG); + } + if (tb[NFTA_PAYLOAD_BASE]) { + payload->base = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_BASE])); + e->flags |= (1 << NFT_EXPR_PAYLOAD_BASE); + } + if (tb[NFTA_PAYLOAD_OFFSET]) { + payload->offset = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_OFFSET])); + e->flags |= (1 << NFT_EXPR_PAYLOAD_OFFSET); + } + if (tb[NFTA_PAYLOAD_LEN]) { + payload->len = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_LEN])); + e->flags |= (1 << NFT_EXPR_PAYLOAD_LEN); + } + + return 0; +} + +static int +nft_rule_expr_payload_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data; + + return snprintf(buf, len, "dreg=%u base=%u offset=%u len=%u ", + payload->dreg, payload->base, + payload->offset, payload->len); +} + +struct expr_ops expr_ops_payload = { + .name = "payload", + .alloc_len = sizeof(struct nft_expr_payload), + .max_attr = NFTA_PAYLOAD_MAX, + .set = nft_rule_expr_payload_set, + .get = nft_rule_expr_payload_get, + .parse = nft_rule_expr_payload_parse, + .build = nft_rule_expr_payload_build, + .snprintf = nft_rule_expr_payload_snprintf, +}; diff --git a/src/expr/target.c b/src/expr/target.c new file mode 100644 index 0000000..fccf64b --- /dev/null +++ b/src/expr/target.c @@ -0,0 +1,203 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ + +#include "internal.h" + +#include +#include +#include /* for memcpy */ +#include + +#include + +#include +#include + +#include + +#include "expr_ops.h" + +struct nft_expr_target { + char name[XT_EXTENSION_MAXNAMELEN]; + uint32_t rev; + uint32_t data_len; + const void *data; +}; + +static int +nft_rule_expr_target_set(struct nft_rule_expr *e, uint16_t type, + const void *data, size_t data_len) +{ + struct nft_expr_target *tg = (struct nft_expr_target *)e->data; + + switch(type) { + case NFT_EXPR_TG_NAME: + memcpy(tg->name, data, XT_EXTENSION_MAXNAMELEN); + tg->name[XT_EXTENSION_MAXNAMELEN-1] = '\0'; + break; + case NFT_EXPR_TG_REV: + tg->rev = *((uint32_t *)data); + break; + case NFT_EXPR_TG_INFO: + if (tg->data) + free((void *)tg->data); + + tg->data = data; + tg->data_len = data_len; + break; + default: + return -1; + } + return 0; +} + +static const void * +nft_rule_expr_target_get(struct nft_rule_expr *e, uint16_t type, + size_t *data_len) +{ + struct nft_expr_target *tg = (struct nft_expr_target *)e->data; + + switch(type) { + case NFT_EXPR_TG_NAME: + if (e->flags & (1 << NFT_EXPR_TG_NAME)) { + *data_len = sizeof(tg->name); + return tg->name; + } else + return NULL; + break; + case NFT_EXPR_TG_REV: + if (e->flags & (1 << NFT_EXPR_TG_REV)) { + *data_len = sizeof(tg->rev); + return &tg->rev; + } else + return NULL; + break; + case NFT_EXPR_TG_INFO: + if (e->flags & (1 << NFT_EXPR_TG_INFO)) { + *data_len = tg->data_len; + return tg->data; + } else + return NULL; + break; + default: + break; + } + return NULL; +} + +static int nft_rule_expr_target_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_TARGET_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_TARGET_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_TARGET_REV: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NFTA_TARGET_INFO: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nft_rule_expr_target_build(struct nlmsghdr *nlh, struct nft_rule_expr *e) +{ + struct nft_expr_target *tg = (struct nft_expr_target *)e->data; + + if (e->flags & (1 << NFT_EXPR_TG_NAME)) + mnl_attr_put_strz(nlh, NFTA_TARGET_NAME, tg->name); + if (e->flags & (1 << NFT_EXPR_TG_REV)) + mnl_attr_put_u32(nlh, NFTA_TARGET_REV, htonl(tg->rev)); + if (e->flags & (1 << NFT_EXPR_TG_INFO)) + mnl_attr_put(nlh, NFTA_TARGET_INFO, XT_ALIGN(tg->data_len), tg->data); +} + +static int nft_rule_expr_target_parse(struct nft_rule_expr *e, struct nlattr *attr) +{ + struct nft_expr_target *target = (struct nft_expr_target *)e->data; + struct nlattr *tb[NFTA_TARGET_MAX+1] = {}; + + if (mnl_attr_parse_nested(attr, nft_rule_expr_target_cb, tb) < 0) + return -1; + + if (tb[NFTA_TARGET_NAME]) { + snprintf(target->name, XT_EXTENSION_MAXNAMELEN, "%s", + mnl_attr_get_str(tb[NFTA_TARGET_NAME])); + + target->name[XT_EXTENSION_MAXNAMELEN-1] = '\0'; + e->flags |= (1 << NFT_EXPR_TG_NAME); + } + + if (tb[NFTA_TARGET_REV]) { + target->rev = ntohl(mnl_attr_get_u32(tb[NFTA_TARGET_REV])); + e->flags |= (1 << NFT_EXPR_TG_REV); + } + + if (tb[NFTA_TARGET_INFO]) { + uint32_t len = mnl_attr_get_len(tb[NFTA_TARGET_INFO]); + void *target_data; + + if (target->data) + free((void *) target->data); + + target_data = calloc(1, len); + if (target_data == NULL) + return -1; + + memcpy(target_data, mnl_attr_get_payload(tb[NFTA_TARGET_INFO]), len); + + target->data = target_data; + target->data_len = len; + + e->flags |= (1 << NFT_EXPR_TG_INFO); + } + + return 0; +} + +static int +nft_rule_expr_target_snprintf(char *buf, size_t len, struct nft_rule_expr *e) +{ + struct nft_expr_target *target = (struct nft_expr_target *)e->data; + + return snprintf(buf, len, "name=%s rev=%u ", + target->name, target->rev); +} + +struct expr_ops expr_ops_target = { + .name = "target", + .alloc_len = sizeof(struct nft_expr_target), + .max_attr = NFTA_TARGET_MAX, + .set = nft_rule_expr_target_set, + .get = nft_rule_expr_target_get, + .parse = nft_rule_expr_target_parse, + .build = nft_rule_expr_target_build, + .snprintf = nft_rule_expr_target_snprintf, +}; -- cgit v1.2.3