diff options
Diffstat (limited to 'contrib/baremetal/main.c')
-rw-r--r-- | contrib/baremetal/main.c | 1119 |
1 files changed, 1119 insertions, 0 deletions
diff --git a/contrib/baremetal/main.c b/contrib/baremetal/main.c new file mode 100644 index 00000000..7b0de44c --- /dev/null +++ b/contrib/baremetal/main.c @@ -0,0 +1,1119 @@ +/************************************************************************** +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: + */ |