diff options
-rw-r--r-- | mdk-stage1/Makefile | 2 | ||||
-rw-r--r-- | mdk-stage1/dhcp.c | 874 | ||||
-rw-r--r-- | mdk-stage1/dhcp.h | 140 | ||||
-rw-r--r-- | mdk-stage1/network.c | 140 | ||||
-rw-r--r-- | mdk-stage1/network.h | 21 |
5 files changed, 644 insertions, 533 deletions
diff --git a/mdk-stage1/Makefile b/mdk-stage1/Makefile index 438e8c7cb..73a18e87c 100644 --- a/mdk-stage1/Makefile +++ b/mdk-stage1/Makefile @@ -81,7 +81,7 @@ STAGE1_NETWORK_LIBS = /usr/lib/libresolv.a STAGE1SRC = stage1.c log.c tools.c modules.c probing.c mount.c automatic.c CDROMSRC = cdrom.c DISKSRC = disk.c -NETWORKSRC = network.c dns.c nfsmount.c +NETWORKSRC = network.c dns.c nfsmount.c dhcp.c ALLSRC = $(INITSRC) $(STAGE1SRC) $(CDROMSRC) $(DISKSRC) $(NETWORKSRC) diff --git a/mdk-stage1/dhcp.c b/mdk-stage1/dhcp.c index b20b012cc..ee5f333b2 100644 --- a/mdk-stage1/dhcp.c +++ b/mdk-stage1/dhcp.c @@ -3,9 +3,6 @@ * * Copyright 2000 MandrakeSoft * - * View the homepage: http://us.mandrakesoft.com/~gc/html/stage1.html - * - * * This software may be freely redistributed under the terms of the GNU * public license. * @@ -16,384 +13,591 @@ */ /* - * Portions from GRUB -- GRand Unified Bootloader - * Copyright (C) 2000 Free Software Foundation, Inc. + * Portions from Erik Troan (ewt@redhat.com) * - * Itself based on etherboot-4.6.4 by Martin Renters. + * Copyright 1996 Red Hat Software * */ +/* + * Portions from GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 Free Software Foundation, Inc. + */ + + #include <stdlib.h> #include <unistd.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <net/route.h> +#include <errno.h> +#include <net/ethernet.h> +#include <netinet/ip.h> +#include <netinet/udp.h> #include <sys/time.h> +#include <fcntl.h> + #include "stage1.h" +#include "log.h" +#include "network.h" +#include "frontend.h" #include "dhcp.h" -static int currticks (void) -{ - struct timeval tv; - long csecs; - int ticks_per_csec, ticks_per_usec; +#define NUM_RETRIES 5 - /* Note: 18.2 ticks/sec. */ - /* Get current time. */ - gettimeofday (&tv, 0); +typedef int bp_int32; +typedef short bp_int16; - /* Compute centiseconds. */ - csecs = tv.tv_sec / 10; +#define BOOTP_OPTION_NETMASK 1 +#define BOOTP_OPTION_GATEWAY 3 +#define BOOTP_OPTION_DNS 6 +#define BOOTP_OPTION_HOSTNAME 12 +#define BOOTP_OPTION_DOMAIN 15 +#define BOOTP_OPTION_BROADCAST 28 - /* Ticks per centisecond. */ - ticks_per_csec = csecs * 182; +#define DHCP_OPTION_REQADDR 50 +#define DHCP_OPTION_LEASE 51 +#define DHCP_OPTION_TYPE 53 +#define DHCP_OPTION_SERVER 54 +#define DHCP_OPTION_OPTIONREQ 55 +#define DHCP_OPTION_MAXSIZE 57 - /* Ticks per microsecond. */ - ticks_per_usec = (((tv.tv_sec - csecs * 10) * 1000000 + tv.tv_usec) - * 182 / 10000000); +#define BOOTP_CLIENT_PORT 68 +#define BOOTP_SERVER_PORT 67 - /* Sum them. */ - return ticks_per_csec + ticks_per_usec; -} +#define BOOTP_OPCODE_REQUEST 1 +#define BOOTP_OPCODE_REPLY 2 +#define DHCP_TYPE_DISCOVER 1 +#define DHCP_TYPE_OFFER 2 +#define DHCP_TYPE_REQUEST 3 +#define DHCP_TYPE_ACK 5 +#define DHCP_TYPE_RELEASE 7 -static char rfc1533_cookie[] = { RFC1533_COOKIE }; -static char rfc1533_end[] = { RFC1533_END }; +#define BOOTP_VENDOR_LENGTH 64 +#define DHCP_VENDOR_LENGTH 340 -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, RFC1533_EXTENSIONPATH -}; - -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, - 4 + 2, - /* Standard parameters */ - RFC1533_NETMASK, RFC1533_GATEWAY, - RFC1533_HOSTNAME, RFC1533_EXTENSIONPATH, - /* Etherboot vendortags */ - RFC1533_VENDOR_MAGIC, - RFC1533_VENDOR_CONFIGFILE, -}; - - -static unsigned long xid; -static int sock; - -static int await_reply (int type, int ival, void *ptr, int timeout) +struct bootp_request { + char opcode; + char hw; + char hwlength; + char hopcount; + bp_int32 id; + bp_int16 secs; + bp_int16 flags; + bp_int32 ciaddr, yiaddr, server_ip, bootp_gw_ip; + char hwaddr[16]; + char servername[64]; + char bootfile[128]; + char vendor[DHCP_VENDOR_LENGTH]; +} ; + +static const char vendor_cookie[] = { 99, 130, 83, 99, 255 }; + + +static unsigned int verify_checksum(void * buf2, int length2) { - unsigned long time; - struct iphdr *ip; - struct udphdr *udp; - struct arprequest *arpreply; - struct bootp_t *bootpreply; - unsigned short ptype; - unsigned int protohdrlen = (ETHER_HDR_SIZE + sizeof (struct iphdr) + sizeof (struct udphdr)); + unsigned int csum = 0; + unsigned short * sp; + + for (sp = (unsigned short *) buf2; length2 > 0; (length2 -= 2), sp++) + csum += *sp; + + while (csum >> 16) + csum = (csum & 0xffff) + (csum >> 16); + + return (csum == 0xffff); +} + + +static int initial_setup_interface(char * device, int s) { + struct sockaddr_in * addrp; + struct ifreq req; + struct rtentry route; + int true = 1; - /* Clear the abort flag. */ - ip_abort = 0; + addrp = (struct sockaddr_in *) &req.ifr_addr; - time = currticks () + TIMEOUT; - /* 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 - /* What else could we do with it? */ - continue; - - 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) - && ! grub_memcmp (arpreply->sipaddr, ptr, sizeof (in_addr)) - && type == AWAIT_ARP) - { - grub_memmove ((char *) arptable[ival].node, - arpreply->shwaddr, - ETHER_ADDR_SIZE); - return 1; - } - - grub_memmove ((char *) &tmp, arpreply->tipaddr, - sizeof (in_addr)); - - if (arpreply->opcode == ntohs (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, - ETHER_ADDR_SIZE); - grub_memmove (arpreply->sipaddr, - (char *) &arptable[ARP_CLIENT].ipaddr, - sizeof (in_addr)); - grub_memmove (arpreply->shwaddr, - arptable[ARP_CLIENT].node, - ETHER_ADDR_SIZE); - eth_transmit (arpreply->thwaddr, ARP, - sizeof (struct arprequest), - arpreply); -#ifdef MDEBUG - grub_memmove (&tmp, arpreply->tipaddr, sizeof (in_addr)); - grub_printf ("Sent ARP reply to: %x\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) - && ! grub_memcmp (arpreply->thwaddr, ptr, ETHER_ADDR_SIZE)) - { - grub_memmove ((char *) arptable[ARP_SERVER].node, - arpreply->shwaddr, ETHER_ADDR_SIZE); - 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[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 -#ifdef NO_DHCP_SUPPORT - && (nic.packetlen - >= (ETHER_HDR_SIZE + sizeof (struct bootp_t))) -#else - && (nic.packetlen - >= (ETHER_HDR_SIZE + 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; - /* Kill arp. */ - grub_memset (arptable[ARP_SERVER].node, 0, ETHER_ADDR_SIZE); - arptable[ARP_GATEWAY].ipaddr.s_addr - = bootpreply->bp_giaddr.s_addr; - /* Kill arp. */ - grub_memset (arptable[ARP_GATEWAY].node, 0, ETHER_ADDR_SIZE); - - /* GRUB doesn't autoload any kernel image. */ -#ifndef GRUB - if (bootpreply->bp_file[0]) - { - grub_memmove (kernel_buf, bootpreply->bp_file, 128); - kernel = kernel_buf; + strcpy(req.ifr_name, device); + addrp->sin_family = AF_INET; + addrp->sin_port = 0; + memset(&addrp->sin_addr, 0, sizeof(addrp->sin_addr)); + + req.ifr_flags = 0; /* take it down */ + if (ioctl(s, SIOCSIFFLAGS, &req)) { + log_perror("SIOCSIFFLAGS (downing)"); + return -1; + } + + addrp->sin_family = AF_INET; + addrp->sin_addr.s_addr = htonl(0); + if (ioctl(s, SIOCSIFADDR, &req)) { + log_perror("SIOCSIFADDR"); + return -1; + } + + req.ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING; + if (ioctl(s, SIOCSIFFLAGS, &req)) { + log_perror("SIOCSIFFLAGS (upping)"); + return -1; + } + + memset(&route, 0, sizeof(route)); + memcpy(&route.rt_gateway, addrp, sizeof(*addrp)); + + addrp->sin_family = AF_INET; + addrp->sin_port = 0; + addrp->sin_addr.s_addr = INADDR_ANY; + memcpy(&route.rt_dst, addrp, sizeof(*addrp)); + memcpy(&route.rt_genmask, addrp, sizeof(*addrp)); + + route.rt_dev = device; + route.rt_flags = RTF_UP; + route.rt_metric = 0; + + if (ioctl(s, SIOCADDRT, &route)) { + if (errno != EEXIST) { + close(s); + log_perror("SIOCADDRT"); + return -1; } -#endif /* ! GRUB */ - - 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, 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; - } + + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &true, sizeof(true))) { + close(s); + log_perror("setsockopt"); + return -1; } - } - - return 0; + + return 0; } -static int bootp(struct interface_info * intf) +void set_missing_ip_info(struct interface_info * intf) { - int retry; - int retry1; - struct bootp_t bp; - unsigned long starttime; - struct ifreq req; - int s; + bp_int32 ipNum = *((bp_int32 *) &intf->ip); + bp_int32 nmNum; + + if (intf->netmask.s_addr == 0) + guess_netmask(intf); + + nmNum = *((bp_int32 *) &intf->netmask); + + if (intf->broadcast.s_addr == 0) + *((bp_int32 *) &intf->broadcast) = (ipNum & nmNum) | ~(nmNum); + + if (intf->network.s_addr == 0) + *((bp_int32 *) &intf->network) = ipNum & nmNum; +} + +static void parse_reply(struct bootp_request * breq, struct interface_info * intf) +{ + unsigned char * chptr; + unsigned char option, length; + + memcpy(&intf->ip, &breq->yiaddr, 4); + + chptr = breq->vendor; + chptr += 4; + while (*chptr != 0xFF && (void *) chptr < (void *) breq->vendor + DHCP_VENDOR_LENGTH) { + char tmp_str[500]; + option = *chptr++; + if (!option) + continue; + length = *chptr++; + + switch (option) { + case BOOTP_OPTION_DNS: + memcpy(&dns_server, chptr, sizeof(dns_server)); + if (length >= sizeof(dns_server)*2) + memcpy(&dns_server2, chptr+sizeof(dns_server), sizeof(dns_server2)); + break; + + case BOOTP_OPTION_NETMASK: + memcpy(&intf->netmask, chptr, 4); + break; + + case BOOTP_OPTION_DOMAIN: + memcpy(tmp_str, chptr, length); + tmp_str[length] = '\0'; + domain = strdup(tmp_str); + break; + + case BOOTP_OPTION_BROADCAST: + memcpy(&intf->broadcast, chptr, 4); + break; + + case BOOTP_OPTION_GATEWAY: + memcpy(&gateway, chptr, 4); + break; + + case BOOTP_OPTION_HOSTNAME: + memcpy(tmp_str, chptr, length); + tmp_str[length] = '\0'; + hostname = strdup(tmp_str); + log_message("DHCP: got hostname %s", hostname); + break; + + } + + chptr += length; + } + + set_missing_ip_info(intf); +} + - strcpy(req.ifr_name, intf->name); +static void init_vendor_codes(struct bootp_request * breq) { + memcpy(breq->vendor, vendor_cookie, sizeof(vendor_cookie)); +} + +static char gen_hwaddr[16]; + +static int prepare_request(struct bootp_request * breq, int sock, char * device, time_t startTime) +{ + struct ifreq req; + + memset(breq, 0, sizeof(*breq)); + + breq->opcode = BOOTP_OPCODE_REQUEST; + + strcpy(req.ifr_name, device); if (ioctl(sock, SIOCGIFHWADDR, &req)) { log_perror("SIOCSIFHWADDR"); return -1; } + + breq->hw = 1; /* ethernet */ + breq->hwlength = IFHWADDRLEN; + memcpy(breq->hwaddr, req.ifr_hwaddr.sa_data, IFHWADDRLEN); + memcpy(gen_hwaddr, req.ifr_hwaddr.sa_data, IFHWADDRLEN); + + breq->hopcount = 0; + + init_vendor_codes(breq); + + return 0; +} - 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 (); - - - memmove(bp.bp_hwaddr, req.ifr_hwaddr.sa_data, ETHER_ADDR_SIZE); - - /* Request RFC-style options. */ - memmove(bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); - memmove(bp.bp_vend + sizeof rfc1533_cookie, dhcpdiscover, sizeof dhcpdiscover); - memmove(bp.bp_vend + sizeof rfc1533_cookie + sizeof dhcpdiscover, rfc1533_end, sizeof rfc1533_end); - - 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); +static int get_vendor_code(struct bootp_request * bresp, unsigned char option, void * data) +{ + unsigned char * chptr; + unsigned int length, theOption; + + chptr = bresp->vendor + 4; + while (*chptr != 0xFF && *chptr != option) { + theOption = *chptr++; + if (!theOption) + continue; + length = *chptr++; + chptr += length; + } + + if (*chptr++ == 0xff) + return 1; + + length = *chptr++; + memcpy(data, chptr, length); + + return 0; +} + + +int +currticks (void) +{ + struct timeval tv; + long csecs; + int ticks_per_csec, ticks_per_usec; + + /* Note: 18.2 ticks/sec. */ + + gettimeofday (&tv, 0); + csecs = tv.tv_sec / 10; + ticks_per_csec = csecs * 182; + ticks_per_usec = (((tv.tv_sec - csecs * 10) * 1000000 + tv.tv_usec) * 182 / 10000000); + return ticks_per_csec + ticks_per_usec; +} + + +#define BACKOFF_LIMIT 7 +#define TICKS_PER_SEC 18 +#define MAX_ARP_RETRIES 5 + +void +rfc951_sleep (int exp) +{ + static long seed = 0; + long q; + unsigned long tmo; + + if (exp > BACKOFF_LIMIT) + exp = BACKOFF_LIMIT; + + if (!seed) + /* Initialize linear congruential generator. */ + seed = (currticks () + *(long *) &gen_hwaddr + ((short *) gen_hwaddr)[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. */ + log_message("<sleep>"); + + for (tmo = (tmo & seed) + currticks (); currticks () < tmo;); +} + + +static int handle_transaction(int s, struct bootp_request * breq, struct bootp_request * bresp, + struct sockaddr_in * server_addr, int dhcp_type) +{ + struct timeval tv; + fd_set readfs; + int i, j; + int retry = 1; + int sin; + char eth_packet[ETH_FRAME_LEN]; + struct iphdr * ip_hdr; + struct udphdr * udp_hdr; + unsigned char type; + unsigned long starttime; + int timeout = 1; + + breq->id = starttime = currticks(); + breq->secs = 0; + + sin = socket(AF_PACKET, SOCK_DGRAM, ntohs(ETH_P_IP)); + + while (retry <= MAX_ARP_RETRIES) { + i = sizeof(*breq); + + if (sendto(s, breq, i, 0, (struct sockaddr *) server_addr, sizeof(*server_addr)) != i) { + close(s); + log_perror("sendto"); + return -1; + } - if (await_reply (AWAIT_BOOTP, 0, NULL, TIMEOUT)) - { - if (dhcp_reply == DHCPOFFER) - { - dhcp_reply = 0; - grub_memmove (bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); - grub_memmove (bp.bp_vend + sizeof rfc1533_cookie, - dhcprequest, sizeof dhcprequest); - grub_memmove (bp.bp_vend + sizeof rfc1533_cookie - + sizeof dhcprequest, - rfc1533_end, sizeof rfc1533_end); - grub_memmove (bp.bp_vend + 9, (char *) &dhcp_server, - sizeof (in_addr)); - grub_memmove (bp.bp_vend + 15, (char *) &dhcp_addr, - sizeof (in_addr)); - for (retry1 = 0; retry1 < MAX_BOOTP_RETRIES;) - { - udp_transmit (IP_BROADCAST, 0, BOOTP_SERVER, - sizeof (struct bootp_t), &bp); - dhcp_reply = 0; - if (await_reply (AWAIT_BOOTP, 0, NULL, TIMEOUT)) - if (dhcp_reply == DHCPACK) - { - network_ready = 1; - return 1; - } - - if (ip_abort) - return 0; - - rfc951_sleep (++retry1); - } - - /* Timeout. */ - return 0; - } - else - { - network_ready = 1; - return 1; + FD_ZERO(&readfs); + FD_SET(sin, &readfs); + tv.tv_usec = 0; + tv.tv_sec = timeout; + + while (select(sin + 1, &readfs, NULL, NULL, &tv) == 1) { + + if ((j = recv(sin, eth_packet, sizeof(eth_packet), 0)) == -1) { + log_perror("recv"); + continue; } - } - - if (ip_abort) + + /* We need to do some basic sanity checking of the header */ + if (j < (sizeof(*ip_hdr) + sizeof(*udp_hdr))) + continue; + + ip_hdr = (void *) eth_packet; + if (!verify_checksum(ip_hdr, sizeof(*ip_hdr))) + continue; + + if (ntohs(ip_hdr->tot_len) > j) + continue; + + j = ntohs(ip_hdr->tot_len); + + if (ip_hdr->protocol != IPPROTO_UDP) + continue; + + udp_hdr = (void *) (eth_packet + sizeof(*ip_hdr)); + + if (ntohs(udp_hdr->source) != BOOTP_SERVER_PORT) + continue; + + if (ntohs(udp_hdr->dest) != BOOTP_CLIENT_PORT) + continue; + /* Go on with this packet; it looks sane */ + + /* Originally copied sizeof (*bresp) - this is a security + problem due to a potential underflow of the source + buffer. Also, it trusted that the packet was properly + 0xFF terminated, which is not true in the case of the + DHCP server on Cisco 800 series ISDN router. */ + + memset (bresp, 0xFF, sizeof (*bresp)); + memcpy (bresp, (char *) udp_hdr + sizeof (*udp_hdr), j - sizeof (*ip_hdr) - sizeof (*udp_hdr)); + + /* sanity checks */ + if (bresp->id != breq->id) + continue; + if (bresp->opcode != BOOTP_OPCODE_REPLY) + continue; + if (bresp->hwlength != breq->hwlength) + continue; + if (memcmp(bresp->hwaddr, breq->hwaddr, bresp->hwlength)) + continue; + if (get_vendor_code(bresp, DHCP_OPTION_TYPE, &type) || type != dhcp_type) + continue; + if (memcmp(bresp->vendor, vendor_cookie, 4)) + continue; return 0; - - rfc951_sleep (++retry); - bp.bp_secs = htons ((currticks () - starttime) / 20); + } + rfc951_sleep(retry); + breq->secs = htons ((currticks () - starttime) / 20); + retry++; + timeout *= 2; + if (timeout > 5) + timeout = 5; } - /* Timeout. */ - return 0; + error_message("No DHCP reply received"); + return -1; +} + +static void add_vendor_code(struct bootp_request * breq, unsigned char option, unsigned char length, void * data) +{ + unsigned char * chptr; + int theOption, theLength; + + chptr = breq->vendor; + chptr += 4; + while (*chptr != 0xFF && *chptr != option) { + theOption = *chptr++; + if (!theOption) continue; + theLength = *chptr++; + chptr += theLength; + } + + *chptr++ = option; + *chptr++ = length; + memcpy(chptr, data, length); + chptr[length] = 0xff; } -enum return_type setup_network_intf_as_dhcp(struct interface_info * intf) + +enum return_type perform_dhcp(struct interface_info * intf) { - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock < 0) { - return log_perror("socket"); - return -1; + int s, i; + struct sockaddr_in server_addr; + struct sockaddr_in client_addr; + struct sockaddr_in broadcast_addr; + struct bootp_request breq, bresp; + unsigned char messageType; + unsigned int lease; + time_t startTime = time(NULL); + short aShort; + int num_options; + char requested_options[50]; + + if (strncmp(intf->device, "eth", 3)) { + error_message("DHCP available only for Ethernet networking"); + return RETURN_ERROR; } - return RETURN_ERROR; + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + log_perror("socket"); + return RETURN_ERROR; + } + + if (initial_setup_interface(intf->device, s) != 0) { + close(s); + return RETURN_ERROR; + } + + if (prepare_request(&breq, s, intf->device, startTime) != 0) { + close(s); + return RETURN_ERROR; + } + + messageType = DHCP_TYPE_DISCOVER; + add_vendor_code(&breq, DHCP_OPTION_TYPE, 1, &messageType); + + memset(&client_addr.sin_addr, 0, sizeof(&client_addr.sin_addr)); + client_addr.sin_family = AF_INET; + client_addr.sin_port = htons(BOOTP_CLIENT_PORT); /* bootp client */ + + if (bind(s, (struct sockaddr *) &client_addr, sizeof(client_addr))) { + log_perror("bind"); + return RETURN_ERROR; + } + + broadcast_addr.sin_family = AF_INET; + broadcast_addr.sin_port = htons(BOOTP_SERVER_PORT); /* bootp server */ + memset(&broadcast_addr.sin_addr, 0xff, sizeof(broadcast_addr.sin_addr)); /* broadcast */ + + log_message("DHCP: sending DISCOVER"); + + wait_message("Sending DHCP request..."); + i = handle_transaction(s, &breq, &bresp, &broadcast_addr, DHCP_TYPE_OFFER); + remove_wait_message(); + + if (i != 0) { + close(s); + return RETURN_ERROR; + } + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(BOOTP_SERVER_PORT); /* bootp server */ + if (get_vendor_code(&bresp, DHCP_OPTION_SERVER, &server_addr.sin_addr)) { + close(s); + log_message("DHCPOFFER didn't include server address"); + return RETURN_ERROR; + } + + init_vendor_codes(&breq); + messageType = DHCP_TYPE_REQUEST; + add_vendor_code(&breq, DHCP_OPTION_TYPE, 1, &messageType); + add_vendor_code(&breq, DHCP_OPTION_SERVER, 4, &server_addr.sin_addr); + add_vendor_code(&breq, DHCP_OPTION_REQADDR, 4, &bresp.yiaddr); + + aShort = ntohs(sizeof(struct bootp_request)); + add_vendor_code(&breq, DHCP_OPTION_MAXSIZE, 2, &aShort); + + num_options = 0; + requested_options[num_options++] = BOOTP_OPTION_NETMASK; + requested_options[num_options++] = BOOTP_OPTION_GATEWAY; + requested_options[num_options++] = BOOTP_OPTION_DNS; + requested_options[num_options++] = BOOTP_OPTION_DOMAIN; + requested_options[num_options++] = BOOTP_OPTION_BROADCAST; + add_vendor_code(&breq, DHCP_OPTION_OPTIONREQ, num_options, requested_options); + + /* request a lease of 1 hour */ + i = htonl(60 * 60); + add_vendor_code(&breq, DHCP_OPTION_LEASE, 4, &i); + + log_message("DHCP: sending REQUEST"); + + i = handle_transaction(s, &breq, &bresp, &broadcast_addr, DHCP_TYPE_ACK); + + if (i != 0) { + close(s); + return RETURN_ERROR; + } + + if (get_vendor_code(&bresp, DHCP_OPTION_LEASE, &lease)) { + log_message("failed to get lease time\n"); + return RETURN_ERROR; + } + lease = ntohl(lease); + + close(s); + + intf->netmask.s_addr = 0; + intf->broadcast.s_addr = 0; + intf->network.s_addr = 0; + + parse_reply(&bresp, intf); + + return RETURN_OK; } diff --git a/mdk-stage1/dhcp.h b/mdk-stage1/dhcp.h index f2c537bd4..6fb121ea4 100644 --- a/mdk-stage1/dhcp.h +++ b/mdk-stage1/dhcp.h @@ -26,147 +26,9 @@ #ifndef _DHCP_H_ #define _DHCP_H_ -#include <net/if.h> #include "stage1.h" #include "network.h" -enum return_type setup_network_intf_as_dhcp(struct interface_info * intf); - - -/* -=-=-- */ - -#define BOOTP_REQUEST 1 -#define BOOTP_REPLY 2 -#define MAX_BOOTP_RETRIES 20 - -#define ETHER_ADDR_SIZE 6 /* Size of Ethernet address */ -#define ETHER_HDR_SIZE 14 /* Size of ethernet header */ -#define ETH_MIN_PACKET 64 -#define ETH_MAX_PACKET 1518 - -#define TAG_LEN(p) (*((p)+1)) -#define RFC1533_COOKIE 99, 130, 83, 99 -#define RFC1533_PAD 0 -#define RFC1533_NETMASK 1 -#define RFC1533_TIMEOFFSET 2 -#define RFC1533_GATEWAY 3 -#define RFC1533_TIMESERVER 4 -#define RFC1533_IEN116NS 5 -#define RFC1533_DNS 6 -#define RFC1533_LOGSERVER 7 -#define RFC1533_COOKIESERVER 8 -#define RFC1533_LPRSERVER 9 -#define RFC1533_IMPRESSSERVER 10 -#define RFC1533_RESOURCESERVER 11 -#define RFC1533_HOSTNAME 12 -#define RFC1533_BOOTFILESIZE 13 -#define RFC1533_MERITDUMPFILE 14 -#define RFC1533_DOMAINNAME 15 -#define RFC1533_SWAPSERVER 16 -#define RFC1533_ROOTPATH 17 -#define RFC1533_EXTENSIONPATH 18 -#define RFC1533_IPFORWARDING 19 -#define RFC1533_IPSOURCEROUTING 20 -#define RFC1533_IPPOLICYFILTER 21 -#define RFC1533_IPMAXREASSEMBLY 22 -#define RFC1533_IPTTL 23 -#define RFC1533_IPMTU 24 -#define RFC1533_IPMTUPLATEAU 25 -#define RFC1533_INTMTU 26 -#define RFC1533_INTLOCALSUBNETS 27 -#define RFC1533_INTBROADCAST 28 -#define RFC1533_INTICMPDISCOVER 29 -#define RFC1533_INTICMPRESPOND 30 -#define RFC1533_INTROUTEDISCOVER 31 -#define RFC1533_INTROUTESOLICIT 32 -#define RFC1533_INTSTATICROUTES 33 -#define RFC1533_LLTRAILERENCAP 34 -#define RFC1533_LLARPCACHETMO 35 -#define RFC1533_LLETHERNETENCAP 36 -#define RFC1533_TCPTTL 37 -#define RFC1533_TCPKEEPALIVETMO 38 -#define RFC1533_TCPKEEPALIVEGB 39 -#define RFC1533_NISDOMAIN 40 -#define RFC1533_NISSERVER 41 -#define RFC1533_NTPSERVER 42 -#define RFC1533_VENDOR 43 -#define RFC1533_NBNS 44 -#define RFC1533_NBDD 45 -#define RFC1533_NBNT 46 -#define RFC1533_NBSCOPE 47 -#define RFC1533_XFS 48 -#define RFC1533_XDM 49 -#define RFC2132_REQ_ADDR 50 -#define RFC2132_MSG_TYPE 53 -#define RFC2132_SRV_ID 54 -#define RFC2132_PARAM_LIST 55 -#define RFC2132_MAX_SIZE 57 - -#define DHCPDISCOVER 1 -#define DHCPOFFER 2 -#define DHCPREQUEST 3 -#define DHCPACK 5 - -#define DHCP_OPT_LEN 312 - - -#define TICKS_PER_SEC 18 - -/* Inter-packet retry in ticks */ -#define TIMEOUT (10*TICKS_PER_SEC) - - -struct arprequest { - unsigned short hwtype; - unsigned short protocol; - char hwlen; - char protolen; - unsigned short opcode; - char shwaddr[6]; - char sipaddr[4]; - char thwaddr[6]; - char tipaddr[4]; -}; - -struct iphdr_ { - char verhdrlen; - char service; - unsigned short len; - unsigned short ident; - unsigned short frags; - char ttl; - char protocol; - unsigned short chksum; - struct in_addr src; - struct in_addr dest; -}; - -struct udphdr { - unsigned short src; - unsigned short dest; - unsigned short len; - unsigned short chksum; -}; - -struct bootp_t { - struct iphdr_ ip; - struct udphdr udp; - char bp_op; - char bp_htype; - char bp_hlen; - char bp_hops; - unsigned long bp_xid; - unsigned short bp_secs; - unsigned short unused; - struct in_addr bp_ciaddr; - struct in_addr bp_yiaddr; - struct in_addr bp_siaddr; - struct in_addr bp_giaddr; - char bp_hwaddr[16]; - char bp_sname[64]; - char bp_file[128]; - char bp_vend[DHCP_OPT_LEN]; -}; - +enum return_type perform_dhcp(struct interface_info * intf); #endif diff --git a/mdk-stage1/network.c b/mdk-stage1/network.c index 92f832e26..457afdcaf 100644 --- a/mdk-stage1/network.c +++ b/mdk-stage1/network.c @@ -50,7 +50,7 @@ static void error_message_net(void) /* reduce code size */ } -static int configure_net_device(struct interface_info * intf) +int configure_net_device(struct interface_info * intf) { struct ifreq req; struct rtentry route; @@ -89,7 +89,7 @@ static int configure_net_device(struct interface_info * intf) strcpy(req.ifr_name, intf->device); if (intf->is_up == 1) { - log_message("interface already up, downing"); + log_message("interface already up, downing before reconfigure"); req.ifr_flags = 0; if (ioctl(s, SIOCSIFFLAGS, &req)) { @@ -170,10 +170,11 @@ static int configure_net_device(struct interface_info * intf) } /* host network informations */ -static char * hostname = NULL; -static char * domain = NULL; -static struct in_addr gateway = { 0 }; -static struct in_addr dns_server = { 0 }; +char * hostname = NULL; +char * domain = NULL; +struct in_addr gateway = { 0 }; +struct in_addr dns_server = { 0 }; +struct in_addr dns_server2 = { 0 }; static int add_default_route(void) { @@ -228,9 +229,6 @@ static int write_resolvconf(void) { char * filename = "/etc/resolv.conf"; FILE * f; - if (IS_TESTING) - return 1; - if (dns_server.s_addr == 0) { log_message("resolvconf needs a dns server"); return -1; @@ -245,6 +243,8 @@ static int write_resolvconf(void) { if (domain) fprintf(f, "search %s\n", domain); /* we can live without the domain search (user will have to enter fully-qualified names) */ fprintf(f, "nameserver %s\n", inet_ntoa(dns_server)); + if (dns_server2.s_addr != 0) + fprintf(f, "nameserver %s\n", inet_ntoa(dns_server2)); fclose(f); res_init(); /* reinit the resolver so DNS changes take affect */ @@ -253,16 +253,73 @@ static int write_resolvconf(void) { } -static void guess_netmask(struct interface_info * intf, struct in_addr * addr) +static int save_netinfo(struct interface_info * intf) { + char * file_network = "/tmp/network"; + char file_intf[500]; + FILE * f; + + if (dns_server.s_addr == 0) { + log_message("resolvconf needs a dns server"); + return -1; + } + + f = fopen(file_network, "w"); + if (!f) { + log_perror(file_network); + return -1; + } + + fprintf(f, "NETWORKING=yes\n"); + fprintf(f, "FORWARD_IPV4=false\n"); + + if (hostname) + fprintf(f, "HOSTNAME=%s\n", hostname); + if (domain) + fprintf(f, "DOMAINNAME=%s\n", domain); + + if (gateway.s_addr != 0) + fprintf(f, "GATEWAY=%s\n", inet_ntoa(gateway)); + + fclose(f); + + + strcpy(file_intf, "/tmp/ifcfg-"); + strcat(file_intf, intf->device); + + f = fopen(file_intf, "w"); + if (!f) { + log_perror(file_intf); + return -1; + } + + fprintf(f, "DEVICE=%s\n", intf->device); + + if (intf->boot_proto == BOOTPROTO_DHCP) + fprintf(f, "BOOTPROTO=dhcp\n"); + else { + fprintf(f, "BOOTPROTO=static\n"); + fprintf(f, "IPADDR=%s\n", inet_ntoa(intf->ip)); + fprintf(f, "NETMASK=%s\n", inet_ntoa(intf->netmask)); + fprintf(f, "NETWORK=%s\n", inet_ntoa(intf->network)); + fprintf(f, "BROADCAST=%s\n", inet_ntoa(intf->broadcast)); + } + + fclose(f); + + return 0; +} + + +void guess_netmask(struct interface_info * intf) { unsigned long int tmp = ntohl(intf->ip.s_addr); if (((tmp & 0xFF000000) >> 24) <= 127) - inet_aton("255.0.0.0", addr); + inet_aton("255.0.0.0", &intf->netmask); else if (((tmp & 0xFF000000) >> 24) <= 191) - inet_aton("255.255.0.0", addr); + inet_aton("255.255.0.0", &intf->netmask); else - inet_aton("255.255.255.0", addr); - log_message("netmask guess: %s", inet_ntoa(*addr)); + inet_aton("255.255.255.0", &intf->netmask); + log_message("netmask guess: %s", inet_ntoa(intf->netmask)); } @@ -314,10 +371,10 @@ static enum return_type setup_network_interface(struct interface_info * intf) error_message("Invalid netmask"); return setup_network_interface(intf); } + memcpy(&intf->netmask, &addr, sizeof(addr)); } else - guess_netmask(intf, &addr); - memcpy(&intf->netmask, &addr, sizeof(addr)); + guess_netmask(intf); *((uint32_t *) &intf->broadcast) = (*((uint32_t *) &intf->ip) & *((uint32_t *) &intf->netmask)) | ~(*((uint32_t *) &intf->netmask)); @@ -333,16 +390,13 @@ static enum return_type setup_network_interface(struct interface_info * intf) } intf->boot_proto = BOOTPROTO_STATIC; } else { - error_message("Currently unsupported"); - return RETURN_ERROR; + results = perform_dhcp(intf); - results = RETURN_ERROR; //setup_network_intf_as_dhcp(intf); if (results == RETURN_BACK) return setup_network_interface(intf); if (results == RETURN_ERROR) return results; intf->boot_proto = BOOTPROTO_DHCP; - return RETURN_OK; } if (configure_net_device(intf)) @@ -350,7 +404,7 @@ static enum return_type setup_network_interface(struct interface_info * intf) return add_default_route(); } - +/* static enum return_type configure_network(struct interface_info * intf) { char ips[50]; @@ -389,43 +443,27 @@ static enum return_type configure_network(struct interface_info * intf) return RETURN_OK; } - +*/ static enum return_type bringup_networking(struct interface_info * intf) { static struct interface_info loopback; - enum { BRINGUP_NET, BRINGUP_CONF, BRINGUP_DONE } step = BRINGUP_NET; + enum return_type results = RETURN_ERROR; my_insmod("af_packet", ANY_DRIVER_TYPE, NULL); // if (intf->is_up == 1) // log_message("interface already up (with IP %s)", inet_ntoa(intf->ip)); - while (step != BRINGUP_DONE) { - enum return_type results = RETURN_ERROR; - switch (step) { - case BRINGUP_NET: - results = setup_network_interface(intf); - if (results != RETURN_OK) - return results; - step = BRINGUP_CONF; - break; - - case BRINGUP_CONF: - write_resolvconf(); - results = configure_network(intf); - if (results != RETURN_OK) - step = BRINGUP_NET; - else - step = BRINGUP_DONE; - break; - - case BRINGUP_DONE: - break; - } - } +// while (results != RETURN_OK) { + results = setup_network_interface(intf); + if (results != RETURN_OK) + return results; + write_resolvconf(); +// results = configure_network(intf); +// } - write_resolvconf(); /* maybe we have now domain to write also */ +// write_resolvconf(); /* maybe we have now domain to write also */ if (loopback.is_up == 0) { int rc; @@ -490,6 +528,7 @@ static enum return_type intf_select_and_up(void) static int num_interfaces = 0; struct interface_info * sel_intf = NULL; int i; + enum return_type results; char * iface = interface_select(); if (iface == NULL) @@ -506,7 +545,12 @@ static enum return_type intf_select_and_up(void) num_interfaces++; } - return bringup_networking(sel_intf); + results = bringup_networking(sel_intf); + + if (results == RETURN_OK) + save_netinfo(sel_intf); + + return results; } diff --git a/mdk-stage1/network.h b/mdk-stage1/network.h index 9f59cb29e..8d6b2bb17 100644 --- a/mdk-stage1/network.h +++ b/mdk-stage1/network.h @@ -38,20 +38,21 @@ enum boot_proto_type { BOOTPROTO_STATIC, BOOTPROTO_DHCP }; struct interface_info { char device[10]; int is_ptp, is_up; - int set, manually_set; struct in_addr ip, netmask, broadcast, network; - struct in_addr boot_server; - char * boot_file; enum boot_proto_type boot_proto; }; -struct net_info { - int set, manually_set; - char * hostname, * domain; /* dynamically allocated */ - struct in_addr gateway; - struct in_addr dns_server; - int numDns; -}; + +/* these are to be used only by dhcp.c */ + +void guess_netmask(struct interface_info * intf); +int configure_net_device(struct interface_info * intf); + +extern char * hostname; +extern char * domain; +extern struct in_addr gateway; +extern struct in_addr dns_server; +extern struct in_addr dns_server2; |