These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / net / udp / dhcp.c
index 04fad04..aed5ee3 100644 (file)
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
  */
 
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 #include <string.h>
 #include <stdlib.h>
@@ -44,6 +48,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/dhcppkt.h>
 #include <ipxe/dhcp_arch.h>
 #include <ipxe/features.h>
+#include <config/dhcp.h>
 
 /** @file
  *
@@ -149,30 +154,32 @@ struct dhcp_session_state {
         * @v dhcppkt           DHCP packet
         * @v peer              Destination address
         */
-       int ( * tx ) ( struct dhcp_session *dhcp,
-                      struct dhcp_packet *dhcppkt,
+       int ( * tx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt,
                       struct sockaddr_in *peer );
-       /** Handle received packet
+       /**
+        * Handle received packet
         *
         * @v dhcp              DHCP session
         * @v dhcppkt           DHCP packet
         * @v peer              DHCP server address
         * @v msgtype           DHCP message type
         * @v server_id         DHCP server ID
+        * @v pseudo_id         DHCP server pseudo-ID
         */
-       void ( * rx ) ( struct dhcp_session *dhcp,
-                       struct dhcp_packet *dhcppkt,
-                       struct sockaddr_in *peer,
-                       uint8_t msgtype, struct in_addr server_id );
-       /** Handle timer expiry
+       void ( * rx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt,
+                       struct sockaddr_in *peer, uint8_t msgtype,
+                       struct in_addr server_id, struct in_addr pseudo_id );
+       /**
+        * Handle timer expiry
         *
         * @v dhcp              DHCP session
         */
        void ( * expired ) ( struct dhcp_session *dhcp );
        /** Transmitted message type */
        uint8_t tx_msgtype;
-       /** Apply minimum timeout */
-       uint8_t apply_min_timeout;
+       /** Timeout parameters */
+       uint8_t min_timeout_sec;
+       uint8_t max_timeout_sec;
 };
 
 static struct dhcp_session_state dhcp_state_discover;
@@ -272,9 +279,9 @@ static void dhcp_set_state ( struct dhcp_session *dhcp,
        dhcp->state = state;
        dhcp->start = currticks();
        stop_timer ( &dhcp->timer );
-       dhcp->timer.min_timeout =
-               ( state->apply_min_timeout ? DHCP_MIN_TIMEOUT : 0 );
-       dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT;
+       set_timer_limits ( &dhcp->timer,
+                          ( state->min_timeout_sec * TICKS_PER_SEC ),
+                          ( state->max_timeout_sec * TICKS_PER_SEC ) );
        start_timer_nodelay ( &dhcp->timer );
 }
 
@@ -334,11 +341,13 @@ static int dhcp_discovery_tx ( struct dhcp_session *dhcp,
  * @v peer             DHCP server address
  * @v msgtype          DHCP message type
  * @v server_id                DHCP server ID
+ * @v pseudo_id                DHCP server pseudo-ID
  */
 static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
                                struct dhcp_packet *dhcppkt,
                                struct sockaddr_in *peer, uint8_t msgtype,
-                               struct in_addr server_id ) {
+                               struct in_addr server_id,
+                               struct in_addr pseudo_id ) {
        struct in_addr ip;
        char vci[9]; /* "PXEClient" */
        int vci_len;
@@ -350,8 +359,11 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
        DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
               dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
               ntohs ( peer->sin_port ) );
-       if ( server_id.s_addr != peer->sin_addr.s_addr )
-               DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
+       if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
+            ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
+               DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
+               DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
+       }
 
        /* Identify offered IP address */
        ip = dhcppkt->dhcphdr->yiaddr;
@@ -392,10 +404,10 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
        }
 
        /* Select as ProxyDHCP offer, if applicable */
-       if ( server_id.s_addr && has_pxeclient &&
+       if ( pseudo_id.s_addr && has_pxeclient &&
             ( priority >= dhcp->proxy_priority ) ) {
                dhcppkt_put ( dhcp->proxy_offer );
-               dhcp->proxy_server = server_id;
+               dhcp->proxy_server = pseudo_id;
                dhcp->proxy_offer = dhcppkt_get ( dhcppkt );
                dhcp->proxy_priority = priority;
        }
@@ -415,7 +427,7 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
        /* If we can't yet transition to DHCPREQUEST, do nothing */
        elapsed = ( currticks() - dhcp->start );
        if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_offer ||
-                ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) )
+                ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) )
                return;
 
        /* Transition to DHCPREQUEST */
@@ -430,8 +442,18 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
 static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) {
        unsigned long elapsed = ( currticks() - dhcp->start );
 
+       /* If link is blocked, defer DHCP discovery (and reset timeout) */
+       if ( netdev_link_blocked ( dhcp->netdev ) ) {
+               DBGC ( dhcp, "DHCP %p deferring discovery\n", dhcp );
+               start_timer_fixed ( &dhcp->timer,
+                                   ( DHCP_DISC_START_TIMEOUT_SEC *
+                                     TICKS_PER_SEC ) );
+               return;
+       }
+
        /* Give up waiting for ProxyDHCP before we reach the failure point */
-       if ( dhcp->offer.s_addr && ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) {
+       if ( dhcp->offer.s_addr &&
+            ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) {
                dhcp_set_state ( dhcp, &dhcp_state_request );
                return;
        }
@@ -447,7 +469,8 @@ static struct dhcp_session_state dhcp_state_discover = {
        .rx                     = dhcp_discovery_rx,
        .expired                = dhcp_discovery_expired,
        .tx_msgtype             = DHCPDISCOVER,
-       .apply_min_timeout      = 1,
+       .min_timeout_sec        = DHCP_DISC_START_TIMEOUT_SEC,
+       .max_timeout_sec        = DHCP_DISC_END_TIMEOUT_SEC,
 };
 
 /**
@@ -493,11 +516,13 @@ static int dhcp_request_tx ( struct dhcp_session *dhcp,
  * @v peer             DHCP server address
  * @v msgtype          DHCP message type
  * @v server_id                DHCP server ID
+ * @v pseudo_id                DHCP server pseudo-ID
  */
 static void dhcp_request_rx ( struct dhcp_session *dhcp,
                              struct dhcp_packet *dhcppkt,
                              struct sockaddr_in *peer, uint8_t msgtype,
-                             struct in_addr server_id ) {
+                             struct in_addr server_id,
+                             struct in_addr pseudo_id ) {
        struct in_addr ip;
        struct settings *parent;
        struct settings *settings;
@@ -506,8 +531,11 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
        DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
               dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
               ntohs ( peer->sin_port ) );
-       if ( server_id.s_addr != peer->sin_addr.s_addr )
-               DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
+       if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
+            ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
+               DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
+               DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
+       }
 
        /* Identify leased IP address */
        ip = dhcppkt->dhcphdr->yiaddr;
@@ -584,7 +612,8 @@ static struct dhcp_session_state dhcp_state_request = {
        .rx                     = dhcp_request_rx,
        .expired                = dhcp_request_expired,
        .tx_msgtype             = DHCPREQUEST,
-       .apply_min_timeout      = 0,
+       .min_timeout_sec        = DHCP_REQ_START_TIMEOUT_SEC,
+       .max_timeout_sec        = DHCP_REQ_END_TIMEOUT_SEC,
 };
 
 /**
@@ -623,19 +652,26 @@ static int dhcp_proxy_tx ( struct dhcp_session *dhcp,
  * @v peer             DHCP server address
  * @v msgtype          DHCP message type
  * @v server_id                DHCP server ID
+ * @v pseudo_id                DHCP server pseudo-ID
  */
 static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
                            struct dhcp_packet *dhcppkt,
                            struct sockaddr_in *peer, uint8_t msgtype,
-                           struct in_addr server_id ) {
+                           struct in_addr server_id,
+                           struct in_addr pseudo_id ) {
        struct settings *settings = &dhcppkt->settings;
        int rc;
 
        DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
               dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
               ntohs ( peer->sin_port ) );
-       if ( server_id.s_addr != peer->sin_addr.s_addr )
-               DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
+       if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
+            ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
+               DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
+               DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
+       }
+       if ( dhcp_has_pxeopts ( dhcppkt ) )
+               DBGC ( dhcp, " pxe" );
        DBGC ( dhcp, "\n" );
 
        /* Filter out unacceptable responses */
@@ -643,8 +679,9 @@ static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
                return;
        if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) )
                return;
-       if ( server_id.s_addr /* Linux PXE server omits server ID */ &&
-            ( server_id.s_addr != dhcp->proxy_server.s_addr ) )
+       if ( ( pseudo_id.s_addr != dhcp->proxy_server.s_addr ) )
+               return;
+       if ( ! dhcp_has_pxeopts ( dhcppkt ) )
                return;
 
        /* Register settings */
@@ -669,7 +706,7 @@ static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) {
        unsigned long elapsed = ( currticks() - dhcp->start );
 
        /* Give up waiting for ProxyDHCP before we reach the failure point */
-       if ( elapsed > PROXYDHCP_MAX_TIMEOUT ) {
+       if ( elapsed > DHCP_REQ_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) {
                dhcp_finished ( dhcp, 0 );
                return;
        }
@@ -685,7 +722,8 @@ static struct dhcp_session_state dhcp_state_proxy = {
        .rx                     = dhcp_proxy_rx,
        .expired                = dhcp_proxy_expired,
        .tx_msgtype             = DHCPREQUEST,
-       .apply_min_timeout      = 0,
+       .min_timeout_sec        = DHCP_PROXY_START_TIMEOUT_SEC,
+       .max_timeout_sec        = DHCP_PROXY_END_TIMEOUT_SEC,
 };
 
 /**
@@ -753,19 +791,24 @@ static int dhcp_pxebs_accept ( struct dhcp_session *dhcp,
  * @v peer             DHCP server address
  * @v msgtype          DHCP message type
  * @v server_id                DHCP server ID
+ * @v pseudo_id                DHCP server pseudo-ID
  */
 static void dhcp_pxebs_rx ( struct dhcp_session *dhcp,
                            struct dhcp_packet *dhcppkt,
                            struct sockaddr_in *peer, uint8_t msgtype,
-                           struct in_addr server_id ) {
+                           struct in_addr server_id,
+                           struct in_addr pseudo_id ) {
        struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
        int rc;
 
        DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
               dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
               ntohs ( peer->sin_port ) );
-       if ( server_id.s_addr != peer->sin_addr.s_addr )
-               DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
+       if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
+            ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
+               DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
+               DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
+       }
 
        /* Identify boot menu item */
        dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
@@ -782,8 +825,7 @@ static void dhcp_pxebs_rx ( struct dhcp_session *dhcp,
                return;
        if ( menu_item.type != dhcp->pxe_type )
                return;
-       if ( ! dhcp_pxebs_accept ( dhcp, ( server_id.s_addr ?
-                                          server_id : peer->sin_addr ) ) )
+       if ( ! dhcp_pxebs_accept ( dhcp, pseudo_id ) )
                return;
 
        /* Register settings */
@@ -810,7 +852,7 @@ static void dhcp_pxebs_expired ( struct dhcp_session *dhcp ) {
        /* Give up waiting before we reach the failure point, and fail
         * over to the next server in the attempt list
         */
-       if ( elapsed > PXEBS_MAX_TIMEOUT ) {
+       if ( elapsed > PXEBS_MAX_TIMEOUT_SEC * TICKS_PER_SEC ) {
                dhcp->pxe_attempt++;
                if ( dhcp->pxe_attempt->s_addr ) {
                        dhcp_set_state ( dhcp, &dhcp_state_pxebs );
@@ -832,7 +874,8 @@ static struct dhcp_session_state dhcp_state_pxebs = {
        .rx                     = dhcp_pxebs_rx,
        .expired                = dhcp_pxebs_expired,
        .tx_msgtype             = DHCPREQUEST,
-       .apply_min_timeout      = 1,
+       .min_timeout_sec        = PXEBS_START_TIMEOUT_SEC,
+       .max_timeout_sec        = PXEBS_END_TIMEOUT_SEC,
 };
 
 /****************************************************************************
@@ -1114,6 +1157,7 @@ static int dhcp_deliver ( struct dhcp_session *dhcp,
        struct dhcphdr *dhcphdr;
        uint8_t msgtype = 0;
        struct in_addr server_id = { 0 };
+       struct in_addr pseudo_id;
        int rc = 0;
 
        /* Sanity checks */
@@ -1148,6 +1192,13 @@ static int dhcp_deliver ( struct dhcp_session *dhcp,
        dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER,
                        &server_id, sizeof ( server_id ) );
 
+       /* Identify server pseudo-ID */
+       pseudo_id = server_id;
+       if ( ! pseudo_id.s_addr )
+               pseudo_id = dhcppkt->dhcphdr->siaddr;
+       if ( ! pseudo_id.s_addr )
+               pseudo_id = peer->sin_addr;
+
        /* Check for matching transaction ID */
        if ( dhcphdr->xid != dhcp->xid ) {
                DBGC ( dhcp, "DHCP %p %s from %s:%d has bad transaction "
@@ -1170,7 +1221,7 @@ static int dhcp_deliver ( struct dhcp_session *dhcp,
        }
 
        /* Handle packet based on current state */
-       dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id );
+       dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id, pseudo_id );
 
  err_chaddr:
  err_xid: