瀏覽代碼

Thoughts on how to coerce the PXE TFTP API into something resembling

the TFTP protocol.
tags/v0.9.3
Michael Brown 19 年之前
父節點
當前提交
aeb984a1a9
共有 1 個檔案被更改,包括 141 行新增1 行删除
  1. 141
    1
      src/interface/pxe/pxe_tftp.c

+ 141
- 1
src/interface/pxe/pxe_tftp.c 查看文件

@@ -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
+*/

Loading…
取消
儲存