1 /* This is a file to handle the "simple" NAT cases (redirect and
2 masquerade) required for the compatibility layer.
4 `bind to foreign address' and `getpeername' hacks are not
7 FIXME: Timing is overly simplistic. If anyone complains, make it
11 /* (C) 1999-2001 Paul `Rusty' Russell
12 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License version 2 as
16 * published by the Free Software Foundation.
19 #include <linux/config.h>
20 #include <linux/netfilter.h>
22 #include <linux/udp.h>
23 #include <linux/tcp.h>
24 #include <net/checksum.h>
26 #include <linux/timer.h>
27 #include <linux/netdevice.h>
31 #include <linux/netfilter_ipv4/lockhelp.h>
33 /* Very simple timeout pushed back by each packet */
34 #define REDIR_TIMEOUT (240*HZ)
36 static DECLARE_LOCK(redir_lock
);
37 #define ASSERT_READ_LOCK(x) MUST_BE_LOCKED(&redir_lock)
38 #define ASSERT_WRITE_LOCK(x) MUST_BE_LOCKED(&redir_lock)
40 #include <linux/netfilter_ipv4/listhelp.h>
41 #include "ip_fw_compat.h"
46 #define DEBUGP(format, args...)
49 #ifdef CONFIG_NETFILTER_DEBUG
50 #define IP_NF_ASSERT(x) \
53 /* Wooah! I'm tripping my conntrack in a frenzy of \
55 printk("ASSERT: %s:%i(%s)\n", \
56 __FILE__, __LINE__, __FUNCTION__); \
59 #define IP_NF_ASSERT(x)
63 cheat_check(u_int32_t oldvalinv
, u_int32_t newval
, u_int16_t oldcheck
)
65 u_int32_t diffs
[] = { oldvalinv
, newval
};
66 return csum_fold(csum_partial((char *)diffs
, sizeof(diffs
),
71 u_int32_t orig_srcip
, orig_dstip
;
72 u_int16_t orig_sport
, orig_dport
;
80 struct list_head list
;
81 struct redir_core core
;
82 struct timer_list destroyme
;
85 static LIST_HEAD(redirs
);
88 redir_cmp(const struct redir
*i
,
89 u_int32_t orig_srcip
, u_int32_t orig_dstip
,
90 u_int16_t orig_sport
, u_int16_t orig_dport
)
92 return (i
->core
.orig_srcip
== orig_srcip
93 && i
->core
.orig_dstip
== orig_dstip
94 && i
->core
.orig_sport
== orig_sport
95 && i
->core
.orig_dport
== orig_dport
);
98 /* Search for an existing redirection of the TCP packet. */
100 find_redir(u_int32_t orig_srcip
, u_int32_t orig_dstip
,
101 u_int16_t orig_sport
, u_int16_t orig_dport
)
103 return LIST_FIND(&redirs
, redir_cmp
, struct redir
*,
104 orig_srcip
, orig_dstip
, orig_sport
, orig_dport
);
107 static void do_tcp_redir(struct sk_buff
*skb
, struct redir
*redir
)
109 struct iphdr
*iph
= skb
->nh
.iph
;
110 struct tcphdr
*tcph
= (struct tcphdr
*)((u_int32_t
*)iph
113 tcph
->check
= cheat_check(~redir
->core
.orig_dstip
,
114 redir
->core
.new_dstip
,
115 cheat_check(redir
->core
.orig_dport
^ 0xFFFF,
116 redir
->core
.new_dport
,
118 iph
->check
= cheat_check(~redir
->core
.orig_dstip
,
119 redir
->core
.new_dstip
, iph
->check
);
120 tcph
->dest
= redir
->core
.new_dport
;
121 iph
->daddr
= redir
->core
.new_dstip
;
123 skb
->nfcache
|= NFC_ALTERED
;
127 unredir_cmp(const struct redir
*i
,
128 u_int32_t new_dstip
, u_int32_t orig_srcip
,
129 u_int16_t new_dport
, u_int16_t orig_sport
)
131 return (i
->core
.orig_srcip
== orig_srcip
132 && i
->core
.new_dstip
== new_dstip
133 && i
->core
.orig_sport
== orig_sport
134 && i
->core
.new_dport
== new_dport
);
137 /* Match reply packet against redir */
138 static struct redir
*
139 find_unredir(u_int32_t new_dstip
, u_int32_t orig_srcip
,
140 u_int16_t new_dport
, u_int16_t orig_sport
)
142 return LIST_FIND(&redirs
, unredir_cmp
, struct redir
*,
143 new_dstip
, orig_srcip
, new_dport
, orig_sport
);
146 /* `unredir' a reply packet. */
147 static void do_tcp_unredir(struct sk_buff
*skb
, struct redir
*redir
)
149 struct iphdr
*iph
= skb
->nh
.iph
;
150 struct tcphdr
*tcph
= (struct tcphdr
*)((u_int32_t
*)iph
153 tcph
->check
= cheat_check(~redir
->core
.new_dstip
,
154 redir
->core
.orig_dstip
,
155 cheat_check(redir
->core
.new_dport
^ 0xFFFF,
156 redir
->core
.orig_dport
,
158 iph
->check
= cheat_check(~redir
->core
.new_dstip
,
159 redir
->core
.orig_dstip
,
161 tcph
->source
= redir
->core
.orig_dport
;
162 iph
->saddr
= redir
->core
.orig_dstip
;
164 skb
->nfcache
|= NFC_ALTERED
;
167 static void destroyme(unsigned long me
)
169 LOCK_BH(&redir_lock
);
170 LIST_DELETE(&redirs
, (struct redir
*)me
);
171 UNLOCK_BH(&redir_lock
);
172 kfree((struct redir
*)me
);
175 /* REDIRECT a packet. */
177 do_redirect(struct sk_buff
*skb
,
178 const struct net_device
*dev
,
181 struct iphdr
*iph
= skb
->nh
.iph
;
184 /* Figure out address: not loopback. */
188 /* Grab first address on interface. */
189 newdst
= ((struct in_device
*)dev
->ip_ptr
)->ifa_list
->ifa_local
;
191 switch (iph
->protocol
) {
194 struct udphdr
*udph
= (struct udphdr
*)((u_int32_t
*)iph
197 /* Must have whole header */
198 if (skb
->len
< iph
->ihl
*4 + sizeof(*udph
))
201 if (udph
->check
) /* 0 is a special case meaning no checksum */
202 udph
->check
= cheat_check(~iph
->daddr
, newdst
,
203 cheat_check(udph
->dest
^ 0xFFFF,
206 iph
->check
= cheat_check(~iph
->daddr
, newdst
, iph
->check
);
207 udph
->dest
= redirpt
;
210 skb
->nfcache
|= NFC_ALTERED
;
214 /* Mangle, maybe record. */
215 struct tcphdr
*tcph
= (struct tcphdr
*)((u_int32_t
*)iph
220 /* Must have whole header */
221 if (skb
->len
< iph
->ihl
*4 + sizeof(*tcph
))
224 DEBUGP("Doing tcp redirect. %08X:%u %08X:%u -> %08X:%u\n",
225 iph
->saddr
, tcph
->source
, iph
->daddr
, tcph
->dest
,
227 LOCK_BH(&redir_lock
);
228 redir
= find_redir(iph
->saddr
, iph
->daddr
,
229 tcph
->source
, tcph
->dest
);
232 redir
= kmalloc(sizeof(struct redir
), GFP_ATOMIC
);
237 list_prepend(&redirs
, redir
);
238 init_timer(&redir
->destroyme
);
239 redir
->destroyme
.function
= destroyme
;
240 redir
->destroyme
.data
= (unsigned long)redir
;
241 redir
->destroyme
.expires
= jiffies
+ REDIR_TIMEOUT
;
242 add_timer(&redir
->destroyme
);
244 /* In case mangling has changed, rewrite this part. */
245 redir
->core
= ((struct redir_core
)
246 { iph
->saddr
, iph
->daddr
,
247 tcph
->source
, tcph
->dest
,
249 do_tcp_redir(skb
, redir
);
253 UNLOCK_BH(&redir_lock
);
257 default: /* give up if not TCP or UDP. */
262 /* Incoming packet: is it a reply to a masqueraded connection, or
263 part of an already-redirected TCP connection? */
265 check_for_redirect(struct sk_buff
*skb
)
267 struct iphdr
*iph
= skb
->nh
.iph
;
268 struct tcphdr
*tcph
= (struct tcphdr
*)((u_int32_t
*)iph
272 if (iph
->protocol
!= IPPROTO_TCP
)
275 /* Must have whole header */
276 if (skb
->len
< iph
->ihl
*4 + sizeof(*tcph
))
279 LOCK_BH(&redir_lock
);
280 redir
= find_redir(iph
->saddr
, iph
->daddr
, tcph
->source
, tcph
->dest
);
282 DEBUGP("Doing tcp redirect again.\n");
283 do_tcp_redir(skb
, redir
);
284 if (del_timer(&redir
->destroyme
)) {
285 redir
->destroyme
.expires
= jiffies
+ REDIR_TIMEOUT
;
286 add_timer(&redir
->destroyme
);
289 UNLOCK_BH(&redir_lock
);
293 check_for_unredirect(struct sk_buff
*skb
)
295 struct iphdr
*iph
= skb
->nh
.iph
;
296 struct tcphdr
*tcph
= (struct tcphdr
*)((u_int32_t
*)iph
300 if (iph
->protocol
!= IPPROTO_TCP
)
303 /* Must have whole header */
304 if (skb
->len
< iph
->ihl
*4 + sizeof(*tcph
))
307 LOCK_BH(&redir_lock
);
308 redir
= find_unredir(iph
->saddr
, iph
->daddr
, tcph
->source
, tcph
->dest
);
310 DEBUGP("Doing tcp unredirect.\n");
311 do_tcp_unredir(skb
, redir
);
312 if (del_timer(&redir
->destroyme
)) {
313 redir
->destroyme
.expires
= jiffies
+ REDIR_TIMEOUT
;
314 add_timer(&redir
->destroyme
);
317 UNLOCK_BH(&redir_lock
);