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> 12 13#define DEBUGP(format, args...) 14 15#define MAX_PORTS 8 16static int ports[MAX_PORTS]; 17static int ports_c = 0; 18 19#ifdef MODULE_PARM 20MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); 21#endif 22 23DECLARE_LOCK_EXTERN(ip_ftp_lock); 24 25 26static unsigned int 27ftp_nat_expected(struct sk_buff **pskb, 28 unsigned int hooknum, 29 struct ip_conntrack *ct, 30 struct ip_nat_info *info) 31{ 32 struct ip_nat_multi_range mr; 33 u_int32_t newdstip, newsrcip, newip; 34 struct ip_ct_ftp_expect *exp_ftp_info; 35 36 struct ip_conntrack *master = master_ct(ct); 37 38 IP_NF_ASSERT(info); 39 IP_NF_ASSERT(master); 40 41 IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum)))); 42 43 DEBUGP("nat_expected: We have a connection!\n"); 44 exp_ftp_info = &ct->master->help.exp_ftp_info; 45 46 LOCK_BH(&ip_ftp_lock); 47 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)); 61 } 62 UNLOCK_BH(&ip_ftp_lock); 63 64 if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) 65 newip = newsrcip; 66 else 67 newip = newdstip; 68 69 DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip)); 70 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; 75 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) }); 83 } 84 return ip_nat_setup_info(ct, &mr, hooknum); 85} 86 87static int 88mangle_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) 95{ 96 char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")]; 97 98 MUST_BE_LOCKED(&ip_ftp_lock); 99 100 sprintf(buffer, "%u,%u,%u,%u,%u,%u", 101 NIPQUAD(newip), port>>8, port&0xFF); 102 103 DEBUGP("calling ip_nat_mangle_tcp_packet\n"); 104 105 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 106 matchlen, buffer, strlen(buffer)); 107} 108 109/* |1|132.235.1.2|6275| */ 110static int 111mangle_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) 118{ 119 char buffer[sizeof("|1|255.255.255.255|65535|")]; 120 121 MUST_BE_LOCKED(&ip_ftp_lock); 122 123 sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port); 124 125 DEBUGP("calling ip_nat_mangle_tcp_packet\n"); 126 127 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 128 matchlen, buffer, strlen(buffer)); 129} 130 131/* |1|132.235.1.2|6275| */ 132static int 133mangle_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) 140{ 141 char buffer[sizeof("|||65535|")]; 142 143 MUST_BE_LOCKED(&ip_ftp_lock); 144 145 sprintf(buffer, "|||%u|", port); 146 147 DEBUGP("calling ip_nat_mangle_tcp_packet\n"); 148 149 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 150 matchlen, buffer, strlen(buffer)); 151} 152 153static 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 162}; 163 164static 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) 169{ 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; 175 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)); 180 181 /* Change address inside packet to match way we're mapping 182 this connection. */ 183 if (ct_ftp_info->ftptype == IP_CT_FTP_PASV 184 || ct_ftp_info->ftptype == IP_CT_FTP_EPSV) { 185 /* PASV/EPSV response: must be where client thinks server 186 is */ 187 newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; 188 /* Expect something from client->server */ 189 newtuple.src.ip = 190 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; 191 newtuple.dst.ip = 192 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; 193 } else { 194 /* PORT command: must be where server thinks client is */ 195 newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; 196 /* Expect something from server->client */ 197 newtuple.src.ip = 198 ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; 199 newtuple.dst.ip = 200 ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; 201 } 202 newtuple.dst.protonum = IPPROTO_TCP; 203 newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port; 204 205 /* Try to get same port: if not, try to change it. */ 206 for (port = ct_ftp_info->port; port != 0; port++) { 207 newtuple.dst.u.tcp.port = htons(port); 208 209 if (ip_conntrack_change_expect(expect, &newtuple) == 0) 210 break; 211 } 212 if (port == 0) 213 return 0; 214 215 if (!mangle[ct_ftp_info->ftptype](pskb, newip, port, 216 expect->seq - ntohl(tcph->seq), 217 ct_ftp_info->len, ct, ctinfo)) 218 return 0; 219 220 return 1; 221} 222 223static unsigned int help(struct ip_conntrack *ct, 224 struct ip_conntrack_expect *exp, 225 struct ip_nat_info *info, 226 enum ip_conntrack_info ctinfo, 227 unsigned int hooknum, 228 struct sk_buff **pskb) 229{ 230 struct iphdr *iph = (*pskb)->nh.iph; 231 struct tcphdr *tcph = (void *)iph + iph->ihl*4; 232 unsigned int datalen; 233 int dir; 234 struct ip_ct_ftp_expect *ct_ftp_info; 235 236 if (!exp) 237 DEBUGP("ip_nat_ftp: no exp!!"); 238 239 ct_ftp_info = &exp->help.exp_ftp_info; 240 241 /* Only mangle things once: original direction in POST_ROUTING 242 and reply direction on PRE_ROUTING. */ 243 dir = CTINFO2DIR(ctinfo); 244 if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) 245 || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { 246 DEBUGP("nat_ftp: Not touching dir %s at hook %s\n", 247 dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", 248 hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" 249 : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" 250 : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); 251 return NF_ACCEPT; 252 } 253 254 datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; 255 LOCK_BH(&ip_ftp_lock); 256 /* If it's in the right range... */ 257 if (between(exp->seq + ct_ftp_info->len, 258 ntohl(tcph->seq), 259 ntohl(tcph->seq) + datalen)) { 260 if (!ftp_data_fixup(ct_ftp_info, ct, pskb, ctinfo, exp)) { 261 UNLOCK_BH(&ip_ftp_lock); 262 return NF_DROP; 263 } 264 } else { 265 /* Half a match? This means a partial retransmisison. 266 It's a cracker being funky. */ 267 if (net_ratelimit()) { 268 printk("FTP_NAT: partial packet %u/%u in %u/%u\n", 269 exp->seq, ct_ftp_info->len, 270 ntohl(tcph->seq), 271 ntohl(tcph->seq) + datalen); 272 } 273 UNLOCK_BH(&ip_ftp_lock); 274 return NF_DROP; 275 } 276 UNLOCK_BH(&ip_ftp_lock); 277 278 return NF_ACCEPT; 279} 280 281static struct ip_nat_helper ftp[MAX_PORTS]; 282static char ftp_names[MAX_PORTS][10]; 283 284/* Not __exit: called from init() */ 285static void fini(void) 286{ 287 int i; 288 289 for (i = 0; i < ports_c; i++) { 290 DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]); 291 ip_nat_helper_unregister(&ftp[i]); 292 } 293} 294 295static int __init init(void) 296{ 297 int i, ret = 0; 298 char *tmpname; 299 300 if (ports[0] == 0) 301 ports[0] = FTP_PORT; 302 303 for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { 304 305 memset(&ftp[i], 0, sizeof(struct ip_nat_helper)); 306 307 ftp[i].tuple.dst.protonum = IPPROTO_TCP; 308 ftp[i].tuple.src.u.tcp.port = htons(ports[i]); 309 ftp[i].mask.dst.protonum = 0xFFFF; 310 ftp[i].mask.src.u.tcp.port = 0xFFFF; 311 ftp[i].help = help; 312 ftp[i].me = THIS_MODULE; 313 ftp[i].flags = 0; 314 ftp[i].expect = ftp_nat_expected; 315 316 tmpname = &ftp_names[i][0]; 317 if (ports[i] == FTP_PORT) 318 sprintf(tmpname, "ftp"); 319 else 320 sprintf(tmpname, "ftp-%d", i); 321 ftp[i].name = tmpname; 322 323 DEBUGP("ip_nat_ftp: Trying to register for port %d\n", 324 ports[i]); 325 ret = ip_nat_helper_register(&ftp[i]); 326 327 if (ret) { 328 printk("ip_nat_ftp: error registering " 329 "helper for port %d\n", ports[i]); 330 fini(); 331 return ret; 332 } 333 ports_c++; 334 } 335 336 return ret; 337} 338 339module_init(init); 340module_exit(fini); 341MODULE_LICENSE("GPL"); 342