|  | /* | 
|  | *  GRUB  --  GRand Unified Bootloader | 
|  | *  Copyright (C) 2000,2001,2002  Free Software Foundation, Inc. | 
|  | * | 
|  | *  This program is free software; you can redistribute it and/or modify | 
|  | *  it under the terms of the GNU General Public License as published by | 
|  | *  the Free Software Foundation; either version 2 of the License, or | 
|  | *  (at your option) any later version. | 
|  | * | 
|  | *  This program is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | *  GNU General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU General Public License | 
|  | *  along with this program; if not, write to the Free Software | 
|  | *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | */ | 
|  |  | 
|  | /* Based on "src/main.c" in etherboot-5.0.5.  */ | 
|  |  | 
|  | /************************************************************************** | 
|  | 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 GRUB	1 | 
|  | #include <etherboot.h> | 
|  | #include <nic.h> | 
|  |  | 
|  | /* #define DEBUG	1 */ | 
|  |  | 
|  | struct arptable_t arptable[MAX_ARP]; | 
|  |  | 
|  | /* Set if the user pushes Control-C.  */ | 
|  | int ip_abort = 0; | 
|  | /* Set if an ethernet card is probed and IP addresses are set.  */ | 
|  | int network_ready = 0; | 
|  |  | 
|  | struct rom_info rom; | 
|  |  | 
|  | static int vendorext_isvalid; | 
|  | static unsigned long netmask; | 
|  | static struct bootpd_t bootp_data; | 
|  | static unsigned long xid; | 
|  | static unsigned char *end_of_rfc1533 = NULL; | 
|  |  | 
|  | #ifndef	NO_DHCP_SUPPORT | 
|  | #endif /* NO_DHCP_SUPPORT */ | 
|  |  | 
|  | /* äEth */ | 
|  | static unsigned char vendorext_magic[] = {0xE4, 0x45, 0x74, 0x68}; | 
|  | static const unsigned char broadcast[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; | 
|  |  | 
|  | #ifdef	NO_DHCP_SUPPORT | 
|  |  | 
|  | static unsigned char rfc1533_cookie[5] = {RFC1533_COOKIE, RFC1533_END}; | 
|  |  | 
|  | #else /* ! NO_DHCP_SUPPORT */ | 
|  |  | 
|  | static int dhcp_reply; | 
|  | static in_addr dhcp_server = {0L}; | 
|  | static in_addr dhcp_addr = {0L}; | 
|  | static unsigned char rfc1533_cookie[] = {RFC1533_COOKIE}; | 
|  | static unsigned char rfc1533_end[] = {RFC1533_END}; | 
|  |  | 
|  | static const unsigned char dhcpdiscover[] = | 
|  | { | 
|  | RFC2132_MSG_TYPE, 1, DHCPDISCOVER, | 
|  | RFC2132_MAX_SIZE,2,	/* request as much as we can */ | 
|  | ETH_MAX_MTU / 256, ETH_MAX_MTU % 256, | 
|  | RFC2132_PARAM_LIST, 4, RFC1533_NETMASK, RFC1533_GATEWAY, | 
|  | RFC1533_HOSTNAME, RFC1533_EXTENSIONPATH | 
|  | }; | 
|  |  | 
|  | static const unsigned 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 */ | 
|  | ETH_MAX_MTU / 256, ETH_MAX_MTU % 256, | 
|  | /* request parameters */ | 
|  | RFC2132_PARAM_LIST, | 
|  | /* 4 standard + 2 vendortags */ | 
|  | 4 + 2, | 
|  | /* Standard parameters */ | 
|  | RFC1533_NETMASK, RFC1533_GATEWAY, | 
|  | RFC1533_HOSTNAME, RFC1533_EXTENSIONPATH, | 
|  | /* Etherboot vendortags */ | 
|  | RFC1533_VENDOR_MAGIC, | 
|  | RFC1533_VENDOR_CONFIGFILE, | 
|  | }; | 
|  |  | 
|  | #endif /* ! NO_DHCP_SUPPORT */ | 
|  |  | 
|  | static unsigned short ipchksum (unsigned short *ip, int len); | 
|  | static unsigned short udpchksum (struct iphdr *packet); | 
|  |  | 
|  | void | 
|  | print_network_configuration (void) | 
|  | { | 
|  | if (! eth_probe ()) | 
|  | grub_printf ("No ethernet card found.\n"); | 
|  | else if (! network_ready) | 
|  | grub_printf ("Not initialized yet.\n"); | 
|  | else | 
|  | { | 
|  | etherboot_printf ("Address: %@\n", arptable[ARP_CLIENT].ipaddr.s_addr); | 
|  | etherboot_printf ("Netmask: %@\n", netmask); | 
|  | etherboot_printf ("Server: %@\n", arptable[ARP_SERVER].ipaddr.s_addr); | 
|  | etherboot_printf ("Gateway: %@\n", arptable[ARP_GATEWAY].ipaddr.s_addr); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************** | 
|  | 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)); | 
|  | } | 
|  |  | 
|  | /* ifconfig - configure network interface.  */ | 
|  | int | 
|  | ifconfig (char *ip, char *sm, char *gw, char *svr) | 
|  | { | 
|  | in_addr tmp; | 
|  |  | 
|  | if (sm) | 
|  | { | 
|  | if (! inet_aton (sm, &tmp)) | 
|  | return 0; | 
|  |  | 
|  | netmask = tmp.s_addr; | 
|  | } | 
|  |  | 
|  | if (ip) | 
|  | { | 
|  | if (! inet_aton (ip, &arptable[ARP_CLIENT].ipaddr)) | 
|  | return 0; | 
|  |  | 
|  | if (! netmask && ! sm) | 
|  | netmask = default_netmask (); | 
|  | } | 
|  |  | 
|  | if (gw && ! inet_aton (gw, &arptable[ARP_GATEWAY].ipaddr)) | 
|  | return 0; | 
|  |  | 
|  | /* Clear out the ARP entry.  */ | 
|  | grub_memset (arptable[ARP_GATEWAY].node, 0, ETH_ALEN); | 
|  |  | 
|  | if (svr && ! inet_aton (svr, &arptable[ARP_SERVER].ipaddr)) | 
|  | return 0; | 
|  |  | 
|  | /* Likewise.  */ | 
|  | grub_memset (arptable[ARP_SERVER].node, 0, ETH_ALEN); | 
|  |  | 
|  | if (ip || sm) | 
|  | { | 
|  | if (IP_BROADCAST == (netmask | arptable[ARP_CLIENT].ipaddr.s_addr) | 
|  | || netmask == (netmask | arptable[ARP_CLIENT].ipaddr.s_addr) | 
|  | || ! netmask) | 
|  | network_ready = 0; | 
|  | else | 
|  | network_ready = 1; | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************** | 
|  | 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 *) ((unsigned 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; | 
|  | udp->chksum = htons (udpchksum (ip)); | 
|  |  | 
|  | if (udp->chksum == 0) | 
|  | udp->chksum = 0xffff; | 
|  |  | 
|  | 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) | 
|  | { | 
|  | etherboot_printf ("%@ is not in my arp table!\n", destip); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < ETH_ALEN; i++) | 
|  | if (arptable[arpentry].node[i]) | 
|  | break; | 
|  |  | 
|  | if (i == ETH_ALEN) | 
|  | { | 
|  | /* Need to do arp request.  */ | 
|  | #ifdef DEBUG | 
|  | grub_printf ("arp request.\n"); | 
|  | #endif | 
|  | arpreq.hwtype = htons (1); | 
|  | arpreq.protocol = htons (IP); | 
|  | arpreq.hwlen = ETH_ALEN; | 
|  | arpreq.protolen = 4; | 
|  | arpreq.opcode = htons (ARP_REQUEST); | 
|  | grub_memmove (arpreq.shwaddr, arptable[ARP_CLIENT].node, | 
|  | ETH_ALEN); | 
|  | grub_memmove (arpreq.sipaddr, (char *) &arptable[ARP_CLIENT].ipaddr, | 
|  | sizeof (in_addr)); | 
|  | grub_memset (arpreq.thwaddr, 0, ETH_ALEN); | 
|  | grub_memmove (arpreq.tipaddr, (char *) &destip, sizeof (in_addr)); | 
|  |  | 
|  | for (retry = 1; retry <= MAX_ARP_RETRIES; retry++) | 
|  | { | 
|  | long timeout; | 
|  |  | 
|  | eth_transmit (broadcast, ARP, sizeof (arpreq), &arpreq); | 
|  | timeout = rfc2131_sleep_interval (TIMEOUT, retry); | 
|  |  | 
|  | if (await_reply (AWAIT_ARP, arpentry, arpreq.tipaddr, timeout)) | 
|  | goto xmit; | 
|  |  | 
|  | if (ip_abort) | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | xmit: | 
|  | eth_transmit (arptable[arpentry].node, IP, len, buf); | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | TFTP - Download extended BOOTP data, or kernel image | 
|  | **************************************************************************/ | 
|  | static int | 
|  | tftp (const char *name, int (*fnc) (unsigned char *, int, int, int)) | 
|  | { | 
|  | int retry = 0; | 
|  | static unsigned short iport = 2000; | 
|  | unsigned short oport = 0; | 
|  | unsigned short len, block = 0, prevblock = 0; | 
|  | int bcounter = 0; | 
|  | struct tftp_t *tr; | 
|  | struct tftpreq_t tp; | 
|  | int rc; | 
|  | int packetsize = TFTP_DEFAULTSIZE_PACKET; | 
|  |  | 
|  | /* Clear out the Rx queue first.  It contains nothing of interest, | 
|  | * except possibly ARP requests from the DHCP/TFTP server.  We use | 
|  | * polling throughout Etherboot, so some time may have passed since we | 
|  | * last polled the receive queue, which may now be filled with | 
|  | * broadcast packets.  This will cause the reply to the packets we are | 
|  | * about to send to be lost immediately.  Not very clever.  */ | 
|  | await_reply (AWAIT_QDRAIN, 0, NULL, 0); | 
|  |  | 
|  | tp.opcode = htons (TFTP_RRQ); | 
|  | len = (grub_sprintf ((char *) tp.u.rrq, "%s%coctet%cblksize%c%d", | 
|  | name, 0, 0, 0, TFTP_MAX_PACKET) | 
|  | + sizeof (tp.ip) + sizeof (tp.udp) + sizeof (tp.opcode) + 1); | 
|  | if (! udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, ++iport, | 
|  | TFTP_PORT, len, &tp)) | 
|  | return 0; | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | long timeout; | 
|  |  | 
|  | #ifdef CONGESTED | 
|  | timeout = rfc2131_sleep_interval (block ? TFTP_REXMT : TIMEOUT, retry); | 
|  | #else | 
|  | timeout = rfc2131_sleep_interval (TIMEOUT, retry); | 
|  | #endif | 
|  |  | 
|  | if (! await_reply (AWAIT_TFTP, iport, NULL, timeout)) | 
|  | { | 
|  | if (! block && retry++ < MAX_TFTP_RETRIES) | 
|  | { | 
|  | /* Maybe initial request was lost.  */ | 
|  | 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 | 
|  | grub_printf ("<REXMT>\n"); | 
|  | #endif | 
|  | udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, | 
|  | iport, oport, | 
|  | TFTP_MIN_PACKET, &tp); | 
|  | continue; | 
|  | } | 
|  | #endif | 
|  | /* Timeout.  */ | 
|  | break; | 
|  | } | 
|  |  | 
|  | tr = (struct tftp_t *) &nic.packet[ETH_HLEN]; | 
|  | if (tr->opcode == ntohs (TFTP_ERROR)) | 
|  | { | 
|  | grub_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; | 
|  |  | 
|  | /* Shouldn't happen.  */ | 
|  | if (prevblock) | 
|  | /* Ignore it.  */ | 
|  | continue; | 
|  |  | 
|  | 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 (! grub_strcmp ("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 = (grub_sprintf ((char *) tp.u.err.errmsg, | 
|  | "RFC1782 error") | 
|  | + sizeof (tp.ip) + sizeof (tp.udp) | 
|  | + sizeof (tp.opcode) + sizeof (tp.u.err.errcode) | 
|  | + 1); | 
|  | udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, | 
|  | iport, ntohs (tr->udp.src), | 
|  | len, &tp); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (p > e) | 
|  | goto noak; | 
|  |  | 
|  | /* This ensures that the packet does not get processed as data!  */ | 
|  | block = tp.u.ack.block = 0; | 
|  | } | 
|  | else if (tr->opcode == ntohs (TFTP_DATA)) | 
|  | { | 
|  | len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 4; | 
|  | /* Shouldn't happen.  */ | 
|  | if (len > packetsize) | 
|  | /* Ignore it.  */ | 
|  | continue; | 
|  |  | 
|  | 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); | 
|  |  | 
|  | /* Should be continuous.  */ | 
|  | tp.opcode = htons (TFTP_ACK); | 
|  | oport = ntohs (tr->udp.src); | 
|  | /* Ack.  */ | 
|  | udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, iport, | 
|  | oport, TFTP_MIN_PACKET, &tp); | 
|  |  | 
|  | 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; | 
|  | /* Is it the right place to zero the timer?  */ | 
|  | retry = 0; | 
|  |  | 
|  | if ((rc = fnc (tr->u.data.download, | 
|  | ++bcounter, len, len < packetsize)) >= 0) | 
|  | return rc; | 
|  |  | 
|  | /* End of data.  */ | 
|  | if (len < packetsize) | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | RARP - Get my IP address and load information | 
|  | **************************************************************************/ | 
|  | int | 
|  | rarp (void) | 
|  | { | 
|  | int retry; | 
|  |  | 
|  | /* arp and rarp requests share the same packet structure.  */ | 
|  | struct arprequest rarpreq; | 
|  |  | 
|  | /* Make sure that an ethernet is probed.  */ | 
|  | if (! eth_probe ()) | 
|  | return 0; | 
|  |  | 
|  | /* Clear the ready flag.  */ | 
|  | network_ready = 0; | 
|  |  | 
|  | grub_memset (&rarpreq, 0, sizeof (rarpreq)); | 
|  |  | 
|  | rarpreq.hwtype = htons (1); | 
|  | rarpreq.protocol = htons (IP); | 
|  | rarpreq.hwlen = ETH_ALEN; | 
|  | rarpreq.protolen = 4; | 
|  | rarpreq.opcode = htons (RARP_REQUEST); | 
|  | grub_memmove ((char *) &rarpreq.shwaddr, arptable[ARP_CLIENT].node, | 
|  | ETH_ALEN); | 
|  | /* sipaddr is already zeroed out */ | 
|  | grub_memmove ((char *) &rarpreq.thwaddr, arptable[ARP_CLIENT].node, | 
|  | ETH_ALEN); | 
|  | /* tipaddr is already zeroed out */ | 
|  |  | 
|  | for (retry = 0; retry < MAX_ARP_RETRIES; ++retry) | 
|  | { | 
|  | long timeout; | 
|  |  | 
|  | eth_transmit (broadcast, RARP, sizeof (rarpreq), &rarpreq); | 
|  |  | 
|  | timeout = rfc2131_sleep_interval (TIMEOUT, retry); | 
|  | if (await_reply (AWAIT_RARP, 0, rarpreq.shwaddr, timeout)) | 
|  | break; | 
|  |  | 
|  | if (ip_abort) | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (retry < MAX_ARP_RETRIES) | 
|  | { | 
|  | network_ready = 1; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | BOOTP - Get my IP address and load information | 
|  | **************************************************************************/ | 
|  | int | 
|  | bootp (void) | 
|  | { | 
|  | int retry; | 
|  | #ifndef	NO_DHCP_SUPPORT | 
|  | int reqretry; | 
|  | #endif /* ! NO_DHCP_SUPPORT */ | 
|  | struct bootpip_t ip; | 
|  | unsigned long starttime; | 
|  |  | 
|  | /* Make sure that an ethernet is probed.  */ | 
|  | if (! eth_probe ()) | 
|  | return 0; | 
|  |  | 
|  | /* Clear the ready flag.  */ | 
|  | network_ready = 0; | 
|  |  | 
|  | #ifdef DEBUG | 
|  | grub_printf ("network is ready.\n"); | 
|  | #endif | 
|  |  | 
|  | grub_memset (&ip, 0, sizeof (struct bootpip_t)); | 
|  | ip.bp.bp_op = BOOTP_REQUEST; | 
|  | ip.bp.bp_htype = 1; | 
|  | ip.bp.bp_hlen = ETH_ALEN; | 
|  | starttime = currticks (); | 
|  | /* Use lower 32 bits of node address, more likely to be | 
|  | distinct than the time since booting */ | 
|  | grub_memmove (&xid, &arptable[ARP_CLIENT].node[2], sizeof(xid)); | 
|  | ip.bp.bp_xid = xid += htonl (starttime); | 
|  | grub_memmove (ip.bp.bp_hwaddr, arptable[ARP_CLIENT].node, ETH_ALEN); | 
|  | #ifdef DEBUG | 
|  | etherboot_printf ("bp_op = %d\n", ip.bp.bp_op); | 
|  | etherboot_printf ("bp_htype = %d\n", ip.bp.bp_htype); | 
|  | etherboot_printf ("bp_hlen = %d\n", ip.bp.bp_hlen); | 
|  | etherboot_printf ("bp_xid = %d\n", ip.bp.bp_xid); | 
|  | etherboot_printf ("bp_hwaddr = %!\n", ip.bp.bp_hwaddr); | 
|  | etherboot_printf ("bp_hops = %d\n", (int) ip.bp.bp_hops); | 
|  | etherboot_printf ("bp_secs = %d\n", (int) ip.bp.bp_hwaddr); | 
|  | #endif | 
|  |  | 
|  | #ifdef	NO_DHCP_SUPPORT | 
|  | /* Request RFC-style options.  */ | 
|  | grub_memmove (ip.bp.bp_vend, rfc1533_cookie, 5); | 
|  | #else | 
|  | /* Request RFC-style options.  */ | 
|  | grub_memmove (ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); | 
|  | grub_memmove (ip.bp.bp_vend + sizeof rfc1533_cookie, dhcpdiscover, | 
|  | sizeof dhcpdiscover); | 
|  | grub_memmove (ip.bp.bp_vend + sizeof rfc1533_cookie + sizeof dhcpdiscover, | 
|  | rfc1533_end, sizeof rfc1533_end); | 
|  | #endif /* ! NO_DHCP_SUPPORT */ | 
|  |  | 
|  | for (retry = 0; retry < MAX_BOOTP_RETRIES;) | 
|  | { | 
|  | long timeout; | 
|  |  | 
|  | #ifdef DEBUG | 
|  | grub_printf ("retry = %d\n", retry); | 
|  | #endif | 
|  |  | 
|  | /* 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 bootpip_t), &ip); | 
|  | timeout = rfc2131_sleep_interval (TIMEOUT, retry++); | 
|  | #ifdef NO_DHCP_SUPPORT | 
|  | if (await_reply (AWAIT_BOOTP, 0, NULL, timeout)) | 
|  | { | 
|  | network_ready = 1; | 
|  | return 1; | 
|  | } | 
|  | #else /* ! NO_DHCP_SUPPORT */ | 
|  | if (await_reply (AWAIT_BOOTP, 0, NULL, timeout)) | 
|  | { | 
|  | if (dhcp_reply != DHCPOFFER) | 
|  | { | 
|  | network_ready = 1; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | dhcp_reply = 0; | 
|  | #ifdef DEBUG | 
|  | etherboot_printf ("bp_op = %d\n", (int) ip.bp.bp_op); | 
|  | etherboot_printf ("bp_htype = %d\n", (int) ip.bp.bp_htype); | 
|  | etherboot_printf ("bp_hlen = %d\n", (int) ip.bp.bp_hlen); | 
|  | etherboot_printf ("bp_xid = %d\n", (int) ip.bp.bp_xid); | 
|  | etherboot_printf ("bp_hwaddr = %!\n", ip.bp.bp_hwaddr); | 
|  | etherboot_printf ("bp_hops = %d\n", (int) ip.bp.bp_hops); | 
|  | etherboot_printf ("bp_secs = %d\n", (int) ip.bp.bp_hwaddr); | 
|  | #endif | 
|  | grub_memmove (ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); | 
|  | grub_memmove (ip.bp.bp_vend + sizeof rfc1533_cookie, | 
|  | dhcprequest, sizeof dhcprequest); | 
|  | grub_memmove (ip.bp.bp_vend + sizeof rfc1533_cookie | 
|  | + sizeof dhcprequest, | 
|  | rfc1533_end, sizeof rfc1533_end); | 
|  | grub_memmove (ip.bp.bp_vend + 9, (char *) &dhcp_server, | 
|  | sizeof (in_addr)); | 
|  | grub_memmove (ip.bp.bp_vend + 15, (char *) &dhcp_addr, | 
|  | sizeof (in_addr)); | 
|  | #ifdef DEBUG | 
|  | grub_printf ("errnum = %d\n", errnum); | 
|  | #endif | 
|  | for (reqretry = 0; reqretry < MAX_BOOTP_RETRIES;) | 
|  | { | 
|  | int ret; | 
|  | #ifdef DEBUG | 
|  | grub_printf ("reqretry = %d\n", reqretry); | 
|  | #endif | 
|  |  | 
|  | ret = udp_transmit (IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER, | 
|  | sizeof (struct bootpip_t), &ip); | 
|  | if (! ret) | 
|  | grub_printf ("udp_transmit failed.\n"); | 
|  |  | 
|  | dhcp_reply = 0; | 
|  | timeout = rfc2131_sleep_interval (TIMEOUT, reqretry++); | 
|  | if (await_reply (AWAIT_BOOTP, 0, NULL, timeout)) | 
|  | if (dhcp_reply == DHCPACK) | 
|  | { | 
|  | network_ready = 1; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | #ifdef DEBUG | 
|  | grub_printf ("dhcp_reply = %d\n", dhcp_reply); | 
|  | #endif | 
|  |  | 
|  | if (ip_abort) | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | #endif /* ! NO_DHCP_SUPPORT */ | 
|  |  | 
|  | if (ip_abort) | 
|  | return 0; | 
|  |  | 
|  | ip.bp.bp_secs = htons ((currticks () - starttime) / TICKS_PER_SEC); | 
|  | } | 
|  |  | 
|  | /* Timeout.  */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | UDPCHKSUM - Checksum UDP Packet (one of the rare cases when assembly is | 
|  | actually simpler...) | 
|  | RETURNS: checksum, 0 on checksum error. This | 
|  | allows for using the same routine for RX and TX summing: | 
|  | RX  if (packet->udp.chksum && udpchksum(packet)) | 
|  | error("checksum error"); | 
|  | TX  packet->udp.chksum=0; | 
|  | if (0==(packet->udp.chksum=udpchksum(packet))) | 
|  | packet->upd.chksum=0xffff; | 
|  | **************************************************************************/ | 
|  | static inline void | 
|  | dosum (unsigned short *start, unsigned int len, unsigned short *sum) | 
|  | { | 
|  | __asm__ __volatile__ | 
|  | ("clc\n" | 
|  | "1:\tlodsw\n\t" | 
|  | "xchg %%al,%%ah\n\t"	/* convert to host byte order */ | 
|  | "adcw %%ax,%0\n\t"		/* add carry of previous iteration */ | 
|  | "loop 1b\n\t" | 
|  | "adcw $0,%0"		/* add carry of last iteration */ | 
|  | : "=b" (*sum), "=S"(start), "=c"(len) | 
|  | : "0"(*sum), "1"(start), "2"(len) | 
|  | : "ax", "cc" | 
|  | ); | 
|  | } | 
|  |  | 
|  | /* UDP sum: | 
|  | * proto, src_ip, dst_ip, udp_dport, udp_sport, 2*udp_len, payload | 
|  | */ | 
|  | static unsigned short | 
|  | udpchksum (struct iphdr *packet) | 
|  | { | 
|  | int len = ntohs (packet->len); | 
|  | unsigned short rval; | 
|  |  | 
|  | /* add udplength + protocol number */ | 
|  | rval = (len - sizeof (struct iphdr)) + IP_UDP; | 
|  |  | 
|  | /* pad to an even number of bytes */ | 
|  | if (len % 2) { | 
|  | ((char *) packet)[len++] = 0; | 
|  | } | 
|  |  | 
|  | /* sum over src/dst ipaddr + udp packet */ | 
|  | len -= (char *) &packet->src - (char *) packet; | 
|  | dosum ((unsigned short *) &packet->src, len >> 1, &rval); | 
|  |  | 
|  | /* take one's complement */ | 
|  | return ~rval; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | 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; | 
|  | unsigned short ptype; | 
|  | unsigned int protohdrlen = (ETH_HLEN + sizeof (struct iphdr) | 
|  | + sizeof (struct udphdr)); | 
|  |  | 
|  | /* Clear the abort flag.  */ | 
|  | ip_abort = 0; | 
|  |  | 
|  | time = timeout + currticks (); | 
|  | /* The timeout check is done below.  The timeout is only checked if | 
|  | * there is no packet in the Rx queue.  This assumes that eth_poll() | 
|  | * needs a negligible amount of time.  */ | 
|  | for (;;) | 
|  | { | 
|  | if (eth_poll ()) | 
|  | { | 
|  | /* We have something!  */ | 
|  |  | 
|  | /* Check for ARP - No IP hdr.  */ | 
|  | if (nic.packetlen >= ETH_HLEN) | 
|  | { | 
|  | ptype = (((unsigned short) nic.packet[12]) << 8 | 
|  | | ((unsigned short) nic.packet[13])); | 
|  | } | 
|  | else | 
|  | /* What else could we do with it?  */ | 
|  | continue; | 
|  |  | 
|  | if (nic.packetlen >= ETH_HLEN + sizeof (struct arprequest) | 
|  | && ptype == ARP) | 
|  | { | 
|  | unsigned long tmp; | 
|  |  | 
|  | arpreply = (struct arprequest *) &nic.packet[ETH_HLEN]; | 
|  |  | 
|  | if (arpreply->opcode == htons (ARP_REPLY) | 
|  | && ! grub_memcmp (arpreply->sipaddr, ptr, sizeof (in_addr)) | 
|  | && type == AWAIT_ARP) | 
|  | { | 
|  | grub_memmove ((char *) arptable[ival].node, | 
|  | arpreply->shwaddr, | 
|  | ETH_ALEN); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | grub_memmove ((char *) &tmp, arpreply->tipaddr, | 
|  | sizeof (in_addr)); | 
|  |  | 
|  | if (arpreply->opcode == htons (ARP_REQUEST) | 
|  | && tmp == arptable[ARP_CLIENT].ipaddr.s_addr) | 
|  | { | 
|  | arpreply->opcode = htons (ARP_REPLY); | 
|  | grub_memmove (arpreply->tipaddr, arpreply->sipaddr, | 
|  | sizeof (in_addr)); | 
|  | grub_memmove (arpreply->thwaddr, (char *) arpreply->shwaddr, | 
|  | ETH_ALEN); | 
|  | grub_memmove (arpreply->sipaddr, | 
|  | (char *) &arptable[ARP_CLIENT].ipaddr, | 
|  | sizeof (in_addr)); | 
|  | grub_memmove (arpreply->shwaddr, | 
|  | arptable[ARP_CLIENT].node, | 
|  | ETH_ALEN); | 
|  | eth_transmit (arpreply->thwaddr, ARP, | 
|  | sizeof (struct arprequest), | 
|  | arpreply); | 
|  | #ifdef MDEBUG | 
|  | grub_memmove (&tmp, arpreply->tipaddr, sizeof (in_addr)); | 
|  | etherboot_printf ("Sent ARP reply to: %@\n", tmp); | 
|  | #endif	/* MDEBUG */ | 
|  | } | 
|  |  | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (type == AWAIT_QDRAIN) | 
|  | continue; | 
|  |  | 
|  | /* Check for RARP - No IP hdr.  */ | 
|  | if (type == AWAIT_RARP | 
|  | && nic.packetlen >= ETH_HLEN + sizeof (struct arprequest) | 
|  | && ptype == RARP) | 
|  | { | 
|  | arpreply = (struct arprequest *) &nic.packet[ETH_HLEN]; | 
|  |  | 
|  | if (arpreply->opcode == htons (RARP_REPLY) | 
|  | && ! grub_memcmp (arpreply->thwaddr, ptr, ETH_ALEN)) | 
|  | { | 
|  | grub_memmove ((char *) arptable[ARP_SERVER].node, | 
|  | arpreply->shwaddr, ETH_ALEN); | 
|  | grub_memmove ((char *) &arptable[ARP_SERVER].ipaddr, | 
|  | arpreply->sipaddr, sizeof (in_addr)); | 
|  | grub_memmove ((char *) &arptable[ARP_CLIENT].ipaddr, | 
|  | arpreply->tipaddr, sizeof (in_addr)); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* Anything else has IP header.  */ | 
|  | if (nic.packetlen < protohdrlen || ptype != IP) | 
|  | continue; | 
|  |  | 
|  | ip = (struct iphdr *) &nic.packet[ETH_HLEN]; | 
|  | if (ip->verhdrlen != 0x45 | 
|  | || ipchksum ((unsigned short *) ip, sizeof (struct iphdr)) | 
|  | || ip->protocol != IP_UDP) | 
|  | continue; | 
|  |  | 
|  | /* | 
|  | - Till Straumann <Till.Straumann@TU-Berlin.de> | 
|  | added udp checksum (safer on a wireless link) | 
|  | added fragmentation check: I had a corrupted image | 
|  | in memory due to fragmented TFTP packets - took me | 
|  | 3 days to find the cause for this :-( | 
|  | */ | 
|  |  | 
|  | /* If More Fragments bit and Fragment Offset field | 
|  | are non-zero then packet is fragmented */ | 
|  | if (ip->frags & htons(0x3FFF)) | 
|  | { | 
|  | grub_printf ("ALERT: got a fragmented packet - reconfigure your server\n"); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | udp = (struct udphdr *) &nic.packet[(ETH_HLEN | 
|  | + sizeof (struct iphdr))]; | 
|  | if (udp->chksum && udpchksum (ip)) | 
|  | { | 
|  | grub_printf ("UDP checksum error\n"); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* BOOTP ?  */ | 
|  | bootpreply = (struct bootp_t *) | 
|  | &nic.packet[(ETH_HLEN + sizeof (struct iphdr) | 
|  | + sizeof (struct udphdr))]; | 
|  | if (type == AWAIT_BOOTP | 
|  | #ifdef NO_DHCP_SUPPORT | 
|  | && (nic.packetlen >= (ETH_HLEN + sizeof (struct bootp_t))) | 
|  | #else | 
|  | && (nic.packetlen | 
|  | >= (ETH_HLEN + sizeof (struct bootp_t) - DHCP_OPT_LEN)) | 
|  | #endif /* ! NO_DHCP_SUPPORT */ | 
|  | && udp->dest == htons (BOOTP_CLIENT) | 
|  | && bootpreply->bp_op == BOOTP_REPLY | 
|  | && bootpreply->bp_xid == xid | 
|  | && (! grub_memcmp (broadcast, bootpreply->bp_hwaddr, ETH_ALEN) | 
|  | || ! grub_memcmp (arptable[ARP_CLIENT].node, | 
|  | bootpreply->bp_hwaddr, ETH_ALEN))) | 
|  | { | 
|  | #ifdef DEBUG | 
|  | grub_printf ("BOOTP packet was received.\n"); | 
|  | #endif | 
|  | arptable[ARP_CLIENT].ipaddr.s_addr | 
|  | = bootpreply->bp_yiaddr.s_addr; | 
|  | #ifndef	NO_DHCP_SUPPORT | 
|  | dhcp_addr.s_addr = bootpreply->bp_yiaddr.s_addr; | 
|  | #ifdef DEBUG | 
|  | etherboot_printf ("dhcp_addr = %@\n", dhcp_addr.s_addr); | 
|  | #endif | 
|  | #endif /* ! NO_DHCP_SUPPORT */ | 
|  | netmask = default_netmask (); | 
|  | arptable[ARP_SERVER].ipaddr.s_addr | 
|  | = bootpreply->bp_siaddr.s_addr; | 
|  | /* Kill arp.  */ | 
|  | grub_memset (arptable[ARP_SERVER].node, 0, ETH_ALEN); | 
|  | arptable[ARP_GATEWAY].ipaddr.s_addr | 
|  | = bootpreply->bp_giaddr.s_addr; | 
|  | /* Kill arp.  */ | 
|  | grub_memset (arptable[ARP_GATEWAY].node, 0, ETH_ALEN); | 
|  |  | 
|  | grub_memmove ((char *) BOOTP_DATA_ADDR, (char *) bootpreply, | 
|  | sizeof (struct bootpd_t)); | 
|  | #ifdef NO_DHCP_SUPPORT | 
|  | decode_rfc1533 (BOOTP_DATA_ADDR->bootp_reply.bp_vend, | 
|  | 0, BOOTP_VENDOR_LEN + MAX_BOOTP_EXTLEN, 1); | 
|  | #else | 
|  | decode_rfc1533 (BOOTP_DATA_ADDR->bootp_reply.bp_vend, | 
|  | 0, DHCP_OPT_LEN + MAX_BOOTP_EXTLEN, 1); | 
|  | #endif /* ! NO_DHCP_SUPPORT */ | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* TFTP ? */ | 
|  | if (type == AWAIT_TFTP && ntohs (udp->dest) == ival) | 
|  | return 1; | 
|  | } | 
|  | 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 (checkkey () != -1 && ASCII_CHAR (getkey ()) == CTRL_C) | 
|  | { | 
|  | ip_abort = 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* 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 (unsigned char *p, int block, int len, int eof) | 
|  | { | 
|  | static unsigned char *extdata = NULL, *extend = NULL; | 
|  | unsigned char *extpath = NULL; | 
|  | unsigned char *endp; | 
|  |  | 
|  | if (block == 0) | 
|  | { | 
|  | end_of_rfc1533 = NULL; | 
|  | vendorext_isvalid = 0; | 
|  |  | 
|  | if (grub_memcmp (p, rfc1533_cookie, 4)) | 
|  | /* no RFC 1533 header found */ | 
|  | return 0; | 
|  |  | 
|  | p += 4; | 
|  | endp = p + len; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (block == 1) | 
|  | { | 
|  | if (grub_memcmp (p, rfc1533_cookie, 4)) | 
|  | /* no RFC 1533 header found */ | 
|  | return 0; | 
|  |  | 
|  | p += 4; | 
|  | len -= 4; | 
|  | } | 
|  |  | 
|  | if (extend + len | 
|  | <= ((unsigned char *) | 
|  | &(BOOTP_DATA_ADDR->bootp_extension[MAX_BOOTP_EXTLEN]))) | 
|  | { | 
|  | grub_memmove (extend, p, len); | 
|  | extend += len; | 
|  | } | 
|  | else | 
|  | { | 
|  | grub_printf ("Overflow in vendor data buffer! Aborting...\n"); | 
|  | *extdata = RFC1533_END; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | p = extdata; | 
|  | endp = extend; | 
|  | } | 
|  |  | 
|  | if (! eof) | 
|  | return -1; | 
|  |  | 
|  | 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) | 
|  | { | 
|  | grub_memmove ((char *) &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)) | 
|  | grub_memmove ((char *) &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) | 
|  | { | 
|  | grub_memmove ((char *) &dhcp_server, p + 2, sizeof (in_addr)); | 
|  | #ifdef DEBUG | 
|  | etherboot_printf ("dhcp_server = %@\n", dhcp_server.s_addr); | 
|  | #endif | 
|  | } | 
|  | #endif /* ! NO_DHCP_SUPPORT */ | 
|  | else if (c == RFC1533_VENDOR_MAGIC | 
|  | && TAG_LEN(p) >= 6 | 
|  | && ! grub_memcmp (p + 2, vendorext_magic, 4) | 
|  | && p[6] == RFC1533_VENDOR_MAJOR) | 
|  | vendorext_isvalid++; | 
|  | /* GRUB now handles its own tag. Get the name of a configuration | 
|  | file from the network. Cool...  */ | 
|  | else if (c == RFC1533_VENDOR_CONFIGFILE) | 
|  | { | 
|  | int l = TAG_LEN (p); | 
|  |  | 
|  | /* Eliminate the trailing NULs according to RFC 2132.  */ | 
|  | while (*(p + 2 + l - 1) == '\000' && l > 0) | 
|  | l--; | 
|  |  | 
|  | /* XXX: Should check if LEN is less than the maximum length | 
|  | of CONFIG_FILE. This kind of robustness will be a goal | 
|  | in GRUB 1.0.  */ | 
|  | grub_memmove (config_file, p + 2, l); | 
|  | config_file[l] = 0; | 
|  | } | 
|  |  | 
|  | p += TAG_LEN (p) + 2; | 
|  | } | 
|  |  | 
|  | extdata = extend = endp; | 
|  |  | 
|  | /* Perhaps we can eliminate this because we doesn't require so | 
|  | much information, but I leave this alone.  */ | 
|  | if (block == 0 && extpath != NULL) | 
|  | { | 
|  | char fname[64]; | 
|  | int fnamelen = TAG_LEN (extpath); | 
|  |  | 
|  | while (*(extpath + 2 + fnamelen - 1) == '\000' && fnamelen > 0) | 
|  | fnamelen--; | 
|  |  | 
|  | if (fnamelen + 1 > sizeof (fname)) | 
|  | { | 
|  | grub_printf ("Too long file name for Extensions Path\n"); | 
|  | return 0; | 
|  | } | 
|  | else if (! fnamelen) | 
|  | { | 
|  | grub_printf ("Empty file name for Extensions Path\n"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | grub_memmove (fname, extpath + 2, fnamelen); | 
|  | fname[fnamelen] = '\000'; | 
|  | grub_printf ("Loading BOOTP-extension file: %s\n", fname); | 
|  | tftp (fname, decode_rfc1533); | 
|  | } | 
|  |  | 
|  | /* Proceed with next block.  */ | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | IPCHKSUM - Checksum IP Header | 
|  | **************************************************************************/ | 
|  | static unsigned short | 
|  | ipchksum (unsigned short *ip, int len) | 
|  | { | 
|  | unsigned long sum = 0; | 
|  | len >>= 1; | 
|  | while (len--) | 
|  | { | 
|  | sum += *(ip++); | 
|  | if (sum > 0xFFFF) | 
|  | sum -= 0xFFFF; | 
|  | } | 
|  | return (~sum) & 0x0000FFFF; | 
|  | } | 
|  |  | 
|  | #define TWO_SECOND_DIVISOR (2147483647l/TICKS_PER_SEC) | 
|  |  | 
|  | /************************************************************************** | 
|  | RFC2131_SLEEP_INTERVAL - sleep for expotentially longer times | 
|  | **************************************************************************/ | 
|  | long | 
|  | rfc2131_sleep_interval (int base, 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 Schneier's | 
|  | "Applied Cryptography" */ | 
|  | q = seed / 53668; | 
|  | if ((seed = 40014 * (seed - 53668 * q) - 12211 *q ) < 0) | 
|  | seed += 2147483563L; | 
|  | tmo = (base << exp) + (TICKS_PER_SEC - (seed / TWO_SECOND_DIVISOR)); | 
|  | return tmo; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | CLEANUP - shut down networking | 
|  | **************************************************************************/ | 
|  | void | 
|  | cleanup_net (void) | 
|  | { | 
|  | if (network_ready) | 
|  | { | 
|  | /* Stop receiving packets.  */ | 
|  | eth_disable (); | 
|  | network_ready = 0; | 
|  | } | 
|  | } |