[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.
This commit is contained in:
parent
d230b53df2
commit
6941793416
|
@ -81,6 +81,9 @@ struct dhcp_packet;
|
||||||
/** Vendor encapsulated options */
|
/** Vendor encapsulated options */
|
||||||
#define DHCP_VENDOR_ENCAP 43
|
#define DHCP_VENDOR_ENCAP 43
|
||||||
|
|
||||||
|
/** PXE boot server multicast address */
|
||||||
|
#define DHCP_PXE_BOOT_SERVER_MCAST DHCP_ENCAP_OPT ( DHCP_VENDOR_ENCAP, 7 )
|
||||||
|
|
||||||
/** Requested IP address */
|
/** Requested IP address */
|
||||||
#define DHCP_REQUESTED_ADDRESS 50
|
#define DHCP_REQUESTED_ADDRESS 50
|
||||||
|
|
||||||
|
@ -480,6 +483,9 @@ struct dhcphdr {
|
||||||
/** Settings block name used for ProxyDHCP responses */
|
/** Settings block name used for ProxyDHCP responses */
|
||||||
#define PROXYDHCP_SETTINGS_NAME "proxydhcp"
|
#define PROXYDHCP_SETTINGS_NAME "proxydhcp"
|
||||||
|
|
||||||
|
/** Setting block name used for BootServerDHCP responses */
|
||||||
|
#define BSDHCP_SETTINGS_NAME "bs"
|
||||||
|
|
||||||
extern int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
|
extern int dhcp_create_packet ( struct dhcp_packet *dhcppkt,
|
||||||
struct net_device *netdev, uint8_t msgtype,
|
struct net_device *netdev, uint8_t msgtype,
|
||||||
struct dhcp_options *options,
|
struct dhcp_options *options,
|
||||||
|
|
|
@ -176,6 +176,7 @@ int create_fakeproxydhcpack ( struct net_device *netdev,
|
||||||
void *data, size_t max_len ) {
|
void *data, size_t max_len ) {
|
||||||
struct dhcp_packet dhcppkt;
|
struct dhcp_packet dhcppkt;
|
||||||
struct settings *settings;
|
struct settings *settings;
|
||||||
|
struct settings *bs_settings;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Identify ProxyDHCP settings */
|
/* Identify ProxyDHCP settings */
|
||||||
|
@ -200,5 +201,15 @@ int create_fakeproxydhcpack ( struct net_device *netdev,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Merge in BootServerDHCP options, if present */
|
||||||
|
bs_settings = find_settings ( BSDHCP_SETTINGS_NAME );
|
||||||
|
if ( bs_settings ) {
|
||||||
|
if ( ( rc = copy_settings ( &dhcppkt, bs_settings ) ) != 0 ) {
|
||||||
|
DBG ( "Could not set BootServerDHCPACK settings: "
|
||||||
|
"%s\n", strerror ( rc ) );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,6 +287,8 @@ enum dhcp_session_state {
|
||||||
DHCP_STATE_REQUEST,
|
DHCP_STATE_REQUEST,
|
||||||
/** Sending ProxyDHCPREQUESTs, waiting for ProxyDHCPACK */
|
/** Sending ProxyDHCPREQUESTs, waiting for ProxyDHCPACK */
|
||||||
DHCP_STATE_PROXYREQUEST,
|
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_DISCOVER: return "DHCPDISCOVER";
|
||||||
case DHCP_STATE_REQUEST: return "DHCPREQUEST";
|
case DHCP_STATE_REQUEST: return "DHCPREQUEST";
|
||||||
case DHCP_STATE_PROXYREQUEST: return "ProxyDHCPREQUEST";
|
case DHCP_STATE_PROXYREQUEST: return "ProxyDHCPREQUEST";
|
||||||
|
case DHCP_STATE_BSREQUEST: return "BootServerREQUEST";
|
||||||
default: return "<invalid>";
|
default: return "<invalid>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,6 +329,12 @@ struct dhcp_session {
|
||||||
struct dhcp_settings *dhcpoffer;
|
struct dhcp_settings *dhcpoffer;
|
||||||
/** ProxyDHCPOFFER obtained during DHCPDISCOVER */
|
/** ProxyDHCPOFFER obtained during DHCPDISCOVER */
|
||||||
struct dhcp_settings *proxydhcpoffer;
|
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 */
|
/** Retransmission timer */
|
||||||
struct retry_timer timer;
|
struct retry_timer timer;
|
||||||
/** Start time of the current state (in ticks) */
|
/** Start time of the current state (in ticks) */
|
||||||
|
@ -344,6 +353,9 @@ static void dhcp_free ( struct refcnt *refcnt ) {
|
||||||
netdev_put ( dhcp->netdev );
|
netdev_put ( dhcp->netdev );
|
||||||
dhcpset_put ( dhcp->dhcpoffer );
|
dhcpset_put ( dhcp->dhcpoffer );
|
||||||
dhcpset_put ( dhcp->proxydhcpoffer );
|
dhcpset_put ( dhcp->proxydhcpoffer );
|
||||||
|
dhcpset_put ( dhcp->dhcpack );
|
||||||
|
dhcpset_put ( dhcp->proxydhcpack );
|
||||||
|
dhcpset_put ( dhcp->bsdhcpack );
|
||||||
free ( dhcp );
|
free ( dhcp );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,6 +567,10 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
|
||||||
.sin_family = AF_INET,
|
.sin_family = AF_INET,
|
||||||
.sin_port = htons ( PROXYDHCP_PORT ),
|
.sin_port = htons ( PROXYDHCP_PORT ),
|
||||||
};
|
};
|
||||||
|
static struct sockaddr_in client = {
|
||||||
|
.sin_family = AF_INET,
|
||||||
|
.sin_port = htons ( BOOTPC_PORT ),
|
||||||
|
};
|
||||||
struct xfer_metadata meta = {
|
struct xfer_metadata meta = {
|
||||||
.netdev = dhcp->netdev,
|
.netdev = dhcp->netdev,
|
||||||
};
|
};
|
||||||
|
@ -584,6 +600,7 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
|
||||||
DBGC ( dhcp, "DHCP %p transmitting ProxyDHCPREQUEST\n", dhcp );
|
DBGC ( dhcp, "DHCP %p transmitting ProxyDHCPREQUEST\n", dhcp );
|
||||||
assert ( dhcp->dhcpoffer );
|
assert ( dhcp->dhcpoffer );
|
||||||
assert ( dhcp->proxydhcpoffer );
|
assert ( dhcp->proxydhcpoffer );
|
||||||
|
assert ( dhcp->dhcpack );
|
||||||
offer = &dhcp->proxydhcpoffer->dhcppkt;
|
offer = &dhcp->proxydhcpoffer->dhcppkt;
|
||||||
ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
|
ciaddr = dhcp->dhcpoffer->dhcppkt.dhcphdr->yiaddr;
|
||||||
check_len = dhcppkt_fetch ( offer, DHCP_SERVER_IDENTIFIER,
|
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 ( proxydhcp_server.sin_addr.s_addr != 0 );
|
||||||
assert ( check_len == sizeof ( proxydhcp_server.sin_addr ) );
|
assert ( check_len == sizeof ( proxydhcp_server.sin_addr ) );
|
||||||
break;
|
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:
|
default:
|
||||||
assert ( 0 );
|
assert ( 0 );
|
||||||
break;
|
break;
|
||||||
|
@ -613,6 +648,12 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) {
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Explicitly specify source address, if available. */
|
||||||
|
if ( ciaddr.s_addr ) {
|
||||||
|
client.sin_addr = ciaddr;
|
||||||
|
meta.src = ( struct sockaddr * ) &client;
|
||||||
|
}
|
||||||
|
|
||||||
/* Transmit the packet */
|
/* Transmit the packet */
|
||||||
iob_put ( iobuf, dhcppkt.len );
|
iob_put ( iobuf, dhcppkt.len );
|
||||||
rc = xfer_deliver_iob_meta ( &dhcp->xfer, iobuf, &meta );
|
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
|
* @v dhcp DHCP session
|
||||||
*/
|
*/
|
||||||
static void dhcp_next_state ( struct dhcp_session *dhcp ) {
|
static void dhcp_next_state ( struct dhcp_session *dhcp ) {
|
||||||
|
struct in_addr bs_mcast = { 0 };
|
||||||
|
|
||||||
switch ( dhcp->state ) {
|
switch ( dhcp->state ) {
|
||||||
case DHCP_STATE_DISCOVER:
|
case DHCP_STATE_DISCOVER:
|
||||||
|
@ -662,6 +704,17 @@ static void dhcp_next_state ( struct dhcp_session *dhcp ) {
|
||||||
}
|
}
|
||||||
/* Fall through */
|
/* Fall through */
|
||||||
case DHCP_STATE_PROXYREQUEST:
|
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 );
|
dhcp_finished ( dhcp, 0 );
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -845,9 +898,13 @@ static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Record DHCPACK */
|
||||||
|
assert ( dhcp->dhcpack == NULL );
|
||||||
|
dhcp->dhcpack = dhcpset_get ( dhcpack );
|
||||||
|
|
||||||
/* Register settings */
|
/* Register settings */
|
||||||
parent = netdev_settings ( dhcp->netdev );
|
parent = netdev_settings ( dhcp->netdev );
|
||||||
if ( ( rc = dhcp_store_dhcpack ( dhcp, dhcpack, parent ) ) !=0 )
|
if ( ( rc = dhcp_store_dhcpack ( dhcp, dhcpack, parent ) ) != 0 )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Transition to next state */
|
/* Transition to next state */
|
||||||
|
@ -885,6 +942,10 @@ static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp,
|
||||||
/* Rename settings */
|
/* Rename settings */
|
||||||
proxydhcpack->settings.name = PROXYDHCP_SETTINGS_NAME;
|
proxydhcpack->settings.name = PROXYDHCP_SETTINGS_NAME;
|
||||||
|
|
||||||
|
/* Record ProxyDHCPACK */
|
||||||
|
assert ( dhcp->proxydhcpack == NULL );
|
||||||
|
dhcp->proxydhcpack = dhcpset_get ( proxydhcpack );
|
||||||
|
|
||||||
/* Register settings */
|
/* Register settings */
|
||||||
if ( ( rc = dhcp_store_dhcpack ( dhcp, proxydhcpack, NULL ) ) != 0 )
|
if ( ( rc = dhcp_store_dhcpack ( dhcp, proxydhcpack, NULL ) ) != 0 )
|
||||||
return;
|
return;
|
||||||
|
@ -893,6 +954,31 @@ static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp,
|
||||||
dhcp_next_state ( dhcp );
|
dhcp_next_state ( 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
|
* Receive new data
|
||||||
*
|
*
|
||||||
|
@ -969,6 +1055,11 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
|
||||||
( src_port == htons ( PROXYDHCP_PORT ) ) )
|
( src_port == htons ( PROXYDHCP_PORT ) ) )
|
||||||
dhcp_rx_proxydhcpack ( dhcp, dhcpset );
|
dhcp_rx_proxydhcpack ( dhcp, dhcpset );
|
||||||
break;
|
break;
|
||||||
|
case DHCP_STATE_BSREQUEST:
|
||||||
|
if ( ( msgtype == DHCPACK ) &&
|
||||||
|
( src_port == htons ( PROXYDHCP_PORT ) ) )
|
||||||
|
dhcp_rx_bsdhcpack ( dhcp, dhcpset );
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assert ( 0 );
|
assert ( 0 );
|
||||||
break;
|
break;
|
||||||
|
|
Reference in New Issue