summaryrefslogtreecommitdiffstats
path: root/src/proto
diff options
context:
space:
mode:
authorMichael Brown2005-06-01 15:13:05 +0200
committerMichael Brown2005-06-01 15:13:05 +0200
commit53a4436d94886b6b6b0d931158db2c79490591a1 (patch)
tree9f5a3b6f6644ed39d63758f66e8acbda672c903f /src/proto
parentNow have enough functions to implement a standard TFTP client in around 50 (diff)
downloadipxe-53a4436d94886b6b6b0d931158db2c79490591a1.tar.gz
ipxe-53a4436d94886b6b6b0d931158db2c79490591a1.tar.xz
ipxe-53a4436d94886b6b6b0d931158db2c79490591a1.zip
TFTP upgraded to use a core function library (in tftpcore.c) which will be
shared between TFTP, TFTM and MTFTP protocols.
Diffstat (limited to 'src/proto')
-rw-r--r--src/proto/tcp.c2
-rw-r--r--src/proto/tftm.c4
-rw-r--r--src/proto/tftp.c232
-rw-r--r--src/proto/tftpcore.c42
4 files changed, 127 insertions, 153 deletions
diff --git a/src/proto/tcp.c b/src/proto/tcp.c
index 197bfce2..1e0531d5 100644
--- a/src/proto/tcp.c
+++ b/src/proto/tcp.c
@@ -1,7 +1,7 @@
#include "etherboot.h"
#include "ip.h"
#include "tcp.h"
-
+#include "nic.h"
void build_tcp_hdr(unsigned long destip, unsigned int srcsock,
unsigned int destsock, long send_seq, long recv_seq,
diff --git a/src/proto/tftm.c b/src/proto/tftm.c
index 426d0dda..6dbf1af2 100644
--- a/src/proto/tftm.c
+++ b/src/proto/tftm.c
@@ -1,3 +1,5 @@
+#if 0
+
/**************************************************************************
*
* proto_tftm.c -- Etherboot Multicast TFTP
@@ -481,3 +483,5 @@ static struct protocol tftm_protocol __protocol = {
.default_port = TFTM_PORT,
.load = url_tftm,
};
+
+#endif
diff --git a/src/proto/tftp.c b/src/proto/tftp.c
index b154f575..e5bf5a83 100644
--- a/src/proto/tftp.c
+++ b/src/proto/tftp.c
@@ -1,169 +1,111 @@
#include "etherboot.h"
-#include "in.h"
-#include "nic.h"
#include "proto.h"
+#include "errno.h"
#include "tftp.h"
+#include "tftpcore.h"
-/* Utility function for tftp_block() */
-static int await_tftp ( int ival, void *ptr __unused,
- unsigned short ptype __unused, struct iphdr *ip,
- struct udphdr *udp, struct tcphdr *tcp __unused ) {
- if ( ! udp ) {
- return 0;
- }
- if ( arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr )
- return 0;
- if ( ntohs ( udp->dest ) != ival )
- return 0;
- return 1;
-}
+/** @file
+ *
+ * TFTP protocol
+ */
-/*
- * Download a single block via TFTP. This function is non-static so
- * that pxe_export.c can call it.
+/**
+ * Process a TFTP block
*
*/
-int tftp_block ( struct tftpreq_info_t *request,
- struct tftpblk_info_t *block ) {
- static struct sockaddr_in server;
- static unsigned short lport = 2000; /* local port */
- struct tftp_t *rcvd = NULL;
- static struct tftpreq_t xmit;
- static unsigned short xmitlen = 0;
- static unsigned short blockidx = 0; /* Last block received */
- static unsigned short retry = 0; /* Retry attempts on last block */
- static int blksize = 0;
- unsigned short recvlen = 0;
+static inline int process_tftp_data ( struct tftp_state *state,
+ struct tftp_data *data,
+ struct buffer *buffer,
+ int *eof ) {
+ unsigned int blksize;
- /* If this is a new request (i.e. if name is set), fill in
- * transmit block with RRQ and send it.
- */
- if ( request ) {
- rx_qdrain(); /* Flush receive queue */
- xmit.opcode = htons(TFTP_RRQ);
- xmitlen = (void*)&xmit.u.rrq - (void*)&xmit +
- sprintf((char*)xmit.u.rrq, "%s%coctet%cblksize%c%d",
- request->name, 0, 0, 0, request->blksize)
- + 1; /* null terminator */
- blockidx = 0; /* Reset counters */
- retry = 0;
- blksize = TFTP_DEFAULTSIZE_PACKET;
- lport++; /* Use new local port */
- server = *(request->server);
- if ( !udp_transmit(server.sin_addr.s_addr, lport,
- server.sin_port, xmitlen, &xmit) )
- return (0);
+ /* Check it's the correct DATA block */
+ if ( ntohs ( data->block ) != ( state->block + 1 ) ) {
+ DBG ( "TFTP: got block %d, wanted block %d\n",
+ ntohs ( data->block ), state->block + 1 );
+ return 1;
}
- /* Exit if no transfer in progress */
- if ( !blksize ) return (0);
- /* Loop to wait until we get a packet we're interested in */
- block->data = NULL; /* Used as flag */
- while ( block->data == NULL ) {
- long timeout = rfc2131_sleep_interval ( blockidx ? TFTP_REXMT :
- TIMEOUT, retry );
- if ( !await_reply(await_tftp, lport, NULL, timeout) ) {
- /* No packet received */
- if ( retry++ > MAX_TFTP_RETRIES ) break;
- /* Retransmit last packet */
- if ( !blockidx ) lport++; /* New lport if new RRQ */
- if ( !udp_transmit(server.sin_addr.s_addr, lport,
- server.sin_port, xmitlen, &xmit) )
- return (0);
- continue; /* Back to waiting for packet */
- }
- /* Packet has been received */
- rcvd = (struct tftp_t *)&nic.packet[ETH_HLEN];
- recvlen = ntohs(rcvd->udp.len) - sizeof(struct udphdr)
- - sizeof(rcvd->opcode);
- server.sin_port = ntohs(rcvd->udp.src);
- retry = 0; /* Reset retry counter */
- switch ( htons(rcvd->opcode) ) {
- case TFTP_ERROR : {
- printf ( "TFTP error %d (%s)\n",
- ntohs(rcvd->u.err.errcode),
- rcvd->u.err.errmsg );
- return (0); /* abort */
- }
- case TFTP_OACK : {
- const char *p = rcvd->u.oack.data;
- const char *e = p + recvlen - 10; /* "blksize\0\d\0" */
-
- *((char*)(p+recvlen-1)) = '\0'; /* Force final 0 */
- if ( blockidx || !request ) break; /* Too late */
- if ( recvlen <= TFTP_MAX_PACKET ) /* sanity */ {
- /* Check for blksize option honoured */
- while ( p < e ) {
- if ( strcasecmp("blksize",p) == 0 &&
- p[7] == '\0' ) {
- blksize = strtoul(p+8,&p,10);
- p++; /* skip null */
- }
- while ( *(p++) ) {};
- }
- }
- if ( blksize < TFTP_DEFAULTSIZE_PACKET ||
- blksize > request->blksize ) {
- /* Incorrect blksize - error and abort */
- xmit.opcode = htons(TFTP_ERROR);
- xmit.u.err.errcode = 8;
- xmitlen = (void*)&xmit.u.err.errmsg
- - (void*)&xmit
- + sprintf((char*)xmit.u.err.errmsg,
- "RFC1782 error")
- + 1;
- udp_transmit(server.sin_addr.s_addr, lport,
- server.sin_port, xmitlen, &xmit);
- return (0);
- }
- } break;
- case TFTP_DATA :
- if ( ntohs(rcvd->u.data.block) != ( blockidx + 1 ) )
- break; /* Re-ACK last block sent */
- if ( recvlen > ( blksize+sizeof(rcvd->u.data.block) ) )
- break; /* Too large; ignore */
- block->data = rcvd->u.data.download;
- block->block = ++blockidx;
- block->len = recvlen - sizeof(rcvd->u.data.block);
- block->eof = ( (unsigned short)block->len < blksize );
- /* If EOF, zero blksize to indicate transfer done */
- if ( block->eof ) blksize = 0;
- break;
- default: break; /* Do nothing */
- }
- /* Send ACK */
- xmit.opcode = htons(TFTP_ACK);
- xmit.u.ack.block = htons(blockidx);
- xmitlen = TFTP_MIN_PACKET;
- udp_transmit ( server.sin_addr.s_addr, lport, server.sin_port,
- xmitlen, &xmit );
+ /* Check it's an acceptable size */
+ blksize = ( ntohs ( data->udp.len )
+ + offsetof ( typeof ( *data ), udp )
+ - offsetof ( typeof ( *data ), data ) );
+ if ( blksize > state->blksize ) {
+ DBG ( "TFTP: oversized block size %d (max %d)\n",
+ blksize, state->blksize );
+ return 1;
}
- return ( block->data ? 1 : 0 );
+ /* Place block in the buffer */
+ if ( ! fill_buffer ( buffer, data->data, state->block * state->blksize,
+ blksize ) ) {
+ DBG ( "TFTP: could not place data in buffer: %m\n" );
+ return 0;
+ }
+ /* Increment block counter */
+ state->block++;
+ /* Set EOF marker */
+ *eof = ( blksize < state->blksize );
+ return 1;
}
-/*
+/**
* Download a file via TFTP
*
*/
int tftp ( char *url __unused, struct sockaddr_in *server, char *file,
struct buffer *buffer ) {
- struct tftpreq_info_t request_data = {
- .server = server,
- .name = file,
- .blksize = TFTP_MAX_PACKET,
- };
- struct tftpreq_info_t *request = &request_data;
- struct tftpblk_info_t block;
- off_t offset = 0;
+ struct tftp_state state;
+ union tftp_any *reply;
+ int eof = 0;
+
+ /* Initialise TFTP state */
+ memset ( &state, 0, sizeof ( state ) );
+ state.server = *server;
+
+ /* Open the file */
+ if ( ! tftp_open ( &state, file, &reply ) ) {
+ DBG ( "TFTP: could not open %@:%d/%s : %m\n",
+ server->sin_addr.s_addr, server->sin_port, file );
+ return 0;
+ }
+
+ /* Process OACK, if any */
+ if ( ntohs ( reply->common.opcode ) == TFTP_OACK ) {
+ if ( ! tftp_process_opts ( &state, &reply->oack ) ) {
+ DBG ( "TFTP: option processing failed : %m\n" );
+ return 0;
+ }
+ reply = NULL;
+ }
+ /* Fetch file, a block at a time */
do {
- if ( ! tftp_block ( request, &block ) )
+ /* Get next block to process. (On the first time
+ * through, we may already have a block from
+ * tftp_open()).
+ */
+ if ( ! reply ) {
+ if ( ! tftp_ack ( &state, &reply ) ) {
+ DBG ( "TFTP: could not get next block: %m\n" );
+ return 0;
+ }
+ }
+ twiddle();
+ /* Check it's a DATA block */
+ if ( ntohs ( reply->common.opcode ) != TFTP_DATA ) {
+ DBG ( "TFTP: unexpected opcode %d\n",
+ ntohs ( reply->common.opcode ) );
+ errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
return 0;
- if ( ! fill_buffer ( buffer, block.data, offset, block.len ) )
+ }
+ /* Process the DATA block */
+ if ( ! process_tftp_data ( &state, &reply->data, buffer,
+ &eof ) )
return 0;
- twiddle();
- offset += block.len;
- request = NULL; /* Send request only once */
- } while ( ! block.eof );
+ reply = NULL;
+ } while ( ! eof );
+
+ /* ACK the final packet, as a courtesy to the server */
+ tftp_ack_nowait ( &state );
return 1;
}
diff --git a/src/proto/tftpcore.c b/src/proto/tftpcore.c
index 51ad8b43..491bab6d 100644
--- a/src/proto/tftpcore.c
+++ b/src/proto/tftpcore.c
@@ -47,21 +47,32 @@ int await_tftp ( int ival __unused, void *ptr, unsigned short ptype __unused,
/* Must have valid UDP (and, therefore, also IP) headers */
if ( ! udp ) {
+ DBG2 ( "TFTPCORE: not UDP\n" );
return 0;
}
/* Packet must come from the TFTP server */
- if ( ip->src.s_addr != state->server.sin_addr.s_addr )
+ if ( ip->src.s_addr != state->server.sin_addr.s_addr ) {
+ DBG2 ( "TFTPCORE: from %@, not from TFTP server %@\n",
+ ip->src.s_addr, state->server.sin_addr.s_addr );
return 0;
+ }
/* Packet must be addressed to the correct UDP port */
- if ( ntohs ( udp->dest ) != state->client.sin_port )
+ if ( ntohs ( udp->dest ) != state->client.sin_port ) {
+ DBG2 ( "TFTPCORE: to UDP port %d, not to TFTP port %d\n",
+ ntohs ( udp->dest ), state->client.sin_port );
return 0;
+ }
/* Packet must be addressed to us, or to our multicast
* listening address (if we have one).
*/
if ( ! ( ( ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr ) ||
( ( state->client.sin_addr.s_addr ) &&
- ( ip->dest.s_addr == state->client.sin_addr.s_addr ) ) ) )
+ ( ip->dest.s_addr == state->client.sin_addr.s_addr ) ) ) ) {
+ DBG2 ( "TFTPCORE: to %@, not to %@ (or %@)\n",
+ ip->dest.s_addr, arptable[ARP_CLIENT].ipaddr.s_addr,
+ state->client.sin_addr.s_addr );
return 0;
+ }
return 1;
}
@@ -164,7 +175,7 @@ int tftp_open ( struct tftp_state *state, const char *filename,
state->server.sin_port = TFTP_PORT;
/* Determine whether or not to use lport */
- fixed_lport = state->server.sin_port;
+ fixed_lport = state->client.sin_port;
/* Set up RRQ */
rrq.opcode = htons ( TFTP_RRQ );
@@ -202,7 +213,11 @@ int tftp_open ( struct tftp_state *state, const char *filename,
if ( await_reply ( await_tftp, 0, state, timeout ) ) {
*reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
state->server.sin_port =
- ntohs ( (*reply)->common.udp.dest );
+ ntohs ( (*reply)->common.udp.src );
+ DBG ( "TFTPCORE: got reply from %@:%d (type %d)\n",
+ state->server.sin_addr.s_addr,
+ state->server.sin_port,
+ ntohs ( (*reply)->common.opcode ) );
if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
tftp_set_errno ( &(*reply)->error );
return 0;
@@ -211,6 +226,7 @@ int tftp_open ( struct tftp_state *state, const char *filename,
}
}
+ DBG ( "TFTPCORE: open request timed out\n" );
errno = PXENV_STATUS_TFTP_OPEN_TIMEOUT;
return 0;
}
@@ -250,6 +266,8 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
const char *p;
const char *end;
+ DBG ( "TFTPCORE: processing OACK\n" );
+
/* End of options */
end = ( ( char * ) &oack->udp ) + ntohs ( oack->udp.len );
@@ -266,6 +284,7 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
return 0;
}
p++;
+ DBG ( "TFTPCORE: got blksize %d\n", state->blksize );
} else if ( strcasecmp ( "tsize", p ) == 0 ) {
p += 6;
state->tsize = strtoul ( p, &p, 10 );
@@ -275,6 +294,7 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
return 0;
}
p++;
+ DBG ( "TFTPCORE: got tsize %d\n", state->tsize );
} else if ( strcasecmp ( "multicast", p ) == 0 ) {
char *e = strchr ( p, ',' );
if ( ( ! e ) || ( e >= end ) ) {
@@ -317,7 +337,12 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
return 0;
}
p++;
+ DBG ( "TFTPCORE: got multicast %@:%d (%s)\n",
+ state->client.sin_addr.s_addr,
+ state->client.sin_port,
+ ( state->master ? "master" : "not master" ) );
} else {
+ DBG ( "TFTPCORE: unknown option \"%s\"\n", p );
p += strlen ( p ) + 1; /* skip option name */
p += strlen ( p ) + 1; /* skip option value */
}
@@ -351,6 +376,7 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
int tftp_ack_nowait ( struct tftp_state *state ) {
struct tftp_ack ack;
+ DBG ( "TFTPCORE: acknowledging data block %d\n", state->block );
ack.opcode = htons ( TFTP_ACK );
ack.block = htons ( state->block );
return udp_transmit ( state->server.sin_addr.s_addr,
@@ -396,9 +422,11 @@ int tftp_ack ( struct tftp_state *state, union tftp_any **reply ) {
DBG ( "TFTP: could not send ACK: %m\n" );
return 0;
}
- if ( await_reply ( await_tftp, 0, &state, timeout ) ) {
+ if ( await_reply ( await_tftp, 0, state, timeout ) ) {
/* We received a reply */
*reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
+ DBG ( "TFTPCORE: got reply (type %d)\n",
+ ntohs ( (*reply)->common.opcode ) );
if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
tftp_set_errno ( &(*reply)->error );
return 0;
@@ -406,7 +434,7 @@ int tftp_ack ( struct tftp_state *state, union tftp_any **reply ) {
return 1;
}
}
- DBG ( "TFTP: ACK retries exceeded\n" );
+ DBG ( "TFTP: timed out during read\n" );
errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
return 0;
}