📄 ip_nat_ftp.c
字号:
/* FTP extension for TCP NAT alteration. */#include <linux/module.h>#include <linux/netfilter_ipv4.h>#include <linux/ip.h>#include <linux/tcp.h>#include <net/tcp.h>#include <linux/netfilter_ipv4/ip_nat.h>#include <linux/netfilter_ipv4/ip_nat_helper.h>#include <linux/netfilter_ipv4/ip_nat_rule.h>#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>#include <linux/netfilter_ipv4/ip_conntrack_helper.h>#if 0#define DEBUGP printk#else#define DEBUGP(format, args...)#endif#define MAX_PORTS 8static int ports[MAX_PORTS];static int ports_c = 0;#ifdef MODULE_PARMMODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");#endifDECLARE_LOCK_EXTERN(ip_ftp_lock);/* FIXME: Time out? --RR */static intftp_nat_expected(struct sk_buff **pskb, unsigned int hooknum, struct ip_conntrack *ct, struct ip_nat_info *info, struct ip_conntrack *master, struct ip_nat_info *masterinfo, unsigned int *verdict){ struct ip_nat_multi_range mr; u_int32_t newdstip, newsrcip, newip; struct ip_ct_ftp *ftpinfo; IP_NF_ASSERT(info); IP_NF_ASSERT(master); IP_NF_ASSERT(masterinfo); IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum)))); DEBUGP("nat_expected: We have a connection!\n"); /* Master must be an ftp connection */ ftpinfo = &master->help.ct_ftp_info; LOCK_BH(&ip_ftp_lock); if (ftpinfo->is_ftp != IP_CONNTR_FTP) { UNLOCK_BH(&ip_ftp_lock); DEBUGP("nat_expected: master not ftp\n"); return 0; } if (ftpinfo->ftptype == IP_CT_FTP_PORT || ftpinfo->ftptype == IP_CT_FTP_EPRT) { /* PORT command: make connection go to the client. */ newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; DEBUGP("nat_expected: PORT cmd. %u.%u.%u.%u->%u.%u.%u.%u\n", NIPQUAD(newsrcip), NIPQUAD(newdstip)); } else { /* PASV command: make the connection go to the server */ newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; DEBUGP("nat_expected: PASV cmd. %u.%u.%u.%u->%u.%u.%u.%u\n", NIPQUAD(newsrcip), NIPQUAD(newdstip)); } UNLOCK_BH(&ip_ftp_lock); if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) newip = newsrcip; else newip = newdstip; DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip)); mr.rangesize = 1; /* We don't want to manip the per-protocol, just the IPs... */ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; mr.range[0].min_ip = mr.range[0].max_ip = newip; /* ... unless we're doing a MANIP_DST, in which case, make sure we map to the correct port */ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; mr.range[0].min = mr.range[0].max = ((union ip_conntrack_manip_proto) { htons(ftpinfo->port) }); } *verdict = ip_nat_setup_info(ct, &mr, hooknum); return 1;}static intmangle_rfc959_packet(struct sk_buff **pskb, u_int32_t newip, u_int16_t port, unsigned int matchoff, unsigned int matchlen, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo){ char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")]; MUST_BE_LOCKED(&ip_ftp_lock); sprintf(buffer, "%u,%u,%u,%u,%u,%u", NIPQUAD(newip), port>>8, port&0xFF); DEBUGP("calling ip_nat_mangle_tcp_packet\n"); return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, matchlen, buffer, strlen(buffer));}/* |1|132.235.1.2|6275| */static intmangle_eprt_packet(struct sk_buff **pskb, u_int32_t newip, u_int16_t port, unsigned int matchoff, unsigned int matchlen, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo){ char buffer[sizeof("|1|255.255.255.255|65535|")]; MUST_BE_LOCKED(&ip_ftp_lock); sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port); DEBUGP("calling ip_nat_mangle_tcp_packet\n"); return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, matchlen, buffer, strlen(buffer));}/* |1|132.235.1.2|6275| */static intmangle_epsv_packet(struct sk_buff **pskb, u_int32_t newip, u_int16_t port, unsigned int matchoff, unsigned int matchlen, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo){ char buffer[sizeof("|||65535|")]; MUST_BE_LOCKED(&ip_ftp_lock); sprintf(buffer, "|||%u|", port); DEBUGP("calling ip_nat_mangle_tcp_packet\n"); return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, matchlen, buffer, strlen(buffer));}static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t, unsigned int, unsigned int, struct ip_conntrack *, enum ip_conntrack_info)= { [IP_CT_FTP_PORT] mangle_rfc959_packet, [IP_CT_FTP_PASV] mangle_rfc959_packet, [IP_CT_FTP_EPRT] mangle_eprt_packet, [IP_CT_FTP_EPSV] mangle_epsv_packet};static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info, struct ip_conntrack *ct, unsigned int datalen, struct sk_buff **pskb, enum ip_conntrack_info ctinfo){ u_int32_t newip; struct iphdr *iph = (*pskb)->nh.iph; struct tcphdr *tcph = (void *)iph + iph->ihl*4; u_int16_t port; struct ip_conntrack_tuple tuple; /* Don't care about source port */ const struct ip_conntrack_tuple mask = { { 0xFFFFFFFF, { 0 } }, { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF } }; memset(&tuple, 0, sizeof(tuple)); MUST_BE_LOCKED(&ip_ftp_lock); DEBUGP("FTP_NAT: seq %u + %u in %u + %u\n", ct_ftp_info->seq, ct_ftp_info->len, ntohl(tcph->seq), datalen); /* Change address inside packet to match way we're mapping this connection. */ if (ct_ftp_info->ftptype == IP_CT_FTP_PASV || ct_ftp_info->ftptype == IP_CT_FTP_EPSV) { /* PASV/EPSV response: must be where client thinks server is */ newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; /* Expect something from client->server */ tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; } else { /* PORT command: must be where server thinks client is */ newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; /* Expect something from server->client */ tuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; tuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; } tuple.dst.protonum = IPPROTO_TCP; /* Try to get same port: if not, try to change it. */ for (port = ct_ftp_info->port; port != 0; port++) { tuple.dst.u.tcp.port = htons(port); if (ip_conntrack_expect_related(ct, &tuple, &mask, NULL) == 0) break; } if (port == 0) return 0; if (!mangle[ct_ftp_info->ftptype](pskb, newip, port, ct_ftp_info->seq - ntohl(tcph->seq), ct_ftp_info->len, ct, ctinfo)) return 0; return 1;}static unsigned int help(struct ip_conntrack *ct, struct ip_nat_info *info, enum ip_conntrack_info ctinfo, unsigned int hooknum, struct sk_buff **pskb){ struct iphdr *iph = (*pskb)->nh.iph; struct tcphdr *tcph = (void *)iph + iph->ihl*4; unsigned int datalen; int dir; int score; struct ip_ct_ftp *ct_ftp_info = &ct->help.ct_ftp_info; /* Delete SACK_OK on initial TCP SYNs. */ if (tcph->syn && !tcph->ack) ip_nat_delete_sack(*pskb, tcph); /* Only mangle things once: original direction in POST_ROUTING and reply direction on PRE_ROUTING. */ dir = CTINFO2DIR(ctinfo); if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { DEBUGP("nat_ftp: Not touching dir %s at hook %s\n", dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); return NF_ACCEPT; } datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; score = 0; LOCK_BH(&ip_ftp_lock); if (ct_ftp_info->len) { /* If it's in the right range... */ score += between(ct_ftp_info->seq, ntohl(tcph->seq), ntohl(tcph->seq) + datalen); score += between(ct_ftp_info->seq + ct_ftp_info->len, ntohl(tcph->seq), ntohl(tcph->seq) + datalen); if (score == 1) { /* Half a match? This means a partial retransmisison. It's a cracker being funky. */ if (net_ratelimit()) { printk("FTP_NAT: partial packet %u/%u in %u/%u\n", ct_ftp_info->seq, ct_ftp_info->len, ntohl(tcph->seq), ntohl(tcph->seq) + datalen); } UNLOCK_BH(&ip_ftp_lock); return NF_DROP; } else if (score == 2) { if (!ftp_data_fixup(ct_ftp_info, ct, datalen, pskb, ctinfo)) { UNLOCK_BH(&ip_ftp_lock); return NF_DROP; } /* skb may have been reallocated */ iph = (*pskb)->nh.iph; tcph = (void *)iph + iph->ihl*4; } } UNLOCK_BH(&ip_ftp_lock); ip_nat_seq_adjust(*pskb, ct, ctinfo); return NF_ACCEPT;}static struct ip_nat_helper ftp[MAX_PORTS];static char ftp_names[MAX_PORTS][6];static struct ip_nat_expect ftp_expect= { { NULL, NULL }, ftp_nat_expected };/* Not __exit: called from init() */static void fini(void){ int i; for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]); ip_nat_helper_unregister(&ftp[i]); } ip_nat_expect_unregister(&ftp_expect);}static int __init init(void){ int i, ret; char *tmpname; ret = ip_nat_expect_register(&ftp_expect); if (ret == 0) { if (ports[0] == 0) ports[0] = 21; for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { memset(&ftp[i], 0, sizeof(struct ip_nat_helper)); ftp[i].tuple.dst.protonum = IPPROTO_TCP; ftp[i].tuple.src.u.tcp.port = htons(ports[i]); ftp[i].mask.dst.protonum = 0xFFFF; ftp[i].mask.src.u.tcp.port = 0xFFFF; ftp[i].help = help; tmpname = &ftp_names[i][0]; sprintf(tmpname, "ftp%2.2d", i); ftp[i].name = tmpname; DEBUGP("ip_nat_ftp: Trying to register for port %d\n", ports[i]); ret = ip_nat_helper_register(&ftp[i]); if (ret) { printk("ip_nat_ftp: error registering helper for port %d\n", ports[i]); fini(); return ret; } ports_c++; } } else { ip_nat_expect_unregister(&ftp_expect); } return ret;}module_init(init);module_exit(fini);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -