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