mirror of https://github.com/ipxe/ipxe.git
Add preliminary support for MTFTP.
parent
9d4e4dbc32
commit
f3265b4bf8
|
@ -29,6 +29,8 @@
|
||||||
#define TFTP_ERR_UNKNOWN_USER 7 /**< No such user */
|
#define TFTP_ERR_UNKNOWN_USER 7 /**< No such user */
|
||||||
#define TFTP_ERR_BAD_OPTS 8 /**< Option negotiation failed */
|
#define TFTP_ERR_BAD_OPTS 8 /**< Option negotiation failed */
|
||||||
|
|
||||||
|
#define MTFTP_PORT 1759 /**< Default MTFTP server port */
|
||||||
|
|
||||||
/** A TFTP read request (RRQ) packet */
|
/** A TFTP read request (RRQ) packet */
|
||||||
struct tftp_rrq {
|
struct tftp_rrq {
|
||||||
uint16_t opcode;
|
uint16_t opcode;
|
||||||
|
|
|
@ -74,24 +74,23 @@ struct tftp_request {
|
||||||
* "tsize" option, this value will be zero.
|
* "tsize" option, this value will be zero.
|
||||||
*/
|
*/
|
||||||
unsigned long tsize;
|
unsigned long tsize;
|
||||||
/** Multicast address
|
|
||||||
*
|
|
||||||
* This is the destination address for multicast data
|
|
||||||
* transmissions.
|
|
||||||
*/
|
|
||||||
struct sockaddr_tcpip multicast;
|
|
||||||
/** Master client
|
|
||||||
*
|
|
||||||
* True if this is the client responsible for sending ACKs.
|
|
||||||
*/
|
|
||||||
int master;
|
|
||||||
|
|
||||||
|
/** Server port
|
||||||
|
*
|
||||||
|
* This is the port to which RRQ packets are sent.
|
||||||
|
*/
|
||||||
|
unsigned int port;
|
||||||
/** Peer address
|
/** Peer address
|
||||||
*
|
*
|
||||||
* The peer address is determined by the first response
|
* The peer address is determined by the first response
|
||||||
* received to the TFTP RRQ.
|
* received to the TFTP RRQ.
|
||||||
*/
|
*/
|
||||||
struct sockaddr_tcpip peer;
|
struct sockaddr_tcpip peer;
|
||||||
|
/** Request flags */
|
||||||
|
unsigned int flags;
|
||||||
|
/** MTFTP timeout count */
|
||||||
|
unsigned int mtftp_timeouts;
|
||||||
|
|
||||||
/** Block bitmap */
|
/** Block bitmap */
|
||||||
struct bitmap bitmap;
|
struct bitmap bitmap;
|
||||||
/** Maximum known length
|
/** Maximum known length
|
||||||
|
@ -110,6 +109,21 @@ struct tftp_request {
|
||||||
struct retry_timer timer;
|
struct retry_timer timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** TFTP request flags */
|
||||||
|
enum {
|
||||||
|
/** Send ACK packets */
|
||||||
|
TFTP_FL_SEND_ACK = 0x0001,
|
||||||
|
/** Request blksize and tsize options */
|
||||||
|
TFTP_FL_RRQ_SIZES = 0x0002,
|
||||||
|
/** Request multicast option */
|
||||||
|
TFTP_FL_RRQ_MULTICAST = 0x0004,
|
||||||
|
/** Perform MTFTP recovery on timeout */
|
||||||
|
TFTP_FL_MTFTP_RECOVERY = 0x0008,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Maximum number of MTFTP open requests before falling back to TFTP */
|
||||||
|
#define MTFTP_MAX_TIMEOUTS 3
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free TFTP request
|
* Free TFTP request
|
||||||
*
|
*
|
||||||
|
@ -147,6 +161,67 @@ static void tftp_done ( struct tftp_request *tftp, int rc ) {
|
||||||
xfer_close ( &tftp->xfer, rc );
|
xfer_close ( &tftp->xfer, rc );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reopen TFTP socket
|
||||||
|
*
|
||||||
|
* @v tftp TFTP connection
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int tftp_reopen ( struct tftp_request *tftp ) {
|
||||||
|
struct sockaddr_tcpip server;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Close socket */
|
||||||
|
xfer_close ( &tftp->socket, 0 );
|
||||||
|
|
||||||
|
/* Disable ACK sending. */
|
||||||
|
tftp->flags &= ~TFTP_FL_SEND_ACK;
|
||||||
|
|
||||||
|
/* Reset peer address */
|
||||||
|
memset ( &tftp->peer, 0, sizeof ( tftp->peer ) );
|
||||||
|
|
||||||
|
/* Open socket */
|
||||||
|
memset ( &server, 0, sizeof ( server ) );
|
||||||
|
server.st_port = htons ( tftp->port );
|
||||||
|
if ( ( rc = xfer_open_named_socket ( &tftp->socket, SOCK_DGRAM,
|
||||||
|
( struct sockaddr * ) &server,
|
||||||
|
tftp->uri->host, NULL ) ) != 0 ) {
|
||||||
|
DBGC ( tftp, "TFTP %p could not open socket: %s\n",
|
||||||
|
tftp, strerror ( rc ) );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reopen TFTP multicast socket
|
||||||
|
*
|
||||||
|
* @v tftp TFTP connection
|
||||||
|
* @v local Local socket address
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int tftp_reopen_mc ( struct tftp_request *tftp,
|
||||||
|
struct sockaddr *local ) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Close multicast socket */
|
||||||
|
xfer_close ( &tftp->mc_socket, 0 );
|
||||||
|
|
||||||
|
/* Open multicast socket. We never send via this socket, so
|
||||||
|
* use the local address as the peer address (since the peer
|
||||||
|
* address cannot be NULL).
|
||||||
|
*/
|
||||||
|
if ( ( rc = xfer_open_socket ( &tftp->mc_socket, SOCK_DGRAM,
|
||||||
|
local, local ) ) != 0 ) {
|
||||||
|
DBGC ( tftp, "TFTP %p could not open multicast "
|
||||||
|
"socket: %s\n", tftp, strerror ( rc ) );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Presize TFTP receive buffers and block bitmap
|
* Presize TFTP receive buffers and block bitmap
|
||||||
*
|
*
|
||||||
|
@ -201,6 +276,35 @@ void tftp_set_request_blksize ( unsigned int blksize ) {
|
||||||
tftp_request_blksize = blksize;
|
tftp_request_blksize = blksize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MTFTP multicast receive address
|
||||||
|
*
|
||||||
|
* This is treated as a global configuration parameter.
|
||||||
|
*/
|
||||||
|
static struct sockaddr_in tftp_mtftp_socket = {
|
||||||
|
.sin_family = AF_INET,
|
||||||
|
.sin_addr.s_addr = htonl ( 0xefff0101 ),
|
||||||
|
.sin_port = htons ( 3001 ),
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set MTFTP multicast address
|
||||||
|
*
|
||||||
|
* @v address Multicast IPv4 address
|
||||||
|
*/
|
||||||
|
void tftp_set_mtftp_address ( struct in_addr address ) {
|
||||||
|
tftp_mtftp_socket.sin_addr = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set MTFTP multicast port
|
||||||
|
*
|
||||||
|
* @v port Multicast port
|
||||||
|
*/
|
||||||
|
void tftp_set_mtftp_port ( unsigned int port ) {
|
||||||
|
tftp_mtftp_socket.sin_port = htons ( port );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transmit RRQ
|
* Transmit RRQ
|
||||||
*
|
*
|
||||||
|
@ -227,11 +331,19 @@ static int tftp_send_rrq ( struct tftp_request *tftp ) {
|
||||||
/* Build request */
|
/* Build request */
|
||||||
rrq = iob_put ( iobuf, sizeof ( *rrq ) );
|
rrq = iob_put ( iobuf, sizeof ( *rrq ) );
|
||||||
rrq->opcode = htons ( TFTP_RRQ );
|
rrq->opcode = htons ( TFTP_RRQ );
|
||||||
iob_put ( iobuf,
|
iob_put ( iobuf, snprintf ( iobuf->tail, iob_tailroom ( iobuf ),
|
||||||
snprintf ( rrq->data, iob_tailroom ( iobuf ),
|
"%s%coctet", path, 0 ) + 1 );
|
||||||
"%s%coctet%cblksize%c%d%ctsize%c0%cmulticast%c",
|
if ( tftp->flags & TFTP_FL_RRQ_SIZES ) {
|
||||||
path, 0, 0, 0, tftp_request_blksize, 0,
|
iob_put ( iobuf, snprintf ( iobuf->tail,
|
||||||
0, 0, 0 ) + 1 );
|
iob_tailroom ( iobuf ),
|
||||||
|
"blksize%c%d%ctsize%c0", 0,
|
||||||
|
tftp_request_blksize, 0, 0 ) + 1 );
|
||||||
|
}
|
||||||
|
if ( tftp->flags & TFTP_FL_RRQ_MULTICAST ) {
|
||||||
|
iob_put ( iobuf, snprintf ( iobuf->tail,
|
||||||
|
iob_tailroom ( iobuf ),
|
||||||
|
"multicast%c", 0 ) + 1 );
|
||||||
|
}
|
||||||
|
|
||||||
/* RRQ always goes to the address specified in the initial
|
/* RRQ always goes to the address specified in the initial
|
||||||
* xfer_open() call
|
* xfer_open() call
|
||||||
|
@ -283,17 +395,16 @@ static int tftp_send_packet ( struct tftp_request *tftp ) {
|
||||||
stop_timer ( &tftp->timer );
|
stop_timer ( &tftp->timer );
|
||||||
start_timer ( &tftp->timer );
|
start_timer ( &tftp->timer );
|
||||||
|
|
||||||
/* If we are the master client, send RRQ or ACK as appropriate */
|
/* Send RRQ or ACK as appropriate */
|
||||||
if ( tftp->master ) {
|
|
||||||
if ( ! tftp->peer.st_family ) {
|
if ( ! tftp->peer.st_family ) {
|
||||||
return tftp_send_rrq ( tftp );
|
return tftp_send_rrq ( tftp );
|
||||||
} else {
|
} else {
|
||||||
|
if ( tftp->flags & TFTP_FL_SEND_ACK ) {
|
||||||
return tftp_send_ack ( tftp );
|
return tftp_send_ack ( tftp );
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
/* Do nothing when not the master client */
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -305,12 +416,61 @@ static int tftp_send_packet ( struct tftp_request *tftp ) {
|
||||||
static void tftp_timer_expired ( struct retry_timer *timer, int fail ) {
|
static void tftp_timer_expired ( struct retry_timer *timer, int fail ) {
|
||||||
struct tftp_request *tftp =
|
struct tftp_request *tftp =
|
||||||
container_of ( timer, struct tftp_request, timer );
|
container_of ( timer, struct tftp_request, timer );
|
||||||
|
int rc;
|
||||||
|
|
||||||
if ( fail ) {
|
/* If we are doing MTFTP, attempt the various recovery strategies */
|
||||||
tftp_done ( tftp, -ETIMEDOUT );
|
if ( tftp->flags & TFTP_FL_MTFTP_RECOVERY ) {
|
||||||
|
if ( tftp->peer.st_family ) {
|
||||||
|
/* If we have received any response from the server,
|
||||||
|
* try resending the RRQ to restart the download.
|
||||||
|
*/
|
||||||
|
DBGC ( tftp, "TFTP %p attempting reopen\n", tftp );
|
||||||
|
if ( ( rc = tftp_reopen ( tftp ) ) != 0 )
|
||||||
|
goto err;
|
||||||
} else {
|
} else {
|
||||||
tftp_send_packet ( tftp );
|
/* Fall back to plain TFTP after several attempts */
|
||||||
|
tftp->mtftp_timeouts++;
|
||||||
|
DBGC ( tftp, "TFTP %p timeout %d waiting for MTFTP "
|
||||||
|
"open\n", tftp, tftp->mtftp_timeouts );
|
||||||
|
|
||||||
|
if ( tftp->mtftp_timeouts > MTFTP_MAX_TIMEOUTS ) {
|
||||||
|
DBGC ( tftp, "TFTP %p falling back to plain "
|
||||||
|
"TFTP\n", tftp );
|
||||||
|
tftp->flags = TFTP_FL_RRQ_SIZES;
|
||||||
|
|
||||||
|
/* Close multicast socket */
|
||||||
|
xfer_close ( &tftp->mc_socket, 0 );
|
||||||
|
|
||||||
|
/* Reset retry timer */
|
||||||
|
start_timer_nodelay ( &tftp->timer );
|
||||||
|
|
||||||
|
/* The blocksize may change: discard
|
||||||
|
* the block bitmap
|
||||||
|
*/
|
||||||
|
bitmap_free ( &tftp->bitmap );
|
||||||
|
memset ( &tftp->bitmap, 0,
|
||||||
|
sizeof ( tftp->bitmap ) );
|
||||||
|
|
||||||
|
/* Reopen on standard TFTP port */
|
||||||
|
tftp->port = TFTP_PORT;
|
||||||
|
if ( ( rc = tftp_reopen ( tftp ) ) != 0 )
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Not doing MTFTP (or have fallen back to plain
|
||||||
|
* TFTP); fail as per normal.
|
||||||
|
*/
|
||||||
|
if ( fail ) {
|
||||||
|
rc = -ETIMEDOUT;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tftp_send_packet ( tftp );
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
tftp_done ( tftp, rc );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -366,15 +526,16 @@ static int tftp_process_tsize ( struct tftp_request *tftp,
|
||||||
*/
|
*/
|
||||||
static int tftp_process_multicast ( struct tftp_request *tftp,
|
static int tftp_process_multicast ( struct tftp_request *tftp,
|
||||||
const char *value ) {
|
const char *value ) {
|
||||||
struct sockaddr_in *sin = ( struct sockaddr_in * ) &tftp->multicast;
|
union {
|
||||||
|
struct sockaddr sa;
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
} socket;
|
||||||
char buf[ strlen ( value ) + 1 ];
|
char buf[ strlen ( value ) + 1 ];
|
||||||
char *addr;
|
char *addr;
|
||||||
char *port;
|
char *port;
|
||||||
char *port_end;
|
char *port_end;
|
||||||
char *mc;
|
char *mc;
|
||||||
char *mc_end;
|
char *mc_end;
|
||||||
struct sockaddr *mc_peer;
|
|
||||||
struct sockaddr *mc_local;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Split value into "addr,port,mc" fields */
|
/* Split value into "addr,port,mc" fields */
|
||||||
|
@ -394,46 +555,34 @@ static int tftp_process_multicast ( struct tftp_request *tftp,
|
||||||
*(mc++) = '\0';
|
*(mc++) = '\0';
|
||||||
|
|
||||||
/* Parse parameters */
|
/* Parse parameters */
|
||||||
if ( *addr ) {
|
if ( strtoul ( mc, &mc_end, 0 ) == 0 )
|
||||||
if ( inet_aton ( addr, &sin->sin_addr ) == 0 ) {
|
tftp->flags &= ~TFTP_FL_SEND_ACK;
|
||||||
|
if ( *mc_end ) {
|
||||||
|
DBGC ( tftp, "TFTP %p multicast invalid mc %s\n", tftp, mc );
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
DBGC ( tftp, "TFTP %p is%s the master client\n",
|
||||||
|
tftp, ( ( tftp->flags & TFTP_FL_SEND_ACK ) ? "" : " not" ) );
|
||||||
|
if ( *addr && *port ) {
|
||||||
|
socket.sin.sin_family = AF_INET;
|
||||||
|
if ( inet_aton ( addr, &socket.sin.sin_addr ) == 0 ) {
|
||||||
DBGC ( tftp, "TFTP %p multicast invalid IP address "
|
DBGC ( tftp, "TFTP %p multicast invalid IP address "
|
||||||
"%s\n", tftp, addr );
|
"%s\n", tftp, addr );
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
DBGC ( tftp, "TFTP %p multicast IP address %s\n",
|
DBGC ( tftp, "TFTP %p multicast IP address %s\n",
|
||||||
tftp, inet_ntoa ( sin->sin_addr ) );
|
tftp, inet_ntoa ( socket.sin.sin_addr ) );
|
||||||
}
|
socket.sin.sin_port = htons ( strtoul ( port, &port_end, 0 ) );
|
||||||
if ( *port ) {
|
|
||||||
sin->sin_port = htons ( strtoul ( port, &port_end, 0 ) );
|
|
||||||
if ( *port_end ) {
|
if ( *port_end ) {
|
||||||
DBGC ( tftp, "TFTP %p multicast invalid port %s\n",
|
DBGC ( tftp, "TFTP %p multicast invalid port %s\n",
|
||||||
tftp, port );
|
tftp, port );
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
DBGC ( tftp, "TFTP %p multicast port %d\n",
|
DBGC ( tftp, "TFTP %p multicast port %d\n",
|
||||||
tftp, ntohs ( sin->sin_port ) );
|
tftp, ntohs ( socket.sin.sin_port ) );
|
||||||
}
|
if ( ( rc = tftp_reopen_mc ( tftp, &socket.sa ) ) != 0 )
|
||||||
tftp->master = strtoul ( mc, &mc_end, 0 );
|
|
||||||
if ( *mc_end ) {
|
|
||||||
DBGC ( tftp, "TFTP %p multicast invalid mc %s\n", tftp, mc );
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
DBGC ( tftp, "TFTP %p is%s the master client\n",
|
|
||||||
tftp, ( tftp->master ? "" : " not" ) );
|
|
||||||
|
|
||||||
/* Open multicast socket, if new address specified */
|
|
||||||
if ( *addr || *port ) {
|
|
||||||
xfer_close ( &tftp->mc_socket, 0 );
|
|
||||||
mc_peer = ( ( struct sockaddr * ) &tftp->peer );
|
|
||||||
mc_local = ( ( struct sockaddr * ) &tftp->multicast );
|
|
||||||
mc_local->sa_family = mc_peer->sa_family;
|
|
||||||
if ( ( rc = xfer_open_socket ( &tftp->mc_socket, SOCK_DGRAM,
|
|
||||||
mc_peer, mc_local ) ) != 0 ) {
|
|
||||||
DBGC ( tftp, "TFTP %p could not open multicast "
|
|
||||||
"socket: %s\n", tftp, strerror ( rc ) );
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -611,10 +760,10 @@ static int tftp_rx_data ( struct tftp_request *tftp,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Translation between TFTP errors and internal error numbers */
|
/** Translation between TFTP errors and internal error numbers */
|
||||||
static const uint8_t tftp_errors[] = {
|
static const int tftp_errors[] = {
|
||||||
[TFTP_ERR_FILE_NOT_FOUND] = PXENV_STATUS_TFTP_FILE_NOT_FOUND,
|
[TFTP_ERR_FILE_NOT_FOUND] = ENOENT,
|
||||||
[TFTP_ERR_ACCESS_DENIED] = PXENV_STATUS_TFTP_ACCESS_VIOLATION,
|
[TFTP_ERR_ACCESS_DENIED] = EACCES,
|
||||||
[TFTP_ERR_ILLEGAL_OP] = PXENV_STATUS_TFTP_UNKNOWN_OPCODE,
|
[TFTP_ERR_ILLEGAL_OP] = ENOTSUP,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -645,7 +794,7 @@ static int tftp_rx_error ( struct tftp_request *tftp, void *buf, size_t len ) {
|
||||||
if ( err < ( sizeof ( tftp_errors ) / sizeof ( tftp_errors[0] ) ) )
|
if ( err < ( sizeof ( tftp_errors ) / sizeof ( tftp_errors[0] ) ) )
|
||||||
rc = -tftp_errors[err];
|
rc = -tftp_errors[err];
|
||||||
if ( ! rc )
|
if ( ! rc )
|
||||||
rc = -PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION;
|
rc = -ENOTSUP;
|
||||||
|
|
||||||
/* Close TFTP request */
|
/* Close TFTP request */
|
||||||
tftp_done ( tftp, rc );
|
tftp_done ( tftp, rc );
|
||||||
|
@ -736,32 +885,29 @@ static int tftp_socket_deliver_iob ( struct xfer_interface *socket,
|
||||||
struct tftp_request *tftp =
|
struct tftp_request *tftp =
|
||||||
container_of ( socket, struct tftp_request, socket );
|
container_of ( socket, struct tftp_request, socket );
|
||||||
|
|
||||||
return tftp_rx ( tftp, iobuf, meta );
|
/* Enable sending ACKs when we receive a unicast packet. This
|
||||||
}
|
* covers three cases:
|
||||||
|
|
||||||
/**
|
|
||||||
* TFTP connection closed by network stack
|
|
||||||
*
|
*
|
||||||
* @v socket Transport layer interface
|
* 1. Standard TFTP; we should always send ACKs, and will
|
||||||
* @v rc Reason for close
|
* always receive a unicast packet before we need to send the
|
||||||
|
* first ACK.
|
||||||
|
*
|
||||||
|
* 2. RFC2090 multicast TFTP; the only unicast packets we will
|
||||||
|
* receive are the OACKs; enable sending ACKs here (before
|
||||||
|
* processing the OACK) and disable it when processing the
|
||||||
|
* multicast option if we are not the master client.
|
||||||
|
*
|
||||||
|
* 3. MTFTP; receiving a unicast datagram indicates that we
|
||||||
|
* are the "master client" and should send ACKs.
|
||||||
*/
|
*/
|
||||||
static void tftp_socket_close ( struct xfer_interface *socket, int rc ) {
|
tftp->flags |= TFTP_FL_SEND_ACK;
|
||||||
struct tftp_request *tftp =
|
|
||||||
container_of ( socket, struct tftp_request, socket );
|
|
||||||
|
|
||||||
DBGC ( tftp, "TFTP %p socket closed: %s\n",
|
return tftp_rx ( tftp, iobuf, meta );
|
||||||
tftp, strerror ( rc ) );
|
|
||||||
|
|
||||||
/* Any close counts as an error */
|
|
||||||
if ( ! rc )
|
|
||||||
rc = -ECONNRESET;
|
|
||||||
|
|
||||||
tftp_done ( tftp, rc );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** TFTP socket operations */
|
/** TFTP socket operations */
|
||||||
static struct xfer_interface_operations tftp_socket_operations = {
|
static struct xfer_interface_operations tftp_socket_operations = {
|
||||||
.close = tftp_socket_close,
|
.close = ignore_xfer_close,
|
||||||
.vredirect = xfer_vopen,
|
.vredirect = xfer_vopen,
|
||||||
.seek = ignore_xfer_seek,
|
.seek = ignore_xfer_seek,
|
||||||
.window = unlimited_xfer_window,
|
.window = unlimited_xfer_window,
|
||||||
|
@ -787,29 +933,9 @@ static int tftp_mc_socket_deliver_iob ( struct xfer_interface *mc_socket,
|
||||||
return tftp_rx ( tftp, iobuf, meta );
|
return tftp_rx ( tftp, iobuf, meta );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TFTP multicast connection closed by network stack
|
|
||||||
*
|
|
||||||
* @v socket Multicast transport layer interface
|
|
||||||
* @v rc Reason for close
|
|
||||||
*/
|
|
||||||
static void tftp_mc_socket_close ( struct xfer_interface *mc_socket,
|
|
||||||
int rc ) {
|
|
||||||
struct tftp_request *tftp =
|
|
||||||
container_of ( mc_socket, struct tftp_request, mc_socket );
|
|
||||||
|
|
||||||
DBGC ( tftp, "TFTP %p multicast socket closed: %s\n",
|
|
||||||
tftp, strerror ( rc ) );
|
|
||||||
|
|
||||||
/* The multicast socket may be closed when we receive a new
|
|
||||||
* OACK and open/reopen the socket; we should not call
|
|
||||||
* tftp_done() at this point.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/** TFTP multicast socket operations */
|
/** TFTP multicast socket operations */
|
||||||
static struct xfer_interface_operations tftp_mc_socket_operations = {
|
static struct xfer_interface_operations tftp_mc_socket_operations = {
|
||||||
.close = tftp_mc_socket_close,
|
.close = ignore_xfer_close,
|
||||||
.vredirect = xfer_vopen,
|
.vredirect = xfer_vopen,
|
||||||
.seek = ignore_xfer_seek,
|
.seek = ignore_xfer_seek,
|
||||||
.window = unlimited_xfer_window,
|
.window = unlimited_xfer_window,
|
||||||
|
@ -846,15 +972,17 @@ static struct xfer_interface_operations tftp_xfer_operations = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiate TFTP download
|
* Initiate TFTP/TFTM/MTFTP download
|
||||||
*
|
*
|
||||||
* @v xfer Data transfer interface
|
* @v xfer Data transfer interface
|
||||||
* @v uri Uniform Resource Identifier
|
* @v uri Uniform Resource Identifier
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
int tftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
|
static int tftp_core_open ( struct xfer_interface *xfer, struct uri *uri,
|
||||||
|
unsigned int default_port,
|
||||||
|
struct sockaddr *multicast,
|
||||||
|
unsigned int flags ) {
|
||||||
struct tftp_request *tftp;
|
struct tftp_request *tftp;
|
||||||
struct sockaddr_tcpip server;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Sanity checks */
|
/* Sanity checks */
|
||||||
|
@ -874,17 +1002,20 @@ int tftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
|
||||||
xfer_init ( &tftp->mc_socket, &tftp_mc_socket_operations,
|
xfer_init ( &tftp->mc_socket, &tftp_mc_socket_operations,
|
||||||
&tftp->refcnt );
|
&tftp->refcnt );
|
||||||
tftp->blksize = TFTP_DEFAULT_BLKSIZE;
|
tftp->blksize = TFTP_DEFAULT_BLKSIZE;
|
||||||
tftp->master = 1;
|
tftp->flags = flags;
|
||||||
tftp->timer.expired = tftp_timer_expired;
|
tftp->timer.expired = tftp_timer_expired;
|
||||||
|
|
||||||
/* Open socket */
|
/* Open socket */
|
||||||
memset ( &server, 0, sizeof ( server ) );
|
tftp->port = uri_port ( tftp->uri, default_port );
|
||||||
server.st_port = htons ( uri_port ( tftp->uri, TFTP_PORT ) );
|
if ( ( rc = tftp_reopen ( tftp ) ) != 0 )
|
||||||
if ( ( rc = xfer_open_named_socket ( &tftp->socket, SOCK_DGRAM,
|
|
||||||
( struct sockaddr * ) &server,
|
|
||||||
uri->host, NULL ) ) != 0 )
|
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
/* Open multicast socket */
|
||||||
|
if ( multicast ) {
|
||||||
|
if ( ( rc = tftp_reopen_mc ( tftp, multicast ) ) != 0 )
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
/* Start timer to initiate RRQ */
|
/* Start timer to initiate RRQ */
|
||||||
start_timer_nodelay ( &tftp->timer );
|
start_timer_nodelay ( &tftp->timer );
|
||||||
|
|
||||||
|
@ -901,8 +1032,60 @@ int tftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate TFTP download
|
||||||
|
*
|
||||||
|
* @v xfer Data transfer interface
|
||||||
|
* @v uri Uniform Resource Identifier
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int tftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
|
||||||
|
return tftp_core_open ( xfer, uri, TFTP_PORT, NULL,
|
||||||
|
TFTP_FL_RRQ_SIZES );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/** TFTP URI opener */
|
/** TFTP URI opener */
|
||||||
struct uri_opener tftp_uri_opener __uri_opener = {
|
struct uri_opener tftp_uri_opener __uri_opener = {
|
||||||
.scheme = "tftp",
|
.scheme = "tftp",
|
||||||
.open = tftp_open,
|
.open = tftp_open,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate TFTM download
|
||||||
|
*
|
||||||
|
* @v xfer Data transfer interface
|
||||||
|
* @v uri Uniform Resource Identifier
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int tftm_open ( struct xfer_interface *xfer, struct uri *uri ) {
|
||||||
|
return tftp_core_open ( xfer, uri, TFTP_PORT, NULL,
|
||||||
|
( TFTP_FL_RRQ_SIZES |
|
||||||
|
TFTP_FL_RRQ_MULTICAST ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TFTM URI opener */
|
||||||
|
struct uri_opener tftm_uri_opener __uri_opener = {
|
||||||
|
.scheme = "tftm",
|
||||||
|
.open = tftm_open,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate MTFTP download
|
||||||
|
*
|
||||||
|
* @v xfer Data transfer interface
|
||||||
|
* @v uri Uniform Resource Identifier
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int mtftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
|
||||||
|
return tftp_core_open ( xfer, uri, MTFTP_PORT,
|
||||||
|
( struct sockaddr * ) &tftp_mtftp_socket,
|
||||||
|
TFTP_FL_MTFTP_RECOVERY );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** MTFTP URI opener */
|
||||||
|
struct uri_opener mtftp_uri_opener __uri_opener = {
|
||||||
|
.scheme = "mtftp",
|
||||||
|
.open = mtftp_open,
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue