I believe the ip_masq_ftp module is broken when it comes to passive ftp's.
The module doesn't detect the passive mode, and thus doesn't bind the
control channel to the data channel.  If an individual file takes longer
than 15 minutes (default for inactive-but-connected masq connections),
then the control channel won't be there after the file is complete.

You can bump up the timeout for the tcp connections with ipchains -M -S,
or you can use the patch attached here.

This patch fixes the passive detection, but also will create firewall
rules on the fly so that you don't have to open up all outbound
connections.  You only need to do masquerade the ftp port (but not the
ftp-data port, or the upper ports).

        -kf

On Thu, 2 Sep 1999, PointyBird wrote:

> I seem to have a problem with passive mode FTP transfers inbound to our
> site, from the Internet. A proportion of transfers fail. The failures are
> consistent for certain files, and take the form whereby:
> 
> I start the transfer.
> 
> All of the file data is transferred into my site.
> 
> For CuteFTP and AbsoluteFTP on NT, the transfer hangs, and requires that I
> physically end it with the cancel button.
> 
> For NCFTP on Linux, I get the message "Error: host is not responding to
> commands. Hanging up."
> 
> This is consistent for certain very large files. The file is transferred
> complete, but the client stalls, waiting for some kind of completion
> dialogue from the remote FTP server. If I try to transfer the same files
> locally across our lan, there's no problem.
> 
> The same internet to lan transfer completes with no problems on the firewall
> itself.
> 
> I'm running a Linux ipfwadm type masquerade firewall. I could upgrade, but
> for various excellent reasons, I'd like to avoid this.
> 
> Has anyone else experienced this problem?
> 
> 
> 
> 
> 
> -====---====---====---====---====---====---====---====---====---====---====-
>  to unsubscribe email "unsubscribe linux-admin" to [EMAIL PROTECTED]
>  See the linux-admin FAQ: http://www.kalug.lug.net/linux-admin-FAQ/
> 
diff -ru linux-2.2.11/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- linux-2.2.11/net/ipv4/ip_fw.c       Mon Aug  9 15:05:05 1999
+++ linux/net/ipv4/ip_fw.c      Wed Aug 11 11:49:25 1999
@@ -1254,7 +1254,7 @@
                return NULL;
        }
 
-       fwkern = kmalloc(SIZEOF_STRUCT_IP_FW_KERNEL, GFP_KERNEL);
+       fwkern = kmalloc(SIZEOF_STRUCT_IP_FW_KERNEL, GFP_ATOMIC);
        if (!fwkern) {
                duprintf("convert_ipfw: kmalloc failed!\n");
                *errno = ENOMEM;
diff -ru linux-2.2.11/net/ipv4/ip_masq_ftp.c linux/net/ipv4/ip_masq_ftp.c
--- linux-2.2.11/net/ipv4/ip_masq_ftp.c Mon Oct  5 15:28:09 1998
+++ linux/net/ipv4/ip_masq_ftp.c        Tue Aug 17 13:34:44 1999
@@ -16,6 +16,9 @@
  *     Juan Jose Ciarlante     :       Litl bits for 2.1
  *     Juan Jose Ciarlante     :       use ip_masq_listen() 
  *     Juan Jose Ciarlante     :       use private app_data for own flag(s)
+ *     Kelly French            :       Create dynamic firewall rules so
+ *                             :       that the only outbound MASQ rule you
+ *                             :       have to create is for port 21
  *
  *
  *
@@ -45,6 +48,8 @@
 #include <linux/skbuff.h>
 #include <linux/in.h>
 #include <linux/ip.h>
+#include <linux/if.h>
+#include <linux/ip_fw.h>
 #include <linux/init.h>
 #include <net/protocol.h>
 #include <net/tcp.h>
@@ -60,6 +65,19 @@
 static int ports[MAX_MASQ_APP_PORTS] = {21}; /* I rely on the trailing items being 
set to zero */
 struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
 
+/* This is a list of firewall rules associated with an
+   individual ftp session.  We use this list to remove
+   the rules when the control channel goes away */
+struct fwrule {
+       struct fwrule *next;
+       struct ip_fwuser fwuser;
+};
+
+struct ftp_app_data {
+       int look_for_passive;
+       struct fwrule *head;
+};
+
 /*
  *     Debug level
  */
@@ -70,12 +88,20 @@
 
 MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
 
-/*     Dummy variable */
-static int masq_ftp_pasv;
+static char *chain = IP_FW_LABEL_FORWARD;
+MODULE_PARM(chain, "s");
 
 static int
 masq_ftp_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
 {
+       struct ftp_app_data *app_data;
+
+       app_data = kmalloc(sizeof(struct ftp_app_data), GFP_ATOMIC);
+       if (app_data) {
+               app_data->look_for_passive = 0;
+               app_data->head = NULL;
+               ms->app_data = app_data;
+       }
         MOD_INC_USE_COUNT;
         return 0;
 }
@@ -83,10 +109,75 @@
 static int
 masq_ftp_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
 {
+       struct fwrule *current_rule, *next;
+       struct ftp_app_data *app_data;
+       struct ip_fwchange fwchange;
+
+       app_data = ms->app_data;
+       if (app_data) {
+               current_rule = app_data->head;
+               while (current_rule) {
+                       strcpy(fwchange.fwc_label, chain);
+                       memcpy(&fwchange.fwc_rule, &current_rule->fwuser, 
+sizeof(struct ip_fwuser));
+                       ip_fw_ctl(IP_FW_DELETE, &fwchange, sizeof(fwchange));
+
+                       next = current_rule->next;
+                       kfree(current_rule);
+                       current_rule = next;
+               }
+               kfree(app_data);
+       }
         MOD_DEC_USE_COUNT;
         return 0;
 }
 
+/*
+ * Create a new outgoing firewall rule for the data connection
+ */
+static void
+masq_ftp_create_rule(struct ftp_app_data *app_data, __u32 saddr, __u32 daddr, __u16 
+sport, __u16 dport, char *devname)
+{
+       struct fwrule *new;
+       struct ip_fwnew fwnew;
+
+       new = kmalloc(sizeof(struct fwrule), GFP_ATOMIC);
+       if (!new)
+               return;
+
+       new->next = NULL;
+       new->fwuser.ipfw.fw_src.s_addr = saddr;
+       new->fwuser.ipfw.fw_dst.s_addr = daddr;
+       new->fwuser.ipfw.fw_smsk.s_addr = 0xFFFFFFFF;
+       new->fwuser.ipfw.fw_dmsk.s_addr = 0xFFFFFFFF;
+       new->fwuser.ipfw.fw_mark = 0;
+       new->fwuser.ipfw.fw_proto = IPPROTO_TCP;
+       new->fwuser.ipfw.fw_flg = 0;
+       new->fwuser.ipfw.fw_invflg = 0;
+       if (!sport) {
+               new->fwuser.ipfw.fw_spts[0] = 0;
+               new->fwuser.ipfw.fw_spts[1] = 0xffff;
+       } else {
+               new->fwuser.ipfw.fw_spts[0] = sport;
+               new->fwuser.ipfw.fw_spts[1] = sport;
+       }
+       new->fwuser.ipfw.fw_dpts[0] = dport;
+       new->fwuser.ipfw.fw_dpts[1] = dport;
+       new->fwuser.ipfw.fw_redirpt = 0;
+       new->fwuser.ipfw.fw_outputsize = 0;
+       strcpy(new->fwuser.ipfw.fw_vianame, devname);
+       new->fwuser.ipfw.fw_tosand = 0xFF;
+       new->fwuser.ipfw.fw_tosxor = 0;
+       strcpy(new->fwuser.label, IP_FW_LABEL_MASQUERADE);
+
+       fwnew.fwn_rulenum = 1;
+       memcpy(&fwnew.fwn_rule, &new->fwuser, sizeof(struct ip_fwuser));
+       strcpy(fwnew.fwn_label, chain);
+
+       ip_fw_ctl(IP_FW_INSERT, &fwnew, sizeof(fwnew));
+       new->next = app_data->head;
+       app_data->head = new;
+}
+
 int
 masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, 
__u32 maddr)
 {
@@ -101,18 +192,26 @@
        char buf[24];           /* xxx.xxx.xxx.xxx,ppp,ppp\000 */
         unsigned buf_len;
        int diff;
+       struct ftp_app_data *app_data;
+
+       app_data = ms->app_data;
+       if (!app_data)
+               return 0;
 
         skb = *skb_p;
        iph = skb->nh.iph;
         th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
-        data = (char *)&th[1];
+       data = (char *)th + sizeof(struct tcphdr);
 
-        data_limit = skb->h.raw + skb->len - 18;
-        if (skb->len >= 6 && (memcmp(data, "PASV\r\n", 6) == 0 || memcmp(data, 
"pasv\r\n", 6) == 0))
-               ms->app_data = &masq_ftp_pasv;
+       data_limit = skb->h.raw + skb->len;
 
        while (data < data_limit)
        {
+               if ((memcmp(data, "PASV\r\n", 6) == 0 || memcmp(data, "pasv\r\n", 6) 
+== 0)) {
+                       app_data->look_for_passive = 1;
+                       return 0;
+               }
+
                if (memcmp(data,"PORT ",5) && memcmp(data,"port ",5))
                {
                        data ++;
@@ -164,6 +263,8 @@
                        ip_masq_control_add(n_ms, ms);
                }
 
+               masq_ftp_create_rule(app_data, htonl(from), iph->daddr, port, 20, 
+skb->dst->dev->name);
+
                /*
                 * Replace the old PORT with the new one
                 */
@@ -237,16 +338,20 @@
        __u32 to;
        __u16 port;
        struct ip_masq *n_ms;
+       struct ftp_app_data *app_data;
 
-       if (ms->app_data != &masq_ftp_pasv)
+       app_data = ms->app_data;
+       if (!app_data || !app_data->look_for_passive)
                return 0;       /* quick exit if no outstanding PASV */
 
        skb = *skb_p;
        iph = skb->nh.iph;
        th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
-       data = (char *)&th[1];
+       data = (char *)th + sizeof(struct tcphdr);
        data_limit = skb->h.raw + skb->len;
 
+       while (data < data_limit && strncmp("227", data, 3))
+               ++data;
        while (data < data_limit && *data != ' ')
                ++data; 
        while (data < data_limit && *data == ' ')
@@ -296,6 +401,8 @@
                ip_masq_control_add(n_ms, ms);
        }
 
+       masq_ftp_create_rule(app_data, iph->daddr, htonl(to), 0, port, 
+skb->dev->name);
+
 #if 0  /* v0.12 state processing */
 
        /*
@@ -304,7 +411,8 @@
         */
        n_ms->timeout = ip_masq_expire->tcp_fin_timeout*3;
 #endif
-       ms->app_data = NULL;
+       app_data->look_for_passive = 0;
+
        ip_masq_put(n_ms);
 
        return 0;       /* no diff required for incoming packets, thank goodness */
diff -ru linux-2.2.11/net/netsyms.c linux/net/netsyms.c
--- linux-2.2.11/net/netsyms.c  Mon Aug  9 15:05:14 1999
+++ linux/net/netsyms.c Wed Aug 11 10:30:22 1999
@@ -157,6 +157,11 @@
 EXPORT_SYMBOL(sk_run_filter);
 #endif
 
+#ifdef CONFIG_FIREWALL
+#include <linux/ip_fw.h>
+EXPORT_SYMBOL(ip_fw_ctl);
+#endif
+
 EXPORT_SYMBOL(neigh_table_init);
 EXPORT_SYMBOL(neigh_table_clear);
 EXPORT_SYMBOL(__neigh_lookup);

Reply via email to