Browse Source

[tftp] Make TFTP size requests abort transfer with an error

pxenv_tftp_get_fsize is an API call that PXE clients can call to
obtain the size of a remote file. It is implemented by starting a TFTP
transfer with pxe_tftp_open, waiting for the response and then
stopping the transfer with pxe_tftp_close(). This leaves the session
hanging on the TFTP server and it will try to resend the packet
repeatedly (verified with tftpd-hpa) until it times out.

This patch adds a method "tftpsize" that will abort the transfer after
the first packet is received from the server. This will terminate the
session on the server and is the same behaviour as Intel's PXE ROM
exhibits.

Together with a qemu patch to handle the ERROR packet (submitted to
qemu's mailing list), this resolves a specific issue where booting
pxegrub with qemu's TFTP server would be slow or hang.

I've tested this against qemu's tftp server and against my normal boot
infrastructure (tftpd-hpa). Booting pxegrub and loading extra files
now produces a trace similar to Intel's PXE client and there are no
spurious retransmits from tftpd any more.

Signed-off-by: Thomas Horsten <thomas@horsten.com>
Signed-off-by: Milan Plzik <milan.plzik@gmail.com>
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Marty Connor <mdc@etherboot.org>
tags/v1.0.0-rc1
Thomas Horsten 15 years ago
parent
commit
c124f6360d
2 changed files with 81 additions and 5 deletions
  1. 8
    5
      src/arch/i386/interface/pxe/pxe_tftp.c
  2. 73
    0
      src/net/udp/tftp.c

+ 8
- 5
src/arch/i386/interface/pxe/pxe_tftp.c View File

@@ -165,7 +165,8 @@ static struct xfer_interface_operations pxe_tftp_xfer_ops = {
165 165
  * @ret rc		Return status code
166 166
  */
167 167
 static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port,
168
-			   const unsigned char *filename, size_t blksize ) {
168
+			   const unsigned char *filename, size_t blksize,
169
+			   int sizeonly ) {
169 170
 	char uri_string[PXE_TFTP_URI_LEN];
170 171
 	struct in_addr address;
171 172
 	int rc;
@@ -185,7 +186,8 @@ static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port,
185 186
 	if ( blksize < TFTP_DEFAULT_BLKSIZE )
186 187
 		blksize = TFTP_DEFAULT_BLKSIZE;
187 188
 	snprintf ( uri_string, sizeof ( uri_string ),
188
-		   "tftp://%s:%d%s%s?blksize=%zd",
189
+		   "tftp%s://%s:%d%s%s?blksize=%zd",
190
+		   sizeonly ? "size" : "",
189 191
 		   inet_ntoa ( address ), ntohs ( port ),
190 192
 		   ( ( filename[0] == '/' ) ? "" : "/" ), filename, blksize );
191 193
 	DBG ( " %s", uri_string );
@@ -254,7 +256,8 @@ PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
254 256
 	if ( ( rc = pxe_tftp_open ( tftp_open->ServerIPAddress,
255 257
 				    tftp_open->TFTPPort,
256 258
 				    tftp_open->FileName,
257
-				    tftp_open->PacketSize ) ) != 0 ) {
259
+				    tftp_open->PacketSize,
260
+				    0) ) != 0 ) {
258 261
 		tftp_open->Status = PXENV_STATUS ( rc );
259 262
 		return PXENV_EXIT_FAILURE;
260 263
 	}
@@ -488,7 +491,7 @@ PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
488 491
 
489 492
 	/* Open TFTP file */
490 493
 	if ( ( rc = pxe_tftp_open ( tftp_read_file->ServerIPAddress, 0,
491
-				    tftp_read_file->FileName, 0 ) ) != 0 ) {
494
+				    tftp_read_file->FileName, 0, 0 ) ) != 0 ) {
492 495
 		tftp_read_file->Status = PXENV_STATUS ( rc );
493 496
 		return PXENV_EXIT_FAILURE;
494 497
 	}
@@ -558,7 +561,7 @@ PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
558 561
 
559 562
 	/* Open TFTP file */
560 563
 	if ( ( rc = pxe_tftp_open ( tftp_get_fsize->ServerIPAddress, 0,
561
-				    tftp_get_fsize->FileName, 0 ) ) != 0 ) {
564
+				    tftp_get_fsize->FileName, 0, 1 ) ) != 0 ) {
562 565
 		tftp_get_fsize->Status = PXENV_STATUS ( rc );
563 566
 		return PXENV_EXIT_FAILURE;
564 567
 	}

+ 73
- 0
src/net/udp/tftp.c View File

@@ -133,6 +133,8 @@ enum {
133 133
 	TFTP_FL_RRQ_MULTICAST = 0x0004,
134 134
 	/** Perform MTFTP recovery on timeout */
135 135
 	TFTP_FL_MTFTP_RECOVERY = 0x0008,
136
+	/** Only get filesize and then abort the transfer */
137
+	TFTP_FL_SIZEONLY = 0x0010,
136 138
 };
137 139
 
138 140
 /** Maximum number of MTFTP open requests before falling back to TFTP */
@@ -410,6 +412,42 @@ static int tftp_send_ack ( struct tftp_request *tftp ) {
410 412
 	return xfer_deliver_iob_meta ( &tftp->socket, iobuf, &meta );
411 413
 }
412 414
 
415
+/**
416
+ * Transmit ERROR (Abort)
417
+ *
418
+ * @v tftp		TFTP connection
419
+ * @v errcode		TFTP error code
420
+ * @v errmsg		Error message string
421
+ * @ret rc		Return status code
422
+ */
423
+static int tftp_send_error ( struct tftp_request *tftp, int errcode,
424
+			     const char *errmsg ) {
425
+	struct tftp_error *err;
426
+	struct io_buffer *iobuf;
427
+	struct xfer_metadata meta = {
428
+		.dest = ( struct sockaddr * ) &tftp->peer,
429
+	};
430
+	size_t msglen;
431
+
432
+	DBGC2 ( tftp, "TFTP %p sending ERROR %d: %s\n", tftp, errcode,
433
+		errmsg );
434
+
435
+	/* Allocate buffer */
436
+	msglen = sizeof ( *err ) + strlen ( errmsg ) + 1 /* NUL */;
437
+	iobuf = xfer_alloc_iob ( &tftp->socket, msglen );
438
+	if ( ! iobuf )
439
+		return -ENOMEM;
440
+
441
+	/* Build ERROR */
442
+	err = iob_put ( iobuf, msglen );
443
+	err->opcode = htons ( TFTP_ERROR );
444
+	err->errcode = htons ( errcode );
445
+	strcpy ( err->errmsg, errmsg );
446
+
447
+	/* ERR always goes to the peer recorded from the RRQ response */
448
+	return xfer_deliver_iob_meta ( &tftp->socket, iobuf, &meta );
449
+}
450
+
413 451
 /**
414 452
  * Transmit next relevant packet
415 453
  *
@@ -732,6 +770,14 @@ static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) {
732 770
 			goto done;
733 771
 	}
734 772
 
773
+	/* Abort request if only trying to determine file size */
774
+	if ( tftp->flags & TFTP_FL_SIZEONLY ) {
775
+		rc = 0;
776
+		tftp_send_error ( tftp, TFTP_ERR_UNKNOWN_TID, "TFTP Aborted" );
777
+		tftp_done ( tftp, rc );
778
+		return rc;
779
+	}
780
+
735 781
 	/* Request next data block */
736 782
 	tftp_send_packet ( tftp );
737 783
 
@@ -759,6 +805,13 @@ static int tftp_rx_data ( struct tftp_request *tftp,
759 805
 	size_t data_len;
760 806
 	int rc;
761 807
 
808
+	if ( tftp->flags & TFTP_FL_SIZEONLY ) {
809
+		/* If we get here then server doesn't support SIZE option */
810
+		rc = -ENOTSUP;
811
+		tftp_send_error ( tftp, TFTP_ERR_UNKNOWN_TID, "TFTP Aborted" );
812
+		goto done;
813
+	}
814
+
762 815
 	/* Sanity check */
763 816
 	if ( iob_len ( iobuf ) < sizeof ( *data ) ) {
764 817
 		DBGC ( tftp, "TFTP %p received underlength DATA packet "
@@ -1120,6 +1173,26 @@ struct uri_opener tftp_uri_opener __uri_opener = {
1120 1173
 	.open	= tftp_open,
1121 1174
 };
1122 1175
 
1176
+/**
1177
+ * Initiate TFTP-size request
1178
+ *
1179
+ * @v xfer		Data transfer interface
1180
+ * @v uri		Uniform Resource Identifier
1181
+ * @ret rc		Return status code
1182
+ */
1183
+static int tftpsize_open ( struct xfer_interface *xfer, struct uri *uri ) {
1184
+	return tftp_core_open ( xfer, uri, TFTP_PORT, NULL,
1185
+				( TFTP_FL_RRQ_SIZES |
1186
+				  TFTP_FL_SIZEONLY ) );
1187
+
1188
+}
1189
+
1190
+/** TFTP URI opener */
1191
+struct uri_opener tftpsize_uri_opener __uri_opener = {
1192
+	.scheme	= "tftpsize",
1193
+	.open	= tftpsize_open,
1194
+};
1195
+
1123 1196
 /**
1124 1197
  * Initiate TFTM download
1125 1198
  *

Loading…
Cancel
Save