Skip to content

Commit

Permalink
netfilter: nf_tables: Add support for IPv6 NAT
Browse files Browse the repository at this point in the history
This patch generalizes the NAT expression to support both IPv4 and IPv6
using the existing IPv4/IPv6 NAT infrastructure. This also adds the
NAT chain type for IPv6.

This patch collapses the following patches that were posted to the
netfilter-devel mailing list, from Tomasz:

* nf_tables: Change NFTA_NAT_ attributes to better semantic significance
* nf_tables: Split IPv4 NAT into NAT expression and IPv4 NAT chain
* nf_tables: Add support for IPv6 NAT expression
* nf_tables: Add support for IPv6 NAT chain
* nf_tables: Fix up build issue on IPv6 NAT support

And, from Pablo Neira Ayuso:

* fix missing dependencies in nft_chain_nat

Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
Tomasz Bursztyka authored and Pablo Neira Ayuso committed Oct 14, 2013
1 parent 9ddf632 commit eb31628
Show file tree
Hide file tree
Showing 9 changed files with 457 additions and 162 deletions.
18 changes: 10 additions & 8 deletions include/uapi/linux/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -695,18 +695,20 @@ enum nft_nat_types {
* enum nft_nat_attributes - nf_tables nat expression netlink attributes
*
* @NFTA_NAT_TYPE: NAT type (NLA_U32: nft_nat_types)
* @NFTA_NAT_ADDR_MIN: source register of address range start (NLA_U32: nft_registers)
* @NFTA_NAT_ADDR_MAX: source register of address range end (NLA_U32: nft_registers)
* @NFTA_NAT_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers)
* @NFTA_NAT_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers)
* @NFTA_NAT_FAMILY: NAT family (NLA_U32)
* @NFTA_NAT_REG_ADDR_MIN: source register of address range start (NLA_U32: nft_registers)
* @NFTA_NAT_REG_ADDR_MAX: source register of address range end (NLA_U32: nft_registers)
* @NFTA_NAT_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers)
* @NFTA_NAT_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers)
*/
enum nft_nat_attributes {
NFTA_NAT_UNSPEC,
NFTA_NAT_TYPE,
NFTA_NAT_ADDR_MIN,
NFTA_NAT_ADDR_MAX,
NFTA_NAT_PROTO_MIN,
NFTA_NAT_PROTO_MAX,
NFTA_NAT_FAMILY,
NFTA_NAT_REG_ADDR_MIN,
NFTA_NAT_REG_ADDR_MAX,
NFTA_NAT_REG_PROTO_MIN,
NFTA_NAT_REG_PROTO_MAX,
__NFTA_NAT_MAX
};
#define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1)
Expand Down
1 change: 1 addition & 0 deletions net/ipv4/netfilter/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ config NFT_CHAIN_ROUTE_IPV4

config NFT_CHAIN_NAT_IPV4
depends on NF_TABLES_IPV4
depends on NF_NAT_IPV4 && NFT_NAT
tristate "IPv4 nf_tables nat chain support"

config IP_NF_IPTABLES
Expand Down
156 changes: 2 additions & 154 deletions net/ipv4/netfilter/nft_chain_nat_ipv4.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
* Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
* Copyright (c) 2012 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
Expand All @@ -14,10 +15,8 @@
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_nat.h>
Expand All @@ -27,147 +26,6 @@
#include <net/netfilter/nf_nat_l3proto.h>
#include <net/ip.h>

struct nft_nat {
enum nft_registers sreg_addr_min:8;
enum nft_registers sreg_addr_max:8;
enum nft_registers sreg_proto_min:8;
enum nft_registers sreg_proto_max:8;
enum nf_nat_manip_type type;
};

static void nft_nat_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
const struct nft_nat *priv = nft_expr_priv(expr);
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo);
struct nf_nat_range range;

memset(&range, 0, sizeof(range));
if (priv->sreg_addr_min) {
range.min_addr.ip = data[priv->sreg_addr_min].data[0];
range.max_addr.ip = data[priv->sreg_addr_max].data[0];
range.flags |= NF_NAT_RANGE_MAP_IPS;
}

if (priv->sreg_proto_min) {
range.min_proto.all = data[priv->sreg_proto_min].data[0];
range.max_proto.all = data[priv->sreg_proto_max].data[0];
range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
}

data[NFT_REG_VERDICT].verdict =
nf_nat_setup_info(ct, &range, priv->type);
}

static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = {
[NFTA_NAT_ADDR_MIN] = { .type = NLA_U32 },
[NFTA_NAT_ADDR_MAX] = { .type = NLA_U32 },
[NFTA_NAT_PROTO_MIN] = { .type = NLA_U32 },
[NFTA_NAT_PROTO_MAX] = { .type = NLA_U32 },
[NFTA_NAT_TYPE] = { .type = NLA_U32 },
};

static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_nat *priv = nft_expr_priv(expr);
int err;

if (tb[NFTA_NAT_TYPE] == NULL)
return -EINVAL;

switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) {
case NFT_NAT_SNAT:
priv->type = NF_NAT_MANIP_SRC;
break;
case NFT_NAT_DNAT:
priv->type = NF_NAT_MANIP_DST;
break;
default:
return -EINVAL;
}

if (tb[NFTA_NAT_ADDR_MIN]) {
priv->sreg_addr_min = ntohl(nla_get_be32(tb[NFTA_NAT_ADDR_MIN]));
err = nft_validate_input_register(priv->sreg_addr_min);
if (err < 0)
return err;
}

if (tb[NFTA_NAT_ADDR_MAX]) {
priv->sreg_addr_max = ntohl(nla_get_be32(tb[NFTA_NAT_ADDR_MAX]));
err = nft_validate_input_register(priv->sreg_addr_max);
if (err < 0)
return err;
} else
priv->sreg_addr_max = priv->sreg_addr_min;

if (tb[NFTA_NAT_PROTO_MIN]) {
priv->sreg_proto_min = ntohl(nla_get_be32(tb[NFTA_NAT_PROTO_MIN]));
err = nft_validate_input_register(priv->sreg_proto_min);
if (err < 0)
return err;
}

if (tb[NFTA_NAT_PROTO_MAX]) {
priv->sreg_proto_max = ntohl(nla_get_be32(tb[NFTA_NAT_PROTO_MAX]));
err = nft_validate_input_register(priv->sreg_proto_max);
if (err < 0)
return err;
} else
priv->sreg_proto_max = priv->sreg_proto_min;

return 0;
}

static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_nat *priv = nft_expr_priv(expr);

switch (priv->type) {
case NF_NAT_MANIP_SRC:
if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT)))
goto nla_put_failure;
break;
case NF_NAT_MANIP_DST:
if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT)))
goto nla_put_failure;
break;
}

if (nla_put_be32(skb, NFTA_NAT_ADDR_MIN, htonl(priv->sreg_addr_min)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_NAT_ADDR_MAX, htonl(priv->sreg_addr_max)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_NAT_PROTO_MIN, htonl(priv->sreg_proto_min)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_NAT_PROTO_MAX, htonl(priv->sreg_proto_max)))
goto nla_put_failure;
return 0;

nla_put_failure:
return -1;
}

static struct nft_expr_type nft_nat_type;
static const struct nft_expr_ops nft_nat_ops = {
.type = &nft_nat_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_nat)),
.eval = nft_nat_eval,
.init = nft_nat_init,
.dump = nft_nat_dump,
};

static struct nft_expr_type nft_nat_type __read_mostly = {
.name = "nat",
.ops = &nft_nat_ops,
.policy = nft_nat_policy,
.maxattr = NFTA_NAT_MAX,
.owner = THIS_MODULE,
};

/*
* NAT chains
*/
Expand Down Expand Up @@ -306,7 +164,7 @@ static unsigned int nf_nat_output(const struct nf_hook_ops *ops,
return ret;
}

struct nf_chain_type nft_chain_nat_ipv4 = {
static struct nf_chain_type nft_chain_nat_ipv4 = {
.family = NFPROTO_IPV4,
.name = "nat",
.type = NFT_CHAIN_T_NAT,
Expand All @@ -331,20 +189,11 @@ static int __init nft_chain_nat_init(void)
if (err < 0)
return err;

err = nft_register_expr(&nft_nat_type);
if (err < 0)
goto err;

return 0;

err:
nft_unregister_chain_type(&nft_chain_nat_ipv4);
return err;
}

static void __exit nft_chain_nat_exit(void)
{
nft_unregister_expr(&nft_nat_type);
nft_unregister_chain_type(&nft_chain_nat_ipv4);
}

Expand All @@ -354,4 +203,3 @@ module_exit(nft_chain_nat_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_CHAIN(AF_INET, "nat");
MODULE_ALIAS_NFT_EXPR("nat");
5 changes: 5 additions & 0 deletions net/ipv6/netfilter/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ config NFT_CHAIN_ROUTE_IPV6
depends on NF_TABLES_IPV6
tristate "IPv6 nf_tables route chain support"

config NFT_CHAIN_NAT_IPV6
depends on NF_TABLES_IPV6
depends on NF_NAT_IPV6 && NFT_NAT
tristate "IPv6 nf_tables nat chain support"

config IP6_NF_IPTABLES
tristate "IP6 tables support (required for filtering)"
depends on INET && IPV6
Expand Down
1 change: 1 addition & 0 deletions net/ipv6/netfilter/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o
# nf_tables
obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o
obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o
obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o

# matches
obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o
Expand Down
Loading

0 comments on commit eb31628

Please sign in to comment.