TFTP upgraded to use a core function library (in tftpcore.c) which will be
[people/xl0/gpxe.git] / src / proto / tftm.c
1 #if 0
2
3 /**************************************************************************
4 *
5 *    proto_tftm.c -- Etherboot Multicast TFTP 
6 *    Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
7 *
8 *    This program is free software; you can redistribute it and/or modify
9 *    it under the terms of the GNU General Public License as published by
10 *    the Free Software Foundation; either version 2 of the License, or
11 *    (at your option) any later version.
12 *
13 *    This program is distributed in the hope that it will be useful,
14 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *    GNU General Public License for more details.
17 *
18 *    You should have received a copy of the GNU General Public License
19 *    along with this program; if not, write to the Free Software
20 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 *    This code is based on the DOWNLOAD_PROTO_TFTM section of 
23 *    Etherboot 5.3 core/nic.c and:
24 *    
25 *    Anselm Martin Hoffmeister's previous proto_tftm.c multicast work
26 *    Eric Biederman's proto_slam.c
27 *
28 *    $Revision$
29 *    $Author$
30 *    $Date$
31 *
32 *    REVISION HISTORY:
33 *    ================
34 *    09-07-2003 timlegge        Release Version, Capable of Multicast Booting
35 *    08-30-2003 timlegge        Initial version, Assumes consecutive blocks
36 *
37 *    Indent Options: indent -kr -i8
38 ***************************************************************************/
39
40 /*
41  * IMPORTANT
42  *
43  * This file should be rewritten to avoid the use of a bitmap.  Our
44  * buffer routines can cope with being handed blocks in an arbitrary
45  * order, duplicate blocks, etc.  This code could be substantially
46  * simplified by taking advantage of these features.
47  *
48  */
49
50 #include "etherboot.h"
51 #include "proto.h"
52 #include "nic.h"
53
54 struct tftm_info {
55         struct sockaddr_in server;
56         struct sockaddr_in local;
57         struct sockaddr_in multicast;
58         int sent_nack;
59         const char *name;       /* Filename */
60 };
61
62 struct tftm_state {
63         unsigned long block_size;
64         unsigned long total_bytes;
65         unsigned long total_packets;
66         char ismaster;
67         unsigned long received_packets;
68         struct buffer *buffer;
69         unsigned char *image;
70         unsigned char *bitmap;
71         char recvd_oack;
72 } state;
73
74 #define TFTM_PORT 1758
75 #define TFTM_MIN_PACKET 1024
76
77
78 static int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
79                              unsigned long *filesize, struct tftm_info *info);
80
81 static int await_tftm(int ival, void *ptr, unsigned short ptype __unused,
82                       struct iphdr *ip, struct udphdr *udp,
83                       struct tcphdr *tcp __unused)
84 {
85         struct tftm_info *info = ptr;
86
87         /* Check for Unicast data being received */
88         if (ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) {
89                 if (!udp) {
90                         return 0;
91                 }
92                 if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
93                         return 0;
94                 if (ntohs(udp->dest) != ival)
95                         return 0;
96
97                 return 1;       /* Unicast Data Received */
98         }
99
100         /* Also check for Multicast data being received */
101         if ((ip->dest.s_addr == info->multicast.sin_addr.s_addr) &&
102             (ntohs(udp->dest) == info->multicast.sin_port) &&
103             (nic.packetlen >= ETH_HLEN + sizeof(struct iphdr) +
104              sizeof(struct udphdr))) {
105                 return 1;       /* Multicast data received */
106         }
107         return 0;
108 }
109
110 static int proto_tftm(struct tftm_info *info)
111 {
112         int retry = 0;
113         static unsigned short iport = 2000;
114         unsigned short oport = 0;
115         unsigned short len, block = 0, prevblock = 0;
116         struct tftp_t *tr;
117         struct tftpreq_t tp;
118         unsigned long filesize = 0;
119
120         state.image = 0;
121         state.bitmap = 0;
122
123         rx_qdrain();
124
125         /* Warning: the following assumes the layout of bootp_t.
126            But that's fixed by the IP, UDP and BOOTP specs. */
127
128         /* Send a tftm-request to the server */
129         tp.opcode = htons(TFTP_RRQ);    /* Const for "\0x0" "\0x1" =^= ReadReQuest */
130         len =
131             sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
132             sprintf((char *) tp.u.rrq,
133                     "%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
134                     info->name, 0, 0, 0, 0, 0, TFTM_MIN_PACKET, 0, 0) + 1;
135
136         if (!udp_transmit(info->server.sin_addr.s_addr, ++iport,
137                           info->server.sin_port, len, &tp))
138                 return (0);
139
140         /* loop to listen for packets and to receive the file */
141         for (;;) {
142                 long timeout;
143 #ifdef  CONGESTED
144                 timeout =
145                     rfc2131_sleep_interval(block ? TFTP_REXMT : TIMEOUT,
146                                            retry);
147 #else
148                 timeout = rfc2131_sleep_interval(TIMEOUT, retry);
149 #endif
150                 /* Calls the await_reply function in nic.c which in turn calls
151                    await_tftm (1st parameter) as above */
152                 if (!await_reply(await_tftm, iport, info, timeout)) {
153                         if (!block && retry++ < MAX_TFTP_RETRIES) {     /* maybe initial request was lost */
154                                 if (!udp_transmit
155                                     (info->server.sin_addr.s_addr, ++iport,
156                                      info->server.sin_port, len, &tp))
157                                         return (0);
158                                 continue;
159                         }
160 #ifdef  CONGESTED
161                         if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT)) {  /* we resend our last ack */
162                                 DBG("Timed out receiving file");
163                                 len =
164                                     sizeof(tp.ip) + sizeof(tp.udp) +
165                                     sizeof(tp.opcode) +
166                                     sprintf((char *) tp.u.rrq,
167                                             "%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
168                                             info->name, 0, 0, 0, 0, 0,
169                                             TFTM_MIN_PACKET, 0, 0) + 1;
170
171                                 udp_transmit
172                                         (info->server.sin_addr.s_addr,
173                                          ++iport, info->server.sin_port,
174                                          len, &tp);
175                                         continue;
176                         }
177 #endif
178                         break;  /* timeout */
179                 }
180
181                 tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
182
183                 if (tr->opcode == ntohs(TFTP_ERROR)) {
184                         printf("TFTP error %d (%s)\n",
185                                ntohs(tr->u.err.errcode), tr->u.err.errmsg);
186                         break;
187                 }
188
189                 if (tr->opcode == ntohs(TFTP_OACK)) {
190                         int i =
191                             opt_get_multicast(tr, &len, &filesize, info);
192
193                         if (i == 0 || (i != 7 && !state.recvd_oack)) {  /* Multicast unsupported */
194                                 /* Transmit an error message to the server to end the transmission */
195                                 printf
196                                     ("TFTM-Server doesn't understand options [blksize tsize multicast]\n");
197                                 tp.opcode = htons(TFTP_ERROR);
198                                 tp.u.err.errcode = 8;
199                                 /*
200                                  *      Warning: the following assumes the layout of bootp_t.
201                                  *      But that's fixed by the IP, UDP and BOOTP specs.
202                                  */
203                                 len =
204                                     sizeof(tp.ip) + sizeof(tp.udp) +
205                                     sizeof(tp.opcode) +
206                                     sizeof(tp.u.err.errcode) +
207                                     /*
208                                      *      Normally bad form to omit the format string, but in this case
209                                      *      the string we are copying from is fixed. sprintf is just being
210                                      *      used as a strcpy and strlen.
211                                      */
212                                     sprintf((char *) tp.u.err.errmsg,
213                                             "RFC2090 error") + 1;
214                                 udp_transmit(info->server.sin_addr.s_addr,
215                                              iport, ntohs(tr->udp.src),
216                                              len, &tp);
217                                 block = tp.u.ack.block = 0;     /* this ensures, that */
218                                 /* the packet does not get */
219                                 /* processed as data! */
220                                 return (0);
221                         } else {
222                                 unsigned long bitmap_len;
223                                 /* */
224                                 if (!state.recvd_oack) {
225
226                                         state.total_packets =
227                                             1 + (filesize -
228                                                  (filesize %
229                                                   state.block_size)) /
230                                             state.block_size;
231                                         bitmap_len =
232                                             (state.total_packets + 7) / 8;
233                                         if (!state.image) {
234                                                 state.image = phys_to_virt ( state.buffer->start );
235                                                 state.bitmap = state.image + filesize;
236                                                 /* We don't yet use the buffer routines; fake it */
237                                                 state.buffer->fill = filesize;
238
239                                                 memset(state.bitmap, 0,
240                                                        bitmap_len);
241                                         }
242                                         /* If I'm running over multicast join the multicast group */
243                                         join_group(IGMP_SERVER,
244                                               info->multicast.sin_addr.s_addr);
245                                 }
246                                 state.recvd_oack = 1;
247                         }
248
249
250
251                 } else if (tr->opcode == htons(TFTP_DATA)) {
252                         unsigned long data_len;
253                         unsigned char *data;
254                         struct udphdr *udp;
255                         udp =
256                             (struct udphdr *) &nic.packet[ETH_HLEN +
257                                                           sizeof(struct
258                                                                  iphdr)];
259                         len =
260                             ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
261                         data =
262                             nic.packet + ETH_HLEN + sizeof(struct iphdr) +
263                             sizeof(struct udphdr) + 4;
264
265                         if (len > TFTM_MIN_PACKET)      /* shouldn't happen */
266                                 continue;       /* ignore it */
267
268                         block = ntohs(tp.u.ack.block = tr->u.data.block);
269
270                         if (block > state.total_packets) {
271                                 printf("ALERT: Invalid packet number\n");
272                                 continue;
273                         }
274
275                         /* Compute the expected data length */
276                         if (block != state.total_packets) {
277                                 data_len = state.block_size;
278                         } else {
279                                 data_len = filesize % state.block_size;
280                         }
281                         /* If the packet size is wrong drop the packet and then continue */
282                         if (ntohs(udp->len) !=
283                             (data_len + (data - (unsigned char *) udp))) {
284                                 printf
285                                     ("ALERT: udp packet is not the correct size: %d\n",
286                                      block);
287                                 continue;
288                         }
289                         if (nic.packetlen < data_len + (data - nic.packet)) {
290                                 printf
291                                     ("ALERT: Ethernet packet shorter than data_len: %d\n",
292                                      block);
293                                 continue;
294                         }
295
296                         if (data_len > state.block_size) {
297                                 data_len = state.block_size;
298                         }
299                         if (((state.
300                               bitmap[block >> 3] >> (block & 7)) & 1) ==
301                             0) {
302                                 /* Non duplicate packet */
303                                 state.bitmap[block >> 3] |=
304                                     (1 << (block & 7));
305                                 memcpy(state.image +
306                                        ((block - 1) * state.block_size),
307                                        data, data_len);
308                                 state.received_packets++;
309                         } else {
310
311 /*                              printf("<DUP>\n"); */
312                         }
313                 }
314
315                 else {          /* neither TFTP_OACK, TFTP_DATA nor TFTP_ERROR */
316                         break;
317                 }
318
319                 if (state.received_packets <= state.total_packets) {
320                         unsigned long b;
321                         unsigned long len;
322                         unsigned long max;
323                         int value;
324                         int last;
325
326                         /* Compute the last bit and store an inverted trailer */
327                         max = state.total_packets + 1;
328                         value =
329                             ((state.
330                               bitmap[(max - 1) >> 3] >> ((max -
331                                                           1) & 7)) & 1);
332                         value = !value;
333                         state.bitmap[max >> 3] &= ~(1 << (max & 7));
334                         state.bitmap[max >> 3] |= value << (max & 7);
335
336                         len = 0;
337                         last = 0;       /* Start with the received packets */
338                         for (b = 1; b <= max; b++) {
339                                 value =
340                                     (state.bitmap[b >> 3] >> (b & 7)) & 1;
341
342                                 if (value == 0) {
343                                         tp.u.ack.block = htons(b - 1);  /* Acknowledge the previous block */
344                                         break;
345                                 }
346                         }
347                 }
348                 if (state.ismaster) {
349                         tp.opcode = htons(TFTP_ACK);
350                         oport = ntohs(tr->udp.src);
351                         udp_transmit(info->server.sin_addr.s_addr, iport,
352                                      oport, TFTP_MIN_PACKET, &tp); /* ack */
353                 }
354                 if (state.received_packets == state.total_packets) {
355                         /* If the client is finished and not the master,
356                          * ack the last packet */
357                         if (!state.ismaster) {
358                                 tp.opcode = htons(TFTP_ACK);
359                                 /* Ack Last packet to end xfer */
360                                 tp.u.ack.block = htons(state.total_packets);
361                                 oport = ntohs(tr->udp.src);
362                                 udp_transmit(info->server.sin_addr.s_addr,
363                                              iport, oport,
364                                              TFTP_MIN_PACKET, &tp); /* ack */
365                         }
366                         /* We are done get out */
367                         break;
368                 }
369
370                 if ((unsigned short) (block - prevblock) != 1) {
371                         /* Retransmission or OACK, don't process via callback
372                          * and don't change the value of prevblock.  */
373                         continue;
374                 }
375
376                 prevblock = block;
377                 retry = 0;      /* It's the right place to zero the timer? */
378
379         }
380         /* Leave the multicast group */
381         leave_group(IGMP_SERVER);
382         return 1;
383 }
384
385 static int url_tftm ( char *url __unused, struct sockaddr_in *server,
386                       char *file, struct buffer *buffer ) {
387
388         int ret;
389         struct tftm_info info;
390
391         /* Set the defaults */
392         info.server = *server;
393         info.local.sin_addr.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
394         info.local.sin_port = TFTM_PORT; /* Does not matter. */
395         info.multicast = info.local;
396         state.ismaster = 0;
397         info.name = file;
398
399         state.block_size = 0;
400         state.total_bytes = 0;
401         state.total_packets = 0;
402         state.received_packets = 0;
403         state.buffer = buffer;
404         state.image = 0;
405         state.bitmap = 0;
406         state.recvd_oack = 0;
407
408         if (file[0] != '/') {
409                 printf("Bad tftm-URI: [%s]\n", file);
410                 return 0;
411         }
412
413         ret = proto_tftm(&info);
414
415         return ret;
416 }
417
418 /******************************
419 * Parse the multicast options
420 *******************************/
421 static int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
422                              unsigned long *filesize, struct tftm_info *info)
423 {
424         const char *p = tr->u.oack.data, *e = 0;
425         int i = 0;
426         *len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
427         if (*len > TFTM_MIN_PACKET)
428                 return -1;
429         e = p + *len;
430
431         while (*p != '\0' && p < e) {
432                 if (!strcasecmp("tsize", p)) {
433                         p += 6;
434                         if ((*filesize = strtoul(p, &p, 10)) > 0)
435                                 i |= 4;
436                         DBG("\n");
437                         DBG("tsize=%d\n", *filesize);
438                         while (p < e && *p)
439                                 p++;
440                         if (p < e)
441                                 p++;
442                 } else if (!strcasecmp("blksize", p)) {
443                         i |= 2;
444                         p += 8;
445                         state.block_size = strtoul(p, &p, 10);
446                         if (state.block_size != TFTM_MIN_PACKET) {
447                                 printf
448                                     ("TFTM-Server rejected required transfer blocksize %d\n",
449                                      TFTM_MIN_PACKET);
450                                 return 0;
451                         }
452                         DBG("blksize=%d\n", state.block_size);
453                         while (p < e && *p)
454                                 p++;
455                         if (p < e)
456                                 p++;
457                 } else if (!strncmp(p, "multicast", 10)) {
458                         i |= 1;
459                         p += 10;
460                         DBG("multicast options: %s\n", p);
461                         p += 1 + inet_aton(p, &info->multicast.sin_addr);
462                         DBG("multicast ip = %@\n", info->multicast_ip);
463                         info->multicast.sin_port = strtoul(p, &p, 10);
464                         ++p;
465                         DBG("multicast port = %d\n",
466                             info->multicast.sin_port);
467                         state.ismaster = (*p == '1' ? 1 : 0);
468                         DBG("multicast ismaster = %d\n",
469                                state.ismaster);
470                         while (p < e && *p)
471                                 p++;
472                         if (p < e)
473                                 p++;
474                 }
475         }
476         if (p > e)
477                 return 0;
478         return i;
479 }
480
481 static struct protocol tftm_protocol __protocol = {
482         .name = "x-tftm",
483         .default_port = TFTM_PORT,
484         .load = url_tftm,
485 };
486
487 #endif