netfilter: nat: merge ipv4 and ipv6 masquerade functionality
Before: text data bss dec hex filename 13916 1412 4128 19456 4c00 nf_nat.ko 4510 968 4 5482 156a nf_nat_ipv4.ko 5146 944 8 6098 17d2 nf_nat_ipv6.ko After: text data bss dec hex filename 16566 1576 4136 22278 5706 nf_nat.ko 3187 844 0 4031 fbf nf_nat_ipv4.ko 3598 844 0 4442 115a nf_nat_ipv6.ko ... so no drastic changes in combined size. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
d824548dae
commit
d1aca8ab31
|
@ -31,8 +31,7 @@ struct nf_conn;
|
||||||
/* The structure embedded in the conntrack structure. */
|
/* The structure embedded in the conntrack structure. */
|
||||||
struct nf_conn_nat {
|
struct nf_conn_nat {
|
||||||
union nf_conntrack_nat_help help;
|
union nf_conntrack_nat_help help;
|
||||||
#if IS_ENABLED(CONFIG_NF_NAT_MASQUERADE_IPV4) || \
|
#if IS_ENABLED(CONFIG_NF_NAT_MASQUERADE)
|
||||||
IS_ENABLED(CONFIG_NF_NAT_MASQUERADE_IPV6)
|
|
||||||
int masq_index;
|
int masq_index;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@ -61,8 +60,7 @@ static inline bool nf_nat_oif_changed(unsigned int hooknum,
|
||||||
struct nf_conn_nat *nat,
|
struct nf_conn_nat *nat,
|
||||||
const struct net_device *out)
|
const struct net_device *out)
|
||||||
{
|
{
|
||||||
#if IS_ENABLED(CONFIG_NF_NAT_MASQUERADE_IPV4) || \
|
#if IS_ENABLED(CONFIG_NF_NAT_MASQUERADE)
|
||||||
IS_ENABLED(CONFIG_NF_NAT_MASQUERADE_IPV6)
|
|
||||||
return nat && nat->masq_index && hooknum == NF_INET_POST_ROUTING &&
|
return nat && nat->masq_index && hooknum == NF_INET_POST_ROUTING &&
|
||||||
CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL &&
|
CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL &&
|
||||||
nat->masq_index != out->ifindex;
|
nat->masq_index != out->ifindex;
|
||||||
|
|
|
@ -106,9 +106,6 @@ config NF_NAT_IPV4
|
||||||
|
|
||||||
if NF_NAT_IPV4
|
if NF_NAT_IPV4
|
||||||
|
|
||||||
config NF_NAT_MASQUERADE_IPV4
|
|
||||||
bool
|
|
||||||
|
|
||||||
if NF_TABLES
|
if NF_TABLES
|
||||||
config NFT_CHAIN_NAT_IPV4
|
config NFT_CHAIN_NAT_IPV4
|
||||||
depends on NF_TABLES_IPV4
|
depends on NF_TABLES_IPV4
|
||||||
|
@ -123,7 +120,7 @@ config NFT_MASQ_IPV4
|
||||||
tristate "IPv4 masquerading support for nf_tables"
|
tristate "IPv4 masquerading support for nf_tables"
|
||||||
depends on NF_TABLES_IPV4
|
depends on NF_TABLES_IPV4
|
||||||
depends on NFT_MASQ
|
depends on NFT_MASQ
|
||||||
select NF_NAT_MASQUERADE_IPV4
|
select NF_NAT_MASQUERADE
|
||||||
help
|
help
|
||||||
This is the expression that provides IPv4 masquerading support for
|
This is the expression that provides IPv4 masquerading support for
|
||||||
nf_tables.
|
nf_tables.
|
||||||
|
@ -276,7 +273,7 @@ if IP_NF_NAT
|
||||||
|
|
||||||
config IP_NF_TARGET_MASQUERADE
|
config IP_NF_TARGET_MASQUERADE
|
||||||
tristate "MASQUERADE target support"
|
tristate "MASQUERADE target support"
|
||||||
select NF_NAT_MASQUERADE_IPV4
|
select NF_NAT_MASQUERADE
|
||||||
default m if NETFILTER_ADVANCED=n
|
default m if NETFILTER_ADVANCED=n
|
||||||
help
|
help
|
||||||
Masquerading is a special case of NAT: all outgoing connections are
|
Masquerading is a special case of NAT: all outgoing connections are
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
nf_nat_ipv4-y := nf_nat_l3proto_ipv4.o
|
nf_nat_ipv4-y := nf_nat_l3proto_ipv4.o
|
||||||
nf_nat_ipv4-$(CONFIG_NF_NAT_MASQUERADE_IPV4) += nf_nat_masquerade_ipv4.o
|
|
||||||
obj-$(CONFIG_NF_NAT_IPV4) += nf_nat_ipv4.o
|
obj-$(CONFIG_NF_NAT_IPV4) += nf_nat_ipv4.o
|
||||||
|
|
||||||
# defrag
|
# defrag
|
||||||
|
|
|
@ -44,7 +44,7 @@ config NFT_CHAIN_NAT_IPV6
|
||||||
config NFT_MASQ_IPV6
|
config NFT_MASQ_IPV6
|
||||||
tristate "IPv6 masquerade support for nf_tables"
|
tristate "IPv6 masquerade support for nf_tables"
|
||||||
depends on NFT_MASQ
|
depends on NFT_MASQ
|
||||||
select NF_NAT_MASQUERADE_IPV6
|
select NF_NAT_MASQUERADE
|
||||||
help
|
help
|
||||||
This is the expression that provides IPv4 masquerading support for
|
This is the expression that provides IPv4 masquerading support for
|
||||||
nf_tables.
|
nf_tables.
|
||||||
|
@ -116,13 +116,6 @@ config NF_NAT_IPV6
|
||||||
forms of full Network Address Port Translation. This can be
|
forms of full Network Address Port Translation. This can be
|
||||||
controlled by iptables or nft.
|
controlled by iptables or nft.
|
||||||
|
|
||||||
if NF_NAT_IPV6
|
|
||||||
|
|
||||||
config NF_NAT_MASQUERADE_IPV6
|
|
||||||
bool
|
|
||||||
|
|
||||||
endif # NF_NAT_IPV6
|
|
||||||
|
|
||||||
config IP6_NF_IPTABLES
|
config IP6_NF_IPTABLES
|
||||||
tristate "IP6 tables support (required for filtering)"
|
tristate "IP6 tables support (required for filtering)"
|
||||||
depends on INET && IPV6
|
depends on INET && IPV6
|
||||||
|
@ -324,7 +317,7 @@ if IP6_NF_NAT
|
||||||
|
|
||||||
config IP6_NF_TARGET_MASQUERADE
|
config IP6_NF_TARGET_MASQUERADE
|
||||||
tristate "MASQUERADE target support"
|
tristate "MASQUERADE target support"
|
||||||
select NF_NAT_MASQUERADE_IPV6
|
select NF_NAT_MASQUERADE
|
||||||
help
|
help
|
||||||
Masquerading is a special case of NAT: all outgoing connections are
|
Masquerading is a special case of NAT: all outgoing connections are
|
||||||
changed to seem to come from a particular interface's address, and
|
changed to seem to come from a particular interface's address, and
|
||||||
|
|
|
@ -12,7 +12,6 @@ obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o
|
||||||
obj-$(CONFIG_IP6_NF_NAT) += ip6table_nat.o
|
obj-$(CONFIG_IP6_NF_NAT) += ip6table_nat.o
|
||||||
|
|
||||||
nf_nat_ipv6-y := nf_nat_l3proto_ipv6.o
|
nf_nat_ipv6-y := nf_nat_l3proto_ipv6.o
|
||||||
nf_nat_ipv6-$(CONFIG_NF_NAT_MASQUERADE_IPV6) += nf_nat_masquerade_ipv6.o
|
|
||||||
obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o
|
obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o
|
||||||
|
|
||||||
# defrag
|
# defrag
|
||||||
|
|
|
@ -1,240 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* Based on Rusty Russell's IPv6 MASQUERADE target. Development of IPv6
|
|
||||||
* NAT funded by Astaro.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/atomic.h>
|
|
||||||
#include <linux/netdevice.h>
|
|
||||||
#include <linux/ipv6.h>
|
|
||||||
#include <linux/netfilter.h>
|
|
||||||
#include <linux/netfilter_ipv6.h>
|
|
||||||
#include <net/netfilter/nf_nat.h>
|
|
||||||
#include <net/addrconf.h>
|
|
||||||
#include <net/ipv6.h>
|
|
||||||
#include <net/netfilter/ipv6/nf_nat_masquerade.h>
|
|
||||||
|
|
||||||
#define MAX_WORK_COUNT 16
|
|
||||||
|
|
||||||
static atomic_t v6_worker_count;
|
|
||||||
|
|
||||||
static int
|
|
||||||
nat_ipv6_dev_get_saddr(struct net *net, const struct net_device *dev,
|
|
||||||
const struct in6_addr *daddr, unsigned int srcprefs,
|
|
||||||
struct in6_addr *saddr)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_IPV6_MODULE
|
|
||||||
const struct nf_ipv6_ops *v6_ops = nf_get_ipv6_ops();
|
|
||||||
|
|
||||||
if (!v6_ops)
|
|
||||||
return -EHOSTUNREACH;
|
|
||||||
|
|
||||||
return v6_ops->dev_get_saddr(net, dev, daddr, srcprefs, saddr);
|
|
||||||
#else
|
|
||||||
return ipv6_dev_get_saddr(net, dev, daddr, srcprefs, saddr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int
|
|
||||||
nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range,
|
|
||||||
const struct net_device *out)
|
|
||||||
{
|
|
||||||
enum ip_conntrack_info ctinfo;
|
|
||||||
struct nf_conn_nat *nat;
|
|
||||||
struct in6_addr src;
|
|
||||||
struct nf_conn *ct;
|
|
||||||
struct nf_nat_range2 newrange;
|
|
||||||
|
|
||||||
ct = nf_ct_get(skb, &ctinfo);
|
|
||||||
WARN_ON(!(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
|
|
||||||
ctinfo == IP_CT_RELATED_REPLY)));
|
|
||||||
|
|
||||||
if (nat_ipv6_dev_get_saddr(nf_ct_net(ct), out,
|
|
||||||
&ipv6_hdr(skb)->daddr, 0, &src) < 0)
|
|
||||||
return NF_DROP;
|
|
||||||
|
|
||||||
nat = nf_ct_nat_ext_add(ct);
|
|
||||||
if (nat)
|
|
||||||
nat->masq_index = out->ifindex;
|
|
||||||
|
|
||||||
newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
|
|
||||||
newrange.min_addr.in6 = src;
|
|
||||||
newrange.max_addr.in6 = src;
|
|
||||||
newrange.min_proto = range->min_proto;
|
|
||||||
newrange.max_proto = range->max_proto;
|
|
||||||
|
|
||||||
return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6);
|
|
||||||
|
|
||||||
static int device_cmp(struct nf_conn *ct, void *ifindex)
|
|
||||||
{
|
|
||||||
const struct nf_conn_nat *nat = nfct_nat(ct);
|
|
||||||
|
|
||||||
if (!nat)
|
|
||||||
return 0;
|
|
||||||
if (nf_ct_l3num(ct) != NFPROTO_IPV6)
|
|
||||||
return 0;
|
|
||||||
return nat->masq_index == (int)(long)ifindex;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int masq_device_event(struct notifier_block *this,
|
|
||||||
unsigned long event, void *ptr)
|
|
||||||
{
|
|
||||||
const struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
|
||||||
struct net *net = dev_net(dev);
|
|
||||||
|
|
||||||
if (event == NETDEV_DOWN)
|
|
||||||
nf_ct_iterate_cleanup_net(net, device_cmp,
|
|
||||||
(void *)(long)dev->ifindex, 0, 0);
|
|
||||||
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct notifier_block masq_dev_notifier = {
|
|
||||||
.notifier_call = masq_device_event,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct masq_dev_work {
|
|
||||||
struct work_struct work;
|
|
||||||
struct net *net;
|
|
||||||
struct in6_addr addr;
|
|
||||||
int ifindex;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int inet_cmp(struct nf_conn *ct, void *work)
|
|
||||||
{
|
|
||||||
struct masq_dev_work *w = (struct masq_dev_work *)work;
|
|
||||||
struct nf_conntrack_tuple *tuple;
|
|
||||||
|
|
||||||
if (!device_cmp(ct, (void *)(long)w->ifindex))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
|
|
||||||
|
|
||||||
return ipv6_addr_equal(&w->addr, &tuple->dst.u3.in6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void iterate_cleanup_work(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct masq_dev_work *w;
|
|
||||||
|
|
||||||
w = container_of(work, struct masq_dev_work, work);
|
|
||||||
|
|
||||||
nf_ct_iterate_cleanup_net(w->net, inet_cmp, (void *)w, 0, 0);
|
|
||||||
|
|
||||||
put_net(w->net);
|
|
||||||
kfree(w);
|
|
||||||
atomic_dec(&v6_worker_count);
|
|
||||||
module_put(THIS_MODULE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ipv6 inet notifier is an atomic notifier, i.e. we cannot
|
|
||||||
* schedule.
|
|
||||||
*
|
|
||||||
* Unfortunately, nf_ct_iterate_cleanup_net can run for a long
|
|
||||||
* time if there are lots of conntracks and the system
|
|
||||||
* handles high softirq load, so it frequently calls cond_resched
|
|
||||||
* while iterating the conntrack table.
|
|
||||||
*
|
|
||||||
* So we defer nf_ct_iterate_cleanup_net walk to the system workqueue.
|
|
||||||
*
|
|
||||||
* As we can have 'a lot' of inet_events (depending on amount
|
|
||||||
* of ipv6 addresses being deleted), we also need to add an upper
|
|
||||||
* limit to the number of queued work items.
|
|
||||||
*/
|
|
||||||
static int masq_inet6_event(struct notifier_block *this,
|
|
||||||
unsigned long event, void *ptr)
|
|
||||||
{
|
|
||||||
struct inet6_ifaddr *ifa = ptr;
|
|
||||||
const struct net_device *dev;
|
|
||||||
struct masq_dev_work *w;
|
|
||||||
struct net *net;
|
|
||||||
|
|
||||||
if (event != NETDEV_DOWN ||
|
|
||||||
atomic_read(&v6_worker_count) >= MAX_WORK_COUNT)
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
|
|
||||||
dev = ifa->idev->dev;
|
|
||||||
net = maybe_get_net(dev_net(dev));
|
|
||||||
if (!net)
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
|
|
||||||
if (!try_module_get(THIS_MODULE))
|
|
||||||
goto err_module;
|
|
||||||
|
|
||||||
w = kmalloc(sizeof(*w), GFP_ATOMIC);
|
|
||||||
if (w) {
|
|
||||||
atomic_inc(&v6_worker_count);
|
|
||||||
|
|
||||||
INIT_WORK(&w->work, iterate_cleanup_work);
|
|
||||||
w->ifindex = dev->ifindex;
|
|
||||||
w->net = net;
|
|
||||||
w->addr = ifa->addr;
|
|
||||||
schedule_work(&w->work);
|
|
||||||
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
module_put(THIS_MODULE);
|
|
||||||
err_module:
|
|
||||||
put_net(net);
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct notifier_block masq_inet6_notifier = {
|
|
||||||
.notifier_call = masq_inet6_event,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int masq_refcnt;
|
|
||||||
static DEFINE_MUTEX(masq_mutex);
|
|
||||||
|
|
||||||
int nf_nat_masquerade_ipv6_register_notifier(void)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
mutex_lock(&masq_mutex);
|
|
||||||
/* check if the notifier is already set */
|
|
||||||
if (++masq_refcnt > 1)
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
ret = register_netdevice_notifier(&masq_dev_notifier);
|
|
||||||
if (ret)
|
|
||||||
goto err_dec;
|
|
||||||
|
|
||||||
ret = register_inet6addr_notifier(&masq_inet6_notifier);
|
|
||||||
if (ret)
|
|
||||||
goto err_unregister;
|
|
||||||
|
|
||||||
mutex_unlock(&masq_mutex);
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
err_unregister:
|
|
||||||
unregister_netdevice_notifier(&masq_dev_notifier);
|
|
||||||
err_dec:
|
|
||||||
masq_refcnt--;
|
|
||||||
out_unlock:
|
|
||||||
mutex_unlock(&masq_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_register_notifier);
|
|
||||||
|
|
||||||
void nf_nat_masquerade_ipv6_unregister_notifier(void)
|
|
||||||
{
|
|
||||||
mutex_lock(&masq_mutex);
|
|
||||||
/* check if the notifier still has clients */
|
|
||||||
if (--masq_refcnt > 0)
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
unregister_inet6addr_notifier(&masq_inet6_notifier);
|
|
||||||
unregister_netdevice_notifier(&masq_dev_notifier);
|
|
||||||
out_unlock:
|
|
||||||
mutex_unlock(&masq_mutex);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_unregister_notifier);
|
|
|
@ -431,6 +431,9 @@ config NF_NAT_TFTP
|
||||||
config NF_NAT_REDIRECT
|
config NF_NAT_REDIRECT
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config NF_NAT_MASQUERADE
|
||||||
|
bool
|
||||||
|
|
||||||
config NETFILTER_SYNPROXY
|
config NETFILTER_SYNPROXY
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ obj-$(CONFIG_NF_LOG_NETDEV) += nf_log_netdev.o
|
||||||
|
|
||||||
obj-$(CONFIG_NF_NAT) += nf_nat.o
|
obj-$(CONFIG_NF_NAT) += nf_nat.o
|
||||||
nf_nat-$(CONFIG_NF_NAT_REDIRECT) += nf_nat_redirect.o
|
nf_nat-$(CONFIG_NF_NAT_REDIRECT) += nf_nat_redirect.o
|
||||||
|
nf_nat-$(CONFIG_NF_NAT_MASQUERADE) += nf_nat_masquerade.o
|
||||||
|
|
||||||
# NAT helpers
|
# NAT helpers
|
||||||
obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_amanda.o
|
obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_amanda.o
|
||||||
|
|
|
@ -1,25 +1,17 @@
|
||||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
#include <linux/inetdevice.h>
|
#include <linux/inetdevice.h>
|
||||||
#include <linux/ip.h>
|
|
||||||
#include <linux/timer.h>
|
|
||||||
#include <linux/netfilter.h>
|
#include <linux/netfilter.h>
|
||||||
#include <net/protocol.h>
|
|
||||||
#include <net/ip.h>
|
|
||||||
#include <net/checksum.h>
|
|
||||||
#include <net/route.h>
|
|
||||||
#include <linux/netfilter_ipv4.h>
|
#include <linux/netfilter_ipv4.h>
|
||||||
#include <linux/netfilter/x_tables.h>
|
#include <linux/netfilter_ipv6.h>
|
||||||
#include <net/netfilter/nf_nat.h>
|
|
||||||
#include <net/netfilter/ipv4/nf_nat_masquerade.h>
|
#include <net/netfilter/ipv4/nf_nat_masquerade.h>
|
||||||
|
#include <net/netfilter/ipv6/nf_nat_masquerade.h>
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(masq_mutex);
|
||||||
|
static unsigned int masq_refcnt __read_mostly;
|
||||||
|
|
||||||
unsigned int
|
unsigned int
|
||||||
nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum,
|
nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum,
|
||||||
|
@ -78,8 +70,6 @@ static int device_cmp(struct nf_conn *i, void *ifindex)
|
||||||
|
|
||||||
if (!nat)
|
if (!nat)
|
||||||
return 0;
|
return 0;
|
||||||
if (nf_ct_l3num(i) != NFPROTO_IPV4)
|
|
||||||
return 0;
|
|
||||||
return nat->masq_index == (int)(long)ifindex;
|
return nat->masq_index == (int)(long)ifindex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +85,6 @@ static int masq_device_event(struct notifier_block *this,
|
||||||
* conntracks which were associated with that device,
|
* conntracks which were associated with that device,
|
||||||
* and forget them.
|
* and forget them.
|
||||||
*/
|
*/
|
||||||
WARN_ON(dev->ifindex == 0);
|
|
||||||
|
|
||||||
nf_ct_iterate_cleanup_net(net, device_cmp,
|
nf_ct_iterate_cleanup_net(net, device_cmp,
|
||||||
(void *)(long)dev->ifindex, 0, 0);
|
(void *)(long)dev->ifindex, 0, 0);
|
||||||
|
@ -147,9 +136,6 @@ static struct notifier_block masq_inet_notifier = {
|
||||||
.notifier_call = masq_inet_event,
|
.notifier_call = masq_inet_event,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int masq_refcnt;
|
|
||||||
static DEFINE_MUTEX(masq_mutex);
|
|
||||||
|
|
||||||
int nf_nat_masquerade_ipv4_register_notifier(void)
|
int nf_nat_masquerade_ipv4_register_notifier(void)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -194,3 +180,183 @@ void nf_nat_masquerade_ipv4_unregister_notifier(void)
|
||||||
mutex_unlock(&masq_mutex);
|
mutex_unlock(&masq_mutex);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_unregister_notifier);
|
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_unregister_notifier);
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
|
static atomic_t v6_worker_count __read_mostly;
|
||||||
|
|
||||||
|
static int
|
||||||
|
nat_ipv6_dev_get_saddr(struct net *net, const struct net_device *dev,
|
||||||
|
const struct in6_addr *daddr, unsigned int srcprefs,
|
||||||
|
struct in6_addr *saddr)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_IPV6_MODULE
|
||||||
|
const struct nf_ipv6_ops *v6_ops = nf_get_ipv6_ops();
|
||||||
|
|
||||||
|
if (!v6_ops)
|
||||||
|
return -EHOSTUNREACH;
|
||||||
|
|
||||||
|
return v6_ops->dev_get_saddr(net, dev, daddr, srcprefs, saddr);
|
||||||
|
#else
|
||||||
|
return ipv6_dev_get_saddr(net, dev, daddr, srcprefs, saddr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range,
|
||||||
|
const struct net_device *out)
|
||||||
|
{
|
||||||
|
enum ip_conntrack_info ctinfo;
|
||||||
|
struct nf_conn_nat *nat;
|
||||||
|
struct in6_addr src;
|
||||||
|
struct nf_conn *ct;
|
||||||
|
struct nf_nat_range2 newrange;
|
||||||
|
|
||||||
|
ct = nf_ct_get(skb, &ctinfo);
|
||||||
|
WARN_ON(!(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
|
||||||
|
ctinfo == IP_CT_RELATED_REPLY)));
|
||||||
|
|
||||||
|
if (nat_ipv6_dev_get_saddr(nf_ct_net(ct), out,
|
||||||
|
&ipv6_hdr(skb)->daddr, 0, &src) < 0)
|
||||||
|
return NF_DROP;
|
||||||
|
|
||||||
|
nat = nf_ct_nat_ext_add(ct);
|
||||||
|
if (nat)
|
||||||
|
nat->masq_index = out->ifindex;
|
||||||
|
|
||||||
|
newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
|
||||||
|
newrange.min_addr.in6 = src;
|
||||||
|
newrange.max_addr.in6 = src;
|
||||||
|
newrange.min_proto = range->min_proto;
|
||||||
|
newrange.max_proto = range->max_proto;
|
||||||
|
|
||||||
|
return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6);
|
||||||
|
|
||||||
|
struct masq_dev_work {
|
||||||
|
struct work_struct work;
|
||||||
|
struct net *net;
|
||||||
|
struct in6_addr addr;
|
||||||
|
int ifindex;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int inet6_cmp(struct nf_conn *ct, void *work)
|
||||||
|
{
|
||||||
|
struct masq_dev_work *w = (struct masq_dev_work *)work;
|
||||||
|
struct nf_conntrack_tuple *tuple;
|
||||||
|
|
||||||
|
if (!device_cmp(ct, (void *)(long)w->ifindex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
|
||||||
|
|
||||||
|
return ipv6_addr_equal(&w->addr, &tuple->dst.u3.in6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iterate_cleanup_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct masq_dev_work *w;
|
||||||
|
|
||||||
|
w = container_of(work, struct masq_dev_work, work);
|
||||||
|
|
||||||
|
nf_ct_iterate_cleanup_net(w->net, inet6_cmp, (void *)w, 0, 0);
|
||||||
|
|
||||||
|
put_net(w->net);
|
||||||
|
kfree(w);
|
||||||
|
atomic_dec(&v6_worker_count);
|
||||||
|
module_put(THIS_MODULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* atomic notifier; can't call nf_ct_iterate_cleanup_net (it can sleep).
|
||||||
|
*
|
||||||
|
* Defer it to the system workqueue.
|
||||||
|
*
|
||||||
|
* As we can have 'a lot' of inet_events (depending on amount of ipv6
|
||||||
|
* addresses being deleted), we also need to limit work item queue.
|
||||||
|
*/
|
||||||
|
static int masq_inet6_event(struct notifier_block *this,
|
||||||
|
unsigned long event, void *ptr)
|
||||||
|
{
|
||||||
|
struct inet6_ifaddr *ifa = ptr;
|
||||||
|
const struct net_device *dev;
|
||||||
|
struct masq_dev_work *w;
|
||||||
|
struct net *net;
|
||||||
|
|
||||||
|
if (event != NETDEV_DOWN || atomic_read(&v6_worker_count) >= 16)
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
|
dev = ifa->idev->dev;
|
||||||
|
net = maybe_get_net(dev_net(dev));
|
||||||
|
if (!net)
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
|
if (!try_module_get(THIS_MODULE))
|
||||||
|
goto err_module;
|
||||||
|
|
||||||
|
w = kmalloc(sizeof(*w), GFP_ATOMIC);
|
||||||
|
if (w) {
|
||||||
|
atomic_inc(&v6_worker_count);
|
||||||
|
|
||||||
|
INIT_WORK(&w->work, iterate_cleanup_work);
|
||||||
|
w->ifindex = dev->ifindex;
|
||||||
|
w->net = net;
|
||||||
|
w->addr = ifa->addr;
|
||||||
|
schedule_work(&w->work);
|
||||||
|
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_put(THIS_MODULE);
|
||||||
|
err_module:
|
||||||
|
put_net(net);
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct notifier_block masq_inet6_notifier = {
|
||||||
|
.notifier_call = masq_inet6_event,
|
||||||
|
};
|
||||||
|
|
||||||
|
int nf_nat_masquerade_ipv6_register_notifier(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&masq_mutex);
|
||||||
|
/* check if the notifier is already set */
|
||||||
|
if (++masq_refcnt > 1)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
ret = register_netdevice_notifier(&masq_dev_notifier);
|
||||||
|
if (ret)
|
||||||
|
goto err_dec;
|
||||||
|
|
||||||
|
ret = register_inet6addr_notifier(&masq_inet6_notifier);
|
||||||
|
if (ret)
|
||||||
|
goto err_unregister;
|
||||||
|
|
||||||
|
mutex_unlock(&masq_mutex);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err_unregister:
|
||||||
|
unregister_netdevice_notifier(&masq_dev_notifier);
|
||||||
|
err_dec:
|
||||||
|
masq_refcnt--;
|
||||||
|
out_unlock:
|
||||||
|
mutex_unlock(&masq_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_register_notifier);
|
||||||
|
|
||||||
|
void nf_nat_masquerade_ipv6_unregister_notifier(void)
|
||||||
|
{
|
||||||
|
mutex_lock(&masq_mutex);
|
||||||
|
/* check if the notifier still has clients */
|
||||||
|
if (--masq_refcnt > 0)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
unregister_inet6addr_notifier(&masq_inet6_notifier);
|
||||||
|
unregister_netdevice_notifier(&masq_dev_notifier);
|
||||||
|
out_unlock:
|
||||||
|
mutex_unlock(&masq_mutex);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_unregister_notifier);
|
||||||
|
#endif
|
Loading…
Reference in New Issue