1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter.git synced 2025-03-09 15:40:20 +00:00

Add nft fullcone support

This commit is contained in:
Ycarus (Yannick Chabanois) 2023-07-29 16:16:52 +02:00
parent d9b6c05d1e
commit 3feeae5315
6 changed files with 921 additions and 0 deletions

View file

@ -0,0 +1,76 @@
#
# Copyright (C) 2014 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=libnftnl
PKG_CPE_ID:=cpe:/a:netfilter:libnftnl
PKG_VERSION:=1.2.5
PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
PKG_SOURCE_URL:=https://netfilter.org/projects/$(PKG_NAME)/files
PKG_HASH:=966de0a8120c8a53db859889749368bfb2cba0c4f0b4c1a30d264eccc45f1226
PKG_MAINTAINER:=Steven Barth <steven@midlink.org>
PKG_LICENSE:=GPL-2.0-or-later
PKG_LICENSE_FILES:=COPYING
PKG_INSTALL:=1
PKG_BUILD_PARALLEL:=1
PKG_BUILD_FLAGS:=lto
PKG_FIXUP:=autoreconf
include $(INCLUDE_DIR)/package.mk
DISABLE_NLS:=
define Package/libnftnl
SECTION:=libs
CATEGORY:=Libraries
DEPENDS:=+libmnl
TITLE:=Low-level netlink library for the nf_tables subsystem
URL:=http://www.netfilter.org/projects/libnftnl
ABI_VERSION:=11
endef
define Package/libnftnl/description
libnftnl is a userspace library providing a low-level netlink
programming interface (API) to the in-kernel nf_tables subsystem.
endef
TARGET_CFLAGS += $(FPIC)
CONFIGURE_ARGS += \
--enable-static \
--enable-shared
define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/include/libnftnl
$(CP) \
$(PKG_INSTALL_DIR)/usr/include/libnftnl/*.h \
$(1)/usr/include/libnftnl/
$(INSTALL_DIR) $(1)/usr/lib
$(CP) \
$(PKG_INSTALL_DIR)/usr/lib/libnftnl.{so*,a,la} \
$(1)/usr/lib/
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
$(CP) \
$(PKG_INSTALL_DIR)/usr/lib/pkgconfig/libnftnl.pc \
$(1)/usr/lib/pkgconfig/
endef
define Package/libnftnl/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) \
$(PKG_INSTALL_DIR)/usr/lib/libnftnl.so.* \
$(1)/usr/lib/
endef
$(eval $(call BuildPackage,libnftnl))

View file

@ -0,0 +1,267 @@
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
diff --git a/include/libnftnl/expr.h b/include/libnftnl/expr.h
index 00c63ab..7dcf403 100644
--- a/include/libnftnl/expr.h
+++ b/include/libnftnl/expr.h
@@ -244,6 +244,12 @@ enum {
NFTNL_EXPR_MASQ_REG_PROTO_MAX,
};
+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,
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 0ae9120..8b8ae38 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -1433,6 +1433,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
*
diff --git a/src/Makefile.am b/src/Makefile.am
index c3b0ab9..2718218 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -54,6 +54,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 \
diff --git a/src/expr/fullcone.c b/src/expr/fullcone.c
new file mode 100644
index 0000000..aaedd83
--- /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),
+ .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,
+};
diff --git a/src/expr_ops.c b/src/expr_ops.c
index 7248e4f..9dee9f8 100644
--- a/src/expr_ops.c
+++ b/src/expr_ops.c
@@ -19,6 +19,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;
@@ -63,6 +64,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,
--
2.35.1

View file

@ -0,0 +1,22 @@
--- a/root/usr/share/firewall4/templates/ruleset.uc 2023-07-28 18:55:05.492297782 +0200
+++ b/root/usr/share/firewall4/templates/ruleset.uc 2023-07-28 18:58:52.300598623 +0200
@@ -218,9 +218,7 @@
{% for (let rule in fw4.rules(`input_${zone.name}`)): %}
{%+ include("rule.uc", { fw4, rule }) %}
{% endfor %}
-{% if (zone.dflags.dnat): %}
ct status dnat accept comment "!fw4: Accept port redirections"
-{% endif %}
{% fw4.includes('chain-append', `input_${zone.name}`) %}
jump {{ zone.input }}_from_{{ zone.name }}
}
@@ -239,9 +237,7 @@
{% for (let rule in fw4.rules(`forward_${zone.name}`)): %}
{%+ include("rule.uc", { fw4, rule }) %}
{% endfor %}
-{% if (zone.dflags.dnat): %}
ct status dnat accept comment "!fw4: Accept port forwards"
-{% endif %}
{% fw4.includes('chain-append', `forward_${zone.name}`) %}
jump {{ zone.forward }}_to_{{ zone.name }}
{% if (fw4.forward_policy() != "accept" && (zone.log & 1)): %}

View file

@ -0,0 +1,248 @@
From d4081c498ddca184578903fe5199d390bbc0707b Mon Sep 17 00:00:00 2001
From: Syrone Wong <wong.syrone@gmail.com>
Date: Sat, 9 Apr 2022 13:24:19 +0800
Subject: [PATCH] firewall4: add fullcone support
fullcone is drop-in replacement of masq for non-udp traffic
add runtime fullcone rule check, disable it globally if fullcone expr is
invalid
defaults.fullcone is the global switch, while zone.fullcone4 and
zone.fullcone6 are switches for IPv4 and IPv6 respectively, most
IPv6 traffic do NOT need this FullCone NAT functionality.
---
root/etc/config/firewall | 3 +
root/usr/share/firewall4/templates/ruleset.uc | 16 +++-
.../firewall4/templates/zone-fullcone.uc | 4 +
root/usr/share/ucode/fw4.uc | 76 ++++++++++++++++++-
4 files changed, 96 insertions(+), 3 deletions(-)
create mode 100644 root/usr/share/firewall4/templates/zone-fullcone.uc
diff --git a/root/etc/config/firewall b/root/etc/config/firewall
index b9a4647..7187723 100644
--- a/root/etc/config/firewall
+++ b/root/etc/config/firewall
@@ -5,6 +5,7 @@ config defaults
option forward REJECT
# Uncomment this line to disable ipv6 rules
# option disable_ipv6 1
+ option fullcone '1'
config zone
option name lan
@@ -20,6 +21,8 @@ config zone
option input REJECT
option output ACCEPT
option forward REJECT
+ option fullcone4 '1'
+ option fullcone6 '1'
option masq 1
option mtu_fix 1
diff --git a/root/usr/share/firewall4/templates/ruleset.uc b/root/usr/share/firewall4/templates/ruleset.uc
index eaa1f04..e29eae6 100644
--- a/root/usr/share/firewall4/templates/ruleset.uc
+++ b/root/usr/share/firewall4/templates/ruleset.uc
@@ -310,6 +310,12 @@ table inet fw4 {
{% for (let redirect in fw4.redirects(`dstnat_${zone.name}`)): %}
{%+ include("redirect.uc", { fw4, redirect }) %}
{% endfor %}
+{% if (zone.fullcone4): %}
+ {%+ include("zone-fullcone.uc", { fw4, zone, family: 4, direction: "dstnat" }) %}
+{% endif %}
+{% if (zone.fullcone6): %}
+ {%+ include("zone-fullcone.uc", { fw4, zone, family: 6, direction: "dstnat" }) %}
+{% endif %}
{% fw4.includes('chain-append', `dstnat_${zone.name}`) %}
}
@@ -320,20 +326,26 @@ table inet fw4 {
{% for (let redirect in fw4.redirects(`srcnat_${zone.name}`)): %}
{%+ include("redirect.uc", { fw4, redirect }) %}
{% endfor %}
-{% if (zone.masq): %}
+{% if (zone.masq && !zone.fullcone4): %}
{% for (let saddrs in zone.masq4_src_subnets): %}
{% for (let daddrs in zone.masq4_dest_subnets): %}
{%+ include("zone-masq.uc", { fw4, zone, family: 4, saddrs, daddrs }) %}
{% endfor %}
{% endfor %}
{% endif %}
-{% if (zone.masq6): %}
+{% if (zone.masq6 && !zone.fullcone6): %}
{% for (let saddrs in zone.masq6_src_subnets): %}
{% for (let daddrs in zone.masq6_dest_subnets): %}
{%+ include("zone-masq.uc", { fw4, zone, family: 6, saddrs, daddrs }) %}
{% endfor %}
{% endfor %}
{% endif %}
+{% if (zone.fullcone4): %}
+ {%+ include("zone-fullcone.uc", { fw4, zone, family: 4, direction: "srcnat" }) %}
+{% endif %}
+{% if (zone.fullcone6): %}
+ {%+ include("zone-fullcone.uc", { fw4, zone, family: 6, direction: "srcnat" }) %}
+{% endif %}
{% fw4.includes('chain-append', `srcnat_${zone.name}`) %}
}
diff --git a/root/usr/share/firewall4/templates/zone-fullcone.uc b/root/usr/share/firewall4/templates/zone-fullcone.uc
new file mode 100644
index 0000000..77d9806
--- /dev/null
+++ b/root/usr/share/firewall4/templates/zone-fullcone.uc
@@ -0,0 +1,4 @@
+{# /usr/share/firewall4/templates/zone-fullcone.uc #}
+ meta nfproto {{ fw4.nfproto(family) }} fullcone comment "!fw4: Handle {{
+ zone.name
+}} {{ fw4.nfproto(family, true) }} fullcone NAT {{ direction }} traffic"
diff --git a/root/usr/share/ucode/fw4.uc b/root/usr/share/ucode/fw4.uc
index 1b4764c..c5716da 100644
--- a/root/usr/share/ucode/fw4.uc
+++ b/root/usr/share/ucode/fw4.uc
@@ -1,3 +1,5 @@
+// /usr/share/ucode/fw4.uc
+
const fs = require("fs");
const uci = require("uci");
const ubus = require("ubus");
@@ -428,6 +430,25 @@ function nft_try_hw_offload(devices) {
return (rc == 0);
}
+function nft_try_fullcone() {
+ let nft_test =
+ 'add table inet fw4-fullcone-test; ' +
+ 'add chain inet fw4-fullcone-test dstnat { ' +
+ 'type nat hook prerouting priority -100; policy accept; ' +
+ 'fullcone; ' +
+ '}; ' +
+ 'add chain inet fw4-fullcone-test srcnat { ' +
+ 'type nat hook postrouting priority -100; policy accept; ' +
+ 'fullcone; ' +
+ '}; ';
+ let cmd = sprintf("/usr/sbin/nft -c '%s' 2>/dev/null", replace(nft_test, "'", "'\\''"));
+ let ok = system(cmd) == 0;
+ if (!ok) {
+ warn("nft_try_fullcone: cmd "+ cmd + "\n");
+ }
+ return ok;
+}
+
return {
read_kernel_version: function() {
@@ -765,6 +786,18 @@ return {
warn(`[!] ${msg}\n`);
},
+ myinfo: function(fmt, ...args) {
+ if (getenv("QUIET"))
+ return;
+
+ let msg = sprintf(fmt, ...args);
+
+ if (getenv("TTY"))
+ warn(`\033[32m${msg}\033[m\n`);
+ else
+ warn(`[I] ${msg}\n`);
+ },
+
get: function(sid, opt) {
return this.cursor.get("firewall", sid, opt);
},
@@ -946,6 +979,21 @@ return {
}
},
+ myinfo_section: function(s, msg) {
+ if (s[".name"]) {
+ if (s.name)
+ this.myinfo("Section %s (%s) %s", this.section_id(s[".name"]), s.name, msg);
+ else
+ this.myinfo("Section %s %s", this.section_id(s[".name"]), msg);
+ }
+ else {
+ if (s.name)
+ this.myinfo("ubus %s (%s) %s", s.type || "rule", s.name, msg);
+ else
+ this.myinfo("ubus %s %s", s.type || "rule", msg);
+ }
+ },
+
parse_policy: function(val) {
return this.parse_enum(val, [
"accept",
@@ -1385,6 +1433,7 @@ return {
"dnat",
"snat",
"masquerade",
+ "fullcone",
"accept",
"reject",
"drop"
@@ -1852,6 +1901,7 @@ return {
}
let defs = this.parse_options(data, {
+ fullcone: [ "bool", "0" ],
input: [ "policy", "drop" ],
output: [ "policy", "drop" ],
forward: [ "policy", "drop" ],
@@ -1884,6 +1934,11 @@ return {
delete defs.syn_flood;
+ if (!nft_try_fullcone()) {
+ delete defs.fullcone;
+ warn("nft_try_fullcone failed, disable fullcone globally\n");
+ }
+
this.state.defaults = defs;
},
@@ -1908,6 +1963,8 @@ return {
masq_dest: [ "network", null, PARSE_LIST ],
masq6: [ "bool" ],
+ fullcone4: [ "bool", "0" ],
+ fullcone6: [ "bool", "0" ],
extra: [ "string", null, UNSUPPORTED ],
extra_src: [ "string", null, UNSUPPORTED ],
@@ -1940,6 +1997,18 @@ return {
}
}
+ if (this.state.defaults && !this.state.defaults.fullcone) {
+ this.warn_section(data, "fullcone in defaults not enabled, ignore zone fullcone settings");
+ zone.fullcone4 = false;
+ zone.fullcone6 = false;
+ }
+ if (zone.fullcone4) {
+ this.myinfo_section(data, "IPv4 fullcone enabled for zone '" + zone.name + "'");
+ }
+ if (zone.fullcone6) {
+ this.myinfo_section(data, "IPv6 fullcone enabled for zone '" + zone.name + "'");
+ }
+
if (zone.mtu_fix && this.kernel < 0x040a0000) {
this.warn_section(data, "option 'mtu_fix' requires kernel 4.10 or later");
return;
@@ -2110,10 +2179,15 @@ return {
zone.related_subnets = related_subnets;
zone.related_physdevs = related_physdevs;
+ if (zone.fullcone4 || zone.fullcone6) {
+ zone.dflags.snat = true;
+ zone.dflags.dnat = true;
+ }
+
if (zone.masq || zone.masq6)
zone.dflags.snat = true;
- if ((zone.auto_helper && !(zone.masq || zone.masq6)) || length(zone.helper)) {
+ if ((zone.auto_helper && !(zone.masq || zone.masq6 || zone.fullcone4 || zone.fullcone6)) || length(zone.helper)) {
zone.dflags.helper = true;
for (let helper in (length(zone.helper) ? zone.helper : this.state.helpers)) {

View file

@ -0,0 +1,85 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright (C) 2015 OpenWrt.org
#
include $(TOPDIR)/rules.mk
PKG_NAME:=nftables
PKG_VERSION:=1.0.7
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
PKG_SOURCE_URL:=https://netfilter.org/projects/$(PKG_NAME)/files
PKG_HASH:=c12ac941fff9adaedf17367d5ce213789b98a0d314277bc22b3d71e10891f412
PKG_MAINTAINER:=
PKG_LICENSE:=GPL-2.0
PKG_LICENSE_FILES:=COPYING
PKG_FIXUP:=autoreconf
PKG_INSTALL:=1
PKG_BUILD_FLAGS:=lto
include $(INCLUDE_DIR)/package.mk
DISABLE_NLS:=
CONFIGURE_ARGS += \
--disable-debug \
--disable-man-doc \
--with-mini-gmp \
--without-cli \
--disable-python
define Package/nftables/Default
SECTION:=net
CATEGORY:=Network
SUBMENU:=Firewall
TITLE:=nftables userspace utility
DEPENDS:=+kmod-nft-core +libnftnl
URL:=http://netfilter.org/projects/nftables/
PROVIDES:=nftables
endef
define Package/nftables-nojson
$(Package/nftables/Default)
TITLE+= no JSON support
VARIANT:=nojson
DEFAULT_VARIANT:=1
CONFLICTS:=nftables-json
endef
define Package/nftables-json
$(Package/nftables/Default)
TITLE+= with JSON support
VARIANT:=json
DEPENDS+=+jansson
endef
ifeq ($(BUILD_VARIANT),json)
CONFIGURE_ARGS += --with-json
endif
define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/lib $(1)/usr/include
$(CP) $(PKG_INSTALL_DIR)/usr/lib/*.so* $(1)/usr/lib/
$(CP) $(PKG_INSTALL_DIR)/usr/include/nftables $(1)/usr/include/
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/libnftables.pc \
$(1)/usr/lib/pkgconfig/
endef
define Package/nftables/install/Default
$(INSTALL_DIR) $(1)/usr/sbin
$(CP) $(PKG_INSTALL_DIR)/usr/sbin/nft $(1)/usr/sbin/
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/*.so* $(1)/usr/lib/
endef
Package/nftables-nojson/install = $(Package/nftables/install/Default)
Package/nftables-json/install = $(Package/nftables/install/Default)
$(eval $(call BuildPackage,nftables-nojson))
$(eval $(call BuildPackage,nftables-json))

View file

@ -0,0 +1,223 @@
From 58c89e8768711a959fdc6e953df3ea2254ff93c1 Mon Sep 17 00:00:00 2001
From: Syrone Wong <wong.syrone@gmail.com>
Date: Sat, 9 Apr 2022 00:38:51 +0800
Subject: [PATCH] nftables: add fullcone expression support
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
---
include/linux/netfilter/nf_tables.h | 16 ++++++++++
include/statement.h | 1 +
src/netlink_delinearize.c | 48 +++++++++++++++++++++++++++++
src/netlink_linearize.c | 7 +++++
src/parser_bison.y | 28 +++++++++++++++--
src/scanner.l | 1 +
src/statement.c | 1 +
7 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 75df968..beab9d8 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -1409,6 +1409,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
*
diff --git a/include/statement.h b/include/statement.h
index 2a2d300..cbd48dd 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -122,6 +122,7 @@ enum nft_nat_etypes {
__NFT_NAT_SNAT = NFT_NAT_SNAT,
__NFT_NAT_DNAT = NFT_NAT_DNAT,
NFT_NAT_MASQ,
+ NFT_NAT_FULLCONE,
NFT_NAT_REDIR,
};
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 068c3bb..8513113 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1369,6 +1369,53 @@ static void netlink_parse_masq(struct netlink_parse_ctx *ctx,
stmt_free(stmt);
}
+static void netlink_parse_fullcone(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ const struct nftnl_expr *nle)
+{
+ enum nft_registers reg1, reg2;
+ struct expr *proto;
+ struct stmt *stmt;
+ uint32_t flags = 0;
+
+ if (nftnl_expr_is_set(nle, NFTNL_EXPR_FULLCONE_FLAGS))
+ flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_FULLCONE_FLAGS);
+
+ stmt = nat_stmt_alloc(loc, NFT_NAT_FULLCONE);
+ stmt->nat.flags = flags;
+
+ reg1 = netlink_parse_register(nle, NFTNL_EXPR_FULLCONE_REG_PROTO_MIN);
+ if (reg1) {
+ proto = netlink_get_register(ctx, loc, reg1);
+ if (proto == NULL) {
+ netlink_error(ctx, loc,
+ "fullcone statement has no proto expression");
+ goto out_err;
+ }
+ expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN);
+ stmt->nat.proto = proto;
+ }
+
+ reg2 = netlink_parse_register(nle, NFTNL_EXPR_FULLCONE_REG_PROTO_MAX);
+ if (reg2 && reg2 != reg1) {
+ proto = netlink_get_register(ctx, loc, reg2);
+ if (proto == NULL) {
+ netlink_error(ctx, loc,
+ "fullcone statement has no proto expression");
+ goto out_err;
+ }
+ expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN);
+ if (stmt->nat.proto != NULL)
+ proto = range_expr_alloc(loc, stmt->nat.proto, proto);
+ stmt->nat.proto = proto;
+ }
+
+ ctx->stmt = stmt;
+ return;
+out_err:
+ stmt_free(stmt);
+}
+
static void netlink_parse_redir(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
@@ -1787,6 +1834,7 @@ static const struct expr_handler netlink_parsers[] = {
{ .name = "tproxy", .parse = netlink_parse_tproxy },
{ .name = "notrack", .parse = netlink_parse_notrack },
{ .name = "masq", .parse = netlink_parse_masq },
+ { .name = "fullcone", .parse = netlink_parse_fullcone },
{ .name = "redir", .parse = netlink_parse_redir },
{ .name = "dup", .parse = netlink_parse_dup },
{ .name = "queue", .parse = netlink_parse_queue },
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index c8bbcb7..505eafa 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1140,6 +1140,13 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
nftnl_reg_pmin = NFTNL_EXPR_MASQ_REG_PROTO_MIN;
nftnl_reg_pmax = NFTNL_EXPR_MASQ_REG_PROTO_MAX;
break;
+ case NFT_NAT_FULLCONE:
+ nle = alloc_nft_expr("fullcone");
+
+ nftnl_flag_attr = NFTNL_EXPR_FULLCONE_FLAGS;
+ nftnl_reg_pmin = NFTNL_EXPR_FULLCONE_REG_PROTO_MIN;
+ nftnl_reg_pmax = NFTNL_EXPR_FULLCONE_REG_PROTO_MAX;
+ break;
case NFT_NAT_REDIR:
nle = alloc_nft_expr("redir");
diff --git a/src/parser_bison.y b/src/parser_bison.y
index ca5c488..ec9fc9b 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -571,6 +571,7 @@ int nft_lex(void *, void *, void *);
%token SNAT "snat"
%token DNAT "dnat"
%token MASQUERADE "masquerade"
+%token FULLCONE "fullcone"
%token REDIRECT "redirect"
%token RANDOM "random"
%token FULLY_RANDOM "fully-random"
@@ -703,8 +704,8 @@ int nft_lex(void *, void *, void *);
%type <val> limit_burst_pkts limit_burst_bytes limit_mode limit_bytes time_unit quota_mode
%type <stmt> reject_stmt reject_stmt_alloc
%destructor { stmt_free($$); } reject_stmt reject_stmt_alloc
-%type <stmt> nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
-%destructor { stmt_free($$); } nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
+%type <stmt> nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc fullcone_stmt fullcone_stmt_alloc redir_stmt redir_stmt_alloc
+%destructor { stmt_free($$); } nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc fullcone_stmt fullcone_stmt_alloc redir_stmt redir_stmt_alloc
%type <val> nf_nat_flags nf_nat_flag offset_opt
%type <stmt> tproxy_stmt
%destructor { stmt_free($$); } tproxy_stmt
@@ -2853,6 +2854,7 @@ stmt : verdict_stmt
| queue_stmt
| ct_stmt
| masq_stmt close_scope_nat
+ | fullcone_stmt close_scope_nat
| redir_stmt close_scope_nat
| dup_stmt close_scope_dup
| fwd_stmt close_scope_fwd
@@ -3753,6 +3755,28 @@ masq_stmt_args : TO COLON stmt_expr
}
;
+fullcone_stmt : fullcone_stmt_alloc fullcone_stmt_args
+ | fullcone_stmt_alloc
+ ;
+
+fullcone_stmt_alloc : FULLCONE { $$ = nat_stmt_alloc(&@$, NFT_NAT_FULLCONE); }
+ ;
+
+fullcone_stmt_args : TO COLON stmt_expr
+ {
+ $<stmt>0->nat.proto = $3;
+ }
+ | TO COLON stmt_expr nf_nat_flags
+ {
+ $<stmt>0->nat.proto = $3;
+ $<stmt>0->nat.flags = $4;
+ }
+ | nf_nat_flags
+ {
+ $<stmt>0->nat.flags = $1;
+ }
+ ;
+
redir_stmt : redir_stmt_alloc redir_stmt_arg
| redir_stmt_alloc
;
diff --git a/src/scanner.l b/src/scanner.l
index 2154281..c389860 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -453,6 +453,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"snat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; }
"dnat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; }
"masquerade" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; }
+"fullcone" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return FULLCONE; }
"redirect" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; }
"random" { return RANDOM; }
<SCANSTATE_STMT_NAT>{
diff --git a/src/statement.c b/src/statement.c
index 30caf9c..f4866c2 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -650,6 +650,7 @@ const char *nat_etype2str(enum nft_nat_etypes type)
[NFT_NAT_SNAT] = "snat",
[NFT_NAT_DNAT] = "dnat",
[NFT_NAT_MASQ] = "masquerade",
+ [NFT_NAT_FULLCONE] = "fullcone",
[NFT_NAT_REDIR] = "redirect",
};