ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / net / ipv4 / netfilter / ip_fw_compat_masq.c
1 /* Masquerading compatibility layer.
2
3    Note that there are no restrictions on other programs binding to
4    ports 61000:65095 (in 2.0 and 2.2 they get EADDRINUSE).  Just DON'T
5    DO IT.
6  */
7
8 /* (C) 1999-2001 Paul `Rusty' Russell
9  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14  */
15
16 #include <linux/skbuff.h>
17 #include <linux/in.h>
18 #include <linux/ip.h>
19 #include <linux/icmp.h>
20 #include <linux/udp.h>
21 #include <linux/netfilter_ipv4.h>
22 #include <linux/netdevice.h>
23 #include <linux/inetdevice.h>
24 #include <linux/proc_fs.h>
25 #include <linux/module.h>
26 #include <net/route.h>
27
28 #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
29 #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock)
30
31 #include <linux/netfilter_ipv4/ip_conntrack.h>
32 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
33 #include <linux/netfilter_ipv4/ip_nat.h>
34 #include <linux/netfilter_ipv4/ip_nat_core.h>
35 #include <linux/netfilter_ipv4/listhelp.h>
36 #include "ip_fw_compat.h"
37
38 #if 0
39 #define DEBUGP printk
40 #else
41 #define DEBUGP(format, args...)
42 #endif
43
44 unsigned int
45 do_masquerade(struct sk_buff **pskb, const struct net_device *dev)
46 {
47         struct ip_nat_info *info;
48         enum ip_conntrack_info ctinfo;
49         struct ip_conntrack *ct;
50         unsigned int ret;
51
52         /* Sorry, only ICMP, TCP and UDP. */
53         if ((*pskb)->nh.iph->protocol != IPPROTO_ICMP
54             && (*pskb)->nh.iph->protocol != IPPROTO_TCP
55             && (*pskb)->nh.iph->protocol != IPPROTO_UDP)
56                 return NF_DROP;
57
58         /* Feed it to connection tracking; in fact we're in NF_IP_FORWARD,
59            but connection tracking doesn't expect that */
60         ret = ip_conntrack_in(NF_IP_POST_ROUTING, pskb, dev, NULL, NULL);
61         if (ret != NF_ACCEPT) {
62                 DEBUGP("ip_conntrack_in returned %u.\n", ret);
63                 return ret;
64         }
65
66         ct = ip_conntrack_get(*pskb, &ctinfo);
67
68         if (!ct) {
69                 DEBUGP("ip_conntrack_in set to invalid conntrack.\n");
70                 return NF_DROP;
71         }
72
73         info = &ct->nat.info;
74
75         WRITE_LOCK(&ip_nat_lock);
76         /* Setup the masquerade, if not already */
77         if (!info->initialized) {
78                 u_int32_t newsrc;
79                 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = (*pskb)->nh.iph->daddr } } };
80                 struct rtable *rt;
81                 struct ip_nat_multi_range range;
82
83                 /* Pass 0 instead of saddr, since it's going to be changed
84                    anyway. */
85                 if (ip_route_output_key(&rt, &fl) != 0) {
86                         DEBUGP("ipnat_rule_masquerade: Can't reroute.\n");
87                         return NF_DROP;
88                 }
89                 newsrc = inet_select_addr(rt->u.dst.dev, rt->rt_gateway,
90                                           RT_SCOPE_UNIVERSE);
91                 ip_rt_put(rt);
92                 range = ((struct ip_nat_multi_range)
93                          { 1,
94                            {{IP_NAT_RANGE_MAP_IPS|IP_NAT_RANGE_PROTO_SPECIFIED,
95                              newsrc, newsrc,
96                              { htons(61000) }, { htons(65095) } } } });
97
98                 ret = ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
99                 if (ret != NF_ACCEPT) {
100                         WRITE_UNLOCK(&ip_nat_lock);
101                         return ret;
102                 }
103         } else
104                 DEBUGP("Masquerading already done on this conn.\n");
105         WRITE_UNLOCK(&ip_nat_lock);
106
107         return do_bindings(ct, ctinfo, info, NF_IP_POST_ROUTING, pskb);
108 }
109
110 void
111 check_for_masq_error(struct sk_buff **pskb)
112 {
113         enum ip_conntrack_info ctinfo;
114         struct ip_conntrack *ct;
115
116         ct = ip_conntrack_get(*pskb, &ctinfo);
117         /* Wouldn't be here if not tracked already => masq'ed ICMP
118            ping or error related to masq'd connection */
119         IP_NF_ASSERT(ct);
120         if (ctinfo == IP_CT_RELATED) {
121                 icmp_reply_translation(pskb, ct, NF_IP_PRE_ROUTING,
122                                        CTINFO2DIR(ctinfo));
123                 icmp_reply_translation(pskb, ct, NF_IP_POST_ROUTING,
124                                        CTINFO2DIR(ctinfo));
125         }
126 }
127
128 unsigned int
129 check_for_demasq(struct sk_buff **pskb)
130 {
131         struct ip_conntrack_tuple tuple;
132         struct ip_conntrack_protocol *protocol;
133         struct ip_conntrack_tuple_hash *h;
134         enum ip_conntrack_info ctinfo;
135         struct ip_conntrack *ct;
136         int ret;
137
138         protocol = ip_ct_find_proto((*pskb)->nh.iph->protocol);
139
140         /* We don't feed packets to conntrack system unless we know
141            they're part of an connection already established by an
142            explicit masq command. */
143         switch ((*pskb)->nh.iph->protocol) {
144         case IPPROTO_ICMP:
145                 /* ICMP errors. */
146                 ct = icmp_error_track(*pskb, &ctinfo, NF_IP_PRE_ROUTING);
147                 if (ct) {
148                         /* We only do SNAT in the compatibility layer.
149                            So we can manipulate ICMP errors from
150                            server here (== DNAT).  Do SNAT icmp manips
151                            in POST_ROUTING handling. */
152                         if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
153                                 icmp_reply_translation(pskb, ct,
154                                                        NF_IP_PRE_ROUTING,
155                                                        CTINFO2DIR(ctinfo));
156                                 icmp_reply_translation(pskb, ct,
157                                                        NF_IP_POST_ROUTING,
158                                                        CTINFO2DIR(ctinfo));
159                         }
160                         return NF_ACCEPT;
161                 }
162                 /* Fall thru... */
163         case IPPROTO_TCP:
164         case IPPROTO_UDP:
165                 IP_NF_ASSERT(((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
166
167                 if (!get_tuple((*pskb)->nh.iph, *pskb, (*pskb)->nh.iph->ihl*4, &tuple, protocol)) {
168                         if (net_ratelimit())
169                                 printk("ip_fw_compat_masq: Can't get tuple\n");
170                         return NF_ACCEPT;
171                 }
172                 break;
173
174         default:
175                 /* Not ours... */
176                 return NF_ACCEPT;
177         }
178         h = ip_conntrack_find_get(&tuple, NULL);
179
180         /* MUST be found, and MUST be reply. */
181         if (h && DIRECTION(h) == 1) {
182                 ret = ip_conntrack_in(NF_IP_PRE_ROUTING, pskb,
183                                       NULL, NULL, NULL);
184
185                 /* Put back the reference gained from find_get */
186                 nf_conntrack_put(&h->ctrack->infos[0]);
187                 if (ret == NF_ACCEPT) {
188                         struct ip_conntrack *ct;
189                         ct = ip_conntrack_get(*pskb, &ctinfo);
190
191                         if (ct) {
192                                 struct ip_nat_info *info = &ct->nat.info;
193
194                                 do_bindings(ct, ctinfo, info,
195                                             NF_IP_PRE_ROUTING,
196                                             pskb);
197                         } else
198                                 if (net_ratelimit()) 
199                                         printk("ip_fw_compat_masq: conntrack"
200                                                " didn't like\n");
201                 }
202         } else {
203                 if (h)
204                         /* Put back the reference gained from find_get */
205                         nf_conntrack_put(&h->ctrack->infos[0]);
206                 ret = NF_ACCEPT;
207         }
208
209         return ret;
210 }
211
212 int ip_fw_masq_timeouts(void *user, int len)
213 {
214         printk("Sorry: masquerading timeouts set 5DAYS/2MINS/60SECS\n");
215         return 0;
216 }
217
218 static const char *masq_proto_name(u_int16_t protonum)
219 {
220         switch (protonum) {
221         case IPPROTO_TCP: return "TCP";
222         case IPPROTO_UDP: return "UDP";
223         case IPPROTO_ICMP: return "ICMP";
224         default: return "MORE-CAFFEINE-FOR-RUSTY";
225         }
226 }
227
228 static unsigned int
229 print_masq(char *buffer, const struct ip_conntrack *conntrack)
230 {
231         char temp[129];
232
233         /* This is for backwards compatibility, but ick!.
234            We should never export jiffies to userspace.
235         */
236         sprintf(temp,"%s %08X:%04X %08X:%04X %04X %08X %6d %6d %7lu",
237                 masq_proto_name(conntrack->tuplehash[0].tuple.dst.protonum),
238                 ntohl(conntrack->tuplehash[0].tuple.src.ip),
239                 ntohs(conntrack->tuplehash[0].tuple.src.u.all),
240                 ntohl(conntrack->tuplehash[0].tuple.dst.ip),
241                 ntohs(conntrack->tuplehash[0].tuple.dst.u.all),
242                 ntohs(conntrack->tuplehash[1].tuple.dst.u.all),
243                 /* Sorry, no init_seq, delta or previous_delta (yet). */
244                 0, 0, 0,
245                 conntrack->timeout.expires - jiffies);
246
247         return sprintf(buffer, "%-127s\n", temp);
248 }
249
250 /* Returns true when finished. */
251 static int
252 masq_iterate(const struct ip_conntrack_tuple_hash *hash,
253              char *buffer, off_t offset, off_t *upto,
254              unsigned int *len, unsigned int maxlen)
255 {
256         unsigned int newlen;
257
258         IP_NF_ASSERT(hash->ctrack);
259
260         /* Only count originals */
261         if (DIRECTION(hash))
262                 return 0;
263
264         if ((*upto)++ < offset)
265                 return 0;
266
267         newlen = print_masq(buffer + *len, hash->ctrack);
268         if (*len + newlen > maxlen)
269                 return 1;
270         else *len += newlen;
271
272         return 0;
273 }
274
275 /* Everything in the hash is masqueraded. */
276 static int
277 masq_procinfo(char *buffer, char **start, off_t offset, int length)
278 {
279         unsigned int i;
280         int len = 0;
281         off_t upto = 1;
282
283         /* Header: first record */
284         if (offset == 0) {
285                 char temp[128];
286
287                 sprintf(temp,
288                         "Prc FromIP   FPrt ToIP     TPrt Masq Init-seq  Delta PDelta Expires (free=0,0,0)");
289                 len = sprintf(buffer, "%-127s\n", temp);
290                 offset = 1;
291         }
292
293         READ_LOCK(&ip_conntrack_lock);
294         /* Traverse hash; print originals then reply. */
295         for (i = 0; i < ip_conntrack_htable_size; i++) {
296                 if (LIST_FIND(&ip_conntrack_hash[i], masq_iterate,
297                               struct ip_conntrack_tuple_hash *,
298                               buffer, offset, &upto, &len, length))
299                         break;
300         }
301         READ_UNLOCK(&ip_conntrack_lock);
302
303         /* `start' hack - see fs/proc/generic.c line ~165 */
304         *start = (char *)((unsigned int)upto - offset);
305         return len;
306 }
307
308 int __init masq_init(void)
309 {
310         int ret;
311         struct proc_dir_entry *proc;
312
313         ret = ip_conntrack_init();
314         if (ret == 0) {
315                 ret = ip_nat_init();
316                 if (ret == 0) {
317                         proc = proc_net_create("ip_masquerade",
318                                                0, masq_procinfo);
319                         if (proc)
320                                 proc->owner = THIS_MODULE;
321                         else {
322                                 ip_nat_cleanup();
323                                 ip_conntrack_cleanup();
324                                 ret = -ENOMEM;
325                         }
326                 } else
327                         ip_conntrack_cleanup();
328         }
329
330         return ret;
331 }
332
333 void masq_cleanup(void)
334 {
335         ip_nat_cleanup();
336         ip_conntrack_cleanup();
337         proc_net_remove("ip_masquerade");
338 }