Tomato 1.25
[tomato.git] / release / src / linux / linux / net / ipv4 / netfilter / ip_nat_ftp.c
blob1d191c46e4fbcbeeb84bd0a4858e6c5415ab8c24
1 /* FTP extension for TCP NAT alteration. */
2 #include <linux/module.h>
3 #include <linux/netfilter_ipv4.h>
4 #include <linux/ip.h>
5 #include <linux/tcp.h>
6 #include <net/tcp.h>
7 #include <linux/netfilter_ipv4/ip_nat.h>
8 #include <linux/netfilter_ipv4/ip_nat_helper.h>
9 #include <linux/netfilter_ipv4/ip_nat_rule.h>
10 #include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
11 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
13 #define DEBUGP(format, args...)
15 #define MAX_PORTS 8
16 static int ports[MAX_PORTS];
17 static int ports_c = 0;
19 #ifdef MODULE_PARM
20 MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
21 #endif
23 DECLARE_LOCK_EXTERN(ip_ftp_lock);
26 static unsigned int
27 ftp_nat_expected(struct sk_buff **pskb,
28 unsigned int hooknum,
29 struct ip_conntrack *ct,
30 struct ip_nat_info *info)
32 struct ip_nat_multi_range mr;
33 u_int32_t newdstip, newsrcip, newip;
34 struct ip_ct_ftp_expect *exp_ftp_info;
36 struct ip_conntrack *master = master_ct(ct);
38 IP_NF_ASSERT(info);
39 IP_NF_ASSERT(master);
41 IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
43 DEBUGP("nat_expected: We have a connection!\n");
44 exp_ftp_info = &ct->master->help.exp_ftp_info;
46 LOCK_BH(&ip_ftp_lock);
48 if (exp_ftp_info->ftptype == IP_CT_FTP_PORT
49 || exp_ftp_info->ftptype == IP_CT_FTP_EPRT) {
50 /* PORT command: make connection go to the client. */
51 newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
52 newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
53 DEBUGP("nat_expected: PORT cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
54 NIPQUAD(newsrcip), NIPQUAD(newdstip));
55 } else {
56 /* PASV command: make the connection go to the server */
57 newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
58 newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
59 DEBUGP("nat_expected: PASV cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
60 NIPQUAD(newsrcip), NIPQUAD(newdstip));
62 UNLOCK_BH(&ip_ftp_lock);
64 if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
65 newip = newsrcip;
66 else
67 newip = newdstip;
69 DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
71 mr.rangesize = 1;
72 /* We don't want to manip the per-protocol, just the IPs... */
73 mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
74 mr.range[0].min_ip = mr.range[0].max_ip = newip;
76 /* ... unless we're doing a MANIP_DST, in which case, make
77 sure we map to the correct port */
78 if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
79 mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
80 mr.range[0].min = mr.range[0].max
81 = ((union ip_conntrack_manip_proto)
82 { htons(exp_ftp_info->port) });
84 return ip_nat_setup_info(ct, &mr, hooknum);
87 static int
88 mangle_rfc959_packet(struct sk_buff **pskb,
89 u_int32_t newip,
90 u_int16_t port,
91 unsigned int matchoff,
92 unsigned int matchlen,
93 struct ip_conntrack *ct,
94 enum ip_conntrack_info ctinfo)
96 char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
98 MUST_BE_LOCKED(&ip_ftp_lock);
100 sprintf(buffer, "%u,%u,%u,%u,%u,%u",
101 NIPQUAD(newip), port>>8, port&0xFF);
103 DEBUGP("calling ip_nat_mangle_tcp_packet\n");
105 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
106 matchlen, buffer, strlen(buffer));
109 /* |1|132.235.1.2|6275| */
110 static int
111 mangle_eprt_packet(struct sk_buff **pskb,
112 u_int32_t newip,
113 u_int16_t port,
114 unsigned int matchoff,
115 unsigned int matchlen,
116 struct ip_conntrack *ct,
117 enum ip_conntrack_info ctinfo)
119 char buffer[sizeof("|1|255.255.255.255|65535|")];
121 MUST_BE_LOCKED(&ip_ftp_lock);
123 sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port);
125 DEBUGP("calling ip_nat_mangle_tcp_packet\n");
127 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
128 matchlen, buffer, strlen(buffer));
131 /* |1|132.235.1.2|6275| */
132 static int
133 mangle_epsv_packet(struct sk_buff **pskb,
134 u_int32_t newip,
135 u_int16_t port,
136 unsigned int matchoff,
137 unsigned int matchlen,
138 struct ip_conntrack *ct,
139 enum ip_conntrack_info ctinfo)
141 char buffer[sizeof("|||65535|")];
143 MUST_BE_LOCKED(&ip_ftp_lock);
145 sprintf(buffer, "|||%u|", port);
147 DEBUGP("calling ip_nat_mangle_tcp_packet\n");
149 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
150 matchlen, buffer, strlen(buffer));
153 static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t,
154 unsigned int,
155 unsigned int,
156 struct ip_conntrack *,
157 enum ip_conntrack_info)
158 = { [IP_CT_FTP_PORT] mangle_rfc959_packet,
159 [IP_CT_FTP_PASV] mangle_rfc959_packet,
160 [IP_CT_FTP_EPRT] mangle_eprt_packet,
161 [IP_CT_FTP_EPSV] mangle_epsv_packet
164 static int ftp_data_fixup(const struct ip_ct_ftp_expect *ct_ftp_info,
165 struct ip_conntrack *ct,
166 struct sk_buff **pskb,
167 enum ip_conntrack_info ctinfo,
168 struct ip_conntrack_expect *expect)
170 u_int32_t newip;
171 struct iphdr *iph = (*pskb)->nh.iph;
172 struct tcphdr *tcph = (void *)iph + iph->ihl*4;
173 u_int16_t port;
174 struct ip_conntrack_tuple newtuple;
176 MUST_BE_LOCKED(&ip_ftp_lock);
177 DEBUGP("FTP_NAT: seq %u + %u in %u\n",
178 expect->seq, ct_ftp_info->len,
179 ntohl(tcph->seq));
181 /********add by zg 2006.11.21 for cdrouter v3.3 item 123/124(cdrouter_app_11/12) bug ********/
182 memset(&newtuple, 0, sizeof(newtuple));
184 /* Change address inside packet to match way we're mapping
185 this connection. */
186 if (ct_ftp_info->ftptype == IP_CT_FTP_PASV
187 || ct_ftp_info->ftptype == IP_CT_FTP_EPSV) {
188 /* PASV/EPSV response: must be where client thinks server
189 is */
190 newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
191 /* Expect something from client->server */
192 newtuple.src.ip =
193 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
194 newtuple.dst.ip =
195 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
196 } else {
197 /* PORT command: must be where server thinks client is */
198 newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
199 /* Expect something from server->client */
200 newtuple.src.ip =
201 ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
202 newtuple.dst.ip =
203 ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
205 newtuple.dst.protonum = IPPROTO_TCP;
206 newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
208 /* Try to get same port: if not, try to change it. */
209 for (port = ct_ftp_info->port; port != 0; port++) {
210 newtuple.dst.u.tcp.port = htons(port);
212 if (ip_conntrack_change_expect(expect, &newtuple) == 0)
213 break;
215 if (port == 0)
216 return 0;
218 if (!mangle[ct_ftp_info->ftptype](pskb, newip, port,
219 expect->seq - ntohl(tcph->seq),
220 ct_ftp_info->len, ct, ctinfo))
221 return 0;
223 return 1;
226 static unsigned int help(struct ip_conntrack *ct,
227 struct ip_conntrack_expect *exp,
228 struct ip_nat_info *info,
229 enum ip_conntrack_info ctinfo,
230 unsigned int hooknum,
231 struct sk_buff **pskb)
233 struct iphdr *iph = (*pskb)->nh.iph;
234 struct tcphdr *tcph = (void *)iph + iph->ihl*4;
235 unsigned int datalen;
236 int dir;
237 struct ip_ct_ftp_expect *ct_ftp_info;
239 if (!exp)
240 DEBUGP("ip_nat_ftp: no exp!!");
242 ct_ftp_info = &exp->help.exp_ftp_info;
244 /* Only mangle things once: original direction in POST_ROUTING
245 and reply direction on PRE_ROUTING. */
246 dir = CTINFO2DIR(ctinfo);
247 if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
248 || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
249 DEBUGP("nat_ftp: Not touching dir %s at hook %s\n",
250 dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
251 hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
252 : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
253 : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
254 return NF_ACCEPT;
257 datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
258 LOCK_BH(&ip_ftp_lock);
259 /* If it's in the right range... */
260 if (between(exp->seq + ct_ftp_info->len,
261 ntohl(tcph->seq),
262 ntohl(tcph->seq) + datalen)) {
263 if (!ftp_data_fixup(ct_ftp_info, ct, pskb, ctinfo, exp)) {
264 UNLOCK_BH(&ip_ftp_lock);
265 return NF_DROP;
267 } else {
268 /* Half a match? This means a partial retransmisison.
269 It's a cracker being funky. */
270 if (net_ratelimit()) {
271 printk("FTP_NAT: partial packet %u/%u in %u/%u\n",
272 exp->seq, ct_ftp_info->len,
273 ntohl(tcph->seq),
274 ntohl(tcph->seq) + datalen);
276 UNLOCK_BH(&ip_ftp_lock);
277 return NF_DROP;
279 UNLOCK_BH(&ip_ftp_lock);
281 return NF_ACCEPT;
284 static struct ip_nat_helper ftp[MAX_PORTS];
285 static char ftp_names[MAX_PORTS][10];
287 /* Not __exit: called from init() */
288 static void fini(void)
290 int i;
292 for (i = 0; i < ports_c; i++) {
293 DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]);
294 ip_nat_helper_unregister(&ftp[i]);
298 static int __init init(void)
300 int i, ret = 0;
301 char *tmpname;
303 if (ports[0] == 0)
304 ports[0] = FTP_PORT;
306 for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
308 memset(&ftp[i], 0, sizeof(struct ip_nat_helper));
310 ftp[i].tuple.dst.protonum = IPPROTO_TCP;
311 ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
312 ftp[i].mask.dst.protonum = 0xFFFF;
313 ftp[i].mask.src.u.tcp.port = 0xFFFF;
314 ftp[i].help = help;
315 ftp[i].me = THIS_MODULE;
316 ftp[i].flags = 0;
317 ftp[i].expect = ftp_nat_expected;
319 tmpname = &ftp_names[i][0];
320 if (ports[i] == FTP_PORT)
321 sprintf(tmpname, "ftp");
322 else
323 sprintf(tmpname, "ftp-%d", i);
324 ftp[i].name = tmpname;
326 DEBUGP("ip_nat_ftp: Trying to register for port %d\n",
327 ports[i]);
328 ret = ip_nat_helper_register(&ftp[i]);
330 if (ret) {
331 printk("ip_nat_ftp: error registering "
332 "helper for port %d\n", ports[i]);
333 fini();
334 return ret;
336 ports_c++;
339 return ret;
342 module_init(init);
343 module_exit(fini);
344 MODULE_LICENSE("GPL");