On Mon, Mar 2, 2020 at 5:19 PM John Baldwin <j...@freebsd.org> wrote: > > Author: jhb > Date: Mon Mar 2 22:19:30 2020 > New Revision: 358556 > URL: https://svnweb.freebsd.org/changeset/base/358556 > > Log: > Add support for the TFTP windowsize option described in RFC 7440. > > The windowsize option permits multiple blocks to be transmitted > before the receiver sends an ACK improving throughput for larger > files. > > Reviewed by: asomers > MFC after: 2 weeks > Sponsored by: DARPA > Differential Revision: https://reviews.freebsd.org/D23836 > > Modified: > head/libexec/tftpd/tests/functional.c > head/libexec/tftpd/tftp-file.c > head/libexec/tftpd/tftp-file.h > head/libexec/tftpd/tftp-options.c > head/libexec/tftpd/tftp-options.h > head/libexec/tftpd/tftp-transfer.c > head/libexec/tftpd/tftp-utils.c > head/libexec/tftpd/tftp-utils.h > head/libexec/tftpd/tftpd.8 > head/usr.bin/tftp/main.c > head/usr.bin/tftp/tftp.1 > > Modified: head/libexec/tftpd/tests/functional.c > ============================================================================== > --- head/libexec/tftpd/tests/functional.c Mon Mar 2 21:19:51 2020 > (r358555) > +++ head/libexec/tftpd/tests/functional.c Mon Mar 2 22:19:30 2020 > (r358556) > @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); > #include <errno.h> > #include <fcntl.h> > #include <signal.h> > +#include <stdalign.h> > #include <stdio.h> > #include <unistd.h> > > @@ -89,6 +90,13 @@ recv_ack(uint16_t blocknum) > RECV(hdr, NULL, 0); > } > > +static void > +recv_oack(const char *options, size_t options_len) > +{ > + char hdr[] = {0, 6}; > + RECV(hdr, options, options_len); > +} > + > /* > * Receive a data packet from tftpd > * @param blocknum Expected block number to be received > @@ -159,6 +167,11 @@ send_ack(uint16_t blocknum) > > } > > +/* > + * build an option string > + */ > +#define OPTION_STR(name, value) name "\000" value "\000" > + > /* > * send a read request to tftpd. > * @param filename filename as a string, absolute or relative > @@ -166,6 +179,11 @@ send_ack(uint16_t blocknum) > */ > #define SEND_RRQ(filename, mode) SEND_STR("\0\001" filename "\0" mode "\0") > > +/* > + * send a read request with options > + */ > +#define SEND_RRQ_OPT(filename, mode, options) SEND_STR("\0\001" filename > "\0" mode "\000" options) > + > /* > * send a write request to tftpd. > * @param filename filename as a string, absolute or relative > @@ -173,6 +191,11 @@ send_ack(uint16_t blocknum) > */ > #define SEND_WRQ(filename, mode) SEND_STR("\0\002" filename "\0" mode "\0") > > +/* > + * send a write request with options > + */ > +#define SEND_WRQ_OPT(filename, mode, options) SEND_STR("\0\002" filename > "\0" mode "\000" options) > + > /* Define a test case, for both IPv4 and IPv6 */ > #define TFTPD_TC_DEFINE(name, head, ...) \ > static void \ > @@ -573,6 +596,32 @@ TFTPD_TC_DEFINE(rrq_medium,) > } > > /* > + * Read a medium file with a window size of 2. > + */ > +TFTPD_TC_DEFINE(rrq_medium_window,) > +{ > + int fd; > + size_t i; > + uint32_t contents[192]; > + char options[] = OPTION_STR("windowsize", "2"); > + > + for (i = 0; i < nitems(contents); i++) > + contents[i] = i; > + > + fd = open("medium.txt", O_RDWR | O_CREAT, 0644); > + ATF_REQUIRE(fd >= 0); > + write_all(fd, contents, sizeof(contents)); > + close(fd); > + > + SEND_RRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2")); > + recv_oack(options, sizeof(options) - 1); > + send_ack(0); > + recv_data(1, (const char*)&contents[0], 512); > + recv_data(2, (const char*)&contents[128], 256); > + send_ack(2); > +} > + > +/* > * Read a file in netascii format > */ > TFTPD_TC_DEFINE(rrq_netascii,) > @@ -652,6 +701,59 @@ TFTPD_TC_DEFINE(rrq_small,) > } > > /* > + * Read a file following the example in RFC 7440. > + */ > +TFTPD_TC_DEFINE(rrq_window_rfc7440,) > +{ > + int fd; > + size_t i; > + char options[] = OPTION_STR("windowsize", "4"); > + alignas(uint32_t) char contents[13 * 512 - 4]; > + uint32_t *u32p; > + > + u32p = (uint32_t *)contents; > + for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++) > + u32p[i] = i; > + > + fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0644); > + ATF_REQUIRE(fd >= 0); > + write_all(fd, contents, sizeof(contents)); > + close(fd); > + > + SEND_RRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4")); > + recv_oack(options, sizeof(options) - 1); > + send_ack(0); > + recv_data(1, &contents[0 * 512], 512); > + recv_data(2, &contents[1 * 512], 512); > + recv_data(3, &contents[2 * 512], 512); > + recv_data(4, &contents[3 * 512], 512); > + send_ack(4); > + recv_data(5, &contents[4 * 512], 512); > + recv_data(6, &contents[5 * 512], 512); > + recv_data(7, &contents[6 * 512], 512); > + recv_data(8, &contents[7 * 512], 512); > + > + /* ACK 5 as if 6-8 were dropped. */ > + send_ack(5); > + recv_data(6, &contents[5 * 512], 512); > + recv_data(7, &contents[6 * 512], 512); > + recv_data(8, &contents[7 * 512], 512); > + recv_data(9, &contents[8 * 512], 512); > + send_ack(9); > + recv_data(10, &contents[9 * 512], 512); > + recv_data(11, &contents[10 * 512], 512); > + recv_data(12, &contents[11 * 512], 512); > + recv_data(13, &contents[12 * 512], 508); > + > + /* Drop ACK and after timeout receive 10-13. */ > + recv_data(10, &contents[9 * 512], 512); > + recv_data(11, &contents[10 * 512], 512); > + recv_data(12, &contents[11 * 512], 512); > + recv_data(13, &contents[12 * 512], 508); > + send_ack(13); > +} > + > +/* > * Try to transfer a file with an unknown mode. > */ > TFTPD_TC_DEFINE(unknown_modes,) > @@ -872,6 +974,38 @@ TFTPD_TC_DEFINE(wrq_medium,) > } > > /* > + * Write a medium file with a window size of 2. > + */ > +TFTPD_TC_DEFINE(wrq_medium_window,) > +{ > + int fd; > + size_t i; > + ssize_t r; > + uint32_t contents[192]; > + char buffer[1024]; > + char options[] = OPTION_STR("windowsize", "2"); > + > + for (i = 0; i < nitems(contents); i++) > + contents[i] = i; > + > + fd = open("medium.txt", O_RDWR | O_CREAT, 0666); > + ATF_REQUIRE(fd >= 0); > + close(fd); > + > + SEND_WRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2")); > + recv_oack(options, sizeof(options) - 1); > + send_data(1, (const char*)&contents[0], 512); > + send_data(2, (const char*)&contents[128], 256); > + recv_ack(2); > + > + fd = open("medium.txt", O_RDONLY); > + ATF_REQUIRE(fd >= 0); > + r = read(fd, buffer, sizeof(buffer)); > + close(fd); > + require_bufeq((const char*)contents, 768, buffer, r); > +} > + > +/* > * Write a file in netascii format > */ > TFTPD_TC_DEFINE(wrq_netascii,) > @@ -965,7 +1099,71 @@ TFTPD_TC_DEFINE(wrq_truncate,) > ATF_REQUIRE_EQ(sb.st_size, 0); > } > > +/* > + * Write a file following the example in RFC 7440. > + */ > +TFTPD_TC_DEFINE(wrq_window_rfc7440,) > +{ > + int fd; > + size_t i; > + ssize_t r; > + char options[] = OPTION_STR("windowsize", "4"); > + alignas(uint32_t) char contents[13 * 512 - 4];
Hey John, This alignas directive seems to break builds with GCC. Mitchell > + char buffer[sizeof(contents)]; > + uint32_t *u32p; > > + u32p = (uint32_t *)contents; > + for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++) > + u32p[i] = i; > + > + fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0666); > + ATF_REQUIRE(fd >= 0); > + close(fd); > + > + SEND_WRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4")); > + recv_oack(options, sizeof(options) - 1); > + send_data(1, &contents[0 * 512], 512); > + send_data(2, &contents[1 * 512], 512); > + send_data(3, &contents[2 * 512], 512); > + send_data(4, &contents[3 * 512], 512); > + recv_ack(4); > + send_data(5, &contents[4 * 512], 512); > + > + /* Drop 6-8. */ > + recv_ack(5); > + send_data(6, &contents[5 * 512], 512); > + send_data(7, &contents[6 * 512], 512); > + send_data(8, &contents[7 * 512], 512); > + send_data(9, &contents[8 * 512], 512); > + recv_ack(9); > + > + /* Drop 11. */ > + send_data(10, &contents[9 * 512], 512); > + send_data(12, &contents[11 * 512], 512); > + > + /* > + * We can't send 13 here as tftpd has probably already seen 12 > + * and sent the ACK of 10 if running locally. While it would > + * recover by sending another ACK of 10, our state machine > + * would be out of sync. > + */ > + > + /* Ignore ACK for 10 and resend 10-13. */ > + recv_ack(10); > + send_data(10, &contents[9 * 512], 512); > + send_data(11, &contents[10 * 512], 512); > + send_data(12, &contents[11 * 512], 512); > + send_data(13, &contents[12 * 512], 508); > + recv_ack(13); > + > + fd = open("rfc7440.txt", O_RDONLY); > + ATF_REQUIRE(fd >= 0); > + r = read(fd, buffer, sizeof(buffer)); > + close(fd); > + require_bufeq(contents, sizeof(contents), buffer, r); > +} > + > + > /* > * Main > */ > @@ -981,10 +1179,12 @@ ATF_TP_ADD_TCS(tp) > TFTPD_TC_ADD(tp, rrq_eaccess); > TFTPD_TC_ADD(tp, rrq_empty); > TFTPD_TC_ADD(tp, rrq_medium); > + TFTPD_TC_ADD(tp, rrq_medium_window); > TFTPD_TC_ADD(tp, rrq_netascii); > TFTPD_TC_ADD(tp, rrq_nonexistent); > TFTPD_TC_ADD(tp, rrq_path_max); > TFTPD_TC_ADD(tp, rrq_small); > + TFTPD_TC_ADD(tp, rrq_window_rfc7440); > TFTPD_TC_ADD(tp, unknown_modes); > TFTPD_TC_ADD(tp, unknown_opcode); > TFTPD_TC_ADD(tp, w_flag); > @@ -994,10 +1194,12 @@ ATF_TP_ADD_TCS(tp) > TFTPD_TC_ADD(tp, wrq_eaccess); > TFTPD_TC_ADD(tp, wrq_eaccess_world_readable); > TFTPD_TC_ADD(tp, wrq_medium); > + TFTPD_TC_ADD(tp, wrq_medium_window); > TFTPD_TC_ADD(tp, wrq_netascii); > TFTPD_TC_ADD(tp, wrq_nonexistent); > TFTPD_TC_ADD(tp, wrq_small); > TFTPD_TC_ADD(tp, wrq_truncate); > + TFTPD_TC_ADD(tp, wrq_window_rfc7440); > > return (atf_no_error()); > } > > Modified: head/libexec/tftpd/tftp-file.c > ============================================================================== > --- head/libexec/tftpd/tftp-file.c Mon Mar 2 21:19:51 2020 > (r358555) > +++ head/libexec/tftpd/tftp-file.c Mon Mar 2 22:19:30 2020 > (r358556) > @@ -214,6 +214,20 @@ write_close(void) > return 0; > } > > +off_t > +tell_file(void) > +{ > + > + return ftello(file); > +} > + > +int > +seek_file(off_t offset) > +{ > + > + return fseeko(file, offset, SEEK_SET); > +} > + > int > read_init(int fd, FILE *f, const char *mode) > { > > Modified: head/libexec/tftpd/tftp-file.h > ============================================================================== > --- head/libexec/tftpd/tftp-file.h Mon Mar 2 21:19:51 2020 > (r358555) > +++ head/libexec/tftpd/tftp-file.h Mon Mar 2 22:19:30 2020 > (r358556) > @@ -36,4 +36,7 @@ int read_init(int fd, FILE *f, const char *mode); > size_t read_file(char *buffer, int count); > int read_close(void); > > +int seek_file(off_t offset); > +off_t tell_file(void); > + > int synchnet(int peer); > > Modified: head/libexec/tftpd/tftp-options.c > ============================================================================== > --- head/libexec/tftpd/tftp-options.c Mon Mar 2 21:19:51 2020 > (r358555) > +++ head/libexec/tftpd/tftp-options.c Mon Mar 2 22:19:30 2020 > (r358556) > @@ -56,6 +56,7 @@ struct options options[] = { > { "blksize", NULL, NULL, option_blksize, 1 }, > { "blksize2", NULL, NULL, option_blksize2, 0 }, > { "rollover", NULL, NULL, option_rollover, 0 }, > + { "windowsize", NULL, NULL, option_windowsize, 1 }, > { NULL, NULL, NULL, NULL, 0 } > }; > > @@ -271,6 +272,41 @@ option_blksize2(int peer __unused) > if (debug&DEBUG_OPTIONS) > tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'", > options[OPT_BLKSIZE2].o_reply); > + > + return (0); > +} > + > +int > +option_windowsize(int peer) > +{ > + int size; > + > + if (options[OPT_WINDOWSIZE].o_request == NULL) > + return (0); > + > + size = atoi(options[OPT_WINDOWSIZE].o_request); > + if (size < WINDOWSIZE_MIN || size > WINDOWSIZE_MAX) { > + if (acting_as_client) { > + tftp_log(LOG_ERR, > + "Invalid windowsize (%d blocks), aborting", > + size); > + send_error(peer, EBADOP); > + return (1); > + } else { > + tftp_log(LOG_WARNING, > + "Invalid windowsize (%d blocks), ignoring > request", > + size); > + return (0); > + } > + } > + > + /* XXX: Should force a windowsize of 1 for non-seekable files. */ > + asprintf(&options[OPT_WINDOWSIZE].o_reply, "%d", size); > + windowsize = size; > + > + if (debug&DEBUG_OPTIONS) > + tftp_log(LOG_DEBUG, "Setting windowsize to '%s'", > + options[OPT_WINDOWSIZE].o_reply); > > return (0); > } > > Modified: head/libexec/tftpd/tftp-options.h > ============================================================================== > --- head/libexec/tftpd/tftp-options.h Mon Mar 2 21:19:51 2020 > (r358555) > +++ head/libexec/tftpd/tftp-options.h Mon Mar 2 22:19:30 2020 > (r358556) > @@ -42,6 +42,7 @@ int option_timeout(int peer); > int option_blksize(int peer); > int option_blksize2(int peer); > int option_rollover(int peer); > +int option_windowsize(int peer); > > extern int options_extra_enabled; > extern int options_rfc_enabled; > @@ -61,4 +62,5 @@ enum opt_enum { > OPT_BLKSIZE, > OPT_BLKSIZE2, > OPT_ROLLOVER, > + OPT_WINDOWSIZE, > }; > > Modified: head/libexec/tftpd/tftp-transfer.c > ============================================================================== > --- head/libexec/tftpd/tftp-transfer.c Mon Mar 2 21:19:51 2020 > (r358555) > +++ head/libexec/tftpd/tftp-transfer.c Mon Mar 2 22:19:30 2020 > (r358556) > @@ -48,6 +48,12 @@ __FBSDID("$FreeBSD$"); > #include "tftp-options.h" > #include "tftp-transfer.h" > > +struct block_data { > + off_t offset; > + uint16_t block; > + int size; > +}; > + > /* > * Send a file via the TFTP data session. > */ > @@ -55,54 +61,73 @@ void > tftp_send(int peer, uint16_t *block, struct tftp_stats *ts) > { > struct tftphdr *rp; > - int size, n_data, n_ack, try; > - uint16_t oldblock; > + int size, n_data, n_ack, sendtry, acktry; > + u_int i, j; > + uint16_t oldblock, windowblock; > char sendbuffer[MAXPKTSIZE]; > char recvbuffer[MAXPKTSIZE]; > + struct block_data window[WINDOWSIZE_MAX]; > > rp = (struct tftphdr *)recvbuffer; > *block = 1; > ts->amount = 0; > + windowblock = 0; > + acktry = 0; > do { > +read_block: > if (debug&DEBUG_SIMPLE) > - tftp_log(LOG_DEBUG, "Sending block %d", *block); > + tftp_log(LOG_DEBUG, "Sending block %d (window block > %d)", > + *block, windowblock); > > + window[windowblock].offset = tell_file(); > + window[windowblock].block = *block; > size = read_file(sendbuffer, segsize); > if (size < 0) { > tftp_log(LOG_ERR, "read_file returned %d", size); > send_error(peer, errno + 100); > goto abort; > } > + window[windowblock].size = size; > + windowblock++; > > - for (try = 0; ; try++) { > + for (sendtry = 0; ; sendtry++) { > n_data = send_data(peer, *block, sendbuffer, size); > - if (n_data > 0) { > - if (try == maxtimeouts) { > - tftp_log(LOG_ERR, > - "Cannot send DATA packet #%d, " > - "giving up", *block); > - return; > - } > + if (n_data == 0) > + break; > + > + if (sendtry == maxtimeouts) { > tftp_log(LOG_ERR, > - "Cannot send DATA packet #%d, trying > again", > - *block); > - continue; > + "Cannot send DATA packet #%d, " > + "giving up", *block); > + return; > } > + tftp_log(LOG_ERR, > + "Cannot send DATA packet #%d, trying again", > + *block); > + } > > + /* Only check for ACK for last block in window. */ > + if (windowblock == windowsize || size != segsize) { > n_ack = receive_packet(peer, recvbuffer, > MAXPKTSIZE, NULL, timeoutpacket); > if (n_ack < 0) { > if (n_ack == RP_TIMEOUT) { > - if (try == maxtimeouts) { > + if (acktry == maxtimeouts) { > tftp_log(LOG_ERR, > "Timeout #%d send ACK %d " > - "giving up", try, *block); > + "giving up", acktry, > *block); > return; > } > tftp_log(LOG_WARNING, > "Timeout #%d on ACK %d", > - try, *block); > - continue; > + acktry, *block); > + > + acktry++; > + ts->retries++; > + seek_file(window[0].offset); > + *block = window[0].block; > + windowblock = 0; > + goto read_block; > } > > /* Either read failure or ERROR packet */ > @@ -112,18 +137,60 @@ tftp_send(int peer, uint16_t *block, struct tftp_stats > goto abort; > } > if (rp->th_opcode == ACK) { > - ts->blocks++; > - if (rp->th_block == *block) { > - ts->amount += size; > - break; > + /* > + * Look for the ACKed block in our open > + * window. > + */ > + for (i = 0; i < windowblock; i++) { > + if (rp->th_block == window[i].block) > + break; > } > > - /* Re-synchronize with the other side */ > - (void) synchnet(peer); > - if (rp->th_block == (*block - 1)) { > + if (i == windowblock) { > + /* Did not recognize ACK. */ > + if (debug&DEBUG_SIMPLE) > + tftp_log(LOG_DEBUG, > + "ACK %d out of window", > + rp->th_block); > + > + /* Re-synchronize with the other side > */ > + (void) synchnet(peer); > + > + /* Resend the current window. */ > ts->retries++; > - continue; > + seek_file(window[0].offset); > + *block = window[0].block; > + windowblock = 0; > + goto read_block; > } > + > + /* ACKed at least some data. */ > + acktry = 0; > + for (j = 0; j <= i; j++) { > + if (debug&DEBUG_SIMPLE) > + tftp_log(LOG_DEBUG, > + "ACKed block %d", > + window[j].block); > + ts->blocks++; > + ts->amount += window[j].size; > + } > + > + /* > + * Partial ACK. Rewind state to first > + * un-ACKed block. > + */ > + if (i + 1 != windowblock) { > + if (debug&DEBUG_SIMPLE) > + tftp_log(LOG_DEBUG, > + "Partial ACK"); > + seek_file(window[i + 1].offset); > + *block = window[i + 1].block; > + windowblock = 0; > + ts->retries++; > + goto read_block; > + } > + > + windowblock = 0; > } > > } > @@ -161,31 +228,35 @@ tftp_receive(int peer, uint16_t *block, struct tftp_st > struct tftphdr *firstblock, size_t fb_size) > { > struct tftphdr *rp; > - uint16_t oldblock; > - int n_data, n_ack, writesize, i, retry; > + uint16_t oldblock, windowstart; > + int n_data, n_ack, writesize, i, retry, windowblock; > char recvbuffer[MAXPKTSIZE]; > > ts->amount = 0; > + windowblock = 0; > > if (firstblock != NULL) { > writesize = write_file(firstblock->th_data, fb_size); > ts->amount += writesize; > - for (i = 0; ; i++) { > - n_ack = send_ack(peer, *block); > - if (n_ack > 0) { > - if (i == maxtimeouts) { > + windowblock++; > + if (windowsize == 1 || fb_size != segsize) { > + for (i = 0; ; i++) { > + n_ack = send_ack(peer, *block); > + if (n_ack > 0) { > + if (i == maxtimeouts) { > + tftp_log(LOG_ERR, > + "Cannot send ACK packet > #%d, " > + "giving up", *block); > + return; > + } > tftp_log(LOG_ERR, > - "Cannot send ACK packet #%d, " > - "giving up", *block); > - return; > + "Cannot send ACK packet #%d, > trying again", > + *block); > + continue; > } > - tftp_log(LOG_ERR, > - "Cannot send ACK packet #%d, trying > again", > - *block); > - continue; > - } > > - break; > + break; > + } > } > > if (fb_size != segsize) { > @@ -216,7 +287,8 @@ tftp_receive(int peer, uint16_t *block, struct tftp_st > for (retry = 0; ; retry++) { > if (debug&DEBUG_SIMPLE) > tftp_log(LOG_DEBUG, > - "Receiving DATA block %d", *block); > + "Receiving DATA block %d (window block > %d)", > + *block, windowblock); > > n_data = receive_packet(peer, recvbuffer, > MAXPKTSIZE, NULL, timeoutpacket); > @@ -232,6 +304,7 @@ tftp_receive(int peer, uint16_t *block, struct tftp_st > "Timeout #%d on DATA block %d", > retry, *block); > send_ack(peer, oldblock); > + windowblock = 0; > continue; > } > > @@ -247,19 +320,42 @@ tftp_receive(int peer, uint16_t *block, struct tftp_st > if (rp->th_block == *block) > break; > > + /* > + * Ignore duplicate blocks within the > + * window. > + * > + * This does not handle duplicate > + * blocks during a rollover as > + * gracefully, but that should still > + * recover eventually. > + */ > + if (*block > windowsize) > + windowstart = *block - windowsize; > + else > + windowstart = 0; > + if (rp->th_block > windowstart && > + rp->th_block < *block) { > + if (debug&DEBUG_SIMPLE) > + tftp_log(LOG_DEBUG, > + "Ignoring duplicate DATA block > %d", > + rp->th_block); > + windowblock++; > + retry = 0; > + continue; > + } > + > tftp_log(LOG_WARNING, > "Expected DATA block %d, got block %d", > *block, rp->th_block); > > /* Re-synchronize with the other side */ > (void) synchnet(peer); > - if (rp->th_block == (*block-1)) { > - tftp_log(LOG_INFO, "Trying to sync"); > - *block = oldblock; > - ts->retries++; > - goto send_ack; /* rexmit */ > - } > > + tftp_log(LOG_INFO, "Trying to sync"); > + *block = oldblock; > + ts->retries++; > + goto send_ack; /* rexmit */ > + > } else { > tftp_log(LOG_WARNING, > "Expected DATA block, got %s block", > @@ -282,7 +378,11 @@ tftp_receive(int peer, uint16_t *block, struct tftp_st > if (n_data != segsize) > write_close(); > } > + windowblock++; > > + /* Only send ACKs for the last block in the window. */ > + if (windowblock < windowsize && n_data == segsize) > + continue; > send_ack: > for (i = 0; ; i++) { > n_ack = send_ack(peer, *block); > @@ -301,6 +401,9 @@ send_ack: > continue; > } > > + if (debug&DEBUG_SIMPLE) > + tftp_log(LOG_DEBUG, "Sent ACK for %d", > *block); > + windowblock = 0; > break; > } > gettimeofday(&(ts->tstop), NULL); > > Modified: head/libexec/tftpd/tftp-utils.c > ============================================================================== > --- head/libexec/tftpd/tftp-utils.c Mon Mar 2 21:19:51 2020 > (r358555) > +++ head/libexec/tftpd/tftp-utils.c Mon Mar 2 22:19:30 2020 > (r358556) > @@ -51,6 +51,7 @@ int timeoutnetwork = MAX_TIMEOUTS * TIMEOUT; > int maxtimeouts = MAX_TIMEOUTS; > uint16_t segsize = SEGSIZE; > uint16_t pktsize = SEGSIZE + 4; > +uint16_t windowsize = WINDOWSIZE; > > int acting_as_client; > > > Modified: head/libexec/tftpd/tftp-utils.h > ============================================================================== > --- head/libexec/tftpd/tftp-utils.h Mon Mar 2 21:19:51 2020 > (r358555) > +++ head/libexec/tftpd/tftp-utils.h Mon Mar 2 22:19:30 2020 > (r358556) > @@ -46,6 +46,11 @@ __FBSDID("$FreeBSD$"); > #define TIMEOUT_MAX 255 /* Maximum timeout value */ > #define MIN_TIMEOUTS 3 > > +/* For the windowsize option */ > +#define WINDOWSIZE 1 > +#define WINDOWSIZE_MIN 1 > +#define WINDOWSIZE_MAX 65535 > + > extern int timeoutpacket; > extern int timeoutnetwork; > extern int maxtimeouts; > @@ -53,6 +58,7 @@ int settimeouts(int timeoutpacket, int timeoutnetwork, > > extern uint16_t segsize; > extern uint16_t pktsize; > +extern uint16_t windowsize; > > extern int acting_as_client; > > > Modified: head/libexec/tftpd/tftpd.8 > ============================================================================== > --- head/libexec/tftpd/tftpd.8 Mon Mar 2 21:19:51 2020 (r358555) > +++ head/libexec/tftpd/tftpd.8 Mon Mar 2 22:19:30 2020 (r358556) > @@ -28,7 +28,7 @@ > .\" @(#)tftpd.8 8.1 (Berkeley) 6/4/93 > .\" $FreeBSD$ > .\" > -.Dd June 22, 2011 > +.Dd March 2, 2020 > .Dt TFTPD 8 > .Os > .Sh NAME > @@ -245,6 +245,9 @@ The following RFC's are supported: > .Rs > .%T RFC 2349: TFTP Timeout Interval and Transfer Size Options > .Re > +.Rs > +.%T RFC 7440: TFTP Windowsize Option > +.Re > .Pp > The non-standard > .Cm rollover > @@ -291,6 +294,9 @@ Edwin Groothuis <ed...@freebsd.org> performed a major > and > .Xr tftp 1 > code to support RFC2348. > +.Pp > +Support for the windowsize option (RFC7440) was introduced in > +.Fx 13.0 . > .Sh NOTES > Files larger than 33,553,919 octets (65535 blocks, last one <512 > octets) cannot be correctly transferred without client and server > > Modified: head/usr.bin/tftp/main.c > ============================================================================== > --- head/usr.bin/tftp/main.c Mon Mar 2 21:19:51 2020 (r358555) > +++ head/usr.bin/tftp/main.c Mon Mar 2 22:19:30 2020 (r358556) > @@ -114,6 +114,7 @@ static void setblocksize2(int, char **); > static void setoptions(int, char **); > static void setrollover(int, char **); > static void setpacketdrop(int, char **); > +static void setwindowsize(int, char **); > > static void command(bool, EditLine *, History *, HistEvent *) __dead2; > static const char *command_prompt(void); > @@ -158,6 +159,7 @@ static struct cmd cmdtab[] = { > "enable or disable RFC2347 style options" }, > { "help", help, "print help information" }, > { "packetdrop", setpacketdrop, "artificial packetloss feature" }, > + { "windowsize", setwindowsize, "set windowsize[*]" }, > { "?", help, "print help information" }, > { NULL, NULL, NULL } > }; > @@ -1068,4 +1070,28 @@ setpacketdrop(int argc, char *argv[]) > > printf("Randomly %d in 100 packets will be dropped\n", > packetdroppercentage); > +} > + > +static void > +setwindowsize(int argc, char *argv[]) > +{ > + > + if (!options_rfc_enabled) > + printf("RFC2347 style options are not enabled " > + "(but proceeding anyway)\n"); > + > + if (argc != 1) { > + int size = atoi(argv[1]); > + > + if (size < WINDOWSIZE_MIN || size > WINDOWSIZE_MAX) { > + printf("Windowsize should be between %d and %d " > + "blocks.\n", WINDOWSIZE_MIN, WINDOWSIZE_MAX); > + return; > + } else { > + asprintf(&options[OPT_WINDOWSIZE].o_request, "%d", > + size); > + } > + } > + printf("Windowsize is now %s blocks.\n", > + options[OPT_WINDOWSIZE].o_request); > } > > Modified: head/usr.bin/tftp/tftp.1 > ============================================================================== > --- head/usr.bin/tftp/tftp.1 Mon Mar 2 21:19:51 2020 (r358555) > +++ head/usr.bin/tftp/tftp.1 Mon Mar 2 22:19:30 2020 (r358556) > @@ -28,7 +28,7 @@ > .\" @(#)tftp.1 8.2 (Berkeley) 4/18/94 > .\" $FreeBSD$ > .\" > -.Dd Aug 22, 2018 > +.Dd March 2, 2020 > .Dt TFTP 1 > .Os > .Sh NAME > @@ -216,6 +216,14 @@ Toggle packet tracing. > .Pp > .It Cm verbose > Toggle verbose mode. > +.Pp > +.It Cm windowsize Op Ar size > +Sets the TFTP windowsize option in TFTP Read Request or Write Request > packets to > +.Op Ar size > +blocks as specified in RFC 7440. > +Valid values are between 1 and 65535. > +If no windowsize is specified, > +then the default windowsize of 1 block will be used. > .El > .Sh SEE ALSO > .Xr tftpd 8 > @@ -235,6 +243,9 @@ The following RFC's are supported: > .Re > .Rs > .%T RFC 3617: Uniform Resource Identifier (URI) Scheme and Applicability > Statement for the Trivial File Transfer Protocol (TFTP) > +.Re > +.Rs > +.%T RFC 7440: TFTP Windowsize Option > .Re > .Pp > The non-standard _______________________________________________ svn-src-head@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-head To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"