patch-2.4.4 linux/net/ipv4/netfilter/ip_nat_ftp.c
Next file: linux/net/ipv4/netfilter/ip_nat_helper.c
Previous file: linux/net/ipv4/netfilter/ip_nat_core.c
Back to the patch index
Back to the overall index
- Lines: 422
- Date:
Wed Apr 25 15:02:35 2001
- Orig file:
v2.4.3/linux/net/ipv4/netfilter/ip_nat_ftp.c
- Orig date:
Sun Sep 17 10:15:00 2000
diff -u --recursive --new-file v2.4.3/linux/net/ipv4/netfilter/ip_nat_ftp.c linux/net/ipv4/netfilter/ip_nat_ftp.c
@@ -7,7 +7,6 @@
#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_nat_ftp.h>
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
@@ -17,6 +16,16 @@
#define DEBUGP(format, args...)
#endif
+#define MAX_PORTS 8
+static int ports[MAX_PORTS];
+static int ports_c = 0;
+
+#ifdef MODULE_PARM
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
+#endif
+
+DECLARE_LOCK_EXTERN(ip_ftp_lock);
+
/* FIXME: Time out? --RR */
static int
@@ -49,7 +58,8 @@
return 0;
}
- if (ftpinfo->ftptype == IP_CT_FTP_PORT) {
+ 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;
@@ -89,159 +99,88 @@
return 1;
}
-/* This is interesting. We simply use the port given us by the client
- or server. In practice it's extremely unlikely to clash; if it
- does, the rule won't be able to get a unique tuple and will drop
- the packets. */
static int
-mangle_packet(struct sk_buff **pskb,
- u_int32_t newip,
- u_int16_t port,
- unsigned int matchoff,
- unsigned int matchlen,
- struct ip_nat_ftp_info *this_way,
- struct ip_nat_ftp_info *other_way)
+mangle_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)
{
- struct iphdr *iph = (*pskb)->nh.iph;
- struct tcphdr *tcph;
- unsigned char *data;
- unsigned int tcplen, newlen, newtcplen;
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);
- tcplen = (*pskb)->len - iph->ihl * 4;
- newtcplen = tcplen - matchlen + strlen(buffer);
- newlen = iph->ihl*4 + newtcplen;
-
- /* So there I am, in the middle of my `netfilter-is-wonderful'
- talk in Sydney, and someone asks `What happens if you try
- to enlarge a 64k packet here?'. I think I said something
- eloquent like `fuck'. */
- if (newlen > 65535) {
- if (net_ratelimit())
- printk("nat_ftp cheat: %u.%u.%u.%u->%u.%u.%u.%u %u\n",
- NIPQUAD((*pskb)->nh.iph->saddr),
- NIPQUAD((*pskb)->nh.iph->daddr),
- (*pskb)->nh.iph->protocol);
- return 0;
- }
+ DEBUGP("calling ip_nat_mangle_tcp_packet\n");
- if (newlen > (*pskb)->len + skb_tailroom(*pskb)) {
- struct sk_buff *newskb;
- newskb = skb_copy_expand(*pskb, skb_headroom(*pskb),
- newlen - (*pskb)->len,
- GFP_ATOMIC);
- if (!newskb) {
- DEBUGP("ftp: oom\n");
- return 0;
- } else {
- kfree_skb(*pskb);
- *pskb = newskb;
- iph = (*pskb)->nh.iph;
- }
- }
+ return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
+ matchlen, buffer, strlen(buffer));
+}
- tcph = (void *)iph + iph->ihl*4;
- data = (void *)tcph + tcph->doff*4;
+/* |1|132.235.1.2|6275| */
+static int
+mangle_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|")];
- DEBUGP("Mapping `%.*s' [%u %u %u] to new `%s' [%u]\n",
- (int)matchlen, data+matchoff,
- data[matchoff], data[matchoff+1],
- matchlen, buffer, strlen(buffer));
-
- /* SYN adjust. If it's uninitialized, or this is after last
- correction, record it: we don't handle more than one
- adjustment in the window, but do deal with common case of a
- retransmit. */
- if (this_way->syn_offset_before == this_way->syn_offset_after
- || before(this_way->syn_correction_pos, ntohl(tcph->seq))) {
- this_way->syn_correction_pos = ntohl(tcph->seq);
- this_way->syn_offset_before = this_way->syn_offset_after;
- this_way->syn_offset_after = (int32_t)
- this_way->syn_offset_before + newlen - (*pskb)->len;
- }
+ MUST_BE_LOCKED(&ip_ftp_lock);
- /* Move post-replacement */
- memmove(data + matchoff + strlen(buffer),
- data + matchoff + matchlen,
- (*pskb)->tail - (data + matchoff + matchlen));
- memcpy(data + matchoff, buffer, strlen(buffer));
-
- /* Resize packet. */
- if (newlen > (*pskb)->len) {
- DEBUGP("ip_nat_ftp: Extending packet by %u to %u bytes\n",
- newlen - (*pskb)->len, newlen);
- skb_put(*pskb, newlen - (*pskb)->len);
- } else {
- DEBUGP("ip_nat_ftp: Shrinking packet from %u to %u bytes\n",
- (*pskb)->len, newlen);
- skb_trim(*pskb, newlen);
- }
+ sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port);
- /* Fix checksums */
- iph->tot_len = htons(newlen);
- (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4,
- newtcplen - tcph->doff*4, 0);
- tcph->check = 0;
- tcph->check = tcp_v4_check(tcph, newtcplen, iph->saddr, iph->daddr,
- csum_partial((char *)tcph, tcph->doff*4,
- (*pskb)->csum));
- ip_send_check(iph);
- return 1;
+ DEBUGP("calling ip_nat_mangle_tcp_packet\n");
+
+ return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
+ matchlen, buffer, strlen(buffer));
}
-/* Grrr... SACK. Fuck me even harder. Don't want to fix it on the
- fly, so blow it away. */
-static void
-delete_sack(struct sk_buff *skb, struct tcphdr *tcph)
+/* |1|132.235.1.2|6275| */
+static int
+mangle_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)
{
- unsigned int i;
- u_int8_t *opt = (u_int8_t *)tcph;
+ char buffer[sizeof("|||65535|")];
- DEBUGP("Seeking SACKPERM in SYN packet (doff = %u).\n",
- tcph->doff * 4);
- for (i = sizeof(struct tcphdr); i < tcph->doff * 4;) {
- DEBUGP("%u ", opt[i]);
- switch (opt[i]) {
- case TCPOPT_NOP:
- case TCPOPT_EOL:
- i++;
- break;
+ MUST_BE_LOCKED(&ip_ftp_lock);
- case TCPOPT_SACK_PERM:
- goto found_opt;
+ sprintf(buffer, "|||%u|", port);
- default:
- /* Worst that can happen: it will take us over. */
- i += opt[i+1] ?: 1;
- }
- }
- DEBUGP("\n");
- return;
+ DEBUGP("calling ip_nat_mangle_tcp_packet\n");
- found_opt:
- DEBUGP("\n");
- DEBUGP("Found SACKPERM at offset %u.\n", i);
-
- /* Must be within TCP header, and valid SACK perm. */
- if (i + opt[i+1] <= tcph->doff*4 && opt[i+1] == 2) {
- /* Replace with NOPs. */
- tcph->check
- = ip_nat_cheat_check(*((u_int16_t *)(opt + i))^0xFFFF,
- 0, tcph->check);
- opt[i] = opt[i+1] = 0;
- }
- else DEBUGP("Something wrong with SACK_PERM.\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,
- struct ip_nat_ftp_info *ftp,
unsigned int datalen,
- struct sk_buff **pskb)
+ struct sk_buff **pskb,
+ enum ip_conntrack_info ctinfo)
{
u_int32_t newip;
struct iphdr *iph = (*pskb)->nh.iph;
@@ -261,8 +200,9 @@
/* Change address inside packet to match way we're mapping
this connection. */
- if (ct_ftp_info->ftptype == IP_CT_FTP_PASV) {
- /* PASV response: must be where client thinks server
+ 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 */
@@ -287,11 +227,9 @@
if (port == 0)
return 0;
- if (!mangle_packet(pskb, newip, port,
- ct_ftp_info->seq - ntohl(tcph->seq),
- ct_ftp_info->len,
- &ftp[ct_ftp_info->ftptype],
- &ftp[!ct_ftp_info->ftptype]))
+ 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;
@@ -305,18 +243,15 @@
{
struct iphdr *iph = (*pskb)->nh.iph;
struct tcphdr *tcph = (void *)iph + iph->ihl*4;
- u_int32_t newseq, newack;
unsigned int datalen;
int dir;
int score;
struct ip_ct_ftp *ct_ftp_info
= &ct->help.ct_ftp_info;
- struct ip_nat_ftp_info *ftp
- = &ct->nat.help.ftp_info[0];
/* Delete SACK_OK on initial TCP SYNs. */
if (tcph->syn && !tcph->ack)
- delete_sack(*pskb, tcph);
+ ip_nat_delete_sack(*pskb, tcph);
/* Only mangle things once: original direction in POST_ROUTING
and reply direction on PRE_ROUTING. */
@@ -353,71 +288,83 @@
UNLOCK_BH(&ip_ftp_lock);
return NF_DROP;
} else if (score == 2) {
- if (!ftp_data_fixup(ct_ftp_info, ct, ftp, datalen,
- pskb)) {
+ 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;
}
}
- /* Sequence adjust */
- if (after(ntohl(tcph->seq), ftp[dir].syn_correction_pos))
- newseq = ntohl(tcph->seq) + ftp[dir].syn_offset_after;
- else
- newseq = ntohl(tcph->seq) + ftp[dir].syn_offset_before;
- newseq = htonl(newseq);
-
- /* Ack adjust: other dir sees offset seq numbers */
- if (after(ntohl(tcph->ack_seq) - ftp[!dir].syn_offset_before,
- ftp[!dir].syn_correction_pos))
- newack = ntohl(tcph->ack_seq) - ftp[!dir].syn_offset_after;
- else
- newack = ntohl(tcph->ack_seq) - ftp[!dir].syn_offset_before;
- newack = htonl(newack);
UNLOCK_BH(&ip_ftp_lock);
- tcph->check = ip_nat_cheat_check(~tcph->seq, newseq,
- ip_nat_cheat_check(~tcph->ack_seq,
- newack,
- tcph->check));
- tcph->seq = newseq;
- tcph->ack_seq = newack;
+ ip_nat_seq_adjust(*pskb, ct, ctinfo);
return NF_ACCEPT;
}
-static struct ip_nat_helper ftp = { { NULL, NULL },
- { { 0, { __constant_htons(21) } },
- { 0, { 0 }, IPPROTO_TCP } },
- { { 0, { 0xFFFF } },
- { 0, { 0 }, 0xFFFF } },
- help, "ftp" };
+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 ret;
+ int i, ret;
+ char *tmpname;
ret = ip_nat_expect_register(&ftp_expect);
if (ret == 0) {
- ret = ip_nat_helper_register(&ftp);
+ 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++;
+ }
- if (ret != 0)
- ip_nat_expect_unregister(&ftp_expect);
+ } else {
+ ip_nat_expect_unregister(&ftp_expect);
}
return ret;
-}
-
-static void __exit fini(void)
-{
- ip_nat_helper_unregister(&ftp);
- ip_nat_expect_unregister(&ftp_expect);
}
module_init(init);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)