summaryrefslogtreecommitdiffstats
path: root/src/net/udp/dhcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/udp/dhcp.c')
-rw-r--r--src/net/udp/dhcp.c303
1 files changed, 122 insertions, 181 deletions
diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c
index 8789f925..98b8af0b 100644
--- a/src/net/udp/dhcp.c
+++ b/src/net/udp/dhcp.c
@@ -130,9 +130,6 @@ static uint32_t dhcp_xid ( struct net_device *netdev ) {
return xid;
}
-/** Settings block name used for ProxyDHCP responses */
-#define PROXYDHCP_SETTINGS_NAME "proxydhcp"
-
/**
* Create a DHCP packet
*
@@ -180,12 +177,12 @@ int create_dhcp_packet ( struct dhcp_packet *dhcppkt,
memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen );
memcpy ( dhcphdr->options, options->data, options_len );
- /* Initialise DHCP packet structure and settings interface */
+ /* Initialise DHCP packet structure */
memset ( dhcppkt, 0, sizeof ( *dhcppkt ) );
- dhcppkt_init ( dhcppkt, NULL, data, max_len );
+ dhcppkt_init ( dhcppkt, data, max_len );
/* Set DHCP_MESSAGE_TYPE option */
- if ( ( rc = store_setting ( &dhcppkt->settings, DHCP_MESSAGE_TYPE,
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE,
&msgtype, sizeof ( msgtype ) ) ) != 0 )
return rc;
@@ -230,10 +227,10 @@ struct dhcp_client_uuid {
* @v max_len Size of DHCP packet buffer
* @ret rc Return status code
*/
-static int create_dhcp_request ( struct dhcp_packet *dhcppkt,
- struct net_device *netdev,
- struct dhcp_packet *dhcpoffer,
- void *data, size_t max_len ) {
+int create_dhcp_request ( struct dhcp_packet *dhcppkt,
+ struct net_device *netdev,
+ struct dhcp_packet *dhcpoffer,
+ void *data, size_t max_len ) {
struct device_description *desc = &netdev->dev->desc;
struct dhcp_netdev_desc dhcp_desc;
struct dhcp_client_id client_id;
@@ -258,27 +255,26 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt,
struct in_addr server_id;
struct in_addr requested_ip;
- if ( ( rc = fetch_ipv4_setting ( &dhcpoffer->settings,
- DHCP_SERVER_IDENTIFIER,
- &server_id ) ) < 0 ) {
+ if ( dhcppkt_fetch ( dhcpoffer, DHCP_SERVER_IDENTIFIER,
+ &server_id, sizeof ( server_id ) )
+ != sizeof ( server_id ) ) {
DBG ( "DHCP offer missing server identifier\n" );
return -EINVAL;
}
- if ( ( rc = fetch_ipv4_setting ( &dhcpoffer->settings,
- DHCP_EB_YIADDR,
- &requested_ip ) ) < 0 ) {
+ if ( dhcppkt_fetch ( dhcpoffer, DHCP_EB_YIADDR,
+ &requested_ip, sizeof ( requested_ip ) )
+ != sizeof ( requested_ip ) ) {
DBG ( "DHCP offer missing IP address\n" );
return -EINVAL;
}
- if ( ( rc = store_setting ( &dhcppkt->settings,
- DHCP_SERVER_IDENTIFIER, &server_id,
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
+ &server_id,
sizeof ( server_id ) ) ) != 0 ) {
DBG ( "DHCP could not set server identifier: %s\n ",
strerror ( rc ) );
return rc;
}
- if ( ( rc = store_setting ( &dhcppkt->settings,
- DHCP_REQUESTED_ADDRESS,
+ if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
&requested_ip,
sizeof ( requested_ip ) ) ) != 0 ){
DBG ( "DHCP could not set requested address: %s\n",
@@ -289,8 +285,8 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt,
/* Add options to identify the feature list */
dhcp_features_len = ( dhcp_features_end - dhcp_features );
- if ( ( rc = store_setting ( &dhcppkt->settings, DHCP_EB_ENCAP,
- dhcp_features, dhcp_features_len ) ) !=0 ){
+ 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 ) );
return rc;
@@ -300,8 +296,8 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt,
dhcp_desc.type = desc->bus_type;
dhcp_desc.vendor = htons ( desc->vendor );
dhcp_desc.device = htons ( desc->device );
- if ( ( rc = store_setting ( &dhcppkt->settings, DHCP_EB_BUS_ID,
- &dhcp_desc, sizeof ( dhcp_desc ) ) ) !=0 ){
+ 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 ) );
return rc;
@@ -314,8 +310,8 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt,
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 = store_setting ( &dhcppkt->settings, DHCP_CLIENT_ID,
- &client_id, ( ll_addr_len + 1 ) ) ) != 0 ){
+ 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 ) );
return rc;
@@ -324,8 +320,8 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt,
/* Add client UUID, if we have one. Required for PXE. */
client_uuid.type = DHCP_CLIENT_UUID_TYPE;
if ( ( rc = get_uuid ( &client_uuid.uuid ) ) == 0 ) {
- if ( ( rc = store_setting ( &dhcppkt->settings,
- DHCP_CLIENT_UUID, &client_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 ) );
@@ -336,169 +332,109 @@ static int create_dhcp_request ( struct dhcp_packet *dhcppkt,
return 0;
}
-/**
- * Create DHCPDISCOVER packet
+/****************************************************************************
*
- * @v netdev Network device
- * @v data Buffer for DHCP packet
- * @v max_len Size of DHCP packet buffer
- * @ret rc Return status code
+ * DHCP settings
*
- * Used by external code.
*/
-int create_dhcpdiscover ( struct net_device *netdev,
- void *data, size_t max_len ) {
- struct dhcp_packet dhcppkt;
- int rc;
- if ( ( rc = create_dhcp_request ( &dhcppkt, netdev, NULL, data,
- max_len ) ) != 0 ) {
- DBG ( "Could not create DHCPDISCOVER: %s\n",
- strerror ( rc ) );
- return rc;
- }
-
- return 0;
-}
+/** A DHCP settings block */
+struct dhcp_settings {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** Containing I/O buffer */
+ struct io_buffer *iobuf;
+ /** DHCP packet */
+ struct dhcp_packet dhcppkt;
+ /** Setting interface */
+ struct settings settings;
+};
/**
- * Create DHCPACK packet
+ * Free DHCP settings block
*
- * @v netdev Network device
- * @v data Buffer for DHCP packet
- * @v max_len Size of DHCP packet buffer
- * @ret rc Return status code
- *
- * Used by external code.
+ * @v refcnt Reference counter
*/
-int create_dhcpack ( struct net_device *netdev,
- void *data, size_t max_len ) {
- struct dhcp_packet dhcppkt;
- int rc;
-
- /* Create base DHCPACK packet */
- if ( ( rc = create_dhcp_packet ( &dhcppkt, netdev, DHCPACK, NULL,
- data, max_len ) ) != 0 ) {
- DBG ( "Could not create DHCPACK: %s\n", strerror ( rc ) );
- return rc;
- }
-
- /* Merge in globally-scoped settings, then netdev-specific
- * settings. Do it in this order so that netdev-specific
- * settings take precedence regardless of stated priorities.
- */
- if ( ( rc = copy_settings ( &dhcppkt.settings, NULL ) ) != 0 ) {
- DBG ( "Could not set DHCPACK global settings: %s\n",
- strerror ( rc ) );
- return rc;
- }
- if ( ( rc = copy_settings ( &dhcppkt.settings,
- netdev_settings ( netdev ) ) ) != 0 ) {
- DBG ( "Could not set DHCPACK netdev settings: %s\n",
- strerror ( rc ) );
- return rc;
- }
+static void dhcpset_free ( struct refcnt *refcnt ) {
+ struct dhcp_settings *dhcpset =
+ container_of ( refcnt, struct dhcp_settings, refcnt );
- return 0;
+ free_iob ( dhcpset->iobuf );
+ free ( dhcpset );
}
/**
- * Create ProxyDHCPACK packet
- *
- * @v netdev Network device
- * @v data Buffer for DHCP packet
- * @v max_len Size of DHCP packet buffer
- * @ret rc Return status code
+ * Decrement reference count on DHCP settings block
*
- * Used by external code.
+ * @v dhcpset DHCP settings block
*/
-int create_proxydhcpack ( struct net_device *netdev,
- void *data, size_t max_len ) {
- struct dhcp_packet dhcppkt;
- struct settings *settings;
- int rc;
-
- /* Identify ProxyDHCP settings */
- settings = find_settings ( PROXYDHCP_SETTINGS_NAME );
-
- /* No ProxyDHCP settings => return empty block */
- if ( ! settings ) {
- memset ( data, 0, max_len );
- return 0;
- }
-
- /* Create base DHCPACK packet */
- if ( ( rc = create_dhcp_packet ( &dhcppkt, netdev, DHCPACK, NULL,
- data, max_len ) ) != 0 ) {
- DBG ( "Could not create ProxyDHCPACK: %s\n",
- strerror ( rc ) );
- return rc;
- }
-
- /* Merge in ProxyDHCP options */
- if ( ( rc = copy_settings ( &dhcppkt.settings, settings ) ) != 0 ) {
- DBG ( "Could not set ProxyDHCPACK settings: %s\n",
- strerror ( rc ) );
- return rc;
- }
-
- return 0;
+static inline void dhcpset_put ( struct dhcp_settings *dhcpset ) {
+ ref_put ( &dhcpset->refcnt );
}
-/****************************************************************************
- *
- * DHCP packets contained in I/O buffers
+/**
+ * Store value of DHCP setting
*
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v data Setting data, or NULL to clear setting
+ * @v len Length of setting data
+ * @ret rc Return status code
*/
+static int dhcpset_store ( struct settings *settings, struct setting *setting,
+ const void *data, size_t len ) {
+ struct dhcp_settings *dhcpset =
+ container_of ( settings, struct dhcp_settings, settings );
-/** A DHCP packet contained in an I/O buffer */
-struct dhcp_iobuf_packet {
- /** DHCP packet */
- struct dhcp_packet dhcppkt;
- /** Reference counter */
- struct refcnt refcnt;
- /** Containing I/O buffer */
- struct io_buffer *iobuf;
-};
+ return dhcppkt_store ( &dhcpset->dhcppkt, setting->tag, data, len );
+}
/**
- * Free DHCP packet contained in an I/O buffer
+ * Fetch value of setting
*
- * @v refcnt Reference counter
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
*/
-static void dhcpiob_free ( struct refcnt *refcnt ) {
- struct dhcp_iobuf_packet *dhcpiob =
- container_of ( refcnt, struct dhcp_iobuf_packet, refcnt );
+static int dhcpset_fetch ( struct settings *settings, struct setting *setting,
+ void *data, size_t len ) {
+ struct dhcp_settings *dhcpset =
+ container_of ( settings, struct dhcp_settings, settings );
- free_iob ( dhcpiob->iobuf );
- free ( dhcpiob );
+ return dhcppkt_fetch ( &dhcpset->dhcppkt, setting->tag, data, len );
}
+/** DHCP settings operations */
+static struct settings_operations dhcpset_settings_operations = {
+ .store = dhcpset_store,
+ .fetch = dhcpset_fetch,
+};
+
/**
- * Create DHCP packet from I/O buffer
+ * Create DHCP setting block from I/O buffer
*
* @v iobuf I/O buffer
- * @ret dhcpiob DHCP packet contained in I/O buffer
+ * @ret dhcpset DHCP settings block
*
* This function takes ownership of the I/O buffer. Future accesses
- * must be via the @c dhcpiob data structure.
+ * must be via the @c dhcpset data structure.
*/
-static struct dhcp_iobuf_packet * dhcpiob_create ( struct io_buffer *iobuf ) {
- struct dhcp_iobuf_packet *dhcpiob;
-
- dhcpiob = zalloc ( sizeof ( *dhcpiob ) );
- if ( dhcpiob ) {
- dhcpiob->refcnt.free = dhcpiob_free;
- dhcpiob->iobuf = iobuf;
- dhcppkt_init ( &dhcpiob->dhcppkt, &dhcpiob->refcnt,
+static struct dhcp_settings * dhcpset_create_iob ( struct io_buffer *iobuf ) {
+ struct dhcp_settings *dhcpset;
+
+ dhcpset = zalloc ( sizeof ( *dhcpset ) );
+ if ( dhcpset ) {
+ dhcpset->refcnt.free = dhcpset_free;
+ dhcpset->iobuf = iobuf;
+ dhcppkt_init ( &dhcpset->dhcppkt,
iobuf->data, iob_len ( iobuf ) );
+ settings_init ( &dhcpset->settings,
+ &dhcpset_settings_operations, &dhcpset->refcnt,
+ DHCP_SETTINGS_NAME );
}
- return dhcpiob;
-}
-
-static void dhcpiob_put ( struct dhcp_iobuf_packet *dhcpiob ) {
- if ( dhcpiob )
- ref_put ( &dhcpiob->refcnt );
+ return dhcpset;
}
/****************************************************************************
@@ -526,9 +462,9 @@ struct dhcp_session {
*/
int state;
/** Response obtained from DHCP server */
- struct dhcp_iobuf_packet *response;
+ struct dhcp_settings *response;
/** Response obtained from ProxyDHCP server */
- struct dhcp_iobuf_packet *proxy_response;
+ struct dhcp_settings *proxy_response;
/** Retransmission timer */
struct retry_timer timer;
/** Session start time (in ticks) */
@@ -545,8 +481,8 @@ static void dhcp_free ( struct refcnt *refcnt ) {
container_of ( refcnt, struct dhcp_session, refcnt );
netdev_put ( dhcp->netdev );
- dhcpiob_put ( dhcp->response );
- dhcpiob_put ( dhcp->proxy_response );
+ dhcpset_put ( dhcp->response );
+ dhcpset_put ( dhcp->proxy_response );
free ( dhcp );
}
@@ -584,7 +520,7 @@ static int dhcp_register_settings ( struct dhcp_session *dhcp ) {
/* Register ProxyDHCP settings, if present */
if ( dhcp->proxy_response ) {
- settings = &dhcp->proxy_response->dhcppkt.settings;
+ settings = &dhcp->proxy_response->settings;
settings->name = PROXYDHCP_SETTINGS_NAME;
old_settings = find_settings ( settings->name );
if ( old_settings )
@@ -595,7 +531,7 @@ static int dhcp_register_settings ( struct dhcp_session *dhcp ) {
/* Register DHCP settings */
parent = netdev_settings ( dhcp->netdev );
- settings = &dhcp->response->dhcppkt.settings;
+ settings = &dhcp->response->settings;
old_settings = find_child_settings ( parent, settings->name );
if ( old_settings )
unregister_settings ( old_settings );
@@ -701,24 +637,24 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
struct xfer_metadata *meta __unused ) {
struct dhcp_session *dhcp =
container_of ( xfer, struct dhcp_session, xfer );
- struct dhcp_iobuf_packet *response;
- struct dhcp_iobuf_packet **store_response;
+ struct dhcp_settings *response;
+ struct dhcp_settings **store_response;
struct dhcphdr *dhcphdr;
- struct settings *settings;
- unsigned int msgtype;
+ uint8_t msgtype = 0;
+ uint8_t priority = 0;
+ uint8_t existing_priority = 0;
unsigned long elapsed;
int is_proxy;
- int ignore_proxy;
+ uint8_t ignore_proxy = 0;
int rc;
/* Convert packet into a DHCP-packet-in-iobuf */
- response = dhcpiob_create ( iobuf );
+ response = dhcpset_create_iob ( iobuf );
if ( ! response ) {
DBGC ( dhcp, "DHCP %p could not store DHCP packet\n", dhcp );
return -ENOMEM;
}
dhcphdr = response->dhcppkt.dhcphdr;
- settings = &response->dhcppkt.settings;
/* Check for matching transaction ID */
if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
@@ -730,7 +666,8 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
/* Determine and verify message type */
is_proxy = ( dhcphdr->yiaddr.s_addr == 0 );
- msgtype = fetch_uintz_setting ( settings, DHCP_MESSAGE_TYPE );
+ dhcppkt_fetch ( &response->dhcppkt, DHCP_MESSAGE_TYPE, &msgtype,
+ sizeof ( msgtype ) );
DBGC ( dhcp, "DHCP %p received %s%s\n", dhcp,
( is_proxy ? "Proxy" : "" ), dhcp_msgtype_name ( msgtype ) );
if ( ( ( dhcp->state != DHCPDISCOVER ) || ( msgtype != DHCPOFFER ) ) &&
@@ -746,14 +683,18 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
* currently-stored options.
*/
store_response = ( is_proxy ? &dhcp->proxy_response : &dhcp->response);
- if ( ( ! *store_response ) ||
- ( fetch_uintz_setting ( settings, DHCP_EB_PRIORITY ) >=
- fetch_uintz_setting ( &(*store_response)->dhcppkt.settings,
- DHCP_EB_PRIORITY ) ) ) {
- dhcpiob_put ( *store_response );
+ if ( *store_response ) {
+ dhcppkt_fetch ( &(*store_response)->dhcppkt, DHCP_EB_PRIORITY,
+ &existing_priority,
+ sizeof ( existing_priority ) );
+ }
+ dhcppkt_fetch ( &response->dhcppkt, DHCP_EB_PRIORITY, &priority,
+ sizeof ( priority ) );
+ if ( priority >= existing_priority ) {
+ dhcpset_put ( *store_response );
*store_response = response;
} else {
- dhcpiob_put ( response );
+ dhcpset_put ( response );
}
/* If we don't yet have a standard DHCP response (i.e. one
@@ -763,8 +704,8 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
goto out;
/* Handle DHCP response */
- ignore_proxy = fetch_uintz_setting ( &dhcp->response->dhcppkt.settings,
- DHCP_EB_NO_PROXYDHCP );
+ dhcppkt_fetch ( &dhcp->response->dhcppkt, DHCP_EB_NO_PROXYDHCP,
+ &ignore_proxy, sizeof ( ignore_proxy ) );
switch ( dhcp->state ) {
case DHCPDISCOVER:
/* If we have allowed sufficient time for ProxyDHCP
@@ -780,7 +721,7 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
case DHCPREQUEST:
/* DHCP finished; register options and exit */
if ( ignore_proxy && dhcp->proxy_response ) {
- dhcpiob_put ( dhcp->proxy_response );
+ dhcpset_put ( dhcp->proxy_response );
dhcp->proxy_response = NULL;
}
if ( ( rc = dhcp_register_settings ( dhcp ) ) != 0 ) {
@@ -797,7 +738,7 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
return 0;
out_discard:
- dhcpiob_put ( response );
+ dhcpset_put ( response );
return 0;
}