Imported from Etherboot 5.4 tree
[people/lynusvaz/gpxe.git] / src / proto / fsp.c
1     /*********************************************************************\
2     * Copyright (c) 2005 by Radim Kolar (hsn-sendmail.cz)                 *
3     *                                                                     *
4     * You may copy or modify this file in any manner you wish, provided   *
5     * that this notice is always included, and that you hold the author   *
6     * harmless for any loss or damage resulting from the installation or  *
7     * use of this software.                                               *
8     *                                                                     *
9     * This file provides support for FSP v2 protocol written from scratch *
10     * by Radim Kolar,   FSP project leader.                               *
11     *                                                                     *
12     * ABOUT FSP                                                           *
13     * FSP is a lightweight file transfer protocol and is being used for   *
14     * booting, Internet firmware updates, embedded devices and in         *
15     * wireless applications. FSP is very easy to implement; contact Radim *
16     * Kolar if you need hand optimized assembler FSP stacks for various   *
17     * microcontrollers, CPUs or consultations.                            *
18     * http://fsp.sourceforge.net/                                         *
19     *                                                                     *
20     * REVISION HISTORY                                                    *
21     * 1.0 2005-03-17 rkolar   Initial coding                              *
22     * 1.1 2005-03-24 rkolar   We really need to send CC_BYE to the server *
23     *                         at end of transfer, because next stage boot *
24     *                         loader is unable to contact FSP server      *
25     *                         until session timeouts.                     *
26     * 1.2 2005-03-26 rkolar   We need to query filesize in advance,       *
27     *                         because NBI loader do not reads file until  *
28     *                         eof is reached.
29     * REMARKS                                                             *
30     * there is no support for selecting port number of fsp server, maybe  *
31     *   we should parse fsp:// URLs in boot image filename.               *
32     * this implementation has filename limit 255 chars.                   *
33     \*********************************************************************/
34
35 #ifdef DOWNLOAD_PROTO_FSP
36 #include "etherboot.h"
37 #include "nic.h"
38
39 #define FSP_PORT 21
40
41 /* FSP commands */
42 #define CC_GET_FILE     0x42
43 #define CC_BYE          0x4A
44 #define CC_ERR          0x40
45 #define CC_STAT         0x4D
46
47 /* etherboot limits */
48 #define FSP_MAXFILENAME 255
49
50 struct fsp_info {
51         in_addr server_ip;
52         uint16_t server_port;
53         uint16_t local_port;
54         const char *filename;
55         int (*fnc)(unsigned char *, unsigned int, unsigned int, int);
56 };
57
58 struct fsp_header {
59         uint8_t cmd;
60         uint8_t sum;
61         uint16_t key;
62         uint16_t seq;
63         uint16_t len;
64         uint32_t pos;
65 } PACKED;
66
67 #define FSP_MAXPAYLOAD (ETH_MAX_MTU - \
68   (sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct fsp_header)))
69
70 static struct fsp_request {
71         struct iphdr ip;
72         struct udphdr udp;
73         struct fsp_header fsp;
74         unsigned char data[FSP_MAXFILENAME + 1 + 2];
75 } request;
76
77 struct fsp_reply {
78         struct iphdr ip;
79         struct udphdr udp;
80         struct fsp_header fsp;
81         unsigned char data[FSP_MAXPAYLOAD];
82 } PACKED;
83
84
85 static int await_fsp(int ival, void *ptr, unsigned short ptype __unused,
86                       struct iphdr *ip, struct udphdr *udp)
87 {
88         if(!udp)
89             return 0;
90         if (ip->dest.s_addr != arptable[ARP_CLIENT].ipaddr.s_addr) 
91             return 0;
92         if (ntohs(udp->dest) != ival)
93             return 0;
94         if (ntohs(udp->len) < 12+sizeof(struct udphdr))
95             return 0;
96         return 1;
97 }
98
99 static int proto_fsp(struct fsp_info *info)
100 {
101     uint32_t filepos;
102     uint32_t filelength=0;
103     int i,retry;
104     uint16_t reqlen;
105     struct fsp_reply *reply;
106     int block=1;
107     
108     /* prepare FSP request packet */
109     filepos=0;
110     i=strlen(info->filename);
111     if(i>FSP_MAXFILENAME)
112     {
113         printf("Boot filename is too long.\n");
114         return 0;
115     }
116     strcpy(request.data,info->filename);
117     *(uint16_t *)(request.data+i+1)=htons(FSP_MAXPAYLOAD);
118     request.fsp.len=htons(i+1);
119     reqlen=i+3+12;
120
121     rx_qdrain();
122     retry=0;
123
124     /* main loop */
125     for(;;) {
126         int  sum;
127         long timeout;
128
129         /* query filelength if not known */
130         if(filelength == 0)
131             request.fsp.cmd=CC_STAT;
132                 
133         /* prepare request packet */
134         request.fsp.pos=htonl(filepos);
135         request.fsp.seq=random();
136         request.fsp.sum=0;
137         for(i=0,sum=reqlen;i<reqlen;i++)
138         {
139             sum += ((uint8_t *)&request.fsp)[i];
140         }
141         request.fsp.sum= sum + (sum >> 8);
142         /* send request */
143         if (!udp_transmit(info->server_ip.s_addr, info->local_port,
144                          info->server_port, sizeof(request.ip) +
145                          sizeof(request.udp) + reqlen, &request))
146                             return (0);
147         /* wait for retry */                
148 #ifdef  CONGESTED
149         timeout =
150             rfc2131_sleep_interval(filepos ? TFTP_REXMT : TIMEOUT, retry);
151 #else
152         timeout = rfc2131_sleep_interval(TIMEOUT, retry);
153 #endif
154         retry++;
155         if (!await_reply(await_fsp, info->local_port, NULL, timeout))
156             continue;
157         reply=(struct fsp_reply *) &nic.packet[ETH_HLEN];    
158         /* check received packet */
159         if (reply->fsp.seq != request.fsp.seq)
160             continue;
161         reply->udp.len=ntohs(reply->udp.len)-sizeof(struct udphdr);
162         if(reply->udp.len < ntohs(reply->fsp.len) + 12 )
163             continue;
164         sum=-reply->fsp.sum;
165         for(i=0;i<reply->udp.len;i++)
166         {
167             sum += ((uint8_t *)&(reply->fsp))[i];
168         }
169         sum = (sum + (sum >> 8)) & 0xff;
170         if(sum != reply->fsp.sum)
171         {
172             printf("FSP checksum failed. computed %d, but packet has %d.\n",sum,reply->fsp.sum);
173             continue;
174         }
175         if(reply->fsp.cmd == CC_ERR)
176         {
177             printf("\nFSP error: %s",info->filename);
178             if(reply->fsp.len)
179                 printf(" [%s]",reply->data);
180             printf("\n");
181             return 0;
182         }
183         if(reply->fsp.cmd == CC_BYE && filelength == 1)
184         {
185             info->fnc(request.data,block,1,1);
186             return 1;
187         }
188         if(reply->fsp.cmd == CC_STAT)
189         {
190             if(reply->data[8] == 0)
191             {
192                 /* file not found, etc. */
193                 filelength=0xffffffff;
194             } else
195             {
196                 filelength= ntohl(*((uint32_t *)&reply->data[4]));
197             }
198             request.fsp.cmd = CC_GET_FILE;
199             request.fsp.key = reply->fsp.key;
200             retry=0;
201             continue;
202         }
203
204         if(reply->fsp.cmd == CC_GET_FILE)
205         {
206             if(ntohl(reply->fsp.pos) != filepos)
207                 continue;
208             request.fsp.key = reply->fsp.key;
209             retry=0;
210             i=ntohs(reply->fsp.len);
211             if(i == 1)
212             {
213                 request.fsp.cmd=CC_BYE;
214                 request.data[0]=reply->data[0];
215                 continue;
216             }
217             /* let last byte alone */
218             if(i >= filelength)
219                 i = filelength - 1;
220             if(!info->fnc(reply->data,block++,i,0))
221                 return 0;
222             filepos += i;
223             filelength -= i;
224         }
225     }
226
227     return 0;
228 }
229
230 int url_fsp(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
231 {
232         struct fsp_info info;
233         /* Set the defaults */
234         info.server_ip.s_addr    = arptable[ARP_SERVER].ipaddr.s_addr;
235         info.server_port         = FSP_PORT;
236         info.local_port          = 1024 + random() & 0xfbff;
237         info.fnc                 = fnc;
238         
239         /* Now parse the url */
240         /* printf("fsp-URI: [%s]\n", name); */
241         /* quick hack for now */
242         info.filename=name;
243         return proto_fsp(&info);
244 }
245 #endif