svn commit: r358556 - in head: libexec/tftpd libexec/tftpd/tests usr.bin/tftp
Mitchell Horne
mhorne at freebsd.org
Thu Mar 5 18:09:44 UTC 2020
On Mon, Mar 2, 2020 at 5:19 PM John Baldwin <jhb at 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 <edwin at 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
More information about the svn-src-head
mailing list