changeset 3097:a75d37e86790

Create new package for DHCP/BOOTP support so that it can be disable (not built). Moved options specific to DHCP/BOOTP into the new package. Rewrite DHCP/BOOTP implementation to fix several problems: race condition when receiving responses from multiple servers; broken retry mechanism; XID is hard-wired constant; XID not verified properly. [ Bugzilla 1001177 ]
author grante
date Tue, 06 Mar 2012 16:45:24 +0000
parents 73ce0d6424ec
children 64c563c52ef5
files packages/redboot/current/ChangeLog packages/redboot/current/cdl/redboot.cdl packages/redboot/current/include/redboot.h packages/redboot/current/src/net/bootp.c packages/redboot/current/src/net/net_io.c
diffstat 5 files changed, 383 insertions(+), 277 deletions(-) [+]
line wrap: on
line diff
--- a/packages/redboot/current/ChangeLog
+++ b/packages/redboot/current/ChangeLog
@@ -1,3 +1,20 @@
+2012-03-06  Grant Edwards  <grant.b.edwards@gmail.com>
+
+	* src/net/net_io.c:
+	* cdl/redboot.cdl: Added new CDL option
+	CYGPKG_REDBOOT_NETWORKING_BOOTP to control whether BOOTP/DHCP
+	support is built into Redboot.  Moved "DEFAULT_NO_BOOTP" option
+	into the new package.
+	[ Bugzilla 1001177 ]
+
+	* src/net/bootp.c:
+	* cdl/redboot.cdl: Re-wrote BOOTP/DHCP client code to fix retry
+	mechanism and to fix race conditions that were corrupting the
+	received IP configuration data.  Added verbose BOOTP/DHCP output
+	CDL option.
+	[ Bugzilla 1001177 ]
+
+
 2011-12-02  John Dallaway  <john@dallaway.org.uk>
 
 	* cdl/redboot.cdl: Fix typographical error. Issue reported by
--- a/packages/redboot/current/cdl/redboot.cdl
+++ b/packages/redboot/current/cdl/redboot.cdl
@@ -251,6 +251,7 @@ cdl_package CYGPKG_REDBOOT {
             display       "Include POSIX checksum command"
             doc           ref/cksum-command.html
             default_value 1
+            requires      CYGPKG_CRC
             compile -library=libextras.a cksum.c
         }
 
@@ -339,7 +340,7 @@ cdl_package CYGPKG_REDBOOT {
             active_if     CYGPKG_IO_ETH_DRIVERS
             default_value 1
             implements    CYGINT_REDBOOT_LOAD_METHOD
-            compile net/bootp.c net/udp.c net/ip.c net/pktbuf.c net/cksum.c
+            compile net/udp.c net/ip.c net/pktbuf.c net/cksum.c
             compile net/enet.c net/icmp.c net/tcp.c net/timers.c net/arp.c
             compile net/inet_addr.c
             compile -library=libextras.a net/ping.c net/net_io.c 
@@ -389,17 +390,6 @@ cdl_package CYGPKG_REDBOOT {
                     an IP address is configured into the Flash
                     configuration, that will be used in preference."
     
-                cdl_option CYGSEM_REDBOOT_DEFAULT_NO_BOOTP {
-                     display          "Do not try to use BOOTP"
-                     flavor           bool
-                     default_value    0
-                     description      "
-                         By default Redboot tries to use BOOTP to get an IP
-                         address. If there's no BOOTP server on your network
-                         use this option to avoid the wait until the
-                         timeout. This option is overriden by the
-                         configuration stored in flash."
-                }
                 cdl_option CYGDAT_REDBOOT_DEFAULT_BOOTP_SERVER_IP_ADDR {
                      display           "Default bootp server"
                      flavor            booldata
@@ -415,21 +405,54 @@ cdl_package CYGPKG_REDBOOT {
                           preference."
                 }
             }
-    
-            cdl_component CYGSEM_REDBOOT_NETWORKING_DHCP {
-                display          "Use DHCP to get IP information"
+
+            cdl_component CYGPKG_REDBOOT_NETWORKING_BOOTP {
+                display          "Enable BOOTP/DHCP support"
                 flavor           bool
                 default_value    1
+                compile          net/bootp.c
                 description      "
-                    Use DHCP protocol to obtain pertinent IP addresses, such 
-                    as the client, server, gateway, etc."
+                    Use BOOTP/DHCP protocol to obtain pertinent IP
+                    addresses, such as the client, server, gateway,
+                    etc."
+                
+                cdl_component CYGSEM_REDBOOT_NETWORKING_DHCP {
+                    display          "Enable DHCP in addition to BOOTP support"
+                    flavor           bool
+                    default_value    1
+                    description      "
+                    Enables DHCP protocol in addition to BOOTP.  If not enabled,
+                    only BOOTP will be suported."
+                }
+
+                cdl_component CYGSEM_REDBOOT_NETWORKING_BOOTP_VERBOSE {
+                    display          "Enable verbose output from BOOTP/DHCP code"
+                    flavor           bool
+                    default_value    0
+                    description      "
+                    Enable more verbose output from BOOTP/DHCP that logs rx/tx
+                    packet types and state-machine transitions."
+                }
+
+                cdl_option CYGSEM_REDBOOT_DEFAULT_NO_BOOTP {
+                    display          "Do not try to use BOOTP/DHCP"
+                    flavor           bool
+                    default_value    0
+                    description      "
+                         By default Redboot tries to use BOOTP/DHCP to
+                         get an IP address when BOOTP/DHCP support is
+                         present in Redboot. If there's no BOOTP/DHCP
+                         server on your network use this option to
+                         avoid waiting for the BOOTP/DHCP timeouts and
+                         retries. This option is overriden by the
+                         configuration stored in flash."
+                }
             }
-    
+
             cdl_component CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY {
                 display          "Use a gateway for non-local IP traffic"
                 flavor           bool
                 default_value    1
-                requires         CYGSEM_REDBOOT_NETWORKING_DHCP
                 description      "
                   Enabling this option will allow the RedBoot networking
                 stack to use a \[single\] gateway to reach a non-local
@@ -932,6 +955,7 @@ cdl_package CYGPKG_REDBOOT {
                 display       "Use CRC checksums on FIS images."
                 flavor        bool
                 default_value 1
+                requires      CYGPKG_CRC
                 description "
                   When this option is enabled, RedBoot will use CRC checksums
                   when reading and writing flash images."
--- a/packages/redboot/current/include/redboot.h
+++ b/packages/redboot/current/include/redboot.h
@@ -67,8 +67,10 @@
 #include <net/net.h>
 #include <net/bootp.h>
 // Determine an IP address for this node, using BOOTP
+#ifdef CYGPKG_REDBOOT_NETWORKING_BOOTP
 extern int __bootp_find_local_ip(bootp_header_t *info);
 #endif
+#endif
 
 #ifdef DEFINE_VARS
 #define EXTERN
--- a/packages/redboot/current/src/net/bootp.c
+++ b/packages/redboot/current/src/net/bootp.c
@@ -40,11 +40,13 @@
 //#####DESCRIPTIONBEGIN####
 //
 // Author(s):    gthomas
+//               grant edwards
 // Contributors: gthomas
-// Date:         2000-07-14
-// Purpose:      
-// Description:  
-//              
+//               grant edwards
+// Date:         2011-03-18
+// Purpose:
+// Description:
+//
 // This code is part of RedBoot (tm).
 //
 //####DESCRIPTIONEND####
@@ -55,299 +57,341 @@
 #include <net/net.h>
 #include <net/bootp.h>
 
-#define SHOULD_BE_RANDOM  0x12345555
+#define RETRY_TIME_MS  2000
+#define MAX_RETRIES    4
 
-/* How many milliseconds to wait before retrying the request */
-#define RETRY_TIME  2000
-#define MAX_RETRIES    8
-
+static unsigned xid;                   // transaction ID,  should be random/unique
+static const ip_route_t broadcast = { {255, 255, 255, 255},
+                                      {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} };
 static bootp_header_t *bp_info;
-  
+
 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
-static const unsigned char dhcpCookie[] = {99,130,83,99};
-static const unsigned char dhcpEnd[] = {255};
-static const unsigned char dhcpDiscover[] = {53,1,1};
-static const unsigned char dhcpRequest[] = {53,1,3};
-static const unsigned char dhcpRequestIP[] = {50,4};
-static const unsigned char dhcpParamRequestList[] = {55,3,1,3,6};
+static const unsigned char dhcpCookie[] =           { 99, 130, 83, 99 };
+static const unsigned char dhcpDiscover[] =         { 53, 1, 1 };
+static const unsigned char dhcpRequest[] =          { 53, 1, 3 };
+static const unsigned char dhcpRequestIP[] =        { 50, 4 };
+static const unsigned char dhcpParamRequestList[] = { 55, 3, 1, 3, 6 };
+static const unsigned char dhcpEnd[] =              { 255 };
+#endif
+
+// FSM states -- only NONE and DONE are used in BOOTP mode
 static enum {
     DHCP_NONE = 0,
-    DHCP_DISCOVER,
-    DHCP_OFFER,
-    DHCP_REQUEST,
-    DHCP_ACK
+    DHCP_WAITING_FOR_OFFER,
+    DHCP_WAITING_FOR_ACK,
+    DHCP_DONE,
+    DHCP_FAILED
 } dhcpState;
+
+#if !defined(CYGSEM_REDBOOT_NETWORKING_BOOTP_VERBOSE)
+# define debug_printf(format, ...)     /* noop */
+#else
+# define debug_printf(format, ...)  diag_printf(format, ##__VA_ARGS__)
+static const char *dhcpStateString[] =  { "NONE", "WAITING_FOR_OFFER", "WAITING_FOR_ACK", "DONE", "FAILED" };
+# ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
+static const char *dhcpTypeString[] =  { "0x00", "DISCOVER", "OFFER", "REQUEST", "0x04", "ACK", "NAK", "0x07" };
+# endif
+#endif
+
+// parse network configuration from a DHCP ACK packet or BOOTP REPLY
+static void
+parseConfig(bootp_header_t *bp, int len)
+{
+#ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
+    unsigned char  *end,
+                   *p;
+    int             optlen;
+#endif
+    memcpy(__local_ip_addr, &bp->bp_yiaddr, sizeof __local_ip_addr);
+#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
+    memcpy(__local_ip_gate, &bp->bp_giaddr, sizeof __local_ip_gate);
 #endif
 
+#ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
+
+    // are there option fields to parse?
+    if (memcmp(bp->bp_vend, dhcpCookie, sizeof dhcpCookie))
+        return;
+
+    p = bp->bp_vend + 4;
+    end = (unsigned char *)bp + len;
+    while (p < end) {
+        unsigned char tag = *p;
+        if (tag == TAG_END)
+            break;
+        if (tag == TAG_PAD)
+            optlen = 1;
+        else {
+            optlen = p[1];
+            p += 2;
+            switch (tag) {
+# ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
+            case TAG_SUBNET_MASK:
+                memcpy(__local_ip_mask, p, sizeof __local_ip_mask);
+                break;
+            case TAG_GATEWAY:
+                memcpy(__local_ip_gate, p, sizeof __local_ip_gate);
+                break;
+# endif
+# ifdef CYGPKG_REDBOOT_NETWORKING_DNS
+            case TAG_DOMAIN_SERVER:
+                memcpy(&__bootp_dns_addr, p, sizeof __bootp_dns_addr);
+                __bootp_dns_set = 1;
+                break;
+# endif
+            default:
+                break;
+            }
+        }
+        p += optlen;
+    }
+#endif
+}
+
+// functions used to prepare BOOTP/DHCP tx packets
+
+// basic BOOTP request
 static void
-bootp_handler(udp_socket_t *skt, char *buf, int len,
-	      ip_route_t *src_route, word src_port)
+prep_bootp_request(bootp_header_t *b)
+{
+    memset(b, 0, sizeof *b);
+    b->bp_op = BOOTREQUEST;
+    b->bp_htype = HTYPE_ETHERNET;
+    b->bp_hlen = sizeof __local_enet_addr;
+    b->bp_xid = xid;
+    memcpy(b->bp_chaddr, __local_enet_addr, sizeof b->bp_chaddr);
+}
+
+#ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
+
+# define AddOption(p,d) do {memcpy(p,d,sizeof d); p += sizeof d;} while (0)
+
+// add DHCP DISCOVER fields to a basic BOOTP request
+static int
+prep_dhcp_discover(bootp_header_t *b)
+{
+    unsigned char  *p = b->bp_vend;
+    AddOption(p, dhcpCookie);
+    AddOption(p, dhcpDiscover);
+    AddOption(p, dhcpParamRequestList);
+    AddOption(p, dhcpEnd);
+    if (p < &b->bp_vend[BP_MIN_VEND_SIZE])
+        p = &b->bp_vend[BP_MIN_VEND_SIZE];
+    return p - (unsigned char *)b;
+}
+
+// add DHCP REQUEST fields to a basic BOOTP request using data from supplied DHCP OFFER
+static int
+prep_dhcp_request(bootp_header_t *b, bootp_header_t *offer)
+{
+    unsigned char  *p = b->bp_vend;
+    AddOption(p, dhcpCookie);
+    AddOption(p, dhcpRequest);
+    AddOption(p, dhcpRequestIP);
+    memcpy(p, &offer->bp_yiaddr, dhcpRequestIP[1]);
+    p += dhcpRequestIP[1];                            // Ask for the address just given
+    AddOption(p, dhcpParamRequestList);
+    AddOption(p, dhcpEnd);
+    if (p < &b->bp_vend[BP_MIN_VEND_SIZE])
+        p = &b->bp_vend[BP_MIN_VEND_SIZE];
+    return p - (unsigned char *)b;
+}
+#endif
+
+// Macro used to change state of BOOTP/DHCP state machine
+#define NewDhcpState(state)  do {dhcpState = state; debug_printf("DHCP state: %s\n",dhcpStateString[state]);}while(0)
+
+// send BOOTP REQUEST or DHCP DISCOVER
+static void
+bootp_start(void)
+{
+    int             txSize;
+    bootp_header_t  b;
+    // send out a BOOTP request or DHCP DISCOVER
+    prep_bootp_request(&b);            // basic BOOTP request
+#ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
+    debug_printf("DHCP  send: DISCOVER\n");
+    NewDhcpState(DHCP_WAITING_FOR_OFFER);
+    txSize = prep_dhcp_discover(&b);   // make it into DHCP DISCOVER
+#else
+    debug_printf("BOOTP send: REQUEST\n");
+    txSize = sizeof(b);
+#endif
+    __udp_send((char *)&b, txSize, (ip_route_t*)&broadcast, IPPORT_BOOTPS, IPPORT_BOOTPC);
+}
+
+// save packet for use by other commands
+static void
+save_packet(void *b, int len)
+{
+    memset(bp_info, 0, sizeof *bp_info);
+    if ((unsigned)len > sizeof *bp_info)
+        len = sizeof *bp_info;
+    memcpy(bp_info, b, len);
+}
+
+// Handler for received BOOTP/DHCP packets
+static void
+bootp_handler(udp_socket_t *skt, char *buf, int len, ip_route_t *src_route,
+              word src_port)
 {
     bootp_header_t *b;
 #ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
-    unsigned char *p, expected = 0;
+    int             txSize;
+    int             type;
+    bootp_header_t  txpkt;
+    unsigned        expected = 0;
 #endif
 
-    b = (bootp_header_t *)buf;
-    if (bp_info) {
-        memset(bp_info,0,sizeof *bp_info);
-        if (len > sizeof *bp_info)
-            len = sizeof *bp_info;
-        memcpy(bp_info, b, len);
+    b = (bootp_header_t *) buf;
+
+    // only accept BOOTP REPLY responses
+    if (b->bp_op != BOOTREPLY)
+        return;
+
+    // must be sent to me
+    if (memcmp(b->bp_chaddr, __local_enet_addr, b->bp_hlen))
+        return;
+
+    // verify XID
+    if (b->bp_xid != xid)
+        return;
+
+#if !defined(CYGSEM_REDBOOT_NETWORKING_DHCP)
+    // simple BOOTP - this is all there is!
+    debug_printf("BOOTP recv: REPLY\n");
+    if (dhcpState != DHCP_DONE) {
+        save_packet(b, len);
+        parseConfig(b, len);
+        NewDhcpState(DHCP_DONE);
+    }
+#else
+    // DHCP support is enabled...
+
+    // Check to see that it's a DHCP packet with a DHCP type field
+
+    type = -1;
+    if (!memcmp(b->bp_vend, dhcpCookie, sizeof dhcpCookie)) {
+        unsigned char *p = b->bp_vend + 4;
+        while (p < (unsigned char *)b + len) {
+            if (*p == TAG_DHCP_MESS_TYPE) {
+                type = p[2];
+                break;
+            }
+            p += p[1] + 2;
+        }
     }
 
-    // Only accept pure REPLY responses
-    if (b->bp_op != BOOTREPLY)
-      return;
-    
-    // Must be sent to me, as well!
-    if (memcmp(b->bp_chaddr, __local_enet_addr, 6))
-      return;
+    if (type == -1) {
+        // apparently we have a BOOTP (but not not DHCP) server
+        debug_printf("DHCP  recv: BOOTP-REPLY -- falling back to BOOTP mode\n");
+        if (dhcpState != DHCP_DONE) {
+            save_packet(b, len);
+            parseConfig(b, len);
+            NewDhcpState(DHCP_DONE);
+        }
+        return;
+    }
         
-#ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
-    p = b->bp_vend;
-    if (memcmp(p, dhcpCookie, sizeof(dhcpCookie)))
-      return;
-    p += 4;
-
-    // Find the DHCP Message Type tag
-    while (*p != TAG_DHCP_MESS_TYPE) {
-        p += p[1] + 2;
-        if (p >= (unsigned char*)b + sizeof(*bp_info))
-            return;
-    }
-
-    p += 2;
+    // it's a real DHCP packet
+    debug_printf("DHCP  recv: %s [%d]\n", dhcpTypeString[type], type);
 
     switch (dhcpState) {
-    case DHCP_DISCOVER:
-        // The discover message has been sent, only accept an offer reply
-        if (*p == DHCP_MESS_TYPE_OFFER) {
-            dhcpState = DHCP_OFFER;
+    case DHCP_WAITING_FOR_OFFER:
+        if (type == (expected = DHCP_MESS_TYPE_OFFER)) {
+            prep_bootp_request(&txpkt);
+            txSize = prep_dhcp_request(&txpkt, b);
+            debug_printf("DHCP  send: REQUEST\n");
+            NewDhcpState(DHCP_WAITING_FOR_ACK);
+            __udp_send((char *)&txpkt, txSize, (ip_route_t *)&broadcast, IPPORT_BOOTPS, IPPORT_BOOTPC);
             return;
-        } else {
-            expected = DHCP_MESS_TYPE_OFFER;
         }
         break;
-    case DHCP_REQUEST:
-        // The request message has been sent, only accept an ack reply
-        if (*p == DHCP_MESS_TYPE_ACK) {
-            dhcpState = DHCP_ACK;
+
+    case DHCP_WAITING_FOR_ACK:
+        if (type == (expected = DHCP_MESS_TYPE_ACK)) {
+            save_packet(b, len);
+            parseConfig(b, len);
+            NewDhcpState(DHCP_DONE);
             return;
-        } else {
-            expected = DHCP_MESS_TYPE_ACK;
         }
         break;
-    case DHCP_NONE:
-    case DHCP_OFFER:
-    case DHCP_ACK:
-        // Quitely ignore these - they indicate repeated message from server
+
+    default:
+        debug_printf("DHCP packet ignored\n");
         return;
     }
-    // See if we've been NAK'd - if so, give up and try again
-    if (*p == DHCP_MESS_TYPE_NAK) {
-        dhcpState = DHCP_NONE;
+
+    if (type == DHCP_MESS_TYPE_NAK && dhcpState != DHCP_DONE) {
+        NewDhcpState(DHCP_FAILED);
         return;
     }
-    diag_printf("DHCP reply: %d, not %d\n", (int)*p, (int)expected);
-    return;
-#else
-    // Simple BOOTP - this is all there is!
-    memcpy(__local_ip_addr, &b->bp_yiaddr, 4);
+
+    debug_printf("DHCP packet ignored -- expected %d[%s]\n", expected, dhcpTypeString[expected]);
 #endif
 }
 
-#define AddOption(p,d) do {memcpy(p,d,sizeof d); p += sizeof d;} while (0)
 
-/*
- * Find our IP address and copy to __local_ip_addr.
- * Return zero if successful, -1 if not.
- */
+// Request IP configuration via BOOTP/DHCP.
+// Return zero if successful, -1 if not.
+
 int
 __bootp_find_local_ip(bootp_header_t *info)
 {
-    udp_socket_t udp_skt;
-    bootp_header_t b;
-    ip_route_t     r;
-    int            retry;
-    unsigned long  start;
-    ip_addr_t saved_ip_addr;
-#ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
-    unsigned char *p;
-    int oldState;
-#endif
-    int txSize;
-    bool abort = false;
-    static int xid = SHOULD_BE_RANDOM;
+    udp_socket_t    udp_skt;
+    int             retry;
+    unsigned long   start;
+    ip_addr_t       saved_ip_addr;
 
-#ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
-    dhcpState = DHCP_NONE;
-#endif
+    bp_info = info;
+
+    diag_printf("\nRequesting IP conf via BOOTP/DHCP...\n");
 
-    // Where we want the results saved
-    bp_info = info;
-    // Preserve any IP address we currently have, just in case
-    memcpy(saved_ip_addr, __local_ip_addr, sizeof(__local_ip_addr));
+    memcpy(&xid, __local_enet_addr, sizeof xid);
+    xid ^= (__local_enet_addr[4]<<16) + __local_enet_addr[5];
+    xid ^= (unsigned)&retry + (unsigned)&__bootp_find_local_ip;
 
-    // fill out route for a broadcast
-    r.ip_addr[0] = 255;
-    r.ip_addr[1] = 255;
-    r.ip_addr[2] = 255;
-    r.ip_addr[3] = 255;
-    r.enet_addr[0] = 255;
-    r.enet_addr[1] = 255;
-    r.enet_addr[2] = 255;
-    r.enet_addr[3] = 255;
-    r.enet_addr[4] = 255;
-    r.enet_addr[5] = 255;
+    debug_printf("XID: %08x\n",xid);
 
-    // setup a socket listener for bootp replies
+    memcpy(saved_ip_addr, __local_ip_addr, sizeof __local_ip_addr);    // save our IP in case of failure
+
+    NewDhcpState(DHCP_NONE);
+
     __udp_install_listener(&udp_skt, IPPORT_BOOTPC, bootp_handler);
 
-    retry = MAX_RETRIES;  
-    do {
-	start = MS_TICKS();
+    retry = MAX_RETRIES;
 
-        // Build up the BOOTP/DHCP request
-        memset(&b, 0, sizeof(b));
-        b.bp_op = BOOTREQUEST;
-        b.bp_htype = HTYPE_ETHERNET;
-        b.bp_hlen = 6;
-        b.bp_xid = xid++;
-        memcpy(b.bp_chaddr, __local_enet_addr, 6);
+    while (retry > 0) {
+        start = MS_TICKS();
         memset(__local_ip_addr, 0, sizeof(__local_ip_addr));
-         
-#ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
-        p = b.bp_vend;
-        switch (dhcpState) {
-        case DHCP_NONE:
-        case DHCP_DISCOVER:
-            AddOption(p,dhcpCookie);
-            AddOption(p,dhcpDiscover);
-            AddOption(p,dhcpParamRequestList);
-            AddOption(p,dhcpEnd);
-            dhcpState = DHCP_DISCOVER;
-            break;
-        case DHCP_OFFER:
-            retry = MAX_RETRIES;
-        case DHCP_REQUEST:
-            b.bp_xid = bp_info->bp_xid;  // Match what server sent
-            AddOption(p,dhcpCookie);
-            AddOption(p,dhcpRequest);
-            AddOption(p,dhcpRequestIP);
-            memcpy(p, &bp_info->bp_yiaddr, 4);  p += 4;  // Ask for the address just given
-            AddOption(p,dhcpParamRequestList);
-            AddOption(p,dhcpEnd);
-            dhcpState = DHCP_REQUEST;
-            memset(&b.bp_yiaddr, 0xFF, 4);
-            memset(&b.bp_siaddr, 0xFF, 4);
-            memset(&b.bp_yiaddr, 0x00, 4);
-            memset(&b.bp_siaddr, 0x00, 4);
-            break;
-        case DHCP_ACK:
-            // Ignore these states (they won't happen)
-            break;
-        }
-     
-        // Some servers insist on a minimum amount of "vendor" data
-        if (p < &b.bp_vend[BP_MIN_VEND_SIZE]) p = &b.bp_vend[BP_MIN_VEND_SIZE];
-        txSize = p - (unsigned char*)&b;
-        oldState = dhcpState;
-#else
-        txSize = sizeof(b);
-#endif
 
-	__udp_send((char *)&b, txSize, &r, IPPORT_BOOTPS, IPPORT_BOOTPC);
-
-        // If we're retrying, inform the user
-        if (retry == (MAX_RETRIES-1))
-            diag_printf("... waiting for BOOTP information\n");
+        // send bootp REQUEST or dhcp DISCOVER
+        bootp_start();
 
-	do {
-	    __enet_poll();
-#ifdef CYGSEM_REDBOOT_NETWORKING_DHCP
-            if (dhcpState != oldState) {
-                if (dhcpState == DHCP_ACK) {
-                    unsigned char *end;
-                    int optlen;
-                    // Address information has now arrived!
-                    memcpy(__local_ip_addr, &bp_info->bp_yiaddr, 4);
-#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
-                    memcpy(__local_ip_gate, &bp_info->bp_giaddr, 4);
-#endif
-                    p = bp_info->bp_vend+4;
-                    end = (unsigned char *)bp_info+sizeof(*bp_info);
-                    while (p < end) {
-                        unsigned char tag = *p;
-                        if (tag == TAG_END)
-                            break;
-                        if (tag == TAG_PAD)
-                            optlen = 1;
-                        else {
-                            optlen = p[1];
-                            p += 2;
-                            switch (tag) {
-#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
-                            case TAG_SUBNET_MASK:  // subnet mask
-                                memcpy(__local_ip_mask,p,4); 
-                                break;
-                            case TAG_GATEWAY:  // router
-                                memcpy(__local_ip_gate,p,4); 
-                                break;
-#endif
-#ifdef CYGPKG_REDBOOT_NETWORKING_DNS
-			    case TAG_DOMAIN_SERVER:
-//				diag_printf(" DNS server found!\n");
-				memcpy(&__bootp_dns_addr, p, 4);
-				__bootp_dns_set = 1;
-				break;
-#ifdef CYGPKG_REDBOOT_NETWORKING_DNS_DHCP_DOMAIN
-                            case TAG_DOMAIN_NAME:
-                                if(optlen < sizeof(__bootp_dns_domain)) {
-                                    memcpy(__bootp_dns_domain, p, optlen);
-                                    __bootp_dns_domain[optlen] = '\0';
-                                    __bootp_dns_domain_set = 1;
-                                } else {
-                                  diag_printf("DNS domain name too long\n");
-                                }
-                                break;
-#endif //CYGPKG_REDBOOT_NETWORKING_DNS_DHCP_DOMAIN
-#endif //CYGPKG_REDBOOT_NETWORKING_DNS
-                            default:
-                                break;
-                            }
-                        }
-                        p += optlen;
-                    }
-                    __udp_remove_listener(IPPORT_BOOTPC);
-                    return 0;
-                } else {
-                    break;  // State changed, handle it
-                }
-            }
-#else
-            // All done, if address response has arrived
-	    if (__local_ip_addr[0] || __local_ip_addr[1] ||
-		__local_ip_addr[2] || __local_ip_addr[3]) {
-		/* success */
-		__udp_remove_listener(IPPORT_BOOTPC);
-		return 0;
-	    }
-#endif
-            if (_rb_break(1)) {
-                // The user typed ^C on the console
-                abort = true;
+        // wait for timeout, user-abort, or for receive packet handler to fail/succeed
+        while ((MS_TICKS_DELAY() - start) < RETRY_TIME_MS) {
+            __enet_poll();
+            if (dhcpState == DHCP_FAILED)
                 break;
-            }
-            MS_TICKS_DELAY();  // Count for ^C test
-	} while ((MS_TICKS_DELAY() - start) < RETRY_TIME);
-    } while (!abort && (retry-- > 0));
+            if (dhcpState == DHCP_DONE)
+                goto done;
+            if (_rb_break(1))          // did user hit ^C?
+                goto failed;
+            MS_TICKS_DELAY();
+        }
+        --retry;
+        ++xid;
+        diag_printf("TIMEOUT%s\n", retry ? ", retrying..." : "");
+    }
 
-    // timed out
+failed:
+    diag_printf("FAIL\n");
     __udp_remove_listener(IPPORT_BOOTPC);
-    // Restore any previous IP address
-    memcpy(__local_ip_addr, saved_ip_addr, sizeof(__local_ip_addr));
+    memcpy(__local_ip_addr, saved_ip_addr, sizeof __local_ip_addr);    // restore prev IP
     return -1;
+
+done:
+    diag_printf("OK\n");
+    __udp_remove_listener(IPPORT_BOOTPC);
+    return 0;
 }
-
-
--- a/packages/redboot/current/src/net/net_io.c
+++ b/packages/redboot/current/src/net/net_io.c
@@ -59,6 +59,10 @@
 #include <cyg/hal/hal_intr.h>
 #include <cyg/infra/cyg_ass.h>         // assertion macros
 
+#ifndef CYGSEM_REDBOOT_DEFAULT_NO_BOOTP
+#define CYGSEM_REDBOOT_DEFAULT_NO_BOOTP 0
+#endif
+
 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG
 #include <flash_config.h>
 
@@ -90,15 +94,14 @@ RedBoot_config_option("Default network d
 // negated (if false, the others apply) which makes the names even more
 // confusing.
 
-#ifndef CYGSEM_REDBOOT_DEFAULT_NO_BOOTP
-#define CYGSEM_REDBOOT_DEFAULT_NO_BOOTP 0
-#endif
+#ifdef CYGPKG_REDBOOT_NETWORKING_BOOTP
 RedBoot_config_option("Use BOOTP for network configuration",
                       bootp, 
                       ALWAYS_ENABLED, true,
                       CONFIG_BOOL,
                       !CYGSEM_REDBOOT_DEFAULT_NO_BOOTP
     );
+#endif
 RedBoot_config_option("Local IP address",
                       bootp_my_ip,
                       "bootp", false,
@@ -689,10 +692,10 @@ net_init(void)
 #endif
 
     // Set defaults as appropriate
-#ifdef CYGSEM_REDBOOT_DEFAULT_NO_BOOTP
+#if defined(CYGPKG_REDBOOT_NETWORKING_BOOTP) && !CYGSEM_REDBOOT_DEFAULT_NO_BOOTP
+    use_bootp = true;
+#else
     use_bootp = false;
-#else
-    use_bootp = true;
 #endif
 #ifdef CYGDBG_REDBOOT_NET_DEBUG
     net_debug = true;
@@ -707,7 +710,9 @@ net_init(void)
 #endif
     flash_get_config("net_debug", &net_debug, CONFIG_BOOL);
     flash_get_config("gdb_port", &gdb_port, CONFIG_INT);
+#ifdef CYGPKG_REDBOOT_NETWORKING_BOOTP
     flash_get_config("bootp", &use_bootp, CONFIG_BOOL);
+#endif
     if (!use_bootp) {
         flash_get_IP("bootp_my_ip", &__local_ip_addr);
 #ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
@@ -767,6 +772,7 @@ net_init(void)
         return;
     }    
     // Initialize the network [if present]
+#ifdef CYGPKG_REDBOOT_NETWORKING_BOOTP
     if (use_bootp) {
         if (__bootp_find_local_ip(&my_bootp_info) == 0) {
             have_net = true;
@@ -781,7 +787,9 @@ net_init(void)
                 have_net = true;
             }
         }
-    } else {
+    }
+#endif
+    if (!use_bootp) {
         if (__local_ip_addr[0] == 0 && __local_ip_addr[1] == 0 &&
             __local_ip_addr[2] == 0 && __local_ip_addr[3] == 0) {
             show_eth_info();
@@ -813,7 +821,11 @@ net_init(void)
     }
 }
 
-static char usage[] = "[-b] [-l <local_ip_address>[/<mask_len>]] [-h <server_address>]"
+static char usage[] = ""
+#ifdef CYGPKG_REDBOOT_NETWORKING_BOOTP
+       " [-b]"
+#endif
+       " [-l <local_ip_address>[/<mask_len>]] [-h <server_address>]"
 #ifdef CYGPKG_REDBOOT_NETWORKING_DNS
 	" [-d <dns_server_address>]"
 #ifdef CYGPKG_REDBOOT_NETWORKING_DNS_FCONFIG_DOMAIN                                                  
@@ -836,7 +848,9 @@ do_ip_addr(int argc, char *argv[])
     struct option_info opts[5];
     char *ip_addr, *host_addr;
     bool ip_addr_set, host_addr_set;
+#ifdef CYGPKG_REDBOOT_NETWORKING_BOOTP
     bool do_bootp = false;
+#endif
     struct sockaddr_in host;
 #ifdef CYGPKG_REDBOOT_NETWORKING_DNS
     char *dns_addr;
@@ -857,9 +871,12 @@ do_ip_addr(int argc, char *argv[])
               (void *)&ip_addr, (bool *)&ip_addr_set, "local IP address");
     init_opts(&opts[1], 'h', true, OPTION_ARG_TYPE_STR, 
               (void *)&host_addr, (bool *)&host_addr_set, "default server address");
+    num_opts = 2;
+#ifdef CYGPKG_REDBOOT_NETWORKING_BOOTP
     init_opts(&opts[2], 'b', false, OPTION_ARG_TYPE_FLG,
               &do_bootp, 0, "use BOOTP");
-    num_opts = 3;
+    num_opts++;
+#endif
 #ifdef CYGPKG_REDBOOT_NETWORKING_DNS
     init_opts(&opts[num_opts], 'd', true, OPTION_ARG_TYPE_STR, 
               (void *)&dns_addr, (bool *)&dns_addr_set, "DNS server address");
@@ -875,11 +892,13 @@ do_ip_addr(int argc, char *argv[])
     if (!scan_opts(argc, argv, 1, opts, num_opts, 0, 0, "")) {
         return;
     }
+#ifdef CYGPKG_REDBOOT_NETWORKING_BOOTP
     if (do_bootp) {
         if (__bootp_find_local_ip(&my_bootp_info) != 0) {
             diag_printf("Failed to get BOOTP address\n");
         }
     }
+#endif
     if (ip_addr_set) {
 #ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
         char *slash_pos;