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>
25 #include <linux/timer.h>
26 #include <linux/netdevice.h>
30 #include <linux/netfilter_ipv4/lockhelp.h>
32 /* Very simple timeout pushed back by each packet */
33 #define REDIR_TIMEOUT (240*HZ)
35 static DECLARE_LOCK(redir_lock);
36 #define ASSERT_READ_LOCK(x) MUST_BE_LOCKED(&redir_lock)
37 #define ASSERT_WRITE_LOCK(x) MUST_BE_LOCKED(&redir_lock)
39 #include <linux/netfilter_ipv4/listhelp.h>
40 #include "ip_fw_compat.h"
45 #define DEBUGP(format, args...)
48 #ifdef CONFIG_NETFILTER_DEBUG
49 #define IP_NF_ASSERT(x) \
52 /* Wooah! I'm tripping my conntrack in a frenzy of \
54 printk("ASSERT: %s:%i(%s)\n", \
55 __FILE__, __LINE__, __FUNCTION__); \
58 #define IP_NF_ASSERT(x)
62 cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck)
64 u_int32_t diffs[] = { oldvalinv, newval };
65 return csum_fold(csum_partial((char *)diffs, sizeof(diffs),
70 u_int32_t orig_srcip, orig_dstip;
71 u_int16_t orig_sport, orig_dport;
79 struct list_head list;
80 struct redir_core core;
81 struct timer_list destroyme;
84 static LIST_HEAD(redirs);
87 redir_cmp(const struct redir *i,
88 u_int32_t orig_srcip, u_int32_t orig_dstip,
89 u_int16_t orig_sport, u_int16_t orig_dport)
91 return (i->core.orig_srcip == orig_srcip
92 && i->core.orig_dstip == orig_dstip
93 && i->core.orig_sport == orig_sport
94 && i->core.orig_dport == orig_dport);
97 /* Search for an existing redirection of the TCP packet. */
99 find_redir(u_int32_t orig_srcip, u_int32_t orig_dstip,
100 u_int16_t orig_sport, u_int16_t orig_dport)
102 return LIST_FIND(&redirs, redir_cmp, struct redir *,
103 orig_srcip, orig_dstip, orig_sport, orig_dport);
106 static void do_tcp_redir(struct sk_buff *skb, struct redir *redir)
108 struct iphdr *iph = skb->nh.iph;
109 struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
112 tcph->check = cheat_check(~redir->core.orig_dstip,
113 redir->core.new_dstip,
114 cheat_check(redir->core.orig_dport ^ 0xFFFF,
115 redir->core.new_dport,
117 iph->check = cheat_check(~redir->core.orig_dstip,
118 redir->core.new_dstip, iph->check);
119 tcph->dest = redir->core.new_dport;
120 iph->daddr = redir->core.new_dstip;
122 skb->nfcache |= NFC_ALTERED;
126 unredir_cmp(const struct redir *i,
127 u_int32_t new_dstip, u_int32_t orig_srcip,
128 u_int16_t new_dport, u_int16_t orig_sport)
130 return (i->core.orig_srcip == orig_srcip
131 && i->core.new_dstip == new_dstip
132 && i->core.orig_sport == orig_sport
133 && i->core.new_dport == new_dport);
136 /* Match reply packet against redir */
137 static struct redir *
138 find_unredir(u_int32_t new_dstip, u_int32_t orig_srcip,
139 u_int16_t new_dport, u_int16_t orig_sport)
141 return LIST_FIND(&redirs, unredir_cmp, struct redir *,
142 new_dstip, orig_srcip, new_dport, orig_sport);
145 /* `unredir' a reply packet. */
146 static void do_tcp_unredir(struct sk_buff *skb, struct redir *redir)
148 struct iphdr *iph = skb->nh.iph;
149 struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
152 tcph->check = cheat_check(~redir->core.new_dstip,
153 redir->core.orig_dstip,
154 cheat_check(redir->core.new_dport ^ 0xFFFF,
155 redir->core.orig_dport,
157 iph->check = cheat_check(~redir->core.new_dstip,
158 redir->core.orig_dstip,
160 tcph->source = redir->core.orig_dport;
161 iph->saddr = redir->core.orig_dstip;
163 skb->nfcache |= NFC_ALTERED;
166 static void destroyme(unsigned long me)
168 LOCK_BH(&redir_lock);
169 LIST_DELETE(&redirs, (struct redir *)me);
170 UNLOCK_BH(&redir_lock);
171 kfree((struct redir *)me);
174 /* REDIRECT a packet. */
176 do_redirect(struct sk_buff *skb,
177 const struct net_device *dev,
180 struct iphdr *iph = skb->nh.iph;
183 /* Figure out address: not loopback. */
187 /* Grab first address on interface. */
188 newdst = ((struct in_device *)dev->ip_ptr)->ifa_list->ifa_local;
190 switch (iph->protocol) {
193 struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph
196 /* Must have whole header */
197 if (skb->len < iph->ihl*4 + sizeof(*udph))
200 if (udph->check) /* 0 is a special case meaning no checksum */
201 udph->check = cheat_check(~iph->daddr, newdst,
202 cheat_check(udph->dest ^ 0xFFFF,
205 iph->check = cheat_check(~iph->daddr, newdst, iph->check);
206 udph->dest = redirpt;
209 skb->nfcache |= NFC_ALTERED;
213 /* Mangle, maybe record. */
214 struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
219 /* Must have whole header */
220 if (skb->len < iph->ihl*4 + sizeof(*tcph))
223 DEBUGP("Doing tcp redirect. %08X:%u %08X:%u -> %08X:%u\n",
224 iph->saddr, tcph->source, iph->daddr, tcph->dest,
226 LOCK_BH(&redir_lock);
227 redir = find_redir(iph->saddr, iph->daddr,
228 tcph->source, tcph->dest);
231 redir = kmalloc(sizeof(struct redir), GFP_ATOMIC);
236 list_prepend(&redirs, redir);
237 init_timer(&redir->destroyme);
238 redir->destroyme.function = destroyme;
239 redir->destroyme.data = (unsigned long)redir;
240 redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
241 add_timer(&redir->destroyme);
243 /* In case mangling has changed, rewrite this part. */
244 redir->core = ((struct redir_core)
245 { iph->saddr, iph->daddr,
246 tcph->source, tcph->dest,
248 do_tcp_redir(skb, redir);
252 UNLOCK_BH(&redir_lock);
256 default: /* give up if not TCP or UDP. */
261 /* Incoming packet: is it a reply to a masqueraded connection, or
262 part of an already-redirected TCP connection? */
264 check_for_redirect(struct sk_buff *skb)
266 struct iphdr *iph = skb->nh.iph;
267 struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
271 if (iph->protocol != IPPROTO_TCP)
274 /* Must have whole header */
275 if (skb->len < iph->ihl*4 + sizeof(*tcph))
278 LOCK_BH(&redir_lock);
279 redir = find_redir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
281 DEBUGP("Doing tcp redirect again.\n");
282 do_tcp_redir(skb, redir);
283 if (del_timer(&redir->destroyme)) {
284 redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
285 add_timer(&redir->destroyme);
288 UNLOCK_BH(&redir_lock);
292 check_for_unredirect(struct sk_buff *skb)
294 struct iphdr *iph = skb->nh.iph;
295 struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
299 if (iph->protocol != IPPROTO_TCP)
302 /* Must have whole header */
303 if (skb->len < iph->ihl*4 + sizeof(*tcph))
306 LOCK_BH(&redir_lock);
307 redir = find_unredir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
309 DEBUGP("Doing tcp unredirect.\n");
310 do_tcp_unredir(skb, redir);
311 if (del_timer(&redir->destroyme)) {
312 redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
313 add_timer(&redir->destroyme);
316 UNLOCK_BH(&redir_lock);