diff options
| author | Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com> | 2013-10-10 07:39:19 -0400 |
|---|---|---|
| committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-10-14 12:00:58 -0400 |
| commit | eb31628e37a0a4e01fffd79dcc7f815d2357f53a (patch) | |
| tree | 8f78d641ddc8817109b55cedb114e4a4a8d9c045 | |
| parent | 9ddf63235749a9efa1fad2eeb74be2ee9b580f8d (diff) | |
netfilter: nf_tables: Add support for IPv6 NAT
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>
| -rw-r--r-- | include/uapi/linux/netfilter/nf_tables.h | 18 | ||||
| -rw-r--r-- | net/ipv4/netfilter/Kconfig | 1 | ||||
| -rw-r--r-- | net/ipv4/netfilter/nft_chain_nat_ipv4.c | 156 | ||||
| -rw-r--r-- | net/ipv6/netfilter/Kconfig | 5 | ||||
| -rw-r--r-- | net/ipv6/netfilter/Makefile | 1 | ||||
| -rw-r--r-- | net/ipv6/netfilter/nft_chain_nat_ipv6.c | 211 | ||||
| -rw-r--r-- | net/netfilter/Kconfig | 6 | ||||
| -rw-r--r-- | net/netfilter/Makefile | 1 | ||||
| -rw-r--r-- | net/netfilter/nft_nat.c | 220 |
9 files changed, 457 insertions, 162 deletions
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index a9c4bce1988f..7d4a1992f89c 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h | |||
| @@ -695,18 +695,20 @@ enum nft_nat_types { | |||
| 695 | * enum nft_nat_attributes - nf_tables nat expression netlink attributes | 695 | * enum nft_nat_attributes - nf_tables nat expression netlink attributes |
| 696 | * | 696 | * |
| 697 | * @NFTA_NAT_TYPE: NAT type (NLA_U32: nft_nat_types) | 697 | * @NFTA_NAT_TYPE: NAT type (NLA_U32: nft_nat_types) |
| 698 | * @NFTA_NAT_ADDR_MIN: source register of address range start (NLA_U32: nft_registers) | 698 | * @NFTA_NAT_FAMILY: NAT family (NLA_U32) |
| 699 | * @NFTA_NAT_ADDR_MAX: source register of address range end (NLA_U32: nft_registers) | 699 | * @NFTA_NAT_REG_ADDR_MIN: source register of address range start (NLA_U32: nft_registers) |
| 700 | * @NFTA_NAT_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers) | 700 | * @NFTA_NAT_REG_ADDR_MAX: source register of address range end (NLA_U32: nft_registers) |
| 701 | * @NFTA_NAT_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers) | 701 | * @NFTA_NAT_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers) |
| 702 | * @NFTA_NAT_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers) | ||
| 702 | */ | 703 | */ |
| 703 | enum nft_nat_attributes { | 704 | enum nft_nat_attributes { |
| 704 | NFTA_NAT_UNSPEC, | 705 | NFTA_NAT_UNSPEC, |
| 705 | NFTA_NAT_TYPE, | 706 | NFTA_NAT_TYPE, |
| 706 | NFTA_NAT_ADDR_MIN, | 707 | NFTA_NAT_FAMILY, |
| 707 | NFTA_NAT_ADDR_MAX, | 708 | NFTA_NAT_REG_ADDR_MIN, |
| 708 | NFTA_NAT_PROTO_MIN, | 709 | NFTA_NAT_REG_ADDR_MAX, |
| 709 | NFTA_NAT_PROTO_MAX, | 710 | NFTA_NAT_REG_PROTO_MIN, |
| 711 | NFTA_NAT_REG_PROTO_MAX, | ||
| 710 | __NFTA_NAT_MAX | 712 | __NFTA_NAT_MAX |
| 711 | }; | 713 | }; |
| 712 | #define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1) | 714 | #define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1) |
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index ae65fe98bfbe..1f37ef67f1ac 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig | |||
| @@ -50,6 +50,7 @@ config NFT_CHAIN_ROUTE_IPV4 | |||
| 50 | 50 | ||
| 51 | config NFT_CHAIN_NAT_IPV4 | 51 | config NFT_CHAIN_NAT_IPV4 |
| 52 | depends on NF_TABLES_IPV4 | 52 | depends on NF_TABLES_IPV4 |
| 53 | depends on NF_NAT_IPV4 && NFT_NAT | ||
| 53 | tristate "IPv4 nf_tables nat chain support" | 54 | tristate "IPv4 nf_tables nat chain support" |
| 54 | 55 | ||
| 55 | config IP_NF_IPTABLES | 56 | config IP_NF_IPTABLES |
diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index e09c201adf84..cf2c792cd971 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> | 2 | * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> |
| 3 | * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org> | 3 | * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org> |
| 4 | * Copyright (c) 2012 Intel Corporation | ||
| 4 | * | 5 | * |
| 5 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU General Public License version 2 as | 7 | * it under the terms of the GNU General Public License version 2 as |
| @@ -14,10 +15,8 @@ | |||
| 14 | #include <linux/list.h> | 15 | #include <linux/list.h> |
| 15 | #include <linux/skbuff.h> | 16 | #include <linux/skbuff.h> |
| 16 | #include <linux/ip.h> | 17 | #include <linux/ip.h> |
| 17 | #include <linux/netlink.h> | ||
| 18 | #include <linux/netfilter.h> | 18 | #include <linux/netfilter.h> |
| 19 | #include <linux/netfilter_ipv4.h> | 19 | #include <linux/netfilter_ipv4.h> |
| 20 | #include <linux/netfilter/nfnetlink.h> | ||
| 21 | #include <linux/netfilter/nf_tables.h> | 20 | #include <linux/netfilter/nf_tables.h> |
| 22 | #include <net/netfilter/nf_conntrack.h> | 21 | #include <net/netfilter/nf_conntrack.h> |
| 23 | #include <net/netfilter/nf_nat.h> | 22 | #include <net/netfilter/nf_nat.h> |
| @@ -27,147 +26,6 @@ | |||
| 27 | #include <net/netfilter/nf_nat_l3proto.h> | 26 | #include <net/netfilter/nf_nat_l3proto.h> |
| 28 | #include <net/ip.h> | 27 | #include <net/ip.h> |
| 29 | 28 | ||
| 30 | struct nft_nat { | ||
| 31 | enum nft_registers sreg_addr_min:8; | ||
| 32 | enum nft_registers sreg_addr_max:8; | ||
| 33 | enum nft_registers sreg_proto_min:8; | ||
| 34 | enum nft_registers sreg_proto_max:8; | ||
| 35 | enum nf_nat_manip_type type; | ||
| 36 | }; | ||
| 37 | |||
| 38 | static void nft_nat_eval(const struct nft_expr *expr, | ||
| 39 | struct nft_data data[NFT_REG_MAX + 1], | ||
| 40 | const struct nft_pktinfo *pkt) | ||
| 41 | { | ||
| 42 | const struct nft_nat *priv = nft_expr_priv(expr); | ||
| 43 | enum ip_conntrack_info ctinfo; | ||
| 44 | struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo); | ||
| 45 | struct nf_nat_range range; | ||
| 46 | |||
| 47 | memset(&range, 0, sizeof(range)); | ||
| 48 | if (priv->sreg_addr_min) { | ||
| 49 | range.min_addr.ip = data[priv->sreg_addr_min].data[0]; | ||
| 50 | range.max_addr.ip = data[priv->sreg_addr_max].data[0]; | ||
| 51 | range.flags |= NF_NAT_RANGE_MAP_IPS; | ||
| 52 | } | ||
| 53 | |||
| 54 | if (priv->sreg_proto_min) { | ||
| 55 | range.min_proto.all = data[priv->sreg_proto_min].data[0]; | ||
| 56 | range.max_proto.all = data[priv->sreg_proto_max].data[0]; | ||
| 57 | range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; | ||
| 58 | } | ||
| 59 | |||
| 60 | data[NFT_REG_VERDICT].verdict = | ||
| 61 | nf_nat_setup_info(ct, &range, priv->type); | ||
| 62 | } | ||
| 63 | |||
| 64 | static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = { | ||
| 65 | [NFTA_NAT_ADDR_MIN] = { .type = NLA_U32 }, | ||
| 66 | [NFTA_NAT_ADDR_MAX] = { .type = NLA_U32 }, | ||
| 67 | [NFTA_NAT_PROTO_MIN] = { .type = NLA_U32 }, | ||
| 68 | [NFTA_NAT_PROTO_MAX] = { .type = NLA_U32 }, | ||
| 69 | [NFTA_NAT_TYPE] = { .type = NLA_U32 }, | ||
| 70 | }; | ||
| 71 | |||
| 72 | static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, | ||
| 73 | const struct nlattr * const tb[]) | ||
| 74 | { | ||
| 75 | struct nft_nat *priv = nft_expr_priv(expr); | ||
| 76 | int err; | ||
| 77 | |||
| 78 | if (tb[NFTA_NAT_TYPE] == NULL) | ||
| 79 | return -EINVAL; | ||
| 80 | |||
| 81 | switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) { | ||
| 82 | case NFT_NAT_SNAT: | ||
| 83 | priv->type = NF_NAT_MANIP_SRC; | ||
| 84 | break; | ||
| 85 | case NFT_NAT_DNAT: | ||
| 86 | priv->type = NF_NAT_MANIP_DST; | ||
| 87 | break; | ||
| 88 | default: | ||
| 89 | return -EINVAL; | ||
| 90 | } | ||
| 91 | |||
| 92 | if (tb[NFTA_NAT_ADDR_MIN]) { | ||
| 93 | priv->sreg_addr_min = ntohl(nla_get_be32(tb[NFTA_NAT_ADDR_MIN])); | ||
| 94 | err = nft_validate_input_register(priv->sreg_addr_min); | ||
| 95 | if (err < 0) | ||
| 96 | return err; | ||
| 97 | } | ||
| 98 | |||
| 99 | if (tb[NFTA_NAT_ADDR_MAX]) { | ||
| 100 | priv->sreg_addr_max = ntohl(nla_get_be32(tb[NFTA_NAT_ADDR_MAX])); | ||
| 101 | err = nft_validate_input_register(priv->sreg_addr_max); | ||
| 102 | if (err < 0) | ||
| 103 | return err; | ||
| 104 | } else | ||
| 105 | priv->sreg_addr_max = priv->sreg_addr_min; | ||
| 106 | |||
| 107 | if (tb[NFTA_NAT_PROTO_MIN]) { | ||
| 108 | priv->sreg_proto_min = ntohl(nla_get_be32(tb[NFTA_NAT_PROTO_MIN])); | ||
| 109 | err = nft_validate_input_register(priv->sreg_proto_min); | ||
| 110 | if (err < 0) | ||
| 111 | return err; | ||
| 112 | } | ||
| 113 | |||
| 114 | if (tb[NFTA_NAT_PROTO_MAX]) { | ||
| 115 | priv->sreg_proto_max = ntohl(nla_get_be32(tb[NFTA_NAT_PROTO_MAX])); | ||
| 116 | err = nft_validate_input_register(priv->sreg_proto_max); | ||
| 117 | if (err < 0) | ||
| 118 | return err; | ||
| 119 | } else | ||
| 120 | priv->sreg_proto_max = priv->sreg_proto_min; | ||
| 121 | |||
| 122 | return 0; | ||
| 123 | } | ||
| 124 | |||
| 125 | static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr) | ||
| 126 | { | ||
| 127 | const struct nft_nat *priv = nft_expr_priv(expr); | ||
| 128 | |||
| 129 | switch (priv->type) { | ||
| 130 | case NF_NAT_MANIP_SRC: | ||
| 131 | if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT))) | ||
| 132 | goto nla_put_failure; | ||
| 133 | break; | ||
| 134 | case NF_NAT_MANIP_DST: | ||
| 135 | if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT))) | ||
| 136 | goto nla_put_failure; | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | |||
| 140 | if (nla_put_be32(skb, NFTA_NAT_ADDR_MIN, htonl(priv->sreg_addr_min))) | ||
| 141 | goto nla_put_failure; | ||
| 142 | if (nla_put_be32(skb, NFTA_NAT_ADDR_MAX, htonl(priv->sreg_addr_max))) | ||
| 143 | goto nla_put_failure; | ||
| 144 | if (nla_put_be32(skb, NFTA_NAT_PROTO_MIN, htonl(priv->sreg_proto_min))) | ||
| 145 | goto nla_put_failure; | ||
| 146 | if (nla_put_be32(skb, NFTA_NAT_PROTO_MAX, htonl(priv->sreg_proto_max))) | ||
| 147 | goto nla_put_failure; | ||
| 148 | return 0; | ||
| 149 | |||
| 150 | nla_put_failure: | ||
| 151 | return -1; | ||
| 152 | } | ||
| 153 | |||
| 154 | static struct nft_expr_type nft_nat_type; | ||
| 155 | static const struct nft_expr_ops nft_nat_ops = { | ||
| 156 | .type = &nft_nat_type, | ||
| 157 | .size = NFT_EXPR_SIZE(sizeof(struct nft_nat)), | ||
| 158 | .eval = nft_nat_eval, | ||
| 159 | .init = nft_nat_init, | ||
| 160 | .dump = nft_nat_dump, | ||
| 161 | }; | ||
| 162 | |||
| 163 | static struct nft_expr_type nft_nat_type __read_mostly = { | ||
| 164 | .name = "nat", | ||
| 165 | .ops = &nft_nat_ops, | ||
| 166 | .policy = nft_nat_policy, | ||
| 167 | .maxattr = NFTA_NAT_MAX, | ||
| 168 | .owner = THIS_MODULE, | ||
| 169 | }; | ||
| 170 | |||
| 171 | /* | 29 | /* |
| 172 | * NAT chains | 30 | * NAT chains |
| 173 | */ | 31 | */ |
| @@ -306,7 +164,7 @@ static unsigned int nf_nat_output(const struct nf_hook_ops *ops, | |||
| 306 | return ret; | 164 | return ret; |
| 307 | } | 165 | } |
| 308 | 166 | ||
| 309 | struct nf_chain_type nft_chain_nat_ipv4 = { | 167 | static struct nf_chain_type nft_chain_nat_ipv4 = { |
| 310 | .family = NFPROTO_IPV4, | 168 | .family = NFPROTO_IPV4, |
| 311 | .name = "nat", | 169 | .name = "nat", |
| 312 | .type = NFT_CHAIN_T_NAT, | 170 | .type = NFT_CHAIN_T_NAT, |
| @@ -331,20 +189,11 @@ static int __init nft_chain_nat_init(void) | |||
| 331 | if (err < 0) | 189 | if (err < 0) |
| 332 | return err; | 190 | return err; |
| 333 | 191 | ||
| 334 | err = nft_register_expr(&nft_nat_type); | ||
| 335 | if (err < 0) | ||
| 336 | goto err; | ||
| 337 | |||
| 338 | return 0; | 192 | return 0; |
| 339 | |||
| 340 | err: | ||
| 341 | nft_unregister_chain_type(&nft_chain_nat_ipv4); | ||
| 342 | return err; | ||
| 343 | } | 193 | } |
| 344 | 194 | ||
| 345 | static void __exit nft_chain_nat_exit(void) | 195 | static void __exit nft_chain_nat_exit(void) |
| 346 | { | 196 | { |
| 347 | nft_unregister_expr(&nft_nat_type); | ||
| 348 | nft_unregister_chain_type(&nft_chain_nat_ipv4); | 197 | nft_unregister_chain_type(&nft_chain_nat_ipv4); |
| 349 | } | 198 | } |
| 350 | 199 | ||
| @@ -354,4 +203,3 @@ module_exit(nft_chain_nat_exit); | |||
| 354 | MODULE_LICENSE("GPL"); | 203 | MODULE_LICENSE("GPL"); |
| 355 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | 204 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); |
| 356 | MODULE_ALIAS_NFT_CHAIN(AF_INET, "nat"); | 205 | MODULE_ALIAS_NFT_CHAIN(AF_INET, "nat"); |
| 357 | MODULE_ALIAS_NFT_EXPR("nat"); | ||
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 23833064b7b5..7702f9e90a04 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig | |||
| @@ -33,6 +33,11 @@ config NFT_CHAIN_ROUTE_IPV6 | |||
| 33 | depends on NF_TABLES_IPV6 | 33 | depends on NF_TABLES_IPV6 |
| 34 | tristate "IPv6 nf_tables route chain support" | 34 | tristate "IPv6 nf_tables route chain support" |
| 35 | 35 | ||
| 36 | config NFT_CHAIN_NAT_IPV6 | ||
| 37 | depends on NF_TABLES_IPV6 | ||
| 38 | depends on NF_NAT_IPV6 && NFT_NAT | ||
| 39 | tristate "IPv6 nf_tables nat chain support" | ||
| 40 | |||
| 36 | config IP6_NF_IPTABLES | 41 | config IP6_NF_IPTABLES |
| 37 | tristate "IP6 tables support (required for filtering)" | 42 | tristate "IP6 tables support (required for filtering)" |
| 38 | depends on INET && IPV6 | 43 | depends on INET && IPV6 |
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index be4913aa524d..d1b4928f34f7 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile | |||
| @@ -26,6 +26,7 @@ obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o | |||
| 26 | # nf_tables | 26 | # nf_tables |
| 27 | obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o | 27 | obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o |
| 28 | obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o | 28 | obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o |
| 29 | obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o | ||
| 29 | 30 | ||
| 30 | # matches | 31 | # matches |
| 31 | obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o | 32 | obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o |
diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c new file mode 100644 index 000000000000..e86dcd70dc76 --- /dev/null +++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c | |||
| @@ -0,0 +1,211 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2011 Patrick McHardy <kaber@trash.net> | ||
| 3 | * Copyright (c) 2012 Intel Corporation | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/list.h> | ||
| 14 | #include <linux/skbuff.h> | ||
| 15 | #include <linux/ip.h> | ||
| 16 | #include <linux/netfilter.h> | ||
| 17 | #include <linux/netfilter_ipv6.h> | ||
| 18 | #include <linux/netfilter/nf_tables.h> | ||
| 19 | #include <net/netfilter/nf_conntrack.h> | ||
| 20 | #include <net/netfilter/nf_nat.h> | ||
| 21 | #include <net/netfilter/nf_nat_core.h> | ||
| 22 | #include <net/netfilter/nf_tables.h> | ||
| 23 | #include <net/netfilter/nf_tables_ipv6.h> | ||
| 24 | #include <net/netfilter/nf_nat_l3proto.h> | ||
| 25 | #include <net/ipv6.h> | ||
| 26 | |||
| 27 | /* | ||
| 28 | * IPv6 NAT chains | ||
| 29 | */ | ||
| 30 | |||
| 31 | static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, | ||
| 32 | struct sk_buff *skb, | ||
| 33 | const struct net_device *in, | ||
| 34 | const struct net_device *out, | ||
| 35 | int (*okfn)(struct sk_buff *)) | ||
| 36 | { | ||
| 37 | enum ip_conntrack_info ctinfo; | ||
| 38 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
| 39 | struct nf_conn_nat *nat; | ||
| 40 | enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum); | ||
| 41 | __be16 frag_off; | ||
| 42 | int hdrlen; | ||
| 43 | u8 nexthdr; | ||
| 44 | struct nft_pktinfo pkt; | ||
| 45 | unsigned int ret; | ||
| 46 | |||
| 47 | if (ct == NULL || nf_ct_is_untracked(ct)) | ||
| 48 | return NF_ACCEPT; | ||
| 49 | |||
| 50 | nat = nfct_nat(ct); | ||
| 51 | if (nat == NULL) { | ||
| 52 | /* Conntrack module was loaded late, can't add extension. */ | ||
| 53 | if (nf_ct_is_confirmed(ct)) | ||
| 54 | return NF_ACCEPT; | ||
| 55 | nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | ||
| 56 | if (nat == NULL) | ||
| 57 | return NF_ACCEPT; | ||
| 58 | } | ||
| 59 | |||
| 60 | switch (ctinfo) { | ||
| 61 | case IP_CT_RELATED: | ||
| 62 | case IP_CT_RELATED + IP_CT_IS_REPLY: | ||
| 63 | nexthdr = ipv6_hdr(skb)->nexthdr; | ||
| 64 | hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), | ||
| 65 | &nexthdr, &frag_off); | ||
| 66 | |||
| 67 | if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { | ||
| 68 | if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo, | ||
| 69 | ops->hooknum, | ||
| 70 | hdrlen)) | ||
| 71 | return NF_DROP; | ||
| 72 | else | ||
| 73 | return NF_ACCEPT; | ||
| 74 | } | ||
| 75 | /* Fall through */ | ||
| 76 | case IP_CT_NEW: | ||
| 77 | if (nf_nat_initialized(ct, maniptype)) | ||
| 78 | break; | ||
| 79 | |||
| 80 | nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out); | ||
| 81 | |||
| 82 | ret = nft_do_chain_pktinfo(&pkt, ops); | ||
| 83 | if (ret != NF_ACCEPT) | ||
| 84 | return ret; | ||
| 85 | if (!nf_nat_initialized(ct, maniptype)) { | ||
| 86 | ret = nf_nat_alloc_null_binding(ct, ops->hooknum); | ||
| 87 | if (ret != NF_ACCEPT) | ||
| 88 | return ret; | ||
| 89 | } | ||
| 90 | default: | ||
| 91 | break; | ||
| 92 | } | ||
| 93 | |||
| 94 | return nf_nat_packet(ct, ctinfo, ops->hooknum, skb); | ||
| 95 | } | ||
| 96 | |||
| 97 | static unsigned int nf_nat_ipv6_prerouting(const struct nf_hook_ops *ops, | ||
| 98 | struct sk_buff *skb, | ||
| 99 | const struct net_device *in, | ||
| 100 | const struct net_device *out, | ||
| 101 | int (*okfn)(struct sk_buff *)) | ||
| 102 | { | ||
| 103 | struct in6_addr daddr = ipv6_hdr(skb)->daddr; | ||
| 104 | unsigned int ret; | ||
| 105 | |||
| 106 | ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); | ||
| 107 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
| 108 | ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr)) | ||
| 109 | skb_dst_drop(skb); | ||
| 110 | |||
| 111 | return ret; | ||
| 112 | } | ||
| 113 | |||
| 114 | static unsigned int nf_nat_ipv6_postrouting(const struct nf_hook_ops *ops, | ||
| 115 | struct sk_buff *skb, | ||
| 116 | const struct net_device *in, | ||
| 117 | const struct net_device *out, | ||
| 118 | int (*okfn)(struct sk_buff *)) | ||
| 119 | { | ||
| 120 | enum ip_conntrack_info ctinfo __maybe_unused; | ||
| 121 | const struct nf_conn *ct __maybe_unused; | ||
| 122 | unsigned int ret; | ||
| 123 | |||
| 124 | ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); | ||
| 125 | #ifdef CONFIG_XFRM | ||
| 126 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
| 127 | !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && | ||
| 128 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
| 129 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
| 130 | |||
| 131 | if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, | ||
| 132 | &ct->tuplehash[!dir].tuple.dst.u3) || | ||
| 133 | (ct->tuplehash[dir].tuple.src.u.all != | ||
| 134 | ct->tuplehash[!dir].tuple.dst.u.all)) | ||
| 135 | if (nf_xfrm_me_harder(skb, AF_INET6) < 0) | ||
| 136 | ret = NF_DROP; | ||
| 137 | } | ||
| 138 | #endif | ||
| 139 | return ret; | ||
| 140 | } | ||
| 141 | |||
| 142 | static unsigned int nf_nat_ipv6_output(const struct nf_hook_ops *ops, | ||
| 143 | struct sk_buff *skb, | ||
| 144 | const struct net_device *in, | ||
| 145 | const struct net_device *out, | ||
| 146 | int (*okfn)(struct sk_buff *)) | ||
| 147 | { | ||
| 148 | enum ip_conntrack_info ctinfo; | ||
| 149 | const struct nf_conn *ct; | ||
| 150 | unsigned int ret; | ||
| 151 | |||
| 152 | ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); | ||
| 153 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
| 154 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
| 155 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
| 156 | |||
| 157 | if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, | ||
| 158 | &ct->tuplehash[!dir].tuple.src.u3)) { | ||
| 159 | if (ip6_route_me_harder(skb)) | ||
| 160 | ret = NF_DROP; | ||
| 161 | } | ||
| 162 | #ifdef CONFIG_XFRM | ||
| 163 | else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && | ||
| 164 | ct->tuplehash[dir].tuple.dst.u.all != | ||
| 165 | ct->tuplehash[!dir].tuple.src.u.all) | ||
| 166 | if (nf_xfrm_me_harder(skb, AF_INET6)) | ||
| 167 | ret = NF_DROP; | ||
| 168 | #endif | ||
| 169 | } | ||
| 170 | return ret; | ||
| 171 | } | ||
| 172 | |||
| 173 | static struct nf_chain_type nft_chain_nat_ipv6 = { | ||
| 174 | .family = NFPROTO_IPV6, | ||
| 175 | .name = "nat", | ||
| 176 | .type = NFT_CHAIN_T_NAT, | ||
| 177 | .hook_mask = (1 << NF_INET_PRE_ROUTING) | | ||
| 178 | (1 << NF_INET_POST_ROUTING) | | ||
| 179 | (1 << NF_INET_LOCAL_OUT) | | ||
| 180 | (1 << NF_INET_LOCAL_IN), | ||
| 181 | .fn = { | ||
| 182 | [NF_INET_PRE_ROUTING] = nf_nat_ipv6_prerouting, | ||
| 183 | [NF_INET_POST_ROUTING] = nf_nat_ipv6_postrouting, | ||
| 184 | [NF_INET_LOCAL_OUT] = nf_nat_ipv6_output, | ||
| 185 | [NF_INET_LOCAL_IN] = nf_nat_ipv6_fn, | ||
| 186 | }, | ||
| 187 | .me = THIS_MODULE, | ||
| 188 | }; | ||
| 189 | |||
| 190 | static int __init nft_chain_nat_ipv6_init(void) | ||
| 191 | { | ||
| 192 | int err; | ||
| 193 | |||
| 194 | err = nft_register_chain_type(&nft_chain_nat_ipv6); | ||
| 195 | if (err < 0) | ||
| 196 | return err; | ||
| 197 | |||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | static void __exit nft_chain_nat_ipv6_exit(void) | ||
| 202 | { | ||
| 203 | nft_unregister_chain_type(&nft_chain_nat_ipv6); | ||
| 204 | } | ||
| 205 | |||
| 206 | module_init(nft_chain_nat_ipv6_init); | ||
| 207 | module_exit(nft_chain_nat_ipv6_exit); | ||
| 208 | |||
| 209 | MODULE_LICENSE("GPL"); | ||
| 210 | MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>"); | ||
| 211 | MODULE_ALIAS_NFT_CHAIN(AF_INET6, "nat"); | ||
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 49e362707379..48acec17e27a 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
| @@ -450,6 +450,12 @@ config NFT_LIMIT | |||
| 450 | depends on NF_TABLES | 450 | depends on NF_TABLES |
| 451 | tristate "Netfilter nf_tables limit module" | 451 | tristate "Netfilter nf_tables limit module" |
| 452 | 452 | ||
| 453 | config NFT_NAT | ||
| 454 | depends on NF_TABLES | ||
| 455 | depends on NF_CONNTRACK | ||
| 456 | depends on NF_NAT | ||
| 457 | tristate "Netfilter nf_tables nat module" | ||
| 458 | |||
| 453 | config NFT_COMPAT | 459 | config NFT_COMPAT |
| 454 | depends on NF_TABLES | 460 | depends on NF_TABLES |
| 455 | depends on NETFILTER_XTABLES | 461 | depends on NETFILTER_XTABLES |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index a6781450b6fb..394483b2c193 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
| @@ -75,6 +75,7 @@ obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o | |||
| 75 | obj-$(CONFIG_NFT_META) += nft_meta.o | 75 | obj-$(CONFIG_NFT_META) += nft_meta.o |
| 76 | obj-$(CONFIG_NFT_CT) += nft_ct.o | 76 | obj-$(CONFIG_NFT_CT) += nft_ct.o |
| 77 | obj-$(CONFIG_NFT_LIMIT) += nft_limit.o | 77 | obj-$(CONFIG_NFT_LIMIT) += nft_limit.o |
| 78 | obj-$(CONFIG_NFT_NAT) += nft_nat.o | ||
| 78 | #nf_tables-objs += nft_meta_target.o | 79 | #nf_tables-objs += nft_meta_target.o |
| 79 | obj-$(CONFIG_NFT_RBTREE) += nft_rbtree.o | 80 | obj-$(CONFIG_NFT_RBTREE) += nft_rbtree.o |
| 80 | obj-$(CONFIG_NFT_HASH) += nft_hash.o | 81 | obj-$(CONFIG_NFT_HASH) += nft_hash.o |
diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c new file mode 100644 index 000000000000..b0b87b2d2411 --- /dev/null +++ b/net/netfilter/nft_nat.c | |||
| @@ -0,0 +1,220 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> | ||
| 3 | * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org> | ||
| 4 | * Copyright (c) 2012 Intel Corporation | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/init.h> | ||
| 14 | #include <linux/skbuff.h> | ||
| 15 | #include <linux/ip.h> | ||
| 16 | #include <linux/string.h> | ||
| 17 | #include <linux/netlink.h> | ||
| 18 | #include <linux/netfilter.h> | ||
| 19 | #include <linux/netfilter_ipv4.h> | ||
| 20 | #include <linux/netfilter/nfnetlink.h> | ||
| 21 | #include <linux/netfilter/nf_tables.h> | ||
| 22 | #include <net/netfilter/nf_conntrack.h> | ||
| 23 | #include <net/netfilter/nf_nat.h> | ||
| 24 | #include <net/netfilter/nf_nat_core.h> | ||
| 25 | #include <net/netfilter/nf_tables.h> | ||
| 26 | #include <net/netfilter/nf_nat_l3proto.h> | ||
| 27 | #include <net/ip.h> | ||
| 28 | |||
| 29 | struct nft_nat { | ||
| 30 | enum nft_registers sreg_addr_min:8; | ||
| 31 | enum nft_registers sreg_addr_max:8; | ||
| 32 | enum nft_registers sreg_proto_min:8; | ||
| 33 | enum nft_registers sreg_proto_max:8; | ||
| 34 | int family; | ||
| 35 | enum nf_nat_manip_type type; | ||
| 36 | }; | ||
| 37 | |||
| 38 | static void nft_nat_eval(const struct nft_expr *expr, | ||
| 39 | struct nft_data data[NFT_REG_MAX + 1], | ||
| 40 | const struct nft_pktinfo *pkt) | ||
| 41 | { | ||
| 42 | const struct nft_nat *priv = nft_expr_priv(expr); | ||
| 43 | enum ip_conntrack_info ctinfo; | ||
| 44 | struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo); | ||
| 45 | struct nf_nat_range range; | ||
| 46 | |||
| 47 | memset(&range, 0, sizeof(range)); | ||
| 48 | if (priv->sreg_addr_min) { | ||
| 49 | if (priv->family == AF_INET) { | ||
| 50 | range.min_addr.ip = data[priv->sreg_addr_min].data[0]; | ||
| 51 | range.max_addr.ip = data[priv->sreg_addr_max].data[0]; | ||
| 52 | |||
| 53 | } else { | ||
| 54 | memcpy(range.min_addr.ip6, | ||
| 55 | data[priv->sreg_addr_min].data, | ||
| 56 | sizeof(struct nft_data)); | ||
| 57 | memcpy(range.max_addr.ip6, | ||
| 58 | data[priv->sreg_addr_max].data, | ||
| 59 | sizeof(struct nft_data)); | ||
| 60 | } | ||
| 61 | range.flags |= NF_NAT_RANGE_MAP_IPS; | ||
| 62 | } | ||
| 63 | |||
| 64 | if (priv->sreg_proto_min) { | ||
| 65 | range.min_proto.all = data[priv->sreg_proto_min].data[0]; | ||
| 66 | range.max_proto.all = data[priv->sreg_proto_max].data[0]; | ||
| 67 | range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; | ||
| 68 | } | ||
| 69 | |||
| 70 | data[NFT_REG_VERDICT].verdict = | ||
| 71 | nf_nat_setup_info(ct, &range, priv->type); | ||
| 72 | } | ||
| 73 | |||
| 74 | static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = { | ||
| 75 | [NFTA_NAT_TYPE] = { .type = NLA_U32 }, | ||
| 76 | [NFTA_NAT_FAMILY] = { .type = NLA_U32 }, | ||
| 77 | [NFTA_NAT_REG_ADDR_MIN] = { .type = NLA_U32 }, | ||
| 78 | [NFTA_NAT_REG_ADDR_MAX] = { .type = NLA_U32 }, | ||
| 79 | [NFTA_NAT_REG_PROTO_MIN] = { .type = NLA_U32 }, | ||
| 80 | [NFTA_NAT_REG_PROTO_MAX] = { .type = NLA_U32 }, | ||
| 81 | }; | ||
| 82 | |||
| 83 | static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, | ||
| 84 | const struct nlattr * const tb[]) | ||
| 85 | { | ||
| 86 | struct nft_nat *priv = nft_expr_priv(expr); | ||
| 87 | int err; | ||
| 88 | |||
| 89 | if (tb[NFTA_NAT_TYPE] == NULL) | ||
| 90 | return -EINVAL; | ||
| 91 | |||
| 92 | switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) { | ||
| 93 | case NFT_NAT_SNAT: | ||
| 94 | priv->type = NF_NAT_MANIP_SRC; | ||
| 95 | break; | ||
| 96 | case NFT_NAT_DNAT: | ||
| 97 | priv->type = NF_NAT_MANIP_DST; | ||
| 98 | break; | ||
| 99 | default: | ||
| 100 | return -EINVAL; | ||
| 101 | } | ||
| 102 | |||
| 103 | if (tb[NFTA_NAT_FAMILY] == NULL) | ||
| 104 | return -EINVAL; | ||
| 105 | |||
| 106 | priv->family = ntohl(nla_get_be32(tb[NFTA_NAT_FAMILY])); | ||
| 107 | if (priv->family != AF_INET && priv->family != AF_INET6) | ||
| 108 | return -EINVAL; | ||
| 109 | |||
| 110 | if (tb[NFTA_NAT_REG_ADDR_MIN]) { | ||
| 111 | priv->sreg_addr_min = ntohl(nla_get_be32( | ||
| 112 | tb[NFTA_NAT_REG_ADDR_MIN])); | ||
| 113 | err = nft_validate_input_register(priv->sreg_addr_min); | ||
| 114 | if (err < 0) | ||
| 115 | return err; | ||
| 116 | } | ||
| 117 | |||
| 118 | if (tb[NFTA_NAT_REG_ADDR_MAX]) { | ||
| 119 | priv->sreg_addr_max = ntohl(nla_get_be32( | ||
| 120 | tb[NFTA_NAT_REG_ADDR_MAX])); | ||
| 121 | err = nft_validate_input_register(priv->sreg_addr_max); | ||
| 122 | if (err < 0) | ||
| 123 | return err; | ||
| 124 | } else | ||
| 125 | priv->sreg_addr_max = priv->sreg_addr_min; | ||
| 126 | |||
| 127 | if (tb[NFTA_NAT_REG_PROTO_MIN]) { | ||
| 128 | priv->sreg_proto_min = ntohl(nla_get_be32( | ||
| 129 | tb[NFTA_NAT_REG_PROTO_MIN])); | ||
| 130 | err = nft_validate_input_register(priv->sreg_proto_min); | ||
| 131 | if (err < 0) | ||
| 132 | return err; | ||
| 133 | } | ||
| 134 | |||
| 135 | if (tb[NFTA_NAT_REG_PROTO_MAX]) { | ||
| 136 | priv->sreg_proto_max = ntohl(nla_get_be32( | ||
| 137 | tb[NFTA_NAT_REG_PROTO_MAX])); | ||
| 138 | err = nft_validate_input_register(priv->sreg_proto_max); | ||
| 139 | if (err < 0) | ||
| 140 | return err; | ||
| 141 | } else | ||
| 142 | priv->sreg_proto_max = priv->sreg_proto_min; | ||
| 143 | |||
| 144 | return 0; | ||
| 145 | } | ||
| 146 | |||
| 147 | static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr) | ||
| 148 | { | ||
| 149 | const struct nft_nat *priv = nft_expr_priv(expr); | ||
| 150 | |||
| 151 | switch (priv->type) { | ||
| 152 | case NF_NAT_MANIP_SRC: | ||
| 153 | if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT))) | ||
| 154 | goto nla_put_failure; | ||
| 155 | break; | ||
| 156 | case NF_NAT_MANIP_DST: | ||
| 157 | if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT))) | ||
| 158 | goto nla_put_failure; | ||
| 159 | break; | ||
| 160 | } | ||
| 161 | |||
| 162 | if (nla_put_be32(skb, NFTA_NAT_FAMILY, htonl(priv->family))) | ||
| 163 | goto nla_put_failure; | ||
| 164 | if (nla_put_be32(skb, | ||
| 165 | NFTA_NAT_REG_ADDR_MIN, htonl(priv->sreg_addr_min))) | ||
| 166 | goto nla_put_failure; | ||
| 167 | if (nla_put_be32(skb, | ||
| 168 | NFTA_NAT_REG_ADDR_MAX, htonl(priv->sreg_addr_max))) | ||
| 169 | goto nla_put_failure; | ||
| 170 | if (nla_put_be32(skb, | ||
| 171 | NFTA_NAT_REG_PROTO_MIN, htonl(priv->sreg_proto_min))) | ||
| 172 | goto nla_put_failure; | ||
| 173 | if (nla_put_be32(skb, | ||
| 174 | NFTA_NAT_REG_PROTO_MAX, htonl(priv->sreg_proto_max))) | ||
| 175 | goto nla_put_failure; | ||
| 176 | return 0; | ||
| 177 | |||
| 178 | nla_put_failure: | ||
| 179 | return -1; | ||
| 180 | } | ||
| 181 | |||
| 182 | static struct nft_expr_type nft_nat_type; | ||
| 183 | static const struct nft_expr_ops nft_nat_ops = { | ||
| 184 | .type = &nft_nat_type, | ||
| 185 | .size = NFT_EXPR_SIZE(sizeof(struct nft_nat)), | ||
| 186 | .eval = nft_nat_eval, | ||
| 187 | .init = nft_nat_init, | ||
| 188 | .dump = nft_nat_dump, | ||
| 189 | }; | ||
| 190 | |||
| 191 | static struct nft_expr_type nft_nat_type __read_mostly = { | ||
| 192 | .name = "nat", | ||
| 193 | .ops = &nft_nat_ops, | ||
| 194 | .policy = nft_nat_policy, | ||
| 195 | .maxattr = NFTA_NAT_MAX, | ||
| 196 | .owner = THIS_MODULE, | ||
| 197 | }; | ||
| 198 | |||
| 199 | static int __init nft_nat_module_init(void) | ||
| 200 | { | ||
| 201 | int err; | ||
| 202 | |||
| 203 | err = nft_register_expr(&nft_nat_type); | ||
| 204 | if (err < 0) | ||
| 205 | return err; | ||
| 206 | |||
| 207 | return 0; | ||
| 208 | } | ||
| 209 | |||
| 210 | static void __exit nft_nat_module_exit(void) | ||
| 211 | { | ||
| 212 | nft_unregister_expr(&nft_nat_type); | ||
| 213 | } | ||
| 214 | |||
| 215 | module_init(nft_nat_module_init); | ||
| 216 | module_exit(nft_nat_module_exit); | ||
| 217 | |||
| 218 | MODULE_LICENSE("GPL"); | ||
| 219 | MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>"); | ||
| 220 | MODULE_ALIAS_NFT_EXPR("nat"); | ||
