iPXE
Data Structures | Functions | Variables
dhcp.c File Reference

Dynamic Host Configuration Protocol. More...

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <byteswap.h>
#include <ipxe/if_ether.h>
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
#include <ipxe/device.h>
#include <ipxe/xfer.h>
#include <ipxe/open.h>
#include <ipxe/job.h>
#include <ipxe/retry.h>
#include <ipxe/tcpip.h>
#include <ipxe/ip.h>
#include <ipxe/uuid.h>
#include <ipxe/timer.h>
#include <ipxe/settings.h>
#include <ipxe/dhcp.h>
#include <ipxe/dhcpopts.h>
#include <ipxe/dhcppkt.h>
#include <ipxe/dhcp_arch.h>
#include <ipxe/features.h>
#include <config/dhcp.h>

Go to the source code of this file.

Data Structures

struct  dhcp_session_state
 DHCP session state operations. More...
struct  dhcp_session
 A DHCP session. More...

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static int dhcp_tx (struct dhcp_session *dhcp)
 Transmit DHCP request.
struct setting dhcp_server_setting __setting (SETTING_MISC, dhcp-server)
 DHCP server address setting.
static const char * dhcp_msgtype_name (unsigned int msgtype)
 Name a DHCP packet type.
static void dhcp_free (struct refcnt *refcnt)
 Free DHCP session.
static void dhcp_finished (struct dhcp_session *dhcp, int rc)
 Mark DHCP session as complete.
static void dhcp_set_state (struct dhcp_session *dhcp, struct dhcp_session_state *state)
 Transition to new DHCP session state.
static int dhcp_has_pxeopts (struct dhcp_packet *dhcppkt)
 Check if DHCP packet contains PXE options.
static int dhcp_discovery_tx (struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt __unused, struct sockaddr_in *peer)
 Construct transmitted packet for DHCP discovery.
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 pseudo_id)
 Handle received packet during DHCP discovery.
static void dhcp_discovery_expired (struct dhcp_session *dhcp)
 Handle timer expiry during DHCP discovery.
static int dhcp_request_tx (struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer)
 Construct transmitted packet for DHCP request.
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 pseudo_id)
 Handle received packet during DHCP request.
static void dhcp_request_expired (struct dhcp_session *dhcp)
 Handle timer expiry during DHCP discovery.
static int dhcp_proxy_tx (struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer)
 Construct transmitted packet for ProxyDHCP request.
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 pseudo_id)
 Handle received packet during ProxyDHCP request.
static void dhcp_proxy_expired (struct dhcp_session *dhcp)
 Handle timer expiry during ProxyDHCP request.
static int dhcp_pxebs_tx (struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer)
 Construct transmitted packet for PXE Boot Server Discovery.
static int dhcp_pxebs_accept (struct dhcp_session *dhcp, struct in_addr bs)
 Check to see if PXE Boot Server address is acceptable.
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 pseudo_id)
 Handle received packet during PXE Boot Server Discovery.
static void dhcp_pxebs_expired (struct dhcp_session *dhcp)
 Handle timer expiry during PXE Boot Server Discovery.
int dhcp_create_packet (struct dhcp_packet *dhcppkt, struct net_device *netdev, uint8_t msgtype, uint32_t xid, const void *options, size_t options_len, void *data, size_t max_len)
 Create a DHCP packet.
int dhcp_create_request (struct dhcp_packet *dhcppkt, struct net_device *netdev, unsigned int msgtype, uint32_t xid, struct in_addr ciaddr, void *data, size_t max_len)
 Create DHCP request packet.
static int dhcp_deliver (struct dhcp_session *dhcp, struct io_buffer *iobuf, struct xfer_metadata *meta)
 Receive new data.
static void dhcp_timer_expired (struct retry_timer *timer, int fail)
 Handle DHCP retry timer expiry.
int start_dhcp (struct interface *job, struct net_device *netdev)
 Start DHCP state machine on a network device.
static void pxebs_list (struct dhcp_session *dhcp, void *raw, size_t raw_len, struct in_addr *ip)
 Retrieve list of PXE boot servers for a given server type.
int start_pxebs (struct interface *job, struct net_device *netdev, unsigned int pxe_type)
 Start PXE Boot Server Discovery on a network device.

Variables

static const uint8_t dhcp_op []
 DHCP operation types.
static uint8_t dhcp_request_options_data []
 Raw option data for options common to all DHCP requests.
static struct settingdhcp_request_settings []
 Settings copied in to all DHCP requests.
uint32_t dhcp_last_xid
 Most recent DHCP transaction ID.
static struct dhcp_session_state dhcp_state_discover
 DHCP discovery state operations.
static struct dhcp_session_state dhcp_state_request
 DHCP request state operations.
static struct dhcp_session_state dhcp_state_proxy
 ProxyDHCP request state operations.
static struct dhcp_session_state dhcp_state_pxebs
 PXE Boot Server Discovery state operations.
static struct interface_operation dhcp_xfer_operations []
 DHCP data transfer interface operations.
static struct interface_descriptor dhcp_xfer_desc
 DHCP data transfer interface descriptor.
static struct interface_operation dhcp_job_op []
 DHCP job control interface operations.
static struct interface_descriptor dhcp_job_desc
 DHCP job control interface descriptor.
static struct sockaddr dhcp_peer
 DHCP peer address for socket opening.
struct net_device_configurator
dhcp_configurator 
__net_device_configurator
 DHCP network device configurator.

Detailed Description

Dynamic Host Configuration Protocol.

Definition in file dhcp.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static int dhcp_tx ( struct dhcp_session dhcp) [static]

Transmit DHCP request.

Parameters:
dhcpDHCP session
Return values:
rcReturn status code

Definition at line 1095 of file dhcp.c.

References AF_INET, dhcp_session::count, io_buffer::data, DBGC, dhcp_create_request(), DHCP_MIN_LEN, dhcp_packet::dhcphdr, dhcppkt_len(), done, ENOMEM, free_iob(), htons, iob_disown, iob_put, iob_tailroom(), dhcp_session::local, xfer_metadata::netdev, dhcp_session::netdev, dhcp_session::offer, dhcp_session::proxy_offer, rc, in_addr::s_addr, dhcphdr::secs, sockaddr_in::sin_addr, sockaddr_in::sin_family, start_timer(), dhcp_session::state, strerror(), dhcp_session::timer, dhcp_session_state::tx, dhcp_session_state::tx_msgtype, dhcp_session::xfer, xfer_alloc_iob(), xfer_deliver(), and dhcp_session::xid.

Referenced by dhcp_discovery_expired(), dhcp_proxy_expired(), dhcp_pxebs_expired(), and dhcp_request_expired().

                                                 {
        static struct sockaddr_in peer = {
                .sin_family = AF_INET,
        };
        struct xfer_metadata meta = {
                .netdev = dhcp->netdev,
                .src = ( struct sockaddr * ) &dhcp->local,
                .dest = ( struct sockaddr * ) &peer,
        };
        struct io_buffer *iobuf;
        uint8_t msgtype = dhcp->state->tx_msgtype;
        struct dhcp_packet dhcppkt;
        int rc;

        /* Start retry timer.  Do this first so that failures to
         * transmit will be retried.
         */
        start_timer ( &dhcp->timer );

        /* Allocate buffer for packet */
        iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN );
        if ( ! iobuf )
                return -ENOMEM;

        /* Create basic DHCP packet in temporary buffer */
        if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, msgtype,
                                          dhcp->xid, dhcp->local.sin_addr,
                                          iobuf->data,
                                          iob_tailroom ( iobuf ) ) ) != 0 ) {
                DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n",
                       dhcp, strerror ( rc ) );
                goto done;
        }

        /* (Ab)use the "secs" field to convey metadata about the DHCP
         * session state into packet traces.  Useful for extracting
         * debug information from non-debug builds.
         */
        dhcppkt.dhcphdr->secs = htons ( ( dhcp->count << 2 ) |
                                        ( dhcp->offer.s_addr ? 0x02 : 0 ) |
                                        ( dhcp->proxy_offer ? 0x01 : 0 ) );

        /* Fill in packet based on current state */
        if ( ( rc = dhcp->state->tx ( dhcp, &dhcppkt, &peer ) ) != 0 ) {
                DBGC ( dhcp, "DHCP %p could not fill DHCP request: %s\n",
                       dhcp, strerror ( rc ) );
                goto done;
        }

        /* Transmit the packet */
        iob_put ( iobuf, dhcppkt_len ( &dhcppkt ) );
        if ( ( rc = xfer_deliver ( &dhcp->xfer, iob_disown ( iobuf ),
                                   &meta ) ) != 0 ) {
                DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n",
                       dhcp, strerror ( rc ) );
                goto done;
        }

 done:
        free_iob ( iobuf );
        return rc;
}
struct setting dhcp_server_setting __setting ( SETTING_MISC  ,
dhcp-  server 
) [read]

DHCP server address setting.

static const char* dhcp_msgtype_name ( unsigned int  msgtype) [inline, static]

Name a DHCP packet type.

Parameters:
msgtypeDHCP message type
Return values:
stringDHCP mesasge type name

Definition at line 131 of file dhcp.c.

References DHCPACK, DHCPDECLINE, DHCPDISCOVER, DHCPINFORM, DHCPNAK, DHCPNONE, DHCPOFFER, DHCPRELEASE, and DHCPREQUEST.

Referenced by dhcp_deliver(), dhcp_discovery_rx(), dhcp_proxy_rx(), dhcp_pxebs_rx(), and dhcp_request_rx().

                                                                      {
        switch ( msgtype ) {
        case DHCPNONE:          return "BOOTP"; /* Non-DHCP packet */
        case DHCPDISCOVER:      return "DHCPDISCOVER";
        case DHCPOFFER:         return "DHCPOFFER";
        case DHCPREQUEST:       return "DHCPREQUEST";
        case DHCPDECLINE:       return "DHCPDECLINE";
        case DHCPACK:           return "DHCPACK";
        case DHCPNAK:           return "DHCPNAK";
        case DHCPRELEASE:       return "DHCPRELEASE";
        case DHCPINFORM:        return "DHCPINFORM";
        default:                return "DHCP<invalid>";
        }
}
static void dhcp_free ( struct refcnt refcnt) [static]

Free DHCP session.

Parameters:
refcntReference counter

Definition at line 252 of file dhcp.c.

References container_of, dhcppkt_put(), free, dhcp_session::netdev, netdev_put(), and dhcp_session::proxy_offer.

Referenced by start_dhcp(), and start_pxebs().

                                                {
        struct dhcp_session *dhcp =
                container_of ( refcnt, struct dhcp_session, refcnt );

        netdev_put ( dhcp->netdev );
        dhcppkt_put ( dhcp->proxy_offer );
        free ( dhcp );
}
static void dhcp_finished ( struct dhcp_session dhcp,
int  rc 
) [static]

Mark DHCP session as complete.

Parameters:
dhcpDHCP session
rcReturn status code

Definition at line 267 of file dhcp.c.

References intf_shutdown(), dhcp_session::job, stop_timer(), dhcp_session::timer, and dhcp_session::xfer.

Referenced by dhcp_proxy_expired(), dhcp_proxy_rx(), dhcp_pxebs_expired(), dhcp_pxebs_rx(), dhcp_request_rx(), dhcp_timer_expired(), start_dhcp(), and start_pxebs().

                                                                {

        /* Stop retry timer */
        stop_timer ( &dhcp->timer );

        /* Shut down interfaces */
        intf_shutdown ( &dhcp->xfer, rc );
        intf_shutdown ( &dhcp->job, rc );
}
static void dhcp_set_state ( struct dhcp_session dhcp,
struct dhcp_session_state state 
) [static]

Transition to new DHCP session state.

Parameters:
dhcpDHCP session
stateNew session state

Definition at line 283 of file dhcp.c.

References currticks(), DBGC, dhcp_session_state::max_timeout_sec, dhcp_session_state::min_timeout_sec, dhcp_session_state::name, dhcp_session::start, start_timer_nodelay(), state, dhcp_session::state, stop_timer(), TICKS_PER_SEC, and dhcp_session::timer.

Referenced by dhcp_discovery_expired(), dhcp_discovery_rx(), dhcp_pxebs_expired(), dhcp_request_rx(), start_dhcp(), and start_pxebs().

                                                                {

        DBGC ( dhcp, "DHCP %p entering %s state\n", dhcp, state->name );
        dhcp->state = state;
        dhcp->start = currticks();
        stop_timer ( &dhcp->timer );
        set_timer_limits ( &dhcp->timer,
                           ( state->min_timeout_sec * TICKS_PER_SEC ),
                           ( state->max_timeout_sec * TICKS_PER_SEC ) );
        start_timer_nodelay ( &dhcp->timer );
}
static int dhcp_has_pxeopts ( struct dhcp_packet dhcppkt) [static]

Check if DHCP packet contains PXE options.

Parameters:
dhcppktDHCP packet
Return values:
has_pxeoptsDHCP packet contains PXE options

It is assumed that the packet is already known to contain option 60 set to "PXEClient".

Definition at line 305 of file dhcp.c.

References DHCP_BOOTFILE_NAME, DHCP_PXE_BOOT_MENU, dhcp_packet::dhcphdr, dhcppkt_fetch(), NULL, in_addr::s_addr, and dhcphdr::siaddr.

Referenced by dhcp_discovery_rx(), dhcp_proxy_rx(), and dhcp_request_rx().

                                                            {

        /* Check for a next-server and boot filename */
        if ( dhcppkt->dhcphdr->siaddr.s_addr &&
             ( dhcppkt_fetch ( dhcppkt, DHCP_BOOTFILE_NAME, NULL, 0 ) > 0 ) )
                return 1;

        /* Check for a PXE boot menu */
        if ( dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU, NULL, 0 ) > 0 )
                return 1;

        return 0;
}
static int dhcp_discovery_tx ( struct dhcp_session dhcp,
struct dhcp_packet *dhcppkt  __unused,
struct sockaddr_in peer 
) [static]

Construct transmitted packet for DHCP discovery.

Parameters:
dhcpDHCP session
dhcppktDHCP packet
peerDestination address

Definition at line 332 of file dhcp.c.

References BOOTPS_PORT, DBGC, htons, INADDR_BROADCAST, in_addr::s_addr, sockaddr_in::sin_addr, and sockaddr_in::sin_port.

                                                          {

        DBGC ( dhcp, "DHCP %p DHCPDISCOVER\n", dhcp );

        /* Set server address */
        peer->sin_addr.s_addr = INADDR_BROADCAST;
        peer->sin_port = htons ( BOOTPS_PORT );

        return 0;
}
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  pseudo_id 
) [static]

Handle received packet during DHCP discovery.

Parameters:
dhcpDHCP session
dhcppktDHCP packet
peerDHCP server address
msgtypeDHCP message type
server_idDHCP server ID
pseudo_idDHCP server pseudo-ID

Definition at line 355 of file dhcp.c.

References BOOTPS_PORT, currticks(), DBGC, DHCP_DISC_PROXY_TIMEOUT_SEC, DHCP_EB_NO_PXEDHCP, DHCP_EB_PRIORITY, dhcp_has_pxeopts(), dhcp_msgtype_name(), dhcp_set_state(), dhcp_state_request, DHCP_VENDOR_CLASS_ID, dhcp_packet::dhcphdr, DHCPOFFER, dhcppkt_fetch(), dhcppkt_get(), dhcppkt_put(), htons, inet_ntoa(), ip, dhcp_session::no_pxedhcp, ntohs, dhcp_session::offer, priority, dhcp_session::priority, dhcp_session::proxy_offer, dhcp_session::proxy_priority, dhcp_session::proxy_server, in_addr::s_addr, dhcp_session::server, sockaddr_in::sin_addr, sockaddr_in::sin_port, dhcp_session::start, strncmp(), TICKS_PER_SEC, and dhcphdr::yiaddr.

                                                           {
        struct in_addr ip;
        char vci[9]; /* "PXEClient" */
        int vci_len;
        int has_pxeclient;
        int8_t priority = 0;
        uint8_t no_pxedhcp = 0;
        unsigned long elapsed;

        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 ) ||
             ( 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;
        if ( ip.s_addr )
                DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );

        /* Identify "PXEClient" vendor class */
        vci_len = dhcppkt_fetch ( dhcppkt, DHCP_VENDOR_CLASS_ID,
                                  vci, sizeof ( vci ) );
        has_pxeclient = ( ( vci_len >= ( int ) sizeof ( vci ) ) &&
                          ( strncmp ( "PXEClient", vci, sizeof (vci) ) == 0 ));
        if ( has_pxeclient ) {
                DBGC ( dhcp, "%s",
                       ( dhcp_has_pxeopts ( dhcppkt ) ? " pxe" : " proxy" ) );
        }

        /* Identify priority */
        dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority,
                        sizeof ( priority ) );
        if ( priority )
                DBGC ( dhcp, " pri %d", priority );

        /* Identify ignore-PXE flag */
        dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &no_pxedhcp,
                        sizeof ( no_pxedhcp ) );
        if ( no_pxedhcp )
                DBGC ( dhcp, " nopxe" );
        DBGC ( dhcp, "\n" );

        /* Select as DHCP offer, if applicable */
        if ( ip.s_addr && ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
             ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) &&
             ( priority >= dhcp->priority ) ) {
                dhcp->offer = ip;
                dhcp->server = server_id;
                dhcp->priority = priority;
                dhcp->no_pxedhcp = no_pxedhcp;
        }

        /* Select as ProxyDHCP offer, if applicable */
        if ( pseudo_id.s_addr && has_pxeclient &&
             ( priority >= dhcp->proxy_priority ) ) {
                dhcppkt_put ( dhcp->proxy_offer );
                dhcp->proxy_server = pseudo_id;
                dhcp->proxy_offer = dhcppkt_get ( dhcppkt );
                dhcp->proxy_priority = priority;
        }

        /* We can exit the discovery state when we have a valid
         * DHCPOFFER, and either:
         *
         *  o  The DHCPOFFER instructs us to ignore ProxyDHCPOFFERs, or
         *  o  We have a valid ProxyDHCPOFFER, or
         *  o  We have allowed sufficient time for ProxyDHCPOFFERs.
         */

        /* If we don't yet have a DHCPOFFER, do nothing */
        if ( ! dhcp->offer.s_addr )
                return;

        /* If we can't yet transition to DHCPREQUEST, do nothing */
        elapsed = ( currticks() - dhcp->start );
        if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_offer ||
                 ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) )
                return;

        /* Transition to DHCPREQUEST */
        dhcp_set_state ( dhcp, &dhcp_state_request );
}
static void dhcp_discovery_expired ( struct dhcp_session dhcp) [static]

Handle timer expiry during DHCP discovery.

Parameters:
dhcpDHCP session

Definition at line 451 of file dhcp.c.

References dhcp_session::count, currticks(), DBGC, DHCP_DISC_MAX_DEFERRALS, DHCP_DISC_PROXY_TIMEOUT_SEC, DHCP_DISC_START_TIMEOUT_SEC, dhcp_set_state(), dhcp_state_request, dhcp_tx(), dhcp_session::netdev, netdev_link_blocked(), dhcp_session::offer, in_addr::s_addr, dhcp_session::start, start_timer_fixed(), TICKS_PER_SEC, and dhcp_session::timer.

                                                                 {
        unsigned long elapsed = ( currticks() - dhcp->start );

        /* If link is blocked, defer DHCP discovery (and reset timeout) */
        if ( netdev_link_blocked ( dhcp->netdev ) &&
             ( dhcp->count <= DHCP_DISC_MAX_DEFERRALS ) ) {
                DBGC ( dhcp, "DHCP %p deferring discovery\n", dhcp );
                dhcp->start = currticks();
                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 > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) {
                dhcp_set_state ( dhcp, &dhcp_state_request );
                return;
        }

        /* Otherwise, retransmit current packet */
        dhcp_tx ( dhcp );
}
static int dhcp_request_tx ( struct dhcp_session dhcp,
struct dhcp_packet dhcppkt,
struct sockaddr_in peer 
) [static]

Construct transmitted packet for DHCP request.

Parameters:
dhcpDHCP session
dhcppktDHCP packet
peerDestination address

Definition at line 494 of file dhcp.c.

References BOOTPS_PORT, DBGC, DHCP_REQUESTED_ADDRESS, DHCP_SERVER_IDENTIFIER, dhcppkt_store(), htons, INADDR_BROADCAST, inet_ntoa(), dhcp_session::offer, rc, in_addr::s_addr, dhcp_session::server, sockaddr_in::sin_addr, and sockaddr_in::sin_port.

                                                        {
        int rc;

        DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d",
               dhcp, inet_ntoa ( dhcp->server ), BOOTPS_PORT );
        DBGC ( dhcp, " for %s\n", inet_ntoa ( dhcp->offer ) );

        /* Set server ID */
        if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
                                    &dhcp->server,
                                    sizeof ( dhcp->server ) ) ) != 0 )
                return rc;

        /* Set requested IP address */
        if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
                                    &dhcp->offer,
                                    sizeof ( dhcp->offer ) ) ) != 0 )
                return rc;

        /* Set server address */
        peer->sin_addr.s_addr = INADDR_BROADCAST;
        peer->sin_port = htons ( BOOTPS_PORT );

        return 0;
}
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  pseudo_id 
) [static]

Handle received packet during DHCP request.

Parameters:
dhcpDHCP session
dhcppktDHCP packet
peerDHCP server address
msgtypeDHCP message type
server_idDHCP server ID
pseudo_idDHCP server pseudo-ID

Definition at line 532 of file dhcp.c.

References BOOTPS_PORT, DBGC, dhcp_finished(), dhcp_has_pxeopts(), dhcp_msgtype_name(), dhcp_set_state(), DHCP_SETTINGS_NAME, dhcp_state_proxy, DHCPACK, dhcp_packet::dhcphdr, htons, inet_ntoa(), ip, dhcp_session::local, dhcp_session::netdev, netdev_settings(), dhcp_session::no_pxedhcp, ntohs, NULL, dhcp_session::offer, settings::parent, dhcp_session::proxy_offer, PROXYDHCP_SETTINGS_NAME, rc, register_settings(), in_addr::s_addr, dhcp_session::server, dhcp_packet::settings, sockaddr_in::sin_addr, sockaddr_in::sin_port, strerror(), and dhcphdr::yiaddr.

                                                         {
        struct in_addr ip;
        struct settings *parent;
        struct settings *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 ) ||
             ( 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;
        if ( ip.s_addr )
                DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
        DBGC ( dhcp, "\n" );

        /* Filter out unacceptable responses */
        if ( peer->sin_port != htons ( BOOTPS_PORT ) )
                return;
        if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
                return;
        if ( server_id.s_addr != dhcp->server.s_addr )
                return;
        if ( ip.s_addr != dhcp->offer.s_addr )
                return;

        /* Record assigned address */
        dhcp->local.sin_addr = ip;

        /* Register settings */
        parent = netdev_settings ( dhcp->netdev );
        settings = &dhcppkt->settings;
        if ( ( rc = register_settings ( settings, parent,
                                        DHCP_SETTINGS_NAME ) ) != 0 ) {
                DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
                       dhcp, strerror ( rc ) );
                dhcp_finished ( dhcp, rc );
                return;
        }

        /* Perform ProxyDHCP if applicable */
        if ( dhcp->proxy_offer /* Have ProxyDHCP offer */ &&
             ( ! dhcp->no_pxedhcp ) /* ProxyDHCP not disabled */ ) {
                if ( dhcp_has_pxeopts ( dhcp->proxy_offer ) ) {
                        /* PXE options already present; register settings
                         * without performing a ProxyDHCPREQUEST
                         */
                        settings = &dhcp->proxy_offer->settings;
                        if ( ( rc = register_settings ( settings, NULL,
                                           PROXYDHCP_SETTINGS_NAME ) ) != 0 ) {
                                DBGC ( dhcp, "DHCP %p could not register "
                                       "proxy settings: %s\n",
                                       dhcp, strerror ( rc ) );
                                dhcp_finished ( dhcp, rc );
                                return;
                        }
                } else {
                        /* PXE options not present; use a ProxyDHCPREQUEST */
                        dhcp_set_state ( dhcp, &dhcp_state_proxy );
                        return;
                }
        }

        /* Terminate DHCP */
        dhcp_finished ( dhcp, 0 );
}
static void dhcp_request_expired ( struct dhcp_session dhcp) [static]

Handle timer expiry during DHCP discovery.

Parameters:
dhcpDHCP session

Definition at line 613 of file dhcp.c.

References dhcp_tx().

                                                               {

        /* Retransmit current packet */
        dhcp_tx ( dhcp );
}
static int dhcp_proxy_tx ( struct dhcp_session dhcp,
struct dhcp_packet dhcppkt,
struct sockaddr_in peer 
) [static]

Construct transmitted packet for ProxyDHCP request.

Parameters:
dhcpDHCP session
dhcppktDHCP packet
peerDestination address

Definition at line 637 of file dhcp.c.

References DBGC, DHCP_SERVER_IDENTIFIER, dhcppkt_store(), htons, inet_ntoa(), dhcp_session::proxy_server, PXE_PORT, rc, sockaddr_in::sin_addr, and sockaddr_in::sin_port.

                                                      {
        int rc;

        DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s\n", dhcp,
               inet_ntoa ( dhcp->proxy_server ) );

        /* Set server ID */
        if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
                                    &dhcp->proxy_server,
                                    sizeof ( dhcp->proxy_server ) ) ) != 0 )
                return rc;

        /* Set server address */
        peer->sin_addr = dhcp->proxy_server;
        peer->sin_port = htons ( PXE_PORT );

        return 0;
}
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  pseudo_id 
) [static]

Handle received packet during ProxyDHCP request.

Parameters:
dhcpDHCP session
dhcppktDHCP packet
peerDHCP server address
msgtypeDHCP message type
server_idDHCP server ID
pseudo_idDHCP server pseudo-ID

Definition at line 668 of file dhcp.c.

References DBGC, dhcp_finished(), dhcp_has_pxeopts(), dhcp_msgtype_name(), DHCPACK, DHCPOFFER, inet_ntoa(), ntohs, NULL, dhcp_session::proxy_server, PROXYDHCP_SETTINGS_NAME, PXE_PORT, rc, register_settings(), in_addr::s_addr, dhcp_packet::settings, sockaddr_in::sin_addr, sockaddr_in::sin_port, and strerror().

                                                       {
        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 ) ||
             ( 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 */
        if ( peer->sin_port != ntohs ( PXE_PORT ) )
                return;
        if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) )
                return;
        if ( ( pseudo_id.s_addr != dhcp->proxy_server.s_addr ) )
                return;
        if ( ! dhcp_has_pxeopts ( dhcppkt ) )
                return;

        /* Register settings */
        if ( ( rc = register_settings ( settings, NULL,
                                        PROXYDHCP_SETTINGS_NAME ) ) != 0 ) {
                DBGC ( dhcp, "DHCP %p could not register proxy settings: %s\n",
                       dhcp, strerror ( rc ) );
                dhcp_finished ( dhcp, rc );
                return;
        }

        /* Terminate DHCP */
        dhcp_finished ( dhcp, 0 );
}
static void dhcp_proxy_expired ( struct dhcp_session dhcp) [static]

Handle timer expiry during ProxyDHCP request.

Parameters:
dhcpDHCP session

Definition at line 716 of file dhcp.c.

References currticks(), dhcp_finished(), DHCP_REQ_PROXY_TIMEOUT_SEC, dhcp_tx(), dhcp_session::start, and TICKS_PER_SEC.

                                                             {
        unsigned long elapsed = ( currticks() - dhcp->start );

        /* Give up waiting for ProxyDHCP before we reach the failure point */
        if ( elapsed > DHCP_REQ_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) {
                dhcp_finished ( dhcp, 0 );
                return;
        }

        /* Retransmit current packet */
        dhcp_tx ( dhcp );
}
static int dhcp_pxebs_tx ( struct dhcp_session dhcp,
struct dhcp_packet dhcppkt,
struct sockaddr_in peer 
) [static]

Construct transmitted packet for PXE Boot Server Discovery.

Parameters:
dhcpDHCP session
dhcppktDHCP packet
peerDestination address

Definition at line 747 of file dhcp.c.

References BOOTPS_PORT, DBGC, DHCP_PXE_BOOT_MENU_ITEM, dhcppkt_store(), htons, INADDR_BROADCAST, inet_ntoa(), le16_to_cpu, ntohs, dhcp_session::pxe_attempt, PXE_PORT, dhcp_session::pxe_type, rc, in_addr::s_addr, sockaddr_in::sin_addr, sockaddr_in::sin_port, and dhcp_pxe_boot_menu_item::type.

                                                      {
        struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 };
        int rc;

        /* Set server address */
        peer->sin_addr = *(dhcp->pxe_attempt);
        peer->sin_port = ( ( peer->sin_addr.s_addr == INADDR_BROADCAST ) ?
                           htons ( BOOTPS_PORT ) : htons ( PXE_PORT ) );

        DBGC ( dhcp, "DHCP %p PXEBS REQUEST to %s:%d for type %d\n",
               dhcp, inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ),
               le16_to_cpu ( dhcp->pxe_type ) );

        /* Set boot menu item */
        menu_item.type = dhcp->pxe_type;
        if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM,
                                    &menu_item, sizeof ( menu_item ) ) ) != 0 )
                return rc;

        return 0;
}
static int dhcp_pxebs_accept ( struct dhcp_session dhcp,
struct in_addr  bs 
) [static]

Check to see if PXE Boot Server address is acceptable.

Parameters:
dhcpDHCP session
bsBoot Server address
Return values:
acceptBoot Server is acceptable

Definition at line 778 of file dhcp.c.

References DBGC, inet_ntoa(), dhcp_session::pxe_accept, and in_addr::s_addr.

Referenced by dhcp_pxebs_rx().

                                                   {
        struct in_addr *accept;

        /* Accept if we have no acceptance filter */
        if ( ! dhcp->pxe_accept )
                return 1;

        /* Scan through acceptance list */
        for ( accept = dhcp->pxe_accept ; accept->s_addr ; accept++ ) {
                if ( accept->s_addr == bs.s_addr )
                        return 1;
        }

        DBGC ( dhcp, "DHCP %p rejecting server %s\n",
               dhcp, inet_ntoa ( bs ) );
        return 0;
}
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  pseudo_id 
) [static]

Handle received packet during PXE Boot Server Discovery.

Parameters:
dhcpDHCP session
dhcppktDHCP packet
peerDHCP server address
msgtypeDHCP message type
server_idDHCP server ID
pseudo_idDHCP server pseudo-ID

Definition at line 807 of file dhcp.c.

References BOOTPS_PORT, DBGC, dhcp_finished(), dhcp_msgtype_name(), DHCP_PXE_BOOT_MENU_ITEM, dhcp_pxebs_accept(), DHCPACK, dhcppkt_fetch(), htons, inet_ntoa(), ntohs, NULL, PXE_PORT, dhcp_session::pxe_type, PXEBS_SETTINGS_NAME, rc, register_settings(), in_addr::s_addr, dhcp_packet::settings, sockaddr_in::sin_addr, sockaddr_in::sin_port, strerror(), and dhcp_pxe_boot_menu_item::type.

                                                       {
        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 ) ||
             ( 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,
                        &menu_item, sizeof ( menu_item ) );
        if ( menu_item.type )
                DBGC ( dhcp, " for type %d", ntohs ( menu_item.type ) );
        DBGC ( dhcp, "\n" );

        /* Filter out unacceptable responses */
        if ( ( peer->sin_port != htons ( BOOTPS_PORT ) ) &&
             ( peer->sin_port != htons ( PXE_PORT ) ) )
                return;
        if ( msgtype != DHCPACK )
                return;
        if ( menu_item.type != dhcp->pxe_type )
                return;
        if ( ! dhcp_pxebs_accept ( dhcp, pseudo_id ) )
                return;

        /* Register settings */
        if ( ( rc = register_settings ( &dhcppkt->settings, NULL,
                                        PXEBS_SETTINGS_NAME ) ) != 0 ) {
                DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
                       dhcp, strerror ( rc ) );
                dhcp_finished ( dhcp, rc );
                return;
        }

        /* Terminate DHCP */
        dhcp_finished ( dhcp, 0 );
}
static void dhcp_pxebs_expired ( struct dhcp_session dhcp) [static]

Handle timer expiry during PXE Boot Server Discovery.

Parameters:
dhcpDHCP session

Definition at line 860 of file dhcp.c.

References currticks(), dhcp_finished(), dhcp_set_state(), dhcp_state_pxebs, dhcp_tx(), ETIMEDOUT, dhcp_session::pxe_attempt, PXEBS_MAX_TIMEOUT_SEC, in_addr::s_addr, dhcp_session::start, and TICKS_PER_SEC.

                                                             {
        unsigned long elapsed = ( currticks() - dhcp->start );

        /* 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_SEC * TICKS_PER_SEC ) {
                dhcp->pxe_attempt++;
                if ( dhcp->pxe_attempt->s_addr ) {
                        dhcp_set_state ( dhcp, &dhcp_state_pxebs );
                        return;
                } else {
                        dhcp_finished ( dhcp, -ETIMEDOUT );
                        return;
                }
        }

        /* Retransmit current packet */
        dhcp_tx ( dhcp );
}
int dhcp_create_packet ( struct dhcp_packet dhcppkt,
struct net_device netdev,
uint8_t  msgtype,
uint32_t  xid,
const void *  options,
size_t  options_len,
void *  data,
size_t  max_len 
)

Create a DHCP packet.

Parameters:
dhcppktDHCP packet structure to fill in
netdevNetwork device
msgtypeDHCP message type
xidTransaction ID (in network-endian order)
optionsInitial options to include (or NULL)
options_lenLength of initial options
dataBuffer for DHCP packet
max_lenSize of DHCP packet buffer
Return values:
rcReturn status code

Creates a DHCP packet in the specified buffer, and initialise a DHCP packet structure.

Definition at line 914 of file dhcp.c.

References BOOTP_FL_BROADCAST, dhcphdr::chaddr, data, DHCP_MAGIC_COOKIE, DHCP_MESSAGE_TYPE, dhcp_op, dhcppkt_init(), dhcppkt_store(), ENOSPC, ll_protocol::flags, dhcphdr::flags, dhcphdr::hlen, htonl, htons, dhcphdr::htype, ipv4_has_any_addr(), net_device::ll_addr, ll_protocol::ll_addr_len, LL_NAME_ONLY, ll_protocol::ll_proto, net_device::ll_protocol, dhcphdr::magic, memcpy(), memset(), ntohs, dhcphdr::op, dhcphdr::options, rc, and dhcphdr::xid.

Referenced by create_fakedhcpack(), create_fakepxebsack(), and dhcp_create_request().

                                                      {
        struct dhcphdr *dhcphdr = data;
        int rc;

        /* Sanity check */
        if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) )
                return -ENOSPC;

        /* Initialise DHCP packet content */
        memset ( dhcphdr, 0, max_len );
        dhcphdr->xid = xid;
        dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
        dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
        dhcphdr->op = dhcp_op[msgtype];
        dhcphdr->hlen = netdev->ll_protocol->ll_addr_len;
        memcpy ( dhcphdr->chaddr, netdev->ll_addr,
                 netdev->ll_protocol->ll_addr_len );
        memcpy ( dhcphdr->options, options, options_len );

        /* If the local link-layer address functions only as a name
         * (i.e. cannot be used as a destination address), then
         * request broadcast responses.
         */
        if ( netdev->ll_protocol->flags & LL_NAME_ONLY )
                dhcphdr->flags |= htons ( BOOTP_FL_BROADCAST );

        /* If the network device already has an IPv4 address then
         * unicast responses from the DHCP server may be rejected, so
         * request broadcast responses.
         */
        if ( ipv4_has_any_addr ( netdev ) )
                dhcphdr->flags |= htons ( BOOTP_FL_BROADCAST );

        /* Initialise DHCP packet structure */
        memset ( dhcppkt, 0, sizeof ( *dhcppkt ) );
        dhcppkt_init ( dhcppkt, data, max_len );
        
        /* Set DHCP_MESSAGE_TYPE option */
        if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE,
                                    &msgtype, sizeof ( msgtype ) ) ) != 0 )
                return rc;

        return 0;
}
int dhcp_create_request ( struct dhcp_packet dhcppkt,
struct net_device netdev,
unsigned int  msgtype,
uint32_t  xid,
struct in_addr  ciaddr,
void *  data,
size_t  max_len 
)

Create DHCP request packet.

Parameters:
dhcppktDHCP packet structure to fill in
netdevNetwork device
msgtypeDHCP message type
xidTransaction ID (in network-endian order)
ciaddrClient IP address
dataBuffer for DHCP packet
max_lenSize of DHCP packet buffer
Return values:
rcReturn status code

Creates a DHCP request packet in the specified buffer, and initialise a DHCP packet structure.

Definition at line 977 of file dhcp.c.

References assert, dhcphdr::ciaddr, DBG, DHCP_CLIENT_ID, DHCP_CLIENT_UUID, DHCP_CLIENT_UUID_TYPE, dhcp_create_packet(), DHCP_EB_BUS_ID, DHCP_EB_ENCAP, DHCP_FEATURES, dhcp_request_options_data, dhcp_request_settings, dhcp_packet::dhcphdr, dhcppkt_store(), fetch_raw_setting(), fetch_raw_setting_copy(), fetch_uuid_setting(), free, len, dhcp_client_id::ll_addr, net_device::ll_addr, ll_protocol::ll_addr_len, ll_protocol::ll_proto, dhcp_client_id::ll_proto, net_device::ll_protocol, memcpy(), setting::name, netdev_settings(), ntohs, NULL, raw, rc, strerror(), table_num_entries, table_start, setting::tag, dhcp_client_uuid::type, dhcp_client_uuid::uuid, and uuid_mangle().

Referenced by create_fakedhcpdiscover(), and dhcp_tx().

                                                       {
        struct dhcp_netdev_desc dhcp_desc;
        struct dhcp_client_id client_id;
        struct dhcp_client_uuid client_uuid;
        const struct setting *setting;
        uint8_t *dhcp_features;
        size_t dhcp_features_len;
        size_t ll_addr_len;
        void *raw;
        ssize_t len;
        unsigned int i;
        int rc;

        /* Create DHCP packet */
        if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype, xid,
                                         dhcp_request_options_data,
                                         sizeof ( dhcp_request_options_data ),
                                         data, max_len ) ) != 0 ) {
                DBG ( "DHCP could not create DHCP packet: %s\n",
                      strerror ( rc ) );
                goto err_create_packet;
        }

        /* Set client IP address */
        dhcppkt->dhcphdr->ciaddr = ciaddr;

        /* Add options to identify the feature list */
        dhcp_features = table_start ( DHCP_FEATURES );
        dhcp_features_len = table_num_entries ( DHCP_FEATURES );
        if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features,
                                    dhcp_features_len ) ) != 0 ) {
                DBG ( "DHCP could not set features list option: %s\n",
                      strerror ( rc ) );
                goto err_store_features;
        }

        /* Add options to identify the network device */
        fetch_raw_setting ( netdev_settings ( netdev ), &busid_setting,
                            &dhcp_desc, sizeof ( dhcp_desc ) );
        if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc,
                                    sizeof ( dhcp_desc ) ) ) != 0 ) {
                DBG ( "DHCP could not set bus ID option: %s\n",
                      strerror ( rc ) );
                goto err_store_busid;
        }

        /* Add DHCP client identifier.  Required for Infiniband, and
         * doesn't hurt other link layers.
         */
        client_id.ll_proto = ntohs ( netdev->ll_protocol->ll_proto );
        ll_addr_len = netdev->ll_protocol->ll_addr_len;
        assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) );
        memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len );
        if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_ID, &client_id,
                                    ( ll_addr_len + 1 ) ) ) != 0 ) {
                DBG ( "DHCP could not set client ID: %s\n",
                      strerror ( rc ) );
                goto err_store_client_id;
        }

        /* Add client UUID, if we have one.  Required for PXE.  The
         * PXE spec does not specify a byte ordering for UUIDs, but
         * RFC4578 suggests that it follows the EFI spec, in which the
         * first three fields are little-endian.
         */
        client_uuid.type = DHCP_CLIENT_UUID_TYPE;
        if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting,
                                          &client_uuid.uuid ) ) >= 0 ) {
                uuid_mangle ( &client_uuid.uuid );
                if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID,
                                            &client_uuid,
                                            sizeof ( client_uuid ) ) ) != 0 ) {
                        DBG ( "DHCP could not set client UUID: %s\n",
                              strerror ( rc ) );
                        goto err_store_client_uuid;
                }
        }

        /* Add request settings, if applicable */
        for ( i = 0 ; i < ( sizeof ( dhcp_request_settings ) /
                            sizeof ( dhcp_request_settings[0] ) ) ; i++ ) {
                setting = dhcp_request_settings[i];
                if ( ( len = fetch_raw_setting_copy ( NULL, setting,
                                                      &raw ) ) >= 0 ) {
                        rc = dhcppkt_store ( dhcppkt, setting->tag, raw, len );
                        free ( raw );
                        if ( rc != 0 ) {
                                DBG ( "DHCP could not set %s: %s\n",
                                      setting->name, strerror ( rc ) );
                                goto err_store_raw;
                        }
                }
        }

 err_store_raw:
 err_store_client_uuid:
 err_store_client_id:
 err_store_busid:
 err_store_features:
 err_create_packet:
        return rc;
}
static int dhcp_deliver ( struct dhcp_session dhcp,
struct io_buffer iobuf,
struct xfer_metadata meta 
) [static]

Receive new data.

Parameters:
dhcpDHCP session
iobufI/O buffer
metaTransfer metadata
Return values:
rcReturn status code

Definition at line 1166 of file dhcp.c.

References dhcphdr::chaddr, io_buffer::data, DBGC, DHCP_MESSAGE_TYPE, dhcp_msgtype_name(), DHCP_SERVER_IDENTIFIER, dhcp_packet::dhcphdr, dhcppkt_fetch(), dhcppkt_init(), dhcppkt_put(), EINVAL, ENOMEM, free_iob(), inet_ntoa(), iob_len(), net_device::ll_addr, ll_protocol::ll_addr_len, net_device::ll_protocol, memcmp(), memcpy(), netdev, dhcp_session::netdev, ll_protocol::ntoa, ntohs, rc, dhcp_session_state::rx, in_addr::s_addr, dhcphdr::siaddr, sockaddr_in::sin_addr, sockaddr_in::sin_port, xfer_metadata::src, dhcp_session::state, dhcp_session::xid, dhcphdr::xid, and zalloc().

                                                       {
        struct net_device *netdev = dhcp->netdev;
        struct ll_protocol *ll_protocol = netdev->ll_protocol;
        struct sockaddr_in *peer;
        size_t data_len;
        struct dhcp_packet *dhcppkt;
        struct dhcphdr *dhcphdr;
        uint8_t msgtype = 0;
        struct in_addr server_id = { 0 };
        struct in_addr pseudo_id;
        int rc = 0;

        /* Sanity checks */
        if ( ! meta->src ) {
                DBGC ( dhcp, "DHCP %p received packet without source port\n",
                       dhcp );
                rc = -EINVAL;
                goto err_no_src;
        }
        peer = ( struct sockaddr_in * ) meta->src;

        /* Create a DHCP packet containing the I/O buffer contents.
         * Whilst we could just use the original buffer in situ, that
         * would waste the unused space in the packet buffer, and also
         * waste a relatively scarce fully-aligned I/O buffer.
         */
        data_len = iob_len ( iobuf );
        dhcppkt = zalloc ( sizeof ( *dhcppkt ) + data_len );
        if ( ! dhcppkt ) {
                rc = -ENOMEM;
                goto err_alloc_dhcppkt;
        }
        dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
        memcpy ( dhcphdr, iobuf->data, data_len );
        dhcppkt_init ( dhcppkt, dhcphdr, data_len );

        /* Identify message type */
        dhcppkt_fetch ( dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
                        sizeof ( msgtype ) );

        /* Identify server ID */
        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 "
                       "ID\n", dhcp, dhcp_msgtype_name ( msgtype ),
                       inet_ntoa ( peer->sin_addr ),
                       ntohs ( peer->sin_port ) );
                rc = -EINVAL;
                goto err_xid;
        };

        /* Check for matching client hardware address */
        if ( memcmp ( dhcphdr->chaddr, netdev->ll_addr,
                      ll_protocol->ll_addr_len ) != 0 ) {
                DBGC ( dhcp, "DHCP %p %s from %s:%d has bad chaddr %s\n",
                       dhcp, dhcp_msgtype_name ( msgtype ),
                       inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ),
                       ll_protocol->ntoa ( dhcphdr->chaddr ) );
                rc = -EINVAL;
                goto err_chaddr;
        }

        /* Handle packet based on current state */
        dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id, pseudo_id );

 err_chaddr:
 err_xid:
        dhcppkt_put ( dhcppkt );
 err_alloc_dhcppkt:
 err_no_src:
        free_iob ( iobuf );
        return rc;
}
static void dhcp_timer_expired ( struct retry_timer timer,
int  fail 
) [static]

Handle DHCP retry timer expiry.

Parameters:
timerDHCP retry timer
failFailure indicator

Definition at line 1267 of file dhcp.c.

References container_of, dhcp_session::count, dhcp_finished(), ETIMEDOUT, dhcp_session_state::expired, and dhcp_session::state.

Referenced by start_dhcp(), and start_pxebs().

                                                                       {
        struct dhcp_session *dhcp =
                container_of ( timer, struct dhcp_session, timer );

        /* If we have failed, terminate DHCP */
        if ( fail ) {
                dhcp_finished ( dhcp, -ETIMEDOUT );
                return;
        }

        /* Increment transmission counter */
        dhcp->count++;

        /* Handle timer expiry based on current state */
        dhcp->state->expired ( dhcp );
}
int start_dhcp ( struct interface job,
struct net_device netdev 
)

Start DHCP state machine on a network device.

Parameters:
jobJob control interface
netdevNetwork device
Return values:
rcReturn status code

Starts DHCP on the specified network device. If successful, the DHCPACK (and ProxyDHCPACK, if applicable) will be registered as option sources.

Definition at line 1327 of file dhcp.c.

References AF_INET, BOOTPC_PORT, dhcp_finished(), dhcp_free(), dhcp_last_xid, dhcp_set_state(), dhcp_timer_expired(), ENOMEM, htons, intf_init(), intf_plug_plug(), dhcp_session::job, dhcp_session::local, dhcp_session::netdev, netdev_get(), random(), rc, ref_init, ref_put, dhcp_session::refcnt, sockaddr_in::sin_family, sockaddr_in::sin_port, SOCK_DGRAM, dhcp_session::timer, dhcp_session::xfer, xfer_open_socket(), dhcp_session::xid, and zalloc().

                                                                    {
        struct dhcp_session *dhcp;
        int rc;

        /* Allocate and initialise structure */
        dhcp = zalloc ( sizeof ( *dhcp ) );
        if ( ! dhcp )
                return -ENOMEM;
        ref_init ( &dhcp->refcnt, dhcp_free );
        intf_init ( &dhcp->job, &dhcp_job_desc, &dhcp->refcnt );
        intf_init ( &dhcp->xfer, &dhcp_xfer_desc, &dhcp->refcnt );
        timer_init ( &dhcp->timer, dhcp_timer_expired, &dhcp->refcnt );
        dhcp->netdev = netdev_get ( netdev );
        dhcp->local.sin_family = AF_INET;
        dhcp->local.sin_port = htons ( BOOTPC_PORT );
        dhcp->xid = random();

        /* Store DHCP transaction ID for fakedhcp code */
        dhcp_last_xid = dhcp->xid;

        /* Instantiate child objects and attach to our interfaces */
        if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer,
                                  ( struct sockaddr * ) &dhcp->local ) ) != 0 )
                goto err;

        /* Enter DHCPDISCOVER state */
        dhcp_set_state ( dhcp, &dhcp_state_discover );

        /* Attach parent interface, mortalise self, and return */
        intf_plug_plug ( &dhcp->job, job );
        ref_put ( &dhcp->refcnt );
        return 0;

 err:
        dhcp_finished ( dhcp, rc );
        ref_put ( &dhcp->refcnt );
        return rc;
}
static void pxebs_list ( struct dhcp_session dhcp,
void *  raw,
size_t  raw_len,
struct in_addr ip 
) [static]

Retrieve list of PXE boot servers for a given server type.

Parameters:
dhcpDHCP session
rawDHCP PXE boot server list
raw_lenLength of DHCP PXE boot server list
ipIP address list to fill in

The caller must ensure that the IP address list has sufficient space.

Definition at line 1377 of file dhcp.c.

References DBGC, dhcp_pxe_boot_server::ip, dhcp_pxe_boot_server::num_ip, offsetof, dhcp_session::pxe_type, raw, and dhcp_pxe_boot_server::type.

Referenced by start_pxebs().

                                                              {
        struct dhcp_pxe_boot_server *server = raw;
        size_t server_len;
        unsigned int i;

        while ( raw_len ) {
                if ( raw_len < sizeof ( *server ) ) {
                        DBGC ( dhcp, "DHCP %p malformed PXE server list\n",
                               dhcp );
                        break;
                }
                server_len = offsetof ( typeof ( *server ),
                                        ip[ server->num_ip ] );
                if ( raw_len < server_len ) {
                        DBGC ( dhcp, "DHCP %p malformed PXE server list\n",
                               dhcp );
                        break;
                }
                if ( server->type == dhcp->pxe_type ) {
                        for ( i = 0 ; i < server->num_ip ; i++ )
                                *(ip++) = server->ip[i];
                }
                server = ( ( ( void * ) server ) + server_len );
                raw_len -= server_len;
        }
}
int start_pxebs ( struct interface job,
struct net_device netdev,
unsigned int  pxe_type 
)

Start PXE Boot Server Discovery on a network device.

Parameters:
jobJob control interface
netdevNetwork device
pxe_typePXE server type
Return values:
rcReturn status code

Starts PXE Boot Server Discovery on the specified network device. If successful, the Boot Server ACK will be registered as an option source.

Definition at line 1417 of file dhcp.c.

References AF_INET, BOOTPC_PORT, cpu_to_le16, DBGC, dhcp_finished(), dhcp_free(), DHCP_PXE_BOOT_SERVER_MCAST, DHCP_PXE_BOOT_SERVERS, DHCP_PXE_DISCOVERY_CONTROL, dhcp_set_state(), dhcp_timer_expired(), EINVAL, ENOMEM, fetch_ipv4_setting(), fetch_raw_setting(), fetch_uintz_setting(), htons, INADDR_BROADCAST, inet_ntoa(), intf_init(), intf_plug_plug(), ip, dhcp_session::job, dhcp_session::local, dhcp_session::netdev, netdev_get(), netdev_settings(), NULL, dhcp_session::pxe_accept, dhcp_session::pxe_attempt, dhcp_session::pxe_type, pxebs_list(), PXEBS_NO_BROADCAST, PXEBS_NO_MULTICAST, PXEBS_NO_UNKNOWN_SERVERS, rc, ref_init, ref_put, dhcp_session::refcnt, in_addr::s_addr, sockaddr_in::sin_addr, sockaddr_in::sin_family, sockaddr_in::sin_port, SOCK_DGRAM, setting::tag, dhcp_session::timer, dhcp_session::xfer, xfer_open_socket(), and zalloc().

Referenced by pxebs().

                                          {
        struct setting pxe_discovery_control_setting =
                { .tag = DHCP_PXE_DISCOVERY_CONTROL };
        struct setting pxe_boot_servers_setting =
                { .tag = DHCP_PXE_BOOT_SERVERS };
        struct setting pxe_boot_server_mcast_setting =
                { .tag = DHCP_PXE_BOOT_SERVER_MCAST };
        ssize_t pxebs_list_len;
        struct dhcp_session *dhcp;
        struct in_addr *ip;
        unsigned int pxe_discovery_control;
        int rc;

        /* Get upper bound for PXE boot server IP address list */
        pxebs_list_len = fetch_raw_setting ( NULL, &pxe_boot_servers_setting,
                                             NULL, 0 );
        if ( pxebs_list_len < 0 )
                pxebs_list_len = 0;

        /* Allocate and initialise structure */
        dhcp = zalloc ( sizeof ( *dhcp ) + sizeof ( *ip ) /* mcast */ +
                        sizeof ( *ip ) /* bcast */ + pxebs_list_len +
                        sizeof ( *ip ) /* terminator */ );
        if ( ! dhcp )
                return -ENOMEM;
        ref_init ( &dhcp->refcnt, dhcp_free );
        intf_init ( &dhcp->job, &dhcp_job_desc, &dhcp->refcnt );
        intf_init ( &dhcp->xfer, &dhcp_xfer_desc, &dhcp->refcnt );
        timer_init ( &dhcp->timer, dhcp_timer_expired, &dhcp->refcnt );
        dhcp->netdev = netdev_get ( netdev );
        dhcp->local.sin_family = AF_INET;
        fetch_ipv4_setting ( netdev_settings ( netdev ), &ip_setting,
                             &dhcp->local.sin_addr );
        dhcp->local.sin_port = htons ( BOOTPC_PORT );
        dhcp->pxe_type = cpu_to_le16 ( pxe_type );

        /* Construct PXE boot server IP address lists */
        pxe_discovery_control =
                fetch_uintz_setting ( NULL, &pxe_discovery_control_setting );
        ip = ( ( ( void * ) dhcp ) + sizeof ( *dhcp ) );
        dhcp->pxe_attempt = ip;
        if ( ! ( pxe_discovery_control & PXEBS_NO_MULTICAST ) ) {
                fetch_ipv4_setting ( NULL, &pxe_boot_server_mcast_setting, ip);
                if ( ip->s_addr )
                        ip++;
        }
        if ( ! ( pxe_discovery_control & PXEBS_NO_BROADCAST ) )
                (ip++)->s_addr = INADDR_BROADCAST;
        if ( pxe_discovery_control & PXEBS_NO_UNKNOWN_SERVERS )
                dhcp->pxe_accept = ip;
        if ( pxebs_list_len ) {
                uint8_t buf[pxebs_list_len];

                fetch_raw_setting ( NULL, &pxe_boot_servers_setting,
                                    buf, sizeof ( buf ) );
                pxebs_list ( dhcp, buf, sizeof ( buf ), ip );
        }
        if ( ! dhcp->pxe_attempt->s_addr ) {
                DBGC ( dhcp, "DHCP %p has no PXE boot servers for type %04x\n",
                       dhcp, pxe_type );
                rc = -EINVAL;
                goto err;
        }

        /* Dump out PXE server lists */
        DBGC ( dhcp, "DHCP %p attempting", dhcp );
        for ( ip = dhcp->pxe_attempt ; ip->s_addr ; ip++ )
                DBGC ( dhcp, " %s", inet_ntoa ( *ip ) );
        DBGC ( dhcp, "\n" );
        if ( dhcp->pxe_accept ) {
                DBGC ( dhcp, "DHCP %p accepting", dhcp );
                for ( ip = dhcp->pxe_accept ; ip->s_addr ; ip++ )
                        DBGC ( dhcp, " %s", inet_ntoa ( *ip ) );
                DBGC ( dhcp, "\n" );
        }

        /* Instantiate child objects and attach to our interfaces */
        if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer,
                                  ( struct sockaddr * ) &dhcp->local ) ) != 0 )
                goto err;

        /* Enter PXEBS state */
        dhcp_set_state ( dhcp, &dhcp_state_pxebs );

        /* Attach parent interface, mortalise self, and return */
        intf_plug_plug ( &dhcp->job, job );
        ref_put ( &dhcp->refcnt );
        return 0;

 err:
        dhcp_finished ( dhcp, rc );
        ref_put ( &dhcp->refcnt );
        return rc;
}

Variable Documentation

const uint8_t dhcp_op[] [static]
Initial value:

DHCP operation types.

This table maps from DHCP message types (i.e. values of the DHCP_MESSAGE_TYPE option) to values of the "op" field within a DHCP packet.

Definition at line 69 of file dhcp.c.

Referenced by dhcp_create_packet().

struct setting* dhcp_request_settings[] [static]
Initial value:
 {
        &user_class_setting,
        &vendor_class_setting,
}

Settings copied in to all DHCP requests.

Definition at line 103 of file dhcp.c.

Referenced by dhcp_create_request().

Most recent DHCP transaction ID.

This is exposed for use by the fakedhcp code when reconstructing DHCP packets for PXE NBPs.

Definition at line 123 of file dhcp.c.

Referenced by create_fakedhcpack(), create_fakedhcpdiscover(), create_fakepxebsack(), and start_dhcp().

static struct dhcp_session_state dhcp_state_discover [static]
Initial value:
 {
        .name                   = "discovery",
        .tx                     = dhcp_discovery_tx,
        .rx                     = dhcp_discovery_rx,
        .expired                = dhcp_discovery_expired,
        .tx_msgtype             = DHCPDISCOVER,
        .min_timeout_sec        = DHCP_DISC_START_TIMEOUT_SEC,
        .max_timeout_sec        = DHCP_DISC_END_TIMEOUT_SEC,
}

DHCP discovery state operations.

Definition at line 193 of file dhcp.c.

static struct dhcp_session_state dhcp_state_request [static]
Initial value:
 {
        .name                   = "request",
        .tx                     = dhcp_request_tx,
        .rx                     = dhcp_request_rx,
        .expired                = dhcp_request_expired,
        .tx_msgtype             = DHCPREQUEST,
        .min_timeout_sec        = DHCP_REQ_START_TIMEOUT_SEC,
        .max_timeout_sec        = DHCP_REQ_END_TIMEOUT_SEC,
}

DHCP request state operations.

Definition at line 194 of file dhcp.c.

Referenced by dhcp_discovery_expired(), and dhcp_discovery_rx().

static struct dhcp_session_state dhcp_state_proxy [static]
Initial value:
 {
        .name                   = "ProxyDHCP",
        .tx                     = dhcp_proxy_tx,
        .rx                     = dhcp_proxy_rx,
        .expired                = dhcp_proxy_expired,
        .tx_msgtype             = DHCPREQUEST,
        .min_timeout_sec        = DHCP_PROXY_START_TIMEOUT_SEC,
        .max_timeout_sec        = DHCP_PROXY_END_TIMEOUT_SEC,
}

ProxyDHCP request state operations.

Definition at line 195 of file dhcp.c.

Referenced by dhcp_request_rx().

static struct dhcp_session_state dhcp_state_pxebs [static]
Initial value:
 {
        .name                   = "PXEBS",
        .tx                     = dhcp_pxebs_tx,
        .rx                     = dhcp_pxebs_rx,
        .expired                = dhcp_pxebs_expired,
        .tx_msgtype             = DHCPREQUEST,
        .min_timeout_sec        = PXEBS_START_TIMEOUT_SEC,
        .max_timeout_sec        = PXEBS_END_TIMEOUT_SEC,
}

PXE Boot Server Discovery state operations.

Definition at line 196 of file dhcp.c.

Referenced by dhcp_pxebs_expired().

Initial value:

DHCP data transfer interface operations.

Definition at line 1253 of file dhcp.c.

Initial value:

DHCP data transfer interface descriptor.

Definition at line 1258 of file dhcp.c.

struct interface_operation dhcp_job_op[] [static]
Initial value:

DHCP job control interface operations.

Definition at line 1291 of file dhcp.c.

Initial value:

DHCP job control interface descriptor.

Definition at line 1296 of file dhcp.c.

struct sockaddr dhcp_peer [static]
Initial value:
 {
        .sa_family = AF_INET,
}

DHCP peer address for socket opening.

This is a dummy address; the only useful portion is the socket family (so that we get a UDP connection). The DHCP client will set the IP address and source port explicitly on each transmission.

Definition at line 1312 of file dhcp.c.

Initial value:
 {
        .name = "dhcp",
        .start = start_dhcp,
}

DHCP network device configurator.

Definition at line 1514 of file dhcp.c.