david/ipxe
david
/
ipxe
Archived
1
0
Fork 0
This repository has been archived on 2020-12-06. You can view files and clone it, but cannot push or open issues or pull requests.
ipxe/contrib/baremetal/main.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:
*/