12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172 |
- /*
- * 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;
- #define BOOTP_DATA_ADDR (&bootp_data)
- #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) - BOOTP_VENDOR_LEN))
- #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)
- {
- 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)
- {
- 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;
- }
- }
|