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

netfilter: nf_nat: generalize IPv4 masquerading support for nf_tables

Let's refactor the code so we can reach the masquerade functionality
from outside the xt context (ie. nftables).

The patch includes the addition of an atomic counter to the masquerade
notifier: the stuff to be done by the notifier is the same for xt and
nftables. Therefore, only one notification handler is needed.

This factorization only involves IPv4; a similar patch follows to
handle IPv6.

Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Arturo Borrero and committed by
Pablo Neira Ayuso
8dd33cc9 c55fbbb4

+184 -99
+14
include/net/netfilter/ipv4/nf_nat_masquerade.h
··· 1 + #ifndef _NF_NAT_MASQUERADE_IPV4_H_ 2 + #define _NF_NAT_MASQUERADE_IPV4_H_ 3 + 4 + #include <net/netfilter/nf_nat.h> 5 + 6 + unsigned int 7 + nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum, 8 + const struct nf_nat_range *range, 9 + const struct net_device *out); 10 + 11 + void nf_nat_masquerade_ipv4_register_notifier(void); 12 + void nf_nat_masquerade_ipv4_unregister_notifier(void); 13 + 14 + #endif /*_NF_NAT_MASQUERADE_IPV4_H_ */
+7
net/ipv4/netfilter/Kconfig
··· 184 184 185 185 if NF_NAT_IPV4 186 186 187 + config NF_NAT_MASQUERADE_IPV4 188 + tristate "IPv4 masquerade support" 189 + help 190 + This is the kernel functionality to provide NAT in the masquerade 191 + flavour (automatic source address selection). 192 + 187 193 config IP_NF_TARGET_MASQUERADE 188 194 tristate "MASQUERADE target support" 195 + select NF_NAT_MASQUERADE_IPV4 189 196 default m if NETFILTER_ADVANCED=n 190 197 help 191 198 Masquerading is a special case of NAT: all outgoing connections are
+1
net/ipv4/netfilter/Makefile
··· 27 27 obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o 28 28 obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o 29 29 obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o 30 + obj-$(CONFIG_NF_NAT_MASQUERADE_IPV4) += nf_nat_masquerade_ipv4.o 30 31 31 32 # NAT protocols (nf_nat) 32 33 obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o
+9 -99
net/ipv4/netfilter/ipt_MASQUERADE.c
··· 22 22 #include <linux/netfilter_ipv4.h> 23 23 #include <linux/netfilter/x_tables.h> 24 24 #include <net/netfilter/nf_nat.h> 25 + #include <net/netfilter/ipv4/nf_nat_masquerade.h> 25 26 26 27 MODULE_LICENSE("GPL"); 27 28 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); ··· 47 46 static unsigned int 48 47 masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par) 49 48 { 50 - struct nf_conn *ct; 51 - struct nf_conn_nat *nat; 52 - enum ip_conntrack_info ctinfo; 53 - struct nf_nat_range newrange; 49 + struct nf_nat_range range; 54 50 const struct nf_nat_ipv4_multi_range_compat *mr; 55 - const struct rtable *rt; 56 - __be32 newsrc, nh; 57 - 58 - NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING); 59 - 60 - ct = nf_ct_get(skb, &ctinfo); 61 - nat = nfct_nat(ct); 62 - 63 - NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || 64 - ctinfo == IP_CT_RELATED_REPLY)); 65 - 66 - /* Source address is 0.0.0.0 - locally generated packet that is 67 - * probably not supposed to be masqueraded. 68 - */ 69 - if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == 0) 70 - return NF_ACCEPT; 71 51 72 52 mr = par->targinfo; 73 - rt = skb_rtable(skb); 74 - nh = rt_nexthop(rt, ip_hdr(skb)->daddr); 75 - newsrc = inet_select_addr(par->out, nh, RT_SCOPE_UNIVERSE); 76 - if (!newsrc) { 77 - pr_info("%s ate my IP address\n", par->out->name); 78 - return NF_DROP; 79 - } 53 + range.flags = mr->range[0].flags; 54 + range.min_proto = mr->range[0].min; 55 + range.max_proto = mr->range[0].max; 80 56 81 - nat->masq_index = par->out->ifindex; 82 - 83 - /* Transfer from original range. */ 84 - memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); 85 - memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); 86 - newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; 87 - newrange.min_addr.ip = newsrc; 88 - newrange.max_addr.ip = newsrc; 89 - newrange.min_proto = mr->range[0].min; 90 - newrange.max_proto = mr->range[0].max; 91 - 92 - /* Hand modified range to generic setup. */ 93 - return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC); 57 + return nf_nat_masquerade_ipv4(skb, par->hooknum, &range, par->out); 94 58 } 95 - 96 - static int 97 - device_cmp(struct nf_conn *i, void *ifindex) 98 - { 99 - const struct nf_conn_nat *nat = nfct_nat(i); 100 - 101 - if (!nat) 102 - return 0; 103 - if (nf_ct_l3num(i) != NFPROTO_IPV4) 104 - return 0; 105 - return nat->masq_index == (int)(long)ifindex; 106 - } 107 - 108 - static int masq_device_event(struct notifier_block *this, 109 - unsigned long event, 110 - void *ptr) 111 - { 112 - const struct net_device *dev = netdev_notifier_info_to_dev(ptr); 113 - struct net *net = dev_net(dev); 114 - 115 - if (event == NETDEV_DOWN) { 116 - /* Device was downed. Search entire table for 117 - conntracks which were associated with that device, 118 - and forget them. */ 119 - NF_CT_ASSERT(dev->ifindex != 0); 120 - 121 - nf_ct_iterate_cleanup(net, device_cmp, 122 - (void *)(long)dev->ifindex, 0, 0); 123 - } 124 - 125 - return NOTIFY_DONE; 126 - } 127 - 128 - static int masq_inet_event(struct notifier_block *this, 129 - unsigned long event, 130 - void *ptr) 131 - { 132 - struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev; 133 - struct netdev_notifier_info info; 134 - 135 - netdev_notifier_info_init(&info, dev); 136 - return masq_device_event(this, event, &info); 137 - } 138 - 139 - static struct notifier_block masq_dev_notifier = { 140 - .notifier_call = masq_device_event, 141 - }; 142 - 143 - static struct notifier_block masq_inet_notifier = { 144 - .notifier_call = masq_inet_event, 145 - }; 146 59 147 60 static struct xt_target masquerade_tg_reg __read_mostly = { 148 61 .name = "MASQUERADE", ··· 75 160 76 161 ret = xt_register_target(&masquerade_tg_reg); 77 162 78 - if (ret == 0) { 79 - /* Register for device down reports */ 80 - register_netdevice_notifier(&masq_dev_notifier); 81 - /* Register IP address change reports */ 82 - register_inetaddr_notifier(&masq_inet_notifier); 83 - } 163 + if (ret == 0) 164 + nf_nat_masquerade_ipv4_register_notifier(); 84 165 85 166 return ret; 86 167 } ··· 84 173 static void __exit masquerade_tg_exit(void) 85 174 { 86 175 xt_unregister_target(&masquerade_tg_reg); 87 - unregister_netdevice_notifier(&masq_dev_notifier); 88 - unregister_inetaddr_notifier(&masq_inet_notifier); 176 + nf_nat_masquerade_ipv4_unregister_notifier(); 89 177 } 90 178 91 179 module_init(masquerade_tg_init);
+153
net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
··· 1 + /* (C) 1999-2001 Paul `Rusty' Russell 2 + * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + */ 8 + 9 + #include <linux/types.h> 10 + #include <linux/module.h> 11 + #include <linux/atomic.h> 12 + #include <linux/inetdevice.h> 13 + #include <linux/ip.h> 14 + #include <linux/timer.h> 15 + #include <linux/netfilter.h> 16 + #include <net/protocol.h> 17 + #include <net/ip.h> 18 + #include <net/checksum.h> 19 + #include <net/route.h> 20 + #include <linux/netfilter_ipv4.h> 21 + #include <linux/netfilter/x_tables.h> 22 + #include <net/netfilter/nf_nat.h> 23 + #include <net/netfilter/ipv4/nf_nat_masquerade.h> 24 + 25 + unsigned int 26 + nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum, 27 + const struct nf_nat_range *range, 28 + const struct net_device *out) 29 + { 30 + struct nf_conn *ct; 31 + struct nf_conn_nat *nat; 32 + enum ip_conntrack_info ctinfo; 33 + struct nf_nat_range newrange; 34 + const struct rtable *rt; 35 + __be32 newsrc, nh; 36 + 37 + NF_CT_ASSERT(hooknum == NF_INET_POST_ROUTING); 38 + 39 + ct = nf_ct_get(skb, &ctinfo); 40 + nat = nfct_nat(ct); 41 + 42 + NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || 43 + ctinfo == IP_CT_RELATED_REPLY)); 44 + 45 + /* Source address is 0.0.0.0 - locally generated packet that is 46 + * probably not supposed to be masqueraded. 47 + */ 48 + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == 0) 49 + return NF_ACCEPT; 50 + 51 + rt = skb_rtable(skb); 52 + nh = rt_nexthop(rt, ip_hdr(skb)->daddr); 53 + newsrc = inet_select_addr(out, nh, RT_SCOPE_UNIVERSE); 54 + if (!newsrc) { 55 + pr_info("%s ate my IP address\n", out->name); 56 + return NF_DROP; 57 + } 58 + 59 + nat->masq_index = out->ifindex; 60 + 61 + /* Transfer from original range. */ 62 + memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); 63 + memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); 64 + newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; 65 + newrange.min_addr.ip = newsrc; 66 + newrange.max_addr.ip = newsrc; 67 + newrange.min_proto = range->min_proto; 68 + newrange.max_proto = range->max_proto; 69 + 70 + /* Hand modified range to generic setup. */ 71 + return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC); 72 + } 73 + EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4); 74 + 75 + static int device_cmp(struct nf_conn *i, void *ifindex) 76 + { 77 + const struct nf_conn_nat *nat = nfct_nat(i); 78 + 79 + if (!nat) 80 + return 0; 81 + if (nf_ct_l3num(i) != NFPROTO_IPV4) 82 + return 0; 83 + return nat->masq_index == (int)(long)ifindex; 84 + } 85 + 86 + static int masq_device_event(struct notifier_block *this, 87 + unsigned long event, 88 + void *ptr) 89 + { 90 + const struct net_device *dev = netdev_notifier_info_to_dev(ptr); 91 + struct net *net = dev_net(dev); 92 + 93 + if (event == NETDEV_DOWN) { 94 + /* Device was downed. Search entire table for 95 + * conntracks which were associated with that device, 96 + * and forget them. 97 + */ 98 + NF_CT_ASSERT(dev->ifindex != 0); 99 + 100 + nf_ct_iterate_cleanup(net, device_cmp, 101 + (void *)(long)dev->ifindex, 0, 0); 102 + } 103 + 104 + return NOTIFY_DONE; 105 + } 106 + 107 + static int masq_inet_event(struct notifier_block *this, 108 + unsigned long event, 109 + void *ptr) 110 + { 111 + struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev; 112 + struct netdev_notifier_info info; 113 + 114 + netdev_notifier_info_init(&info, dev); 115 + return masq_device_event(this, event, &info); 116 + } 117 + 118 + static struct notifier_block masq_dev_notifier = { 119 + .notifier_call = masq_device_event, 120 + }; 121 + 122 + static struct notifier_block masq_inet_notifier = { 123 + .notifier_call = masq_inet_event, 124 + }; 125 + 126 + static atomic_t masquerade_notifier_refcount = ATOMIC_INIT(0); 127 + 128 + void nf_nat_masquerade_ipv4_register_notifier(void) 129 + { 130 + /* check if the notifier was already set */ 131 + if (atomic_inc_return(&masquerade_notifier_refcount) > 1) 132 + return; 133 + 134 + /* Register for device down reports */ 135 + register_netdevice_notifier(&masq_dev_notifier); 136 + /* Register IP address change reports */ 137 + register_inetaddr_notifier(&masq_inet_notifier); 138 + } 139 + EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_register_notifier); 140 + 141 + void nf_nat_masquerade_ipv4_unregister_notifier(void) 142 + { 143 + /* check if the notifier still has clients */ 144 + if (atomic_dec_return(&masquerade_notifier_refcount) > 0) 145 + return; 146 + 147 + unregister_netdevice_notifier(&masq_dev_notifier); 148 + unregister_inetaddr_notifier(&masq_inet_notifier); 149 + } 150 + EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_unregister_notifier); 151 + 152 + MODULE_LICENSE("GPL"); 153 + MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");