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
Unified · Split
| @@ -695,18 +695,20 @@ enum nft_nat_types { · 694 unmodified lines | ||
| 695 | 695 | * enum nft_nat_attributes - nf_tables nat expression netlink attributes |
| 696 | 696 | * |
| 697 | 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) | |
| 699 | − * @NFTA_NAT_ADDR_MAX: source register of address range end (NLA_U32: nft_registers) | |
| 700 | − * @NFTA_NAT_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers) | |
| 701 | − * @NFTA_NAT_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers) | |
| 698 | + * @NFTA_NAT_FAMILY: NAT family (NLA_U32) | |
| 699 | + * @NFTA_NAT_REG_ADDR_MIN: source register of address 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_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 | 704 | enum nft_nat_attributes { |
| 704 | 705 | NFTA_NAT_UNSPEC, |
| 705 | 706 | NFTA_NAT_TYPE, |
| 706 | − NFTA_NAT_ADDR_MIN, | |
| 707 | − NFTA_NAT_ADDR_MAX, | |
| 708 | − NFTA_NAT_PROTO_MIN, | |
| 709 | − NFTA_NAT_PROTO_MAX, | |
| 707 | + NFTA_NAT_FAMILY, | |
| 708 | + NFTA_NAT_REG_ADDR_MIN, | |
| 709 | + NFTA_NAT_REG_ADDR_MAX, | |
| 710 | + NFTA_NAT_REG_PROTO_MIN, | |
| 711 | + NFTA_NAT_REG_PROTO_MAX, | |
| 710 | 712 | __NFTA_NAT_MAX |
| 711 | 713 | }; |
| 712 | 714 | #define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1) |
| @@ -50,6 +50,7 @@ config NFT_CHAIN_ROUTE_IPV4 · 49 unmodified lines | ||
| 50 | 50 | |
| 51 | 51 | config NFT_CHAIN_NAT_IPV4 |
| 52 | 52 | depends on NF_TABLES_IPV4 |
| 53 | + depends on NF_NAT_IPV4 && NFT_NAT | |
| 53 | 54 | tristate "IPv4 nf_tables nat chain support" |
| 54 | 55 | |
| 55 | 56 | config IP_NF_IPTABLES |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | /* |
| 2 | 2 | * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> |
| 3 | 3 | * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org> |
| 4 | + * Copyright (c) 2012 Intel Corporation | |
| 4 | 5 | * |
| 5 | 6 | * This program is free software; you can redistribute it and/or modify |
| 6 | 7 | * it under the terms of the GNU General Public License version 2 as |
| @@ -14,10 +15,8 @@ · 7 unmodified lines | ||
| 14 | 15 | #include <linux/list.h> |
| 15 | 16 | #include <linux/skbuff.h> |
| 16 | 17 | #include <linux/ip.h> |
| 17 | −#include <linux/netlink.h> | |
| 18 | 18 | #include <linux/netfilter.h> |
| 19 | 19 | #include <linux/netfilter_ipv4.h> |
| 20 | −#include <linux/netfilter/nfnetlink.h> | |
| 21 | 20 | #include <linux/netfilter/nf_tables.h> |
| 22 | 21 | #include <net/netfilter/nf_conntrack.h> |
| 23 | 22 | #include <net/netfilter/nf_nat.h> |
| @@ -27,147 +26,6 @@ · 3 unmodified lines | ||
| 27 | 26 | #include <net/netfilter/nf_nat_l3proto.h> |
| 28 | 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 | 30 | * NAT chains |
| 173 | 31 | */ |
| @@ -306,7 +164,7 @@ static unsigned int nf_nat_output(const struct nf_hook_ops *ops, · 132 unmodified lines | ||
| 306 | 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 | 168 | .family = NFPROTO_IPV4, |
| 311 | 169 | .name = "nat", |
| 312 | 170 | .type = NFT_CHAIN_T_NAT, |
| @@ -331,20 +189,11 @@ static int __init nft_chain_nat_init(void) · 18 unmodified lines | ||
| 331 | 189 | if (err < 0) |
| 332 | 190 | return err; |
| 333 | 191 | |
| 334 | − err = nft_register_expr(&nft_nat_type); | |
| 335 | − if (err < 0) | |
| 336 | − goto err; | |
| 337 | − | |
| 338 | 192 | return 0; |
| 339 | − | |
| 340 | −err: | |
| 341 | − nft_unregister_chain_type(&nft_chain_nat_ipv4); | |
| 342 | − return err; | |
| 343 | 193 | } |
| 344 | 194 | |
| 345 | 195 | static void __exit nft_chain_nat_exit(void) |
| 346 | 196 | { |
| 347 | − nft_unregister_expr(&nft_nat_type); | |
| 348 | 197 | nft_unregister_chain_type(&nft_chain_nat_ipv4); |
| 349 | 198 | } |
| 350 | 199 | |
| @@ -354,4 +203,3 @@ module_exit(nft_chain_nat_exit); · 3 unmodified lines | ||
| 354 | 203 | MODULE_LICENSE("GPL"); |
| 355 | 204 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); |
| 356 | 205 | MODULE_ALIAS_NFT_CHAIN(AF_INET, "nat"); |
| 357 | −MODULE_ALIAS_NFT_EXPR("nat"); | |
| @@ -33,6 +33,11 @@ config NFT_CHAIN_ROUTE_IPV6 · 32 unmodified lines | ||
| 33 | 33 | depends on NF_TABLES_IPV6 |
| 34 | 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 | 41 | config IP6_NF_IPTABLES |
| 37 | 42 | tristate "IP6 tables support (required for filtering)" |
| 38 | 43 | depends on INET && IPV6 |
| @@ -26,6 +26,7 @@ obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o · 25 unmodified lines | ||
| 26 | 26 | # nf_tables |
| 27 | 27 | obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o |
| 28 | 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 | 31 | # matches |
| 31 | 32 | obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o |
| @@ -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"); | |
| @@ -450,6 +450,12 @@ config NFT_LIMIT · 449 unmodified lines | ||
| 450 | 450 | depends on NF_TABLES |
| 451 | 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 | 459 | config NFT_COMPAT |
| 454 | 460 | depends on NF_TABLES |
| 455 | 461 | depends on NETFILTER_XTABLES |
| @@ -75,6 +75,7 @@ obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o · 74 unmodified lines | ||
| 75 | 75 | obj-$(CONFIG_NFT_META) += nft_meta.o |
| 76 | 76 | obj-$(CONFIG_NFT_CT) += nft_ct.o |
| 77 | 77 | obj-$(CONFIG_NFT_LIMIT) += nft_limit.o |
| 78 | +obj-$(CONFIG_NFT_NAT) += nft_nat.o | |
| 78 | 79 | #nf_tables-objs += nft_meta_target.o |
| 79 | 80 | obj-$(CONFIG_NFT_RBTREE) += nft_rbtree.o |
| 80 | 81 | obj-$(CONFIG_NFT_HASH) += nft_hash.o |
| @@ -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"); | |