X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fnetfilter%2Fipt_MASQUERADE.c;h=d669685afd04dac843529c8171bd4994af8b8813;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=54bc4684cc9debc9e668f83199889dcb87286195;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index 54bc4684c..d669685af 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -2,15 +2,15 @@ (depending on route). */ /* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team + * (C) 2002-2006 Netfilter Core Team * * 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 #include +#include #include #include #include @@ -18,8 +18,13 @@ #include #include #include +#include #include +#ifdef CONFIG_NF_NAT_NEEDED +#include +#else #include +#endif #include MODULE_LICENSE("GPL"); @@ -33,31 +38,18 @@ MODULE_DESCRIPTION("iptables MASQUERADE target module"); #endif /* Lock protects masq region inside conntrack */ -static DECLARE_RWLOCK(masq_lock); +static DEFINE_RWLOCK(masq_lock); /* FIXME: Multiple targets. --RR */ static int masquerade_check(const char *tablename, - const struct ipt_entry *e, + const void *e, + const struct xt_target *target, void *targinfo, - unsigned int targinfosize, unsigned int hook_mask) { - const struct ip_nat_multi_range *mr = targinfo; + const struct ip_nat_multi_range_compat *mr = targinfo; - if (strcmp(tablename, "nat") != 0) { - DEBUGP("masquerade_check: bad table `%s'.\n", tablename); - return 0; - } - if (targinfosize != IPT_ALIGN(sizeof(*mr))) { - DEBUGP("masquerade_check: size %u != %u.\n", - targinfosize, sizeof(*mr)); - return 0; - } - if (hook_mask & ~(1 << NF_IP_POST_ROUTING)) { - DEBUGP("masquerade_check: bad hooks %x.\n", hook_mask); - return 0; - } if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { DEBUGP("masquerade_check: bad MAP_IPS.\n"); return 0; @@ -74,102 +66,126 @@ masquerade_target(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, - const void *targinfo, - void *userinfo) + const struct xt_target *target, + const void *targinfo) { +#ifdef CONFIG_NF_NAT_NEEDED + struct nf_conn_nat *nat; +#endif struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; - const struct ip_nat_multi_range *mr; - struct ip_nat_multi_range newrange; - u_int32_t newsrc; + struct ip_nat_range newrange; + const struct ip_nat_multi_range_compat *mr; struct rtable *rt; + __be32 newsrc; IP_NF_ASSERT(hooknum == NF_IP_POST_ROUTING); - /* FIXME: For the moment, don't do local packets, breaks - testsuite for 2.3.49 --RR */ - if ((*pskb)->sk) - return NF_ACCEPT; - ct = ip_conntrack_get(*pskb, &ctinfo); - IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW - || ctinfo == IP_CT_RELATED)); +#ifdef CONFIG_NF_NAT_NEEDED + nat = nfct_nat(ct); +#endif + IP_NF_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. + */ +#ifdef CONFIG_NF_NAT_NEEDED + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == 0) +#else + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip == 0) +#endif + return NF_ACCEPT; mr = targinfo; - - { - struct flowi fl = { .nl_u = { .ip4_u = - { .daddr = (*pskb)->nh.iph->daddr, - .tos = (RT_TOS((*pskb)->nh.iph->tos) | - RTO_CONN), -#ifdef CONFIG_IP_ROUTE_FWMARK - .fwmark = (*pskb)->nfmark -#endif - } } }; - if (ip_route_output_key(&rt, &fl) != 0) { - /* Funky routing can do this. */ - if (net_ratelimit()) - printk("MASQUERADE:" - " No route: Rusty's brain broke!\n"); - return NF_DROP; - } - if (rt->u.dst.dev != out) { - if (net_ratelimit()) - printk("MASQUERADE:" - " Route sent us somewhere else.\n"); - ip_rt_put(rt); - return NF_DROP; - } + 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; } - newsrc = rt->rt_src; - DEBUGP("newsrc = %u.%u.%u.%u\n", NIPQUAD(newsrc)); - ip_rt_put(rt); - - WRITE_LOCK(&masq_lock); + write_lock_bh(&masq_lock); +#ifdef CONFIG_NF_NAT_NEEDED + nat->masq_index = out->ifindex; +#else ct->nat.masq_index = out->ifindex; - WRITE_UNLOCK(&masq_lock); +#endif + write_unlock_bh(&masq_lock); /* Transfer from original range. */ - newrange = ((struct ip_nat_multi_range) - { 1, { { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, - newsrc, newsrc, - mr->range[0].min, mr->range[0].max } } }); + newrange = ((struct ip_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 ip_nat_setup_info(ct, &newrange, hooknum); } static inline int -device_cmp(const struct ip_conntrack *i, void *_ina) +device_cmp(struct ip_conntrack *i, void *ifindex) { - int ret = 0; - struct in_ifaddr *ina = _ina; + int ret; +#ifdef CONFIG_NF_NAT_NEEDED + struct nf_conn_nat *nat = nfct_nat(i); - READ_LOCK(&masq_lock); - /* If it's masquerading out this interface with a different address, - or we don't know the new address of this interface. */ - if (i->nat.masq_index == ina->ifa_dev->dev->ifindex - && i->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip != ina->ifa_address) - ret = 1; - READ_UNLOCK(&masq_lock); + if (!nat) + return 0; +#endif + + read_lock_bh(&masq_lock); +#ifdef CONFIG_NF_NAT_NEEDED + ret = (nat->masq_index == (int)(long)ifindex); +#else + ret = (i->nat.masq_index == (int)(long)ifindex); +#endif + read_unlock_bh(&masq_lock); return ret; } +static int masq_device_event(struct notifier_block *this, + unsigned long event, + void *ptr) +{ + struct net_device *dev = ptr; + + if (event == NETDEV_DOWN) { + /* Device was downed. Search entire table for + conntracks which were associated with that device, + and forget them. */ + IP_NF_ASSERT(dev->ifindex != 0); + + ip_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) { - /* For some configurations, interfaces often come back with - * the same address. If not, clean up old conntrack - * entries. */ - if (event == NETDEV_UP) - ip_ct_selective_cleanup(device_cmp, ptr); + 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. */ + IP_NF_ASSERT(dev->ifindex != 0); + + ip_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, }; @@ -177,28 +193,35 @@ static struct notifier_block masq_inet_notifier = { static struct ipt_target masquerade = { .name = "MASQUERADE", .target = masquerade_target, + .targetsize = sizeof(struct ip_nat_multi_range_compat), + .table = "nat", + .hooks = 1 << NF_IP_POST_ROUTING, .checkentry = masquerade_check, .me = THIS_MODULE, }; -static int __init init(void) +static int __init ipt_masquerade_init(void) { int ret; ret = ipt_register_target(&masquerade); - if (ret == 0) + 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 fini(void) +static void __exit ipt_masquerade_fini(void) { ipt_unregister_target(&masquerade); + unregister_netdevice_notifier(&masq_dev_notifier); unregister_inetaddr_notifier(&masq_inet_notifier); } -module_init(init); -module_exit(fini); +module_init(ipt_masquerade_init); +module_exit(ipt_masquerade_fini);