mirror of
https://github.com/xcat2/xNBA.git
synced 2024-11-22 17:41:55 +00:00
1120 lines
31 KiB
C
1120 lines
31 KiB
C
/**************************************************************************
|
|
ETHERBOOT - BOOTP/TFTP Bootstrap Program
|
|
|
|
Author: Martin Renters
|
|
Date: Dec/93
|
|
|
|
Literature dealing with the network protocols:
|
|
ARP - RFC826
|
|
RARP - RFC903
|
|
UDP - RFC768
|
|
BOOTP - RFC951, RFC2132 (vendor extensions)
|
|
DHCP - RFC2131, RFC2132 (options)
|
|
TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize)
|
|
RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper)
|
|
NFS - RFC1094, RFC1813 (v3, useful for clarifications, not implemented)
|
|
|
|
**************************************************************************/
|
|
|
|
/* #define MDEBUG */
|
|
|
|
#include "etherboot.h"
|
|
#include "nic.h"
|
|
|
|
int jmp_bootmenu[10];
|
|
|
|
struct arptable_t arptable[MAX_ARP];
|
|
|
|
const char *kernel;
|
|
char kernel_buf[128];
|
|
struct rom_info rom;
|
|
|
|
#ifdef IMAGE_MENU
|
|
static char *imagelist[RFC1533_VENDOR_NUMOFIMG];
|
|
static int useimagemenu;
|
|
int menutmo,menudefault;
|
|
unsigned char *defparams = NULL;
|
|
int defparams_max = 0;
|
|
#endif
|
|
#ifdef MOTD
|
|
char *motd[RFC1533_VENDOR_NUMOFMOTD];
|
|
#endif
|
|
#ifdef IMAGE_FREEBSD
|
|
int freebsd_howto = 0;
|
|
#endif
|
|
int vendorext_isvalid;
|
|
char config_buffer[TFTP_MAX_PACKET+1]; /* +1 for null byte */
|
|
unsigned long netmask;
|
|
char *hostname = "";
|
|
int hostnamelen = 0;
|
|
#if defined(ETHERBOOT16) || defined(INTERNAL_BOOTP_DATA)
|
|
struct bootpd_t bootp_data;
|
|
#endif
|
|
unsigned long xid;
|
|
unsigned char *end_of_rfc1533 = NULL;
|
|
#ifndef NO_DHCP_SUPPORT
|
|
int dhcp_reply;
|
|
in_addr dhcp_server = { 0L };
|
|
in_addr dhcp_addr = { 0L };
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
|
|
unsigned char vendorext_magic[] = {0xE4,0x45,0x74,0x68}; /* äEth */
|
|
#ifdef NO_DHCP_SUPPORT
|
|
char rfc1533_cookie[5] = { RFC1533_COOKIE, RFC1533_END };
|
|
#else
|
|
char rfc1533_cookie[] = { RFC1533_COOKIE};
|
|
char rfc1533_end[]={RFC1533_END };
|
|
static const char dhcpdiscover[]={
|
|
RFC2132_MSG_TYPE,1,DHCPDISCOVER,
|
|
RFC2132_MAX_SIZE,2, /* request as much as we can */
|
|
sizeof(struct bootpd_t) / 256, sizeof(struct bootpd_t) % 256,
|
|
RFC2132_PARAM_LIST,4,RFC1533_NETMASK,RFC1533_GATEWAY,
|
|
RFC1533_HOSTNAME
|
|
};
|
|
static const char dhcprequest []={
|
|
RFC2132_MSG_TYPE,1,DHCPREQUEST,
|
|
RFC2132_SRV_ID,4,0,0,0,0,
|
|
RFC2132_REQ_ADDR,4,0,0,0,0,
|
|
RFC2132_MAX_SIZE,2, /* request as much as we can */
|
|
sizeof(struct bootpd_t) / 256, sizeof(struct bootpd_t) % 256,
|
|
/* request parameters */
|
|
RFC2132_PARAM_LIST,
|
|
#ifdef IMAGE_FREEBSD
|
|
/* 4 standard + 6 vendortags + 8 motd + 16 menu items */
|
|
4 + 6 + 8 + 16,
|
|
#else
|
|
/* 4 standard + 5 vendortags + 8 motd + 16 menu items */
|
|
4 + 5 + 8 + 16,
|
|
#endif
|
|
/* Standard parameters */
|
|
RFC1533_NETMASK, RFC1533_GATEWAY,
|
|
RFC1533_HOSTNAME,
|
|
RFC1533_ROOTPATH, /* only passed to the booted image */
|
|
/* Etherboot vendortags */
|
|
RFC1533_VENDOR_MAGIC,
|
|
RFC1533_VENDOR_ADDPARM,
|
|
RFC1533_VENDOR_ETHDEV,
|
|
#ifdef IMAGE_FREEBSD
|
|
RFC1533_VENDOR_HOWTO,
|
|
#endif
|
|
RFC1533_VENDOR_MNUOPTS, RFC1533_VENDOR_SELECTION,
|
|
/* 8 MOTD entries */
|
|
RFC1533_VENDOR_MOTD,
|
|
RFC1533_VENDOR_MOTD+1,
|
|
RFC1533_VENDOR_MOTD+2,
|
|
RFC1533_VENDOR_MOTD+3,
|
|
RFC1533_VENDOR_MOTD+4,
|
|
RFC1533_VENDOR_MOTD+5,
|
|
RFC1533_VENDOR_MOTD+6,
|
|
RFC1533_VENDOR_MOTD+7,
|
|
/* 16 image entries */
|
|
RFC1533_VENDOR_IMG,
|
|
RFC1533_VENDOR_IMG+1,
|
|
RFC1533_VENDOR_IMG+2,
|
|
RFC1533_VENDOR_IMG+3,
|
|
RFC1533_VENDOR_IMG+4,
|
|
RFC1533_VENDOR_IMG+5,
|
|
RFC1533_VENDOR_IMG+6,
|
|
RFC1533_VENDOR_IMG+7,
|
|
RFC1533_VENDOR_IMG+8,
|
|
RFC1533_VENDOR_IMG+9,
|
|
RFC1533_VENDOR_IMG+10,
|
|
RFC1533_VENDOR_IMG+11,
|
|
RFC1533_VENDOR_IMG+12,
|
|
RFC1533_VENDOR_IMG+13,
|
|
RFC1533_VENDOR_IMG+14,
|
|
RFC1533_VENDOR_IMG+15,
|
|
};
|
|
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
static const char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
|
|
|
/**************************************************************************
|
|
MAIN - Kick off routine
|
|
**************************************************************************/
|
|
int main(void)
|
|
{
|
|
char *p;
|
|
static int card_retries = 0;
|
|
int i;
|
|
|
|
for (p=_edata; p<_end; p++)
|
|
*p = 0; /* Zero BSS */
|
|
|
|
#ifdef CONSOLE_SERIAL
|
|
(void)serial_init();
|
|
#endif
|
|
|
|
#ifdef DELIMITERLINES
|
|
for (i=0; i<80; i++) putchar('=');
|
|
#endif
|
|
|
|
#ifdef ETHERBOOT32
|
|
rom = *(struct rom_info *)ROM_INFO_LOCATION;
|
|
printf("ROM segment %#x length %#x reloc %#x\n", rom.rom_segment,
|
|
rom.rom_length << 1, ((unsigned long)_start) >> 4);
|
|
#endif
|
|
#ifdef ETHERBOOT16
|
|
fmemcpy(&rom, (Address)ROM_INFO_LOCATION, sizeof(rom));
|
|
printf("ROM segment %#x length %#x\n", rom.rom_segment,
|
|
rom.rom_length << 1);
|
|
#endif
|
|
#ifdef ASK_BOOT
|
|
while (1) {
|
|
int c;
|
|
unsigned long time;
|
|
printf(ASK_PROMPT);
|
|
#if ASK_BOOT > 0
|
|
for (time = currticks() + ASK_BOOT*TICKS_PER_SEC; !iskey(); )
|
|
if (currticks() > time) {
|
|
c = ANS_DEFAULT;
|
|
goto done;
|
|
}
|
|
#endif
|
|
c = getchar();
|
|
if ((c >= 'a') && (c <= 'z')) c &= 0x5F;
|
|
if (c == '\n') c = ANS_DEFAULT;
|
|
done:
|
|
if ((c >= ' ') && (c <= '~')) putchar(c);
|
|
putchar('\n');
|
|
if (c == ANS_LOCAL)
|
|
exit(0);
|
|
if (c == ANS_NETWORK)
|
|
break;
|
|
}
|
|
#endif
|
|
#if (TRY_FLOPPY_FIRST > 0) && defined(FLOPPY)
|
|
disk_init();
|
|
printf("Trying floppy");
|
|
for (i = TRY_FLOPPY_FIRST; i-- > 0; ) {
|
|
putchar('.');
|
|
if (disk_read(0, 0, 0, 0, ((char *) FLOPPY_BOOT_LOCATION)) != 0x8000) {
|
|
printf("using floppy\n");
|
|
exit(0);
|
|
}
|
|
}
|
|
printf("no floppy\n");
|
|
#endif /* TRY_FLOPPY_FIRST && FLOPPY */
|
|
print_config();
|
|
gateA20_set();
|
|
#ifdef EMERGENCYDISKBOOT
|
|
if (!eth_probe()) {
|
|
printf("No adapter found\n");
|
|
exit(0);
|
|
}
|
|
#else
|
|
while (!eth_probe()) {
|
|
printf("No adapter found");
|
|
if (!setjmp(jmp_bootmenu))
|
|
rfc951_sleep(++card_retries);
|
|
}
|
|
#endif
|
|
kernel = DEFAULT_BOOTFILE;
|
|
while (1) {
|
|
if ((i = setjmp(jmp_bootmenu)) != 0) {
|
|
#if defined(ANSIESC) && defined(CONSOLE_CRT)
|
|
ansi_reset();
|
|
#endif
|
|
bootmenu(--i);
|
|
} else {
|
|
load();
|
|
}
|
|
#if defined(ANSIESC) && defined(CONSOLE_CRT)
|
|
ansi_reset();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/**************************************************************************
|
|
LOADKERNEL - Try to load kernel image
|
|
**************************************************************************/
|
|
#ifndef FLOPPY
|
|
#define loadkernel(s) download((s),downloadkernel)
|
|
#else
|
|
static int loadkernel(const char *fname)
|
|
{
|
|
if (!memcmp(fname,"/dev/",5) && fname[6] == 'd') {
|
|
int dev, part = 0;
|
|
if (fname[5] == 'f') {
|
|
if ((dev = fname[7] - '0') < 0 || dev > 3)
|
|
goto nodisk; }
|
|
else if (fname[5] == 'h' || fname[5] == 's') {
|
|
if ((dev = 0x80 + fname[7] - 'a') < 0x80 || dev > 0x83)
|
|
goto nodisk;
|
|
if (fname[8]) {
|
|
part = fname[8] - '0';
|
|
if (fname[9])
|
|
part = 10*part + fname[9] - '0'; }
|
|
/* bootdisk cannot cope with more than eight partitions */
|
|
if (part < 0 || part > 8)
|
|
goto nodisk; }
|
|
else
|
|
goto nodisk;
|
|
return(bootdisk(dev,part)); }
|
|
nodisk:
|
|
return download(fname, downloadkernel);
|
|
}
|
|
#endif
|
|
|
|
/**************************************************************************
|
|
LOAD - Try to get booted
|
|
**************************************************************************/
|
|
void load()
|
|
{
|
|
static int bootp_completed = 0;
|
|
|
|
/* Find a server to get BOOTP reply from */
|
|
if (!bootp_completed ||
|
|
!arptable[ARP_CLIENT].ipaddr.s_addr || !arptable[ARP_SERVER].ipaddr.s_addr) {
|
|
retry:
|
|
bootp_completed = 0;
|
|
#ifdef RARP_NOT_BOOTP
|
|
printf("Searching for server (RARP)...\n");
|
|
#else
|
|
#ifndef NO_DHCP_SUPPORT
|
|
printf("Searching for server (DHCP)...\n");
|
|
#else
|
|
printf("Searching for server (BOOTP)...\n");
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef RARP_NOT_BOOTP
|
|
if (!rarp()) {
|
|
#else
|
|
if (!bootp()) {
|
|
#endif
|
|
printf("No Server found\n");
|
|
#ifdef EMERGENCYDISKBOOT
|
|
exit(0);
|
|
#else
|
|
goto retry;
|
|
#endif
|
|
}
|
|
bootp_completed++;
|
|
}
|
|
printf("Me: %I, Server: %I",
|
|
arptable[ARP_CLIENT].ipaddr.s_addr,
|
|
arptable[ARP_SERVER].ipaddr.s_addr);
|
|
if (BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr)
|
|
printf(", Relay: %I",
|
|
BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr);
|
|
if (arptable[ARP_GATEWAY].ipaddr.s_addr)
|
|
printf(", Gateway %I", arptable[ARP_GATEWAY].ipaddr.s_addr);
|
|
putchar('\n');
|
|
|
|
#ifdef MDEBUG
|
|
printf("\n=>>"); getchar();
|
|
#endif
|
|
|
|
#ifdef MOTD
|
|
if (vendorext_isvalid)
|
|
show_motd();
|
|
#endif
|
|
/* Now use TFTP to load file */
|
|
#ifdef IMAGE_MENU
|
|
if (vendorext_isvalid && useimagemenu) {
|
|
selectImage(imagelist);
|
|
bootp_completed = 0;
|
|
}
|
|
#endif
|
|
#ifdef DOWNLOAD_PROTO_NFS
|
|
rpc_init();
|
|
#endif
|
|
for (;;) {
|
|
printf("Loading %s ",kernel);
|
|
while (!loadkernel(kernel)) {
|
|
printf("Unable to load file.\n");
|
|
sleep(2); /* lay off server for a while */
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************************************
|
|
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));
|
|
}
|
|
|
|
/**************************************************************************
|
|
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 *)((long)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) {
|
|
printf("%I is not in my arp table!\n", destip);
|
|
return(0);
|
|
}
|
|
for (i = 0; i<ETHER_ADDR_SIZE; i++)
|
|
if (arptable[arpentry].node[i]) break;
|
|
if (i == ETHER_ADDR_SIZE) { /* Need to do arp request */
|
|
arpreq.hwtype = htons(1);
|
|
arpreq.protocol = htons(IP);
|
|
arpreq.hwlen = ETHER_ADDR_SIZE;
|
|
arpreq.protolen = 4;
|
|
arpreq.opcode = htons(ARP_REQUEST);
|
|
memcpy(arpreq.shwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
|
|
memcpy(arpreq.sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
|
|
memset(arpreq.thwaddr, 0, ETHER_ADDR_SIZE);
|
|
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);
|
|
}
|
|
|
|
/**************************************************************************
|
|
DOWNLOADKERNEL - Try to load file
|
|
**************************************************************************/
|
|
int downloadkernel(data, block, len, eof)
|
|
unsigned char *data;
|
|
int block, len, eof;
|
|
{
|
|
#ifdef SIZEINDICATOR
|
|
static int rlen = 0;
|
|
|
|
if (!(block % 4) || eof) {
|
|
int size;
|
|
size = ((block-1) * rlen + len) / 1024;
|
|
|
|
putchar('\b');
|
|
putchar('\b');
|
|
putchar('\b');
|
|
putchar('\b');
|
|
|
|
putchar('0' + (size/1000)%10);
|
|
putchar('0' + (size/100)%10);
|
|
putchar('0' + (size/10)%10);
|
|
putchar('0' + (size/1)%10);
|
|
}
|
|
#endif
|
|
if (block == 1)
|
|
{
|
|
#ifdef SIZEINDICATOR
|
|
rlen=len;
|
|
#endif
|
|
if (!eof && (
|
|
#ifdef TAGGED_IMAGE
|
|
*((unsigned long *)data) == 0x1B031336L ||
|
|
#endif
|
|
#ifdef ELF_IMAGE
|
|
*((unsigned long *)data) == 0x464C457FL ||
|
|
#endif
|
|
#ifdef AOUT_IMAGE
|
|
*((unsigned short *)data) == 0x010BL ||
|
|
#endif
|
|
((unsigned short *)data)[255] == 0xAA55))
|
|
{
|
|
;
|
|
}
|
|
else if (eof)
|
|
{
|
|
memcpy(config_buffer, data, len);
|
|
config_buffer[len] = 0;
|
|
return (1); /* done */
|
|
}
|
|
else
|
|
{
|
|
printf("error: not a tagged image\n");
|
|
return(0); /* error */
|
|
}
|
|
}
|
|
if (len != 0) {
|
|
if (!os_download(block, data, len))
|
|
return(0); /* error */
|
|
}
|
|
if (eof) {
|
|
os_download(block+1, data, 0); /* does not return */
|
|
return(0); /* error */
|
|
}
|
|
return(-1); /* there is more data */
|
|
}
|
|
|
|
#ifdef DOWNLOAD_PROTO_TFTP
|
|
/**************************************************************************
|
|
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 tftp_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);
|
|
len = (sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
|
|
name, 0, 0, 0, TFTP_MAX_PACKET) - ((char *)&tp)) + 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
|
|
printf("<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[ETHER_HDR_SIZE];
|
|
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)) {
|
|
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;
|
|
len = (sprintf((char *)tp.u.err.errmsg,
|
|
"RFC1782 error")
|
|
- ((char *)&tp)) + 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 == ntohs(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);
|
|
}
|
|
#endif /* DOWNLOAD_PROTO_TFTP */
|
|
|
|
#ifdef RARP_NOT_BOOTP
|
|
/**************************************************************************
|
|
RARP - Get my IP address and load information
|
|
**************************************************************************/
|
|
int rarp()
|
|
{
|
|
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 = ETHER_ADDR_SIZE;
|
|
rarpreq.protolen = 4;
|
|
rarpreq.opcode = htons(RARP_REQUEST);
|
|
memcpy(&rarpreq.shwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
|
|
/* sipaddr is already zeroed out */
|
|
memcpy(&rarpreq.thwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
|
|
/* 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) {
|
|
sprintf(kernel = kernel_buf, "/tftpboot/kernel.%I", arptable[ARP_CLIENT].ipaddr);
|
|
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
#else
|
|
|
|
/**************************************************************************
|
|
BOOTP - Get my IP address and load information
|
|
**************************************************************************/
|
|
int bootp()
|
|
{
|
|
int retry;
|
|
#ifndef NO_DHCP_SUPPORT
|
|
int retry1;
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
struct bootp_t bp;
|
|
unsigned long starttime;
|
|
#ifdef T509HACK
|
|
int flag;
|
|
|
|
flag = 1;
|
|
#endif
|
|
memset(&bp, 0, sizeof(struct bootp_t));
|
|
bp.bp_op = BOOTP_REQUEST;
|
|
bp.bp_htype = 1;
|
|
bp.bp_hlen = ETHER_ADDR_SIZE;
|
|
bp.bp_xid = xid = starttime = currticks();
|
|
memcpy(bp.bp_hwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
|
|
#ifdef NO_DHCP_SUPPORT
|
|
memcpy(bp.bp_vend, rfc1533_cookie, 5); /* request RFC-style options */
|
|
#else
|
|
memcpy(bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); /* request RFC-style options */
|
|
memcpy(bp.bp_vend+sizeof rfc1533_cookie, dhcpdiscover, sizeof dhcpdiscover);
|
|
memcpy(bp.bp_vend+sizeof rfc1533_cookie +sizeof dhcpdiscover, rfc1533_end, sizeof rfc1533_end);
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
|
|
for (retry = 0; retry < MAX_BOOTP_RETRIES; ) {
|
|
|
|
/* 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);
|
|
|
|
udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER,
|
|
sizeof(struct bootp_t), &bp);
|
|
#ifdef T509HACK
|
|
if (flag) {
|
|
flag--;
|
|
} else {
|
|
if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT))
|
|
return(1);
|
|
rfc951_sleep(++retry);
|
|
|
|
}
|
|
#else
|
|
#ifdef NO_DHCP_SUPPORT
|
|
if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT))
|
|
#else
|
|
if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT)){
|
|
if (dhcp_reply==DHCPOFFER){
|
|
dhcp_reply=0;
|
|
memcpy(bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie);
|
|
memcpy(bp.bp_vend+sizeof rfc1533_cookie, dhcprequest, sizeof dhcprequest);
|
|
memcpy(bp.bp_vend+sizeof rfc1533_cookie +sizeof dhcprequest, rfc1533_end, sizeof rfc1533_end);
|
|
memcpy(bp.bp_vend+9, &dhcp_server, sizeof(in_addr));
|
|
memcpy(bp.bp_vend+15, &dhcp_addr, sizeof(in_addr));
|
|
for (retry1 = 0; retry1 < MAX_BOOTP_RETRIES;) {
|
|
udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER,
|
|
sizeof(struct bootp_t), &bp);
|
|
dhcp_reply=0;
|
|
if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT))
|
|
if (dhcp_reply==DHCPACK)
|
|
return(1);
|
|
rfc951_sleep(++retry1);
|
|
}
|
|
} else
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
return(1);
|
|
#ifndef NO_DHCP_SUPPORT
|
|
}
|
|
rfc951_sleep(++retry);
|
|
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
#endif
|
|
bp.bp_secs = htons((currticks()-starttime)/20);
|
|
}
|
|
return(0);
|
|
}
|
|
#endif /* RARP_NOT_BOOTP */
|
|
|
|
/**************************************************************************
|
|
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 = ETHER_HDR_SIZE + 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 >= ETHER_HDR_SIZE) {
|
|
ptype = ((unsigned short) nic.packet[12]) << 8
|
|
| ((unsigned short) nic.packet[13]);
|
|
} else continue; /* what else could we do with it? */
|
|
if ((nic.packetlen >= ETHER_HDR_SIZE +
|
|
sizeof(struct arprequest)) &&
|
|
(ptype == ARP) ) {
|
|
unsigned long tmp;
|
|
|
|
arpreply = (struct arprequest *)
|
|
&nic.packet[ETHER_HDR_SIZE];
|
|
if ((arpreply->opcode == ntohs(ARP_REPLY)) &&
|
|
!memcmp(arpreply->sipaddr, ptr, sizeof(in_addr)) &&
|
|
(type == AWAIT_ARP)) {
|
|
memcpy(arptable[ival].node, arpreply->shwaddr, ETHER_ADDR_SIZE);
|
|
return(1);
|
|
}
|
|
memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
|
|
if ((arpreply->opcode == ntohs(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, ETHER_ADDR_SIZE);
|
|
memcpy(arpreply->sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
|
|
memcpy(arpreply->shwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
|
|
eth_transmit(arpreply->thwaddr, ARP,
|
|
sizeof(struct arprequest),
|
|
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 >= ETHER_HDR_SIZE +
|
|
sizeof(struct arprequest)) &&
|
|
(ptype == RARP)) {
|
|
arpreply = (struct arprequest *)
|
|
&nic.packet[ETHER_HDR_SIZE];
|
|
if ((arpreply->opcode == ntohs(RARP_REPLY)) &&
|
|
!memcmp(arpreply->thwaddr, ptr, ETHER_ADDR_SIZE)) {
|
|
memcpy(arptable[ARP_SERVER].node, arpreply->shwaddr, ETHER_ADDR_SIZE);
|
|
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[ETHER_HDR_SIZE];
|
|
if ((ip->verhdrlen != 0x45) ||
|
|
ipchksum((unsigned short *)ip, sizeof(struct iphdr)) ||
|
|
(ip->protocol != IP_UDP)) continue;
|
|
udp = (struct udphdr *)&nic.packet[ETHER_HDR_SIZE +
|
|
sizeof(struct iphdr)];
|
|
|
|
/* BOOTP ? */
|
|
bootpreply = (struct bootp_t *)&nic.packet[ETHER_HDR_SIZE];
|
|
if ((type == AWAIT_BOOTP) &&
|
|
(nic.packetlen >= (ETHER_HDR_SIZE +
|
|
#ifdef NO_DHCP_SUPPORT
|
|
sizeof(struct bootp_t))) &&
|
|
#else
|
|
sizeof(struct bootp_t))-DHCP_OPT_LEN) &&
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
(ntohs(udp->dest) == BOOTP_CLIENT) &&
|
|
(bootpreply->bp_op == BOOTP_REPLY) &&
|
|
(bootpreply->bp_xid == xid)) {
|
|
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, ETHER_ADDR_SIZE); /* Kill arp */
|
|
arptable[ARP_GATEWAY].ipaddr.s_addr =
|
|
bootpreply->bp_giaddr.s_addr;
|
|
memset(arptable[ARP_GATEWAY].node, 0, ETHER_ADDR_SIZE); /* Kill arp */
|
|
if (bootpreply->bp_file[0]) {
|
|
memcpy(kernel_buf, bootpreply->bp_file, 128);
|
|
kernel = kernel_buf;
|
|
}
|
|
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 */
|
|
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[ETHER_HDR_SIZE];
|
|
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
|
|
exit(0);
|
|
#else
|
|
longjmp(jmp_bootmenu,1);
|
|
#endif
|
|
/* Do the timeout after at least a full queue walk. */
|
|
if ((timeout == 0) || (currticks() > time)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**************************************************************************
|
|
DECODE_RFC1533 - Decodes RFC1533 header
|
|
**************************************************************************/
|
|
int decode_rfc1533(p, block, len, eof)
|
|
register unsigned char *p;
|
|
int block, len, eof;
|
|
{
|
|
static unsigned char *extdata = NULL, *extend = NULL;
|
|
unsigned char *extpath = NULL;
|
|
unsigned char *endp;
|
|
|
|
if (block == 0) {
|
|
#ifdef IMAGE_MENU
|
|
memset(imagelist, 0, sizeof(imagelist));
|
|
menudefault = useimagemenu = 0;
|
|
menutmo = -1;
|
|
#endif
|
|
#ifdef MOTD
|
|
memset(motd, 0, sizeof(motd));
|
|
#endif
|
|
end_of_rfc1533 = NULL;
|
|
vendorext_isvalid = 0;
|
|
if (memcmp(p, rfc1533_cookie, 4))
|
|
return(0); /* no RFC 1533 header found */
|
|
p += 4;
|
|
endp = p + len; }
|
|
else {
|
|
if (block == 1) {
|
|
if (memcmp(p, rfc1533_cookie, 4))
|
|
return(0); /* no RFC 1533 header found */
|
|
p += 4;
|
|
len -= 4; }
|
|
if (extend + len <= (unsigned char *)&(BOOTP_DATA_ADDR->bootp_extension[MAX_BOOTP_EXTLEN])) {
|
|
memcpy(extend, p, len);
|
|
extend += len;
|
|
} else {
|
|
printf("Overflow in vendor data buffer! Aborting...\n");
|
|
*extdata = RFC1533_END;
|
|
return(0);
|
|
}
|
|
p = extdata; endp = extend;
|
|
}
|
|
if (eof) {
|
|
while(p < endp) {
|
|
unsigned char c = *p;
|
|
if (c == RFC1533_PAD) {p++; continue;}
|
|
else if (c == RFC1533_END) {
|
|
end_of_rfc1533 = endp = p; continue; }
|
|
else if (c == RFC1533_NETMASK) {memcpy(&netmask, p+2, sizeof(in_addr));}
|
|
|
|
else if (c == RFC1533_GATEWAY) {
|
|
/* This is a little simplistic, but it will
|
|
usually be sufficient.
|
|
Take only the first entry */
|
|
if (TAG_LEN(p) >= sizeof(in_addr))
|
|
memcpy(&arptable[ARP_GATEWAY].ipaddr, p+2, sizeof(in_addr));
|
|
}
|
|
else if (c == RFC1533_EXTENSIONPATH)
|
|
extpath = p;
|
|
#ifndef NO_DHCP_SUPPORT
|
|
else if (c == RFC2132_MSG_TYPE)
|
|
{ dhcp_reply=*(p+2);
|
|
}
|
|
else if (c == RFC2132_SRV_ID)
|
|
{
|
|
memcpy(&dhcp_server, p+2, sizeof(in_addr));
|
|
}
|
|
#endif /* NO_DHCP_SUPPORT */
|
|
else if (c == RFC1533_HOSTNAME)
|
|
{
|
|
hostname = p + 2;
|
|
hostnamelen = *(p + 1);
|
|
}
|
|
else if (c == RFC1533_VENDOR_MAGIC
|
|
#ifndef IMAGE_FREEBSD /* since FreeBSD uses tag 128 for swap definition */
|
|
&& TAG_LEN(p) >= 6 &&
|
|
!memcmp(p+2,vendorext_magic,4) &&
|
|
p[6] == RFC1533_VENDOR_MAJOR
|
|
#endif
|
|
)
|
|
vendorext_isvalid++;
|
|
#ifdef IMAGE_FREEBSD
|
|
else if (c == RFC1533_VENDOR_HOWTO) {
|
|
freebsd_howto = ((p[2]*256+p[3])*256+p[4])*256+p[5];
|
|
}
|
|
#endif
|
|
#ifdef IMAGE_MENU
|
|
else if (c == RFC1533_VENDOR_MNUOPTS) {
|
|
parse_menuopts(p+2, TAG_LEN(p));
|
|
}
|
|
else if (c >= RFC1533_VENDOR_IMG &&
|
|
c<RFC1533_VENDOR_IMG+RFC1533_VENDOR_NUMOFIMG){
|
|
imagelist[c - RFC1533_VENDOR_IMG] = p;
|
|
useimagemenu++;
|
|
}
|
|
#endif
|
|
#ifdef MOTD
|
|
else if (c >= RFC1533_VENDOR_MOTD &&
|
|
c < RFC1533_VENDOR_MOTD +
|
|
RFC1533_VENDOR_NUMOFMOTD)
|
|
motd[c - RFC1533_VENDOR_MOTD] = p;
|
|
#endif
|
|
else {
|
|
#if 0
|
|
unsigned char *q;
|
|
printf("Unknown RFC1533-tag ");
|
|
for(q=p;q<p+2+TAG_LEN(p);q++)
|
|
printf("%x ",*q);
|
|
putchar('\n');
|
|
#endif
|
|
}
|
|
p += TAG_LEN(p) + 2;
|
|
}
|
|
extdata = extend = endp;
|
|
if (block == 0 && extpath != NULL) {
|
|
char fname[64];
|
|
memcpy(fname, extpath+2, TAG_LEN(extpath));
|
|
fname[(int)TAG_LEN(extpath)] = '\000';
|
|
printf("Loading BOOTP-extension file: %s\n",fname);
|
|
download(fname,decode_rfc1533);
|
|
}
|
|
}
|
|
return(-1); /* proceed with next block */
|
|
}
|
|
|
|
/**************************************************************************
|
|
IPCHKSUM - Checksum IP Header
|
|
**************************************************************************/
|
|
unsigned short ipchksum(ip, len)
|
|
register unsigned short *ip;
|
|
register 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(exp)
|
|
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 */
|
|
printf("<sleep>\n");
|
|
|
|
for (tmo = (tmo&seed)+currticks(); currticks() < tmo; )
|
|
if (iskey() && (getchar() == ESC)) longjmp(jmp_bootmenu,1);
|
|
return;
|
|
}
|
|
|
|
/**************************************************************************
|
|
CLEANUP_NET - shut down networking
|
|
**************************************************************************/
|
|
void cleanup_net(void)
|
|
{
|
|
#ifdef DOWNLOAD_PROTO_NFS
|
|
nfs_umountall(ARP_SERVER);
|
|
#endif
|
|
eth_disable();
|
|
eth_reset();
|
|
}
|
|
|
|
/**************************************************************************
|
|
CLEANUP - shut down etherboot so that the OS may be called right away
|
|
**************************************************************************/
|
|
void cleanup(void)
|
|
{
|
|
#if defined(ANSIESC) && defined(CONSOLE_CRT)
|
|
ansi_reset();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-basic-offset: 8
|
|
* End:
|
|
*/
|