iPXE
dhcp.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <string.h>
00027 #include <stdlib.h>
00028 #include <stdio.h>
00029 #include <ctype.h>
00030 #include <errno.h>
00031 #include <assert.h>
00032 #include <byteswap.h>
00033 #include <ipxe/if_ether.h>
00034 #include <ipxe/iobuf.h>
00035 #include <ipxe/netdevice.h>
00036 #include <ipxe/device.h>
00037 #include <ipxe/xfer.h>
00038 #include <ipxe/open.h>
00039 #include <ipxe/job.h>
00040 #include <ipxe/retry.h>
00041 #include <ipxe/tcpip.h>
00042 #include <ipxe/ip.h>
00043 #include <ipxe/uuid.h>
00044 #include <ipxe/timer.h>
00045 #include <ipxe/settings.h>
00046 #include <ipxe/dhcp.h>
00047 #include <ipxe/dhcpopts.h>
00048 #include <ipxe/dhcppkt.h>
00049 #include <ipxe/dhcp_arch.h>
00050 #include <ipxe/features.h>
00051 #include <config/dhcp.h>
00052 
00053 /** @file
00054  *
00055  * Dynamic Host Configuration Protocol
00056  *
00057  */
00058 
00059 struct dhcp_session;
00060 static int dhcp_tx ( struct dhcp_session *dhcp );
00061 
00062 /**
00063  * DHCP operation types
00064  *
00065  * This table maps from DHCP message types (i.e. values of the @c
00066  * DHCP_MESSAGE_TYPE option) to values of the "op" field within a DHCP
00067  * packet.
00068  */
00069 static const uint8_t dhcp_op[] = {
00070         [DHCPDISCOVER]  = BOOTP_REQUEST,
00071         [DHCPOFFER]     = BOOTP_REPLY,
00072         [DHCPREQUEST]   = BOOTP_REQUEST,
00073         [DHCPDECLINE]   = BOOTP_REQUEST,
00074         [DHCPACK]       = BOOTP_REPLY,
00075         [DHCPNAK]       = BOOTP_REPLY,
00076         [DHCPRELEASE]   = BOOTP_REQUEST,
00077         [DHCPINFORM]    = BOOTP_REQUEST,
00078 };
00079 
00080 /** Raw option data for options common to all DHCP requests */
00081 static uint8_t dhcp_request_options_data[] = {
00082         DHCP_MESSAGE_TYPE, DHCP_BYTE ( 0 ),
00083         DHCP_MAX_MESSAGE_SIZE,
00084         DHCP_WORD ( ETH_MAX_MTU - 20 /* IP header */ - 8 /* UDP header */ ),
00085         DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( DHCP_ARCH_CLIENT_ARCHITECTURE ),
00086         DHCP_CLIENT_NDI, DHCP_OPTION ( DHCP_ARCH_CLIENT_NDI ),
00087         DHCP_VENDOR_CLASS_ID,
00088         DHCP_STRING ( DHCP_VENDOR_PXECLIENT ( DHCP_ARCH_CLIENT_ARCHITECTURE,
00089                                               DHCP_ARCH_CLIENT_NDI ) ),
00090         DHCP_USER_CLASS_ID, DHCP_STRING ( 'i', 'P', 'X', 'E' ),
00091         DHCP_PARAMETER_REQUEST_LIST,
00092         DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS,
00093                       DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME,
00094                       DHCP_ROOT_PATH, DHCP_MTU, DHCP_VENDOR_ENCAP,
00095                       DHCP_VENDOR_CLASS_ID, DHCP_TFTP_SERVER_NAME,
00096                       DHCP_BOOTFILE_NAME, DHCP_DOMAIN_SEARCH,
00097                       128, 129, 130, 131, 132, 133, 134, 135, /* for PXE */
00098                       DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ),
00099         DHCP_END
00100 };
00101 
00102 /** Settings copied in to all DHCP requests */
00103 static const struct setting * dhcp_request_settings[] = {
00104         &user_class_setting,
00105         &vendor_class_setting,
00106 };
00107 
00108 /** DHCP server address setting */
00109 const struct setting dhcp_server_setting __setting ( SETTING_MISC,
00110                                                      dhcp-server ) = {
00111         .name = "dhcp-server",
00112         .description = "DHCP server",
00113         .tag = DHCP_SERVER_IDENTIFIER,
00114         .type = &setting_type_ipv4,
00115 };
00116 
00117 /**
00118  * Most recent DHCP transaction ID
00119  *
00120  * This is exposed for use by the fakedhcp code when reconstructing
00121  * DHCP packets for PXE NBPs.
00122  */
00123 uint32_t dhcp_last_xid;
00124 
00125 /**
00126  * Name a DHCP packet type
00127  *
00128  * @v msgtype           DHCP message type
00129  * @ret string          DHCP mesasge type name
00130  */
00131 static inline const char * dhcp_msgtype_name ( unsigned int msgtype ) {
00132         switch ( msgtype ) {
00133         case DHCPNONE:          return "BOOTP"; /* Non-DHCP packet */
00134         case DHCPDISCOVER:      return "DHCPDISCOVER";
00135         case DHCPOFFER:         return "DHCPOFFER";
00136         case DHCPREQUEST:       return "DHCPREQUEST";
00137         case DHCPDECLINE:       return "DHCPDECLINE";
00138         case DHCPACK:           return "DHCPACK";
00139         case DHCPNAK:           return "DHCPNAK";
00140         case DHCPRELEASE:       return "DHCPRELEASE";
00141         case DHCPINFORM:        return "DHCPINFORM";
00142         default:                return "DHCP<invalid>";
00143         }
00144 }
00145 
00146 /****************************************************************************
00147  *
00148  * DHCP session
00149  *
00150  */
00151 
00152 struct dhcp_session;
00153 
00154 /** DHCP session state operations */
00155 struct dhcp_session_state {
00156         /** State name */
00157         const char *name;
00158         /**
00159          * Construct transmitted packet
00160          *
00161          * @v dhcp              DHCP session
00162          * @v dhcppkt           DHCP packet
00163          * @v peer              Destination address
00164          */
00165         int ( * tx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt,
00166                        struct sockaddr_in *peer );
00167         /**
00168          * Handle received packet
00169          *
00170          * @v dhcp              DHCP session
00171          * @v dhcppkt           DHCP packet
00172          * @v peer              DHCP server address
00173          * @v msgtype           DHCP message type
00174          * @v server_id         DHCP server ID
00175          * @v pseudo_id         DHCP server pseudo-ID
00176          */
00177         void ( * rx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt,
00178                         struct sockaddr_in *peer, uint8_t msgtype,
00179                         struct in_addr server_id, struct in_addr pseudo_id );
00180         /**
00181          * Handle timer expiry
00182          *
00183          * @v dhcp              DHCP session
00184          */
00185         void ( * expired ) ( struct dhcp_session *dhcp );
00186         /** Transmitted message type */
00187         uint8_t tx_msgtype;
00188         /** Timeout parameters */
00189         uint8_t min_timeout_sec;
00190         uint8_t max_timeout_sec;
00191 };
00192 
00193 static struct dhcp_session_state dhcp_state_discover;
00194 static struct dhcp_session_state dhcp_state_request;
00195 static struct dhcp_session_state dhcp_state_proxy;
00196 static struct dhcp_session_state dhcp_state_pxebs;
00197 
00198 /** A DHCP session */
00199 struct dhcp_session {
00200         /** Reference counter */
00201         struct refcnt refcnt;
00202         /** Job control interface */
00203         struct interface job;
00204         /** Data transfer interface */
00205         struct interface xfer;
00206 
00207         /** Network device being configured */
00208         struct net_device *netdev;
00209         /** Local socket address */
00210         struct sockaddr_in local;
00211         /** State of the session */
00212         struct dhcp_session_state *state;
00213         /** Transaction ID (in network-endian order) */
00214         uint32_t xid;
00215 
00216         /** Offered IP address */
00217         struct in_addr offer;
00218         /** DHCP server */
00219         struct in_addr server;
00220         /** DHCP offer priority */
00221         int priority;
00222 
00223         /** ProxyDHCP protocol extensions should be ignored */
00224         int no_pxedhcp;
00225         /** ProxyDHCP server */
00226         struct in_addr proxy_server;
00227         /** ProxyDHCP offer */
00228         struct dhcp_packet *proxy_offer;
00229         /** ProxyDHCP offer priority */
00230         int proxy_priority;
00231 
00232         /** PXE Boot Server type */
00233         uint16_t pxe_type;
00234         /** List of PXE Boot Servers to attempt */
00235         struct in_addr *pxe_attempt;
00236         /** List of PXE Boot Servers to accept */
00237         struct in_addr *pxe_accept;
00238 
00239         /** Retransmission timer */
00240         struct retry_timer timer;
00241         /** Transmission counter */
00242         unsigned int count;
00243         /** Start time of the current state (in ticks) */
00244         unsigned long start;
00245 };
00246 
00247 /**
00248  * Free DHCP session
00249  *
00250  * @v refcnt            Reference counter
00251  */
00252 static void dhcp_free ( struct refcnt *refcnt ) {
00253         struct dhcp_session *dhcp =
00254                 container_of ( refcnt, struct dhcp_session, refcnt );
00255 
00256         netdev_put ( dhcp->netdev );
00257         dhcppkt_put ( dhcp->proxy_offer );
00258         free ( dhcp );
00259 }
00260 
00261 /**
00262  * Mark DHCP session as complete
00263  *
00264  * @v dhcp              DHCP session
00265  * @v rc                Return status code
00266  */
00267 static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) {
00268 
00269         /* Stop retry timer */
00270         stop_timer ( &dhcp->timer );
00271 
00272         /* Shut down interfaces */
00273         intf_shutdown ( &dhcp->xfer, rc );
00274         intf_shutdown ( &dhcp->job, rc );
00275 }
00276 
00277 /**
00278  * Transition to new DHCP session state
00279  *
00280  * @v dhcp              DHCP session
00281  * @v state             New session state
00282  */
00283 static void dhcp_set_state ( struct dhcp_session *dhcp,
00284                              struct dhcp_session_state *state ) {
00285 
00286         DBGC ( dhcp, "DHCP %p entering %s state\n", dhcp, state->name );
00287         dhcp->state = state;
00288         dhcp->start = currticks();
00289         stop_timer ( &dhcp->timer );
00290         set_timer_limits ( &dhcp->timer,
00291                            ( state->min_timeout_sec * TICKS_PER_SEC ),
00292                            ( state->max_timeout_sec * TICKS_PER_SEC ) );
00293         start_timer_nodelay ( &dhcp->timer );
00294 }
00295 
00296 /**
00297  * Check if DHCP packet contains PXE options
00298  *
00299  * @v dhcppkt           DHCP packet
00300  * @ret has_pxeopts     DHCP packet contains PXE options
00301  *
00302  * It is assumed that the packet is already known to contain option 60
00303  * set to "PXEClient".
00304  */
00305 static int dhcp_has_pxeopts ( struct dhcp_packet *dhcppkt ) {
00306 
00307         /* Check for a next-server and boot filename */
00308         if ( dhcppkt->dhcphdr->siaddr.s_addr &&
00309              ( dhcppkt_fetch ( dhcppkt, DHCP_BOOTFILE_NAME, NULL, 0 ) > 0 ) )
00310                 return 1;
00311 
00312         /* Check for a PXE boot menu */
00313         if ( dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU, NULL, 0 ) > 0 )
00314                 return 1;
00315 
00316         return 0;
00317 }
00318 
00319 /****************************************************************************
00320  *
00321  * DHCP state machine
00322  *
00323  */
00324 
00325 /**
00326  * Construct transmitted packet for DHCP discovery
00327  *
00328  * @v dhcp              DHCP session
00329  * @v dhcppkt           DHCP packet
00330  * @v peer              Destination address
00331  */
00332 static int dhcp_discovery_tx ( struct dhcp_session *dhcp,
00333                                struct dhcp_packet *dhcppkt __unused,
00334                                struct sockaddr_in *peer ) {
00335 
00336         DBGC ( dhcp, "DHCP %p DHCPDISCOVER\n", dhcp );
00337 
00338         /* Set server address */
00339         peer->sin_addr.s_addr = INADDR_BROADCAST;
00340         peer->sin_port = htons ( BOOTPS_PORT );
00341 
00342         return 0;
00343 }
00344 
00345 /**
00346  * Handle received packet during DHCP discovery
00347  *
00348  * @v dhcp              DHCP session
00349  * @v dhcppkt           DHCP packet
00350  * @v peer              DHCP server address
00351  * @v msgtype           DHCP message type
00352  * @v server_id         DHCP server ID
00353  * @v pseudo_id         DHCP server pseudo-ID
00354  */
00355 static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
00356                                 struct dhcp_packet *dhcppkt,
00357                                 struct sockaddr_in *peer, uint8_t msgtype,
00358                                 struct in_addr server_id,
00359                                 struct in_addr pseudo_id ) {
00360         struct in_addr ip;
00361         char vci[9]; /* "PXEClient" */
00362         int vci_len;
00363         int has_pxeclient;
00364         int8_t priority = 0;
00365         uint8_t no_pxedhcp = 0;
00366         unsigned long elapsed;
00367 
00368         DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
00369                dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
00370                ntohs ( peer->sin_port ) );
00371         if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
00372              ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
00373                 DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
00374                 DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
00375         }
00376 
00377         /* Identify offered IP address */
00378         ip = dhcppkt->dhcphdr->yiaddr;
00379         if ( ip.s_addr )
00380                 DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
00381 
00382         /* Identify "PXEClient" vendor class */
00383         vci_len = dhcppkt_fetch ( dhcppkt, DHCP_VENDOR_CLASS_ID,
00384                                   vci, sizeof ( vci ) );
00385         has_pxeclient = ( ( vci_len >= ( int ) sizeof ( vci ) ) &&
00386                           ( strncmp ( "PXEClient", vci, sizeof (vci) ) == 0 ));
00387         if ( has_pxeclient ) {
00388                 DBGC ( dhcp, "%s",
00389                        ( dhcp_has_pxeopts ( dhcppkt ) ? " pxe" : " proxy" ) );
00390         }
00391 
00392         /* Identify priority */
00393         dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority,
00394                         sizeof ( priority ) );
00395         if ( priority )
00396                 DBGC ( dhcp, " pri %d", priority );
00397 
00398         /* Identify ignore-PXE flag */
00399         dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &no_pxedhcp,
00400                         sizeof ( no_pxedhcp ) );
00401         if ( no_pxedhcp )
00402                 DBGC ( dhcp, " nopxe" );
00403         DBGC ( dhcp, "\n" );
00404 
00405         /* Select as DHCP offer, if applicable */
00406         if ( ip.s_addr && ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
00407              ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) &&
00408              ( priority >= dhcp->priority ) ) {
00409                 dhcp->offer = ip;
00410                 dhcp->server = server_id;
00411                 dhcp->priority = priority;
00412                 dhcp->no_pxedhcp = no_pxedhcp;
00413         }
00414 
00415         /* Select as ProxyDHCP offer, if applicable */
00416         if ( pseudo_id.s_addr && has_pxeclient &&
00417              ( priority >= dhcp->proxy_priority ) ) {
00418                 dhcppkt_put ( dhcp->proxy_offer );
00419                 dhcp->proxy_server = pseudo_id;
00420                 dhcp->proxy_offer = dhcppkt_get ( dhcppkt );
00421                 dhcp->proxy_priority = priority;
00422         }
00423 
00424         /* We can exit the discovery state when we have a valid
00425          * DHCPOFFER, and either:
00426          *
00427          *  o  The DHCPOFFER instructs us to ignore ProxyDHCPOFFERs, or
00428          *  o  We have a valid ProxyDHCPOFFER, or
00429          *  o  We have allowed sufficient time for ProxyDHCPOFFERs.
00430          */
00431 
00432         /* If we don't yet have a DHCPOFFER, do nothing */
00433         if ( ! dhcp->offer.s_addr )
00434                 return;
00435 
00436         /* If we can't yet transition to DHCPREQUEST, do nothing */
00437         elapsed = ( currticks() - dhcp->start );
00438         if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_offer ||
00439                  ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) )
00440                 return;
00441 
00442         /* Transition to DHCPREQUEST */
00443         dhcp_set_state ( dhcp, &dhcp_state_request );
00444 }
00445 
00446 /**
00447  * Handle timer expiry during DHCP discovery
00448  *
00449  * @v dhcp              DHCP session
00450  */
00451 static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) {
00452         unsigned long elapsed = ( currticks() - dhcp->start );
00453 
00454         /* If link is blocked, defer DHCP discovery (and reset timeout) */
00455         if ( netdev_link_blocked ( dhcp->netdev ) &&
00456              ( dhcp->count <= DHCP_DISC_MAX_DEFERRALS ) ) {
00457                 DBGC ( dhcp, "DHCP %p deferring discovery\n", dhcp );
00458                 dhcp->start = currticks();
00459                 start_timer_fixed ( &dhcp->timer,
00460                                     ( DHCP_DISC_START_TIMEOUT_SEC *
00461                                       TICKS_PER_SEC ) );
00462                 return;
00463         }
00464 
00465         /* Give up waiting for ProxyDHCP before we reach the failure point */
00466         if ( dhcp->offer.s_addr &&
00467              ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) {
00468                 dhcp_set_state ( dhcp, &dhcp_state_request );
00469                 return;
00470         }
00471 
00472         /* Otherwise, retransmit current packet */
00473         dhcp_tx ( dhcp );
00474 }
00475 
00476 /** DHCP discovery state operations */
00477 static struct dhcp_session_state dhcp_state_discover = {
00478         .name                   = "discovery",
00479         .tx                     = dhcp_discovery_tx,
00480         .rx                     = dhcp_discovery_rx,
00481         .expired                = dhcp_discovery_expired,
00482         .tx_msgtype             = DHCPDISCOVER,
00483         .min_timeout_sec        = DHCP_DISC_START_TIMEOUT_SEC,
00484         .max_timeout_sec        = DHCP_DISC_END_TIMEOUT_SEC,
00485 };
00486 
00487 /**
00488  * Construct transmitted packet for DHCP request
00489  *
00490  * @v dhcp              DHCP session
00491  * @v dhcppkt           DHCP packet
00492  * @v peer              Destination address
00493  */
00494 static int dhcp_request_tx ( struct dhcp_session *dhcp,
00495                              struct dhcp_packet *dhcppkt,
00496                              struct sockaddr_in *peer ) {
00497         int rc;
00498 
00499         DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d",
00500                dhcp, inet_ntoa ( dhcp->server ), BOOTPS_PORT );
00501         DBGC ( dhcp, " for %s\n", inet_ntoa ( dhcp->offer ) );
00502 
00503         /* Set server ID */
00504         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
00505                                     &dhcp->server,
00506                                     sizeof ( dhcp->server ) ) ) != 0 )
00507                 return rc;
00508 
00509         /* Set requested IP address */
00510         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
00511                                     &dhcp->offer,
00512                                     sizeof ( dhcp->offer ) ) ) != 0 )
00513                 return rc;
00514 
00515         /* Set server address */
00516         peer->sin_addr.s_addr = INADDR_BROADCAST;
00517         peer->sin_port = htons ( BOOTPS_PORT );
00518 
00519         return 0;
00520 }
00521 
00522 /**
00523  * Handle received packet during DHCP request
00524  *
00525  * @v dhcp              DHCP session
00526  * @v dhcppkt           DHCP packet
00527  * @v peer              DHCP server address
00528  * @v msgtype           DHCP message type
00529  * @v server_id         DHCP server ID
00530  * @v pseudo_id         DHCP server pseudo-ID
00531  */
00532 static void dhcp_request_rx ( struct dhcp_session *dhcp,
00533                               struct dhcp_packet *dhcppkt,
00534                               struct sockaddr_in *peer, uint8_t msgtype,
00535                               struct in_addr server_id,
00536                               struct in_addr pseudo_id ) {
00537         struct in_addr ip;
00538         struct settings *parent;
00539         struct settings *settings;
00540         int rc;
00541 
00542         DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
00543                dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
00544                ntohs ( peer->sin_port ) );
00545         if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
00546              ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
00547                 DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
00548                 DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
00549         }
00550 
00551         /* Identify leased IP address */
00552         ip = dhcppkt->dhcphdr->yiaddr;
00553         if ( ip.s_addr )
00554                 DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
00555         DBGC ( dhcp, "\n" );
00556 
00557         /* Filter out unacceptable responses */
00558         if ( peer->sin_port != htons ( BOOTPS_PORT ) )
00559                 return;
00560         if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
00561                 return;
00562         if ( server_id.s_addr != dhcp->server.s_addr )
00563                 return;
00564         if ( ip.s_addr != dhcp->offer.s_addr )
00565                 return;
00566 
00567         /* Record assigned address */
00568         dhcp->local.sin_addr = ip;
00569 
00570         /* Register settings */
00571         parent = netdev_settings ( dhcp->netdev );
00572         settings = &dhcppkt->settings;
00573         if ( ( rc = register_settings ( settings, parent,
00574                                         DHCP_SETTINGS_NAME ) ) != 0 ) {
00575                 DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
00576                        dhcp, strerror ( rc ) );
00577                 dhcp_finished ( dhcp, rc );
00578                 return;
00579         }
00580 
00581         /* Perform ProxyDHCP if applicable */
00582         if ( dhcp->proxy_offer /* Have ProxyDHCP offer */ &&
00583              ( ! dhcp->no_pxedhcp ) /* ProxyDHCP not disabled */ ) {
00584                 if ( dhcp_has_pxeopts ( dhcp->proxy_offer ) ) {
00585                         /* PXE options already present; register settings
00586                          * without performing a ProxyDHCPREQUEST
00587                          */
00588                         settings = &dhcp->proxy_offer->settings;
00589                         if ( ( rc = register_settings ( settings, NULL,
00590                                            PROXYDHCP_SETTINGS_NAME ) ) != 0 ) {
00591                                 DBGC ( dhcp, "DHCP %p could not register "
00592                                        "proxy settings: %s\n",
00593                                        dhcp, strerror ( rc ) );
00594                                 dhcp_finished ( dhcp, rc );
00595                                 return;
00596                         }
00597                 } else {
00598                         /* PXE options not present; use a ProxyDHCPREQUEST */
00599                         dhcp_set_state ( dhcp, &dhcp_state_proxy );
00600                         return;
00601                 }
00602         }
00603 
00604         /* Terminate DHCP */
00605         dhcp_finished ( dhcp, 0 );
00606 }
00607 
00608 /**
00609  * Handle timer expiry during DHCP discovery
00610  *
00611  * @v dhcp              DHCP session
00612  */
00613 static void dhcp_request_expired ( struct dhcp_session *dhcp ) {
00614 
00615         /* Retransmit current packet */
00616         dhcp_tx ( dhcp );
00617 }
00618 
00619 /** DHCP request state operations */
00620 static struct dhcp_session_state dhcp_state_request = {
00621         .name                   = "request",
00622         .tx                     = dhcp_request_tx,
00623         .rx                     = dhcp_request_rx,
00624         .expired                = dhcp_request_expired,
00625         .tx_msgtype             = DHCPREQUEST,
00626         .min_timeout_sec        = DHCP_REQ_START_TIMEOUT_SEC,
00627         .max_timeout_sec        = DHCP_REQ_END_TIMEOUT_SEC,
00628 };
00629 
00630 /**
00631  * Construct transmitted packet for ProxyDHCP request
00632  *
00633  * @v dhcp              DHCP session
00634  * @v dhcppkt           DHCP packet
00635  * @v peer              Destination address
00636  */
00637 static int dhcp_proxy_tx ( struct dhcp_session *dhcp,
00638                            struct dhcp_packet *dhcppkt,
00639                            struct sockaddr_in *peer ) {
00640         int rc;
00641 
00642         DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s\n", dhcp,
00643                inet_ntoa ( dhcp->proxy_server ) );
00644 
00645         /* Set server ID */
00646         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
00647                                     &dhcp->proxy_server,
00648                                     sizeof ( dhcp->proxy_server ) ) ) != 0 )
00649                 return rc;
00650 
00651         /* Set server address */
00652         peer->sin_addr = dhcp->proxy_server;
00653         peer->sin_port = htons ( PXE_PORT );
00654 
00655         return 0;
00656 }
00657 
00658 /**
00659  * Handle received packet during ProxyDHCP request
00660  *
00661  * @v dhcp              DHCP session
00662  * @v dhcppkt           DHCP packet
00663  * @v peer              DHCP server address
00664  * @v msgtype           DHCP message type
00665  * @v server_id         DHCP server ID
00666  * @v pseudo_id         DHCP server pseudo-ID
00667  */
00668 static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
00669                             struct dhcp_packet *dhcppkt,
00670                             struct sockaddr_in *peer, uint8_t msgtype,
00671                             struct in_addr server_id,
00672                             struct in_addr pseudo_id ) {
00673         struct settings *settings = &dhcppkt->settings;
00674         int rc;
00675 
00676         DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
00677                dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
00678                ntohs ( peer->sin_port ) );
00679         if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
00680              ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
00681                 DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
00682                 DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
00683         }
00684         if ( dhcp_has_pxeopts ( dhcppkt ) )
00685                 DBGC ( dhcp, " pxe" );
00686         DBGC ( dhcp, "\n" );
00687 
00688         /* Filter out unacceptable responses */
00689         if ( peer->sin_port != ntohs ( PXE_PORT ) )
00690                 return;
00691         if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) )
00692                 return;
00693         if ( ( pseudo_id.s_addr != dhcp->proxy_server.s_addr ) )
00694                 return;
00695         if ( ! dhcp_has_pxeopts ( dhcppkt ) )
00696                 return;
00697 
00698         /* Register settings */
00699         if ( ( rc = register_settings ( settings, NULL,
00700                                         PROXYDHCP_SETTINGS_NAME ) ) != 0 ) {
00701                 DBGC ( dhcp, "DHCP %p could not register proxy settings: %s\n",
00702                        dhcp, strerror ( rc ) );
00703                 dhcp_finished ( dhcp, rc );
00704                 return;
00705         }
00706 
00707         /* Terminate DHCP */
00708         dhcp_finished ( dhcp, 0 );
00709 }
00710 
00711 /**
00712  * Handle timer expiry during ProxyDHCP request
00713  *
00714  * @v dhcp              DHCP session
00715  */
00716 static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) {
00717         unsigned long elapsed = ( currticks() - dhcp->start );
00718 
00719         /* Give up waiting for ProxyDHCP before we reach the failure point */
00720         if ( elapsed > DHCP_REQ_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) {
00721                 dhcp_finished ( dhcp, 0 );
00722                 return;
00723         }
00724 
00725         /* Retransmit current packet */
00726         dhcp_tx ( dhcp );
00727 }
00728 
00729 /** ProxyDHCP request state operations */
00730 static struct dhcp_session_state dhcp_state_proxy = {
00731         .name                   = "ProxyDHCP",
00732         .tx                     = dhcp_proxy_tx,
00733         .rx                     = dhcp_proxy_rx,
00734         .expired                = dhcp_proxy_expired,
00735         .tx_msgtype             = DHCPREQUEST,
00736         .min_timeout_sec        = DHCP_PROXY_START_TIMEOUT_SEC,
00737         .max_timeout_sec        = DHCP_PROXY_END_TIMEOUT_SEC,
00738 };
00739 
00740 /**
00741  * Construct transmitted packet for PXE Boot Server Discovery
00742  *
00743  * @v dhcp              DHCP session
00744  * @v dhcppkt           DHCP packet
00745  * @v peer              Destination address
00746  */
00747 static int dhcp_pxebs_tx ( struct dhcp_session *dhcp,
00748                            struct dhcp_packet *dhcppkt,
00749                            struct sockaddr_in *peer ) {
00750         struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
00751         int rc;
00752 
00753         /* Set server address */
00754         peer->sin_addr = *(dhcp->pxe_attempt);
00755         peer->sin_port = ( ( peer->sin_addr.s_addr == INADDR_BROADCAST ) ?
00756                            htons ( BOOTPS_PORT ) : htons ( PXE_PORT ) );
00757 
00758         DBGC ( dhcp, "DHCP %p PXEBS REQUEST to %s:%d for type %d\n",
00759                dhcp, inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ),
00760                le16_to_cpu ( dhcp->pxe_type ) );
00761 
00762         /* Set boot menu item */
00763         menu_item.type = dhcp->pxe_type;
00764         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
00765                                     &menu_item, sizeof ( menu_item ) ) ) != 0 )
00766                 return rc;
00767 
00768         return 0;
00769 }
00770 
00771 /**
00772  * Check to see if PXE Boot Server address is acceptable
00773  *
00774  * @v dhcp              DHCP session
00775  * @v bs                Boot Server address
00776  * @ret accept          Boot Server is acceptable
00777  */
00778 static int dhcp_pxebs_accept ( struct dhcp_session *dhcp,
00779                                struct in_addr bs ) {
00780         struct in_addr *accept;
00781 
00782         /* Accept if we have no acceptance filter */
00783         if ( ! dhcp->pxe_accept )
00784                 return 1;
00785 
00786         /* Scan through acceptance list */
00787         for ( accept = dhcp->pxe_accept ; accept->s_addr ; accept++ ) {
00788                 if ( accept->s_addr == bs.s_addr )
00789                         return 1;
00790         }
00791 
00792         DBGC ( dhcp, "DHCP %p rejecting server %s\n",
00793                dhcp, inet_ntoa ( bs ) );
00794         return 0;
00795 }
00796 
00797 /**
00798  * Handle received packet during PXE Boot Server Discovery
00799  *
00800  * @v dhcp              DHCP session
00801  * @v dhcppkt           DHCP packet
00802  * @v peer              DHCP server address
00803  * @v msgtype           DHCP message type
00804  * @v server_id         DHCP server ID
00805  * @v pseudo_id         DHCP server pseudo-ID
00806  */
00807 static void dhcp_pxebs_rx ( struct dhcp_session *dhcp,
00808                             struct dhcp_packet *dhcppkt,
00809                             struct sockaddr_in *peer, uint8_t msgtype,
00810                             struct in_addr server_id,
00811                             struct in_addr pseudo_id ) {
00812         struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
00813         int rc;
00814 
00815         DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
00816                dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
00817                ntohs ( peer->sin_port ) );
00818         if ( ( server_id.s_addr != peer->sin_addr.s_addr ) ||
00819              ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) {
00820                 DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) );
00821                 DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) );
00822         }
00823 
00824         /* Identify boot menu item */
00825         dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
00826                         &menu_item, sizeof ( menu_item ) );
00827         if ( menu_item.type )
00828                 DBGC ( dhcp, " for type %d", ntohs ( menu_item.type ) );
00829         DBGC ( dhcp, "\n" );
00830 
00831         /* Filter out unacceptable responses */
00832         if ( ( peer->sin_port != htons ( BOOTPS_PORT ) ) &&
00833              ( peer->sin_port != htons ( PXE_PORT ) ) )
00834                 return;
00835         if ( msgtype != DHCPACK )
00836                 return;
00837         if ( menu_item.type != dhcp->pxe_type )
00838                 return;
00839         if ( ! dhcp_pxebs_accept ( dhcp, pseudo_id ) )
00840                 return;
00841 
00842         /* Register settings */
00843         if ( ( rc = register_settings ( &dhcppkt->settings, NULL,
00844                                         PXEBS_SETTINGS_NAME ) ) != 0 ) {
00845                 DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
00846                        dhcp, strerror ( rc ) );
00847                 dhcp_finished ( dhcp, rc );
00848                 return;
00849         }
00850 
00851         /* Terminate DHCP */
00852         dhcp_finished ( dhcp, 0 );
00853 }
00854 
00855 /**
00856  * Handle timer expiry during PXE Boot Server Discovery
00857  *
00858  * @v dhcp              DHCP session
00859  */
00860 static void dhcp_pxebs_expired ( struct dhcp_session *dhcp ) {
00861         unsigned long elapsed = ( currticks() - dhcp->start );
00862 
00863         /* Give up waiting before we reach the failure point, and fail
00864          * over to the next server in the attempt list
00865          */
00866         if ( elapsed > PXEBS_MAX_TIMEOUT_SEC * TICKS_PER_SEC ) {
00867                 dhcp->pxe_attempt++;
00868                 if ( dhcp->pxe_attempt->s_addr ) {
00869                         dhcp_set_state ( dhcp, &dhcp_state_pxebs );
00870                         return;
00871                 } else {
00872                         dhcp_finished ( dhcp, -ETIMEDOUT );
00873                         return;
00874                 }
00875         }
00876 
00877         /* Retransmit current packet */
00878         dhcp_tx ( dhcp );
00879 }
00880 
00881 /** PXE Boot Server Discovery state operations */
00882 static struct dhcp_session_state dhcp_state_pxebs = {
00883         .name                   = "PXEBS",
00884         .tx                     = dhcp_pxebs_tx,
00885         .rx                     = dhcp_pxebs_rx,
00886         .expired                = dhcp_pxebs_expired,
00887         .tx_msgtype             = DHCPREQUEST,
00888         .min_timeout_sec        = PXEBS_START_TIMEOUT_SEC,
00889         .max_timeout_sec        = PXEBS_END_TIMEOUT_SEC,
00890 };
00891 
00892 /****************************************************************************
00893  *
00894  * Packet construction
00895  *
00896  */
00897 
00898 /**
00899  * Create a DHCP packet
00900  *
00901  * @v dhcppkt           DHCP packet structure to fill in
00902  * @v netdev            Network device
00903  * @v msgtype           DHCP message type
00904  * @v xid               Transaction ID (in network-endian order)
00905  * @v options           Initial options to include (or NULL)
00906  * @v options_len       Length of initial options
00907  * @v data              Buffer for DHCP packet
00908  * @v max_len           Size of DHCP packet buffer
00909  * @ret rc              Return status code
00910  *
00911  * Creates a DHCP packet in the specified buffer, and initialise a
00912  * DHCP packet structure.
00913  */
00914 int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
00915                          struct net_device *netdev, uint8_t msgtype,
00916                          uint32_t xid, const void *options, size_t options_len,
00917                          void *data, size_t max_len ) {
00918         struct dhcphdr *dhcphdr = data;
00919         int rc;
00920 
00921         /* Sanity check */
00922         if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) )
00923                 return -ENOSPC;
00924 
00925         /* Initialise DHCP packet content */
00926         memset ( dhcphdr, 0, max_len );
00927         dhcphdr->xid = xid;
00928         dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
00929         dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
00930         dhcphdr->op = dhcp_op[msgtype];
00931         dhcphdr->hlen = netdev->ll_protocol->ll_addr_len;
00932         memcpy ( dhcphdr->chaddr, netdev->ll_addr,
00933                  netdev->ll_protocol->ll_addr_len );
00934         memcpy ( dhcphdr->options, options, options_len );
00935 
00936         /* If the local link-layer address functions only as a name
00937          * (i.e. cannot be used as a destination address), then
00938          * request broadcast responses.
00939          */
00940         if ( netdev->ll_protocol->flags & LL_NAME_ONLY )
00941                 dhcphdr->flags |= htons ( BOOTP_FL_BROADCAST );
00942 
00943         /* If the network device already has an IPv4 address then
00944          * unicast responses from the DHCP server may be rejected, so
00945          * request broadcast responses.
00946          */
00947         if ( ipv4_has_any_addr ( netdev ) )
00948                 dhcphdr->flags |= htons ( BOOTP_FL_BROADCAST );
00949 
00950         /* Initialise DHCP packet structure */
00951         memset ( dhcppkt, 0, sizeof ( *dhcppkt ) );
00952         dhcppkt_init ( dhcppkt, data, max_len );
00953         
00954         /* Set DHCP_MESSAGE_TYPE option */
00955         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE,
00956                                     &msgtype, sizeof ( msgtype ) ) ) != 0 )
00957                 return rc;
00958 
00959         return 0;
00960 }
00961 
00962 /**
00963  * Create DHCP request packet
00964  *
00965  * @v dhcppkt           DHCP packet structure to fill in
00966  * @v netdev            Network device
00967  * @v msgtype           DHCP message type
00968  * @v xid               Transaction ID (in network-endian order)
00969  * @v ciaddr            Client IP address
00970  * @v data              Buffer for DHCP packet
00971  * @v max_len           Size of DHCP packet buffer
00972  * @ret rc              Return status code
00973  *
00974  * Creates a DHCP request packet in the specified buffer, and
00975  * initialise a DHCP packet structure.
00976  */
00977 int dhcp_create_request ( struct dhcp_packet *dhcppkt,
00978                           struct net_device *netdev, unsigned int msgtype,
00979                           uint32_t xid, struct in_addr ciaddr,
00980                           void *data, size_t max_len ) {
00981         struct dhcp_netdev_desc dhcp_desc;
00982         struct dhcp_client_id client_id;
00983         struct dhcp_client_uuid client_uuid;
00984         const struct setting *setting;
00985         uint8_t *dhcp_features;
00986         size_t dhcp_features_len;
00987         size_t ll_addr_len;
00988         void *raw;
00989         ssize_t len;
00990         unsigned int i;
00991         int rc;
00992 
00993         /* Create DHCP packet */
00994         if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype, xid,
00995                                          dhcp_request_options_data,
00996                                          sizeof ( dhcp_request_options_data ),
00997                                          data, max_len ) ) != 0 ) {
00998                 DBG ( "DHCP could not create DHCP packet: %s\n",
00999                       strerror ( rc ) );
01000                 goto err_create_packet;
01001         }
01002 
01003         /* Set client IP address */
01004         dhcppkt->dhcphdr->ciaddr = ciaddr;
01005 
01006         /* Add options to identify the feature list */
01007         dhcp_features = table_start ( DHCP_FEATURES );
01008         dhcp_features_len = table_num_entries ( DHCP_FEATURES );
01009         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features,
01010                                     dhcp_features_len ) ) != 0 ) {
01011                 DBG ( "DHCP could not set features list option: %s\n",
01012                       strerror ( rc ) );
01013                 goto err_store_features;
01014         }
01015 
01016         /* Add options to identify the network device */
01017         fetch_raw_setting ( netdev_settings ( netdev ), &busid_setting,
01018                             &dhcp_desc, sizeof ( dhcp_desc ) );
01019         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc,
01020                                     sizeof ( dhcp_desc ) ) ) != 0 ) {
01021                 DBG ( "DHCP could not set bus ID option: %s\n",
01022                       strerror ( rc ) );
01023                 goto err_store_busid;
01024         }
01025 
01026         /* Add DHCP client identifier.  Required for Infiniband, and
01027          * doesn't hurt other link layers.
01028          */
01029         client_id.ll_proto = ntohs ( netdev->ll_protocol->ll_proto );
01030         ll_addr_len = netdev->ll_protocol->ll_addr_len;
01031         assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) );
01032         memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len );
01033         if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_ID, &client_id,
01034                                     ( ll_addr_len + 1 ) ) ) != 0 ) {
01035                 DBG ( "DHCP could not set client ID: %s\n",
01036                       strerror ( rc ) );
01037                 goto err_store_client_id;
01038         }
01039 
01040         /* Add client UUID, if we have one.  Required for PXE.  The
01041          * PXE spec does not specify a byte ordering for UUIDs, but
01042          * RFC4578 suggests that it follows the EFI spec, in which the
01043          * first three fields are little-endian.
01044          */
01045         client_uuid.type = DHCP_CLIENT_UUID_TYPE;
01046         if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting,
01047                                           &client_uuid.uuid ) ) >= 0 ) {
01048                 uuid_mangle ( &client_uuid.uuid );
01049                 if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID,
01050                                             &client_uuid,
01051                                             sizeof ( client_uuid ) ) ) != 0 ) {
01052                         DBG ( "DHCP could not set client UUID: %s\n",
01053                               strerror ( rc ) );
01054                         goto err_store_client_uuid;
01055                 }
01056         }
01057 
01058         /* Add request settings, if applicable */
01059         for ( i = 0 ; i < ( sizeof ( dhcp_request_settings ) /
01060                             sizeof ( dhcp_request_settings[0] ) ) ; i++ ) {
01061                 setting = dhcp_request_settings[i];
01062                 if ( ( len = fetch_raw_setting_copy ( NULL, setting,
01063                                                       &raw ) ) >= 0 ) {
01064                         rc = dhcppkt_store ( dhcppkt, setting->tag, raw, len );
01065                         free ( raw );
01066                         if ( rc != 0 ) {
01067                                 DBG ( "DHCP could not set %s: %s\n",
01068                                       setting->name, strerror ( rc ) );
01069                                 goto err_store_raw;
01070                         }
01071                 }
01072         }
01073 
01074  err_store_raw:
01075  err_store_client_uuid:
01076  err_store_client_id:
01077  err_store_busid:
01078  err_store_features:
01079  err_create_packet:
01080         return rc;
01081 }
01082 
01083 /****************************************************************************
01084  *
01085  * Data transfer interface
01086  *
01087  */
01088 
01089 /**
01090  * Transmit DHCP request
01091  *
01092  * @v dhcp              DHCP session
01093  * @ret rc              Return status code
01094  */
01095 static int dhcp_tx ( struct dhcp_session *dhcp ) {
01096         static struct sockaddr_in peer = {
01097                 .sin_family = AF_INET,
01098         };
01099         struct xfer_metadata meta = {
01100                 .netdev = dhcp->netdev,
01101                 .src = ( struct sockaddr * ) &dhcp->local,
01102                 .dest = ( struct sockaddr * ) &peer,
01103         };
01104         struct io_buffer *iobuf;
01105         uint8_t msgtype = dhcp->state->tx_msgtype;
01106         struct dhcp_packet dhcppkt;
01107         int rc;
01108 
01109         /* Start retry timer.  Do this first so that failures to
01110          * transmit will be retried.
01111          */
01112         start_timer ( &dhcp->timer );
01113 
01114         /* Allocate buffer for packet */
01115         iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN );
01116         if ( ! iobuf )
01117                 return -ENOMEM;
01118 
01119         /* Create basic DHCP packet in temporary buffer */
01120         if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, msgtype,
01121                                           dhcp->xid, dhcp->local.sin_addr,
01122                                           iobuf->data,
01123                                           iob_tailroom ( iobuf ) ) ) != 0 ) {
01124                 DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n",
01125                        dhcp, strerror ( rc ) );
01126                 goto done;
01127         }
01128 
01129         /* (Ab)use the "secs" field to convey metadata about the DHCP
01130          * session state into packet traces.  Useful for extracting
01131          * debug information from non-debug builds.
01132          */
01133         dhcppkt.dhcphdr->secs = htons ( ( dhcp->count << 2 ) |
01134                                         ( dhcp->offer.s_addr ? 0x02 : 0 ) |
01135                                         ( dhcp->proxy_offer ? 0x01 : 0 ) );
01136 
01137         /* Fill in packet based on current state */
01138         if ( ( rc = dhcp->state->tx ( dhcp, &dhcppkt, &peer ) ) != 0 ) {
01139                 DBGC ( dhcp, "DHCP %p could not fill DHCP request: %s\n",
01140                        dhcp, strerror ( rc ) );
01141                 goto done;
01142         }
01143 
01144         /* Transmit the packet */
01145         iob_put ( iobuf, dhcppkt_len ( &dhcppkt ) );
01146         if ( ( rc = xfer_deliver ( &dhcp->xfer, iob_disown ( iobuf ),
01147                                    &meta ) ) != 0 ) {
01148                 DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n",
01149                        dhcp, strerror ( rc ) );
01150                 goto done;
01151         }
01152 
01153  done:
01154         free_iob ( iobuf );
01155         return rc;
01156 }
01157 
01158 /**
01159  * Receive new data
01160  *
01161  * @v dhcp              DHCP session
01162  * @v iobuf             I/O buffer
01163  * @v meta              Transfer metadata
01164  * @ret rc              Return status code
01165  */
01166 static int dhcp_deliver ( struct dhcp_session *dhcp,
01167                           struct io_buffer *iobuf,
01168                           struct xfer_metadata *meta ) {
01169         struct net_device *netdev = dhcp->netdev;
01170         struct ll_protocol *ll_protocol = netdev->ll_protocol;
01171         struct sockaddr_in *peer;
01172         size_t data_len;
01173         struct dhcp_packet *dhcppkt;
01174         struct dhcphdr *dhcphdr;
01175         uint8_t msgtype = 0;
01176         struct in_addr server_id = { 0 };
01177         struct in_addr pseudo_id;
01178         int rc = 0;
01179 
01180         /* Sanity checks */
01181         if ( ! meta->src ) {
01182                 DBGC ( dhcp, "DHCP %p received packet without source port\n",
01183                        dhcp );
01184                 rc = -EINVAL;
01185                 goto err_no_src;
01186         }
01187         peer = ( struct sockaddr_in * ) meta->src;
01188 
01189         /* Create a DHCP packet containing the I/O buffer contents.
01190          * Whilst we could just use the original buffer in situ, that
01191          * would waste the unused space in the packet buffer, and also
01192          * waste a relatively scarce fully-aligned I/O buffer.
01193          */
01194         data_len = iob_len ( iobuf );
01195         dhcppkt = zalloc ( sizeof ( *dhcppkt ) + data_len );
01196         if ( ! dhcppkt ) {
01197                 rc = -ENOMEM;
01198                 goto err_alloc_dhcppkt;
01199         }
01200         dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
01201         memcpy ( dhcphdr, iobuf->data, data_len );
01202         dhcppkt_init ( dhcppkt, dhcphdr, data_len );
01203 
01204         /* Identify message type */
01205         dhcppkt_fetch ( dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
01206                         sizeof ( msgtype ) );
01207 
01208         /* Identify server ID */
01209         dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER,
01210                         &server_id, sizeof ( server_id ) );
01211 
01212         /* Identify server pseudo-ID */
01213         pseudo_id = server_id;
01214         if ( ! pseudo_id.s_addr )
01215                 pseudo_id = dhcppkt->dhcphdr->siaddr;
01216         if ( ! pseudo_id.s_addr )
01217                 pseudo_id = peer->sin_addr;
01218 
01219         /* Check for matching transaction ID */
01220         if ( dhcphdr->xid != dhcp->xid ) {
01221                 DBGC ( dhcp, "DHCP %p %s from %s:%d has bad transaction "
01222                        "ID\n", dhcp, dhcp_msgtype_name ( msgtype ),
01223                        inet_ntoa ( peer->sin_addr ),
01224                        ntohs ( peer->sin_port ) );
01225                 rc = -EINVAL;
01226                 goto err_xid;
01227         };
01228 
01229         /* Check for matching client hardware address */
01230         if ( memcmp ( dhcphdr->chaddr, netdev->ll_addr,
01231                       ll_protocol->ll_addr_len ) != 0 ) {
01232                 DBGC ( dhcp, "DHCP %p %s from %s:%d has bad chaddr %s\n",
01233                        dhcp, dhcp_msgtype_name ( msgtype ),
01234                        inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ),
01235                        ll_protocol->ntoa ( dhcphdr->chaddr ) );
01236                 rc = -EINVAL;
01237                 goto err_chaddr;
01238         }
01239 
01240         /* Handle packet based on current state */
01241         dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id, pseudo_id );
01242 
01243  err_chaddr:
01244  err_xid:
01245         dhcppkt_put ( dhcppkt );
01246  err_alloc_dhcppkt:
01247  err_no_src:
01248         free_iob ( iobuf );
01249         return rc;
01250 }
01251 
01252 /** DHCP data transfer interface operations */
01253 static struct interface_operation dhcp_xfer_operations[] = {
01254         INTF_OP ( xfer_deliver, struct dhcp_session *, dhcp_deliver ),
01255 };
01256 
01257 /** DHCP data transfer interface descriptor */
01258 static struct interface_descriptor dhcp_xfer_desc =
01259         INTF_DESC ( struct dhcp_session, xfer, dhcp_xfer_operations );
01260 
01261 /**
01262  * Handle DHCP retry timer expiry
01263  *
01264  * @v timer             DHCP retry timer
01265  * @v fail              Failure indicator
01266  */
01267 static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
01268         struct dhcp_session *dhcp =
01269                 container_of ( timer, struct dhcp_session, timer );
01270 
01271         /* If we have failed, terminate DHCP */
01272         if ( fail ) {
01273                 dhcp_finished ( dhcp, -ETIMEDOUT );
01274                 return;
01275         }
01276 
01277         /* Increment transmission counter */
01278         dhcp->count++;
01279 
01280         /* Handle timer expiry based on current state */
01281         dhcp->state->expired ( dhcp );
01282 }
01283 
01284 /****************************************************************************
01285  *
01286  * Job control interface
01287  *
01288  */
01289 
01290 /** DHCP job control interface operations */
01291 static struct interface_operation dhcp_job_op[] = {
01292         INTF_OP ( intf_close, struct dhcp_session *, dhcp_finished ),
01293 };
01294 
01295 /** DHCP job control interface descriptor */
01296 static struct interface_descriptor dhcp_job_desc =
01297         INTF_DESC ( struct dhcp_session, job, dhcp_job_op );
01298 
01299 /****************************************************************************
01300  *
01301  * Instantiators
01302  *
01303  */
01304 
01305 /**
01306  * DHCP peer address for socket opening
01307  *
01308  * This is a dummy address; the only useful portion is the socket
01309  * family (so that we get a UDP connection).  The DHCP client will set
01310  * the IP address and source port explicitly on each transmission.
01311  */
01312 static struct sockaddr dhcp_peer = {
01313         .sa_family = AF_INET,
01314 };
01315 
01316 /**
01317  * Start DHCP state machine on a network device
01318  *
01319  * @v job               Job control interface
01320  * @v netdev            Network device
01321  * @ret rc              Return status code
01322  *
01323  * Starts DHCP on the specified network device.  If successful, the
01324  * DHCPACK (and ProxyDHCPACK, if applicable) will be registered as
01325  * option sources.
01326  */
01327 int start_dhcp ( struct interface *job, struct net_device *netdev ) {
01328         struct dhcp_session *dhcp;
01329         int rc;
01330 
01331         /* Allocate and initialise structure */
01332         dhcp = zalloc ( sizeof ( *dhcp ) );
01333         if ( ! dhcp )
01334                 return -ENOMEM;
01335         ref_init ( &dhcp->refcnt, dhcp_free );
01336         intf_init ( &dhcp->job, &dhcp_job_desc, &dhcp->refcnt );
01337         intf_init ( &dhcp->xfer, &dhcp_xfer_desc, &dhcp->refcnt );
01338         timer_init ( &dhcp->timer, dhcp_timer_expired, &dhcp->refcnt );
01339         dhcp->netdev = netdev_get ( netdev );
01340         dhcp->local.sin_family = AF_INET;
01341         dhcp->local.sin_port = htons ( BOOTPC_PORT );
01342         dhcp->xid = random();
01343 
01344         /* Store DHCP transaction ID for fakedhcp code */
01345         dhcp_last_xid = dhcp->xid;
01346 
01347         /* Instantiate child objects and attach to our interfaces */
01348         if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer,
01349                                   ( struct sockaddr * ) &dhcp->local ) ) != 0 )
01350                 goto err;
01351 
01352         /* Enter DHCPDISCOVER state */
01353         dhcp_set_state ( dhcp, &dhcp_state_discover );
01354 
01355         /* Attach parent interface, mortalise self, and return */
01356         intf_plug_plug ( &dhcp->job, job );
01357         ref_put ( &dhcp->refcnt );
01358         return 0;
01359 
01360  err:
01361         dhcp_finished ( dhcp, rc );
01362         ref_put ( &dhcp->refcnt );
01363         return rc;
01364 }
01365 
01366 /**
01367  * Retrieve list of PXE boot servers for a given server type
01368  *
01369  * @v dhcp              DHCP session
01370  * @v raw               DHCP PXE boot server list
01371  * @v raw_len           Length of DHCP PXE boot server list
01372  * @v ip                IP address list to fill in
01373  *
01374  * The caller must ensure that the IP address list has sufficient
01375  * space.
01376  */
01377 static void pxebs_list ( struct dhcp_session *dhcp, void *raw,
01378                          size_t raw_len, struct in_addr *ip ) {
01379         struct dhcp_pxe_boot_server *server = raw;
01380         size_t server_len;
01381         unsigned int i;
01382 
01383         while ( raw_len ) {
01384                 if ( raw_len < sizeof ( *server ) ) {
01385                         DBGC ( dhcp, "DHCP %p malformed PXE server list\n",
01386                                dhcp );
01387                         break;
01388                 }
01389                 server_len = offsetof ( typeof ( *server ),
01390                                         ip[ server->num_ip ] );
01391                 if ( raw_len < server_len ) {
01392                         DBGC ( dhcp, "DHCP %p malformed PXE server list\n",
01393                                dhcp );
01394                         break;
01395                 }
01396                 if ( server->type == dhcp->pxe_type ) {
01397                         for ( i = 0 ; i < server->num_ip ; i++ )
01398                                 *(ip++) = server->ip[i];
01399                 }
01400                 server = ( ( ( void * ) server ) + server_len );
01401                 raw_len -= server_len;
01402         }
01403 }
01404 
01405 /**
01406  * Start PXE Boot Server Discovery on a network device
01407  *
01408  * @v job               Job control interface
01409  * @v netdev            Network device
01410  * @v pxe_type          PXE server type
01411  * @ret rc              Return status code
01412  *
01413  * Starts PXE Boot Server Discovery on the specified network device.
01414  * If successful, the Boot Server ACK will be registered as an option
01415  * source.
01416  */
01417 int start_pxebs ( struct interface *job, struct net_device *netdev,
01418                   unsigned int pxe_type ) {
01419         struct setting pxe_discovery_control_setting =
01420                 { .tag = DHCP_PXE_DISCOVERY_CONTROL };
01421         struct setting pxe_boot_servers_setting =
01422                 { .tag = DHCP_PXE_BOOT_SERVERS };
01423         struct setting pxe_boot_server_mcast_setting =
01424                 { .tag = DHCP_PXE_BOOT_SERVER_MCAST };
01425         ssize_t pxebs_list_len;
01426         struct dhcp_session *dhcp;
01427         struct in_addr *ip;
01428         unsigned int pxe_discovery_control;
01429         int rc;
01430 
01431         /* Get upper bound for PXE boot server IP address list */
01432         pxebs_list_len = fetch_raw_setting ( NULL, &pxe_boot_servers_setting,
01433                                              NULL, 0 );
01434         if ( pxebs_list_len < 0 )
01435                 pxebs_list_len = 0;
01436 
01437         /* Allocate and initialise structure */
01438         dhcp = zalloc ( sizeof ( *dhcp ) + sizeof ( *ip ) /* mcast */ +
01439                         sizeof ( *ip ) /* bcast */ + pxebs_list_len +
01440                         sizeof ( *ip ) /* terminator */ );
01441         if ( ! dhcp )
01442                 return -ENOMEM;
01443         ref_init ( &dhcp->refcnt, dhcp_free );
01444         intf_init ( &dhcp->job, &dhcp_job_desc, &dhcp->refcnt );
01445         intf_init ( &dhcp->xfer, &dhcp_xfer_desc, &dhcp->refcnt );
01446         timer_init ( &dhcp->timer, dhcp_timer_expired, &dhcp->refcnt );
01447         dhcp->netdev = netdev_get ( netdev );
01448         dhcp->local.sin_family = AF_INET;
01449         fetch_ipv4_setting ( netdev_settings ( netdev ), &ip_setting,
01450                              &dhcp->local.sin_addr );
01451         dhcp->local.sin_port = htons ( BOOTPC_PORT );
01452         dhcp->pxe_type = cpu_to_le16 ( pxe_type );
01453 
01454         /* Construct PXE boot server IP address lists */
01455         pxe_discovery_control =
01456                 fetch_uintz_setting ( NULL, &pxe_discovery_control_setting );
01457         ip = ( ( ( void * ) dhcp ) + sizeof ( *dhcp ) );
01458         dhcp->pxe_attempt = ip;
01459         if ( ! ( pxe_discovery_control & PXEBS_NO_MULTICAST ) ) {
01460                 fetch_ipv4_setting ( NULL, &pxe_boot_server_mcast_setting, ip);
01461                 if ( ip->s_addr )
01462                         ip++;
01463         }
01464         if ( ! ( pxe_discovery_control & PXEBS_NO_BROADCAST ) )
01465                 (ip++)->s_addr = INADDR_BROADCAST;
01466         if ( pxe_discovery_control & PXEBS_NO_UNKNOWN_SERVERS )
01467                 dhcp->pxe_accept = ip;
01468         if ( pxebs_list_len ) {
01469                 uint8_t buf[pxebs_list_len];
01470 
01471                 fetch_raw_setting ( NULL, &pxe_boot_servers_setting,
01472                                     buf, sizeof ( buf ) );
01473                 pxebs_list ( dhcp, buf, sizeof ( buf ), ip );
01474         }
01475         if ( ! dhcp->pxe_attempt->s_addr ) {
01476                 DBGC ( dhcp, "DHCP %p has no PXE boot servers for type %04x\n",
01477                        dhcp, pxe_type );
01478                 rc = -EINVAL;
01479                 goto err;
01480         }
01481 
01482         /* Dump out PXE server lists */
01483         DBGC ( dhcp, "DHCP %p attempting", dhcp );
01484         for ( ip = dhcp->pxe_attempt ; ip->s_addr ; ip++ )
01485                 DBGC ( dhcp, " %s", inet_ntoa ( *ip ) );
01486         DBGC ( dhcp, "\n" );
01487         if ( dhcp->pxe_accept ) {
01488                 DBGC ( dhcp, "DHCP %p accepting", dhcp );
01489                 for ( ip = dhcp->pxe_accept ; ip->s_addr ; ip++ )
01490                         DBGC ( dhcp, " %s", inet_ntoa ( *ip ) );
01491                 DBGC ( dhcp, "\n" );
01492         }
01493 
01494         /* Instantiate child objects and attach to our interfaces */
01495         if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer,
01496                                   ( struct sockaddr * ) &dhcp->local ) ) != 0 )
01497                 goto err;
01498 
01499         /* Enter PXEBS state */
01500         dhcp_set_state ( dhcp, &dhcp_state_pxebs );
01501 
01502         /* Attach parent interface, mortalise self, and return */
01503         intf_plug_plug ( &dhcp->job, job );
01504         ref_put ( &dhcp->refcnt );
01505         return 0;
01506 
01507  err:
01508         dhcp_finished ( dhcp, rc );
01509         ref_put ( &dhcp->refcnt );
01510         return rc;
01511 }
01512 
01513 /** DHCP network device configurator */
01514 struct net_device_configurator dhcp_configurator __net_device_configurator = {
01515         .name = "dhcp",
01516         .start = start_dhcp,
01517 };