Thoughts on how to coerce the PXE TFTP API into something resembling
[people/xl0/gpxe.git] / src / interface / pxe / pxe_tftp.c
1 /** @file
2  *
3  * PXE TFTP API
4  *
5  */
6
7 /*
8  * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25 #include "pxe.h"
26
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.
73  *
74  * Status: working
75  */
76 PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
77         struct sockaddr_in tftp_server;
78         struct tftpreq_info_t request;
79         struct tftpblk_info_t block;
80
81         DBG ( "PXENV_TFTP_OPEN" );
82         ENSURE_READY ( tftp_open );
83
84         /* Set server address and port */
85         tftp_server.sin_addr.s_addr = tftp_open->ServerIPAddress
86                 ? tftp_open->ServerIPAddress
87                 : arptable[ARP_SERVER].ipaddr.s_addr;
88         tftp_server.sin_port = ntohs ( tftp_open->TFTPPort );
89 #ifdef WORK_AROUND_BPBATCH_BUG        
90         /* Force use of port 69; BpBatch tries to use port 4 for some         
91         * bizarre reason.         */        
92         tftp_server.sin_port = TFTP_PORT;
93 #endif
94         /* Ignore gateway address; we can route properly */
95         /* Fill in request structure */
96         request.server = &tftp_server;
97         request.name = tftp_open->FileName;
98         request.blksize = tftp_open->PacketSize;
99         DBG ( " %@:%d/%s (%d)", tftp_open->ServerIPAddress,
100               tftp_open->TFTPPort, request.name, request.blksize );
101         if ( !request.blksize ) request.blksize = TFTP_DEFAULTSIZE_PACKET;
102         /* Make request and get first packet */
103         if ( !tftp_block ( &request, &block ) ) {
104                 tftp_open->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
105                 return PXENV_EXIT_FAILURE;
106         }
107         /* Fill in PacketSize */
108         tftp_open->PacketSize = request.blksize;
109         /* Store first block for later retrieval by TFTP_READ */
110         pxe_stack->tftpdata.magic_cookie = PXE_TFTP_MAGIC_COOKIE;
111         pxe_stack->tftpdata.len = block.len;
112         pxe_stack->tftpdata.eof = block.eof;
113         memcpy ( pxe_stack->tftpdata.data, block.data, block.len );
114
115         tftp_open->Status = PXENV_STATUS_SUCCESS;
116         return PXENV_EXIT_SUCCESS;
117 }
118
119 /* PXENV_TFTP_CLOSE
120  *
121  * Status: working
122  */
123 PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) {
124         DBG ( "PXENV_TFTP_CLOSE" );
125         ENSURE_READY ( tftp_close );
126         tftp_close->Status = PXENV_STATUS_SUCCESS;
127         return PXENV_EXIT_SUCCESS;
128 }
129
130 /* PXENV_TFTP_READ
131  *
132  * Status: working
133  */
134 PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) {
135         struct tftpblk_info_t block;
136
137         DBG ( "PXENV_TFTP_READ" );
138         ENSURE_READY ( tftp_read );
139
140         /* Do we have a block pending */
141         if ( pxe_stack->tftpdata.magic_cookie == PXE_TFTP_MAGIC_COOKIE ) {
142                 block.data = pxe_stack->tftpdata.data;
143                 block.len = pxe_stack->tftpdata.len;
144                 block.eof = pxe_stack->tftpdata.eof;
145                 block.block = 1; /* Will be the first block */
146                 pxe_stack->tftpdata.magic_cookie = 0;
147         } else {
148                 if ( !tftp_block ( NULL, &block ) ) {
149                         tftp_read->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
150                         return PXENV_EXIT_FAILURE;
151                 }
152         }
153
154         /* Return data */
155         tftp_read->PacketNumber = block.block;
156         tftp_read->BufferSize = block.len;
157         memcpy ( SEGOFF16_TO_PTR(tftp_read->Buffer), block.data, block.len );
158         DBG ( " %d to %hx:%hx", block.len, tftp_read->Buffer.segment,
159               tftp_read->Buffer.offset );
160  
161         tftp_read->Status = PXENV_STATUS_SUCCESS;
162         return PXENV_EXIT_SUCCESS;
163 }
164
165 /* PXENV_TFTP_READ_FILE
166  *
167  * Status: working
168  */
169
170 int pxe_tftp_read_block ( unsigned char *data, unsigned int block __unused,
171                           unsigned int len, int eof ) {
172         if ( pxe_stack->readfile.buffer ) {
173                 if ( pxe_stack->readfile.offset + len >=
174                      pxe_stack->readfile.bufferlen ) return -1;
175                 memcpy ( pxe_stack->readfile.buffer +
176                          pxe_stack->readfile.offset, data, len );
177         }
178         pxe_stack->readfile.offset += len;
179         return eof ? 0 : 1;
180 }
181
182 PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
183                                     *tftp_read_file ) {
184         struct sockaddr_in tftp_server;
185         int rc;
186
187         DBG ( "PXENV_TFTP_READ_FILE %s to [%x,%x)", tftp_read_file->FileName,
188               tftp_read_file->Buffer,
189               tftp_read_file->Buffer + tftp_read_file->BufferSize );
190         ENSURE_READY ( tftp_read_file );
191
192         /* inserted by Klaus Wittemeier */
193         /* KERNEL_BUF stores the name of the last required file */
194         /* This is a fix to make Microsoft Remote Install Services work (RIS) */
195         memcpy(KERNEL_BUF, tftp_read_file->FileName, sizeof(KERNEL_BUF));
196         /* end of insertion */
197
198         /* Set server address and port */
199         tftp_server.sin_addr.s_addr = tftp_read_file->ServerIPAddress
200                 ? tftp_read_file->ServerIPAddress
201                 : arptable[ARP_SERVER].ipaddr.s_addr;
202         tftp_server.sin_port = ntohs ( tftp_read_file->TFTPSrvPort );
203
204         pxe_stack->readfile.buffer = phys_to_virt ( tftp_read_file->Buffer );
205         pxe_stack->readfile.bufferlen = tftp_read_file->BufferSize;
206         pxe_stack->readfile.offset = 0;
207
208         rc = tftp ( NULL, &tftp_server, tftp_read_file->FileName,
209                     pxe_tftp_read_block );
210         if ( rc ) {
211                 tftp_read_file->Status = PXENV_STATUS_FAILURE;
212                 return PXENV_EXIT_FAILURE;
213         }
214         tftp_read_file->Status = PXENV_STATUS_SUCCESS;
215         return PXENV_EXIT_SUCCESS;
216 }
217
218 /* PXENV_TFTP_GET_FSIZE
219  *
220  * Status: working, though ugly (we actually read the whole file,
221  * because it's too ugly to make Etherboot request the tsize option
222  * and hand it to us).
223  */
224 PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
225                                     *tftp_get_fsize ) {
226         int rc;
227
228         DBG ( "PXENV_TFTP_GET_FSIZE" );
229         ENSURE_READY ( tftp_get_fsize );
230
231         pxe_stack->readfile.buffer = NULL;
232         pxe_stack->readfile.bufferlen = 0;
233         pxe_stack->readfile.offset = 0;
234
235 #warning "Rewrite pxenv_tftp_get_fsize, please"
236         if ( rc ) {
237                 tftp_get_fsize->FileSize = 0;
238                 tftp_get_fsize->Status = PXENV_STATUS_FAILURE;
239                 return PXENV_EXIT_FAILURE;
240         }
241         tftp_get_fsize->FileSize = pxe_stack->readfile.offset;
242         tftp_get_fsize->Status = PXENV_STATUS_SUCCESS;
243         return PXENV_EXIT_SUCCESS;
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 */