Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save love4taylor/a56985ecde4f16bab2cf54ee1c6a0c32 to your computer and use it in GitHub Desktop.
Save love4taylor/a56985ecde4f16bab2cf54ee1c6a0c32 to your computer and use it in GitHub Desktop.
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
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, &regs->data[priv->sreg_addr_min],
+ sizeof(range.min_addr.ip6));
+ memcpy(range.max_addr.ip6, &regs->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