| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |     /*********************************************************************\
    * 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
 |