netfilter: nft_nat: add netmap support

This patch allows you to NAT the network address prefix onto another
network address prefix, a.k.a. netmapping.

Userspace must specify the NF_NAT_RANGE_NETMAP flag and the prefix
address through the NFTA_NAT_REG_ADDR_MIN and NFTA_NAT_REG_ADDR_MAX
netlink attributes.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Pablo Neira Ayuso 2020-04-24 21:55:37 +02:00
parent acd766e31b
commit 3ff7ddb135
2 changed files with 48 additions and 2 deletions

View file

@ -11,6 +11,7 @@
#define NF_NAT_RANGE_PERSISTENT (1 << 3)
#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)
#define NF_NAT_RANGE_PROTO_OFFSET (1 << 5)
#define NF_NAT_RANGE_NETMAP (1 << 6)
#define NF_NAT_RANGE_PROTO_RANDOM_ALL \
(NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
@ -18,7 +19,8 @@
#define NF_NAT_RANGE_MASK \
(NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED | \
NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PERSISTENT | \
NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET)
NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET | \
NF_NAT_RANGE_NETMAP)
struct nf_nat_ipv4_range {
unsigned int flags;

View file

@ -60,6 +60,46 @@ static void nft_nat_setup_proto(struct nf_nat_range2 *range,
nft_reg_load16(&regs->data[priv->sreg_proto_max]);
}
static void nft_nat_setup_netmap(struct nf_nat_range2 *range,
const struct nft_pktinfo *pkt,
const struct nft_nat *priv)
{
struct sk_buff *skb = pkt->skb;
union nf_inet_addr new_addr;
__be32 netmask;
int i, len = 0;
switch (priv->type) {
case NFT_NAT_SNAT:
if (nft_pf(pkt) == NFPROTO_IPV4) {
new_addr.ip = ip_hdr(skb)->saddr;
len = sizeof(struct in_addr);
} else {
new_addr.in6 = ipv6_hdr(skb)->saddr;
len = sizeof(struct in6_addr);
}
break;
case NFT_NAT_DNAT:
if (nft_pf(pkt) == NFPROTO_IPV4) {
new_addr.ip = ip_hdr(skb)->daddr;
len = sizeof(struct in_addr);
} else {
new_addr.in6 = ipv6_hdr(skb)->daddr;
len = sizeof(struct in6_addr);
}
break;
}
for (i = 0; i < len / sizeof(__be32); i++) {
netmask = ~(range->min_addr.ip6[i] ^ range->max_addr.ip6[i]);
new_addr.ip6[i] &= ~netmask;
new_addr.ip6[i] |= range->min_addr.ip6[i] & netmask;
}
range->min_addr = new_addr;
range->max_addr = new_addr;
}
static void nft_nat_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
@ -70,8 +110,12 @@ static void nft_nat_eval(const struct nft_expr *expr,
struct nf_nat_range2 range;
memset(&range, 0, sizeof(range));
if (priv->sreg_addr_min)
if (priv->sreg_addr_min) {
nft_nat_setup_addr(&range, regs, priv);
if (priv->flags & NF_NAT_RANGE_NETMAP)
nft_nat_setup_netmap(&range, pkt, priv);
}
if (priv->sreg_proto_min)
nft_nat_setup_proto(&range, regs, priv);