A data structure used to parse NFT_MSG_(NEW|DEL)DEV messages into. Since the kernel does not support these message types in requests yet, implement a parser and getters only. Signed-off-by: Phil Sutter <phil@xxxxxx> --- include/libnftnl/Makefile.am | 1 + include/libnftnl/device.h | 39 +++++++ include/linux/netfilter/nf_tables.h | 8 ++ src/Makefile.am | 1 + src/device.c | 153 ++++++++++++++++++++++++++++ src/libnftnl.map | 10 ++ 6 files changed, 212 insertions(+) create mode 100644 include/libnftnl/device.h create mode 100644 src/device.c diff --git a/include/libnftnl/Makefile.am b/include/libnftnl/Makefile.am index d846a574f4386..feaa7285f0070 100644 --- a/include/libnftnl/Makefile.am +++ b/include/libnftnl/Makefile.am @@ -2,6 +2,7 @@ pkginclude_HEADERS = batch.h \ table.h \ trace.h \ chain.h \ + device.h \ object.h \ rule.h \ expr.h \ diff --git a/include/libnftnl/device.h b/include/libnftnl/device.h new file mode 100644 index 0000000000000..8c437a1fe3ccf --- /dev/null +++ b/include/libnftnl/device.h @@ -0,0 +1,39 @@ +#ifndef _LIBNFTNL_DEVICE_H_ +#define _LIBNFTNL_DEVICE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct nftnl_device; + +struct nftnl_device *nftnl_device_alloc(void); +void nftnl_device_free(const struct nftnl_device *); + +enum nftnl_device_attr { + NFTNL_DEVICE_NAME = 0, + NFTNL_DEVICE_TABLE, + NFTNL_DEVICE_FLOWTABLE, + NFTNL_DEVICE_CHAIN, + NFTNL_DEVICE_SPEC, + NFTNL_DEVICE_FAMILY, + __NFTNL_DEVICE_MAX, +}; +#define NFTNL_DEVICE_MAX (__NFTNL_DEVICE_MAX - 1) + +bool nftnl_device_is_set(const struct nftnl_device *d, uint16_t attr); + +const void *nftnl_device_get_data(const struct nftnl_device *d, + uint16_t attr, uint32_t *data_len); +const char *nftnl_device_get_str(const struct nftnl_device *d, uint16_t attr); +int32_t nftnl_device_get_s32(const struct nftnl_device *d, uint16_t attr); + +struct nlmsghdr; + +int nftnl_device_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_device *d); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _LIBNFTNL_DEVICE_H_ */ diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index c48b19333630d..2f73d367cf429 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -1740,10 +1740,18 @@ enum nft_synproxy_attributes { * enum nft_device_attributes - nf_tables device netlink attributes * * @NFTA_DEVICE_NAME: name of this device (NLA_STRING) + * @NFTA_DEVICE_TABLE: table containing the flowtable or chain hooking into the device (NLA_STRING) + * @NFTA_DEVICE_FLOWTABLE: flowtable hooking into the device (NLA_STRING) + * @NFTA_DEVICE_CHAIN: chain hooking into the device (NLA_STRING) + * @NFTA_DEVICE_SPEC: hook spec matching the device (NLA_STRING) */ enum nft_devices_attributes { NFTA_DEVICE_UNSPEC, NFTA_DEVICE_NAME, + NFTA_DEVICE_TABLE, + NFTA_DEVICE_FLOWTABLE, + NFTA_DEVICE_CHAIN, + NFTA_DEVICE_SPEC, __NFTA_DEVICE_MAX }; #define NFTA_DEVICE_MAX (__NFTA_DEVICE_MAX - 1) diff --git a/src/Makefile.am b/src/Makefile.am index 3cd259c04d1c3..34dbe7ced1a2a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,6 +13,7 @@ libnftnl_la_SOURCES = utils.c \ table.c \ trace.c \ chain.c \ + device.c \ object.c \ rule.c \ set.c \ diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000000000..79102f34752ea --- /dev/null +++ b/src/device.c @@ -0,0 +1,153 @@ +/* + * (C) 2024 Red Hat GmbH + * Author: Phil Sutter <phil@xxxxxx> + * + * 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 <linux/netfilter/nfnetlink.h> + +#include <libnftnl/device.h> + +struct nftnl_device { + const char *name; + const char *table; + const char *flowtable; + const char *chain; + const char *spec; + int32_t family; + uint32_t flags; +}; + +EXPORT_SYMBOL(nftnl_device_alloc); +struct nftnl_device *nftnl_device_alloc(void) +{ + return calloc(1, sizeof(struct nftnl_device)); +} + +EXPORT_SYMBOL(nftnl_device_free); +void nftnl_device_free(const struct nftnl_device *d) +{ + if (d->flags & (1 << NFTNL_DEVICE_NAME)) + xfree(d->name); + if (d->flags & (1 << NFTNL_DEVICE_TABLE)) + xfree(d->table); + if (d->flags & (1 << NFTNL_DEVICE_FLOWTABLE)) + xfree(d->flowtable); + if (d->flags & (1 << NFTNL_DEVICE_CHAIN)) + xfree(d->chain); + if (d->flags & (1 << NFTNL_DEVICE_SPEC)) + xfree(d->spec); + xfree(d); +} + +EXPORT_SYMBOL(nftnl_device_is_set); +bool nftnl_device_is_set(const struct nftnl_device *d, uint16_t attr) +{ + return d->flags & (1 << attr); +} + +EXPORT_SYMBOL(nftnl_device_get_data); +const void *nftnl_device_get_data(const struct nftnl_device *d, + uint16_t attr, uint32_t *data_len) +{ + if (!(d->flags & (1 << attr))) + return NULL; + + switch (attr) { + case NFTNL_DEVICE_NAME: + *data_len = strlen(d->name) + 1; + return d->name; + case NFTNL_DEVICE_TABLE: + *data_len = strlen(d->table) + 1; + return d->table; + case NFTNL_DEVICE_FLOWTABLE: + *data_len = strlen(d->flowtable) + 1; + return d->flowtable; + case NFTNL_DEVICE_CHAIN: + *data_len = strlen(d->chain) + 1; + return d->chain; + case NFTNL_DEVICE_SPEC: + *data_len = strlen(d->spec) + 1; + return d->spec; + case NFTNL_DEVICE_FAMILY: + *data_len = sizeof(int32_t); + return &d->family; + } + return NULL; +} + +EXPORT_SYMBOL(nftnl_device_get_str); +const char *nftnl_device_get_str(const struct nftnl_device *d, uint16_t attr) +{ + uint32_t data_len; + + return nftnl_device_get_data(d, attr, &data_len); +} + +EXPORT_SYMBOL(nftnl_device_get_s32); +int32_t nftnl_device_get_s32(const struct nftnl_device *d, uint16_t attr) +{ + uint32_t data_len = 0; + const int32_t *val = nftnl_device_get_data(d, attr, &data_len); + + nftnl_assert(val, attr, data_len == sizeof(int32_t)); + + return val ? *val : 0; +} + +static int nftnl_device_parse_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_DEVICE_MAX) < 0) + return MNL_CB_OK; + + /* all attributes are of string type */ + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + abi_breakage(); + + tb[type] = attr; + return MNL_CB_OK; +} + +EXPORT_SYMBOL(nftnl_device_nlmsg_parse); +int nftnl_device_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_device *d) +{ + struct nlattr *tb[NFTA_DEVICE_MAX + 1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + int ret = 0; + + if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_device_parse_attr_cb, tb) < 0) + return -1; + + if (nftnl_parse_str_attr(tb[NFTA_DEVICE_NAME], NFTNL_DEVICE_NAME, + &d->name, &d->flags) < 0) + return -1; + if (nftnl_parse_str_attr(tb[NFTA_DEVICE_TABLE], NFTNL_DEVICE_TABLE, + &d->table, &d->flags) < 0) + return -1; + if (nftnl_parse_str_attr(tb[NFTA_DEVICE_FLOWTABLE], + NFTNL_DEVICE_FLOWTABLE, + &d->flowtable, &d->flags) < 0) + return -1; + if (nftnl_parse_str_attr(tb[NFTA_DEVICE_CHAIN], NFTNL_DEVICE_CHAIN, + &d->chain, &d->flags) < 0) + return -1; + if (tb[NFTA_DEVICE_SPEC]) { + d->spec = strdup(mnl_attr_get_ifname(tb[NFTA_DEVICE_SPEC])); + if (!d->spec) + return -1; + d->flags |= (1 << NFTNL_DEVICE_SPEC); + } + + d->family = nfg->nfgen_family; + d->flags |= (1 << NFTNL_DEVICE_FAMILY); + + return ret; +} diff --git a/src/libnftnl.map b/src/libnftnl.map index 8fffff19eb2e2..96ee683fb0611 100644 --- a/src/libnftnl.map +++ b/src/libnftnl.map @@ -383,3 +383,13 @@ LIBNFTNL_16 { LIBNFTNL_17 { nftnl_set_elem_nlmsg_build; } LIBNFTNL_16; + +LIBNFTNL_18 { + nftnl_device_alloc; + nftnl_device_free; + nftnl_device_is_set; + nftnl_device_get_data; + nftnl_device_get_str; + nftnl_device_get_s32; + nftnl_device_nlmsg_parse; +} LIBNFTNL_17; -- 2.43.0