NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

bin/60443: ftp ASCII transfers are too slow



>Number:         60443
>Category:       bin
>Synopsis:       ftp ASCII transfers are too slow
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Jul 12 08:00:00 +0000 2026
>Originator:     RVP
>Release:        NetBSD/amd64 11.99.6
>Organization:
>Environment:
NetBSD/amd64 11.99.6
>Description:
FTP ASCII transfers seem to interact badly with Nagle's algorithm
because ASCII xfers rely on libc's BUFSIZ (1KB) internal buffer.

If you write 1KB at a time (relatively slowly because you're doing
getc(3)/putc(3) to locale '\n'), then it looks the 200ms Nagle timeout
comes into play.

At that's what it seems to me from looking at the tcpdump packet times.
>How-To-Repeat:
1. Create a big text file.
   dd if=/dev/urandom bs=1m count=100 | sed -n l > in.txt

2. Run ftpd.
   /usr/libexec/ftpd -Ddl

3. Run tcpdump to capture packet times.

4. Get or Put the file--active or passive mode don't matter.
   Reduce `xferbuf' to 8192 to make the xfer crawl real quick.
   Also turn `progress' off. so that interrupts don't confound things.
>Fix:
You need 2 patches. One for ftp(1) and a similar one for ftpd(8).

---START patch ftp(1)---
diff -urN usr.bin/ftp.orig/ftp.c usr.bin/ftp/ftp.c
--- usr.bin/ftp.orig/ftp.c	2026-02-07 03:55:18.973125260 +0000
+++ usr.bin/ftp/ftp.c	2026-02-08 15:51:14.124404272 +0000
@@ -1728,6 +1728,23 @@
 	return (1);
 }
 
+static FILE *
+ftp_fdopen(int fd, const char *lmode)
+{
+	enum { BSIZ = XFERBUFMIN * 4 };	/* 4K (any >MTU) */
+	FILE *fp;
+	void* buf;
+
+	if ((fp = fdopen(fd, lmode)) == NULL)
+		return NULL;
+	if ((buf = ftp_malloc(BSIZ)) == NULL) {
+		fclose(fp);
+		return NULL;	
+	}
+	(void)setvbuf(fp, buf, _IOFBF, BSIZ);
+	return fp;
+}
+
 FILE *
 dataconn(const char *lmode)
 {
@@ -1738,7 +1755,7 @@
 	socklen_t	fromlen;
 
 	if (passivemode)	/* passive data connection */
-		return (fdopen(data, lmode));
+		return (ftp_fdopen(data, lmode));
 
 				/* active mode data connection */
 
@@ -1798,7 +1815,7 @@
 		}
 	}
 #endif
-	return (fdopen(data, lmode));
+	return (ftp_fdopen(data, lmode));
 
  dataconn_failed:
 	(void)close(data);
---END patch ftp(1)---

---START patch ftpd(8)---
diff -urN libexec/ftpd.orig/ftpd.c libexec/ftpd/ftpd.c
--- libexec/ftpd.orig/ftpd.c	2025-06-28 04:00:41.032045996 +0000
+++ libexec/ftpd/ftpd.c	2026-02-08 15:35:04.378214731 +0000
@@ -1917,6 +1917,23 @@
 }
 
 static FILE *
+ftp_fdopen(int fd, const char *lmode)
+{
+	enum { BSIZ = FTP_BUFLEN * 8 };	/* 4KB (any >MTU) */
+	FILE *fp;
+	void* buf;
+
+	if ((fp = fdopen(fd, lmode)) == NULL)
+		return NULL;
+	if ((buf = malloc(BSIZ)) == NULL) {
+		fclose(fp);
+		return NULL;	
+	}
+	(void)setvbuf(fp, buf, _IOFBF, BSIZ);
+	return fp;
+}
+
+static FILE *
 getdatasock(const char *fmode)
 {
 	int		on, s, t, tries;
@@ -1924,7 +1941,7 @@
 
 	on = 1;
 	if (data >= 0)
-		return (fdopen(data, fmode));
+		return (ftp_fdopen(data, fmode));
 	if (! dropprivs) {
 		if (seteuid((uid_t)0) < 0) {
 			syslog(LOG_NOTICE, "getdatasock: can't seteuid 0: %m");
@@ -1979,7 +1996,7 @@
 			syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
 	}
 #endif
-	return (fdopen(s, fmode));
+	return (ftp_fdopen(s, fmode));
  bad:
 		/* Return the real value of errno (close may change it) */
 	t = errno;
@@ -2044,13 +2061,13 @@
 #endif
 		reply(150, "Opening %s mode data connection for '%s'%s.",
 		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
-		return (fdopen(pdata, fmode));
+		return (ftp_fdopen(pdata, fmode));
 	}
 	if (data >= 0) {
 		reply(125, "Using existing data connection for '%s'%s.",
 		    name, sizebuf);
 		usedefault = 1;
-		return (fdopen(data, fmode));
+		return (ftp_fdopen(data, fmode));
 	}
 	if (usedefault)
 		data_dest = his_addr;
---END patch ftpd(8)---




Home | Main Index | Thread Index | Old Index