Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tomasz Bursztyka and committed by
Pablo Neira Ayuso
eb31628e 9ddf6323

+457 -162
+10 -8
include/uapi/linux/netfilter/nf_tables.h
··· 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)
+1
net/ipv4/netfilter/Kconfig
··· 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
+2 -154
net/ipv4/netfilter/nft_chain_nat_ipv4.c
··· 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 ··· 15 14 #include <linux/list.h> 16 15 #include <linux/skbuff.h> 17 16 #include <linux/ip.h> 18 - #include <linux/netlink.h> 19 17 #include <linux/netfilter.h> 20 18 #include <linux/netfilter_ipv4.h> 21 - #include <linux/netfilter/nfnetlink.h> 22 19 #include <linux/netfilter/nf_tables.h> 23 20 #include <net/netfilter/nf_conntrack.h> 24 21 #include <net/netfilter/nf_nat.h> ··· 25 26 #include <net/netfilter/nf_tables_ipv4.h> 26 27 #include <net/netfilter/nf_nat_l3proto.h> 27 28 #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 - enum nf_nat_manip_type type; 35 - }; 36 - 37 - static void nft_nat_eval(const struct nft_expr *expr, 38 - struct nft_data data[NFT_REG_MAX + 1], 39 - const struct nft_pktinfo *pkt) 40 - { 41 - const struct nft_nat *priv = nft_expr_priv(expr); 42 - enum ip_conntrack_info ctinfo; 43 - struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo); 44 - struct nf_nat_range range; 45 - 46 - memset(&range, 0, sizeof(range)); 47 - if (priv->sreg_addr_min) { 48 - range.min_addr.ip = data[priv->sreg_addr_min].data[0]; 49 - range.max_addr.ip = data[priv->sreg_addr_max].data[0]; 50 - range.flags |= NF_NAT_RANGE_MAP_IPS; 51 - } 52 - 53 - if (priv->sreg_proto_min) { 54 - range.min_proto.all = data[priv->sreg_proto_min].data[0]; 55 - range.max_proto.all = data[priv->sreg_proto_max].data[0]; 56 - range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; 57 - } 58 - 59 - data[NFT_REG_VERDICT].verdict = 60 - nf_nat_setup_info(ct, &range, priv->type); 61 - } 62 - 63 - static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = { 64 - [NFTA_NAT_ADDR_MIN] = { .type = NLA_U32 }, 65 - [NFTA_NAT_ADDR_MAX] = { .type = NLA_U32 }, 66 - [NFTA_NAT_PROTO_MIN] = { .type = NLA_U32 }, 67 - [NFTA_NAT_PROTO_MAX] = { .type = NLA_U32 }, 68 - [NFTA_NAT_TYPE] = { .type = NLA_U32 }, 69 - }; 70 - 71 - static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, 72 - const struct nlattr * const tb[]) 73 - { 74 - struct nft_nat *priv = nft_expr_priv(expr); 75 - int err; 76 - 77 - if (tb[NFTA_NAT_TYPE] == NULL) 78 - return -EINVAL; 79 - 80 - switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) { 81 - case NFT_NAT_SNAT: 82 - priv->type = NF_NAT_MANIP_SRC; 83 - break; 84 - case NFT_NAT_DNAT: 85 - priv->type = NF_NAT_MANIP_DST; 86 - break; 87 - default: 88 - return -EINVAL; 89 - } 90 - 91 - if (tb[NFTA_NAT_ADDR_MIN]) { 92 - priv->sreg_addr_min = ntohl(nla_get_be32(tb[NFTA_NAT_ADDR_MIN])); 93 - err = nft_validate_input_register(priv->sreg_addr_min); 94 - if (err < 0) 95 - return err; 96 - } 97 - 98 - if (tb[NFTA_NAT_ADDR_MAX]) { 99 - priv->sreg_addr_max = ntohl(nla_get_be32(tb[NFTA_NAT_ADDR_MAX])); 100 - err = nft_validate_input_register(priv->sreg_addr_max); 101 - if (err < 0) 102 - return err; 103 - } else 104 - priv->sreg_addr_max = priv->sreg_addr_min; 105 - 106 - if (tb[NFTA_NAT_PROTO_MIN]) { 107 - priv->sreg_proto_min = ntohl(nla_get_be32(tb[NFTA_NAT_PROTO_MIN])); 108 - err = nft_validate_input_register(priv->sreg_proto_min); 109 - if (err < 0) 110 - return err; 111 - } 112 - 113 - if (tb[NFTA_NAT_PROTO_MAX]) { 114 - priv->sreg_proto_max = ntohl(nla_get_be32(tb[NFTA_NAT_PROTO_MAX])); 115 - err = nft_validate_input_register(priv->sreg_proto_max); 116 - if (err < 0) 117 - return err; 118 - } else 119 - priv->sreg_proto_max = priv->sreg_proto_min; 120 - 121 - return 0; 122 - } 123 - 124 - static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr) 125 - { 126 - const struct nft_nat *priv = nft_expr_priv(expr); 127 - 128 - switch (priv->type) { 129 - case NF_NAT_MANIP_SRC: 130 - if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT))) 131 - goto nla_put_failure; 132 - break; 133 - case NF_NAT_MANIP_DST: 134 - if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT))) 135 - goto nla_put_failure; 136 - break; 137 - } 138 - 139 - if (nla_put_be32(skb, NFTA_NAT_ADDR_MIN, htonl(priv->sreg_addr_min))) 140 - goto nla_put_failure; 141 - if (nla_put_be32(skb, NFTA_NAT_ADDR_MAX, htonl(priv->sreg_addr_max))) 142 - goto nla_put_failure; 143 - if (nla_put_be32(skb, NFTA_NAT_PROTO_MIN, htonl(priv->sreg_proto_min))) 144 - goto nla_put_failure; 145 - if (nla_put_be32(skb, NFTA_NAT_PROTO_MAX, htonl(priv->sreg_proto_max))) 146 - goto nla_put_failure; 147 - return 0; 148 - 149 - nla_put_failure: 150 - return -1; 151 - } 152 - 153 - static struct nft_expr_type nft_nat_type; 154 - static const struct nft_expr_ops nft_nat_ops = { 155 - .type = &nft_nat_type, 156 - .size = NFT_EXPR_SIZE(sizeof(struct nft_nat)), 157 - .eval = nft_nat_eval, 158 - .init = nft_nat_init, 159 - .dump = nft_nat_dump, 160 - }; 161 - 162 - static struct nft_expr_type nft_nat_type __read_mostly = { 163 - .name = "nat", 164 - .ops = &nft_nat_ops, 165 - .policy = nft_nat_policy, 166 - .maxattr = NFTA_NAT_MAX, 167 - .owner = THIS_MODULE, 168 - }; 169 29 170 30 /* 171 31 * NAT chains ··· 164 306 return ret; 165 307 } 166 308 167 - struct nf_chain_type nft_chain_nat_ipv4 = { 309 + static struct nf_chain_type nft_chain_nat_ipv4 = { 168 310 .family = NFPROTO_IPV4, 169 311 .name = "nat", 170 312 .type = NFT_CHAIN_T_NAT, ··· 189 331 if (err < 0) 190 332 return err; 191 333 192 - err = nft_register_expr(&nft_nat_type); 193 - if (err < 0) 194 - goto err; 195 - 196 334 return 0; 197 - 198 - err: 199 - nft_unregister_chain_type(&nft_chain_nat_ipv4); 200 - return err; 201 335 } 202 336 203 337 static void __exit nft_chain_nat_exit(void) 204 338 { 205 - nft_unregister_expr(&nft_nat_type); 206 339 nft_unregister_chain_type(&nft_chain_nat_ipv4); 207 340 } 208 341 ··· 203 354 MODULE_LICENSE("GPL"); 204 355 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 205 356 MODULE_ALIAS_NFT_CHAIN(AF_INET, "nat"); 206 - MODULE_ALIAS_NFT_EXPR("nat");
+5
net/ipv6/netfilter/Kconfig
··· 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
+1
net/ipv6/netfilter/Makefile
··· 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
+211
net/ipv6/netfilter/nft_chain_nat_ipv6.c
··· 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");
+6
net/netfilter/Kconfig
··· 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
+1
net/netfilter/Makefile
··· 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
+220
net/netfilter/nft_nat.c
··· 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");