mirror of
https://github.com/xcat2/xNBA.git
synced 2024-11-23 18:10:10 +00:00
246 lines
7.2 KiB
C
246 lines
7.2 KiB
C
/*********************************************************************\
|
|
* Copyright (c) 2005 by Radim Kolar (hsn-sendmail.cz) *
|
|
* *
|
|
* You may copy or modify this file in any manner you wish, provided *
|
|
* that this notice is always included, and that you hold the author *
|
|
* harmless for any loss or damage resulting from the installation or *
|
|
* use of this software. *
|
|
* *
|
|
* This file provides support for FSP v2 protocol written from scratch *
|
|
* by Radim Kolar, FSP project leader. *
|
|
* *
|
|
* ABOUT FSP *
|
|
* FSP is a lightweight file transfer protocol and is being used for *
|
|
* booting, Internet firmware updates, embedded devices and in *
|
|
* wireless applications. FSP is very easy to implement; contact Radim *
|
|
* Kolar if you need hand optimized assembler FSP stacks for various *
|
|
* microcontrollers, CPUs or consultations. *
|
|
* http://fsp.sourceforge.net/ *
|
|
* *
|
|
* REVISION HISTORY *
|
|
* 1.0 2005-03-17 rkolar Initial coding *
|
|
* 1.1 2005-03-24 rkolar We really need to send CC_BYE to the server *
|
|
* at end of transfer, because next stage boot *
|
|
* loader is unable to contact FSP server *
|
|
* until session timeouts. *
|
|
* 1.2 2005-03-26 rkolar We need to query filesize in advance, *
|
|
* because NBI loader do not reads file until *
|
|
* eof is reached.
|
|
* REMARKS *
|
|
* there is no support for selecting port number of fsp server, maybe *
|
|
* we should parse fsp:// URLs in boot image filename. *
|
|
* this implementation has filename limit 255 chars. *
|
|
\*********************************************************************/
|
|
|
|
#ifdef DOWNLOAD_PROTO_FSP
|
|
#include "etherboot.h"
|
|
#include "nic.h"
|
|
|
|
#define FSP_PORT 21
|
|
|
|
/* FSP commands */
|
|
#define CC_GET_FILE 0x42
|
|
#define CC_BYE 0x4A
|
|
#define CC_ERR 0x40
|
|
#define CC_STAT 0x4D
|
|
|
|
/* etherboot limits */
|
|
#define FSP_MAXFILENAME 255
|
|
|
|
struct fsp_info {
|
|
in_addr server_ip;
|
|
uint16_t server_port;
|
|
uint16_t local_port;
|
|
const char *filename;
|
|
int (*fnc)(unsigned char *, unsigned int, unsigned int, int);
|
|
};
|
|
|
|
struct fsp_header {
|
|
uint8_t cmd;
|
|
uint8_t sum;
|
|
uint16_t key;
|
|
uint16_t seq;
|
|
uint16_t len;
|
|
uint32_t pos;
|
|
} PACKED;
|
|
|
|
#define FSP_MAXPAYLOAD (ETH_MAX_MTU - \
|
|
(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct fsp_header)))
|
|
|
|
static struct fsp_request {
|
|
struct iphdr ip;
|
|
struct udphdr udp;
|
|
struct fsp_header fsp;
|
|
unsigned char data[FSP_MAXFILENAME + 1 + 2];
|
|
} request;
|
|
|
|
struct fsp_reply {
|
|
struct iphdr ip;
|
|
struct udphdr udp;
|
|
struct fsp_header fsp;
|
|
unsigned char data[FSP_MAXPAYLOAD];
|
|
} PACKED;
|
|
|
|
|
|
static int await_fsp(int ival, void *ptr, unsigned short ptype __unused,
|
|
struct iphdr *ip, struct udphdr *udp)
|
|
{
|
|
if(!udp)
|
|
return 0;
|
|
if (ip->dest.s_addr != arptable[ARP_CLIENT].ipaddr.s_addr)
|
|
return 0;
|
|
if (ntohs(udp->dest) != ival)
|
|
return 0;
|
|
if (ntohs(udp->len) < 12+sizeof(struct udphdr))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int proto_fsp(struct fsp_info *info)
|
|
{
|
|
uint32_t filepos;
|
|
uint32_t filelength=0;
|
|
int i,retry;
|
|
uint16_t reqlen;
|
|
struct fsp_reply *reply;
|
|
int block=1;
|
|
|
|
/* prepare FSP request packet */
|
|
filepos=0;
|
|
i=strlen(info->filename);
|
|
if(i>FSP_MAXFILENAME)
|
|
{
|
|
printf("Boot filename is too long.\n");
|
|
return 0;
|
|
}
|
|
strcpy(request.data,info->filename);
|
|
*(uint16_t *)(request.data+i+1)=htons(FSP_MAXPAYLOAD);
|
|
request.fsp.len=htons(i+1);
|
|
reqlen=i+3+12;
|
|
|
|
rx_qdrain();
|
|
retry=0;
|
|
|
|
/* main loop */
|
|
for(;;) {
|
|
int sum;
|
|
long timeout;
|
|
|
|
/* query filelength if not known */
|
|
if(filelength == 0)
|
|
request.fsp.cmd=CC_STAT;
|
|
|
|
/* prepare request packet */
|
|
request.fsp.pos=htonl(filepos);
|
|
request.fsp.seq=random();
|
|
request.fsp.sum=0;
|
|
for(i=0,sum=reqlen;i<reqlen;i++)
|
|
{
|
|
sum += ((uint8_t *)&request.fsp)[i];
|
|
}
|
|
request.fsp.sum= sum + (sum >> 8);
|
|
/* send request */
|
|
if (!udp_transmit(info->server_ip.s_addr, info->local_port,
|
|
info->server_port, sizeof(request.ip) +
|
|
sizeof(request.udp) + reqlen, &request))
|
|
return (0);
|
|
/* wait for retry */
|
|
#ifdef CONGESTED
|
|
timeout =
|
|
rfc2131_sleep_interval(filepos ? TFTP_REXMT : TIMEOUT, retry);
|
|
#else
|
|
timeout = rfc2131_sleep_interval(TIMEOUT, retry);
|
|
#endif
|
|
retry++;
|
|
if (!await_reply(await_fsp, info->local_port, NULL, timeout))
|
|
continue;
|
|
reply=(struct fsp_reply *) &nic.packet[ETH_HLEN];
|
|
/* check received packet */
|
|
if (reply->fsp.seq != request.fsp.seq)
|
|
continue;
|
|
reply->udp.len=ntohs(reply->udp.len)-sizeof(struct udphdr);
|
|
if(reply->udp.len < ntohs(reply->fsp.len) + 12 )
|
|
continue;
|
|
sum=-reply->fsp.sum;
|
|
for(i=0;i<reply->udp.len;i++)
|
|
{
|
|
sum += ((uint8_t *)&(reply->fsp))[i];
|
|
}
|
|
sum = (sum + (sum >> 8)) & 0xff;
|
|
if(sum != reply->fsp.sum)
|
|
{
|
|
printf("FSP checksum failed. computed %d, but packet has %d.\n",sum,reply->fsp.sum);
|
|
continue;
|
|
}
|
|
if(reply->fsp.cmd == CC_ERR)
|
|
{
|
|
printf("\nFSP error: %s",info->filename);
|
|
if(reply->fsp.len)
|
|
printf(" [%s]",reply->data);
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
if(reply->fsp.cmd == CC_BYE && filelength == 1)
|
|
{
|
|
info->fnc(request.data,block,1,1);
|
|
return 1;
|
|
}
|
|
if(reply->fsp.cmd == CC_STAT)
|
|
{
|
|
if(reply->data[8] == 0)
|
|
{
|
|
/* file not found, etc. */
|
|
filelength=0xffffffff;
|
|
} else
|
|
{
|
|
filelength= ntohl(*((uint32_t *)&reply->data[4]));
|
|
}
|
|
request.fsp.cmd = CC_GET_FILE;
|
|
request.fsp.key = reply->fsp.key;
|
|
retry=0;
|
|
continue;
|
|
}
|
|
|
|
if(reply->fsp.cmd == CC_GET_FILE)
|
|
{
|
|
if(ntohl(reply->fsp.pos) != filepos)
|
|
continue;
|
|
request.fsp.key = reply->fsp.key;
|
|
retry=0;
|
|
i=ntohs(reply->fsp.len);
|
|
if(i == 1)
|
|
{
|
|
request.fsp.cmd=CC_BYE;
|
|
request.data[0]=reply->data[0];
|
|
continue;
|
|
}
|
|
/* let last byte alone */
|
|
if(i >= filelength)
|
|
i = filelength - 1;
|
|
if(!info->fnc(reply->data,block++,i,0))
|
|
return 0;
|
|
filepos += i;
|
|
filelength -= i;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int url_fsp(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
|
|
{
|
|
struct fsp_info info;
|
|
/* Set the defaults */
|
|
info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
|
|
info.server_port = FSP_PORT;
|
|
info.local_port = 1024 + random() & 0xfbff;
|
|
info.fnc = fnc;
|
|
|
|
/* Now parse the url */
|
|
/* printf("fsp-URI: [%s]\n", name); */
|
|
/* quick hack for now */
|
|
info.filename=name;
|
|
return proto_fsp(&info);
|
|
}
|
|
#endif
|