Mercurial > ecos
changeset 2453:9ccbf76434a0
2007-12-21 Oyvind Harboe <oyvind.harboe@zylin.com>
* src/tftp_client.c, include/arpa/tftp.h, cdl/net.cdl: tftp
blksize negotiation support. >512 byte block sizes improves tftp
GET performance. Switched to memcpy(), which matters for larger
blocks.
| author | asl |
|---|---|
| date | Sun, 06 Jan 2008 11:13:51 +0000 |
| parents | 5298598f7174 |
| children | 1f046a6b2a6c |
| files | packages/net/common/current/ChangeLog packages/net/common/current/cdl/net.cdl packages/net/common/current/include/arpa/tftp.h packages/net/common/current/src/tftp_client.c |
| diffstat | 4 files changed, 378 insertions(+), 262 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/net/common/current/ChangeLog +++ b/packages/net/common/current/ChangeLog @@ -1,3 +1,10 @@ +2007-12-21 Oyvind Harboe <oyvind.harboe@zylin.com> + + * src/tftp_client.c, include/arpa/tftp.h, cdl/net.cdl: tftp + blksize negotiation support. >512 byte block sizes improves tftp + GET performance. Switched to memcpy(), which matters for larger + blocks. + 2007-01-15 Gary Thomas <gary@mlbassoc.com> * src/dhcp_support.c (dhcp_mgt_entry): Better handling when restarting
--- a/packages/net/common/current/cdl/net.cdl +++ b/packages/net/common/current/cdl/net.cdl @@ -167,6 +167,31 @@ cdl_package CYGPKG_NET { threads can have precedence over TFTP server processing." } + cdl_component CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET { + display "Extension to allow negotiation of big packets" + requires CYGINT_ISO_STDIO_FORMATTED_IO + flavor bool + default_value 0 + description " + Implements RFC 2348, an optional extension + to the TFTP protocol to allow the client and + server to negotiate to use bigger + packets. This can make upload/download + faster" + + cdl_option CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET_SIZE { + display "Packet size to negotiate" + flavor data + default_value 512 + legal_values 512 to 65464 + description " + Size of the packets to negotiate. In an error + free environment, bigger packets will result + in faster transfers." + } + } + + cdl_option CYGPKG_NET_TFTPD_THREAD_STACK_SIZE { display "Stack size for TFTP threads." flavor data
--- a/packages/net/common/current/include/arpa/tftp.h +++ b/packages/net/common/current/include/arpa/tftp.h @@ -70,6 +70,7 @@ #define DATA 03 /* data packet */ #define ACK 04 /* acknowledgement */ #define ERROR 05 /* error code */ +#define OACK 06 /* option acknowledge */ struct tftphdr { short th_opcode; /* packet type */
--- a/packages/net/common/current/src/tftp_client.c +++ b/packages/net/common/current/src/tftp_client.c @@ -57,6 +57,8 @@ #include <network.h> #include <arpa/tftp.h> #include <tftp_support.h> +#include <stdlib.h> +#include <stdio.h> #define min(x,y) (x<y ? x : y) @@ -66,14 +68,20 @@ // On error, *err will hold the reason. // This version uses the server name. This can be a name for DNS lookup // or a dotty or colony number format for IPv4 or IPv6. -int tftp_client_get(const char * const filename, - const char * const server, - const int port, - char *buf, - int len, - const int mode, - int * const err) { - +static int tftp_client_get_inner(char *data, + const char * const filename, + const char * const server, + const int port, + char *buf, + int len, + const int mode, + int * const err +#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET + ,int negotiate +#endif + ) { + + int blksize=SEGSIZE; int result = 0; int s=-1; int actual_len, data_len; @@ -85,7 +93,6 @@ int tftp_client_get(const char * const f int error; struct sockaddr local_addr, from_addr; - char data[SEGSIZE+sizeof(struct tftphdr)]; struct tftphdr *hdr = (struct tftphdr *)data; const char *fp; char *cp, *bp; @@ -112,6 +119,16 @@ int tftp_client_get(const char * const f } while (*fp) *cp++ = *fp++; *cp++ = '\0'; +#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET + if (negotiate) { + fp="blksize"; + while (*fp) + *cp++ = *fp++; + *cp++ = '\0'; + cp+=sprintf(cp, "%d", CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET_SIZE); + *cp++ = '\0'; + } +#endif memset(&hints,0,sizeof(hints)); hints.ai_family = PF_UNSPEC; @@ -124,74 +141,74 @@ int tftp_client_get(const char * const f addrinfo = res; while (addrinfo) { s = socket(addrinfo->ai_family, addrinfo->ai_socktype, - addrinfo->ai_protocol); + addrinfo->ai_protocol); if (s >= 0) { - memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen); - switch(addrinfo->ai_addr->sa_family) { - case AF_INET: { - struct sockaddr_in * saddr = - (struct sockaddr_in *) addrinfo->ai_addr; - struct sockaddr_in * laddr = - (struct sockaddr_in *) &local_addr; - if (port) { - saddr->sin_port = htons(port); - } - laddr->sin_port = htons(get_port++); - laddr->sin_addr.s_addr = INADDR_ANY; - break; - } + memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen); + switch(addrinfo->ai_addr->sa_family) { + case AF_INET: { + struct sockaddr_in * saddr = + (struct sockaddr_in *) addrinfo->ai_addr; + struct sockaddr_in * laddr = + (struct sockaddr_in *) &local_addr; + if (port) { + saddr->sin_port = htons(port); + } + laddr->sin_port = htons(get_port++); + laddr->sin_addr.s_addr = INADDR_ANY; + break; + } #ifdef CYGPKG_NET_INET6 - case AF_INET6: { - struct sockaddr_in6 * saddr = - (struct sockaddr_in6 *) addrinfo->ai_addr; - struct sockaddr_in6 * laddr = - (struct sockaddr_in6 *) &local_addr; - if (port) { - saddr->sin6_port = htons(port); - } - laddr->sin6_port = htons(get_port++); - laddr->sin6_addr = in6addr_any; - break; - } + case AF_INET6: { + struct sockaddr_in6 * saddr = + (struct sockaddr_in6 *) addrinfo->ai_addr; + struct sockaddr_in6 * laddr = + (struct sockaddr_in6 *) &local_addr; + if (port) { + saddr->sin6_port = htons(port); + } + laddr->sin6_port = htons(get_port++); + laddr->sin6_addr = in6addr_any; + break; + } #endif - default: - *err = TFTP_NETERR; - goto out; - } - - if (bind(s,&local_addr,addrinfo->ai_addrlen) < 0) { + default: *err = TFTP_NETERR; goto out; } - - // Send request - if (sendto(s, data, (int)(cp-data), 0, - addrinfo->ai_addr, - addrinfo->ai_addrlen) < 0) { - // Problem sending request - *err = TFTP_NETERR; - goto nextaddr; - } - // Read data - bp = buf; - while (true) { - timeout.tv_sec = TFTP_TIMEOUT_PERIOD; - timeout.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(s, &fds); - if (select(s+1, &fds, 0, 0, &timeout) <= 0) { + if (bind(s,&local_addr,addrinfo->ai_addrlen) < 0) { + *err = TFTP_NETERR; + goto out; + } + + // Send request + if (sendto(s, data, (int)(cp-data), 0, + addrinfo->ai_addr, + addrinfo->ai_addrlen) < 0) { + // Problem sending request + *err = TFTP_NETERR; + goto nextaddr; + } + + // Read data + bp = buf; + while (true) { + timeout.tv_sec = TFTP_TIMEOUT_PERIOD; + timeout.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(s, &fds); + if (select(s+1, &fds, 0, 0, &timeout) <= 0) { total_timeouts++; if ((last_good_block == 0) && (total_timeouts > TFTP_RETRIES_MAX)) { - // Timeout - no data received. Probably no server. - *err = TFTP_TIMEOUT; - goto nextaddr; + // Timeout - no data received. Probably no server. + *err = TFTP_TIMEOUT; + goto nextaddr; } - if (total_timeouts > TFTP_TIMEOUT_MAX) { + if (total_timeouts > TFTP_TIMEOUT_MAX) { // Timeout - have received data. Network problem? *err = TFTP_TIMEOUT; goto out; - } + } if (last_good_block == 0 ) { // Send request @@ -213,72 +230,92 @@ int tftp_client_get(const char * const f goto out; } } - } else { - recv_len = sizeof(data); - from_len = sizeof(from_addr); - if ((data_len = recvfrom(s, &data, recv_len, 0, - &from_addr, &from_len)) < 0) { - // What happened? - *err = TFTP_NETERR; - goto out; - } - if (ntohs(hdr->th_opcode) == DATA) { - actual_len = 0; - if (ntohs(hdr->th_block) == (last_good_block+1)) { - // Consume this data - cp = hdr->th_data; - data_len -= 4; /* Sizeof TFTP header */ - actual_len = data_len; - result += actual_len; - while (data_len-- > 0) { - if (len-- > 0) { - *bp++ = *cp++; - } else { - // Buffer overflow - *err = TFTP_TOOLARGE; - goto out; - } - } - last_good_block++; - } else { - // To prevent an out-of-sequence packet from - // terminating transmission prematurely, set - // actual_len to a full size packet. - actual_len = SEGSIZE; - } - // Send out the ACK - hdr->th_opcode = htons(ACK); - hdr->th_block = htons(last_good_block); - if (sendto(s, data, 4 /* FIXME */, 0, - &from_addr, from_len) < 0) { - // Problem sending request - *err = TFTP_NETERR; - goto out; - } - // A short packet marks the end of the file. - if ((actual_len >= 0) && (actual_len < SEGSIZE)) { - // End of data - close(s); - freeaddrinfo(res); - return result; - } - } else - if (ntohs(hdr->th_opcode) == ERROR) { - *err = ntohs(hdr->th_code); - goto out; - } else { - // What kind of packet is this? - *err = TFTP_PROTOCOL; - goto out; - } - } - } + } else { + recv_len = blksize+sizeof(struct tftphdr); + from_len = sizeof(from_addr); + if ((data_len = recvfrom(s, data, recv_len, 0, + &from_addr, &from_len)) < 0) { + // What happened? + *err = TFTP_NETERR; + goto out; + } +#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET + if (ntohs(hdr->th_opcode) == OACK) { + // We can have only *one* option, the one we sent.. + if (strncmp(data+2, "blksize", data_len)==0) { + blksize=atol(data+2+strlen("blksize")+1); + } else { + // option ignored, use default. + } + // Send out the ACK + hdr->th_opcode = htons(ACK); + hdr->th_block = htons(last_good_block); + if (sendto(s, data, 4 /* FIXME */, 0, + &from_addr, from_len) < 0) { + // Problem sending request + *err = TFTP_NETERR; + goto out; + } + } else +#endif + if (ntohs(hdr->th_opcode) == DATA) { + actual_len = 0; + if (ntohs(hdr->th_block) == (last_good_block+1)) { + // Consume this data + cp = hdr->th_data; + data_len -= 4; /* Sizeof TFTP header */ + actual_len = data_len; + result += actual_len; + if (len<data_len) + { + // Buffer overflow + *err = TFTP_TOOLARGE; + goto out; + } + memcpy(bp, cp, data_len); + bp+=data_len; + len-=data_len; + last_good_block++; + } else { + // To prevent an out-of-sequence packet from + // terminating transmission prematurely, set + // actual_len to a full size packet. + actual_len = blksize; + } + // Send out the ACK + hdr->th_opcode = htons(ACK); + hdr->th_block = htons(last_good_block); + if (sendto(s, data, 4 /* FIXME */, 0, + &from_addr, from_len) < 0) { + // Problem sending request + *err = TFTP_NETERR; + goto out; + } + // A short packet marks the end of the file. + /* 4 = Sizeof TFTP header */ + if ((actual_len >= 0) && (actual_len < blksize)) { + // End of data + close(s); + freeaddrinfo(res); + return result; + } + } else + if (ntohs(hdr->th_opcode) == ERROR) { + *err = ntohs(hdr->th_code); + goto out; + } else { + // What kind of packet is this? + *err = TFTP_PROTOCOL; + goto out; + } + } + } } // If we got here, it means there was a problem connecting to the // server. Try the next address returned by getaddrinfo nextaddr: if (-1 != s) { - close(s); + close(s); } addrinfo=addrinfo->ai_next; } @@ -290,6 +327,52 @@ int tftp_client_get(const char * const f freeaddrinfo(res); return -1; } + + +int tftp_client_get(const char * const filename, + const char * const server, + const int port, + char *buf, + int len, + const int mode, + int * const err) { + int result; +#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET + char *data = malloc(CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET_SIZE+ + sizeof(struct tftphdr)); + if (data==NULL) { + *err=TFTP_ENOSPACE; + return -1; + } +#else + char data[SEGSIZE+sizeof(struct tftphdr)]; +#endif + result=tftp_client_get_inner(data, filename, server, + port, buf, len, mode, err +#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET + ,1 +#endif + ); + if (result<0) + { +#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET + // try without negotiating packet size. The serves that do + // not support options negotiation may or may not ignore the + // options. If they return an error in the case of options + // this code path will try without packet size negotiation. + result=tftp_client_get_inner(data, filename, server, + port, buf, len, mode, err, + 0); +#endif + } + +#ifdef CYGPKG_NET_TFTPD_CLIENT_BIG_PACKET + free(data); +#endif + + return result; +} + // // Read a file from a host into a local buffer. Returns the // number of bytes actually read, or (-1) if an error occurs. @@ -309,7 +392,7 @@ tftp_get(const char * const filename, int port; ret = inet_ntop(AF_INET, (void *)&server->sin_addr, - server_name, sizeof(server_name)); + server_name, sizeof(server_name)); if (NULL == ret) { *err = TFTP_NETERR; return -1; @@ -335,7 +418,7 @@ tftp_put(const char * const filename, int port; ret = inet_ntop(AF_INET, (void *)&server->sin_addr, - server_name, sizeof(server_name)); + server_name, sizeof(server_name)); if (NULL == ret) { *err = TFTP_NETERR; return -1; @@ -352,12 +435,12 @@ tftp_put(const char * const filename, // This version uses the server name. This can be a name for DNS lookup // or a dotty or colony number format for IPv4 or IPv6. int tftp_client_put(const char * const filename, - const char * const server, - const int port, - const char *buf, - int len, - const int mode, - int * const err) { + const char * const server, + const int port, + const char *buf, + int len, + const int mode, + int * const err) { int result = 0; int s = -1, actual_len, data_len; @@ -390,177 +473,177 @@ int tftp_client_put(const char * const f addrinfo = res; while (addrinfo) { s = socket(addrinfo->ai_family, addrinfo->ai_socktype, - addrinfo->ai_protocol); + addrinfo->ai_protocol); if (s >= 0) { - memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen); - switch(addrinfo->ai_addr->sa_family) { - case AF_INET: { - struct sockaddr_in * saddr = - (struct sockaddr_in *) addrinfo->ai_addr; - struct sockaddr_in * laddr = - (struct sockaddr_in *) &local_addr; - if (port) { - saddr->sin_port = htons(port); - } - laddr->sin_port = htons(put_port++); - laddr->sin_addr.s_addr = INADDR_ANY; - break; - } + memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen); + switch(addrinfo->ai_addr->sa_family) { + case AF_INET: { + struct sockaddr_in * saddr = + (struct sockaddr_in *) addrinfo->ai_addr; + struct sockaddr_in * laddr = + (struct sockaddr_in *) &local_addr; + if (port) { + saddr->sin_port = htons(port); + } + laddr->sin_port = htons(put_port++); + laddr->sin_addr.s_addr = INADDR_ANY; + break; + } #ifdef CYGPKG_NET_INET6 - case AF_INET6: { - struct sockaddr_in6 * saddr = - (struct sockaddr_in6 *) addrinfo->ai_addr; - struct sockaddr_in6 * laddr = - (struct sockaddr_in6 *) &local_addr; - if (port) { - saddr->sin6_port = htons(port); - } - laddr->sin6_port = htons(put_port++); - laddr->sin6_addr = in6addr_any; - break; - } + case AF_INET6: { + struct sockaddr_in6 * saddr = + (struct sockaddr_in6 *) addrinfo->ai_addr; + struct sockaddr_in6 * laddr = + (struct sockaddr_in6 *) &local_addr; + if (port) { + saddr->sin6_port = htons(port); + } + laddr->sin6_port = htons(put_port++); + laddr->sin6_addr = in6addr_any; + break; + } #endif - default: - *err = TFTP_NETERR; - goto out; - } - if (bind(s, - (struct sockaddr *)&local_addr, - addrinfo->ai_addrlen) < 0) { - // Problem setting up my end - *err = TFTP_NETERR; - goto out; - } + default: + *err = TFTP_NETERR; + goto out; + } + if (bind(s, + (struct sockaddr *)&local_addr, + addrinfo->ai_addrlen) < 0) { + // Problem setting up my end + *err = TFTP_NETERR; + goto out; + } - while (1) { - // Create initial request - hdr->th_opcode = htons(WRQ); // Create/write file - cp = (char *)&hdr->th_stuff; - fp = filename; - while (*fp) *cp++ = *fp++; - *cp++ = '\0'; - if (mode == TFTP_NETASCII) { + while (1) { + // Create initial request + hdr->th_opcode = htons(WRQ); // Create/write file + cp = (char *)&hdr->th_stuff; + fp = filename; + while (*fp) *cp++ = *fp++; + *cp++ = '\0'; + if (mode == TFTP_NETASCII) { fp = "NETASCII"; - } else if (mode == TFTP_OCTET) { + } else if (mode == TFTP_OCTET) { fp = "OCTET"; - } else { + } else { *err = TFTP_INVALID; - goto out; - } - while (*fp) *cp++ = *fp++; - *cp++ = '\0'; - // Send request - if (sendto(s, data, (int)(cp-data), 0, - addrinfo->ai_addr, - addrinfo->ai_addrlen) < 0) { + goto out; + } + while (*fp) *cp++ = *fp++; + *cp++ = '\0'; + // Send request + if (sendto(s, data, (int)(cp-data), 0, + addrinfo->ai_addr, + addrinfo->ai_addrlen) < 0) { // Problem sending request *err = TFTP_NETERR; - goto nextaddr; - } - // Wait for ACK - timeout.tv_sec = TFTP_TIMEOUT_PERIOD; - timeout.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(s, &fds); - if (select(s+1, &fds, 0, 0, &timeout) <= 0) { + goto nextaddr; + } + // Wait for ACK + timeout.tv_sec = TFTP_TIMEOUT_PERIOD; + timeout.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(s, &fds); + if (select(s+1, &fds, 0, 0, &timeout) <= 0) { if (++total_timeouts > TFTP_RETRIES_MAX) { - // Timeout - no ACK received - *err = TFTP_TIMEOUT; - goto nextaddr; + // Timeout - no ACK received + *err = TFTP_TIMEOUT; + goto nextaddr; } - } else { + } else { recv_len = sizeof(data); from_len = sizeof(from_addr); if ((data_len = recvfrom(s, &data, recv_len, 0, &from_addr, &from_len)) < 0) { - // What happened? - *err = TFTP_NETERR; - goto out; + // What happened? + *err = TFTP_NETERR; + goto out; } if (ntohs(hdr->th_opcode) == ACK) { - // Write request accepted - start sending data - break; + // Write request accepted - start sending data + break; } else - if (ntohs(hdr->th_opcode) == ERROR) { + if (ntohs(hdr->th_opcode) == ERROR) { *err = ntohs(hdr->th_code); goto out; - } else { + } else { // What kind of packet is this? - goto out; - } - } - } - - // Send data - sfp = buf; - last_good_block = 1; - while (result < len) { - // Build packet of data to send - data_len = min(SEGSIZE, len-result); - hdr->th_opcode = htons(DATA); - hdr->th_block = htons(last_good_block); - cp = hdr->th_data; - fp = sfp; - actual_len = data_len + 4; - // FIXME - what about "netascii" data? - while (data_len-- > 0) *cp++ = *fp++; - // Send data packet - if (sendto(s, data, actual_len, 0, - &from_addr, from_len) < 0) { + goto out; + } + } + } + + // Send data + sfp = buf; + last_good_block = 1; + while (result < len) { + // Build packet of data to send + data_len = min(SEGSIZE, len-result); + hdr->th_opcode = htons(DATA); + hdr->th_block = htons(last_good_block); + cp = hdr->th_data; + fp = sfp; + actual_len = data_len + 4; + // FIXME - what about "netascii" data? + while (data_len-- > 0) *cp++ = *fp++; + // Send data packet + if (sendto(s, data, actual_len, 0, + &from_addr, from_len) < 0) { // Problem sending request *err = TFTP_NETERR; - goto out; - } - // Wait for ACK - timeout.tv_sec = TFTP_TIMEOUT_PERIOD; - timeout.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(s, &fds); - if (select(s+1, &fds, 0, 0, &timeout) <= 0) { + goto out; + } + // Wait for ACK + timeout.tv_sec = TFTP_TIMEOUT_PERIOD; + timeout.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(s, &fds); + if (select(s+1, &fds, 0, 0, &timeout) <= 0) { if (++total_timeouts > TFTP_TIMEOUT_MAX) { - // Timeout - no data received - *err = TFTP_TIMEOUT; - goto out; + // Timeout - no data received + *err = TFTP_TIMEOUT; + goto out; } - } else { + } else { recv_len = sizeof(data); from_len = sizeof(from_addr); if ((data_len = recvfrom(s, &data, recv_len, 0, &from_addr, &from_len)) < 0) { - // What happened? - *err = TFTP_NETERR; - goto out; + // What happened? + *err = TFTP_NETERR; + goto out; } if (ntohs(hdr->th_opcode) == ACK) { - if (ntohs(hdr->th_block) == last_good_block) { - // Advance pointers, etc - sfp = fp; - result += (actual_len - 4); - last_good_block++; - } else { - diag_printf("Send block #%d, got ACK for #%d\n", - last_good_block, ntohs(hdr->th_block)); - } + if (ntohs(hdr->th_block) == last_good_block) { + // Advance pointers, etc + sfp = fp; + result += (actual_len - 4); + last_good_block++; + } else { + diag_printf("Send block #%d, got ACK for #%d\n", + last_good_block, ntohs(hdr->th_block)); + } } else - if (ntohs(hdr->th_opcode) == ERROR) { + if (ntohs(hdr->th_opcode) == ERROR) { *err = ntohs(hdr->th_code); goto out; - } else { + } else { // What kind of packet is this? *err = TFTP_PROTOCOL; - goto out; - } - } - } - close (s); - return result; + goto out; + } + } + } + close (s); + return result; } // If we got here, it means there was a problem connecting to the // server. Try the next address returned by getaddrinfo nextaddr: if (-1 != s) { - close(s); + close(s); } addrinfo=addrinfo->ai_next; }
