mirror of
https://github.com/ipxe/ipxe
synced 2026-04-04 03:00:20 +03:00
Moved protocols to proto/
This commit is contained in:
610
src/core/nfs.c
610
src/core/nfs.c
@@ -1,610 +0,0 @@
|
||||
#ifdef DOWNLOAD_PROTO_NFS
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
|
||||
/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
|
||||
* large portions are copied verbatim) as distributed in OSKit 0.97. A few
|
||||
* changes were necessary to adapt the code to Etherboot and to fix several
|
||||
* inconsistencies. Also the RPC message preparation is done "by hand" to
|
||||
* avoid adding netsprintf() which I find hard to understand and use. */
|
||||
|
||||
/* NOTE 2: Etherboot does not care about things beyond the kernel image, so
|
||||
* it loads the kernel image off the boot server (ARP_SERVER) and does not
|
||||
* access the client root disk (root-path in dhcpd.conf), which would use
|
||||
* ARP_ROOTSERVER. The root disk is something the operating system we are
|
||||
* about to load needs to use. This is different from the OSKit 0.97 logic. */
|
||||
|
||||
/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
|
||||
* If a symlink is encountered, it is followed as far as possible (recursion
|
||||
* possible, maximum 16 steps). There is no clearing of ".."'s inside the
|
||||
* path, so please DON'T DO THAT. thx. */
|
||||
|
||||
#define START_OPORT 700 /* mountd usually insists on secure ports */
|
||||
#define OPORT_SWEEP 200 /* make sure we don't leave secure range */
|
||||
|
||||
static int oport = START_OPORT;
|
||||
static int mount_port = -1;
|
||||
static int nfs_port = -1;
|
||||
static int fs_mounted = 0;
|
||||
static unsigned long rpc_id;
|
||||
|
||||
/**************************************************************************
|
||||
RPC_INIT - set up the ID counter to something fairly random
|
||||
**************************************************************************/
|
||||
void rpc_init(void)
|
||||
{
|
||||
unsigned long t;
|
||||
|
||||
t = currticks();
|
||||
rpc_id = t ^ (t << 8) ^ (t << 16);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
RPC_PRINTERROR - Print a low level RPC error message
|
||||
**************************************************************************/
|
||||
static void rpc_printerror(struct rpc_t *rpc)
|
||||
{
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus) {
|
||||
/* rpc_printerror() is called for any RPC related error,
|
||||
* suppress output if no low level RPC error happened. */
|
||||
printf("RPC error: (%d,%d,%d)\n", ntohl(rpc->u.reply.rstatus),
|
||||
ntohl(rpc->u.reply.verifier),
|
||||
ntohl(rpc->u.reply.astatus));
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
AWAIT_RPC - Wait for an rpc packet
|
||||
**************************************************************************/
|
||||
static int await_rpc(int ival, void *ptr,
|
||||
unsigned short ptype, struct iphdr *ip, struct udphdr *udp)
|
||||
{
|
||||
struct rpc_t *rpc;
|
||||
if (!udp)
|
||||
return 0;
|
||||
if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
|
||||
return 0;
|
||||
if (ntohs(udp->dest) != ival)
|
||||
return 0;
|
||||
if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + 8)
|
||||
return 0;
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (*(unsigned long *)ptr != ntohl(rpc->u.reply.id))
|
||||
return 0;
|
||||
if (MSG_REPLY != ntohl(rpc->u.reply.type))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
RPC_LOOKUP - Lookup RPC Port numbers
|
||||
**************************************************************************/
|
||||
static int rpc_lookup(int addr, int prog, int ver, int sport)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
int retries;
|
||||
long *p;
|
||||
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_PORTMAP);
|
||||
buf.u.call.vers = htonl(2); /* portmapper is version 2 */
|
||||
buf.u.call.proc = htonl(PORTMAP_GETPORT);
|
||||
p = (long *)buf.u.call.data;
|
||||
*p++ = 0; *p++ = 0; /* auth credential */
|
||||
*p++ = 0; *p++ = 0; /* auth verifier */
|
||||
*p++ = htonl(prog);
|
||||
*p++ = htonl(ver);
|
||||
*p++ = htonl(IP_UDP);
|
||||
*p++ = 0;
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout;
|
||||
udp_transmit(arptable[addr].ipaddr.s_addr, sport, SUNRPC_PORT,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
if (await_reply(await_rpc, sport, &id, timeout)) {
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus) {
|
||||
rpc_printerror(rpc);
|
||||
return -1;
|
||||
} else {
|
||||
return ntohl(rpc->u.reply.data[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
|
||||
**************************************************************************/
|
||||
static long *rpc_add_credentials(long *p)
|
||||
{
|
||||
int hl;
|
||||
|
||||
/* Here's the executive summary on authentication requirements of the
|
||||
* various NFS server implementations: Linux accepts both AUTH_NONE
|
||||
* and AUTH_UNIX authentication (also accepts an empty hostname field
|
||||
* in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
|
||||
* AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
|
||||
* scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
|
||||
* it (if the BOOTP/DHCP reply didn't give one, just use an empty
|
||||
* hostname). */
|
||||
|
||||
hl = (hostnamelen + 3) & ~3;
|
||||
|
||||
/* Provide an AUTH_UNIX credential. */
|
||||
*p++ = htonl(1); /* AUTH_UNIX */
|
||||
*p++ = htonl(hl+20); /* auth length */
|
||||
*p++ = htonl(0); /* stamp */
|
||||
*p++ = htonl(hostnamelen); /* hostname string */
|
||||
if (hostnamelen & 3) {
|
||||
*(p + hostnamelen / 4) = 0; /* add zero padding */
|
||||
}
|
||||
memcpy(p, hostname, hostnamelen);
|
||||
p += hl / 4;
|
||||
*p++ = 0; /* uid */
|
||||
*p++ = 0; /* gid */
|
||||
*p++ = 0; /* auxiliary gid list */
|
||||
|
||||
/* Provide an AUTH_NONE verifier. */
|
||||
*p++ = 0; /* AUTH_NONE */
|
||||
*p++ = 0; /* auth length */
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_PRINTERROR - Print a NFS error message
|
||||
**************************************************************************/
|
||||
static void nfs_printerror(int err)
|
||||
{
|
||||
switch (-err) {
|
||||
case NFSERR_PERM:
|
||||
printf("Not owner\n");
|
||||
break;
|
||||
case NFSERR_NOENT:
|
||||
printf("No such file or directory\n");
|
||||
break;
|
||||
case NFSERR_ACCES:
|
||||
printf("Permission denied\n");
|
||||
break;
|
||||
case NFSERR_ISDIR:
|
||||
printf("Directory given where filename expected\n");
|
||||
break;
|
||||
case NFSERR_INVAL:
|
||||
printf("Invalid filehandle\n");
|
||||
break; // INVAL is not defined in NFSv2, some NFS-servers
|
||||
// seem to use it in answers to v2 nevertheless.
|
||||
case 9998:
|
||||
printf("low-level RPC failure (parameter decoding problem?)\n");
|
||||
break;
|
||||
case 9999:
|
||||
printf("low-level RPC failure (authentication problem?)\n");
|
||||
break;
|
||||
default:
|
||||
printf("Unknown NFS error %d\n", -err);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_MOUNT - Mount an NFS Filesystem
|
||||
**************************************************************************/
|
||||
static int nfs_mount(int server, int port, char *path, char *fh, int sport)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
int retries;
|
||||
long *p;
|
||||
int pathlen = strlen(path);
|
||||
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_MOUNT);
|
||||
buf.u.call.vers = htonl(1); /* mountd is version 1 */
|
||||
buf.u.call.proc = htonl(MOUNT_ADDENTRY);
|
||||
p = rpc_add_credentials((long *)buf.u.call.data);
|
||||
*p++ = htonl(pathlen);
|
||||
if (pathlen & 3) {
|
||||
*(p + pathlen / 4) = 0; /* add zero padding */
|
||||
}
|
||||
memcpy(p, path, pathlen);
|
||||
p += (pathlen + 3) / 4;
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout;
|
||||
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
if (await_reply(await_rpc, sport, &id, timeout)) {
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
|
||||
rpc_printerror(rpc);
|
||||
if (rpc->u.reply.rstatus) {
|
||||
/* RPC failed, no verifier, data[0] */
|
||||
return -9999;
|
||||
}
|
||||
if (rpc->u.reply.astatus) {
|
||||
/* RPC couldn't decode parameters */
|
||||
return -9998;
|
||||
}
|
||||
return -ntohl(rpc->u.reply.data[0]);
|
||||
} else {
|
||||
fs_mounted = 1;
|
||||
memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
|
||||
**************************************************************************/
|
||||
void nfs_umountall(int server)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
int retries;
|
||||
long *p;
|
||||
|
||||
if (!arptable[server].ipaddr.s_addr) {
|
||||
/* Haven't sent a single UDP packet to this server */
|
||||
return;
|
||||
}
|
||||
if ((mount_port == -1) || (!fs_mounted)) {
|
||||
/* Nothing mounted, nothing to umount */
|
||||
return;
|
||||
}
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_MOUNT);
|
||||
buf.u.call.vers = htonl(1); /* mountd is version 1 */
|
||||
buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
|
||||
p = rpc_add_credentials((long *)buf.u.call.data);
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
udp_transmit(arptable[server].ipaddr.s_addr, oport, mount_port,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
if (await_reply(await_rpc, oport, &id, timeout)) {
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus) {
|
||||
rpc_printerror(rpc);
|
||||
}
|
||||
fs_mounted = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***************************************************************************
|
||||
* NFS_READLINK (AH 2003-07-14)
|
||||
* This procedure is called when read of the first block fails -
|
||||
* this probably happens when it's a directory or a symlink
|
||||
* In case of successful readlink(), the dirname is manipulated,
|
||||
* so that inside the nfs() function a recursion can be done.
|
||||
**************************************************************************/
|
||||
static int nfs_readlink(int server, int port, char *fh, char *path, char *nfh,
|
||||
int sport)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
long *p;
|
||||
int retries;
|
||||
int pathlen = strlen(path);
|
||||
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_NFS);
|
||||
buf.u.call.vers = htonl(2); /* nfsd is version 2 */
|
||||
buf.u.call.proc = htonl(NFS_READLINK);
|
||||
p = rpc_add_credentials((long *)buf.u.call.data);
|
||||
memcpy(p, nfh, NFS_FHSIZE);
|
||||
p += (NFS_FHSIZE / 4);
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
if (await_reply(await_rpc, sport, &id, timeout)) {
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
|
||||
rpc_printerror(rpc);
|
||||
if (rpc->u.reply.rstatus) {
|
||||
/* RPC failed, no verifier, data[0] */
|
||||
return -9999;
|
||||
}
|
||||
if (rpc->u.reply.astatus) {
|
||||
/* RPC couldn't decode parameters */
|
||||
return -9998;
|
||||
}
|
||||
return -ntohl(rpc->u.reply.data[0]);
|
||||
} else {
|
||||
// It *is* a link.
|
||||
// If it's a relative link, append everything to dirname, filename TOO!
|
||||
retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
|
||||
if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
|
||||
path[pathlen++] = '/';
|
||||
while ( ( retries + pathlen ) > 298 ) {
|
||||
retries--;
|
||||
}
|
||||
if ( retries > 0 ) {
|
||||
memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
|
||||
} else { retries = 0; }
|
||||
path[pathlen + retries] = 0;
|
||||
} else {
|
||||
// Else make it the only path.
|
||||
if ( retries > 298 ) { retries = 298; }
|
||||
memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
|
||||
path[retries] = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/**************************************************************************
|
||||
NFS_LOOKUP - Lookup Pathname
|
||||
**************************************************************************/
|
||||
static int nfs_lookup(int server, int port, char *fh, char *path, char *nfh,
|
||||
int sport)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
long *p;
|
||||
int retries;
|
||||
int pathlen = strlen(path);
|
||||
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_NFS);
|
||||
buf.u.call.vers = htonl(2); /* nfsd is version 2 */
|
||||
buf.u.call.proc = htonl(NFS_LOOKUP);
|
||||
p = rpc_add_credentials((long *)buf.u.call.data);
|
||||
memcpy(p, fh, NFS_FHSIZE);
|
||||
p += (NFS_FHSIZE / 4);
|
||||
*p++ = htonl(pathlen);
|
||||
if (pathlen & 3) {
|
||||
*(p + pathlen / 4) = 0; /* add zero padding */
|
||||
}
|
||||
memcpy(p, path, pathlen);
|
||||
p += (pathlen + 3) / 4;
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
if (await_reply(await_rpc, sport, &id, timeout)) {
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
|
||||
rpc_printerror(rpc);
|
||||
if (rpc->u.reply.rstatus) {
|
||||
/* RPC failed, no verifier, data[0] */
|
||||
return -9999;
|
||||
}
|
||||
if (rpc->u.reply.astatus) {
|
||||
/* RPC couldn't decode parameters */
|
||||
return -9998;
|
||||
}
|
||||
return -ntohl(rpc->u.reply.data[0]);
|
||||
} else {
|
||||
memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_READ - Read File on NFS Server
|
||||
**************************************************************************/
|
||||
static int nfs_read(int server, int port, char *fh, int offset, int len,
|
||||
int sport)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
int retries;
|
||||
long *p;
|
||||
|
||||
static int tokens=0;
|
||||
/*
|
||||
* Try to implement something similar to a window protocol in
|
||||
* terms of response to losses. On successful receive, increment
|
||||
* the number of tokens by 1 (cap at 256). On failure, halve it.
|
||||
* When the number of tokens is >= 2, use a very short timeout.
|
||||
*/
|
||||
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_NFS);
|
||||
buf.u.call.vers = htonl(2); /* nfsd is version 2 */
|
||||
buf.u.call.proc = htonl(NFS_READ);
|
||||
p = rpc_add_credentials((long *)buf.u.call.data);
|
||||
memcpy(p, fh, NFS_FHSIZE);
|
||||
p += NFS_FHSIZE / 4;
|
||||
*p++ = htonl(offset);
|
||||
*p++ = htonl(len);
|
||||
*p++ = 0; /* unused parameter */
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
if (tokens >= 2)
|
||||
timeout = TICKS_PER_SEC/2;
|
||||
|
||||
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
if (await_reply(await_rpc, sport, &id, timeout)) {
|
||||
if (tokens < 256)
|
||||
tokens++;
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
|
||||
rpc_printerror(rpc);
|
||||
if (rpc->u.reply.rstatus) {
|
||||
/* RPC failed, no verifier, data[0] */
|
||||
return -9999;
|
||||
}
|
||||
if (rpc->u.reply.astatus) {
|
||||
/* RPC couldn't decode parameters */
|
||||
return -9998;
|
||||
}
|
||||
return -ntohl(rpc->u.reply.data[0]);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
tokens >>= 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS - Download extended BOOTP data, or kernel image from NFS server
|
||||
**************************************************************************/
|
||||
int nfs(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
|
||||
{
|
||||
static int recursion = 0;
|
||||
int sport;
|
||||
int err, namelen = strlen(name);
|
||||
char dirname[300], *fname;
|
||||
char dirfh[NFS_FHSIZE]; /* file handle of directory */
|
||||
char filefh[NFS_FHSIZE]; /* file handle of kernel image */
|
||||
unsigned int block;
|
||||
int rlen, size, offs, len;
|
||||
struct rpc_t *rpc;
|
||||
|
||||
rx_qdrain();
|
||||
|
||||
sport = oport++;
|
||||
if (oport > START_OPORT+OPORT_SWEEP) {
|
||||
oport = START_OPORT;
|
||||
}
|
||||
if ( name != dirname ) {
|
||||
memcpy(dirname, name, namelen + 1);
|
||||
}
|
||||
recursion = 0;
|
||||
nfssymlink:
|
||||
if ( recursion > NFS_MAXLINKDEPTH ) {
|
||||
printf ( "\nRecursion: More than %d symlinks followed. Abort.\n", NFS_MAXLINKDEPTH );
|
||||
return 0;
|
||||
}
|
||||
recursion++;
|
||||
fname = dirname + (namelen - 1);
|
||||
while (fname >= dirname) {
|
||||
if (*fname == '/') {
|
||||
*fname = '\0';
|
||||
fname++;
|
||||
break;
|
||||
}
|
||||
fname--;
|
||||
}
|
||||
if (fname < dirname) {
|
||||
printf("can't parse file name %s\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mount_port == -1) {
|
||||
mount_port = rpc_lookup(ARP_SERVER, PROG_MOUNT, 1, sport);
|
||||
}
|
||||
if (nfs_port == -1) {
|
||||
nfs_port = rpc_lookup(ARP_SERVER, PROG_NFS, 2, sport);
|
||||
}
|
||||
if (nfs_port == -1 || mount_port == -1) {
|
||||
printf("can't get nfs/mount ports from portmapper\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
err = nfs_mount(ARP_SERVER, mount_port, dirname, dirfh, sport);
|
||||
if (err) {
|
||||
printf("mounting %s: ", dirname);
|
||||
nfs_printerror(err);
|
||||
/* just to be sure... */
|
||||
nfs_umountall(ARP_SERVER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = nfs_lookup(ARP_SERVER, nfs_port, dirfh, fname, filefh, sport);
|
||||
if (err) {
|
||||
printf("looking up %s: ", fname);
|
||||
nfs_printerror(err);
|
||||
nfs_umountall(ARP_SERVER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
offs = 0;
|
||||
block = 1; /* blocks are numbered starting from 1 */
|
||||
size = -1; /* will be set properly with the first reply */
|
||||
len = NFS_READ_SIZE; /* first request is always full size */
|
||||
do {
|
||||
err = nfs_read(ARP_SERVER, nfs_port, filefh, offs, len, sport);
|
||||
if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
|
||||
// An error occured. NFS servers tend to sending
|
||||
// errors 21 / 22 when symlink instead of real file
|
||||
// is requested. So check if it's a symlink!
|
||||
block = nfs_readlink(ARP_SERVER, nfs_port, dirfh, dirname,
|
||||
filefh, sport);
|
||||
if ( 0 == block ) {
|
||||
printf("\nLoading symlink:%s ..",dirname);
|
||||
goto nfssymlink;
|
||||
}
|
||||
nfs_printerror(err);
|
||||
nfs_umountall(ARP_SERVER);
|
||||
return 0;
|
||||
}
|
||||
if (err) {
|
||||
printf("reading at offset %d: ", offs);
|
||||
nfs_printerror(err);
|
||||
nfs_umountall(ARP_SERVER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
|
||||
/* size must be found out early to allow EOF detection */
|
||||
if (size == -1) {
|
||||
size = ntohl(rpc->u.reply.data[6]);
|
||||
}
|
||||
rlen = ntohl(rpc->u.reply.data[18]);
|
||||
if (rlen > len) {
|
||||
rlen = len; /* shouldn't happen... */
|
||||
}
|
||||
|
||||
err = fnc((char *)&rpc->u.reply.data[19], block, rlen,
|
||||
(offs+rlen == size));
|
||||
if (err <= 0) {
|
||||
nfs_umountall(ARP_SERVER);
|
||||
return err;
|
||||
}
|
||||
|
||||
block++;
|
||||
offs += rlen;
|
||||
/* last request is done with matching requested read size */
|
||||
if (size-offs < NFS_READ_SIZE) {
|
||||
len = size-offs;
|
||||
}
|
||||
} while (len != 0);
|
||||
/* len == 0 means that all the file has been read */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* DOWNLOAD_PROTO_NFS */
|
||||
@@ -1,206 +0,0 @@
|
||||
#include "etherboot.h"
|
||||
#include "http.h"
|
||||
|
||||
#ifdef DOWNLOAD_PROTO_HTTP
|
||||
|
||||
/* The block size is currently chosen to be 512 bytes. This means, we can
|
||||
allocate the receive buffer on the stack, but it results in a noticeable
|
||||
performance penalty.
|
||||
This is what needs to be done in order to increase the block size:
|
||||
- size negotiation needs to be implemented in TCP
|
||||
- the buffer needs to be allocated on the heap
|
||||
- path MTU discovery needs to be implemented
|
||||
*/ /***/ /* FIXME */
|
||||
#define BLOCKSIZE TFTP_DEFAULTSIZE_PACKET
|
||||
|
||||
/**************************************************************************
|
||||
SEND_TCP_CALLBACK - Send data using TCP
|
||||
**************************************************************************/
|
||||
struct send_recv_state {
|
||||
int (*fnc)(unsigned char *data, int block, int len, int eof);
|
||||
char *send_buffer;
|
||||
char *recv_buffer;
|
||||
int send_length;
|
||||
int recv_length;
|
||||
int bytes_sent;
|
||||
int block;
|
||||
int bytes_received;
|
||||
enum { RESULT_CODE, HEADER, DATA, ERROR, MOVED } recv_state;
|
||||
int rc;
|
||||
char location[MAX_URL+1];
|
||||
};
|
||||
|
||||
static int send_tcp_request(int length, void *buffer, void *ptr) {
|
||||
struct send_recv_state *state = (struct send_recv_state *)ptr;
|
||||
|
||||
if (length > state->send_length - state->bytes_sent)
|
||||
length = state->send_length - state->bytes_sent;
|
||||
memcpy(buffer, state->send_buffer + state->bytes_sent, length);
|
||||
state->bytes_sent += length;
|
||||
return (length);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
RECV_TCP_CALLBACK - Receive data using TCP
|
||||
**************************************************************************/
|
||||
static int recv_tcp_request(int length, const void *buffer, void *ptr) {
|
||||
struct send_recv_state *state = (struct send_recv_state *)ptr;
|
||||
|
||||
/* Assume that the lines in an HTTP header do not straddle a packet */
|
||||
/* boundary. This is probably a reasonable assumption */
|
||||
if (state->recv_state == RESULT_CODE) {
|
||||
while (length > 0) {
|
||||
/* Find HTTP result code */
|
||||
if (*(const char *)buffer == ' ') {
|
||||
const char *ptr = ((const char *)buffer) + 1;
|
||||
int rc = strtoul(ptr, &ptr, 10);
|
||||
if (ptr >= (const char *)buffer + length) {
|
||||
state->recv_state = ERROR;
|
||||
return 0;
|
||||
}
|
||||
state->rc = rc;
|
||||
state->recv_state = HEADER;
|
||||
goto header;
|
||||
}
|
||||
++(const char *)buffer;
|
||||
length--;
|
||||
}
|
||||
state->recv_state = ERROR;
|
||||
return 0;
|
||||
}
|
||||
if (state->recv_state == HEADER) {
|
||||
header: while (length > 0) {
|
||||
/* Check for HTTP redirect */
|
||||
if (state->rc >= 300 && state->rc < 400 &&
|
||||
!memcmp(buffer, "Location: ", 10)) {
|
||||
char *ptr = state->location;
|
||||
int i;
|
||||
memcpy(ptr, buffer + 10, MAX_URL);
|
||||
for (i = 0; i < MAX_URL && *ptr > ' ';
|
||||
i++, ptr++);
|
||||
*ptr = '\000';
|
||||
state->recv_state = MOVED;
|
||||
return 1;
|
||||
}
|
||||
/* Find beginning of line */
|
||||
while (length > 0) {
|
||||
length--;
|
||||
if (*((const char *)buffer)++ == '\n')
|
||||
break;
|
||||
}
|
||||
/* Check for end of header */
|
||||
if (length >= 2 && !memcmp(buffer, "\r\n", 2)) {
|
||||
state->recv_state = DATA;
|
||||
buffer += 2;
|
||||
length -= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state->recv_state == DATA) {
|
||||
state->bytes_received += length;
|
||||
while (length > 0) {
|
||||
int copy_length = BLOCKSIZE - state->recv_length;
|
||||
if (copy_length > length)
|
||||
copy_length = length;
|
||||
memcpy(state->recv_buffer + state->recv_length,
|
||||
buffer, copy_length);
|
||||
if ((state->recv_length += copy_length) == BLOCKSIZE) {
|
||||
if (!state->fnc(state->recv_buffer,
|
||||
++state->block, BLOCKSIZE, 0))
|
||||
return 0;
|
||||
state->recv_length = 0;
|
||||
}
|
||||
length -= copy_length;
|
||||
buffer += copy_length;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
HTTP_GET - Get data using HTTP
|
||||
**************************************************************************/
|
||||
int http(const char *url,
|
||||
int (*fnc)(unsigned char *, unsigned int, unsigned int, int)) {
|
||||
static const char GET[] = "GET /%s HTTP/1.0\r\n\r\n";
|
||||
static char recv_buffer[BLOCKSIZE];
|
||||
in_addr destip;
|
||||
int port;
|
||||
int length;
|
||||
struct send_recv_state state;
|
||||
|
||||
state.fnc = fnc;
|
||||
state.rc = -1;
|
||||
state.block = 0;
|
||||
state.recv_buffer = recv_buffer;
|
||||
length = strlen(url);
|
||||
if (length <= MAX_URL) {
|
||||
memcpy(state.location, url, length+1);
|
||||
destip = arptable[ARP_SERVER].ipaddr;
|
||||
port = url_port;
|
||||
if (port == -1)
|
||||
port = 80;
|
||||
goto first_time;
|
||||
|
||||
do {
|
||||
state.rc = -1;
|
||||
state.block = 0;
|
||||
url = state.location;
|
||||
if (memcmp("http://", url, 7))
|
||||
break;
|
||||
url += 7;
|
||||
length = inet_aton(url, &destip);
|
||||
if (!length) {
|
||||
/* As we do not have support for DNS, assume*/
|
||||
/* that HTTP redirects always point to the */
|
||||
/* same machine */
|
||||
if (state.recv_state == MOVED) {
|
||||
while (*url &&
|
||||
*url != ':' && *url != '/') url++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*(url += length) == ':') {
|
||||
port = strtoul(url, &url, 10);
|
||||
} else {
|
||||
port = 80;
|
||||
}
|
||||
if (!*url)
|
||||
url = "/";
|
||||
if (*url != '/')
|
||||
break;
|
||||
url++;
|
||||
|
||||
first_time:
|
||||
length = strlen(url);
|
||||
state.send_length = sizeof(GET) - 3 + length;
|
||||
|
||||
{ char buf[state.send_length + 1];
|
||||
sprintf(state.send_buffer = buf, GET, url);
|
||||
state.bytes_sent = 0;
|
||||
|
||||
state.bytes_received = 0;
|
||||
state.recv_state = RESULT_CODE;
|
||||
|
||||
state.recv_length = 0;
|
||||
tcp_transaction(destip.s_addr, 80, &state,
|
||||
send_tcp_request, recv_tcp_request);
|
||||
}
|
||||
} while (state.recv_state == MOVED);
|
||||
} else {
|
||||
memcpy(state.location, url, MAX_URL);
|
||||
state.location[MAX_URL] = '\000';
|
||||
}
|
||||
|
||||
if (state.rc == 200) {
|
||||
return fnc(recv_buffer, ++state.block, state.recv_length, 1);
|
||||
} else {
|
||||
printf("Failed to download %s (rc = %d)\n",
|
||||
state.location, state.rc);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DOWNLOAD_PROTO_HTTP */
|
||||
@@ -1,541 +0,0 @@
|
||||
#ifdef DOWNLOAD_PROTO_SLAM
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
|
||||
#define SLAM_PORT 10000
|
||||
#define SLAM_MULTICAST_IP ((239<<24)|(255<<16)|(1<<8)|(1<<0))
|
||||
#define SLAM_MULTICAST_PORT 10000
|
||||
#define SLAM_LOCAL_PORT 10000
|
||||
|
||||
/* Set the timeout intervals to at least 1 second so
|
||||
* on a 100Mbit ethernet can receive 10000 packets
|
||||
* in one second.
|
||||
*
|
||||
* The only case that is likely to trigger all of the nodes
|
||||
* firing a nack packet is a slow server. The odds of this
|
||||
* happening could be reduced being slightly smarter and utilizing
|
||||
* the multicast channels for nacks. But that only improves the odds
|
||||
* it doesn't improve the worst case. So unless this proves to be
|
||||
* a common case having the control data going unicast should increase
|
||||
* the odds of the data not being dropped.
|
||||
*
|
||||
* When doing exponential backoff we increase just the timeout
|
||||
* interval and not the base to optimize for throughput. This is only
|
||||
* expected to happen when the server is down. So having some nodes
|
||||
* pinging immediately should get the transmission restarted quickly after a
|
||||
* server restart. The host nic won't be to baddly swamped because of
|
||||
* the random distribution of the nodes.
|
||||
*
|
||||
*/
|
||||
#define SLAM_INITIAL_MIN_TIMEOUT (TICKS_PER_SEC/3)
|
||||
#define SLAM_INITIAL_TIMEOUT_INTERVAL (TICKS_PER_SEC)
|
||||
#define SLAM_BASE_MIN_TIMEOUT (2*TICKS_PER_SEC)
|
||||
#define SLAM_BASE_TIMEOUT_INTERVAL (4*TICKS_PER_SEC)
|
||||
#define SLAM_BACKOFF_LIMIT 5
|
||||
#define SLAM_MAX_RETRIES 20
|
||||
|
||||
/*** Packets Formats ***
|
||||
* Data Packet:
|
||||
* transaction
|
||||
* total bytes
|
||||
* block size
|
||||
* packet #
|
||||
* data
|
||||
*
|
||||
* Status Request Packet
|
||||
* transaction
|
||||
* total bytes
|
||||
* block size
|
||||
*
|
||||
* Status Packet
|
||||
* received packets
|
||||
* requested packets
|
||||
* received packets
|
||||
* requested packets
|
||||
* ...
|
||||
* received packets
|
||||
* requested packtes
|
||||
* 0
|
||||
*/
|
||||
|
||||
#define MAX_HDR (7 + 7 + 7) /* transaction, total size, block size */
|
||||
#define MIN_HDR (1 + 1 + 1) /* transactino, total size, block size */
|
||||
|
||||
#define MAX_SLAM_REQUEST MAX_HDR
|
||||
#define MIN_SLAM_REQUEST MIN_HDR
|
||||
|
||||
#define MIN_SLAM_DATA (MIN_HDR + 1)
|
||||
|
||||
static struct slam_nack {
|
||||
struct iphdr ip;
|
||||
struct udphdr udp;
|
||||
unsigned char data[ETH_MAX_MTU -
|
||||
(sizeof(struct iphdr) + sizeof(struct udphdr))];
|
||||
} nack;
|
||||
|
||||
struct slam_state {
|
||||
unsigned char hdr[MAX_HDR];
|
||||
unsigned long hdr_len;
|
||||
unsigned long block_size;
|
||||
unsigned long total_bytes;
|
||||
unsigned long total_packets;
|
||||
|
||||
unsigned long received_packets;
|
||||
|
||||
unsigned char *image;
|
||||
unsigned char *bitmap;
|
||||
} state;
|
||||
|
||||
|
||||
static void init_slam_state(void)
|
||||
{
|
||||
state.hdr_len = sizeof(state.hdr);
|
||||
memset(state.hdr, 0, state.hdr_len);
|
||||
state.block_size = 0;
|
||||
state.total_packets = 0;
|
||||
|
||||
state.received_packets = 0;
|
||||
|
||||
state.image = 0;
|
||||
state.bitmap = 0;
|
||||
}
|
||||
|
||||
struct slam_info {
|
||||
in_addr server_ip;
|
||||
in_addr multicast_ip;
|
||||
in_addr local_ip;
|
||||
uint16_t server_port;
|
||||
uint16_t multicast_port;
|
||||
uint16_t local_port;
|
||||
int (*fnc)(unsigned char *, unsigned int, unsigned int, int);
|
||||
int sent_nack;
|
||||
};
|
||||
|
||||
#define SLAM_TIMEOUT 0
|
||||
#define SLAM_REQUEST 1
|
||||
#define SLAM_DATA 2
|
||||
static int await_slam(int ival __unused, void *ptr,
|
||||
unsigned short ptype __unused, struct iphdr *ip, struct udphdr *udp)
|
||||
{
|
||||
struct slam_info *info = ptr;
|
||||
if (!udp) {
|
||||
return 0;
|
||||
}
|
||||
/* I can receive two kinds of packets here, a multicast data packet,
|
||||
* or a unicast request for information
|
||||
*/
|
||||
/* Check for a data request packet */
|
||||
if ((ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) &&
|
||||
(ntohs(udp->dest) == info->local_port) &&
|
||||
(nic.packetlen >=
|
||||
ETH_HLEN +
|
||||
sizeof(struct iphdr) +
|
||||
sizeof(struct udphdr) +
|
||||
MIN_SLAM_REQUEST)) {
|
||||
return SLAM_REQUEST;
|
||||
}
|
||||
/* Check for a multicast data packet */
|
||||
if ((ip->dest.s_addr == info->multicast_ip.s_addr) &&
|
||||
(ntohs(udp->dest) == info->multicast_port) &&
|
||||
(nic.packetlen >=
|
||||
ETH_HLEN +
|
||||
sizeof(struct iphdr) +
|
||||
sizeof(struct udphdr) +
|
||||
MIN_SLAM_DATA)) {
|
||||
return SLAM_DATA;
|
||||
}
|
||||
#if 0
|
||||
printf("#");
|
||||
printf("dest: %@ port: %d len: %d\n",
|
||||
ip->dest.s_addr, ntohs(udp->dest), nic.packetlen);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int slam_encode(
|
||||
unsigned char **ptr, unsigned char *end, unsigned long value)
|
||||
{
|
||||
unsigned char *data = *ptr;
|
||||
int bytes;
|
||||
bytes = sizeof(value);
|
||||
while ((bytes > 0) && ((0xff & (value >> ((bytes -1)<<3))) == 0)) {
|
||||
bytes--;
|
||||
}
|
||||
if (bytes <= 0) {
|
||||
bytes = 1;
|
||||
}
|
||||
if (data + bytes >= end) {
|
||||
return -1;
|
||||
}
|
||||
if ((0xe0 & (value >> ((bytes -1)<<3))) == 0) {
|
||||
/* packed together */
|
||||
*data = (bytes << 5) | (value >> ((bytes -1)<<3));
|
||||
} else {
|
||||
bytes++;
|
||||
*data = (bytes << 5);
|
||||
}
|
||||
bytes--;
|
||||
data++;
|
||||
while(bytes) {
|
||||
*(data++) = 0xff & (value >> ((bytes -1)<<3));
|
||||
bytes--;
|
||||
}
|
||||
*ptr = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int slam_skip(unsigned char **ptr, unsigned char *end)
|
||||
{
|
||||
int bytes;
|
||||
if (*ptr >= end) {
|
||||
return -1;
|
||||
}
|
||||
bytes = ((**ptr) >> 5) & 7;
|
||||
if (bytes == 0) {
|
||||
return -1;
|
||||
}
|
||||
if (*ptr + bytes >= end) {
|
||||
return -1;
|
||||
}
|
||||
(*ptr) += bytes;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static unsigned long slam_decode(unsigned char **ptr, unsigned char *end, int *err)
|
||||
{
|
||||
unsigned long value;
|
||||
unsigned bytes;
|
||||
if (*ptr >= end) {
|
||||
*err = -1;
|
||||
}
|
||||
bytes = ((**ptr) >> 5) & 7;
|
||||
if ((bytes == 0) || (bytes > sizeof(unsigned long))) {
|
||||
*err = -1;
|
||||
return 0;
|
||||
}
|
||||
if ((*ptr) + bytes >= end) {
|
||||
*err = -1;
|
||||
}
|
||||
value = (**ptr) & 0x1f;
|
||||
bytes--;
|
||||
(*ptr)++;
|
||||
while(bytes) {
|
||||
value <<= 8;
|
||||
value |= **ptr;
|
||||
(*ptr)++;
|
||||
bytes--;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
static long slam_sleep_interval(int exp)
|
||||
{
|
||||
long range;
|
||||
long divisor;
|
||||
long interval;
|
||||
range = SLAM_BASE_TIMEOUT_INTERVAL;
|
||||
if (exp < 0) {
|
||||
divisor = RAND_MAX/SLAM_INITIAL_TIMEOUT_INTERVAL;
|
||||
} else {
|
||||
if (exp > SLAM_BACKOFF_LIMIT)
|
||||
exp = SLAM_BACKOFF_LIMIT;
|
||||
divisor = RAND_MAX/(range << exp);
|
||||
}
|
||||
interval = random()/divisor;
|
||||
if (exp < 0) {
|
||||
interval += SLAM_INITIAL_MIN_TIMEOUT;
|
||||
} else {
|
||||
interval += SLAM_BASE_MIN_TIMEOUT;
|
||||
}
|
||||
return interval;
|
||||
}
|
||||
|
||||
|
||||
static unsigned char *reinit_slam_state(
|
||||
unsigned char *header, unsigned char *end)
|
||||
{
|
||||
unsigned long total_bytes;
|
||||
unsigned long block_size;
|
||||
|
||||
unsigned long bitmap_len;
|
||||
unsigned long max_packet_len;
|
||||
unsigned char *data;
|
||||
int err;
|
||||
|
||||
#if 0
|
||||
printf("reinit\n");
|
||||
#endif
|
||||
data = header;
|
||||
|
||||
state.hdr_len = 0;
|
||||
err = slam_skip(&data, end); /* transaction id */
|
||||
total_bytes = slam_decode(&data, end, &err);
|
||||
block_size = slam_decode(&data, end, &err);
|
||||
if (err) {
|
||||
printf("ALERT: slam size out of range\n");
|
||||
return 0;
|
||||
}
|
||||
state.block_size = block_size;
|
||||
state.total_bytes = total_bytes;
|
||||
state.total_packets = (total_bytes + block_size - 1)/block_size;
|
||||
state.hdr_len = data - header;
|
||||
state.received_packets = 0;
|
||||
|
||||
data = state.hdr;
|
||||
slam_encode(&data, &state.hdr[sizeof(state.hdr)], state.total_packets);
|
||||
max_packet_len = data - state.hdr;
|
||||
memcpy(state.hdr, header, state.hdr_len);
|
||||
|
||||
#if 0
|
||||
printf("block_size: %ld\n", block_size);
|
||||
printf("total_bytes: %ld\n", total_bytes);
|
||||
printf("total_packets: %ld\n", state.total_packets);
|
||||
printf("hdr_len: %ld\n", state.hdr_len);
|
||||
printf("max_packet_len: %ld\n", max_packet_len);
|
||||
#endif
|
||||
|
||||
if (state.block_size > ETH_MAX_MTU - (
|
||||
sizeof(struct iphdr) + sizeof(struct udphdr) +
|
||||
state.hdr_len + max_packet_len)) {
|
||||
printf("ALERT: slam blocksize to large\n");
|
||||
return 0;
|
||||
}
|
||||
if (state.bitmap) {
|
||||
forget(state.bitmap);
|
||||
}
|
||||
bitmap_len = (state.total_packets + 1 + 7)/8;
|
||||
state.bitmap = allot(bitmap_len);
|
||||
state.image = allot(total_bytes);
|
||||
if ((unsigned long)state.image < 1024*1024) {
|
||||
printf("ALERT: slam filesize to large for available memory\n");
|
||||
return 0;
|
||||
}
|
||||
memset(state.bitmap, 0, bitmap_len);
|
||||
|
||||
return header + state.hdr_len;
|
||||
}
|
||||
|
||||
static int slam_recv_data(unsigned char *data)
|
||||
{
|
||||
unsigned long packet;
|
||||
unsigned long data_len;
|
||||
int err;
|
||||
struct udphdr *udp;
|
||||
udp = (struct udphdr *)&nic.packet[ETH_HLEN + sizeof(struct iphdr)];
|
||||
err = 0;
|
||||
packet = slam_decode(&data, &nic.packet[nic.packetlen], &err);
|
||||
if (err || (packet > state.total_packets)) {
|
||||
printf("ALERT: Invalid packet number\n");
|
||||
return 0;
|
||||
}
|
||||
/* Compute the expected data length */
|
||||
if (packet != state.total_packets -1) {
|
||||
data_len = state.block_size;
|
||||
} else {
|
||||
data_len = state.total_bytes % state.block_size;
|
||||
}
|
||||
/* If the packet size is wrong drop the packet and then continue */
|
||||
if (ntohs(udp->len) != (data_len + (data - (unsigned char*)udp))) {
|
||||
printf("ALERT: udp packet is not the correct size\n");
|
||||
return 1;
|
||||
}
|
||||
if (nic.packetlen < data_len + (data - nic.packet)) {
|
||||
printf("ALERT: Ethernet packet shorter than data_len\n");
|
||||
return 1;
|
||||
}
|
||||
if (data_len > state.block_size) {
|
||||
data_len = state.block_size;
|
||||
}
|
||||
if (((state.bitmap[packet >> 3] >> (packet & 7)) & 1) == 0) {
|
||||
/* Non duplicate packet */
|
||||
state.bitmap[packet >> 3] |= (1 << (packet & 7));
|
||||
memcpy(state.image + (packet*state.block_size), data, data_len);
|
||||
state.received_packets++;
|
||||
} else {
|
||||
#ifdef MDEBUG
|
||||
printf("<DUP>\n");
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void transmit_nack(unsigned char *ptr, struct slam_info *info)
|
||||
{
|
||||
int nack_len;
|
||||
/* Ensure the packet is null terminated */
|
||||
*ptr++ = 0;
|
||||
nack_len = ptr - (unsigned char *)&nack;
|
||||
build_udp_hdr(info->server_ip.s_addr,
|
||||
info->local_port, info->server_port, 1, nack_len, &nack);
|
||||
ip_transmit(nack_len, &nack);
|
||||
#if defined(MDEBUG) && 0
|
||||
printf("Sent NACK to %@ bytes: %d have:%ld/%ld\n",
|
||||
info->server_ip, nack_len,
|
||||
state.received_packets, state.total_packets);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void slam_send_nack(struct slam_info *info)
|
||||
{
|
||||
unsigned char *ptr, *end;
|
||||
/* Either I timed out or I was explicitly
|
||||
* asked for a request packet
|
||||
*/
|
||||
ptr = &nack.data[0];
|
||||
/* Reserve space for the trailling null */
|
||||
end = &nack.data[sizeof(nack.data) -1];
|
||||
if (!state.bitmap) {
|
||||
slam_encode(&ptr, end, 0);
|
||||
slam_encode(&ptr, end, 1);
|
||||
}
|
||||
else {
|
||||
/* Walk the bitmap */
|
||||
unsigned long i;
|
||||
unsigned long len;
|
||||
unsigned long max;
|
||||
int value;
|
||||
int last;
|
||||
/* Compute the last bit and store an inverted trailer */
|
||||
max = state.total_packets;
|
||||
value = ((state.bitmap[(max -1) >> 3] >> ((max -1) & 7) ) & 1);
|
||||
value = !value;
|
||||
state.bitmap[max >> 3] &= ~(1 << (max & 7));
|
||||
state.bitmap[max >> 3] |= value << (max & 7);
|
||||
|
||||
len = 0;
|
||||
last = 1; /* Start with the received packets */
|
||||
for(i = 0; i <= max; i++) {
|
||||
value = (state.bitmap[i>>3] >> (i & 7)) & 1;
|
||||
if (value == last) {
|
||||
len++;
|
||||
} else {
|
||||
if (slam_encode(&ptr, end, len))
|
||||
break;
|
||||
last = value;
|
||||
len = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
info->sent_nack = 1;
|
||||
transmit_nack(ptr, info);
|
||||
}
|
||||
|
||||
static void slam_send_disconnect(struct slam_info *info)
|
||||
{
|
||||
if (info->sent_nack) {
|
||||
/* A disconnect is a packet with just the null terminator */
|
||||
transmit_nack(&nack.data[0], info);
|
||||
}
|
||||
info->sent_nack = 0;
|
||||
}
|
||||
|
||||
|
||||
static int proto_slam(struct slam_info *info)
|
||||
{
|
||||
int retry;
|
||||
long timeout;
|
||||
|
||||
init_slam_state();
|
||||
|
||||
retry = -1;
|
||||
rx_qdrain();
|
||||
/* Arp for my server */
|
||||
if (arptable[ARP_SERVER].ipaddr.s_addr != info->server_ip.s_addr) {
|
||||
arptable[ARP_SERVER].ipaddr.s_addr = info->server_ip.s_addr;
|
||||
memset(arptable[ARP_SERVER].node, 0, ETH_ALEN);
|
||||
}
|
||||
/* If I'm running over multicast join the multicast group */
|
||||
join_group(IGMP_SERVER, info->multicast_ip.s_addr);
|
||||
for(;;) {
|
||||
unsigned char *header;
|
||||
unsigned char *data;
|
||||
int type;
|
||||
header = data = 0;
|
||||
|
||||
timeout = slam_sleep_interval(retry);
|
||||
type = await_reply(await_slam, 0, info, timeout);
|
||||
/* Compute the timeout for next time */
|
||||
if (type == SLAM_TIMEOUT) {
|
||||
/* If I timeouted recompute the next timeout */
|
||||
if (retry++ > SLAM_MAX_RETRIES) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
retry = 0;
|
||||
}
|
||||
if ((type == SLAM_DATA) || (type == SLAM_REQUEST)) {
|
||||
/* Check the incomming packet and reinit the data
|
||||
* structures if necessary.
|
||||
*/
|
||||
header = &nic.packet[ETH_HLEN +
|
||||
sizeof(struct iphdr) + sizeof(struct udphdr)];
|
||||
data = header + state.hdr_len;
|
||||
if (memcmp(state.hdr, header, state.hdr_len) != 0) {
|
||||
/* Something is fishy reset the transaction */
|
||||
data = reinit_slam_state(header, &nic.packet[nic.packetlen]);
|
||||
if (!data) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == SLAM_DATA) {
|
||||
if (!slam_recv_data(data)) {
|
||||
return 0;
|
||||
}
|
||||
if (state.received_packets == state.total_packets) {
|
||||
/* We are done get out */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((type == SLAM_TIMEOUT) || (type == SLAM_REQUEST)) {
|
||||
/* Either I timed out or I was explicitly
|
||||
* asked by a request packet
|
||||
*/
|
||||
slam_send_nack(info);
|
||||
}
|
||||
}
|
||||
slam_send_disconnect(info);
|
||||
|
||||
/* Leave the multicast group */
|
||||
leave_group(IGMP_SERVER);
|
||||
/* FIXME don't overwrite myself */
|
||||
/* load file to correct location */
|
||||
return info->fnc(state.image, 1, state.total_bytes, 1);
|
||||
}
|
||||
|
||||
|
||||
int url_slam(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
|
||||
{
|
||||
struct slam_info info;
|
||||
/* Set the defaults */
|
||||
info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
|
||||
info.server_port = SLAM_PORT;
|
||||
info.multicast_ip.s_addr = htonl(SLAM_MULTICAST_IP);
|
||||
info.multicast_port = SLAM_MULTICAST_PORT;
|
||||
info.local_ip.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
|
||||
info.local_port = SLAM_LOCAL_PORT;
|
||||
info.fnc = fnc;
|
||||
info.sent_nack = 0;
|
||||
/* Now parse the url */
|
||||
if (url_port != -1) {
|
||||
info.server_port = url_port;
|
||||
}
|
||||
if (name[0]) {
|
||||
/* multicast ip */
|
||||
name += inet_aton(name, &info.multicast_ip);
|
||||
if (name[0] == ':') {
|
||||
name++;
|
||||
info.multicast_port = strtoul(name, &name, 10);
|
||||
}
|
||||
}
|
||||
if (name[0]) {
|
||||
printf("\nBad url\n");
|
||||
return 0;
|
||||
}
|
||||
return proto_slam(&info);
|
||||
}
|
||||
|
||||
#endif /* DOWNLOAD_PROTO_SLAM */
|
||||
@@ -1,491 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* proto_tftm.c -- Etherboot Multicast TFTP
|
||||
* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* This code is based on the DOWNLOAD_PROTO_TFTM section of
|
||||
* Etherboot 5.3 core/nic.c and:
|
||||
*
|
||||
* Anselm Martin Hoffmeister's previous proto_tftm.c multicast work
|
||||
* Eric Biederman's proto_slam.c
|
||||
*
|
||||
* $Revision$
|
||||
* $Author$
|
||||
* $Date$
|
||||
*
|
||||
* REVISION HISTORY:
|
||||
* ================
|
||||
* 09-07-2003 timlegge Release Version, Capable of Multicast Booting
|
||||
* 08-30-2003 timlegge Initial version, Assumes consecutive blocks
|
||||
*
|
||||
* Indent Options: indent -kr -i8
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef DOWNLOAD_PROTO_TFTM
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
|
||||
//#define TFTM_DEBUG
|
||||
#ifdef TFTM_DEBUG
|
||||
#define debug(x) printf x
|
||||
#else
|
||||
#define debug(x)
|
||||
#endif
|
||||
struct tftm_info {
|
||||
in_addr server_ip;
|
||||
in_addr multicast_ip;
|
||||
in_addr local_ip;
|
||||
uint16_t server_port;
|
||||
uint16_t multicast_port;
|
||||
uint16_t local_port;
|
||||
int (*fnc) (unsigned char *, unsigned int, unsigned int, int);
|
||||
int sent_nack;
|
||||
const char *name; /* Filename */
|
||||
};
|
||||
|
||||
struct tftm_state {
|
||||
unsigned long block_size;
|
||||
unsigned long total_bytes;
|
||||
unsigned long total_packets;
|
||||
char ismaster;
|
||||
unsigned long received_packets;
|
||||
unsigned char *image;
|
||||
unsigned char *bitmap;
|
||||
char recvd_oack;
|
||||
} state;
|
||||
|
||||
#define TFTM_PORT 1758
|
||||
#define TFTM_MIN_PACKET 1024
|
||||
|
||||
|
||||
int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
|
||||
unsigned long *filesize, struct tftm_info *info);
|
||||
|
||||
static int await_tftm(int ival, void *ptr, unsigned short ptype __unused,
|
||||
struct iphdr *ip, struct udphdr *udp)
|
||||
{
|
||||
struct tftm_info *info = ptr;
|
||||
|
||||
/* Check for Unicast data being received */
|
||||
if (ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) {
|
||||
if (!udp) {
|
||||
return 0;
|
||||
}
|
||||
if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
|
||||
return 0;
|
||||
if (ntohs(udp->dest) != ival)
|
||||
return 0;
|
||||
|
||||
return 1; /* Unicast Data Received */
|
||||
}
|
||||
|
||||
/* Also check for Multicast data being received */
|
||||
if ((ip->dest.s_addr == info->multicast_ip.s_addr) &&
|
||||
(ntohs(udp->dest) == info->multicast_port) &&
|
||||
(nic.packetlen >= ETH_HLEN + sizeof(struct iphdr) +
|
||||
sizeof(struct udphdr))) {
|
||||
return 1; /* Multicast data received */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proto_tftm(struct tftm_info *info)
|
||||
{
|
||||
int retry = 0;
|
||||
static unsigned short iport = 2000;
|
||||
unsigned short oport = 0;
|
||||
unsigned short len, block = 0, prevblock = 0;
|
||||
struct tftp_t *tr;
|
||||
struct tftpreq_t tp;
|
||||
unsigned long filesize = 0;
|
||||
|
||||
state.image = 0;
|
||||
state.bitmap = 0;
|
||||
|
||||
rx_qdrain();
|
||||
|
||||
/* Warning: the following assumes the layout of bootp_t.
|
||||
But that's fixed by the IP, UDP and BOOTP specs. */
|
||||
|
||||
/* Send a tftm-request to the server */
|
||||
tp.opcode = htons(TFTP_RRQ); /* Const for "\0x0" "\0x1" =^= ReadReQuest */
|
||||
len =
|
||||
sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
|
||||
sprintf((char *) tp.u.rrq,
|
||||
"%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
|
||||
info->name, 0, 0, 0, 0, 0, TFTM_MIN_PACKET, 0, 0) + 1;
|
||||
|
||||
if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
|
||||
TFTM_PORT, len, &tp))
|
||||
return (0);
|
||||
|
||||
/* loop to listen for packets and to receive the file */
|
||||
for (;;) {
|
||||
long timeout;
|
||||
#ifdef CONGESTED
|
||||
timeout =
|
||||
rfc2131_sleep_interval(block ? TFTP_REXMT : TIMEOUT,
|
||||
retry);
|
||||
#else
|
||||
timeout = rfc2131_sleep_interval(TIMEOUT, retry);
|
||||
#endif
|
||||
/* Calls the await_reply function in nic.c which in turn calls
|
||||
await_tftm (1st parameter) as above */
|
||||
if (!await_reply(await_tftm, iport, info, timeout)) {
|
||||
if (!block && retry++ < MAX_TFTP_RETRIES) { /* maybe initial request was lost */
|
||||
if (!udp_transmit
|
||||
(arptable[ARP_SERVER].ipaddr.s_addr,
|
||||
++iport, TFTM_PORT, len, &tp))
|
||||
return (0);
|
||||
continue;
|
||||
}
|
||||
#ifdef CONGESTED
|
||||
if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT)) { /* we resend our last ack */
|
||||
#ifdef MDEBUG
|
||||
printf("<REXMT>\n");
|
||||
#endif
|
||||
debug(("Timed out receiving file"));
|
||||
len =
|
||||
sizeof(tp.ip) + sizeof(tp.udp) +
|
||||
sizeof(tp.opcode) +
|
||||
sprintf((char *) tp.u.rrq,
|
||||
"%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
|
||||
info->name, 0, 0, 0, 0, 0,
|
||||
TFTM_MIN_PACKET, 0, 0) + 1;
|
||||
|
||||
udp_transmit
|
||||
(arptable[ARP_SERVER].ipaddr.s_addr,
|
||||
++iport, TFTM_PORT, len, &tp);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
break; /* timeout */
|
||||
}
|
||||
|
||||
tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
|
||||
|
||||
if (tr->opcode == ntohs(TFTP_ERROR)) {
|
||||
printf("TFTP error %d (%s)\n",
|
||||
ntohs(tr->u.err.errcode), tr->u.err.errmsg);
|
||||
break;
|
||||
}
|
||||
|
||||
if (tr->opcode == ntohs(TFTP_OACK)) {
|
||||
int i =
|
||||
opt_get_multicast(tr, &len, &filesize, info);
|
||||
|
||||
if (i == 0 || (i != 7 && !state.recvd_oack)) { /* Multicast unsupported */
|
||||
/* Transmit an error message to the server to end the transmission */
|
||||
printf
|
||||
("TFTM-Server doesn't understand options [blksize tsize multicast]\n");
|
||||
tp.opcode = htons(TFTP_ERROR);
|
||||
tp.u.err.errcode = 8;
|
||||
/*
|
||||
* Warning: the following assumes the layout of bootp_t.
|
||||
* But that's fixed by the IP, UDP and BOOTP specs.
|
||||
*/
|
||||
len =
|
||||
sizeof(tp.ip) + sizeof(tp.udp) +
|
||||
sizeof(tp.opcode) +
|
||||
sizeof(tp.u.err.errcode) +
|
||||
/*
|
||||
* Normally bad form to omit the format string, but in this case
|
||||
* the string we are copying from is fixed. sprintf is just being
|
||||
* used as a strcpy and strlen.
|
||||
*/
|
||||
sprintf((char *) tp.u.err.errmsg,
|
||||
"RFC2090 error") + 1;
|
||||
udp_transmit(arptable[ARP_SERVER].ipaddr.
|
||||
s_addr, iport,
|
||||
ntohs(tr->udp.src), len, &tp);
|
||||
block = tp.u.ack.block = 0; /* this ensures, that */
|
||||
/* the packet does not get */
|
||||
/* processed as data! */
|
||||
return (0);
|
||||
} else {
|
||||
unsigned long bitmap_len;
|
||||
/* */
|
||||
if (!state.recvd_oack) {
|
||||
|
||||
state.total_packets =
|
||||
1 + (filesize -
|
||||
(filesize %
|
||||
state.block_size)) /
|
||||
state.block_size;
|
||||
bitmap_len =
|
||||
(state.total_packets + 7) / 8;
|
||||
if (!state.image) {
|
||||
state.bitmap =
|
||||
allot(bitmap_len);
|
||||
state.image =
|
||||
allot(filesize);
|
||||
|
||||
if ((unsigned long) state.
|
||||
image < 1024 * 1024) {
|
||||
printf
|
||||
("ALERT: tftp filesize to large for available memory\n");
|
||||
return 0;
|
||||
}
|
||||
memset(state.bitmap, 0,
|
||||
bitmap_len);
|
||||
}
|
||||
/* If I'm running over multicast join the multicast group */
|
||||
join_group(IGMP_SERVER,
|
||||
info->multicast_ip.
|
||||
s_addr);
|
||||
}
|
||||
state.recvd_oack = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else if (tr->opcode == htons(TFTP_DATA)) {
|
||||
unsigned long data_len;
|
||||
unsigned char *data;
|
||||
struct udphdr *udp;
|
||||
udp =
|
||||
(struct udphdr *) &nic.packet[ETH_HLEN +
|
||||
sizeof(struct
|
||||
iphdr)];
|
||||
len =
|
||||
ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
|
||||
data =
|
||||
nic.packet + ETH_HLEN + sizeof(struct iphdr) +
|
||||
sizeof(struct udphdr) + 4;
|
||||
|
||||
if (len > TFTM_MIN_PACKET) /* shouldn't happen */
|
||||
continue; /* ignore it */
|
||||
|
||||
block = ntohs(tp.u.ack.block = tr->u.data.block);
|
||||
|
||||
if (block > state.total_packets) {
|
||||
printf("ALERT: Invalid packet number\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Compute the expected data length */
|
||||
if (block != state.total_packets) {
|
||||
data_len = state.block_size;
|
||||
} else {
|
||||
data_len = filesize % state.block_size;
|
||||
}
|
||||
/* If the packet size is wrong drop the packet and then continue */
|
||||
if (ntohs(udp->len) !=
|
||||
(data_len + (data - (unsigned char *) udp))) {
|
||||
printf
|
||||
("ALERT: udp packet is not the correct size: %d\n",
|
||||
block);
|
||||
continue;
|
||||
}
|
||||
if (nic.packetlen < data_len + (data - nic.packet)) {
|
||||
printf
|
||||
("ALERT: Ethernet packet shorter than data_len: %d\n",
|
||||
block);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data_len > state.block_size) {
|
||||
data_len = state.block_size;
|
||||
}
|
||||
if (((state.
|
||||
bitmap[block >> 3] >> (block & 7)) & 1) ==
|
||||
0) {
|
||||
/* Non duplicate packet */
|
||||
state.bitmap[block >> 3] |=
|
||||
(1 << (block & 7));
|
||||
memcpy(state.image +
|
||||
((block - 1) * state.block_size),
|
||||
data, data_len);
|
||||
state.received_packets++;
|
||||
} else {
|
||||
|
||||
/* printf("<DUP>\n"); */
|
||||
}
|
||||
}
|
||||
|
||||
else { /* neither TFTP_OACK, TFTP_DATA nor TFTP_ERROR */
|
||||
break;
|
||||
}
|
||||
|
||||
if (state.received_packets <= state.total_packets) {
|
||||
unsigned long b;
|
||||
unsigned long len;
|
||||
unsigned long max;
|
||||
int value;
|
||||
int last;
|
||||
|
||||
/* Compute the last bit and store an inverted trailer */
|
||||
max = state.total_packets + 1;
|
||||
value =
|
||||
((state.
|
||||
bitmap[(max - 1) >> 3] >> ((max -
|
||||
1) & 7)) & 1);
|
||||
value = !value;
|
||||
state.bitmap[max >> 3] &= ~(1 << (max & 7));
|
||||
state.bitmap[max >> 3] |= value << (max & 7);
|
||||
|
||||
len = 0;
|
||||
last = 0; /* Start with the received packets */
|
||||
for (b = 1; b <= max; b++) {
|
||||
value =
|
||||
(state.bitmap[b >> 3] >> (b & 7)) & 1;
|
||||
|
||||
if (value == 0) {
|
||||
tp.u.ack.block = htons(b - 1); /* Acknowledge the previous block */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.ismaster) {
|
||||
tp.opcode = htons(TFTP_ACK);
|
||||
oport = ntohs(tr->udp.src);
|
||||
udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport, oport, TFTP_MIN_PACKET, &tp); /* ack */
|
||||
}
|
||||
if (state.received_packets == state.total_packets) {
|
||||
/* If the client is finished and not the master,
|
||||
* ack the last packet */
|
||||
if (!state.ismaster) {
|
||||
tp.opcode = htons(TFTP_ACK);
|
||||
/* Ack Last packet to end xfer */
|
||||
tp.u.ack.block = htons(state.total_packets);
|
||||
oport = ntohs(tr->udp.src);
|
||||
udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport, oport, TFTP_MIN_PACKET, &tp); /* ack */
|
||||
}
|
||||
/* We are done get out */
|
||||
forget(state.bitmap);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((unsigned short) (block - prevblock) != 1) {
|
||||
/* Retransmission or OACK, don't process via callback
|
||||
* and don't change the value of prevblock. */
|
||||
continue;
|
||||
}
|
||||
|
||||
prevblock = block;
|
||||
retry = 0; /* It's the right place to zero the timer? */
|
||||
|
||||
}
|
||||
/* Leave the multicast group */
|
||||
leave_group(IGMP_SERVER);
|
||||
return info->fnc(state.image, 1, filesize, 1);
|
||||
}
|
||||
|
||||
int url_tftm(const char *name,
|
||||
int (*fnc) (unsigned char *, unsigned int, unsigned int, int))
|
||||
{
|
||||
|
||||
int ret;
|
||||
struct tftm_info info;
|
||||
|
||||
/* Set the defaults */
|
||||
info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
|
||||
info.server_port = TFTM_PORT;
|
||||
info.local_ip.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
|
||||
info.local_port = TFTM_PORT; /* Does not matter. So take tftm port too. */
|
||||
info.multicast_ip.s_addr = info.local_ip.s_addr;
|
||||
info.multicast_port = TFTM_PORT;
|
||||
info.fnc = fnc;
|
||||
state.ismaster = 0;
|
||||
info.name = name;
|
||||
|
||||
state.block_size = 0;
|
||||
state.total_bytes = 0;
|
||||
state.total_packets = 0;
|
||||
state.received_packets = 0;
|
||||
state.image = 0;
|
||||
state.bitmap = 0;
|
||||
state.recvd_oack = 0;
|
||||
|
||||
if (name[0] != '/') {
|
||||
/* server ip given, so use it */
|
||||
name += inet_aton(info.name, &info.server_ip);
|
||||
/* No way to specify another port for now */
|
||||
}
|
||||
if (name[0] != '/') {
|
||||
printf("Bad tftm-URI: [%s]\n", info.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = proto_tftm(&info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* Parse the multicast options
|
||||
*******************************/
|
||||
int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
|
||||
unsigned long *filesize, struct tftm_info *info)
|
||||
{
|
||||
const char *p = tr->u.oack.data, *e = 0;
|
||||
int i = 0;
|
||||
*len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
|
||||
if (*len > TFTM_MIN_PACKET)
|
||||
return -1;
|
||||
e = p + *len;
|
||||
|
||||
while (*p != '\0' && p < e) {
|
||||
if (!strcasecmp("tsize", p)) {
|
||||
p += 6;
|
||||
if ((*filesize = strtoul(p, &p, 10)) > 0)
|
||||
i |= 4;
|
||||
debug(("\n"));
|
||||
debug(("tsize=%d\n", *filesize));
|
||||
while (p < e && *p)
|
||||
p++;
|
||||
if (p < e)
|
||||
p++;
|
||||
} else if (!strcasecmp("blksize", p)) {
|
||||
i |= 2;
|
||||
p += 8;
|
||||
state.block_size = strtoul(p, &p, 10);
|
||||
if (state.block_size != TFTM_MIN_PACKET) {
|
||||
printf
|
||||
("TFTM-Server rejected required transfer blocksize %d\n",
|
||||
TFTM_MIN_PACKET);
|
||||
return 0;
|
||||
}
|
||||
debug(("blksize=%d\n", state.block_size));
|
||||
while (p < e && *p)
|
||||
p++;
|
||||
if (p < e)
|
||||
p++;
|
||||
} else if (!strncmp(p, "multicast", 10)) {
|
||||
i |= 1;
|
||||
p += 10;
|
||||
debug(("multicast options: %s\n", p));
|
||||
p += 1 + inet_aton(p, &info->multicast_ip);
|
||||
debug(("multicast ip = %@\n", info->multicast_ip));
|
||||
info->multicast_port = strtoul(p, &p, 10);
|
||||
++p;
|
||||
debug(("multicast port = %d\n",
|
||||
info->multicast_port));
|
||||
state.ismaster = (*p == '1' ? 1 : 0);
|
||||
debug(("multicast ismaster = %d\n",
|
||||
state.ismaster));
|
||||
while (p < e && *p)
|
||||
p++;
|
||||
if (p < e)
|
||||
p++;
|
||||
}
|
||||
}
|
||||
if (p > e)
|
||||
return 0;
|
||||
return i;
|
||||
}
|
||||
#endif /* DOWNLOAD_PROTO_TFTP */
|
||||
Reference in New Issue
Block a user