david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

Update TFTP to use a struct buffer rather than a callback.

Add debug autocolourisation to TFTP.
This commit is contained in:
Michael Brown 2007-01-11 15:14:54 +00:00
parent d9204ef676
commit 0010e10ef3
3 changed files with 81 additions and 74 deletions

View File

@ -11,6 +11,7 @@
#include <gpxe/udp.h> #include <gpxe/udp.h>
#include <gpxe/async.h> #include <gpxe/async.h>
#include <gpxe/retry.h> #include <gpxe/retry.h>
#include <gpxe/buffer.h>
#define TFTP_PORT 69 /**< Default TFTP server port */ #define TFTP_PORT 69 /**< Default TFTP server port */
#define TFTP_DEFAULT_BLKSIZE 512 /**< Default TFTP data block size */ #define TFTP_DEFAULT_BLKSIZE 512 /**< Default TFTP data block size */
@ -89,17 +90,30 @@ struct tftp_session {
struct udp_connection udp; struct udp_connection udp;
/** Filename */ /** Filename */
const char *filename; const char *filename;
/** Data buffer to fill */
/** struct buffer *buffer;
* Callback function /** Requested data block size
* *
* @v tftp TFTP connection * This is the "blksize" option requested from the TFTP
* @v block Block number * server. It may or may not be honoured.
* @v data Data
* @v len Length of data
*/ */
void ( * callback ) ( struct tftp_session *tftp, unsigned int block, unsigned int request_blksize;
void *data, size_t len );
/** Data block size
*
* This is the "blksize" option negotiated with the TFTP
* server. (If the TFTP server does not support TFTP options,
* this will default to 512).
*/
unsigned int blksize;
/** File size
*
* This is the value returned in the "tsize" option from the
* TFTP server. If the TFTP server does not support the
* "tsize" option, this value will be zero.
*/
unsigned long tsize;
/** /**
* Transfer ID * Transfer ID
* *
@ -119,26 +133,6 @@ struct tftp_session {
* (i.e. that no blocks have yet been received). * (i.e. that no blocks have yet been received).
*/ */
int state; int state;
/** Requested data block size
*
* This is the "blksize" option requested from the TFTP
* server. It may or may not be honoured.
*/
unsigned int request_blksize;
/** Data block size
*
* This is the "blksize" option negotiated with the TFTP
* server. (If the TFTP server does not support TFTP options,
* this will default to 512).
*/
unsigned int blksize;
/** File size
*
* This is the value returned in the "tsize" option from the
* TFTP server. If the TFTP server does not support the
* "tsize" option, this value will be zero.
*/
unsigned long tsize;
/** Asynchronous operation for this session */ /** Asynchronous operation for this session */
struct async_operation aop; struct async_operation aop;

View File

@ -59,10 +59,11 @@ static int tftp_process_blksize ( struct tftp_session *tftp,
tftp->blksize = strtoul ( value, &end, 10 ); tftp->blksize = strtoul ( value, &end, 10 );
if ( *end ) { if ( *end ) {
DBG ( "TFTP %p got invalid blksize \"%s\"\n", tftp, value ); DBGC ( tftp, "TFTP %p got invalid blksize \"%s\"\n",
tftp, value );
return -EINVAL; return -EINVAL;
} }
DBG ( "TFTP %p blksize=%d\n", tftp, tftp->blksize ); DBGC ( tftp, "TFTP %p blksize=%d\n", tftp, tftp->blksize );
return 0; return 0;
} }
@ -80,10 +81,11 @@ static int tftp_process_tsize ( struct tftp_session *tftp,
tftp->tsize = strtoul ( value, &end, 10 ); tftp->tsize = strtoul ( value, &end, 10 );
if ( *end ) { if ( *end ) {
DBG ( "TFTP %p got invalid tsize \"%s\"\n", tftp, value ); DBGC ( tftp, "TFTP %p got invalid tsize \"%s\"\n",
tftp, value );
return -EINVAL; return -EINVAL;
} }
DBG ( "TFTP %p tsize=%ld\n", tftp, tftp->tsize ); DBGC ( tftp, "TFTP %p tsize=%ld\n", tftp, tftp->tsize );
return 0; return 0;
} }
@ -112,8 +114,8 @@ static int tftp_process_option ( struct tftp_session *tftp,
return option->process ( tftp, value ); return option->process ( tftp, value );
} }
DBG ( "TFTP %p received unknown option \"%s\" = \"%s\"\n", DBGC ( tftp, "TFTP %p received unknown option \"%s\" = \"%s\"\n",
tftp, name, value ); tftp, name, value );
return -EINVAL; return -EINVAL;
} }
@ -201,7 +203,7 @@ static int tftp_send_rrq ( struct tftp_session *tftp, void *buf, size_t len ) {
void *data; void *data;
void *end; void *end;
DBG ( "TFTP %p requesting \"%s\"\n", tftp, tftp->filename ); DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, tftp->filename );
data = rrq->data; data = rrq->data;
end = ( buf + len ); end = ( buf + len );
@ -218,7 +220,7 @@ static int tftp_send_rrq ( struct tftp_session *tftp, void *buf, size_t len ) {
return udp_send ( &tftp->udp, buf, ( data - buf ) ); return udp_send ( &tftp->udp, buf, ( data - buf ) );
overflow: overflow:
DBG ( "TFTP %p RRQ out of space\n", tftp ); DBGC ( tftp, "TFTP %p RRQ out of space\n", tftp );
return -ENOBUFS; return -ENOBUFS;
} }
@ -239,12 +241,13 @@ static int tftp_rx_oack ( struct tftp_session *tftp, void *buf, size_t len ) {
/* Sanity check */ /* Sanity check */
if ( len < sizeof ( *oack ) ) { if ( len < sizeof ( *oack ) ) {
DBG ( "TFTP %p received underlength OACK packet length %d\n", DBGC ( tftp, "TFTP %p received underlength OACK packet "
tftp, len ); "length %d\n", tftp, len );
return -EINVAL; return -EINVAL;
} }
if ( end[-1] != '\0' ) { if ( end[-1] != '\0' ) {
DBG ( "TFTP %p received OACK missing final NUL\n", tftp ); DBGC ( tftp, "TFTP %p received OACK missing final NUL\n",
tftp );
return -EINVAL; return -EINVAL;
} }
@ -253,8 +256,8 @@ static int tftp_rx_oack ( struct tftp_session *tftp, void *buf, size_t len ) {
while ( name < end ) { while ( name < end ) {
value = ( name + strlen ( name ) + 1 ); value = ( name + strlen ( name ) + 1 );
if ( value == end ) { if ( value == end ) {
DBG ( "TFTP %p received OACK missing value for option " DBGC ( tftp, "TFTP %p received OACK missing value "
"\"%s\"\n", tftp, name ); "for option \"%s\"\n", tftp, name );
return -EINVAL; return -EINVAL;
} }
if ( ( rc = tftp_process_option ( tftp, name, value ) ) != 0 ) if ( ( rc = tftp_process_option ( tftp, name, value ) ) != 0 )
@ -279,19 +282,28 @@ static int tftp_rx_oack ( struct tftp_session *tftp, void *buf, size_t len ) {
static int tftp_rx_data ( struct tftp_session *tftp, void *buf, size_t len ) { static int tftp_rx_data ( struct tftp_session *tftp, void *buf, size_t len ) {
struct tftp_data *data = buf; struct tftp_data *data = buf;
unsigned int block; unsigned int block;
size_t data_offset;
size_t data_len; size_t data_len;
int rc;
/* Sanity check */ /* Sanity check */
if ( len < sizeof ( *data ) ) { if ( len < sizeof ( *data ) ) {
DBG ( "TFTP %p received underlength DATA packet length %d\n", DBGC ( tftp, "TFTP %p received underlength DATA packet "
tftp, len ); "length %d\n", tftp, len );
return -EINVAL; return -EINVAL;
} }
/* Pass to callback */ /* Fill data buffer */
block = ntohs ( data->block ); block = ntohs ( data->block );
data_offset = ( ( block - 1 ) * tftp->blksize );
data_len = ( len - offsetof ( typeof ( *data ), data ) ); data_len = ( len - offsetof ( typeof ( *data ), data ) );
tftp->callback ( tftp, block, data->data, data_len ); if ( ( rc = fill_buffer ( tftp->buffer, data->data, data_offset,
data_len ) ) != 0 ) {
DBGC ( tftp, "TFTP %p could not fill data buffer: %s\n",
tftp, strerror ( rc ) );
tftp_done ( tftp, rc );
return rc;
}
/* Mark block as received */ /* Mark block as received */
tftp_received ( tftp, block ); tftp_received ( tftp, block );
@ -334,13 +346,13 @@ static int tftp_rx_error ( struct tftp_session *tftp, void *buf, size_t len ) {
/* Sanity check */ /* Sanity check */
if ( len < sizeof ( *error ) ) { if ( len < sizeof ( *error ) ) {
DBG ( "TFTP %p received underlength ERROR packet length %d\n", DBGC ( tftp, "TFTP %p received underlength ERROR packet "
tftp, len ); "length %d\n", tftp, len );
return -EINVAL; return -EINVAL;
} }
DBG ( "TFTP %p received ERROR packet with code %d, message \"%s\"\n", DBGC ( tftp, "TFTP %p received ERROR packet with code %d, message "
tftp, ntohs ( error->errcode ), error->errmsg ); "\"%s\"\n", tftp, ntohs ( error->errcode ), error->errmsg );
/* Determine final operation result */ /* Determine final operation result */
err = ntohs ( error->errcode ); err = ntohs ( error->errcode );
@ -392,30 +404,31 @@ static int tftp_newdata ( struct udp_connection *conn, void *data, size_t len,
struct tftp_common *common = data; struct tftp_common *common = data;
if ( len < sizeof ( *common ) ) { if ( len < sizeof ( *common ) ) {
DBG ( "TFTP %p received underlength packet length %d\n", DBGC ( tftp, "TFTP %p received underlength packet length %d\n",
tftp, len ); tftp, len );
return -EINVAL; return -EINVAL;
} }
/* Filter by TID. Set TID on first response received */ /* Filter by TID. Set TID on first response received */
if ( tftp->tid ) { if ( tftp->tid ) {
if ( tftp->tid != st_src->st_port ) { if ( tftp->tid != st_src->st_port ) {
DBG ( "TFTP %p received packet from wrong port " DBGC ( tftp, "TFTP %p received packet from wrong port "
"(got %d, wanted %d)\n", tftp, "(got %d, wanted %d)\n", tftp,
ntohs ( st_src->st_port ), ntohs ( tftp->tid ) ); ntohs ( st_src->st_port ), ntohs ( tftp->tid ));
return -EINVAL; return -EINVAL;
} }
} else { } else {
tftp->tid = st_src->st_port; tftp->tid = st_src->st_port;
DBG ( "TFTP %p using remote port %d\n", tftp, DBGC ( tftp, "TFTP %p using remote port %d\n", tftp,
ntohs ( tftp->tid ) ); ntohs ( tftp->tid ) );
udp_connect_port ( &tftp->udp, tftp->tid ); udp_connect_port ( &tftp->udp, tftp->tid );
} }
/* Filter by source address */ /* Filter by source address */
if ( memcmp ( st_src, udp_peer ( &tftp->udp ), if ( memcmp ( st_src, udp_peer ( &tftp->udp ),
sizeof ( *st_src ) ) != 0 ) { sizeof ( *st_src ) ) != 0 ) {
DBG ( "TFTP %p received packet from foreign source\n", tftp ); DBGC ( tftp, "TFTP %p received packet from foreign source\n",
tftp );
return -EINVAL; return -EINVAL;
} }
@ -427,8 +440,8 @@ static int tftp_newdata ( struct udp_connection *conn, void *data, size_t len,
case htons ( TFTP_ERROR ): case htons ( TFTP_ERROR ):
return tftp_rx_error ( tftp, data, len ); return tftp_rx_error ( tftp, data, len );
default: default:
DBG ( "TFTP %p received strange packet type %d\n", tftp, DBGC ( tftp, "TFTP %p received strange packet type %d\n", tftp,
ntohs ( common->opcode ) ); ntohs ( common->opcode ) );
return -EINVAL; return -EINVAL;
}; };
} }
@ -449,16 +462,18 @@ struct async_operation * tftp_get ( struct tftp_session *tftp ) {
int rc; int rc;
assert ( tftp->filename != NULL ); assert ( tftp->filename != NULL );
assert ( tftp->callback != NULL ); assert ( tftp->buffer != NULL );
assert ( tftp->udp.peer.st_family != 0 ); assert ( tftp->udp.peer.st_family != 0 );
/* Initialise TFTP session */ /* Initialise TFTP session */
tftp->udp.udp_op = &tftp_udp_operations;
tftp->timer.expired = tftp_timer_expired;
tftp->state = -1;
tftp->blksize = TFTP_DEFAULT_BLKSIZE;
if ( ! tftp->request_blksize ) if ( ! tftp->request_blksize )
tftp->request_blksize = TFTP_MAX_BLKSIZE; tftp->request_blksize = TFTP_MAX_BLKSIZE;
tftp->blksize = TFTP_DEFAULT_BLKSIZE;
tftp->tsize = 0;
tftp->tid = 0;
tftp->state = -1;
tftp->udp.udp_op = &tftp_udp_operations;
tftp->timer.expired = tftp_timer_expired;
/* Open UDP connection */ /* Open UDP connection */
if ( ( rc = udp_open ( &tftp->udp, 0 ) ) != 0 ) { if ( ( rc = udp_open ( &tftp->udp, 0 ) ) != 0 ) {

View File

@ -5,25 +5,23 @@
#include <gpxe/tftp.h> #include <gpxe/tftp.h>
#include <gpxe/async.h> #include <gpxe/async.h>
#include <gpxe/uaccess.h> #include <gpxe/uaccess.h>
#include <gpxe/buffer.h>
#include "pxe.h" #include "pxe.h"
static void test_tftp_callback ( struct tftp_session *tftp, unsigned int block,
void *data, size_t len ) {
unsigned long offset = ( ( block - 1 ) * tftp->blksize );
userptr_t pxe_buffer = real_to_user ( 0, 0x7c00 );
copy_to_user ( pxe_buffer, offset, data, len );
}
int test_tftp ( struct net_device *netdev, struct sockaddr_tcpip *target, int test_tftp ( struct net_device *netdev, struct sockaddr_tcpip *target,
const char *filename ) { const char *filename ) {
struct tftp_session tftp; struct tftp_session tftp;
struct buffer buffer;
int rc; int rc;
memset ( &buffer, 0, sizeof ( buffer ) );
buffer.addr = real_to_user ( 0, 0x7c00 );
buffer.len = ( 512 * 1024 - 0x7c00 );
memset ( &tftp, 0, sizeof ( tftp ) ); memset ( &tftp, 0, sizeof ( tftp ) );
udp_connect ( &tftp.udp, target ); udp_connect ( &tftp.udp, target );
tftp.filename = filename; tftp.filename = filename;
tftp.callback = test_tftp_callback; tftp.buffer = &buffer;
printf ( "Fetching \"%s\" via TFTP\n", filename ); printf ( "Fetching \"%s\" via TFTP\n", filename );
if ( ( rc = async_wait ( tftp_get ( &tftp ) ) ) != 0 ) if ( ( rc = async_wait ( tftp_get ( &tftp ) ) ) != 0 )