summaryrefslogtreecommitdiffstats
path: root/src/net/udp/dhcp.c
diff options
context:
space:
mode:
authorMichael Brown2009-01-21 04:43:26 +0100
committerMichael Brown2009-01-21 04:43:26 +0100
commit6941793416474f68f3dcbbf55258ac35de65026a (patch)
treedf2efeae175b95f2e0f2eb1218f271358c6c8ac6 /src/net/udp/dhcp.c
parent[tcpip] Allow for transmission to multicast IPv4 addresses (diff)
downloadipxe-6941793416474f68f3dcbbf55258ac35de65026a.tar.gz
ipxe-6941793416474f68f3dcbbf55258ac35de65026a.tar.xz
ipxe-6941793416474f68f3dcbbf55258ac35de65026a.zip
[dhcp] Add preliminary support for PXE Boot Servers
Some PXE configurations require us to perform a third DHCP transaction (in addition to the real DHCP transaction and the ProxyDHCP transaction) in order to retrieve information from a "Boot Server". This is an experimental implementation, since the actual behaviour is not well specified in the PXE spec.
Diffstat (limited to 'src/net/udp/dhcp.c')
-rw-r--r--src/net/udp/dhcp.c93
1 files changed, 92 insertions, 1 deletions
diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c
index dd3763b4..90987c77 100644
--- a/src/net/udp/dhcp.c
+++ b/src/net/udp/dhcp.c
@@ -287,6 +287,8 @@ enum dhcp_session_state {
DHCP_STATE_REQUEST,
/** Sending ProxyDHCPREQUESTs, waiting for ProxyDHCPACK */
DHCP_STATE_PROXYREQUEST,
+ /** Sending BootServerDHCPREQUESTs, waiting for BootServerDHCPACK */
+ DHCP_STATE_BSREQUEST,
};
/**
@@ -300,6 +302,7 @@ static inline const char * dhcp_state_name ( enum dhcp_session_state state ) {
case DHCP_STATE_DISCOVER: return "DHCPDISCOVER";
case DHCP_STATE_REQUEST: return "DHCPREQUEST";
case DHCP_STATE_PROXYREQUEST: return "ProxyDHCPREQUEST";
+ case DHCP_STATE_BSREQUEST: return "BootServerREQUEST";
default: return "<invalid>";
}
}
@@ -326,6 +329,12 @@ struct dhcp_session {
struct dhcp_settings *dhcpoffer;
/** ProxyDHCPOFFER obtained during DHCPDISCOVER */
struct dhcp_settings *proxydhcpoffer;
+ /** DHCPACK obtained during DHCPREQUEST */
+ struct dhcp_settings *dhcpack;
+ /** ProxyDHCPACK obtained during ProxyDHCPREQUEST */
+ struct dhcp_settings *proxydhcpack;
+ /** BootServerDHCPACK obtained during BootServerDHCPREQUEST */
+ struct dhcp_settings *bsdhcpack;
/** Retransmission timer */
struct retry_timer timer;
/** Start time of the current state (in ticks) */
@@ -344,6 +353,9 @@ static void dhcp_free ( struct refcnt *refcnt ) {
netdev_put ( dhcp->netdev );
dhcpset_put ( dhcp->dhcpoffer );
dhcpset_put ( dhcp->proxydhcpoffer );
+ dhcpset_put ( dhcp->dhcpack );
+ dhcpset_put ( dhcp->proxydhcpack );
+ dhcpset_put ( dhcp->bsdhcpack );
free ( dhcp );
}
@@ -555,6 +567,10 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
.sin_family = AF_INET,
.sin_port = htons ( PROXYDHCP_PORT ),
};
+ static struct sockaddr_in client = {
+ .sin_family = AF_INET,
+ .sin_port = htons ( BOOTPC_PORT ),
+ };
struct xfer_metadata meta = {
.netdev = dhcp->netdev,
};
@@ -584,6 +600,7 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
DBGC ( dhcp, "DHCP %p transmitting ProxyDHCPREQUEST\n", dhcp );
assert ( dhcp->dhcpoffer );
assert ( dhcp->proxydhcpoffer );
+ assert ( dhcp->dhcpack );
offer = &dhcp->proxydhcpoffer->dhcppkt;
ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
check_len = dhcppkt_fetch ( offer, DHCP_SERVER_IDENTIFIER,
@@ -594,6 +611,24 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
assert ( proxydhcp_server.sin_addr.s_addr != 0 );
assert ( check_len == sizeof ( proxydhcp_server.sin_addr ) );
break;
+ case DHCP_STATE_BSREQUEST:
+ DBGC ( dhcp, "DHCP %p transmitting BootServerREQUEST\n",
+ dhcp );
+ assert ( dhcp->dhcpoffer );
+ assert ( dhcp->proxydhcpoffer );
+ assert ( dhcp->dhcpack );
+ assert ( dhcp->proxydhcpack );
+ offer = &dhcp->proxydhcpoffer->dhcppkt;
+ ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
+ check_len = dhcppkt_fetch ( &dhcp->proxydhcpack->dhcppkt,
+ DHCP_PXE_BOOT_SERVER_MCAST,
+ &proxydhcp_server.sin_addr,
+ sizeof(proxydhcp_server.sin_addr));
+ meta.dest = ( struct sockaddr * ) &proxydhcp_server;
+ assert ( ciaddr.s_addr != 0 );
+ assert ( proxydhcp_server.sin_addr.s_addr != 0 );
+ assert ( check_len == sizeof ( proxydhcp_server.sin_addr ) );
+ break;
default:
assert ( 0 );
break;
@@ -613,6 +648,12 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
goto done;
}
+ /* Explicitly specify source address, if available. */
+ if ( ciaddr.s_addr ) {
+ client.sin_addr = ciaddr;
+ meta.src = ( struct sockaddr * ) &client;
+ }
+
/* Transmit the packet */
iob_put ( iobuf, dhcppkt.len );
rc = xfer_deliver_iob_meta ( &dhcp->xfer, iobuf, &meta );
@@ -650,6 +691,7 @@ static void dhcp_set_state ( struct dhcp_session *dhcp,
* @v dhcp DHCP session
*/
static void dhcp_next_state ( struct dhcp_session *dhcp ) {
+ struct in_addr bs_mcast = { 0 };
switch ( dhcp->state ) {
case DHCP_STATE_DISCOVER:
@@ -662,6 +704,17 @@ static void dhcp_next_state ( struct dhcp_session *dhcp ) {
}
/* Fall through */
case DHCP_STATE_PROXYREQUEST:
+ if ( dhcp->proxydhcpack ) {
+ dhcppkt_fetch ( &dhcp->proxydhcpack->dhcppkt,
+ DHCP_PXE_BOOT_SERVER_MCAST,
+ &bs_mcast, sizeof ( bs_mcast ) );
+ if ( bs_mcast.s_addr ) {
+ dhcp_set_state ( dhcp, DHCP_STATE_BSREQUEST );
+ break;
+ }
+ }
+ /* Fall through */
+ case DHCP_STATE_BSREQUEST:
dhcp_finished ( dhcp, 0 );
break;
default:
@@ -845,9 +898,13 @@ static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp,
return;
}
+ /* Record DHCPACK */
+ assert ( dhcp->dhcpack == NULL );
+ dhcp->dhcpack = dhcpset_get ( dhcpack );
+
/* Register settings */
parent = netdev_settings ( dhcp->netdev );
- if ( ( rc = dhcp_store_dhcpack ( dhcp, dhcpack, parent ) ) !=0 )
+ if ( ( rc = dhcp_store_dhcpack ( dhcp, dhcpack, parent ) ) != 0 )
return;
/* Transition to next state */
@@ -885,6 +942,10 @@ static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp,
/* Rename settings */
proxydhcpack->settings.name = PROXYDHCP_SETTINGS_NAME;
+ /* Record ProxyDHCPACK */
+ assert ( dhcp->proxydhcpack == NULL );
+ dhcp->proxydhcpack = dhcpset_get ( proxydhcpack );
+
/* Register settings */
if ( ( rc = dhcp_store_dhcpack ( dhcp, proxydhcpack, NULL ) ) != 0 )
return;
@@ -894,6 +955,31 @@ static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp,
}
/**
+ * Handle received BootServerDHCPACK
+ *
+ * @v dhcp DHCP session
+ * @v bsdhcpack Received BootServerDHCPACK
+ */
+static void dhcp_rx_bsdhcpack ( struct dhcp_session *dhcp,
+ struct dhcp_settings *bsdhcpack ) {
+ int rc;
+
+ /* Rename settings */
+ bsdhcpack->settings.name = BSDHCP_SETTINGS_NAME;
+
+ /* Record ProxyDHCPACK */
+ assert ( dhcp->bsdhcpack == NULL );
+ dhcp->bsdhcpack = dhcpset_get ( bsdhcpack );
+
+ /* Register settings */
+ if ( ( rc = dhcp_store_dhcpack ( dhcp, bsdhcpack, NULL ) ) != 0 )
+ return;
+
+ /* Transition to next state */
+ dhcp_next_state ( dhcp );
+}
+
+/**
* Receive new data
*
* @v xfer Data transfer interface
@@ -969,6 +1055,11 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
( src_port == htons ( PROXYDHCP_PORT ) ) )
dhcp_rx_proxydhcpack ( dhcp, dhcpset );
break;
+ case DHCP_STATE_BSREQUEST:
+ if ( ( msgtype == DHCPACK ) &&
+ ( src_port == htons ( PROXYDHCP_PORT ) ) )
+ dhcp_rx_bsdhcpack ( dhcp, dhcpset );
+ break;
default:
assert ( 0 );
break;