diff mbox

[1/2] libnftnl: rule: Change the "userdata" attribute to use new TLV buffer.

Message ID 1456008234-26845-2-git-send-email-carlosfg@riseup.net
State Superseded
Delegated to: Pablo Neira
Headers show

Commit Message

Carlos Falgueras García Feb. 20, 2016, 10:43 p.m. UTC
Now is it possible to store multiple variable length user data into a rule.

Signed-off-by: Carlos Falgueras García <carlosfg@riseup.net>
---
 src/rule.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 130 insertions(+), 28 deletions(-)
diff mbox

Patch

diff --git a/src/rule.c b/src/rule.c
index 3a32bf6..4e6f375 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -28,6 +28,7 @@ 
 #include <libnftnl/rule.h>
 #include <libnftnl/set.h>
 #include <libnftnl/expr.h>
+#include <libnftnl/attr.h>
 
 struct nftnl_rule {
 	struct list_head head;
@@ -38,10 +39,7 @@  struct nftnl_rule {
 	const char	*chain;
 	uint64_t	handle;
 	uint64_t	position;
-	struct {
-			void		*data;
-			uint32_t	len;
-	} user;
+	struct nftnl_attrbuf	*userdata;
 	struct {
 			uint32_t	flags;
 			uint32_t	proto;
@@ -50,6 +48,14 @@  struct nftnl_rule {
 	struct list_head expr_list;
 };
 
+static void nftnl_rule_parse_userdata(const struct nftnl_attrbuf *attrbuf,
+				      const struct nftnl_attr *tb[]);
+static size_t nftnl_rule_snprintf_data2str(char *buf, size_t size,
+					   const void *data, size_t datalen);
+static size_t nftnl_rule_snprintf_userdata(char *buf, size_t size,
+					   const struct nftnl_attr *tb[],
+					   enum nftnl_attr_data_type dtype);
+
 struct nftnl_rule *nftnl_rule_alloc(void)
 {
 	struct nftnl_rule *r;
@@ -75,6 +81,8 @@  void nftnl_rule_free(struct nftnl_rule *r)
 		xfree(r->table);
 	if (r->chain != NULL)
 		xfree(r->chain);
+	if (r->flags & (1 << NFTNL_RULE_USERDATA))
+		nftnl_attrbuf_free(r->userdata);
 
 	xfree(r);
 }
@@ -162,8 +170,12 @@  void nftnl_rule_set_data(struct nftnl_rule *r, uint16_t attr,
 		r->position = *((uint64_t *)data);
 		break;
 	case NFTNL_RULE_USERDATA:
-		r->user.data = (void *)data;
-		r->user.len = data_len;
+		(r->userdata = nftnl_attrbuf_alloc(data_len));
+		if (!r->userdata) {
+			perror("nftnl_rule_set_data - userdata");
+			exit(EXIT_FAILURE);
+		}
+		nftnl_attrbuf_copy_data(r->userdata, data, data_len);
 		break;
 	}
 	r->flags |= (1 << attr);
@@ -221,8 +233,8 @@  const void *nftnl_rule_get_data(const struct nftnl_rule *r, uint16_t attr,
 		*data_len = sizeof(uint64_t);
 		return &r->position;
 	case NFTNL_RULE_USERDATA:
-		*data_len = r->user.len;
-		return r->user.data;
+		*data_len = nftnl_attrbuf_get_len(r->userdata);
+		return (void *)nftnl_attrbuf_get_data(r->userdata);
 	}
 	return NULL;
 }
@@ -288,8 +300,9 @@  void nftnl_rule_nlmsg_build_payload(struct nlmsghdr *nlh, struct nftnl_rule *r)
 	if (r->flags & (1 << NFTNL_RULE_POSITION))
 		mnl_attr_put_u64(nlh, NFTA_RULE_POSITION, htobe64(r->position));
 	if (r->flags & (1 << NFTNL_RULE_USERDATA)) {
-		mnl_attr_put(nlh, NFTA_RULE_USERDATA, r->user.len,
-			     r->user.data);
+		mnl_attr_put(nlh, NFTA_RULE_USERDATA,
+			     nftnl_attrbuf_get_len(r->userdata),
+			     nftnl_attrbuf_get_data(r->userdata));
 	}
 
 	if (!list_empty(&r->expr_list)) {
@@ -447,19 +460,17 @@  int nftnl_rule_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_rule *r)
 		r->flags |= (1 << NFTNL_RULE_POSITION);
 	}
 	if (tb[NFTA_RULE_USERDATA]) {
+		uint16_t udata_size;
 		const void *udata =
 			mnl_attr_get_payload(tb[NFTA_RULE_USERDATA]);
 
-		if (r->user.data)
-			xfree(r->user.data);
-
-		r->user.len = mnl_attr_get_payload_len(tb[NFTA_RULE_USERDATA]);
+		udata_size = mnl_attr_get_payload_len(tb[NFTA_RULE_USERDATA]);
 
-		r->user.data = malloc(r->user.len);
-		if (r->user.data == NULL)
+		(r->userdata = nftnl_attrbuf_alloc(udata_size));
+		if (!r->userdata)
 			return -1;
+		nftnl_attrbuf_copy_data(r->userdata, udata, udata_size);
 
-		memcpy(r->user.data, udata, r->user.len);
 		r->flags |= (1 << NFTNL_RULE_USERDATA);
 	}
 
@@ -757,6 +768,29 @@  static int nftnl_rule_snprintf_json(char *buf, size_t size, struct nftnl_rule *r
 		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
 	}
 
+	if (r->flags & (1 << NFTNL_RULE_USERDATA)) {
+		const struct nftnl_attr *tb[NFTNL_ATTR_TYPE_MAX+1] = {NULL};
+
+		nftnl_rule_parse_userdata(r->userdata, tb);
+
+		ret = snprintf(buf+offset, len, "\"userdata\":[");
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+		if (tb[NFTNL_ATTR_TYPE_COMMENT]) {
+			ret = snprintf(buf+offset, len, "{\"comment\":\"");
+			SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+			ret = nftnl_rule_snprintf_userdata(buf+offset, size, tb,
+						NFTNL_ATTR_TYPE_COMMENT);
+			SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+			ret = snprintf(buf+offset, len, "\"}");
+			SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+		}
+
+		ret = snprintf(buf+offset, len, "],\n");
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	}
+
 	ret = snprintf(buf+offset, len, "\"expr\":[");
 	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
 
@@ -855,11 +889,76 @@  static int nftnl_rule_snprintf_xml(char *buf, size_t size, struct nftnl_rule *r,
 	return offset;
 }
 
+static int nftnl_rule_parse_userdata_cb(const struct nftnl_attr *attr,
+					void *data)
+{
+	const struct nftnl_attr **tb = data;
+	uint8_t type = nftnl_attr_get_type(attr);
+	uint8_t len = nftnl_attr_get_len(attr);
+	unsigned char *value = nftnl_attr_get_value(attr);
+
+	/* Validation */
+	switch (type) {
+	case NFTNL_ATTR_TYPE_COMMENT:
+		if (value[len-1] != '\0')
+			return NFTNL_CB_ERROR;
+	default:
+		return NFTNL_CB_ERROR;
+	};
+
+	tb[type] = attr;
+	return NFTNL_CB_OK;
+}
+
+static void nftnl_rule_parse_userdata(const struct nftnl_attrbuf *attrbuf,
+				      const struct nftnl_attr *tb[])
+{
+	if (nftnl_attr_parse(attrbuf, nftnl_rule_parse_userdata_cb, tb)
+		!= NFTNL_CB_OK
+	) {
+		fprintf(stderr, "Error parsing rule userdata\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+static size_t nftnl_rule_snprintf_data2str(char *buf, size_t size,
+					   const void *data, size_t datalen)
+{
+	int i;
+	size_t ret, len = size, offset = 0;
+	const char *c = data;
+
+	for (i = 0; i < datalen; i++) {
+		ret = snprintf(buf+offset, len, "%c",
+			       isprint(c[i]) ? c[i] : '?');
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+
+	return offset;
+}
+
+static size_t nftnl_rule_snprintf_userdata(char *buf, size_t size,
+					   const struct nftnl_attr *tb[],
+					   enum nftnl_attr_data_type dtype)
+{
+	size_t ret, offset = 0;
+
+	ret = nftnl_rule_snprintf_data2str(
+		buf,
+		size,
+		nftnl_attr_get_value(tb[dtype]),
+		nftnl_attr_get_len(tb[dtype])-1
+	);
+	SNPRINTF_BUFFER_SIZE(ret, size, size, offset);
+
+	return offset;
+}
+
 static int nftnl_rule_snprintf_default(char *buf, size_t size, struct nftnl_rule *r,
 				     uint32_t type, uint32_t flags)
 {
 	struct nftnl_expr *expr;
-	int ret, len = size, offset = 0, i;
+	int ret, len = size, offset = 0;
 
 	if (r->flags & (1 << NFTNL_RULE_FAMILY)) {
 		ret = snprintf(buf+offset, len, "%s ",
@@ -905,20 +1004,23 @@  static int nftnl_rule_snprintf_default(char *buf, size_t size, struct nftnl_rule
 		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
 	}
 
-	if (r->user.len) {
-		ret = snprintf(buf+offset, len, "  userdata = { ");
-		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
 
-		for (i = 0; i < r->user.len; i++) {
-			char *c = r->user.data;
+	if (r->flags & (1 << NFTNL_RULE_USERDATA)) {
+		const struct nftnl_attr *tb[NFTNL_ATTR_TYPE_MAX+1] = {NULL};
+
+		nftnl_rule_parse_userdata(r->userdata, tb);
 
-			ret = snprintf(buf+offset, len, "%c",
-				       isalnum(c[i]) ? c[i] : 0);
+		if (tb[NFTNL_ATTR_TYPE_COMMENT]) {
+			ret = snprintf(buf+offset, len, "  userdata = { ");
 			SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
-		}
 
-		ret = snprintf(buf+offset, len, " }\n");
-		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+			ret = nftnl_rule_snprintf_userdata(buf+offset, size, tb,
+						NFTNL_ATTR_TYPE_COMMENT);
+			SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+			ret = snprintf(buf+offset, len, " }\n");
+			SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+		}
 
 	}