Support was recently added to nft_nat to allow shifting port-ranges during NAT. Extend this support to allow them to used in masquerading as well. Set `NF_NAT_RANGE_PROTO_SPECIFIED` flag where appropriate. `nft_nat` and `nft_redir` both do this. It is also set in user space. However, it is only ever used internally by the kernel modules, so it would be good to remove the references to it from user space. Signed-off-by: Jeremy Sowden <jeremy@xxxxxxxxxx> --- include/uapi/linux/netfilter/nf_tables.h | 2 ++ net/netfilter/nf_nat_masquerade.c | 2 ++ net/netfilter/nft_masq.c | 25 +++++++++++++++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 2938e878d3fd..08780ed008c7 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1472,12 +1472,14 @@ 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_PROTO_BASE: source register of proto range base offset (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_PROTO_BASE, __NFTA_MASQ_MAX }; #define NFTA_MASQ_MAX (__NFTA_MASQ_MAX - 1) diff --git a/net/netfilter/nf_nat_masquerade.c b/net/netfilter/nf_nat_masquerade.c index 1a506b0c6511..8d40b507d4ad 100644 --- a/net/netfilter/nf_nat_masquerade.c +++ b/net/netfilter/nf_nat_masquerade.c @@ -69,6 +69,7 @@ nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum, newrange.max_addr.ip = newsrc; newrange.min_proto = range->min_proto; newrange.max_proto = range->max_proto; + newrange.base_proto = range->base_proto; /* Hand modified range to generic setup. */ return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC); @@ -264,6 +265,7 @@ nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range, newrange.max_addr.in6 = src; newrange.min_proto = range->min_proto; newrange.max_proto = range->max_proto; + newrange.base_proto = range->base_proto; return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC); } diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index b115d77fbbc7..c9674f5a8c7f 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -17,12 +17,14 @@ struct nft_masq { u32 flags; u8 sreg_proto_min; u8 sreg_proto_max; + u8 sreg_proto_base; }; static const struct nla_policy nft_masq_policy[NFTA_MASQ_MAX + 1] = { [NFTA_MASQ_FLAGS] = { .type = NLA_U32 }, [NFTA_MASQ_REG_PROTO_MIN] = { .type = NLA_U32 }, [NFTA_MASQ_REG_PROTO_MAX] = { .type = NLA_U32 }, + [NFTA_MASQ_REG_PROTO_BASE] = { .type = NLA_U32 }, }; static int nft_masq_validate(const struct nft_ctx *ctx, @@ -43,7 +45,7 @@ static int nft_masq_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]) { - u32 plen = sizeof_field(struct nf_nat_range, min_proto.all); + u32 plen = sizeof_field(struct nf_nat_range2, min_proto.all); struct nft_masq *priv = nft_expr_priv(expr); int err; @@ -65,9 +67,21 @@ static int nft_masq_init(const struct nft_ctx *ctx, plen); if (err < 0) return err; + + if (tb[NFTA_MASQ_REG_PROTO_BASE]) { + err = nft_parse_register_load + (tb[NFTA_MASQ_REG_PROTO_BASE], + &priv->sreg_proto_base, plen); + if (err < 0) + return err; + + priv->flags |= NF_NAT_RANGE_PROTO_OFFSET; + } } else { priv->sreg_proto_max = priv->sreg_proto_min; } + + priv->flags |= NF_NAT_RANGE_PROTO_SPECIFIED; } return nf_ct_netns_get(ctx->net, ctx->family); @@ -88,6 +102,11 @@ static int nft_masq_dump(struct sk_buff *skb, nft_dump_register(skb, NFTA_MASQ_REG_PROTO_MAX, priv->sreg_proto_max)) goto nla_put_failure; + + if (priv->sreg_proto_base) + if (nft_dump_register(skb, NFTA_MASQ_REG_PROTO_BASE, + priv->sreg_proto_base)) + goto nla_put_failure; } return 0; @@ -110,6 +129,10 @@ static void nft_masq_eval(const struct nft_expr *expr, nft_reg_load16(®s->data[priv->sreg_proto_min]); range.max_proto.all = (__force __be16) nft_reg_load16(®s->data[priv->sreg_proto_max]); + + if (priv->sreg_proto_base) + range.base_proto.all = (__force __be16) + nft_reg_load16(®s->data[priv->sreg_proto_base]); } switch (nft_pf(pkt)) { -- 2.39.2