Add support for ct expectation objects, used to define specific expectations. Signed-off-by: Stéphane Veyret <sveyret@xxxxxxxxx> --- include/libnftnl/object.h | 8 ++ include/linux/netfilter/nf_tables.h | 14 +- include/obj.h | 8 ++ src/Makefile.am | 1 + src/obj/ct_expect.c | 213 ++++++++++++++++++++++++++++ src/object.c | 1 + 6 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 src/obj/ct_expect.c diff --git a/include/libnftnl/object.h b/include/libnftnl/object.h index 4ce2230..cce0713 100644 --- a/include/libnftnl/object.h +++ b/include/libnftnl/object.h @@ -70,6 +70,14 @@ enum { NFTNL_OBJ_CT_TIMEOUT_ARRAY, }; +enum { + NFTNL_OBJ_CT_EXPECT_L3PROTO = NFTNL_OBJ_BASE, + NFTNL_OBJ_CT_EXPECT_L4PROTO, + NFTNL_OBJ_CT_EXPECT_DPORT, + NFTNL_OBJ_CT_EXPECT_TIMEOUT, + NFTNL_OBJ_CT_EXPECT_SIZE, +}; + enum { NFTNL_OBJ_LIMIT_RATE = NFTNL_OBJ_BASE, NFTNL_OBJ_LIMIT_UNIT, diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index fd38cdc..7eb8a5b 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -1429,6 +1429,17 @@ enum nft_ct_timeout_attributes { }; #define NFTA_CT_TIMEOUT_MAX (__NFTA_CT_TIMEOUT_MAX - 1) +enum nft_ct_expectation_attributes { + NFTA_CT_EXPECT_UNSPEC, + NFTA_CT_EXPECT_L3PROTO, + NFTA_CT_EXPECT_L4PROTO, + NFTA_CT_EXPECT_DPORT, + NFTA_CT_EXPECT_TIMEOUT, + NFTA_CT_EXPECT_SIZE, + __NFTA_CT_EXPECT_MAX, +}; +#define NFTA_CT_EXPECT_MAX (__NFTA_CT_EXPECT_MAX - 1) + #define NFT_OBJECT_UNSPEC 0 #define NFT_OBJECT_COUNTER 1 #define NFT_OBJECT_QUOTA 2 @@ -1438,7 +1449,8 @@ enum nft_ct_timeout_attributes { #define NFT_OBJECT_TUNNEL 6 #define NFT_OBJECT_CT_TIMEOUT 7 #define NFT_OBJECT_SECMARK 8 -#define __NFT_OBJECT_MAX 9 +#define NFT_OBJECT_CT_EXPECT 9 +#define __NFT_OBJECT_MAX 10 #define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1) /** diff --git a/include/obj.h b/include/obj.h index 35b5c40..9394d79 100644 --- a/include/obj.h +++ b/include/obj.h @@ -42,6 +42,13 @@ struct nftnl_obj { uint8_t l4proto; uint32_t timeout[NFTNL_CTTIMEOUT_ARRAY_MAX]; } ct_timeout; + struct nftnl_obj_ct_expect { + uint16_t l3proto; + uint16_t dport; + uint8_t l4proto; + uint8_t size; + uint32_t timeout; + } ct_expect; struct nftnl_obj_limit { uint64_t rate; uint64_t unit; @@ -99,6 +106,7 @@ extern struct obj_ops obj_ops_counter; extern struct obj_ops obj_ops_quota; extern struct obj_ops obj_ops_ct_helper; extern struct obj_ops obj_ops_ct_timeout; +extern struct obj_ops obj_ops_ct_expect; extern struct obj_ops obj_ops_limit; extern struct obj_ops obj_ops_tunnel; extern struct obj_ops obj_ops_secmark; diff --git a/src/Makefile.am b/src/Makefile.am index 2d5873f..8f9c022 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -65,4 +65,5 @@ libnftnl_la_SOURCES = utils.c \ obj/limit.c \ obj/ct_timeout.c \ obj/secmark.c \ + obj/ct_expect.c \ libnftnl.map diff --git a/src/obj/ct_expect.c b/src/obj/ct_expect.c new file mode 100644 index 0000000..c0bb5ba --- /dev/null +++ b/src/obj/ct_expect.c @@ -0,0 +1,213 @@ +/* + * (C) 2019 by Stéphane Veyret <sveyret@xxxxxxxxx> + * + * 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 <arpa/inet.h> +#include <errno.h> + +#include <libmnl/libmnl.h> + +#include "obj.h" + +static int nftnl_obj_ct_expect_set(struct nftnl_obj *e, uint16_t type, + const void *data, uint32_t data_len) +{ + struct nftnl_obj_ct_expect *exp = nftnl_obj_data(e); + + switch (type) { + case NFTNL_OBJ_CT_EXPECT_L3PROTO: + memcpy(&exp->l3proto, data, sizeof(exp->l3proto)); + break; + case NFTNL_OBJ_CT_EXPECT_L4PROTO: + memcpy(&exp->l4proto, data, sizeof(exp->l4proto)); + break; + case NFTNL_OBJ_CT_EXPECT_DPORT: + memcpy(&exp->dport, data, sizeof(exp->dport)); + break; + case NFTNL_OBJ_CT_EXPECT_TIMEOUT: + memcpy(&exp->timeout, data, sizeof(exp->timeout)); + break; + case NFTNL_OBJ_CT_EXPECT_SIZE: + memcpy(&exp->size, data, sizeof(exp->size)); + break; + default: + return -1; + } + return 0; +} + +static const void *nftnl_obj_ct_expect_get(const struct nftnl_obj *e, + uint16_t type, uint32_t *data_len) +{ + struct nftnl_obj_ct_expect *exp = nftnl_obj_data(e); + + switch (type) { + case NFTNL_OBJ_CT_EXPECT_L3PROTO: + *data_len = sizeof(exp->l3proto); + return &exp->l3proto; + case NFTNL_OBJ_CT_EXPECT_L4PROTO: + *data_len = sizeof(exp->l4proto); + return &exp->l4proto; + case NFTNL_OBJ_CT_EXPECT_DPORT: + *data_len = sizeof(exp->dport); + return &exp->dport; + case NFTNL_OBJ_CT_EXPECT_TIMEOUT: + *data_len = sizeof(exp->timeout); + return &exp->timeout; + case NFTNL_OBJ_CT_EXPECT_SIZE: + *data_len = sizeof(exp->size); + return &exp->size; + } + return NULL; +} + +static int nftnl_obj_ct_expect_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_CT_EXPECT_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NFTA_CT_EXPECT_L3PROTO: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + abi_breakage(); + break; + case NFTA_CT_EXPECT_L4PROTO: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + abi_breakage(); + break; + case NFTA_CT_EXPECT_DPORT: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + abi_breakage(); + break; + case NFTA_CT_EXPECT_TIMEOUT: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + case NFTA_CT_EXPECT_SIZE: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void +nftnl_obj_ct_expect_build(struct nlmsghdr *nlh, const struct nftnl_obj *e) +{ + struct nftnl_obj_ct_expect *exp = nftnl_obj_data(e); + + if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_L3PROTO)) + mnl_attr_put_u16(nlh, NFTA_CT_EXPECT_L3PROTO, htons(exp->l3proto)); + if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_L4PROTO)) + mnl_attr_put_u8(nlh, NFTA_CT_EXPECT_L4PROTO, exp->l4proto); + if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_DPORT)) + mnl_attr_put_u16(nlh, NFTA_CT_EXPECT_DPORT, htons(exp->dport)); + if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_TIMEOUT)) + mnl_attr_put_u32(nlh, NFTA_CT_EXPECT_TIMEOUT, exp->timeout); + if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_SIZE)) + mnl_attr_put_u8(nlh, NFTA_CT_EXPECT_SIZE, exp->size); +} + +static int +nftnl_obj_ct_expect_parse(struct nftnl_obj *e, struct nlattr *attr) +{ + struct nftnl_obj_ct_expect *exp = nftnl_obj_data(e); + struct nlattr *tb[NFTA_CT_EXPECT_MAX + 1] = {}; + + if (mnl_attr_parse_nested(attr, nftnl_obj_ct_expect_cb, tb) < 0) + return -1; + + if (tb[NFTA_CT_EXPECT_L3PROTO]) { + exp->l3proto = ntohs(mnl_attr_get_u16(tb[NFTA_CT_EXPECT_L3PROTO])); + e->flags |= (1 << NFTNL_OBJ_CT_EXPECT_L3PROTO); + } + if (tb[NFTA_CT_EXPECT_L4PROTO]) { + exp->l4proto = mnl_attr_get_u8(tb[NFTA_CT_EXPECT_L4PROTO]); + e->flags |= (1 << NFTNL_OBJ_CT_EXPECT_L4PROTO); + } + if (tb[NFTA_CT_EXPECT_DPORT]) { + exp->dport = ntohs(mnl_attr_get_u16(tb[NFTA_CT_EXPECT_DPORT])); + e->flags |= (1 << NFTNL_OBJ_CT_EXPECT_DPORT); + } + if (tb[NFTA_CT_EXPECT_TIMEOUT]) { + exp->timeout = mnl_attr_get_u32(tb[NFTA_CT_EXPECT_TIMEOUT]); + e->flags |= (1 << NFTNL_OBJ_CT_EXPECT_TIMEOUT); + } + if (tb[NFTA_CT_EXPECT_SIZE]) { + exp->size = mnl_attr_get_u8(tb[NFTA_CT_EXPECT_SIZE]); + e->flags |= (1 << NFTNL_OBJ_CT_EXPECT_SIZE); + } + + return 0; +} + +static int nftnl_obj_ct_expect_snprintf_default(char *buf, size_t len, + const struct nftnl_obj *e) +{ + int ret = 0; + int offset = 0, remain = len; + struct nftnl_obj_ct_expect *exp = nftnl_obj_data(e); + + if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_L3PROTO)) { + ret = snprintf(buf + offset, len, "family %d ", exp->l3proto); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_L4PROTO)) { + ret = snprintf(buf + offset, len, "protocol %d ", exp->l4proto); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_DPORT)) { + ret = snprintf(buf + offset, len, "dport %d ", exp->dport); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_TIMEOUT)) { + ret = snprintf(buf + offset, len, "timeout %d ", exp->timeout); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_SIZE)) { + ret = snprintf(buf + offset, len, "size %d ", exp->size); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + buf[offset] = '\0'; + return offset; +} + +static int nftnl_obj_ct_expect_snprintf(char *buf, size_t len, uint32_t type, + uint32_t flags, + const struct nftnl_obj *e) +{ + if (len) + buf[0] = '\0'; + + switch (type) { + case NFTNL_OUTPUT_DEFAULT: + return nftnl_obj_ct_expect_snprintf_default(buf, len, e); + case NFTNL_OUTPUT_JSON: + default: + break; + } + return -1; +} + +struct obj_ops obj_ops_ct_expect = { + .name = "ct_expect", + .type = NFT_OBJECT_CT_EXPECT, + .alloc_len = sizeof(struct nftnl_obj_ct_expect), + .max_attr = NFTA_CT_EXPECT_MAX, + .set = nftnl_obj_ct_expect_set, + .get = nftnl_obj_ct_expect_get, + .parse = nftnl_obj_ct_expect_parse, + .build = nftnl_obj_ct_expect_build, + .snprintf = nftnl_obj_ct_expect_snprintf, +}; diff --git a/src/object.c b/src/object.c index 5c8d183..d8c87ee 100644 --- a/src/object.c +++ b/src/object.c @@ -33,6 +33,7 @@ static struct obj_ops *obj_ops[__NFT_OBJECT_MAX] = { [NFT_OBJECT_TUNNEL] = &obj_ops_tunnel, [NFT_OBJECT_CT_TIMEOUT] = &obj_ops_ct_timeout, [NFT_OBJECT_SECMARK] = &obj_ops_secmark, + [NFT_OBJECT_CT_EXPECT] = &obj_ops_ct_expect, }; static struct obj_ops *nftnl_obj_ops_lookup(uint32_t type) -- 2.21.0