1 /* (C) 1999-2001 Paul `Rusty' Russell
2 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
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.
9 #include <linux/types.h>
10 #include <linux/module.h>
11 #include <linux/atomic.h>
12 #include <linux/inetdevice.h>
14 #include <linux/timer.h>
15 #include <linux/netfilter.h>
16 #include <net/protocol.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>
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
)
31 struct nf_conn_nat
*nat
;
32 enum ip_conntrack_info ctinfo
;
33 struct nf_nat_range newrange
;
34 const struct rtable
*rt
;
37 NF_CT_ASSERT(hooknum
== NF_INET_POST_ROUTING
);
39 ct
= nf_ct_get(skb
, &ctinfo
);
42 NF_CT_ASSERT(ct
&& (ctinfo
== IP_CT_NEW
|| ctinfo
== IP_CT_RELATED
||
43 ctinfo
== IP_CT_RELATED_REPLY
));
45 /* Source address is 0.0.0.0 - locally generated packet that is
46 * probably not supposed to be masqueraded.
48 if (ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.u3
.ip
== 0)
52 nh
= rt_nexthop(rt
, ip_hdr(skb
)->daddr
);
53 newsrc
= inet_select_addr(out
, nh
, RT_SCOPE_UNIVERSE
);
55 pr_info("%s ate my IP address\n", out
->name
);
59 nat
->masq_index
= out
->ifindex
;
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
;
70 /* Hand modified range to generic setup. */
71 return nf_nat_setup_info(ct
, &newrange
, NF_NAT_MANIP_SRC
);
73 EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4
);
75 static int device_cmp(struct nf_conn
*i
, void *ifindex
)
77 const struct nf_conn_nat
*nat
= nfct_nat(i
);
81 if (nf_ct_l3num(i
) != NFPROTO_IPV4
)
83 return nat
->masq_index
== (int)(long)ifindex
;
86 static int masq_device_event(struct notifier_block
*this,
90 const struct net_device
*dev
= netdev_notifier_info_to_dev(ptr
);
91 struct net
*net
= dev_net(dev
);
93 if (event
== NETDEV_DOWN
) {
94 /* Device was downed. Search entire table for
95 * conntracks which were associated with that device,
98 NF_CT_ASSERT(dev
->ifindex
!= 0);
100 nf_ct_iterate_cleanup(net
, device_cmp
,
101 (void *)(long)dev
->ifindex
, 0, 0);
107 static int masq_inet_event(struct notifier_block
*this,
111 struct net_device
*dev
= ((struct in_ifaddr
*)ptr
)->ifa_dev
->dev
;
112 struct netdev_notifier_info info
;
114 netdev_notifier_info_init(&info
, dev
);
115 return masq_device_event(this, event
, &info
);
118 static struct notifier_block masq_dev_notifier
= {
119 .notifier_call
= masq_device_event
,
122 static struct notifier_block masq_inet_notifier
= {
123 .notifier_call
= masq_inet_event
,
126 static atomic_t masquerade_notifier_refcount
= ATOMIC_INIT(0);
128 void nf_nat_masquerade_ipv4_register_notifier(void)
130 /* check if the notifier was already set */
131 if (atomic_inc_return(&masquerade_notifier_refcount
) > 1)
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
);
139 EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_register_notifier
);
141 void nf_nat_masquerade_ipv4_unregister_notifier(void)
143 /* check if the notifier still has clients */
144 if (atomic_dec_return(&masquerade_notifier_refcount
) > 0)
147 unregister_netdevice_notifier(&masq_dev_notifier
);
148 unregister_inetaddr_notifier(&masq_inet_notifier
);
150 EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_unregister_notifier
);
152 MODULE_LICENSE("GPL");
153 MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");