From 6c39f04febd7cfdbd474233379416babcd0fc341 Mon Sep 17 00:00:00 2001
From: Syrone Wong <wong.syrone@gmail.com>
Date: Fri, 8 Apr 2022 23:52:11 +0800
Subject: [PATCH] libnftnl: add fullcone expression support

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
---
 include/libnftnl/expr.h             |   6 +
 include/linux/netfilter/nf_tables.h |  16 +++
 src/Makefile.am                     |   1 +
 src/expr/fullcone.c                 | 167 ++++++++++++++++++++++++++++
 src/expr_ops.c                      |   2 +
 5 files changed, 192 insertions(+)
 create mode 100644 src/expr/fullcone.c

--- a/include/libnftnl/expr.h
+++ b/include/libnftnl/expr.h
@@ -272,6 +272,12 @@ enum {
 };
 
 enum {
+	NFTNL_EXPR_FULLCONE_FLAGS		= NFTNL_EXPR_BASE,
+	NFTNL_EXPR_FULLCONE_REG_PROTO_MIN,
+	NFTNL_EXPR_FULLCONE_REG_PROTO_MAX,
+};
+
+enum {
 	NFTNL_EXPR_REDIR_REG_PROTO_MIN	= NFTNL_EXPR_BASE,
 	NFTNL_EXPR_REDIR_REG_PROTO_MAX,
 	NFTNL_EXPR_REDIR_FLAGS,
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -1464,6 +1464,22 @@ enum nft_masq_attributes {
 #define NFTA_MASQ_MAX		(__NFTA_MASQ_MAX - 1)
 
 /**
+ * enum nft_fullcone_attributes - nf_tables fullcone expression attributes
+ *
+ * @NFTA_FULLCONE_FLAGS: NAT flags (see NF_NAT_RANGE_* in linux/netfilter/nf_nat.h) (NLA_U32)
+ * @NFTA_FULLCONE_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers)
+ * @NFTA_FULLCONE_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers)
+ */
+enum nft_fullcone_attributes {
+	NFTA_FULLCONE_UNSPEC,
+	NFTA_FULLCONE_FLAGS,
+	NFTA_FULLCONE_REG_PROTO_MIN,
+	NFTA_FULLCONE_REG_PROTO_MAX,
+	__NFTA_FULLCONE_MAX
+};
+#define NFTA_FULLCONE_MAX		(__NFTA_FULLCONE_MAX - 1)
+
+/**
  * enum nft_redir_attributes - nf_tables redirect expression netlink attributes
  *
  * @NFTA_REDIR_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers)
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -55,6 +55,7 @@ libnftnl_la_SOURCES = utils.c		\
 		      expr/target.c	\
 		      expr/tunnel.c	\
 		      expr/masq.c	\
+		      expr/fullcone.c	\
 		      expr/redir.c	\
 		      expr/hash.c	\
 		      expr/socket.c	\
--- /dev/null
+++ b/src/expr/fullcone.c
@@ -0,0 +1,167 @@
+/*
+ * (C) 2022 wongsyrone
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_fullcone {
+	uint32_t		flags;
+	enum nft_registers	sreg_proto_min;
+	enum nft_registers	sreg_proto_max;
+};
+
+static int
+nftnl_expr_fullcone_set(struct nftnl_expr *e, uint16_t type,
+		       const void *data, uint32_t data_len)
+{
+	struct nftnl_expr_fullcone *fullcone = nftnl_expr_data(e);
+
+	switch (type) {
+	case NFTNL_EXPR_FULLCONE_FLAGS:
+		memcpy(&fullcone->flags, data, sizeof(fullcone->flags));
+		break;
+	case NFTNL_EXPR_FULLCONE_REG_PROTO_MIN:
+		memcpy(&fullcone->sreg_proto_min, data, sizeof(fullcone->sreg_proto_min));
+		break;
+	case NFTNL_EXPR_FULLCONE_REG_PROTO_MAX:
+		memcpy(&fullcone->sreg_proto_max, data, sizeof(fullcone->sreg_proto_max));
+		break;
+	default:
+		return -1;
+	}
+	return 0;
+}
+
+static const void *
+nftnl_expr_fullcone_get(const struct nftnl_expr *e, uint16_t type,
+		       uint32_t *data_len)
+{
+	struct nftnl_expr_fullcone *fullcone = nftnl_expr_data(e);
+
+	switch (type) {
+	case NFTNL_EXPR_FULLCONE_FLAGS:
+		*data_len = sizeof(fullcone->flags);
+		return &fullcone->flags;
+	case NFTNL_EXPR_FULLCONE_REG_PROTO_MIN:
+		*data_len = sizeof(fullcone->sreg_proto_min);
+		return &fullcone->sreg_proto_min;
+	case NFTNL_EXPR_FULLCONE_REG_PROTO_MAX:
+		*data_len = sizeof(fullcone->sreg_proto_max);
+		return &fullcone->sreg_proto_max;
+	}
+	return NULL;
+}
+
+static int nftnl_expr_fullcone_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_FULLCONE_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch (type) {
+	case NFTA_FULLCONE_REG_PROTO_MIN:
+	case NFTA_FULLCONE_REG_PROTO_MAX:
+	case NFTA_FULLCONE_FLAGS:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			abi_breakage();
+		break;
+	}
+
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_fullcone_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+	struct nftnl_expr_fullcone *fullcone = nftnl_expr_data(e);
+
+	if (e->flags & (1 << NFTNL_EXPR_FULLCONE_FLAGS))
+		mnl_attr_put_u32(nlh, NFTA_FULLCONE_FLAGS, htobe32(fullcone->flags));
+	if (e->flags & (1 << NFTNL_EXPR_FULLCONE_REG_PROTO_MIN))
+		mnl_attr_put_u32(nlh, NFTA_FULLCONE_REG_PROTO_MIN,
+				 htobe32(fullcone->sreg_proto_min));
+	if (e->flags & (1 << NFTNL_EXPR_FULLCONE_REG_PROTO_MAX))
+		mnl_attr_put_u32(nlh, NFTA_FULLCONE_REG_PROTO_MAX,
+				 htobe32(fullcone->sreg_proto_max));
+}
+
+static int
+nftnl_expr_fullcone_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+	struct nftnl_expr_fullcone *fullcone = nftnl_expr_data(e);
+	struct nlattr *tb[NFTA_FULLCONE_MAX+1] = {};
+
+	if (mnl_attr_parse_nested(attr, nftnl_expr_fullcone_cb, tb) < 0)
+		return -1;
+
+	if (tb[NFTA_FULLCONE_FLAGS]) {
+		fullcone->flags = be32toh(mnl_attr_get_u32(tb[NFTA_FULLCONE_FLAGS]));
+		e->flags |= (1 << NFTNL_EXPR_FULLCONE_FLAGS);
+        }
+	if (tb[NFTA_FULLCONE_REG_PROTO_MIN]) {
+		fullcone->sreg_proto_min =
+			be32toh(mnl_attr_get_u32(tb[NFTA_FULLCONE_REG_PROTO_MIN]));
+		e->flags |= (1 << NFTNL_EXPR_FULLCONE_REG_PROTO_MIN);
+	}
+	if (tb[NFTA_FULLCONE_REG_PROTO_MAX]) {
+		fullcone->sreg_proto_max =
+			be32toh(mnl_attr_get_u32(tb[NFTA_FULLCONE_REG_PROTO_MAX]));
+		e->flags |= (1 << NFTNL_EXPR_FULLCONE_REG_PROTO_MAX);
+	}
+
+	return 0;
+}
+
+static int nftnl_expr_fullcone_snprintf(char *buf, size_t remain,
+				    uint32_t flags, const struct nftnl_expr *e)
+{
+	struct nftnl_expr_fullcone *fullcone = nftnl_expr_data(e);
+	int offset = 0, ret = 0;
+
+	if (e->flags & (1 << NFTNL_EXPR_FULLCONE_REG_PROTO_MIN)) {
+		ret = snprintf(buf + offset, remain, "proto_min reg %u ",
+			       fullcone->sreg_proto_min);
+		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+	}
+	if (e->flags & (1 << NFTNL_EXPR_FULLCONE_REG_PROTO_MAX)) {
+		ret = snprintf(buf + offset, remain, "proto_max reg %u ",
+			       fullcone->sreg_proto_max);
+		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+	}
+	if (e->flags & (1 << NFTNL_EXPR_FULLCONE_FLAGS)) {
+		ret = snprintf(buf + offset, remain, "flags 0x%x ", fullcone->flags);
+		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+	}
+
+	return offset;
+}
+
+struct expr_ops expr_ops_fullcone = {
+	.name		= "fullcone",
+	.alloc_len	= sizeof(struct nftnl_expr_fullcone),
+	.nftnl_max_attr	= NFTA_FULLCONE_MAX,
+	.set		= nftnl_expr_fullcone_set,
+	.get		= nftnl_expr_fullcone_get,
+	.parse		= nftnl_expr_fullcone_parse,
+	.build		= nftnl_expr_fullcone_build,
+	.output		= nftnl_expr_fullcone_snprintf,
+};
--- a/src/expr_ops.c
+++ b/src/expr_ops.c
@@ -20,6 +20,7 @@ extern struct expr_ops expr_ops_limit;
 extern struct expr_ops expr_ops_log;
 extern struct expr_ops expr_ops_lookup;
 extern struct expr_ops expr_ops_masq;
+extern struct expr_ops expr_ops_fullcone;
 extern struct expr_ops expr_ops_match;
 extern struct expr_ops expr_ops_meta;
 extern struct expr_ops expr_ops_ng;
@@ -65,6 +66,7 @@ static struct expr_ops *expr_ops[] = {
 	&expr_ops_log,
 	&expr_ops_lookup,
 	&expr_ops_masq,
+	&expr_ops_fullcone,
 	&expr_ops_match,
 	&expr_ops_meta,
 	&expr_ops_ng,
