diff options
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/netfilter/Kconfig | 13 | ||||
-rw-r--r-- | net/ipv4/netfilter/Makefile | 2 | ||||
-rw-r--r-- | net/ipv4/netfilter/ipt_MASQUERADE.c | 108 | ||||
-rw-r--r-- | net/ipv4/netfilter/iptable_nat.c | 233 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_nat_l3proto_ipv4.c | 199 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_nat_masquerade_ipv4.c | 153 | ||||
-rw-r--r-- | net/ipv4/netfilter/nft_chain_nat_ipv4.c | 157 | ||||
-rw-r--r-- | net/ipv4/netfilter/nft_masq_ipv4.c | 89 |
8 files changed, 536 insertions, 418 deletions
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 7cbcaf4f0194..d189c5262bdb 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig | |||
@@ -232,8 +232,21 @@ config IP_NF_NAT | |||
232 | 232 | ||
233 | if IP_NF_NAT | 233 | if IP_NF_NAT |
234 | 234 | ||
235 | config NF_NAT_MASQUERADE_IPV4 | ||
236 | tristate "IPv4 masquerade support" | ||
237 | help | ||
238 | This is the kernel functionality to provide NAT in the masquerade | ||
239 | flavour (automatic source address selection). | ||
240 | |||
241 | config NFT_MASQ_IPV4 | ||
242 | tristate "IPv4 masquerading support for nf_tables" | ||
243 | depends on NF_TABLES_IPV4 | ||
244 | depends on NFT_MASQ | ||
245 | select NF_NAT_MASQUERADE_IPV4 | ||
246 | |||
235 | config IP_NF_TARGET_MASQUERADE | 247 | config IP_NF_TARGET_MASQUERADE |
236 | tristate "MASQUERADE target support" | 248 | tristate "MASQUERADE target support" |
249 | select NF_NAT_MASQUERADE_IPV4 | ||
237 | default m if NETFILTER_ADVANCED=n | 250 | default m if NETFILTER_ADVANCED=n |
238 | help | 251 | help |
239 | Masquerading is a special case of NAT: all outgoing connections are | 252 | Masquerading is a special case of NAT: all outgoing connections are |
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index edf4af32e9f2..14488cc5fd2c 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile | |||
@@ -27,6 +27,7 @@ obj-$(CONFIG_NF_LOG_IPV4) += nf_log_ipv4.o | |||
27 | obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o | 27 | obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o |
28 | obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o | 28 | obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o |
29 | obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o | 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 | # NAT protocols (nf_nat) | 32 | # NAT protocols (nf_nat) |
32 | obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o | 33 | obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o |
@@ -35,6 +36,7 @@ obj-$(CONFIG_NF_TABLES_IPV4) += nf_tables_ipv4.o | |||
35 | obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o | 36 | obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o |
36 | obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o | 37 | obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o |
37 | obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o | 38 | obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o |
39 | obj-$(CONFIG_NFT_MASQ_IPV4) += nft_masq_ipv4.o | ||
38 | obj-$(CONFIG_NF_TABLES_ARP) += nf_tables_arp.o | 40 | obj-$(CONFIG_NF_TABLES_ARP) += nf_tables_arp.o |
39 | 41 | ||
40 | # generic IP tables | 42 | # generic IP tables |
diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index 00352ce0f0de..da7f02a0b868 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/netfilter_ipv4.h> | 22 | #include <linux/netfilter_ipv4.h> |
23 | #include <linux/netfilter/x_tables.h> | 23 | #include <linux/netfilter/x_tables.h> |
24 | #include <net/netfilter/nf_nat.h> | 24 | #include <net/netfilter/nf_nat.h> |
25 | #include <net/netfilter/ipv4/nf_nat_masquerade.h> | ||
25 | 26 | ||
26 | MODULE_LICENSE("GPL"); | 27 | MODULE_LICENSE("GPL"); |
27 | MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); | 28 | MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); |
@@ -46,103 +47,17 @@ static int masquerade_tg_check(const struct xt_tgchk_param *par) | |||
46 | static unsigned int | 47 | static unsigned int |
47 | masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par) | 48 | masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par) |
48 | { | 49 | { |
49 | struct nf_conn *ct; | 50 | struct nf_nat_range range; |
50 | struct nf_conn_nat *nat; | ||
51 | enum ip_conntrack_info ctinfo; | ||
52 | struct nf_nat_range newrange; | ||
53 | const struct nf_nat_ipv4_multi_range_compat *mr; | 51 | const struct nf_nat_ipv4_multi_range_compat *mr; |
54 | const struct rtable *rt; | ||
55 | __be32 newsrc, nh; | ||
56 | |||
57 | NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING); | ||
58 | |||
59 | ct = nf_ct_get(skb, &ctinfo); | ||
60 | nat = nfct_nat(ct); | ||
61 | |||
62 | NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || | ||
63 | ctinfo == IP_CT_RELATED_REPLY)); | ||
64 | |||
65 | /* Source address is 0.0.0.0 - locally generated packet that is | ||
66 | * probably not supposed to be masqueraded. | ||
67 | */ | ||
68 | if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == 0) | ||
69 | return NF_ACCEPT; | ||
70 | 52 | ||
71 | mr = par->targinfo; | 53 | mr = par->targinfo; |
72 | rt = skb_rtable(skb); | 54 | range.flags = mr->range[0].flags; |
73 | nh = rt_nexthop(rt, ip_hdr(skb)->daddr); | 55 | range.min_proto = mr->range[0].min; |
74 | newsrc = inet_select_addr(par->out, nh, RT_SCOPE_UNIVERSE); | 56 | range.max_proto = mr->range[0].max; |
75 | if (!newsrc) { | ||
76 | pr_info("%s ate my IP address\n", par->out->name); | ||
77 | return NF_DROP; | ||
78 | } | ||
79 | |||
80 | nat->masq_index = par->out->ifindex; | ||
81 | |||
82 | /* Transfer from original range. */ | ||
83 | memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); | ||
84 | memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); | ||
85 | newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; | ||
86 | newrange.min_addr.ip = newsrc; | ||
87 | newrange.max_addr.ip = newsrc; | ||
88 | newrange.min_proto = mr->range[0].min; | ||
89 | newrange.max_proto = mr->range[0].max; | ||
90 | 57 | ||
91 | /* Hand modified range to generic setup. */ | 58 | return nf_nat_masquerade_ipv4(skb, par->hooknum, &range, par->out); |
92 | return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC); | ||
93 | } | 59 | } |
94 | 60 | ||
95 | static int | ||
96 | device_cmp(struct nf_conn *i, void *ifindex) | ||
97 | { | ||
98 | const struct nf_conn_nat *nat = nfct_nat(i); | ||
99 | |||
100 | if (!nat) | ||
101 | return 0; | ||
102 | if (nf_ct_l3num(i) != NFPROTO_IPV4) | ||
103 | return 0; | ||
104 | return nat->masq_index == (int)(long)ifindex; | ||
105 | } | ||
106 | |||
107 | static int masq_device_event(struct notifier_block *this, | ||
108 | unsigned long event, | ||
109 | void *ptr) | ||
110 | { | ||
111 | const struct net_device *dev = netdev_notifier_info_to_dev(ptr); | ||
112 | struct net *net = dev_net(dev); | ||
113 | |||
114 | if (event == NETDEV_DOWN) { | ||
115 | /* Device was downed. Search entire table for | ||
116 | conntracks which were associated with that device, | ||
117 | and forget them. */ | ||
118 | NF_CT_ASSERT(dev->ifindex != 0); | ||
119 | |||
120 | nf_ct_iterate_cleanup(net, device_cmp, | ||
121 | (void *)(long)dev->ifindex, 0, 0); | ||
122 | } | ||
123 | |||
124 | return NOTIFY_DONE; | ||
125 | } | ||
126 | |||
127 | static int masq_inet_event(struct notifier_block *this, | ||
128 | unsigned long event, | ||
129 | void *ptr) | ||
130 | { | ||
131 | struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev; | ||
132 | struct netdev_notifier_info info; | ||
133 | |||
134 | netdev_notifier_info_init(&info, dev); | ||
135 | return masq_device_event(this, event, &info); | ||
136 | } | ||
137 | |||
138 | static struct notifier_block masq_dev_notifier = { | ||
139 | .notifier_call = masq_device_event, | ||
140 | }; | ||
141 | |||
142 | static struct notifier_block masq_inet_notifier = { | ||
143 | .notifier_call = masq_inet_event, | ||
144 | }; | ||
145 | |||
146 | static struct xt_target masquerade_tg_reg __read_mostly = { | 61 | static struct xt_target masquerade_tg_reg __read_mostly = { |
147 | .name = "MASQUERADE", | 62 | .name = "MASQUERADE", |
148 | .family = NFPROTO_IPV4, | 63 | .family = NFPROTO_IPV4, |
@@ -160,12 +75,8 @@ static int __init masquerade_tg_init(void) | |||
160 | 75 | ||
161 | ret = xt_register_target(&masquerade_tg_reg); | 76 | ret = xt_register_target(&masquerade_tg_reg); |
162 | 77 | ||
163 | if (ret == 0) { | 78 | if (ret == 0) |
164 | /* Register for device down reports */ | 79 | nf_nat_masquerade_ipv4_register_notifier(); |
165 | register_netdevice_notifier(&masq_dev_notifier); | ||
166 | /* Register IP address change reports */ | ||
167 | register_inetaddr_notifier(&masq_inet_notifier); | ||
168 | } | ||
169 | 80 | ||
170 | return ret; | 81 | return ret; |
171 | } | 82 | } |
@@ -173,8 +84,7 @@ static int __init masquerade_tg_init(void) | |||
173 | static void __exit masquerade_tg_exit(void) | 84 | static void __exit masquerade_tg_exit(void) |
174 | { | 85 | { |
175 | xt_unregister_target(&masquerade_tg_reg); | 86 | xt_unregister_target(&masquerade_tg_reg); |
176 | unregister_netdevice_notifier(&masq_dev_notifier); | 87 | nf_nat_masquerade_ipv4_unregister_notifier(); |
177 | unregister_inetaddr_notifier(&masq_inet_notifier); | ||
178 | } | 88 | } |
179 | 89 | ||
180 | module_init(masquerade_tg_init); | 90 | module_init(masquerade_tg_init); |
diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index f1787c04a4dd..6b67d7e9a75d 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c | |||
@@ -28,222 +28,57 @@ static const struct xt_table nf_nat_ipv4_table = { | |||
28 | .af = NFPROTO_IPV4, | 28 | .af = NFPROTO_IPV4, |
29 | }; | 29 | }; |
30 | 30 | ||
31 | static unsigned int alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) | 31 | static unsigned int iptable_nat_do_chain(const struct nf_hook_ops *ops, |
32 | { | 32 | struct sk_buff *skb, |
33 | /* Force range to this IP; let proto decide mapping for | 33 | const struct net_device *in, |
34 | * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). | 34 | const struct net_device *out, |
35 | */ | 35 | struct nf_conn *ct) |
36 | struct nf_nat_range range; | ||
37 | |||
38 | range.flags = 0; | ||
39 | pr_debug("Allocating NULL binding for %p (%pI4)\n", ct, | ||
40 | HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ? | ||
41 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip : | ||
42 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip); | ||
43 | |||
44 | return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum)); | ||
45 | } | ||
46 | |||
47 | static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum, | ||
48 | const struct net_device *in, | ||
49 | const struct net_device *out, | ||
50 | struct nf_conn *ct) | ||
51 | { | 36 | { |
52 | struct net *net = nf_ct_net(ct); | 37 | struct net *net = nf_ct_net(ct); |
53 | unsigned int ret; | ||
54 | 38 | ||
55 | ret = ipt_do_table(skb, hooknum, in, out, net->ipv4.nat_table); | 39 | return ipt_do_table(skb, ops->hooknum, in, out, net->ipv4.nat_table); |
56 | if (ret == NF_ACCEPT) { | ||
57 | if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum))) | ||
58 | ret = alloc_null_binding(ct, hooknum); | ||
59 | } | ||
60 | return ret; | ||
61 | } | 40 | } |
62 | 41 | ||
63 | static unsigned int | 42 | static unsigned int iptable_nat_ipv4_fn(const struct nf_hook_ops *ops, |
64 | nf_nat_ipv4_fn(const struct nf_hook_ops *ops, | 43 | struct sk_buff *skb, |
65 | struct sk_buff *skb, | 44 | const struct net_device *in, |
66 | const struct net_device *in, | 45 | const struct net_device *out, |
67 | const struct net_device *out, | 46 | int (*okfn)(struct sk_buff *)) |
68 | int (*okfn)(struct sk_buff *)) | ||
69 | { | 47 | { |
70 | struct nf_conn *ct; | 48 | return nf_nat_ipv4_fn(ops, skb, in, out, iptable_nat_do_chain); |
71 | enum ip_conntrack_info ctinfo; | ||
72 | struct nf_conn_nat *nat; | ||
73 | /* maniptype == SRC for postrouting. */ | ||
74 | enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum); | ||
75 | |||
76 | /* We never see fragments: conntrack defrags on pre-routing | ||
77 | * and local-out, and nf_nat_out protects post-routing. | ||
78 | */ | ||
79 | NF_CT_ASSERT(!ip_is_fragment(ip_hdr(skb))); | ||
80 | |||
81 | ct = nf_ct_get(skb, &ctinfo); | ||
82 | /* Can't track? It's not due to stress, or conntrack would | ||
83 | * have dropped it. Hence it's the user's responsibilty to | ||
84 | * packet filter it out, or implement conntrack/NAT for that | ||
85 | * protocol. 8) --RR | ||
86 | */ | ||
87 | if (!ct) | ||
88 | return NF_ACCEPT; | ||
89 | |||
90 | /* Don't try to NAT if this packet is not conntracked */ | ||
91 | if (nf_ct_is_untracked(ct)) | ||
92 | return NF_ACCEPT; | ||
93 | |||
94 | nat = nf_ct_nat_ext_add(ct); | ||
95 | if (nat == NULL) | ||
96 | return NF_ACCEPT; | ||
97 | |||
98 | switch (ctinfo) { | ||
99 | case IP_CT_RELATED: | ||
100 | case IP_CT_RELATED_REPLY: | ||
101 | if (ip_hdr(skb)->protocol == IPPROTO_ICMP) { | ||
102 | if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, | ||
103 | ops->hooknum)) | ||
104 | return NF_DROP; | ||
105 | else | ||
106 | return NF_ACCEPT; | ||
107 | } | ||
108 | /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ | ||
109 | case IP_CT_NEW: | ||
110 | /* Seen it before? This can happen for loopback, retrans, | ||
111 | * or local packets. | ||
112 | */ | ||
113 | if (!nf_nat_initialized(ct, maniptype)) { | ||
114 | unsigned int ret; | ||
115 | |||
116 | ret = nf_nat_rule_find(skb, ops->hooknum, in, out, ct); | ||
117 | if (ret != NF_ACCEPT) | ||
118 | return ret; | ||
119 | } else { | ||
120 | pr_debug("Already setup manip %s for ct %p\n", | ||
121 | maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST", | ||
122 | ct); | ||
123 | if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) | ||
124 | goto oif_changed; | ||
125 | } | ||
126 | break; | ||
127 | |||
128 | default: | ||
129 | /* ESTABLISHED */ | ||
130 | NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || | ||
131 | ctinfo == IP_CT_ESTABLISHED_REPLY); | ||
132 | if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) | ||
133 | goto oif_changed; | ||
134 | } | ||
135 | |||
136 | return nf_nat_packet(ct, ctinfo, ops->hooknum, skb); | ||
137 | |||
138 | oif_changed: | ||
139 | nf_ct_kill_acct(ct, ctinfo, skb); | ||
140 | return NF_DROP; | ||
141 | } | 49 | } |
142 | 50 | ||
143 | static unsigned int | 51 | static unsigned int iptable_nat_ipv4_in(const struct nf_hook_ops *ops, |
144 | nf_nat_ipv4_in(const struct nf_hook_ops *ops, | 52 | struct sk_buff *skb, |
145 | struct sk_buff *skb, | 53 | const struct net_device *in, |
146 | const struct net_device *in, | 54 | const struct net_device *out, |
147 | const struct net_device *out, | 55 | int (*okfn)(struct sk_buff *)) |
148 | int (*okfn)(struct sk_buff *)) | ||
149 | { | 56 | { |
150 | unsigned int ret; | 57 | return nf_nat_ipv4_in(ops, skb, in, out, iptable_nat_do_chain); |
151 | __be32 daddr = ip_hdr(skb)->daddr; | ||
152 | |||
153 | ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn); | ||
154 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
155 | daddr != ip_hdr(skb)->daddr) | ||
156 | skb_dst_drop(skb); | ||
157 | |||
158 | return ret; | ||
159 | } | 58 | } |
160 | 59 | ||
161 | static unsigned int | 60 | static unsigned int iptable_nat_ipv4_out(const struct nf_hook_ops *ops, |
162 | nf_nat_ipv4_out(const struct nf_hook_ops *ops, | 61 | struct sk_buff *skb, |
163 | struct sk_buff *skb, | 62 | const struct net_device *in, |
164 | const struct net_device *in, | 63 | const struct net_device *out, |
165 | const struct net_device *out, | 64 | int (*okfn)(struct sk_buff *)) |
166 | int (*okfn)(struct sk_buff *)) | ||
167 | { | 65 | { |
168 | #ifdef CONFIG_XFRM | 66 | return nf_nat_ipv4_out(ops, skb, in, out, iptable_nat_do_chain); |
169 | const struct nf_conn *ct; | ||
170 | enum ip_conntrack_info ctinfo; | ||
171 | int err; | ||
172 | #endif | ||
173 | unsigned int ret; | ||
174 | |||
175 | /* root is playing with raw sockets. */ | ||
176 | if (skb->len < sizeof(struct iphdr) || | ||
177 | ip_hdrlen(skb) < sizeof(struct iphdr)) | ||
178 | return NF_ACCEPT; | ||
179 | |||
180 | ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn); | ||
181 | #ifdef CONFIG_XFRM | ||
182 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
183 | !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && | ||
184 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
185 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
186 | |||
187 | if ((ct->tuplehash[dir].tuple.src.u3.ip != | ||
188 | ct->tuplehash[!dir].tuple.dst.u3.ip) || | ||
189 | (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP && | ||
190 | ct->tuplehash[dir].tuple.src.u.all != | ||
191 | ct->tuplehash[!dir].tuple.dst.u.all)) { | ||
192 | err = nf_xfrm_me_harder(skb, AF_INET); | ||
193 | if (err < 0) | ||
194 | ret = NF_DROP_ERR(err); | ||
195 | } | ||
196 | } | ||
197 | #endif | ||
198 | return ret; | ||
199 | } | 67 | } |
200 | 68 | ||
201 | static unsigned int | 69 | static unsigned int iptable_nat_ipv4_local_fn(const struct nf_hook_ops *ops, |
202 | nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops, | 70 | struct sk_buff *skb, |
203 | struct sk_buff *skb, | 71 | const struct net_device *in, |
204 | const struct net_device *in, | 72 | const struct net_device *out, |
205 | const struct net_device *out, | 73 | int (*okfn)(struct sk_buff *)) |
206 | int (*okfn)(struct sk_buff *)) | ||
207 | { | 74 | { |
208 | const struct nf_conn *ct; | 75 | return nf_nat_ipv4_local_fn(ops, skb, in, out, iptable_nat_do_chain); |
209 | enum ip_conntrack_info ctinfo; | ||
210 | unsigned int ret; | ||
211 | int err; | ||
212 | |||
213 | /* root is playing with raw sockets. */ | ||
214 | if (skb->len < sizeof(struct iphdr) || | ||
215 | ip_hdrlen(skb) < sizeof(struct iphdr)) | ||
216 | return NF_ACCEPT; | ||
217 | |||
218 | ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn); | ||
219 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
220 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
221 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
222 | |||
223 | if (ct->tuplehash[dir].tuple.dst.u3.ip != | ||
224 | ct->tuplehash[!dir].tuple.src.u3.ip) { | ||
225 | err = ip_route_me_harder(skb, RTN_UNSPEC); | ||
226 | if (err < 0) | ||
227 | ret = NF_DROP_ERR(err); | ||
228 | } | ||
229 | #ifdef CONFIG_XFRM | ||
230 | else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && | ||
231 | ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP && | ||
232 | ct->tuplehash[dir].tuple.dst.u.all != | ||
233 | ct->tuplehash[!dir].tuple.src.u.all) { | ||
234 | err = nf_xfrm_me_harder(skb, AF_INET); | ||
235 | if (err < 0) | ||
236 | ret = NF_DROP_ERR(err); | ||
237 | } | ||
238 | #endif | ||
239 | } | ||
240 | return ret; | ||
241 | } | 76 | } |
242 | 77 | ||
243 | static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = { | 78 | static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = { |
244 | /* Before packet filtering, change destination */ | 79 | /* Before packet filtering, change destination */ |
245 | { | 80 | { |
246 | .hook = nf_nat_ipv4_in, | 81 | .hook = iptable_nat_ipv4_in, |
247 | .owner = THIS_MODULE, | 82 | .owner = THIS_MODULE, |
248 | .pf = NFPROTO_IPV4, | 83 | .pf = NFPROTO_IPV4, |
249 | .hooknum = NF_INET_PRE_ROUTING, | 84 | .hooknum = NF_INET_PRE_ROUTING, |
@@ -251,7 +86,7 @@ static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = { | |||
251 | }, | 86 | }, |
252 | /* After packet filtering, change source */ | 87 | /* After packet filtering, change source */ |
253 | { | 88 | { |
254 | .hook = nf_nat_ipv4_out, | 89 | .hook = iptable_nat_ipv4_out, |
255 | .owner = THIS_MODULE, | 90 | .owner = THIS_MODULE, |
256 | .pf = NFPROTO_IPV4, | 91 | .pf = NFPROTO_IPV4, |
257 | .hooknum = NF_INET_POST_ROUTING, | 92 | .hooknum = NF_INET_POST_ROUTING, |
@@ -259,7 +94,7 @@ static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = { | |||
259 | }, | 94 | }, |
260 | /* Before packet filtering, change destination */ | 95 | /* Before packet filtering, change destination */ |
261 | { | 96 | { |
262 | .hook = nf_nat_ipv4_local_fn, | 97 | .hook = iptable_nat_ipv4_local_fn, |
263 | .owner = THIS_MODULE, | 98 | .owner = THIS_MODULE, |
264 | .pf = NFPROTO_IPV4, | 99 | .pf = NFPROTO_IPV4, |
265 | .hooknum = NF_INET_LOCAL_OUT, | 100 | .hooknum = NF_INET_LOCAL_OUT, |
@@ -267,7 +102,7 @@ static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = { | |||
267 | }, | 102 | }, |
268 | /* After packet filtering, change source */ | 103 | /* After packet filtering, change source */ |
269 | { | 104 | { |
270 | .hook = nf_nat_ipv4_fn, | 105 | .hook = iptable_nat_ipv4_fn, |
271 | .owner = THIS_MODULE, | 106 | .owner = THIS_MODULE, |
272 | .pf = NFPROTO_IPV4, | 107 | .pf = NFPROTO_IPV4, |
273 | .hooknum = NF_INET_LOCAL_IN, | 108 | .hooknum = NF_INET_LOCAL_IN, |
diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c index 14f5ccd06337..fc37711e11f3 100644 --- a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c | |||
@@ -254,6 +254,205 @@ int nf_nat_icmp_reply_translation(struct sk_buff *skb, | |||
254 | } | 254 | } |
255 | EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation); | 255 | EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation); |
256 | 256 | ||
257 | unsigned int | ||
258 | nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, | ||
259 | const struct net_device *in, const struct net_device *out, | ||
260 | unsigned int (*do_chain)(const struct nf_hook_ops *ops, | ||
261 | struct sk_buff *skb, | ||
262 | const struct net_device *in, | ||
263 | const struct net_device *out, | ||
264 | struct nf_conn *ct)) | ||
265 | { | ||
266 | struct nf_conn *ct; | ||
267 | enum ip_conntrack_info ctinfo; | ||
268 | struct nf_conn_nat *nat; | ||
269 | /* maniptype == SRC for postrouting. */ | ||
270 | enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum); | ||
271 | |||
272 | /* We never see fragments: conntrack defrags on pre-routing | ||
273 | * and local-out, and nf_nat_out protects post-routing. | ||
274 | */ | ||
275 | NF_CT_ASSERT(!ip_is_fragment(ip_hdr(skb))); | ||
276 | |||
277 | ct = nf_ct_get(skb, &ctinfo); | ||
278 | /* Can't track? It's not due to stress, or conntrack would | ||
279 | * have dropped it. Hence it's the user's responsibilty to | ||
280 | * packet filter it out, or implement conntrack/NAT for that | ||
281 | * protocol. 8) --RR | ||
282 | */ | ||
283 | if (!ct) | ||
284 | return NF_ACCEPT; | ||
285 | |||
286 | /* Don't try to NAT if this packet is not conntracked */ | ||
287 | if (nf_ct_is_untracked(ct)) | ||
288 | return NF_ACCEPT; | ||
289 | |||
290 | nat = nf_ct_nat_ext_add(ct); | ||
291 | if (nat == NULL) | ||
292 | return NF_ACCEPT; | ||
293 | |||
294 | switch (ctinfo) { | ||
295 | case IP_CT_RELATED: | ||
296 | case IP_CT_RELATED_REPLY: | ||
297 | if (ip_hdr(skb)->protocol == IPPROTO_ICMP) { | ||
298 | if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, | ||
299 | ops->hooknum)) | ||
300 | return NF_DROP; | ||
301 | else | ||
302 | return NF_ACCEPT; | ||
303 | } | ||
304 | /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ | ||
305 | case IP_CT_NEW: | ||
306 | /* Seen it before? This can happen for loopback, retrans, | ||
307 | * or local packets. | ||
308 | */ | ||
309 | if (!nf_nat_initialized(ct, maniptype)) { | ||
310 | unsigned int ret; | ||
311 | |||
312 | ret = do_chain(ops, skb, in, out, ct); | ||
313 | if (ret != NF_ACCEPT) | ||
314 | return ret; | ||
315 | |||
316 | if (nf_nat_initialized(ct, HOOK2MANIP(ops->hooknum))) | ||
317 | break; | ||
318 | |||
319 | ret = nf_nat_alloc_null_binding(ct, ops->hooknum); | ||
320 | if (ret != NF_ACCEPT) | ||
321 | return ret; | ||
322 | } else { | ||
323 | pr_debug("Already setup manip %s for ct %p\n", | ||
324 | maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST", | ||
325 | ct); | ||
326 | if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) | ||
327 | goto oif_changed; | ||
328 | } | ||
329 | break; | ||
330 | |||
331 | default: | ||
332 | /* ESTABLISHED */ | ||
333 | NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || | ||
334 | ctinfo == IP_CT_ESTABLISHED_REPLY); | ||
335 | if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) | ||
336 | goto oif_changed; | ||
337 | } | ||
338 | |||
339 | return nf_nat_packet(ct, ctinfo, ops->hooknum, skb); | ||
340 | |||
341 | oif_changed: | ||
342 | nf_ct_kill_acct(ct, ctinfo, skb); | ||
343 | return NF_DROP; | ||
344 | } | ||
345 | EXPORT_SYMBOL_GPL(nf_nat_ipv4_fn); | ||
346 | |||
347 | unsigned int | ||
348 | nf_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb, | ||
349 | const struct net_device *in, const struct net_device *out, | ||
350 | unsigned int (*do_chain)(const struct nf_hook_ops *ops, | ||
351 | struct sk_buff *skb, | ||
352 | const struct net_device *in, | ||
353 | const struct net_device *out, | ||
354 | struct nf_conn *ct)) | ||
355 | { | ||
356 | unsigned int ret; | ||
357 | __be32 daddr = ip_hdr(skb)->daddr; | ||
358 | |||
359 | ret = nf_nat_ipv4_fn(ops, skb, in, out, do_chain); | ||
360 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
361 | daddr != ip_hdr(skb)->daddr) | ||
362 | skb_dst_drop(skb); | ||
363 | |||
364 | return ret; | ||
365 | } | ||
366 | EXPORT_SYMBOL_GPL(nf_nat_ipv4_in); | ||
367 | |||
368 | unsigned int | ||
369 | nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb, | ||
370 | const struct net_device *in, const struct net_device *out, | ||
371 | unsigned int (*do_chain)(const struct nf_hook_ops *ops, | ||
372 | struct sk_buff *skb, | ||
373 | const struct net_device *in, | ||
374 | const struct net_device *out, | ||
375 | struct nf_conn *ct)) | ||
376 | { | ||
377 | #ifdef CONFIG_XFRM | ||
378 | const struct nf_conn *ct; | ||
379 | enum ip_conntrack_info ctinfo; | ||
380 | int err; | ||
381 | #endif | ||
382 | unsigned int ret; | ||
383 | |||
384 | /* root is playing with raw sockets. */ | ||
385 | if (skb->len < sizeof(struct iphdr) || | ||
386 | ip_hdrlen(skb) < sizeof(struct iphdr)) | ||
387 | return NF_ACCEPT; | ||
388 | |||
389 | ret = nf_nat_ipv4_fn(ops, skb, in, out, do_chain); | ||
390 | #ifdef CONFIG_XFRM | ||
391 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
392 | !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && | ||
393 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
394 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
395 | |||
396 | if ((ct->tuplehash[dir].tuple.src.u3.ip != | ||
397 | ct->tuplehash[!dir].tuple.dst.u3.ip) || | ||
398 | (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP && | ||
399 | ct->tuplehash[dir].tuple.src.u.all != | ||
400 | ct->tuplehash[!dir].tuple.dst.u.all)) { | ||
401 | err = nf_xfrm_me_harder(skb, AF_INET); | ||
402 | if (err < 0) | ||
403 | ret = NF_DROP_ERR(err); | ||
404 | } | ||
405 | } | ||
406 | #endif | ||
407 | return ret; | ||
408 | } | ||
409 | EXPORT_SYMBOL_GPL(nf_nat_ipv4_out); | ||
410 | |||
411 | unsigned int | ||
412 | nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, | ||
413 | const struct net_device *in, const struct net_device *out, | ||
414 | unsigned int (*do_chain)(const struct nf_hook_ops *ops, | ||
415 | struct sk_buff *skb, | ||
416 | const struct net_device *in, | ||
417 | const struct net_device *out, | ||
418 | struct nf_conn *ct)) | ||
419 | { | ||
420 | const struct nf_conn *ct; | ||
421 | enum ip_conntrack_info ctinfo; | ||
422 | unsigned int ret; | ||
423 | int err; | ||
424 | |||
425 | /* root is playing with raw sockets. */ | ||
426 | if (skb->len < sizeof(struct iphdr) || | ||
427 | ip_hdrlen(skb) < sizeof(struct iphdr)) | ||
428 | return NF_ACCEPT; | ||
429 | |||
430 | ret = nf_nat_ipv4_fn(ops, skb, in, out, do_chain); | ||
431 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
432 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
433 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
434 | |||
435 | if (ct->tuplehash[dir].tuple.dst.u3.ip != | ||
436 | ct->tuplehash[!dir].tuple.src.u3.ip) { | ||
437 | err = ip_route_me_harder(skb, RTN_UNSPEC); | ||
438 | if (err < 0) | ||
439 | ret = NF_DROP_ERR(err); | ||
440 | } | ||
441 | #ifdef CONFIG_XFRM | ||
442 | else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && | ||
443 | ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP && | ||
444 | ct->tuplehash[dir].tuple.dst.u.all != | ||
445 | ct->tuplehash[!dir].tuple.src.u.all) { | ||
446 | err = nf_xfrm_me_harder(skb, AF_INET); | ||
447 | if (err < 0) | ||
448 | ret = NF_DROP_ERR(err); | ||
449 | } | ||
450 | #endif | ||
451 | } | ||
452 | return ret; | ||
453 | } | ||
454 | EXPORT_SYMBOL_GPL(nf_nat_ipv4_local_fn); | ||
455 | |||
257 | static int __init nf_nat_l3proto_ipv4_init(void) | 456 | static int __init nf_nat_l3proto_ipv4_init(void) |
258 | { | 457 | { |
259 | int err; | 458 | int err; |
diff --git a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c new file mode 100644 index 000000000000..c6eb42100e9a --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c | |||
@@ -0,0 +1,153 @@ | |||
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>"); | ||
diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index 3964157d826c..df547bf50078 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c | |||
@@ -26,136 +26,53 @@ | |||
26 | #include <net/netfilter/nf_nat_l3proto.h> | 26 | #include <net/netfilter/nf_nat_l3proto.h> |
27 | #include <net/ip.h> | 27 | #include <net/ip.h> |
28 | 28 | ||
29 | /* | 29 | static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops, |
30 | * NAT chains | 30 | struct sk_buff *skb, |
31 | */ | 31 | const struct net_device *in, |
32 | 32 | const struct net_device *out, | |
33 | static unsigned int nf_nat_fn(const struct nf_hook_ops *ops, | 33 | struct nf_conn *ct) |
34 | struct sk_buff *skb, | ||
35 | const struct net_device *in, | ||
36 | const struct net_device *out, | ||
37 | int (*okfn)(struct sk_buff *)) | ||
38 | { | 34 | { |
39 | enum ip_conntrack_info ctinfo; | ||
40 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
41 | struct nf_conn_nat *nat; | ||
42 | enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum); | ||
43 | struct nft_pktinfo pkt; | 35 | struct nft_pktinfo pkt; |
44 | unsigned int ret; | ||
45 | |||
46 | if (ct == NULL || nf_ct_is_untracked(ct)) | ||
47 | return NF_ACCEPT; | ||
48 | |||
49 | NF_CT_ASSERT(!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET))); | ||
50 | |||
51 | nat = nf_ct_nat_ext_add(ct); | ||
52 | if (nat == NULL) | ||
53 | return NF_ACCEPT; | ||
54 | |||
55 | switch (ctinfo) { | ||
56 | case IP_CT_RELATED: | ||
57 | case IP_CT_RELATED + IP_CT_IS_REPLY: | ||
58 | if (ip_hdr(skb)->protocol == IPPROTO_ICMP) { | ||
59 | if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, | ||
60 | ops->hooknum)) | ||
61 | return NF_DROP; | ||
62 | else | ||
63 | return NF_ACCEPT; | ||
64 | } | ||
65 | /* Fall through */ | ||
66 | case IP_CT_NEW: | ||
67 | if (nf_nat_initialized(ct, maniptype)) | ||
68 | break; | ||
69 | 36 | ||
70 | nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); | 37 | nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); |
71 | 38 | ||
72 | ret = nft_do_chain(&pkt, ops); | 39 | return nft_do_chain(&pkt, ops); |
73 | if (ret != NF_ACCEPT) | ||
74 | return ret; | ||
75 | if (!nf_nat_initialized(ct, maniptype)) { | ||
76 | ret = nf_nat_alloc_null_binding(ct, ops->hooknum); | ||
77 | if (ret != NF_ACCEPT) | ||
78 | return ret; | ||
79 | } | ||
80 | default: | ||
81 | break; | ||
82 | } | ||
83 | |||
84 | return nf_nat_packet(ct, ctinfo, ops->hooknum, skb); | ||
85 | } | 40 | } |
86 | 41 | ||
87 | static unsigned int nf_nat_prerouting(const struct nf_hook_ops *ops, | 42 | static unsigned int nft_nat_ipv4_fn(const struct nf_hook_ops *ops, |
88 | struct sk_buff *skb, | 43 | struct sk_buff *skb, |
89 | const struct net_device *in, | 44 | const struct net_device *in, |
90 | const struct net_device *out, | 45 | const struct net_device *out, |
91 | int (*okfn)(struct sk_buff *)) | 46 | int (*okfn)(struct sk_buff *)) |
92 | { | 47 | { |
93 | __be32 daddr = ip_hdr(skb)->daddr; | 48 | return nf_nat_ipv4_fn(ops, skb, in, out, nft_nat_do_chain); |
94 | unsigned int ret; | ||
95 | |||
96 | ret = nf_nat_fn(ops, skb, in, out, okfn); | ||
97 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
98 | ip_hdr(skb)->daddr != daddr) { | ||
99 | skb_dst_drop(skb); | ||
100 | } | ||
101 | return ret; | ||
102 | } | 49 | } |
103 | 50 | ||
104 | static unsigned int nf_nat_postrouting(const struct nf_hook_ops *ops, | 51 | static unsigned int nft_nat_ipv4_in(const struct nf_hook_ops *ops, |
105 | struct sk_buff *skb, | 52 | struct sk_buff *skb, |
106 | const struct net_device *in, | 53 | const struct net_device *in, |
107 | const struct net_device *out, | 54 | const struct net_device *out, |
108 | int (*okfn)(struct sk_buff *)) | 55 | int (*okfn)(struct sk_buff *)) |
109 | { | 56 | { |
110 | enum ip_conntrack_info ctinfo __maybe_unused; | 57 | return nf_nat_ipv4_in(ops, skb, in, out, nft_nat_do_chain); |
111 | const struct nf_conn *ct __maybe_unused; | ||
112 | unsigned int ret; | ||
113 | |||
114 | ret = nf_nat_fn(ops, skb, in, out, okfn); | ||
115 | #ifdef CONFIG_XFRM | ||
116 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
117 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
118 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
119 | |||
120 | if (ct->tuplehash[dir].tuple.src.u3.ip != | ||
121 | ct->tuplehash[!dir].tuple.dst.u3.ip || | ||
122 | ct->tuplehash[dir].tuple.src.u.all != | ||
123 | ct->tuplehash[!dir].tuple.dst.u.all) | ||
124 | return nf_xfrm_me_harder(skb, AF_INET) == 0 ? | ||
125 | ret : NF_DROP; | ||
126 | } | ||
127 | #endif | ||
128 | return ret; | ||
129 | } | 58 | } |
130 | 59 | ||
131 | static unsigned int nf_nat_output(const struct nf_hook_ops *ops, | 60 | static unsigned int nft_nat_ipv4_out(const struct nf_hook_ops *ops, |
132 | struct sk_buff *skb, | 61 | struct sk_buff *skb, |
133 | const struct net_device *in, | 62 | const struct net_device *in, |
134 | const struct net_device *out, | 63 | const struct net_device *out, |
135 | int (*okfn)(struct sk_buff *)) | 64 | int (*okfn)(struct sk_buff *)) |
136 | { | 65 | { |
137 | enum ip_conntrack_info ctinfo; | 66 | return nf_nat_ipv4_out(ops, skb, in, out, nft_nat_do_chain); |
138 | const struct nf_conn *ct; | 67 | } |
139 | unsigned int ret; | ||
140 | |||
141 | ret = nf_nat_fn(ops, skb, in, out, okfn); | ||
142 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
143 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
144 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
145 | 68 | ||
146 | if (ct->tuplehash[dir].tuple.dst.u3.ip != | 69 | static unsigned int nft_nat_ipv4_local_fn(const struct nf_hook_ops *ops, |
147 | ct->tuplehash[!dir].tuple.src.u3.ip) { | 70 | struct sk_buff *skb, |
148 | if (ip_route_me_harder(skb, RTN_UNSPEC)) | 71 | const struct net_device *in, |
149 | ret = NF_DROP; | 72 | const struct net_device *out, |
150 | } | 73 | int (*okfn)(struct sk_buff *)) |
151 | #ifdef CONFIG_XFRM | 74 | { |
152 | else if (ct->tuplehash[dir].tuple.dst.u.all != | 75 | return nf_nat_ipv4_local_fn(ops, skb, in, out, nft_nat_do_chain); |
153 | ct->tuplehash[!dir].tuple.src.u.all) | ||
154 | if (nf_xfrm_me_harder(skb, AF_INET)) | ||
155 | ret = NF_DROP; | ||
156 | #endif | ||
157 | } | ||
158 | return ret; | ||
159 | } | 76 | } |
160 | 77 | ||
161 | static const struct nf_chain_type nft_chain_nat_ipv4 = { | 78 | static const struct nf_chain_type nft_chain_nat_ipv4 = { |
@@ -168,10 +85,10 @@ static const struct nf_chain_type nft_chain_nat_ipv4 = { | |||
168 | (1 << NF_INET_LOCAL_OUT) | | 85 | (1 << NF_INET_LOCAL_OUT) | |
169 | (1 << NF_INET_LOCAL_IN), | 86 | (1 << NF_INET_LOCAL_IN), |
170 | .hooks = { | 87 | .hooks = { |
171 | [NF_INET_PRE_ROUTING] = nf_nat_prerouting, | 88 | [NF_INET_PRE_ROUTING] = nft_nat_ipv4_in, |
172 | [NF_INET_POST_ROUTING] = nf_nat_postrouting, | 89 | [NF_INET_POST_ROUTING] = nft_nat_ipv4_out, |
173 | [NF_INET_LOCAL_OUT] = nf_nat_output, | 90 | [NF_INET_LOCAL_OUT] = nft_nat_ipv4_local_fn, |
174 | [NF_INET_LOCAL_IN] = nf_nat_fn, | 91 | [NF_INET_LOCAL_IN] = nft_nat_ipv4_fn, |
175 | }, | 92 | }, |
176 | }; | 93 | }; |
177 | 94 | ||
diff --git a/net/ipv4/netfilter/nft_masq_ipv4.c b/net/ipv4/netfilter/nft_masq_ipv4.c new file mode 100644 index 000000000000..6ea1d207b6a5 --- /dev/null +++ b/net/ipv4/netfilter/nft_masq_ipv4.c | |||
@@ -0,0 +1,89 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com> | ||
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/kernel.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/netlink.h> | ||
13 | #include <linux/netfilter.h> | ||
14 | #include <linux/netfilter/nf_tables.h> | ||
15 | #include <net/netfilter/nf_tables.h> | ||
16 | #include <net/netfilter/nft_masq.h> | ||
17 | #include <net/netfilter/ipv4/nf_nat_masquerade.h> | ||
18 | |||
19 | static void nft_masq_ipv4_eval(const struct nft_expr *expr, | ||
20 | struct nft_data data[NFT_REG_MAX + 1], | ||
21 | const struct nft_pktinfo *pkt) | ||
22 | { | ||
23 | struct nft_masq *priv = nft_expr_priv(expr); | ||
24 | struct nf_nat_range range; | ||
25 | unsigned int verdict; | ||
26 | |||
27 | range.flags = priv->flags; | ||
28 | |||
29 | verdict = nf_nat_masquerade_ipv4(pkt->skb, pkt->ops->hooknum, | ||
30 | &range, pkt->out); | ||
31 | |||
32 | data[NFT_REG_VERDICT].verdict = verdict; | ||
33 | } | ||
34 | |||
35 | static int nft_masq_ipv4_init(const struct nft_ctx *ctx, | ||
36 | const struct nft_expr *expr, | ||
37 | const struct nlattr * const tb[]) | ||
38 | { | ||
39 | int err; | ||
40 | |||
41 | err = nft_masq_init(ctx, expr, tb); | ||
42 | if (err < 0) | ||
43 | return err; | ||
44 | |||
45 | nf_nat_masquerade_ipv4_register_notifier(); | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static void nft_masq_ipv4_destroy(const struct nft_ctx *ctx, | ||
50 | const struct nft_expr *expr) | ||
51 | { | ||
52 | nf_nat_masquerade_ipv4_unregister_notifier(); | ||
53 | } | ||
54 | |||
55 | static struct nft_expr_type nft_masq_ipv4_type; | ||
56 | static const struct nft_expr_ops nft_masq_ipv4_ops = { | ||
57 | .type = &nft_masq_ipv4_type, | ||
58 | .size = NFT_EXPR_SIZE(sizeof(struct nft_masq)), | ||
59 | .eval = nft_masq_ipv4_eval, | ||
60 | .init = nft_masq_ipv4_init, | ||
61 | .destroy = nft_masq_ipv4_destroy, | ||
62 | .dump = nft_masq_dump, | ||
63 | }; | ||
64 | |||
65 | static struct nft_expr_type nft_masq_ipv4_type __read_mostly = { | ||
66 | .family = NFPROTO_IPV4, | ||
67 | .name = "masq", | ||
68 | .ops = &nft_masq_ipv4_ops, | ||
69 | .policy = nft_masq_policy, | ||
70 | .maxattr = NFTA_MASQ_MAX, | ||
71 | .owner = THIS_MODULE, | ||
72 | }; | ||
73 | |||
74 | static int __init nft_masq_ipv4_module_init(void) | ||
75 | { | ||
76 | return nft_register_expr(&nft_masq_ipv4_type); | ||
77 | } | ||
78 | |||
79 | static void __exit nft_masq_ipv4_module_exit(void) | ||
80 | { | ||
81 | nft_unregister_expr(&nft_masq_ipv4_type); | ||
82 | } | ||
83 | |||
84 | module_init(nft_masq_ipv4_module_init); | ||
85 | module_exit(nft_masq_ipv4_module_exit); | ||
86 | |||
87 | MODULE_LICENSE("GPL"); | ||
88 | MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>"); | ||
89 | MODULE_ALIAS_NFT_AF_EXPR(AF_INET, "masq"); | ||