/*********************************************************************\ * 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 #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> 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;iudp.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