Created
April 5, 2024 11:58
-
-
Save love4taylor/a56985ecde4f16bab2cf54ee1c6a0c32 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From aa6ba6895b2af23c6b1aca0944d7785af015113d Mon Sep 17 00:00:00 2001 | |
From: love4taylor <i@love4taylor.com> | |
Date: Mon, 11 Mar 2024 22:42:18 +0900 | |
Subject: [PATCH 1/2] netfilter: nat: add brcm fullcone support | |
--- | |
net/netfilter/nf_nat_masquerade.c | 193 ++++++++++++++++++++++++++++++ | |
net/netfilter/xt_MASQUERADE.c | 7 ++ | |
2 files changed, 200 insertions(+) | |
diff --git a/net/netfilter/nf_nat_masquerade.c b/net/netfilter/nf_nat_masquerade.c | |
index 1a506b0c6511..fbde277b0e08 100644 | |
--- a/net/netfilter/nf_nat_masquerade.c | |
+++ b/net/netfilter/nf_nat_masquerade.c | |
@@ -8,6 +8,9 @@ | |
#include <linux/netfilter_ipv6.h> | |
#include <net/netfilter/nf_nat_masquerade.h> | |
+#include <net/netfilter/nf_conntrack_zones.h> | |
+#include <net/netfilter/nf_conntrack_helper.h> | |
+#include <net/netfilter/nf_conntrack_core.h> | |
struct masq_dev_work { | |
struct work_struct work; | |
@@ -24,6 +27,129 @@ static DEFINE_MUTEX(masq_mutex); | |
static unsigned int masq_refcnt __read_mostly; | |
static atomic_t masq_worker_count __read_mostly; | |
+static void bcm_nat_expect(struct nf_conn *ct, | |
+ struct nf_conntrack_expect *exp) | |
+{ | |
+ struct nf_nat_range2 range; | |
+ | |
+ /* This must be a fresh one. */ | |
+ BUG_ON(ct->status & IPS_NAT_DONE_MASK); | |
+ | |
+ /* Change src to where new ct comes from */ | |
+ range.flags = NF_NAT_RANGE_MAP_IPS; | |
+ range.min_addr = range.max_addr = | |
+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3; | |
+ nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); | |
+ | |
+ /* For DST manip, map port here to where it's expected. */ | |
+ range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); | |
+ range.min_proto = range.max_proto = exp->saved_proto; | |
+ range.min_addr = range.max_addr = exp->saved_addr; | |
+ nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); | |
+} | |
+ | |
+/****************************************************************************/ | |
+static int bcm_nat_help(struct sk_buff *skb, unsigned int protoff, | |
+ struct nf_conn *ct, enum ip_conntrack_info ctinfo) | |
+{ | |
+ int dir = CTINFO2DIR(ctinfo); | |
+ struct nf_conn_help *help = nfct_help(ct); | |
+ struct nf_conntrack_expect *exp; | |
+ | |
+ if (dir != IP_CT_DIR_ORIGINAL || | |
+ help->expecting[NF_CT_EXPECT_CLASS_DEFAULT]) | |
+ return NF_ACCEPT; | |
+ | |
+ pr_debug("bcm_nat: packet[%d bytes] ", skb->len); | |
+ nf_ct_dump_tuple(&ct->tuplehash[dir].tuple); | |
+ pr_debug("reply: "); | |
+ nf_ct_dump_tuple(&ct->tuplehash[!dir].tuple); | |
+ | |
+ /* Create expect */ | |
+ if ((exp = nf_ct_expect_alloc(ct)) == NULL) | |
+ return NF_ACCEPT; | |
+ | |
+ nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, AF_INET, NULL, | |
+ &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_UDP, | |
+ NULL, &ct->tuplehash[!dir].tuple.dst.u.udp.port); | |
+ exp->flags = NF_CT_EXPECT_PERMANENT; | |
+ exp->saved_addr = ct->tuplehash[dir].tuple.src.u3; | |
+ exp->saved_proto.udp.port = ct->tuplehash[dir].tuple.src.u.udp.port; | |
+ exp->dir = !dir; | |
+ exp->expectfn = bcm_nat_expect; | |
+ | |
+ /* Setup expect */ | |
+ nf_ct_expect_related(exp, 0); | |
+ nf_ct_expect_put(exp); | |
+ pr_debug("bcm_nat: expect setup\n"); | |
+ | |
+ return NF_ACCEPT; | |
+} | |
+ | |
+/****************************************************************************/ | |
+static struct nf_conntrack_expect_policy bcm_nat_exp_policy __read_mostly = { | |
+ .max_expected = 1000, | |
+ .timeout = 240, | |
+}; | |
+ | |
+/****************************************************************************/ | |
+static struct nf_conntrack_helper nf_conntrack_helper_bcm_nat __read_mostly = { | |
+ .name = "BCM-NAT", | |
+ .me = THIS_MODULE, | |
+ .tuple.src.l3num = AF_INET, | |
+ .tuple.dst.protonum = IPPROTO_UDP, | |
+ .expect_policy = &bcm_nat_exp_policy, | |
+ .expect_class_max = 1, | |
+ .help = bcm_nat_help, | |
+}; | |
+ | |
+/****************************************************************************/ | |
+static inline int find_exp(__be32 ip, __be16 port, struct nf_conn *ct) | |
+{ | |
+ struct nf_conntrack_tuple tuple; | |
+ struct nf_conntrack_expect *i = NULL; | |
+ | |
+ | |
+ memset(&tuple, 0, sizeof(tuple)); | |
+ tuple.src.l3num = AF_INET; | |
+ tuple.dst.protonum = IPPROTO_UDP; | |
+ tuple.dst.u3.ip = ip; | |
+ tuple.dst.u.udp.port = port; | |
+ | |
+ rcu_read_lock(); | |
+ i = __nf_ct_expect_find(nf_ct_net(ct), nf_ct_zone(ct), &tuple); | |
+ rcu_read_unlock(); | |
+ | |
+ return i != NULL; | |
+} | |
+ | |
+/****************************************************************************/ | |
+static inline struct nf_conntrack_expect *find_fullcone_exp(struct nf_conn *ct) | |
+{ | |
+ struct nf_conntrack_tuple * tp = | |
+ &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; | |
+ struct nf_conntrack_expect * exp = NULL; | |
+ struct nf_conntrack_expect * i; | |
+ unsigned int h; | |
+ | |
+ rcu_read_lock(); | |
+ for (h = 0; h < nf_ct_expect_hsize; h++) { | |
+ hlist_for_each_entry_rcu(i, &nf_ct_expect_hash[h], hnode) { | |
+ if (nf_inet_addr_cmp(&i->saved_addr, &tp->src.u3) && | |
+ i->saved_proto.all == tp->src.u.all && | |
+ i->tuple.dst.protonum == tp->dst.protonum && | |
+ i->tuple.src.u3.ip == 0 && | |
+ i->tuple.src.u.udp.port == 0) { | |
+ exp = i; | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ rcu_read_unlock(); | |
+ | |
+ return exp; | |
+} | |
+ | |
unsigned int | |
nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum, | |
const struct nf_nat_range2 *range, | |
@@ -61,6 +187,72 @@ nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum, | |
if (nat) | |
nat->masq_index = out->ifindex; | |
+/* RFC 4787 - 4.2.2. Port Parity | |
+ i.e., an even port will be mapped to an even port, and an odd port will be mapped to an odd port. | |
+*/ | |
+#define CHECK_PORT_PARITY(a, b) ((a%2)==(b%2)) | |
+ if (range->min_addr.ip != 0 /* nat_mode == full cone */ | |
+ && (nfct_help(ct) == NULL || nfct_help(ct)->helper == NULL) | |
+ && nf_ct_protonum(ct) == IPPROTO_UDP) { | |
+ unsigned int ret; | |
+ u_int16_t minport; | |
+ u_int16_t maxport; | |
+ struct nf_conntrack_expect *exp; | |
+ | |
+ pr_debug("bcm_nat: need full cone NAT\n"); | |
+ | |
+ /* Choose port */ | |
+ spin_lock_bh(&nf_conntrack_expect_lock); | |
+ /* Look for existing expectation */ | |
+ exp = find_fullcone_exp(ct); | |
+ if (exp) { | |
+ minport = maxport = exp->tuple.dst.u.udp.port; | |
+ pr_debug("bcm_nat: existing mapped port = %hu\n", | |
+ ntohs(minport)); | |
+ } else { /* no previous expect */ | |
+ u_int16_t newport, tmpport, orgport; | |
+ | |
+ minport = range->min_proto.all == 0? | |
+ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src. | |
+ u.udp.port : range->min_proto.all; | |
+ maxport = range->max_proto.all == 0? | |
+ htons(65535) : range->max_proto.all; | |
+ orgport = ntohs(minport); | |
+ for (newport = ntohs(minport),tmpport = ntohs(maxport); | |
+ newport <= tmpport; newport++) { | |
+ if (CHECK_PORT_PARITY(orgport, newport) && !find_exp(newsrc, htons(newport), ct)) { | |
+ pr_debug("bcm_nat: new mapped port = " | |
+ "%hu\n", newport); | |
+ minport = maxport = htons(newport); | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ spin_unlock_bh(&nf_conntrack_expect_lock); | |
+ | |
+ | |
+ memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); | |
+ memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); | |
+ | |
+ newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS | | |
+ NF_NAT_RANGE_PROTO_SPECIFIED; | |
+ newrange.max_addr.ip = newrange.min_addr.ip = newsrc; | |
+ newrange.min_proto.udp.port = newrange.max_proto.udp.port = minport; | |
+ | |
+ /* Set ct helper */ | |
+ ret = nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC); | |
+ if (ret == NF_ACCEPT) { | |
+ struct nf_conn_help *help = nfct_help(ct); | |
+ if (help == NULL) | |
+ help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); | |
+ if (help != NULL) { | |
+ help->helper = &nf_conntrack_helper_bcm_nat; | |
+ pr_debug("bcm_nat: helper set\n"); | |
+ } | |
+ } | |
+ return ret; | |
+ } | |
+ | |
/* Transfer from original range. */ | |
memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); | |
memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); | |
@@ -352,6 +544,7 @@ EXPORT_SYMBOL_GPL(nf_nat_masquerade_inet_register_notifiers); | |
void nf_nat_masquerade_inet_unregister_notifiers(void) | |
{ | |
+ nf_conntrack_helper_unregister(&nf_conntrack_helper_bcm_nat); | |
mutex_lock(&masq_mutex); | |
/* check if the notifiers still have clients */ | |
if (--masq_refcnt > 0) | |
diff --git a/net/netfilter/xt_MASQUERADE.c b/net/netfilter/xt_MASQUERADE.c | |
index eae05c178336..e8072f0c5a0c 100644 | |
--- a/net/netfilter/xt_MASQUERADE.c | |
+++ b/net/netfilter/xt_MASQUERADE.c | |
@@ -42,6 +42,13 @@ masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par) | |
range.min_proto = mr->range[0].min; | |
range.max_proto = mr->range[0].max; | |
+ // range.min_addr.ip = mr->range[0].min_ip; | |
+ /* Hardcoded fullcone | |
+ 1 == fullcone | |
+ 0 == symmetric */ | |
+ range.min_addr.ip = 1; | |
+ range.max_addr.ip = mr->range[0].max_ip; | |
+ | |
return nf_nat_masquerade_ipv4(skb, xt_hooknum(par), &range, | |
xt_out(par)); | |
} | |
-- | |
2.39.2 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From b18b4a58db0a8b29f56860820a62003c6b3319d1 Mon Sep 17 00:00:00 2001 | |
From: love4taylor <i@love4taylor.com> | |
Date: Fri, 5 Apr 2024 20:57:05 +0900 | |
Subject: [PATCH 2/2] netfilter: nat: add brcm fullcone nft support | |
--- | |
include/uapi/linux/netfilter/nf_tables.h | 8 +++++ | |
net/netfilter/nft_masq.c | 44 ++++++++++++++++++++++++ | |
2 files changed, 52 insertions(+) | |
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h | |
index 117c6a9b845b..885d5e104895 100644 | |
--- a/include/uapi/linux/netfilter/nf_tables.h | |
+++ b/include/uapi/linux/netfilter/nf_tables.h | |
@@ -1477,15 +1477,23 @@ enum nft_tproxy_attributes { | |
* @NFTA_MASQ_FLAGS: NAT flags (see NF_NAT_RANGE_* in linux/netfilter/nf_nat.h) (NLA_U32) | |
* @NFTA_MASQ_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers) | |
* @NFTA_MASQ_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers) | |
+ * @NFTA_MASQ_REG_ADDR_MIN: source register of address range start (NLA_U32: nft_registers) non zero to enable bcm fullcone | |
+ * @NFTA_MASQ_REG_ADDR_MAX: source register of address range end (NLA_U32: nft_registers) | |
*/ | |
enum nft_masq_attributes { | |
NFTA_MASQ_UNSPEC, | |
NFTA_MASQ_FLAGS, | |
NFTA_MASQ_REG_PROTO_MIN, | |
NFTA_MASQ_REG_PROTO_MAX, | |
+ NFTA_MASQ_REG_ADDR_MIN, | |
+ NFTA_MASQ_REG_ADDR_MAX, | |
__NFTA_MASQ_MAX | |
}; | |
#define NFTA_MASQ_MAX (__NFTA_MASQ_MAX - 1) | |
+/** | |
+ * Hardcoded fullcone | |
+ */ | |
+#define NFTA_MASQ_REG_ADDR_MIN 1 | |
/** | |
* enum nft_redir_attributes - nf_tables redirect expression netlink attributes | |
diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c | |
index 8a14aaca93bb..f2c390059381 100644 | |
--- a/net/netfilter/nft_masq.c | |
+++ b/net/netfilter/nft_masq.c | |
@@ -17,6 +17,8 @@ struct nft_masq { | |
u32 flags; | |
u8 sreg_proto_min; | |
u8 sreg_proto_max; | |
+ u8 sreg_addr_min; // non zero to enable brcm fullconenat | |
+ u8 sreg_addr_max; | |
}; | |
static const struct nla_policy nft_masq_policy[NFTA_MASQ_MAX + 1] = { | |
@@ -24,6 +26,8 @@ static const struct nla_policy nft_masq_policy[NFTA_MASQ_MAX + 1] = { | |
NLA_POLICY_MASK(NLA_BE32, NF_NAT_RANGE_MASK), | |
[NFTA_MASQ_REG_PROTO_MIN] = { .type = NLA_U32 }, | |
[NFTA_MASQ_REG_PROTO_MAX] = { .type = NLA_U32 }, | |
+ [NFTA_MASQ_REG_ADDR_MIN] = { .type = NLA_U32 }, | |
+ [NFTA_MASQ_REG_ADDR_MAX] = { .type = NLA_U32 }, | |
}; | |
static int nft_masq_validate(const struct nft_ctx *ctx, | |
@@ -45,6 +49,7 @@ static int nft_masq_init(const struct nft_ctx *ctx, | |
const struct nlattr * const tb[]) | |
{ | |
u32 plen = sizeof_field(struct nf_nat_range, min_proto.all); | |
+ u32 alen = sizeof_field(struct nf_nat_range, min_addr.all); | |
struct nft_masq *priv = nft_expr_priv(expr); | |
int err; | |
@@ -68,6 +73,25 @@ static int nft_masq_init(const struct nft_ctx *ctx, | |
} | |
} | |
+ if (tb[NFTA_MASQ_REG_ADDR_MIN]) { | |
+ err = nft_parse_register_load(tb[NFTA_MASQ_REG_ADDR_MIN], | |
+ &priv->sreg_addr_min, alen); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ if (tb[NFTA_MASQ_REG_ADDR_MAX]) { | |
+ err = nft_parse_register_load(tb[NFTA_MASQ_REG_ADDR_MAX], | |
+ &priv->sreg_addr_max, | |
+ alen); | |
+ if (err < 0) | |
+ return err; | |
+ } else { | |
+ priv->sreg_addr_max = priv->sreg_addr_min; | |
+ } | |
+ | |
+ priv->flags |= NF_NAT_RANGE_MAP_IPS; | |
+ } | |
+ | |
return nf_ct_netns_get(ctx->net, ctx->family); | |
} | |
@@ -88,6 +112,14 @@ static int nft_masq_dump(struct sk_buff *skb, | |
goto nla_put_failure; | |
} | |
+ if (priv->sreg_addr_min) { | |
+ if (nft_dump_register(skb, NFTA_MASQ_REG_ADDR_MIN, | |
+ priv->sreg_addr_min) || | |
+ nft_dump_register(skb, NFTA_MASQ_REG_ADDR_MAX, | |
+ priv->sreg_addr_max)) | |
+ goto nla_put_failure; | |
+ } | |
+ | |
return 0; | |
nla_put_failure: | |
@@ -112,6 +144,12 @@ static void nft_masq_eval(const struct nft_expr *expr, | |
switch (nft_pf(pkt)) { | |
case NFPROTO_IPV4: | |
+ if (priv->sreg_addr_min) { | |
+ range.min_addr.ip = (__force __be32) | |
+ regs->data[priv->sreg_addr_min]; | |
+ range.max_addr.ip = (__force __be32) | |
+ regs->data[priv->sreg_addr_max]; | |
+ } | |
regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, | |
nft_hook(pkt), | |
&range, | |
@@ -119,6 +157,12 @@ static void nft_masq_eval(const struct nft_expr *expr, | |
break; | |
#ifdef CONFIG_NF_TABLES_IPV6 | |
case NFPROTO_IPV6: | |
+ if (priv->sreg_addr_min) { | |
+ memcpy(range.min_addr.ip6, ®s->data[priv->sreg_addr_min], | |
+ sizeof(range.min_addr.ip6)); | |
+ memcpy(range.max_addr.ip6, ®s->data[priv->sreg_addr_max], | |
+ sizeof(range.max_addr.ip6)); | |
+ } | |
regs->verdict.code = nf_nat_masquerade_ipv6(pkt->skb, &range, | |
nft_out(pkt)); | |
break; | |
-- | |
2.39.2 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment