[libnftnl,2/2] chain: multi-device support
diff mbox series

Message ID 20191018094947.9531-2-pablo@netfilter.org
State Changes Requested
Delegated to: Pablo Neira
Headers show
Series
  • [libnftnl,1/2] flowtable: device array dynamic allocation
Related show

Commit Message

Pablo Neira Ayuso Oct. 18, 2019, 9:49 a.m. UTC
Add support for NFTA_HOOK_DEVS.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/libnftnl/chain.h            |   1 +
 include/linux/netfilter/nf_tables.h |   2 +
 src/chain.c                         | 107 +++++++++++++++++++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

Patch
diff mbox series

diff --git a/include/libnftnl/chain.h b/include/libnftnl/chain.h
index 31b48cf32bed..f84a2a3a20f2 100644
--- a/include/libnftnl/chain.h
+++ b/include/libnftnl/chain.h
@@ -31,6 +31,7 @@  enum nftnl_chain_attr {
 	NFTNL_CHAIN_HANDLE,
 	NFTNL_CHAIN_TYPE,
 	NFTNL_CHAIN_DEV,
+	NFTNL_CHAIN_DEVICES,
 	__NFTNL_CHAIN_MAX
 };
 #define NFTNL_CHAIN_MAX (__NFTNL_CHAIN_MAX - 1)
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 2e49bc6541a4..81c27d3c9ffd 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -144,12 +144,14 @@  enum nft_list_attributes {
  * @NFTA_HOOK_HOOKNUM: netfilter hook number (NLA_U32)
  * @NFTA_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
  * @NFTA_HOOK_DEV: netdevice name (NLA_STRING)
+ * @NFTA_HOOK_DEVS: list of netdevices (NLA_NESTED)
  */
 enum nft_hook_attributes {
 	NFTA_HOOK_UNSPEC,
 	NFTA_HOOK_HOOKNUM,
 	NFTA_HOOK_PRIORITY,
 	NFTA_HOOK_DEV,
+	NFTA_HOOK_DEVS,
 	__NFTA_HOOK_MAX
 };
 #define NFTA_HOOK_MAX		(__NFTA_HOOK_MAX - 1)
diff --git a/src/chain.c b/src/chain.c
index 26f9b9d61053..c64bf8d583fe 100644
--- a/src/chain.c
+++ b/src/chain.c
@@ -38,6 +38,8 @@  struct nftnl_chain {
 	const char	*type;
 	const char	*table;
 	const char	*dev;
+	const char	**dev_array;
+	int		dev_array_len;
 	uint32_t	family;
 	uint32_t	policy;
 	uint32_t	hooknum;
@@ -109,6 +111,7 @@  EXPORT_SYMBOL(nftnl_chain_free);
 void nftnl_chain_free(const struct nftnl_chain *c)
 {
 	struct nftnl_rule *r, *tmp;
+	int i;
 
 	list_for_each_entry_safe(r, tmp, &c->rule_list, head)
 		nftnl_rule_free(r);
@@ -121,6 +124,12 @@  void nftnl_chain_free(const struct nftnl_chain *c)
 		xfree(c->type);
 	if (c->flags & (1 << NFTNL_CHAIN_DEV))
 		xfree(c->dev);
+	if (c->flags & (1 << NFTNL_CHAIN_DEVICES)) {
+		for (i = 0; i < c->dev_array_len; i++)
+			xfree(c->dev_array[i]);
+
+		xfree(c->dev_array);
+	}
 	xfree(c);
 }
 
@@ -133,6 +142,8 @@  bool nftnl_chain_is_set(const struct nftnl_chain *c, uint16_t attr)
 EXPORT_SYMBOL(nftnl_chain_unset);
 void nftnl_chain_unset(struct nftnl_chain *c, uint16_t attr)
 {
+	int i;
+
 	if (!(c->flags & (1 << attr)))
 		return;
 
@@ -159,6 +170,11 @@  void nftnl_chain_unset(struct nftnl_chain *c, uint16_t attr)
 	case NFTNL_CHAIN_DEV:
 		xfree(c->dev);
 		break;
+	case NFTNL_CHAIN_DEVICES:
+		for (i = 0; i < c->dev_array_len; i++)
+			xfree(c->dev_array[i]);
+		xfree(c->dev_array);
+		break;
 	default:
 		return;
 	}
@@ -180,6 +196,9 @@  EXPORT_SYMBOL(nftnl_chain_set_data);
 int nftnl_chain_set_data(struct nftnl_chain *c, uint16_t attr,
 			 const void *data, uint32_t data_len)
 {
+	const char **dev_array;
+	int len = 0, i;
+
 	nftnl_assert_attr_exists(attr, NFTNL_CHAIN_MAX);
 	nftnl_assert_validate(data, nftnl_chain_validate, attr, data_len);
 
@@ -240,6 +259,26 @@  int nftnl_chain_set_data(struct nftnl_chain *c, uint16_t attr,
 		if (!c->dev)
 			return -1;
 		break;
+	case NFTNL_CHAIN_DEVICES:
+		dev_array = (const char **)data;
+		while (dev_array[len] != NULL)
+			len++;
+
+		if (c->flags & (1 << NFTNL_CHAIN_DEVICES)) {
+			for (i = 0; i < c->dev_array_len; i++)
+				xfree(c->dev_array[i]);
+			xfree(c->dev_array);
+		}
+
+		c->dev_array = calloc(len + 1, sizeof(char *));
+		if (!c->dev_array)
+			return -1;
+
+		for (i = 0; i < len; i++)
+			c->dev_array[i] = strdup(dev_array[i]);
+
+		c->dev_array_len = len;
+		break;
 	}
 	c->flags |= (1 << attr);
 	return 0;
@@ -325,6 +364,8 @@  const void *nftnl_chain_get_data(const struct nftnl_chain *c, uint16_t attr,
 	case NFTNL_CHAIN_DEV:
 		*data_len = strlen(c->dev) + 1;
 		return c->dev;
+	case NFTNL_CHAIN_DEVICES:
+		return &c->dev_array[0];
 	}
 	return NULL;
 }
@@ -389,6 +430,8 @@  uint8_t nftnl_chain_get_u8(const struct nftnl_chain *c, uint16_t attr)
 EXPORT_SYMBOL(nftnl_chain_nlmsg_build_payload);
 void nftnl_chain_nlmsg_build_payload(struct nlmsghdr *nlh, const struct nftnl_chain *c)
 {
+	int i;
+
 	if (c->flags & (1 << NFTNL_CHAIN_TABLE))
 		mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, c->table);
 	if (c->flags & (1 << NFTNL_CHAIN_NAME))
@@ -402,6 +445,15 @@  void nftnl_chain_nlmsg_build_payload(struct nlmsghdr *nlh, const struct nftnl_ch
 		mnl_attr_put_u32(nlh, NFTA_HOOK_PRIORITY, htonl(c->prio));
 		if (c->flags & (1 << NFTNL_CHAIN_DEV))
 			mnl_attr_put_strz(nlh, NFTA_HOOK_DEV, c->dev);
+		else if (c->flags & (1 << NFTNL_CHAIN_DEVICES)) {
+			struct nlattr *nest_dev;
+
+			nest_dev = mnl_attr_nest_start(nlh, NFTA_HOOK_DEVS);
+			for (i = 0; i < c->dev_array_len; i++)
+				mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME,
+						  c->dev_array[i]);
+			mnl_attr_nest_end(nlh, nest_dev);
+		}
 		mnl_attr_nest_end(nlh, nest);
 	}
 	if (c->flags & (1 << NFTNL_CHAIN_POLICY))
@@ -551,9 +603,44 @@  static int nftnl_chain_parse_hook_cb(const struct nlattr *attr, void *data)
 	return MNL_CB_OK;
 }
 
+static int nftnl_chain_parse_devs(struct nlattr *nest, struct nftnl_chain *c)
+{
+	const char **dev_array;
+	int len = 0, size = 8;
+	struct nlattr *attr;
+
+	dev_array = calloc(8, sizeof(char *));
+	if (!dev_array)
+		return -1;
+
+	mnl_attr_for_each_nested(attr, nest) {
+		if (mnl_attr_get_type(attr) != NFTA_DEVICE_NAME)
+			goto err;
+		dev_array[len++] = strdup(mnl_attr_get_str(attr));
+		if (len >= size) {
+			dev_array = realloc(dev_array, size * 2);
+			if (!dev_array)
+				goto err;
+
+			size *= 2;
+			memset(&dev_array[len], 0, size - len);
+		}
+	}
+
+	c->dev_array = dev_array;
+	c->dev_array_len = len;
+
+	return 0;
+err:
+	while (len--)
+		xfree(dev_array[len]);
+	return -1;
+}
+
 static int nftnl_chain_parse_hook(struct nlattr *attr, struct nftnl_chain *c)
 {
 	struct nlattr *tb[NFTA_HOOK_MAX+1] = {};
+	int ret;
 
 	if (mnl_attr_parse_nested(attr, nftnl_chain_parse_hook_cb, tb) < 0)
 		return -1;
@@ -572,6 +659,12 @@  static int nftnl_chain_parse_hook(struct nlattr *attr, struct nftnl_chain *c)
 			return -1;
 		c->flags |= (1 << NFTNL_CHAIN_DEV);
 	}
+	if (tb[NFTA_HOOK_DEVS]) {
+		ret = nftnl_chain_parse_devs(tb[NFTA_HOOK_DEVS], c);
+		if (ret < 0)
+			return -1;
+		c->flags |= (1 << NFTNL_CHAIN_DEVICES);
+	}
 
 	return 0;
 }
@@ -653,7 +746,7 @@  static inline int nftnl_str2hooknum(int family, const char *hook)
 static int nftnl_chain_snprintf_default(char *buf, size_t size,
 					const struct nftnl_chain *c)
 {
-	int ret, remain = size, offset = 0;
+	int ret, remain = size, offset = 0, i;
 
 	ret = snprintf(buf, remain, "%s %s %s use %u",
 		       nftnl_family2str(c->family), c->table, c->name, c->use);
@@ -681,6 +774,18 @@  static int nftnl_chain_snprintf_default(char *buf, size_t size,
 				       c->dev);
 			SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 		}
+		if (c->flags & (1 << NFTNL_CHAIN_DEVICES)) {
+			ret = snprintf(buf + offset, remain, " dev { ");
+			SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+			for (i = 0; i < c->dev_array_len; i++) {
+				ret = snprintf(buf + offset, remain, " %s ",
+					       c->dev_array[i]);
+				SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+			}
+			ret = snprintf(buf + offset, remain, " } ");
+			SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+		}
 	}
 
 	return offset;