[pxeprefix] Search for the PXE entry points through all methods
[people/asdlkf/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
37 #define FSP_PORT 21
38
39 /* FSP commands */
40 #define CC_GET_FILE     0x42
41 #define CC_BYE          0x4A
42 #define CC_ERR          0x40
43 #define CC_STAT         0x4D
44
45 /* etherboot limits */
46 #define FSP_MAXFILENAME 255
47
48 struct fsp_info {
49         in_addr server_ip;
50         uint16_t server_port;
51         uint16_t local_port;
52         const char *filename;
53         int (*fnc)(unsigned char *, unsigned int, unsigned int, int);
54 };
55
56 struct fsp_header {
57         uint8_t cmd;
58         uint8_t sum;
59         uint16_t key;
60         uint16_t seq;
61         uint16_t len;
62         uint32_t pos;
63 } PACKED;
64
65 #define FSP_MAXPAYLOAD (ETH_MAX_MTU - \
66   (sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct fsp_header)))
67
68 static struct fsp_request {
69         struct iphdr ip;
70         struct udphdr udp;
71         struct fsp_header fsp;
72         unsigned char data[FSP_MAXFILENAME + 1 + 2];
73 } request;
74
75 struct fsp_reply {
76         struct iphdr ip;
77         struct udphdr udp;
78         struct fsp_header fsp;
79         unsigned char data[FSP_MAXPAYLOAD];
80 } PACKED;
81
82
83 static int await_fsp(int ival, void *ptr, unsigned short ptype __unused,
84                       struct iphdr *ip, struct udphdr *udp)
85 {
86         if(!udp)
87             return 0;
88         if (ip->dest.s_addr != arptable[ARP_CLIENT].ipaddr.s_addr) 
89             return 0;
90         if (ntohs(udp->dest) != ival)
91             return 0;
92         if (ntohs(udp->len) < 12+sizeof(struct udphdr))
93             return 0;
94         return 1;
95 }
96
97 static int proto_fsp(struct fsp_info *info)
98 {
99     uint32_t filepos;
100     uint32_t filelength=0;
101     int i,retry;
102     uint16_t reqlen;
103     struct fsp_reply *reply;
104     int block=1;
105     
106     /* prepare FSP request packet */
107     filepos=0;
108     i=strlen(info->filename);
109     if(i>FSP_MAXFILENAME)
110     {
111         printf("Boot filename is too long.\n");
112         return 0;
113     }
114     strcpy(request.data,info->filename);
115     *(uint16_t *)(request.data+i+1)=htons(FSP_MAXPAYLOAD);
116     request.fsp.len=htons(i+1);
117     reqlen=i+3+12;
118
119     rx_qdrain();
120     retry=0;
121
122     /* main loop */
123     for(;;) {
124         int  sum;
125         long timeout;
126
127         /* query filelength if not known */
128         if(filelength == 0)
129             request.fsp.cmd=CC_STAT;
130                 
131         /* prepare request packet */
132         request.fsp.pos=htonl(filepos);
133         request.fsp.seq=random();
134         request.fsp.sum=0;
135         for(i=0,sum=reqlen;i<reqlen;i++)
136         {
137             sum += ((uint8_t *)&request.fsp)[i];
138         }
139         request.fsp.sum= sum + (sum >> 8);
140         /* send request */
141         if (!udp_transmit(info->server_ip.s_addr, info->local_port,
142                          info->server_port, sizeof(request.ip) +
143                          sizeof(request.udp) + reqlen, &request))
144                             return (0);
145         /* wait for retry */                
146 #ifdef  CONGESTED
147         timeout =
148             rfc2131_sleep_interval(filepos ? TFTP_REXMT : TIMEOUT, retry);
149 #else
150         timeout = rfc2131_sleep_interval(TIMEOUT, retry);
151 #endif
152         retry++;
153         if (!await_reply(await_fsp, info->local_port, NULL, timeout))
154             continue;
155         reply=(struct fsp_reply *) &nic.packet[ETH_HLEN];    
156         /* check received packet */
157         if (reply->fsp.seq != request.fsp.seq)
158             continue;
159         reply->udp.len=ntohs(reply->udp.len)-sizeof(struct udphdr);
160         if(reply->udp.len < ntohs(reply->fsp.len) + 12 )
161             continue;
162         sum=-reply->fsp.sum;
163         for(i=0;i<reply->udp.len;i++)
164         {
165             sum += ((uint8_t *)&(reply->fsp))[i];
166         }
167         sum = (sum + (sum >> 8)) & 0xff;
168         if(sum != reply->fsp.sum)
169         {
170             printf("FSP checksum failed. computed %d, but packet has %d.\n",sum,reply->fsp.sum);
171             continue;
172         }
173         if(reply->fsp.cmd == CC_ERR)
174         {
175             printf("\nFSP error: %s",info->filename);
176             if(reply->fsp.len)
177                 printf(" [%s]",reply->data);
178             printf("\n");
179             return 0;
180         }
181         if(reply->fsp.cmd == CC_BYE && filelength == 1)
182         {
183             info->fnc(request.data,block,1,1);
184             return 1;
185         }
186         if(reply->fsp.cmd == CC_STAT)
187         {
188             if(reply->data[8] == 0)
189             {
190                 /* file not found, etc. */
191                 filelength=0xffffffff;
192             } else
193             {
194                 filelength= ntohl(*((uint32_t *)&reply->data[4]));
195             }
196             request.fsp.cmd = CC_GET_FILE;
197             request.fsp.key = reply->fsp.key;
198             retry=0;
199             continue;
200         }
201
202         if(reply->fsp.cmd == CC_GET_FILE)
203         {
204             if(ntohl(reply->fsp.pos) != filepos)
205                 continue;
206             request.fsp.key = reply->fsp.key;
207             retry=0;
208             i=ntohs(reply->fsp.len);
209             if(i == 1)
210             {
211                 request.fsp.cmd=CC_BYE;
212                 request.data[0]=reply->data[0];
213                 continue;
214             }
215             /* let last byte alone */
216             if(i >= filelength)
217                 i = filelength - 1;
218             if(!info->fnc(reply->data,block++,i,0))
219                 return 0;
220             filepos += i;
221             filelength -= i;
222         }
223     }
224
225     return 0;
226 }
227
228 int url_fsp(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
229 {
230         struct fsp_info info;
231         /* Set the defaults */
232         info.server_ip.s_addr    = arptable[ARP_SERVER].ipaddr.s_addr;
233         info.server_port         = FSP_PORT;
234         info.local_port          = 1024 + random() & 0xfbff;
235         info.fnc                 = fnc;
236         
237         /* Now parse the url */
238         /* printf("fsp-URI: [%s]\n", name); */
239         /* quick hack for now */
240         info.filename=name;
241         return proto_fsp(&info);
242 }
243 #endif