1 /* FTP extension for TCP NAT alteration. */
2 #include <linux/module.h>
3 #include <linux/netfilter_ipv4.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...)
16 static int ports
[MAX_PORTS
];
17 static int ports_c
= 0;
20 MODULE_PARM(ports
, "1-" __MODULE_STRING(MAX_PORTS
) "i");
23 DECLARE_LOCK_EXTERN(ip_ftp_lock
);
27 ftp_nat_expected(struct sk_buff
**pskb
,
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
);
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
));
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
)
69 DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip
));
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
);
88 mangle_rfc959_packet(struct sk_buff
**pskb
,
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| */
111 mangle_eprt_packet(struct sk_buff
**pskb
,
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| */
133 mangle_epsv_packet(struct sk_buff
**pskb
,
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
,
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
)
171 struct iphdr
*iph
= (*pskb
)->nh
.iph
;
172 struct tcphdr
*tcph
= (void *)iph
+ iph
->ihl
*4;
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
,
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
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
190 newip
= ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.dst
.ip
;
191 /* Expect something from client->server */
193 ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.ip
;
195 ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.dst
.ip
;
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 */
201 ct
->tuplehash
[IP_CT_DIR_REPLY
].tuple
.src
.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)
218 if (!mangle
[ct_ftp_info
->ftptype
](pskb
, newip
, port
,
219 expect
->seq
- ntohl(tcph
->seq
),
220 ct_ftp_info
->len
, ct
, ctinfo
))
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
;
237 struct ip_ct_ftp_expect
*ct_ftp_info
;
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" : "???");
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
,
262 ntohl(tcph
->seq
) + datalen
)) {
263 if (!ftp_data_fixup(ct_ftp_info
, ct
, pskb
, ctinfo
, exp
)) {
264 UNLOCK_BH(&ip_ftp_lock
);
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
,
274 ntohl(tcph
->seq
) + datalen
);
276 UNLOCK_BH(&ip_ftp_lock
);
279 UNLOCK_BH(&ip_ftp_lock
);
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)
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)
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;
315 ftp
[i
].me
= THIS_MODULE
;
317 ftp
[i
].expect
= ftp_nat_expected
;
319 tmpname
= &ftp_names
[i
][0];
320 if (ports
[i
] == FTP_PORT
)
321 sprintf(tmpname
, "ftp");
323 sprintf(tmpname
, "ftp-%d", i
);
324 ftp
[i
].name
= tmpname
;
326 DEBUGP("ip_nat_ftp: Trying to register for port %d\n",
328 ret
= ip_nat_helper_register(&ftp
[i
]);
331 printk("ip_nat_ftp: error registering "
332 "helper for port %d\n", ports
[i
]);
344 MODULE_LICENSE("GPL");