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

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)