From 53a4436d94886b6b6b0d931158db2c79490591a1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 1 Jun 2005 13:13:05 +0000 Subject: TFTP upgraded to use a core function library (in tftpcore.c) which will be shared between TFTP, TFTM and MTFTP protocols. --- src/proto/tcp.c | 2 +- src/proto/tftm.c | 4 + src/proto/tftp.c | 232 +++++++++++++++++++-------------------------------- src/proto/tftpcore.c | 42 ++++++++-- 4 files changed, 127 insertions(+), 153 deletions(-) (limited to 'src/proto') 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; } -- cgit v1.2.3-55-g7522