Commit 3ff7ddb1 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso
Browse files

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: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent acd766e3
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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;
+45 −1
Original line number Diff line number Diff line
@@ -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);