|
@@ -24,7 +24,52 @@
|
24
|
24
|
|
25
|
25
|
#include "pxe.h"
|
26
|
26
|
|
27
|
|
-/* PXENV_TFTP_OPEN
|
|
27
|
+/**
|
|
28
|
+ * TFTP OPEN
|
|
29
|
+ *
|
|
30
|
+ * @v tftp_open Pointer to a struct s_PXENV_TFTP_OPEN
|
|
31
|
+ * @v s_PXENV_TFTP_OPEN::ServerIPAddress TFTP server IP address
|
|
32
|
+ * @v s_PXENV_TFTP_OPEN::GatewayIPAddress Relay agent IP address, or 0.0.0.0
|
|
33
|
+ * @v s_PXENV_TFTP_OPEN::Filename Name of file to open
|
|
34
|
+ * @v s_PXENV_TFTP_OPEN::TFTPPort TFTP server UDP port
|
|
35
|
+ * @v s_PXENV_TFTP_OPEN::PacketSize TFTP blksize option to request
|
|
36
|
+ * @ret #PXENV_EXIT_SUCCESS File was opened
|
|
37
|
+ * @ret #PXENV_EXIT_FAILURE File was not opened
|
|
38
|
+ * @ret s_PXENV_TFTP_OPEN::Status PXE status code
|
|
39
|
+ * @ret s_PXENV_TFTP_OPEN::PacketSize Negotiated
|
|
40
|
+ * @err ....... ..........
|
|
41
|
+ *
|
|
42
|
+ * Opens a TFTP connection for downloading a file a block at a time
|
|
43
|
+ * using pxenv_tftp_read().
|
|
44
|
+ *
|
|
45
|
+ * If s_PXENV_TFTP_OPEN::GatewayIPAddress is 0.0.0.0, normal IP
|
|
46
|
+ * routing will take place. See the relevant
|
|
47
|
+ * @ref pxe_routing "implementation note" for more details.
|
|
48
|
+ *
|
|
49
|
+ * s_PXENV_TFTP_OPEN::PacketSize must be at least 512.
|
|
50
|
+ *
|
|
51
|
+ * You can only have one TFTP connection open at a time, because the
|
|
52
|
+ * PXE API requires the PXE stack to keep state about the open TFTP
|
|
53
|
+ * connection (rather than letting the caller do so).
|
|
54
|
+ *
|
|
55
|
+ * It is unclear precisely what constitutes a "TFTP open" operation.
|
|
56
|
+ * Clearly, we must send the TFTP open request to the server. Since
|
|
57
|
+ * we must know whether or not the open succeeded, we must wait for
|
|
58
|
+ * the first reply packet from the TFTP server. If the TFTP server
|
|
59
|
+ * supports options, the first reply packet will be an OACK; otherwise
|
|
60
|
+ * it will be a DATA packet. In other words, we may only get to
|
|
61
|
+ * discover whether or not the open succeeded when we receive the
|
|
62
|
+ * first block of data. However, the pxenv_tftp_open() API provides
|
|
63
|
+ * no way for us to return this block of data at this time. See the
|
|
64
|
+ * relevant @ref pxe_note_tftp "implementation note" for Etherboot's
|
|
65
|
+ * solution to this problem.
|
|
66
|
+ *
|
|
67
|
+ *
|
|
68
|
+
|
|
69
|
+ * @note According to the PXE specification version 2.1, this call
|
|
70
|
+ * "opens a file for reading/writing", though how writing is to be
|
|
71
|
+ * achieved without the existence of an API call %pxenv_tftp_write()
|
|
72
|
+ * is not made clear.
|
28
|
73
|
*
|
29
|
74
|
* Status: working
|
30
|
75
|
*/
|
|
@@ -197,3 +242,98 @@ PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
|
197
|
242
|
tftp_get_fsize->Status = PXENV_STATUS_SUCCESS;
|
198
|
243
|
return PXENV_EXIT_SUCCESS;
|
199
|
244
|
}
|
|
245
|
+
|
|
246
|
+/** @page pxe_notes Etherboot PXE implementation notes
|
|
247
|
+
|
|
248
|
+@section pxe_note_tftp Welding together the TFTP protocol and the PXE TFTP API
|
|
249
|
+
|
|
250
|
+The PXE TFTP API is fundamentally poorly designed; the TFTP protocol
|
|
251
|
+simply does not map well into "open file", "read file block", "close
|
|
252
|
+file" operations. The problem is the unreliable nature of UDP
|
|
253
|
+transmissions and the lock-step mechanism employed by TFTP to
|
|
254
|
+guarantee file transfer. The lock-step mechanism requires that if we
|
|
255
|
+time out waiting for a packet to arrive, we must trigger its
|
|
256
|
+retransmission by retransmitting our previously transmitted packet.
|
|
257
|
+
|
|
258
|
+For example, suppose that pxenv_tftp_read() is called to read the
|
|
259
|
+first data block of a file from a server that does not support TFTP
|
|
260
|
+options, and that no data block is received within the timeout period.
|
|
261
|
+In order to trigger the retransmission of this data block
|
|
262
|
+pxenv_tftp_read() must retransmit the TFTP open request. However, the
|
|
263
|
+information used to build the TFTP open request is not available at
|
|
264
|
+this time; it was provided only to the pxenv_tftp_open() call.
|
|
265
|
+
|
|
266
|
+The question of when to transmit the ACK packets is also awkward. At
|
|
267
|
+a first glance, it would seem to be fairly simple: acknowledge a
|
|
268
|
+packet immediately after receiving it. However, since the ACK packet
|
|
269
|
+may itself be lost, the next call to pxenv_tftp_read() must be
|
|
270
|
+prepared to re-acknowledge the packet.
|
|
271
|
+
|
|
272
|
+Another problem to consider is that the pxenv_tftp_open() API call
|
|
273
|
+must return an indication of whether or not the TFTP open request
|
|
274
|
+succeeded. In the case of a TFTP server that doesn't support TFTP
|
|
275
|
+options, the only indication of a successful open is the reception of
|
|
276
|
+the first data block. However, the pxenv_tftp_open() API provides no
|
|
277
|
+way to return this data block at this time. Pretending that we lost
|
|
278
|
+the data block and requesting retransmission is problematic, because
|
|
279
|
+the only way to request retransmission of the first data block in such
|
|
280
|
+a case is to reissue the TFTP open request, which has side effects
|
|
281
|
+such as requiring the allocation of a new local port number.
|
|
282
|
+
|
|
283
|
+At least some PXE stacks (e.g. NILO) solve this problem by violating
|
|
284
|
+the TFTP protocol and never bothering with retransmissions, relying on
|
|
285
|
+the TFTP server to retransmit when it times out waiting for an ACK.
|
|
286
|
+This approach is dubious at best.
|
|
287
|
+
|
|
288
|
+The only viable solution seems to be to allocate a buffer for the
|
|
289
|
+storage of the first data packet returned by the TFTP server, since we
|
|
290
|
+may receive this packet during the pxenv_tftp_open() call but have to
|
|
291
|
+return it from the subsequent pxenv_tftp_read() call. This buffer
|
|
292
|
+must be statically allocated and must be dedicated to providing a
|
|
293
|
+temporary home to TFTP packets. There is nothing in the PXE
|
|
294
|
+specification that prevents a caller from calling
|
|
295
|
+e.g. pxenv_undi_transmit() between calls to the TFTP API, so we cannot
|
|
296
|
+use the normal transmit/receive buffer for this purpose.
|
|
297
|
+
|
|
298
|
+Having paid the storage penalty for this buffer, we can then gain some
|
|
299
|
+simplicity by exploiting it in full. There is at least one
|
|
300
|
+circumstance (pxenv_tftp_open() called to open a file on a server that
|
|
301
|
+does not support TFTP options) in which we will have to enter
|
|
302
|
+pxenv_tftp_read() knowing that our previous transmission (the open
|
|
303
|
+request, in this situation) has already been acknowledged.
|
|
304
|
+Implementation of pxenv_tftp_read() can be made simpler by making this
|
|
305
|
+condition an invariant. Specifically, on each call to
|
|
306
|
+pxenv_tftp_read(), we shall ensure that the following are true:
|
|
307
|
+
|
|
308
|
+ - Our previous transmission has already been acknowledged. We
|
|
309
|
+ therefore do not need to keep state about our previous
|
|
310
|
+ transmission.
|
|
311
|
+
|
|
312
|
+ - The next packet to read is already in a buffer in memory.
|
|
313
|
+
|
|
314
|
+In order to maintain these two conditions, pxenv_tftp_read() must do
|
|
315
|
+the following:
|
|
316
|
+
|
|
317
|
+ - Copy the data packet from our buffer to the caller's buffer.
|
|
318
|
+
|
|
319
|
+ - Acknowledge the data packet that we have just copied. This will
|
|
320
|
+ trigger transmission of the next packet from the server.
|
|
321
|
+
|
|
322
|
+ - Retransmit this acknowledgement packet until the next packet
|
|
323
|
+ arrives.
|
|
324
|
+
|
|
325
|
+ - Copy the packet into our internal buffer, ready for the next call
|
|
326
|
+ to pxenv_tftp_read().
|
|
327
|
+
|
|
328
|
+It can be verified that this preserves the invariant condition, and it
|
|
329
|
+is clear that the resulting implementation of pxenv_tftp_read() can be
|
|
330
|
+relatively simple. (For the special case of the last data packet,
|
|
331
|
+pxenv_tftp_read() should return immediately after sending a single
|
|
332
|
+acknowledgement packet.)
|
|
333
|
+
|
|
334
|
+In order to set up this invariant condition for the first call to
|
|
335
|
+pxenv_tftp_read(), pxenv_tftp_open() must do the following:
|
|
336
|
+
|
|
337
|
+ -
|
|
338
|
+
|
|
339
|
+*/
|