coreboot/src/etherboot/rarp.c
2001-11-30 09:50:07 +00:00

735 lines
22 KiB
C

#include <printk.h>
#include "etherboot.h"
#include "nic.h"
static unsigned long xid;
#define eth_poll() nic.poll(&nic)
#define eth_transmit(a,b,c,d) nic.transmit(&nic,a,b,c, (char *) d)
int decode_rfc1533(unsigned char *p, int block, int len, int eof)
{
return(-1); // don't support it yet, just define it!
}
#define NO_DHCP_SUPPORT
#define EMERGENCYDISKBOOT
#define getchar() ttys0_rx_char()
static const unsigned char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static unsigned long netmask;
struct arptable_t arptable[MAX_ARP];
struct bootpd_t bootp_data;
/***************************************************************************
getdec, a simple sort of atoi
***************************************************************************/
int getdec(char **ptr)
{
char *p = *ptr;
int ret=0;
if ((*p < '0') || (*p > '9')) return(-1);
while ((*p >= '0') && (*p <= '9')) {
ret = ret*10 + (*p - '0');
p++;
}
*ptr = p;
return(ret);
}
/**************************************************************************
STRCASECMP (not entirely correct, but this will do for our purposes)
**************************************************************************/
int strcasecmp(char *a, char *b)
{
while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; }
return((*a & ~0x20) - (*b & ~0x20));
}
/**************************************************************************
DEFAULT_NETMASK - Return default netmask for IP address
**************************************************************************/
static inline unsigned long default_netmask(void)
{
int net = ntohl(arptable[ARP_CLIENT].ipaddr.s_addr) >> 24;
if (net <= 127)
return(htonl(0xff000000));
else if (net < 192)
return(htonl(0xffff0000));
else
return(htonl(0xffffff00));
}
/**************************************************************************
IPCHKSUM - Checksum IP Header
**************************************************************************/
static unsigned short ipchksum(unsigned short *ip, int len)
{
unsigned long sum = 0;
len >>= 1;
while (len--) {
sum += *(ip++);
if (sum > 0xFFFF)
sum -= 0xFFFF;
}
return((~sum) & 0x0000FFFF);
}
/**************************************************************************
RFC951_SLEEP - sleep for expotentially longer times
**************************************************************************/
void rfc951_sleep(int exp)
{
static long seed = 0;
long q;
unsigned long tmo;
#ifdef BACKOFF_LIMIT
if (exp > BACKOFF_LIMIT)
exp = BACKOFF_LIMIT;
#endif
if (!seed) /* Initialize linear congruential generator */
seed = currticks() + *(long *)&arptable[ARP_CLIENT].node
+ ((short *)arptable[ARP_CLIENT].node)[2];
/* simplified version of the LCG given in Bruce Scheier's
"Applied Cryptography" */
q = seed/53668;
if ((seed = 40014*(seed-53668*q) - 12211*q) < 0) seed += 2147483563l;
/* compute mask */
for (tmo = 63; tmo <= 60*TICKS_PER_SEC && --exp > 0; tmo = 2*tmo+1);
/* sleep */
printk_info("<sleep>\n");
for (tmo = (tmo&seed)+currticks(); currticks() < tmo; )
return;
}
/**************************************************************************
AWAIT_REPLY - Wait until we get a response for our request
**************************************************************************/
int await_reply(int type, int ival, void *ptr, int timeout)
{
unsigned long time;
struct iphdr *ip;
struct udphdr *udp;
struct arprequest *arpreply;
struct bootp_t *bootpreply;
struct rpc_t *rpc;
unsigned short ptype;
unsigned int protohdrlen = ETH_HLEN + sizeof(struct iphdr) +
sizeof(struct udphdr);
time = timeout + currticks();
/* The timeout check is done below. The timeout is only checked if
* there is no packet in the Rx queue. This assumes that eth_poll()
* needs a negligible amount of time. */
for (;;) {
if (eth_poll()) { /* We have something! */
/* Check for ARP - No IP hdr */
if (nic.packetlen >= ETH_HLEN) {
ptype = ((unsigned short) nic.packet[12]) << 8
| ((unsigned short) nic.packet[13]);
} else continue; /* what else could we do with it? */
if ((nic.packetlen >= ETH_HLEN +
sizeof(struct arprequest)) &&
(ptype == ARP) ) {
unsigned long tmp;
arpreply = (struct arprequest *)
&nic.packet[ETH_HLEN];
if ((arpreply->opcode == htons(ARP_REPLY)) &&
!memcmp(arpreply->sipaddr, ptr, sizeof(in_addr)) &&
(type == AWAIT_ARP)) {
memcpy(arptable[ival].node, arpreply->shwaddr, ETH_ALEN);
return(1);
}
memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
if ((arpreply->opcode == htons(ARP_REQUEST)) &&
(tmp == arptable[ARP_CLIENT].ipaddr.s_addr)) {
arpreply->opcode = htons(ARP_REPLY);
memcpy(arpreply->tipaddr, arpreply->sipaddr, sizeof(in_addr));
memcpy(arpreply->thwaddr, arpreply->shwaddr, ETH_ALEN);
memcpy(arpreply->sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
memcpy(arpreply->shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
eth_transmit(arpreply->thwaddr, ARP,
sizeof(struct arprequest),
(char *) arpreply);
#ifdef MDEBUG
memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
printf("Sent ARP reply to: %I\n",tmp);
#endif /* MDEBUG */
}
continue;
}
if (type == AWAIT_QDRAIN) {
continue;
}
/* Check for RARP - No IP hdr */
if ((type == AWAIT_RARP) &&
(nic.packetlen >= ETH_HLEN +
sizeof(struct arprequest)) &&
(ptype == RARP)) {
arpreply = (struct arprequest *)
&nic.packet[ETH_HLEN];
if ((arpreply->opcode == htons(RARP_REPLY)) &&
!memcmp(arpreply->thwaddr, ptr, ETH_ALEN)) {
memcpy(arptable[ARP_SERVER].node, arpreply->shwaddr, ETH_ALEN);
memcpy(& arptable[ARP_SERVER].ipaddr, arpreply->sipaddr, sizeof(in_addr));
memcpy(& arptable[ARP_CLIENT].ipaddr, arpreply->tipaddr, sizeof(in_addr));
return(1);
}
continue;
}
/* Anything else has IP header */
if ((nic.packetlen < protohdrlen) ||
(ptype != IP) ) continue;
ip = (struct iphdr *)&nic.packet[ETH_HLEN];
if ((ip->verhdrlen != 0x45) ||
ipchksum((unsigned short *)ip, sizeof(struct iphdr)) ||
(ip->protocol != IP_UDP)) continue;
udp = (struct udphdr *)&nic.packet[ETH_HLEN +
sizeof(struct iphdr)];
/* BOOTP ? */
bootpreply = (struct bootp_t *)&nic.packet[ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr)];
if ((type == AWAIT_BOOTP) &&
(nic.packetlen >= (ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) +
#ifdef NO_DHCP_SUPPORT
sizeof(struct bootp_t))) &&
#else
sizeof(struct bootp_t))-DHCP_OPT_LEN) &&
#endif /* NO_DHCP_SUPPORT */
(udp->dest == htons(BOOTP_CLIENT)) &&
(bootpreply->bp_op == BOOTP_REPLY) &&
(bootpreply->bp_xid == xid) &&
(memcmp(broadcast, bootpreply->bp_hwaddr, ETH_ALEN) ==0 ||
memcmp(arptable[ARP_CLIENT].node, bootpreply->bp_hwaddr, ETH_ALEN) == 0)) {
arptable[ARP_CLIENT].ipaddr.s_addr =
bootpreply->bp_yiaddr.s_addr;
#ifndef NO_DHCP_SUPPORT
dhcp_addr.s_addr = bootpreply->bp_yiaddr.s_addr;
#endif /* NO_DHCP_SUPPORT */
netmask = default_netmask();
arptable[ARP_SERVER].ipaddr.s_addr =
bootpreply->bp_siaddr.s_addr;
memset(arptable[ARP_SERVER].node, 0, ETH_ALEN); /* Kill arp */
arptable[ARP_GATEWAY].ipaddr.s_addr =
bootpreply->bp_giaddr.s_addr;
memset(arptable[ARP_GATEWAY].node, 0, ETH_ALEN); /* Kill arp */
/* bootpreply->bp_file will be copied to KERNEL_BUF in the memcpy */
memcpy((char *)BOOTP_DATA_ADDR, (char *)bootpreply, sizeof(struct bootpd_t));
decode_rfc1533(BOOTP_DATA_ADDR->bootp_reply.bp_vend,
#ifdef NO_DHCP_SUPPORT
0, BOOTP_VENDOR_LEN + MAX_BOOTP_EXTLEN, 1);
#else
0, DHCP_OPT_LEN + MAX_BOOTP_EXTLEN, 1);
#endif /* NO_DHCP_SUPPORT */
#ifdef REQUIRE_VCI_ETHERBOOT
if (!vci_etherboot)
return (0);
#endif
return(1);
}
#ifdef DOWNLOAD_PROTO_TFTP
/* TFTP ? */
if ((type == AWAIT_TFTP) &&
(ntohs(udp->dest) == ival)) return(1);
#endif /* DOWNLOAD_PROTO_TFTP */
#ifdef DOWNLOAD_PROTO_NFS
/* RPC ? */
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
if ((type == AWAIT_RPC) &&
(ntohs(udp->dest) == ival) &&
(*(unsigned long *)ptr == ntohl(rpc->u.reply.id)) &&
(ntohl(rpc->u.reply.type) == MSG_REPLY)) {
return (1);
}
#endif /* DOWNLOAD_PROTO_NFS */
} else {
/* Check for abort key only if the Rx queue is empty -
* as long as we have something to process, don't
* assume that something failed. It is unlikely that
* we have no processing time left between packets. */
if (iskey() && (getchar() == ESC))
#ifdef EMERGENCYDISKBOOT
return(-1);
#else
longjmp(jmp_bootmenu, -1);
#endif
/* Do the timeout after at least a full queue walk. */
if ((timeout == 0) || (currticks() > time)) {
break;
}
}
}
return(0);
}
/**************************************************************************
RARP - Get my IP address and load information
**************************************************************************/
int rarp(void)
{
int retry;
/* arp and rarp requests share the same packet structure. */
struct arprequest rarpreq;
memset(&rarpreq, 0, sizeof(rarpreq));
rarpreq.hwtype = htons(1);
rarpreq.protocol = htons(IP);
rarpreq.hwlen = ETH_ALEN;
rarpreq.protolen = 4;
rarpreq.opcode = htons(RARP_REQUEST);
memcpy(&rarpreq.shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
/* sipaddr is already zeroed out */
memcpy(&rarpreq.thwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
/* tipaddr is already zeroed out */
for (retry = 0; retry < MAX_ARP_RETRIES; rfc951_sleep(++retry)) {
eth_transmit(broadcast, RARP, sizeof(rarpreq), &rarpreq);
if (await_reply(AWAIT_RARP, 0, rarpreq.shwaddr, TIMEOUT))
break;
}
if (retry < MAX_ARP_RETRIES) {
(void)sprintf(KERNEL_BUF, DEFAULT_KERNELPATH, arptable[ARP_CLIENT].ipaddr);
return (1);
}
return (0);
}
/**************************************************************************
UDP_TRANSMIT - Send a UDP datagram
**************************************************************************/
int udp_transmit(unsigned long destip, unsigned int srcsock,
unsigned int destsock, int len, const void *buf)
{
struct iphdr *ip;
struct udphdr *udp;
struct arprequest arpreq;
int arpentry, i;
int retry;
ip = (struct iphdr *)buf;
udp = (struct udphdr *)((char *)buf + sizeof(struct iphdr));
ip->verhdrlen = 0x45;
ip->service = 0;
ip->len = htons(len);
ip->ident = 0;
ip->frags = 0;
ip->ttl = 60;
ip->protocol = IP_UDP;
ip->chksum = 0;
ip->src.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
ip->dest.s_addr = destip;
ip->chksum = ipchksum((unsigned short *)buf, sizeof(struct iphdr));
udp->src = htons(srcsock);
udp->dest = htons(destsock);
udp->len = htons(len - sizeof(struct iphdr));
udp->chksum = 0;
if (destip == IP_BROADCAST) {
eth_transmit(broadcast, IP, len, buf);
} else {
if (((destip & netmask) !=
(arptable[ARP_CLIENT].ipaddr.s_addr & netmask)) &&
arptable[ARP_GATEWAY].ipaddr.s_addr)
destip = arptable[ARP_GATEWAY].ipaddr.s_addr;
for(arpentry = 0; arpentry<MAX_ARP; arpentry++)
if (arptable[arpentry].ipaddr.s_addr == destip) break;
if (arpentry == MAX_ARP) {
printk_info("%I is not in my arp table!\n", destip);
return(0);
}
for (i = 0; i < ETH_ALEN; i++)
if (arptable[arpentry].node[i])
break;
if (i == ETH_ALEN) { /* Need to do arp request */
arpreq.hwtype = htons(1);
arpreq.protocol = htons(IP);
arpreq.hwlen = ETH_ALEN;
arpreq.protolen = 4;
arpreq.opcode = htons(ARP_REQUEST);
memcpy(arpreq.shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
memcpy(arpreq.sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
memset(arpreq.thwaddr, 0, ETH_ALEN);
memcpy(arpreq.tipaddr, &destip, sizeof(in_addr));
for (retry = 1; retry <= MAX_ARP_RETRIES; retry++) {
eth_transmit(broadcast, ARP, sizeof(arpreq),
&arpreq);
if (await_reply(AWAIT_ARP, arpentry,
arpreq.tipaddr, TIMEOUT)) goto xmit;
rfc951_sleep(retry);
/* We have slept for a while - the packet may
* have arrived by now. If not, we have at
* least some room in the Rx buffer for the
* next reply. */
if (await_reply(AWAIT_ARP, arpentry,
arpreq.tipaddr, 0)) goto xmit;
}
return(0);
}
xmit:
eth_transmit(arptable[arpentry].node, IP, len, buf);
}
return(1);
}
/**************************************************************************
TFTP - Download extended BOOTP data, or kernel image
**************************************************************************/
int tftp(const char *name, int (*fnc)(unsigned char *, int, int, int))
{
int retry = 0;
static unsigned short iport = 2000;
unsigned short oport;
unsigned short len, block = 0, prevblock = 0;
int bcounter = 0;
struct tftp_t *tr;
struct tftpreq_t tp;
int rc;
int packetsize = TFTP_DEFAULTSIZE_PACKET;
/* Clear out the Rx queue first. It contains nothing of interest,
* except possibly ARP requests from the DHCP/TFTP server. We use
* polling throughout Etherboot, so some time may have passed since we
* last polled the receive queue, which may now be filled with
* broadcast packets. This will cause the reply to the packets we are
* about to send to be lost immediately. Not very clever. */
await_reply(AWAIT_QDRAIN, 0, NULL, 0);
tp.opcode = htons(TFTP_RRQ);
/* 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) +
sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
name, 0, 0, 0, TFTP_MAX_PACKET) + 1;
if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
TFTP_PORT, len, &tp))
return (0);
for (;;)
{
#ifdef CONGESTED
if (!await_reply(AWAIT_TFTP, iport, NULL, (block ? TFTP_REXMT : TIMEOUT)))
#else
if (!await_reply(AWAIT_TFTP, iport, NULL, TIMEOUT))
#endif
{
if (!block && retry++ < MAX_TFTP_RETRIES)
{ /* maybe initial request was lost */
rfc951_sleep(retry);
if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
++iport, TFTP_PORT, len, &tp))
return (0);
continue;
}
#ifdef CONGESTED
if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
{ /* we resend our last ack */
#ifdef MDEBUG
printk_info("<REXMT>\n");
#endif
udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
iport, oport,
TFTP_MIN_PACKET, &tp);
continue;
}
#endif
break; /* timeout */
}
tr = (struct tftp_t *)&nic.packet[ETH_HLEN];
if (tr->opcode == ntohs(TFTP_ERROR))
{
printk_info("TFTP error %d (%s)\n",
ntohs(tr->u.err.errcode),
tr->u.err.errmsg);
break;
}
if (tr->opcode == ntohs(TFTP_OACK)) {
char *p = tr->u.oack.data, *e;
if (prevblock) /* shouldn't happen */
continue; /* ignore it */
len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
if (len > TFTP_MAX_PACKET)
goto noak;
e = p + len;
while (*p != '\000' && p < e) {
if (!strcasecmp("blksize", p)) {
p += 8;
if ((packetsize = getdec(&p)) <
TFTP_DEFAULTSIZE_PACKET)
goto noak;
while (p < e && *p) p++;
if (p < e)
p++;
}
else {
noak:
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) +
sprintf((char *)tp.u.err.errmsg,
"RFC1782 error") + 1;
udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
iport, ntohs(tr->udp.src),
len, &tp);
return (0);
}
}
if (p > e)
goto noak;
block = tp.u.ack.block = 0; /* this ensures, that */
/* the packet does not get */
/* processed as data! */
}
else if (tr->opcode == htons(TFTP_DATA)) {
len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
if (len > packetsize) /* shouldn't happen */
continue; /* ignore it */
block = ntohs(tp.u.ack.block = tr->u.data.block); }
else /* neither TFTP_OACK nor TFTP_DATA */
break;
if ((block || bcounter) && (block != prevblock+1)) {
/* Block order should be continuous */
tp.u.ack.block = htons(block = prevblock);
}
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 ((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? */
if ((rc = fnc(tr->u.data.download,
++bcounter, len, len < packetsize)) >= 0)
return(rc);
if (len < packetsize) /* End of data */
return (1);
}
return (0);
}
int retry = 0;
static unsigned short iport = 2000;
unsigned short oport;
unsigned short len, block = 0, prevblock = 0;
int bcounter = 0;
struct tftp_t *tr;
struct tftpreq_t tp;
int rc;
int packetsize = TFTP_DEFAULTSIZE_PACKET;
int tftp_init(const char *name)
{
retry = 0;
iport = 2000;
block = 0;
prevblock = 0;
bcounter = 0;
packetsize = TFTP_DEFAULTSIZE_PACKET;
/* Clear out the Rx queue first. It contains nothing of interest,
* except possibly ARP requests from the DHCP/TFTP server. We use
* polling throughout Etherboot, so some time may have passed since we
* last polled the receive queue, which may now be filled with
* broadcast packets. This will cause the reply to the packets we are
* about to send to be lost immediately. Not very clever. */
await_reply(AWAIT_QDRAIN, 0, NULL, 0);
tp.opcode = htons(TFTP_RRQ);
/* 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) +
sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
name, 0, 0, 0, TFTP_MAX_PACKET) + 1;
if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
TFTP_PORT, len, &tp))
return (-1);
#ifdef CONGESTED
while (!await_reply(AWAIT_TFTP, iport, NULL, (block ? TFTP_REXMT : TIMEOUT)))
#else
while (!await_reply(AWAIT_TFTP, iport, NULL, TIMEOUT))
#endif
{
if (!block && retry++ < MAX_TFTP_RETRIES)
{ /* maybe initial request was lost */
rfc951_sleep(retry);
if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
++iport, TFTP_PORT, len, &tp))
return (-1);
continue;
}
#ifdef CONGESTED
if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
{ /* we resend our last ack */
#ifdef MDEBUG
printk_info("<REXMT>\n");
#endif
udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
iport, oport,
TFTP_MIN_PACKET, &tp);
continue;
}
#endif
break; /* timeout */
}
// we have recieved reply. see if it's any good.
tr = (struct tftp_t *)&nic.packet[ETH_HLEN];
if (tr->opcode == ntohs(TFTP_ERROR)) {
printk_info("TFTP error %d (%s)\n",
ntohs(tr->u.err.errcode),
tr->u.err.errmsg);
return(-ntohs(tr->u.err.errcode));
}
if (tr->opcode == ntohs(TFTP_OACK)) {
char *p = tr->u.oack.data, *e;
if (prevblock) /* shouldn't happen */
return(-1); /* ignore it */
len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
if (len > TFTP_MAX_PACKET)
goto noak;
e = p + len;
while (*p != '\000' && p < e) {
if (!strcasecmp("blksize", p)) {
p += 8;
if ((packetsize = getdec(&p)) <
TFTP_DEFAULTSIZE_PACKET)
goto noak;
while (p < e && *p) p++;
if (p < e)
p++;
}
else {
noak:
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) +
sprintf((char *)tp.u.err.errmsg,
"RFC1782 error") + 1;
udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
iport, ntohs(tr->udp.src),
len, &tp);
return (-1);
}
}
if (p > e)
goto noak;
block = tp.u.ack.block = 0; /* this ensures, that */
/* the packet does not get */
/* processed as data! */
}
return(0);
}
int tftp_fetchone(char *buffer)
{
/* Clear out the Rx queue first. It contains nothing of interest,
* except possibly ARP requests from the DHCP/TFTP server. We use
* polling throughout Etherboot, so some time may have passed since we
* last polled the receive queue, which may now be filled with
* broadcast packets. This will cause the reply to the packets we are
* about to send to be lost immediately. Not very clever. */
retry:
await_reply(AWAIT_QDRAIN, 0, NULL, 0);
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 */
#ifdef CONGESTED
while (!await_reply(AWAIT_TFTP, iport, NULL, (block ? TFTP_REXMT : TIMEOUT)))
#else
while (!await_reply(AWAIT_TFTP, iport, NULL, TIMEOUT))
#endif
{
if (!block && retry++ < MAX_TFTP_RETRIES)
{ /* maybe initial request was lost */
rfc951_sleep(retry);
if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
++iport, TFTP_PORT, len, &tp))
return (-1);
continue;
}
#ifdef CONGESTED
if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
{ /* we resend our last ack */
#ifdef MDEBUG
printk_info("<REXMT>\n");
#endif
udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
iport, oport,
TFTP_MIN_PACKET, &tp);
continue;
}
#endif
break; /* timeout */
}
tr = (struct tftp_t *)&nic.packet[ETH_HLEN];
if (tr->opcode == ntohs(TFTP_ERROR))
{
printk_info("TFTP error %d (%s)\n",
ntohs(tr->u.err.errcode),
tr->u.err.errmsg);
return(-tr->u.err.errcode);
}
if (tr->opcode == ntohs(TFTP_OACK))
goto retry; // session is already started! try again.
else if (tr->opcode == htons(TFTP_DATA)) {
len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
if (len > packetsize) /* shouldn't happen */
goto retry; /* bad thing, skip it and try again! */
block = ntohs(tp.u.ack.block = tr->u.data.block);
} else /* neither TFTP_OACK nor TFTP_DATA */
goto retry; // whatever it is, it's invalid! try again.
if ((block || bcounter) && (block != prevblock+1)) {
/* Block order should be continuous */
tp.u.ack.block = htons(block = prevblock);
goto retry;
}
prevblock = block;
retry = 0; /* It's the right place to zero the timer? */
memcpy(buffer,tr->u.data.download, len);
return(len);
}