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
10 #include <linux/config.h>
11 #include <linux/netfilter.h>
13 #include <linux/udp.h>
14 #include <linux/tcp.h>
15 #include <net/checksum.h>
16 #include <linux/timer.h>
17 #include <linux/netdevice.h>
21 #include <linux/netfilter_ipv4/lockhelp.h>
23 static DECLARE_LOCK(redir_lock
);
24 #define ASSERT_READ_LOCK(x) MUST_BE_LOCKED(&redir_lock)
25 #define ASSERT_WRITE_LOCK(x) MUST_BE_LOCKED(&redir_lock)
27 #include <linux/netfilter_ipv4/listhelp.h>
32 #define DEBUGP(format, args...)
35 #ifdef CONFIG_NETFILTER_DEBUG
36 #define IP_NF_ASSERT(x) \
39 /* Wooah! I'm tripping my conntrack in a frenzy of \
41 printk("ASSERT: %s:%i(%s)\n", \
42 __FILE__, __LINE__, __FUNCTION__); \
45 #define IP_NF_ASSERT(x)
49 cheat_check(u_int32_t oldvalinv
, u_int32_t newval
, u_int16_t oldcheck
)
51 u_int32_t diffs
[] = { oldvalinv
, newval
};
52 return csum_fold(csum_partial((char *)diffs
, sizeof(diffs
),
57 u_int32_t orig_srcip
, orig_dstip
;
58 u_int16_t orig_sport
, orig_dport
;
66 struct list_head list
;
67 struct redir_core core
;
68 struct timer_list destroyme
;
71 static LIST_HEAD(redirs
);
74 redir_cmp(const struct redir
*i
,
75 u_int32_t orig_srcip
, u_int32_t orig_dstip
,
76 u_int16_t orig_sport
, u_int16_t orig_dport
)
78 return (i
->core
.orig_srcip
== orig_srcip
79 && i
->core
.orig_dstip
== orig_dstip
80 && i
->core
.orig_sport
== orig_sport
81 && i
->core
.orig_dport
== orig_dport
);
84 /* Search for an existing redirection of the TCP packet. */
86 find_redir(u_int32_t orig_srcip
, u_int32_t orig_dstip
,
87 u_int16_t orig_sport
, u_int16_t orig_dport
)
89 return LIST_FIND(&redirs
, redir_cmp
, struct redir
*,
90 orig_srcip
, orig_dstip
, orig_sport
, orig_dport
);
93 static void do_tcp_redir(struct sk_buff
*skb
, struct redir
*redir
)
95 struct iphdr
*iph
= skb
->nh
.iph
;
96 struct tcphdr
*tcph
= (struct tcphdr
*)((u_int32_t
*)iph
99 tcph
->check
= cheat_check(~redir
->core
.orig_dstip
,
100 redir
->core
.new_dstip
,
101 cheat_check(redir
->core
.orig_dport
^ 0xFFFF,
102 redir
->core
.new_dport
,
104 iph
->check
= cheat_check(~redir
->core
.orig_dstip
,
105 redir
->core
.new_dstip
, iph
->check
);
106 tcph
->dest
= redir
->core
.new_dport
;
107 iph
->daddr
= redir
->core
.new_dstip
;
109 skb
->nfcache
|= NFC_ALTERED
;
113 unredir_cmp(const struct redir
*i
,
114 u_int32_t new_dstip
, u_int32_t orig_srcip
,
115 u_int16_t new_dport
, u_int16_t orig_sport
)
117 return (i
->core
.orig_srcip
== orig_srcip
118 && i
->core
.new_dstip
== new_dstip
119 && i
->core
.orig_sport
== orig_sport
120 && i
->core
.new_dport
== new_dport
);
123 /* Match reply packet against redir */
124 static struct redir
*
125 find_unredir(u_int32_t new_dstip
, u_int32_t orig_srcip
,
126 u_int16_t new_dport
, u_int16_t orig_sport
)
128 return LIST_FIND(&redirs
, unredir_cmp
, struct redir
*,
129 new_dstip
, orig_srcip
, new_dport
, orig_sport
);
132 /* `unredir' a reply packet. */
133 static void do_tcp_unredir(struct sk_buff
*skb
, struct redir
*redir
)
135 struct iphdr
*iph
= skb
->nh
.iph
;
136 struct tcphdr
*tcph
= (struct tcphdr
*)((u_int32_t
*)iph
139 tcph
->check
= cheat_check(~redir
->core
.new_dstip
,
140 redir
->core
.orig_dstip
,
141 cheat_check(redir
->core
.new_dport
^ 0xFFFF,
142 redir
->core
.orig_dport
,
144 iph
->check
= cheat_check(~redir
->core
.new_dstip
,
145 redir
->core
.orig_dstip
,
147 tcph
->source
= redir
->core
.orig_dport
;
148 iph
->saddr
= redir
->core
.orig_dstip
;
150 skb
->nfcache
|= NFC_ALTERED
;
153 /* REDIRECT a packet. */
155 do_redirect(struct sk_buff
*skb
,
156 const struct net_device
*dev
,
159 struct iphdr
*iph
= skb
->nh
.iph
;
162 /* Figure out address: not loopback. */
166 /* Grab first address on interface. */
167 newdst
= ((struct in_device
*)dev
->ip_ptr
)->ifa_list
->ifa_local
;
169 switch (iph
->protocol
) {
172 struct udphdr
*udph
= (struct udphdr
*)((u_int32_t
*)iph
175 if (udph
->check
) /* 0 is a special case meaning no checksum */
176 udph
->check
= cheat_check(~iph
->daddr
, newdst
,
177 cheat_check(udph
->dest
^ 0xFFFF,
180 iph
->check
= cheat_check(~iph
->daddr
, newdst
, iph
->check
);
181 udph
->dest
= redirpt
;
184 skb
->nfcache
|= NFC_ALTERED
;
188 /* Mangle, maybe record. */
189 struct tcphdr
*tcph
= (struct tcphdr
*)((u_int32_t
*)iph
194 DEBUGP("Doing tcp redirect. %08X:%u %08X:%u -> %08X:%u\n",
195 iph
->saddr
, tcph
->source
, iph
->daddr
, tcph
->dest
,
197 LOCK_BH(&redir_lock
);
198 redir
= find_redir(iph
->saddr
, iph
->daddr
,
199 tcph
->source
, tcph
->dest
);
202 redir
= kmalloc(sizeof(struct redir
), GFP_ATOMIC
);
207 list_prepend(&redirs
, redir
);
208 init_timer(&redir
->destroyme
);
210 /* In case mangling has changed, rewrite this part. */
211 redir
->core
= ((struct redir_core
)
212 { iph
->saddr
, iph
->daddr
,
213 tcph
->source
, tcph
->dest
,
215 do_tcp_redir(skb
, redir
);
219 UNLOCK_BH(&redir_lock
);
223 default: /* give up if not TCP or UDP. */
228 static void destroyme(unsigned long me
)
230 LOCK_BH(&redir_lock
);
231 LIST_DELETE(&redirs
, (struct redir
*)me
);
232 UNLOCK_BH(&redir_lock
);
235 /* Incoming packet: is it a reply to a masqueraded connection, or
236 part of an already-redirected TCP connection? */
238 check_for_redirect(struct sk_buff
*skb
)
240 struct iphdr
*iph
= skb
->nh
.iph
;
241 struct tcphdr
*tcph
= (struct tcphdr
*)((u_int32_t
*)iph
245 if (iph
->protocol
!= IPPROTO_TCP
)
248 LOCK_BH(&redir_lock
);
249 redir
= find_redir(iph
->saddr
, iph
->daddr
, tcph
->source
, tcph
->dest
);
251 DEBUGP("Doing tcp redirect again.\n");
252 do_tcp_redir(skb
, redir
);
253 if (tcph
->rst
|| tcph
->fin
) {
254 redir
->destroyme
.function
= destroyme
;
255 redir
->destroyme
.data
= (unsigned long)redir
;
256 mod_timer(&redir
->destroyme
, 75*HZ
);
259 UNLOCK_BH(&redir_lock
);
263 check_for_unredirect(struct sk_buff
*skb
)
265 struct iphdr
*iph
= skb
->nh
.iph
;
266 struct tcphdr
*tcph
= (struct tcphdr
*)((u_int32_t
*)iph
270 if (iph
->protocol
!= IPPROTO_TCP
)
273 LOCK_BH(&redir_lock
);
274 redir
= find_unredir(iph
->saddr
, iph
->daddr
, tcph
->source
, tcph
->dest
);
276 DEBUGP("Doing tcp unredirect.\n");
277 do_tcp_unredir(skb
, redir
);
278 if (tcph
->rst
|| tcph
->fin
) {
279 redir
->destroyme
.function
= destroyme
;
280 redir
->destroyme
.data
= (unsigned long)redir
;
281 mod_timer(&redir
->destroyme
, 75*HZ
);
284 UNLOCK_BH(&redir_lock
);