linux-stable/net/ipv4/netfilter/ipt_MASQUERADE.c
Eric W. Biederman e9dc865340 [NET]: Make device event notification network namespace safe
Every user of the network device notifiers is either a protocol
stack or a pseudo device.  If a protocol stack that does not have
support for multiple network namespaces receives an event for a
device that is not in the initial network namespace it quite possibly
can get confused and do the wrong thing.

To avoid problems until all of the protocol stacks are converted
this patch modifies all netdev event handlers to ignore events on
devices that are not in the initial network namespace.

As the rest of the code is made network namespace aware these
checks can be removed.

Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-10 16:49:09 -07:00

204 lines
5.1 KiB
C

/* Masquerade. Simple mapping which alters range to a local IP address
(depending on route). */
/* (C) 1999-2001 Paul `Rusty' Russell
* (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/inetdevice.h>
#include <linux/ip.h>
#include <linux/timer.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <net/protocol.h>
#include <net/ip.h>
#include <net/checksum.h>
#include <net/route.h>
#include <net/netfilter/nf_nat_rule.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter/x_tables.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
MODULE_DESCRIPTION("iptables MASQUERADE target module");
/* Lock protects masq region inside conntrack */
static DEFINE_RWLOCK(masq_lock);
/* FIXME: Multiple targets. --RR */
static bool
masquerade_check(const char *tablename,
const void *e,
const struct xt_target *target,
void *targinfo,
unsigned int hook_mask)
{
const struct nf_nat_multi_range_compat *mr = targinfo;
if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) {
pr_debug("masquerade_check: bad MAP_IPS.\n");
return false;
}
if (mr->rangesize != 1) {
pr_debug("masquerade_check: bad rangesize %u\n", mr->rangesize);
return false;
}
return true;
}
static unsigned int
masquerade_target(struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const struct xt_target *target,
const void *targinfo)
{
struct nf_conn *ct;
struct nf_conn_nat *nat;
enum ip_conntrack_info ctinfo;
struct nf_nat_range newrange;
const struct nf_nat_multi_range_compat *mr;
const struct rtable *rt;
__be32 newsrc;
NF_CT_ASSERT(hooknum == NF_IP_POST_ROUTING);
ct = nf_ct_get(*pskb, &ctinfo);
nat = nfct_nat(ct);
NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED
|| ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY));
/* Source address is 0.0.0.0 - locally generated packet that is
* probably not supposed to be masqueraded.
*/
if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == 0)
return NF_ACCEPT;
mr = targinfo;
rt = (struct rtable *)(*pskb)->dst;
newsrc = inet_select_addr(out, rt->rt_gateway, RT_SCOPE_UNIVERSE);
if (!newsrc) {
printk("MASQUERADE: %s ate my IP address\n", out->name);
return NF_DROP;
}
write_lock_bh(&masq_lock);
nat->masq_index = out->ifindex;
write_unlock_bh(&masq_lock);
/* Transfer from original range. */
newrange = ((struct nf_nat_range)
{ mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,
newsrc, newsrc,
mr->range[0].min, mr->range[0].max });
/* Hand modified range to generic setup. */
return nf_nat_setup_info(ct, &newrange, hooknum);
}
static int
device_cmp(struct nf_conn *i, void *ifindex)
{
const struct nf_conn_nat *nat = nfct_nat(i);
int ret;
if (!nat)
return 0;
read_lock_bh(&masq_lock);
ret = (nat->masq_index == (int)(long)ifindex);
read_unlock_bh(&masq_lock);
return ret;
}
static int masq_device_event(struct notifier_block *this,
unsigned long event,
void *ptr)
{
const struct net_device *dev = ptr;
if (dev->nd_net != &init_net)
return NOTIFY_DONE;
if (event == NETDEV_DOWN) {
/* Device was downed. Search entire table for
conntracks which were associated with that device,
and forget them. */
NF_CT_ASSERT(dev->ifindex != 0);
nf_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex);
}
return NOTIFY_DONE;
}
static int masq_inet_event(struct notifier_block *this,
unsigned long event,
void *ptr)
{
const struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev;
if (event == NETDEV_DOWN) {
/* IP address was deleted. Search entire table for
conntracks which were associated with that device,
and forget them. */
NF_CT_ASSERT(dev->ifindex != 0);
nf_ct_iterate_cleanup(device_cmp, (void *)(long)dev->ifindex);
}
return NOTIFY_DONE;
}
static struct notifier_block masq_dev_notifier = {
.notifier_call = masq_device_event,
};
static struct notifier_block masq_inet_notifier = {
.notifier_call = masq_inet_event,
};
static struct xt_target masquerade __read_mostly = {
.name = "MASQUERADE",
.family = AF_INET,
.target = masquerade_target,
.targetsize = sizeof(struct nf_nat_multi_range_compat),
.table = "nat",
.hooks = 1 << NF_IP_POST_ROUTING,
.checkentry = masquerade_check,
.me = THIS_MODULE,
};
static int __init ipt_masquerade_init(void)
{
int ret;
ret = xt_register_target(&masquerade);
if (ret == 0) {
/* Register for device down reports */
register_netdevice_notifier(&masq_dev_notifier);
/* Register IP address change reports */
register_inetaddr_notifier(&masq_inet_notifier);
}
return ret;
}
static void __exit ipt_masquerade_fini(void)
{
xt_unregister_target(&masquerade);
unregister_netdevice_notifier(&masq_dev_notifier);
unregister_inetaddr_notifier(&masq_inet_notifier);
}
module_init(ipt_masquerade_init);
module_exit(ipt_masquerade_fini);