2271 lines
50 KiB
C
2271 lines
50 KiB
C
/*++
|
||
|
||
Copyright (c) 2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
protocol.c
|
||
|
||
Abstract:
|
||
|
||
DHCP client protocol handling code
|
||
as well as Autonet implementation
|
||
|
||
Revision History:
|
||
|
||
04/21/2000 davidx
|
||
Created it.
|
||
|
||
05/25/2000 davidx
|
||
Ported over to the new net tree.
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
|
||
|
||
//
|
||
// First 4 bytes of the options field in a DHCP message
|
||
// must match the following magic cookie
|
||
//
|
||
const BYTE DhcpMagicCookie[DHCPCOOKIELEN] = { 99, 130, 83, 99 };
|
||
|
||
//
|
||
// Default address lease time requested:
|
||
// let the server decide
|
||
//
|
||
UINT cfgDefaultLeaseTime = 0;
|
||
|
||
//
|
||
// Minimum retransmission timeout while in
|
||
// RENEWING and REBINDING states: 60 seconds
|
||
//
|
||
UINT cfgMinRenewTimeout = 60;
|
||
|
||
//
|
||
// Number of seconds to wait after reboot
|
||
// before we start sending out DHCPDISCOVER packets
|
||
//
|
||
// NOTE: RFC2131 recommends that we wait between 1 to 10 seconds.
|
||
// But we're only doing 1 seconds here for faster startup time.
|
||
//
|
||
UINT cfgStartupWaitMin = 0;
|
||
UINT cfgStartupWaitMax = 1;
|
||
|
||
//
|
||
// Number of times we'll attempt to resend
|
||
// DHCPDISCOVER and DHCPREQUEST packets
|
||
//
|
||
// NOTE: Non-standard behavior!!!
|
||
// We're capping the timeout between retries to a maximum of 10 seconds.
|
||
//
|
||
UINT cfgDhcpRetryCount = 3;
|
||
UINT cfgDhcpRetryMaxTimeout = 10;
|
||
|
||
//
|
||
// Number of autonet addresses we'll attempt before giving up
|
||
//
|
||
UINT cfgAutonetAddrRetries = 10;
|
||
|
||
//
|
||
// How frequently to look for DHCP server when in Autonet mode
|
||
//
|
||
UINT cfgAutonetDhcpCheckInterval = 5*60;
|
||
|
||
//
|
||
// How many ARP requests to send
|
||
// when checking for address conflict
|
||
//
|
||
UINT cfgConflictCheckRetries = 2;
|
||
|
||
//
|
||
// Global transaction ID
|
||
//
|
||
PRIVATE DWORD DhcpGlobalXid = 0;
|
||
|
||
//
|
||
// Forward function declarations
|
||
//
|
||
PRIVATE VOID DhcpChangeState(DhcpInfo*, INT);
|
||
PRIVATE VOID DhcpSetIfTimerRelative(DhcpInfo*, UINT, UINT);
|
||
PRIVATE VOID DhcpSetIfTimerAbsolute(DhcpInfo*, DHCPTIME);
|
||
PRIVATE VOID DhcpHandleTimeout(DhcpInfo*);
|
||
PRIVATE VOID DhcpResetInterface(DhcpInfo*);
|
||
PRIVATE VOID DhcpSelectAutonetAddr(DhcpInfo*);
|
||
|
||
PRIVATE NTSTATUS DhcpSendDiscover(DhcpInfo*);
|
||
PRIVATE NTSTATUS DhcpSendRequest(DhcpInfo*);
|
||
PRIVATE NTSTATUS DhcpSendInform(DhcpInfo*);
|
||
PRIVATE NTSTATUS DhcpSendDecline(DhcpInfo*, IPADDR, IPADDR);
|
||
PRIVATE NTSTATUS DhcpSendRelease(DhcpInfo*);
|
||
|
||
PRIVATE VOID DhcpUseOptionParams(DhcpInfo*, DhcpOptionParam*);
|
||
PRIVATE NTSTATUS DhcpParseOptionParams(DhcpOptionParam*, const BYTE*, UINT, BYTE*);
|
||
PRIVATE NTSTATUS DhcpProcessOffer(DhcpInfo*, IPADDR, DhcpOptionParam*);
|
||
PRIVATE NTSTATUS DhcpProcessAck(DhcpInfo*, IPADDR, DhcpOptionParam*);
|
||
PRIVATE NTSTATUS DhcpProcessNak(DhcpInfo*, IPADDR);
|
||
PRIVATE VOID DhcpLoadConfigInfo(DhcpInfo*);
|
||
|
||
// Check to see if we're forced to use autonet address
|
||
// (without trying to find DHCP servers)
|
||
INLINE BOOL DhcpForceAutonet(IPADDR addr, IPADDR mask) {
|
||
return (mask == HTONL(AUTONET_ADDRMASK)) &&
|
||
(addr & mask) == (HTONL(AUTONET_ADDRBASE) & mask);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
DhcpInitialize(
|
||
IfInfo* ifp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialize the DHCP related data structure for an interface
|
||
|
||
Arguments:
|
||
|
||
ifp - Points to the interface structure
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
DhcpInfo* dhcp;
|
||
|
||
if ((dhcp = ifp->dhcpInfo) != NULL) {
|
||
dhcp->flags |= FLAG_CREATED_BY_DEBUGGER;
|
||
return NETERR_OK;
|
||
}
|
||
|
||
dhcp = (DhcpInfo*) SysAlloc0(sizeof(DhcpInfo), PTAG_DHCP);
|
||
if (!dhcp) return NETERR_MEMORY;
|
||
|
||
KeInitializeEvent(&dhcp->addrEvent, NotificationEvent, FALSE);
|
||
dhcp->ifp = ifp;
|
||
dhcp->state = STATE_NONE;
|
||
dhcp->flags = FLAG_SEND_DHCPINFORM;
|
||
dhcp->timer = 0xffffffff;
|
||
|
||
ifp->dhcpInfo = dhcp;
|
||
|
||
// Load persistent DHCP configuration parameters
|
||
DhcpLoadConfigInfo(dhcp);
|
||
|
||
if (dhcp->activeaddr != 0 &&
|
||
!DhcpForceAutonet(dhcp->activeaddr, dhcp->activemask)) {
|
||
|
||
//
|
||
// If we're using static address, inform the IP stack
|
||
// BUGBUG - also need to set up the default DNS server?
|
||
//
|
||
IfSetIpAddr(ifp, dhcp->activeaddr, dhcp->activemask);
|
||
DhcpSignalAddrEvent(dhcp);
|
||
DhcpSetDefaultGateways(ifp);
|
||
DhcpChangeState(dhcp, STATE_STATIC_ADDR);
|
||
|
||
} else if (!(ifp->flags & IFFLAG_CONNECTED_BOOT) ||
|
||
(cfgXnetConfigFlags & XNET_INITFLAG_FORCE_AUTONET) ||
|
||
dhcp->activeaddr != 0) {
|
||
|
||
//
|
||
// Skip the DHCP address discovery process and
|
||
// go straight to autonet mode if:
|
||
// 1. The net cable is disconnected at boot time
|
||
// 2. XnetInitialize was called with force-autonet mode
|
||
// 3. User has configed static IP address = 169.254.x.x
|
||
//
|
||
WARNING_("Forcing autonet...");
|
||
dhcp->activeaddr = dhcp->activemask = 0;
|
||
DhcpChangeState(dhcp, STATE_SELECT_AUTOADDR);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Obtain address via DHCP or Autonet
|
||
// Be quiet for a while after startup
|
||
//
|
||
DhcpSetIfTimerRelative(dhcp, cfgStartupWaitMin, cfgStartupWaitMax);
|
||
}
|
||
|
||
return NETERR_OK;
|
||
}
|
||
|
||
|
||
VOID
|
||
DhcpCleanup(
|
||
IfInfo* ifp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Clean up DHCP related data for an interface
|
||
|
||
Arguments:
|
||
|
||
ifp - Points to the interface structure
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
DhcpInfo* dhcp = ifp->dhcpInfo;
|
||
|
||
RUNS_AT_DISPATCH_LEVEL
|
||
if (!dhcp) return;
|
||
if (dhcp->flags & FLAG_CREATED_BY_DEBUGGER) {
|
||
dhcp->flags &= ~FLAG_CREATED_BY_DEBUGGER;
|
||
return;
|
||
}
|
||
ifp->dhcpInfo = NULL;
|
||
|
||
// Release the DHCP address if necessary
|
||
if (ActiveDhcpAddr(dhcp) && (dhcp->flags & FLAG_RELEASE_ON_REBOOT)) {
|
||
DhcpSendRelease(dhcp);
|
||
}
|
||
|
||
// Free the memory for the interface structure
|
||
SysFree(dhcp);
|
||
}
|
||
|
||
|
||
VOID
|
||
DhcpTimerProc(
|
||
IfInfo* ifp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
DHCP timer routine for an interface
|
||
|
||
Arguments:
|
||
|
||
ifp - Points to the interface structure
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
DhcpInfo* dhcp = ifp->dhcpInfo;
|
||
|
||
// Do nothing if the timer hasn't expired
|
||
if (dhcp == NULL ||
|
||
dhcp->timer == 0xffffffff ||
|
||
dhcp->timer-- > 1)
|
||
return;
|
||
|
||
DhcpHandleTimeout(dhcp);
|
||
}
|
||
|
||
|
||
VOID
|
||
DhcpReceivePacket(
|
||
IfInfo* ifp,
|
||
Packet* pkt
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Handle a UDP packet received on an interface
|
||
that's destined for the DHCP client port
|
||
|
||
Arguments:
|
||
|
||
ifp - Points to the interface structure
|
||
pkt - Points to the received packet
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
Note:
|
||
|
||
The caller retains ownership of the received packet.
|
||
So we don't call CompletePacket on it here.
|
||
|
||
--*/
|
||
|
||
{
|
||
DhcpInfo* dhcp = ifp->dhcpInfo;
|
||
DhcpMessage* msg;
|
||
UINT msglen;
|
||
DhcpOptionParam* dhcpParams = NULL;
|
||
BYTE* option;
|
||
BYTE overload = 0;
|
||
NTSTATUS status = NETERR_DISCARDED;
|
||
|
||
RUNS_AT_DISPATCH_LEVEL
|
||
if (!dhcp) return;
|
||
|
||
msg = GETPKTDATA(pkt, DhcpMessage);
|
||
msglen = pkt->datalen;
|
||
|
||
DhcpDumpMessage(msg, msglen);
|
||
|
||
//
|
||
// Basic sanity checks: was the packet really meant for us?
|
||
//
|
||
if (msglen < DHCPHDRLEN ||
|
||
msg->op != BOOTREPLY ||
|
||
msg->htype != DhcpMapHwtype(ifp->iftype) ||
|
||
msg->hlen != ifp->hwaddrlen ||
|
||
!EqualMem(msg->chaddr, ifp->hwaddr, ifp->hwaddrlen) ||
|
||
NTOHL(msg->xid) != dhcp->xid) {
|
||
return;
|
||
}
|
||
|
||
dhcpParams = (DhcpOptionParam*) XnetAlloc0(sizeof(*dhcpParams), PTAG_DHCP);
|
||
if (!dhcpParams) {
|
||
status = NETERR_MEMORY;
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Skip the DHCP magic cookie
|
||
//
|
||
option = msg->options;
|
||
msglen -= DHCPHDRLEN;
|
||
if (msglen < DHCPCOOKIELEN ||
|
||
!EqualMem(option, DhcpMagicCookie, DHCPCOOKIELEN)) {
|
||
goto exit;
|
||
}
|
||
msglen -= DHCPCOOKIELEN;
|
||
option += DHCPCOOKIELEN;
|
||
|
||
//
|
||
// Parse the regular options
|
||
//
|
||
status = DhcpParseOptionParams(dhcpParams, option, msglen, &overload);
|
||
if (!NT_SUCCESS(status)) goto exit;
|
||
|
||
//
|
||
// Parse overloaded options in the msg->file field
|
||
//
|
||
if (overload & 1) {
|
||
status = DhcpParseOptionParams(dhcpParams, msg->file, sizeof(msg->file), NULL);
|
||
if (!NT_SUCCESS(status)) goto exit;
|
||
}
|
||
|
||
//
|
||
// Parse overloaded options in the msg->sname field
|
||
//
|
||
if (overload & 2) {
|
||
status = DhcpParseOptionParams(dhcpParams, msg->sname, sizeof(msg->sname), NULL);
|
||
if (!NT_SUCCESS(status)) goto exit;
|
||
}
|
||
|
||
//
|
||
// The received packet must have a server identifier option
|
||
//
|
||
if (dhcpParams->dhcpServer == 0) {
|
||
status = NETERR_PARAM;
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Now that we've parsed the option data,
|
||
// use it appropriately
|
||
//
|
||
status = NETERR_DISCARDED;
|
||
|
||
switch (dhcp->state) {
|
||
case STATE_INIT:
|
||
// Expecting DHCPOFFER...
|
||
if (dhcpParams->recvMsgType == DHCPOFFER)
|
||
status = DhcpProcessOffer(dhcp, msg->yiaddr, dhcpParams);
|
||
break;
|
||
|
||
case STATE_INIT_REBOOT:
|
||
case STATE_REQUESTING:
|
||
case STATE_RENEWING:
|
||
case STATE_REBINDING:
|
||
// Expecting DHCPACK or DHCPNAK...
|
||
if (dhcpParams->recvMsgType == DHCPACK)
|
||
status = DhcpProcessAck(dhcp, msg->yiaddr, dhcpParams);
|
||
else if (dhcpParams->recvMsgType == DHCPNAK)
|
||
status = DhcpProcessNak(dhcp, dhcpParams->dhcpServer);
|
||
break;
|
||
|
||
case STATE_STATIC_ADDR:
|
||
// Expecting DHCPACK...
|
||
if (dhcpParams->recvMsgType == DHCPACK) {
|
||
// Use the option parameters from the server
|
||
DhcpUseOptionParams(dhcp, dhcpParams);
|
||
status = NETERR_OK;
|
||
}
|
||
break;
|
||
}
|
||
|
||
exit:
|
||
|
||
if (!NT_SUCCESS(status) && status != NETERR_DISCARDED) {
|
||
WARNING_("ProcessDhcpPacket failed: 0x%x", status);
|
||
}
|
||
|
||
XnetFree(dhcpParams);
|
||
}
|
||
|
||
|
||
VOID
|
||
DhcpNotifyAddressConflict(
|
||
IfInfo* ifp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is called when the interface layer detects
|
||
an address conflict (e.g. through ARP)
|
||
|
||
Arguments:
|
||
|
||
ifp - Points to the interface structure
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
DhcpInfo* dhcp = ifp->dhcpInfo;
|
||
|
||
RUNS_AT_DISPATCH_LEVEL
|
||
if (!dhcp) return;
|
||
|
||
if (dhcp->state == STATE_SELECT_AUTOADDR) {
|
||
// the last autonet address we chose was no good
|
||
// so we need to find another one
|
||
DhcpSelectAutonetAddr(dhcp);
|
||
} else {
|
||
WARNING_("!!! Address conflict: %s", IPADDRSTR(dhcp->activeaddr));
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Number of seconds from start of 1601 to start of 2000
|
||
//
|
||
static const LONGLONG StartOf2000 = 0x2ee7dd480;
|
||
|
||
PRIVATE DHCPTIME
|
||
DhcpTime()
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Return the number of seconds ellapsed since 00:00:00
|
||
January 1, 2000, coordinated universal time.
|
||
|
||
Arguments:
|
||
|
||
NONE
|
||
|
||
Return Value:
|
||
|
||
See above.
|
||
|
||
--*/
|
||
|
||
{
|
||
LARGE_INTEGER currentTime;
|
||
|
||
// Get the current UTC time
|
||
// = number of 100 nanoseconds since 1/1/1601
|
||
KeQuerySystemTime(¤tTime);
|
||
|
||
// Return the number of seconds since the start of 2000
|
||
return (DHCPTIME) (currentTime.QuadPart / 10000000 - StartOf2000);
|
||
}
|
||
|
||
|
||
PRIVATE VOID
|
||
DhcpSetIfTimerRelative(
|
||
DhcpInfo* dhcp,
|
||
UINT minWait,
|
||
UINT maxWait
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Set the timer for an interface:
|
||
randomized and relative to current time
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
minWait, maxWait - minimum and maximum wait time, in seconds
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
// Compute the randomized wait time
|
||
minWait += XnetRandScaled(maxWait - minWait);
|
||
|
||
if ((dhcp->timer = minWait) == 0) {
|
||
DhcpHandleTimeout(dhcp);
|
||
}
|
||
}
|
||
|
||
|
||
PRIVATE VOID
|
||
DhcpSetIfTimerAbsolute(
|
||
DhcpInfo* dhcp,
|
||
DHCPTIME timer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Set the timer for an interface: absolute time
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
timer - When the timer should expire
|
||
number of seconds since the start of 1/1/2000
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
DHCPTIME now;
|
||
|
||
if (timer == DHCPTIME_INFINITE) {
|
||
// Infinite wait
|
||
dhcp->timer = 0xffffffff;
|
||
return;
|
||
}
|
||
|
||
now = DhcpTime();
|
||
if (timer > now) {
|
||
dhcp->timer = timer - now;
|
||
} else {
|
||
dhcp->timer = 0;
|
||
DhcpHandleTimeout(dhcp);
|
||
}
|
||
}
|
||
|
||
|
||
PRIVATE VOID
|
||
DhcpComputeTimeout(
|
||
DhcpInfo* dhcp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Compute the next timeout interval after sending out a packet
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
UINT minWait, maxWait;
|
||
|
||
switch (dhcp->state) {
|
||
case STATE_INIT:
|
||
if (ActiveAutonetAddr(dhcp)) {
|
||
//
|
||
// Special case: we're currently using an Autonet address
|
||
// and we're sending out periodic DHCPDISCOVER packets.
|
||
//
|
||
// DhcpSetIfTimerRelative(
|
||
// dhcp,
|
||
// cfgAutonetDhcpCheckInterval,
|
||
// cfgAutonetDhcpCheckInterval);
|
||
|
||
// NOTE: Non-standard behavior!!!
|
||
// We do not send out DHCPDISCOVER messages
|
||
// while we're using an active AutoNet address.
|
||
DhcpSetIfTimerAbsolute(dhcp, DHCPTIME_INFINITE);
|
||
break;
|
||
}
|
||
|
||
// Fall through
|
||
|
||
case STATE_REQUESTING:
|
||
case STATE_INIT_REBOOT:
|
||
//
|
||
// timeout = 2**retry x 2 +/- 1, i.e.
|
||
// 4 +/- 1
|
||
// 8 +/- 1
|
||
// 16 +/- 1
|
||
// 32 +/- 1
|
||
//
|
||
|
||
minWait = 2 << dhcp->retries;
|
||
if (minWait > cfgDhcpRetryMaxTimeout)
|
||
minWait = cfgDhcpRetryMaxTimeout;
|
||
|
||
minWait -= 1;
|
||
maxWait = minWait + 2;
|
||
DhcpSetIfTimerRelative(dhcp, minWait, maxWait);
|
||
break;
|
||
|
||
case STATE_BOUND:
|
||
//
|
||
// timeout when T1 expires
|
||
//
|
||
DhcpSetIfTimerAbsolute(dhcp, dhcp->t1time);
|
||
break;
|
||
|
||
case STATE_RENEWING:
|
||
case STATE_REBINDING: {
|
||
//
|
||
// calculate retransmission timeout for
|
||
// RENEWING and REBINDING states:
|
||
// 1. half the time from now to T2 (renew) or expiration (rebind)
|
||
// 2. make sure it's at least 60 seconds
|
||
//
|
||
|
||
DHCPTIME t1, t2;
|
||
t1 = DhcpTime();
|
||
t2 = (dhcp->state == STATE_RENEWING) ? dhcp->t2time : dhcp->exptime;
|
||
|
||
if (t1 < t2) {
|
||
minWait = (t2-t1) / 2;
|
||
if (minWait >= cfgMinRenewTimeout)
|
||
t1 += minWait;
|
||
else {
|
||
t1 += cfgMinRenewTimeout;
|
||
if (t1 > t2) t1 = t2;
|
||
}
|
||
}
|
||
|
||
DhcpSetIfTimerAbsolute(dhcp, t1);
|
||
}
|
||
break;
|
||
|
||
case STATE_DECLINING:
|
||
//
|
||
// Wait 10 seconds after sending DHCP decline
|
||
//
|
||
DhcpSetIfTimerRelative(dhcp, 10, 10);
|
||
break;
|
||
|
||
case STATE_SELECT_AUTOADDR:
|
||
//
|
||
// Wait ~2 seconds for address conflict detection
|
||
//
|
||
DhcpSetIfTimerRelative(dhcp, 2, 2);
|
||
break;
|
||
|
||
default:
|
||
// Should not happen - shut off the timer just in case
|
||
ASSERT(FALSE);
|
||
|
||
case STATE_STATIC_ADDR:
|
||
case STATE_NONE:
|
||
DhcpSetIfTimerAbsolute(dhcp, DHCPTIME_INFINITE);
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
PRIVATE VOID
|
||
DhcpHandleTimeout(
|
||
DhcpInfo* dhcp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Handle a timeout event for an interface
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
switch (dhcp->state) {
|
||
case STATE_NONE:
|
||
//
|
||
// We got here because we just finished the quiet
|
||
// period after startup
|
||
//
|
||
// If we had a valid lease before, start in
|
||
// INIT-REBOOT state; otherwise, start in INIT state
|
||
//
|
||
DhcpChangeState(dhcp, dhcp->dhcpaddr ? STATE_INIT_REBOOT : STATE_INIT);
|
||
break;
|
||
|
||
case STATE_INIT:
|
||
//
|
||
// We're sending DHCPDISCOVER messages
|
||
//
|
||
if (dhcp->retries >= cfgDhcpRetryCount && !ActiveAutonetAddr(dhcp)) {
|
||
//
|
||
// Too many retries, give up and
|
||
// start Autonet address acquisition process
|
||
//
|
||
VERBOSE_("Couldn't discover DHCP server, trying autonet...");
|
||
DhcpChangeState(dhcp, STATE_SELECT_AUTOADDR);
|
||
} else {
|
||
//
|
||
// Send out another DHCPDISCOVER packet
|
||
//
|
||
VERBOSE_("Retransmit DHCPDISCOVER");
|
||
DhcpSendDiscover(dhcp);
|
||
}
|
||
break;
|
||
|
||
case STATE_INIT_REBOOT:
|
||
case STATE_REQUESTING:
|
||
//
|
||
// We're sending DHCPREQUEST
|
||
//
|
||
if (dhcp->retries >= cfgDhcpRetryCount) {
|
||
//
|
||
// Too many retries, go back to INIT state
|
||
//
|
||
VERBOSE_(dhcp->state == STATE_INIT_REBOOT ?
|
||
"Failed to reuse a prior lease" :
|
||
"No ack for an offered address");
|
||
|
||
DhcpChangeState(dhcp, STATE_INIT);
|
||
} else {
|
||
//
|
||
// Send out another DHCPREQUEST packet
|
||
//
|
||
VERBOSE_("Retransmit DHCPREQUEST");
|
||
DhcpSendRequest(dhcp);
|
||
}
|
||
break;
|
||
|
||
case STATE_DECLINING:
|
||
//
|
||
// We just finished waiting after sending DHCPDECLINE
|
||
//
|
||
DhcpChangeState(dhcp, STATE_INIT);
|
||
break;
|
||
|
||
case STATE_BOUND:
|
||
//
|
||
// T1 expired, start the renewing process
|
||
//
|
||
VERBOSE_("Switching to RENEWING state");
|
||
DhcpChangeState(dhcp, STATE_RENEWING);
|
||
break;
|
||
|
||
case STATE_RENEWING:
|
||
//
|
||
// We're trying to renew a valid address.
|
||
// If T2 expired, start the rebinding process.
|
||
//
|
||
if (DhcpTime() >= dhcp->t2time) {
|
||
VERBOSE_("Switching to REBINDING state");
|
||
DhcpChangeState(dhcp, STATE_REBINDING);
|
||
} else {
|
||
// Send out another DHCPREQUEST
|
||
VERBOSE_("Retransmit DHCPREQUEST");
|
||
DhcpSendRequest(dhcp);
|
||
}
|
||
break;
|
||
|
||
case STATE_REBINDING:
|
||
//
|
||
// We're trying to rebind a valid lease
|
||
// did our lease expire?
|
||
//
|
||
if (DhcpTime() >= dhcp->exptime) {
|
||
// Inform IP stack to discard the active address
|
||
VERBOSE_("Address lease expired - start over");
|
||
DhcpResetInterface(dhcp);
|
||
|
||
// Too bad, go back to INIT state
|
||
DhcpChangeState(dhcp, STATE_INIT);
|
||
} else {
|
||
// Send out another DHCPREQUEST
|
||
VERBOSE_("Retransmit DHCPREQUEST");
|
||
DhcpSendRequest(dhcp);
|
||
}
|
||
break;
|
||
|
||
case STATE_SELECT_AUTOADDR:
|
||
if (++dhcp->retries >= cfgConflictCheckRetries) {
|
||
// We've successfully picked an autonet address.
|
||
VERBOSE_("Selected autonet address: %s", IPADDRSTR(dhcp->autonetaddr));
|
||
|
||
dhcp->activeaddr = dhcp->autonetaddr;
|
||
dhcp->activemask = HTONL(AUTONET_ADDRMASK);
|
||
dhcp->flags |= FLAG_ACTIVE_AUTONETADDR;
|
||
IfSetIpAddr(dhcp->ifp, dhcp->activeaddr, dhcp->activemask);
|
||
DhcpSignalAddrEvent(dhcp);
|
||
|
||
// Switch to INIT state to continue looking
|
||
// for a DHCP server
|
||
dhcp->initRetryCount = 0;
|
||
DhcpChangeState(dhcp, STATE_INIT);
|
||
} else {
|
||
// No response to our previous ARP request.
|
||
// Try again just to be sure.
|
||
DhcpCheckAddressConflict(dhcp->ifp, dhcp->autonetaddr);
|
||
DhcpComputeTimeout(dhcp);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
VERBOSE_("Unexpected timeout");
|
||
DhcpSetIfTimerAbsolute(dhcp, DHCPTIME_INFINITE);
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
PRIVATE VOID
|
||
DhcpChangeState(
|
||
DhcpInfo* dhcp,
|
||
INT state
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Change the state of an interface
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
state - Specifies the new state for the interface
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
INT oldstate = dhcp->state;
|
||
|
||
dhcp->state = state;
|
||
dhcp->retries = 0;
|
||
|
||
// Assign a new transaction ID for the next outgoing message.
|
||
|
||
if (DhcpGlobalXid == 0) DhcpGlobalXid = XnetRand();
|
||
dhcp->xid = DhcpGlobalXid++;
|
||
|
||
switch (state) {
|
||
case STATE_STATIC_ADDR:
|
||
// Should we send DHCPINFORM?
|
||
if (dhcp->flags & FLAG_SEND_DHCPINFORM) {
|
||
DhcpSendInform(dhcp);
|
||
}
|
||
break;
|
||
|
||
case STATE_INIT_REBOOT:
|
||
case STATE_REQUESTING:
|
||
case STATE_RENEWING:
|
||
case STATE_REBINDING:
|
||
// Send out DHCPREQUEST
|
||
DhcpSendRequest(dhcp);
|
||
break;
|
||
|
||
case STATE_INIT:
|
||
if (oldstate == STATE_SELECT_AUTOADDR) {
|
||
// We just selected an autonet address.
|
||
// Continue to look for a DHCP server.
|
||
DhcpComputeTimeout(dhcp);
|
||
} else if (++dhcp->initRetryCount > cfgDhcpRetryCount) {
|
||
// We went through the INIT state too many times
|
||
// without getting a valid address lease. Just give up.
|
||
// We don't try Autonet because in this case there
|
||
// is a DHCP server but somehow we can't work with it.
|
||
WARNING_("Couldn't get a valid DHCP address after many tries");
|
||
DhcpChangeState(dhcp, STATE_NONE);
|
||
|
||
// Signal that Xnet initialization was completed abnormally
|
||
DhcpSignalAddrEvent(dhcp);
|
||
} else {
|
||
// Send out DHCPDISCOVER
|
||
DhcpSendDiscover(dhcp);
|
||
}
|
||
break;
|
||
|
||
case STATE_BOUND:
|
||
DhcpComputeTimeout(dhcp);
|
||
VERBOSE_("Sleep %d seconds till renewal...", dhcp->t1time - DhcpTime());
|
||
break;
|
||
|
||
case STATE_SELECT_AUTOADDR:
|
||
dhcp->initRetryCount = 0;
|
||
DhcpSelectAutonetAddr(dhcp);
|
||
break;
|
||
|
||
case STATE_NONE:
|
||
DhcpSetIfTimerAbsolute(dhcp, DHCPTIME_INFINITE);
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
PRIVATE VOID
|
||
DhcpResetInterface(
|
||
DhcpInfo* dhcp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reset an interface to have no address
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
IfSetIpAddr(dhcp->ifp, 0, 0);
|
||
dhcp->flags &= ~FLAG_ACTIVE_ADDRMASK;
|
||
if (dhcp->state != STATE_STATIC_ADDR)
|
||
dhcp->activeaddr = 0;
|
||
|
||
// Since we don't have an active address anymore,
|
||
// we need to notify the DNS module of the change.
|
||
DnsNotifyDefaultServers(dhcp->ifp);
|
||
}
|
||
|
||
|
||
PRIVATE NTSTATUS
|
||
DhcpSendMessage(
|
||
DhcpInfo* dhcp,
|
||
Packet* pkt,
|
||
BOOL broadcast
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Send out a DHCP message on the specified interface
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
pkt - Points to the message to be sent
|
||
!!! we assume the message buffer is at least 300 bytes
|
||
and unused bytes are zero-filled.
|
||
broadcast - Whether to use broadcast or unicast
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
IpAddrPair addrpair;
|
||
DhcpMessage* msg;
|
||
UINT msglen;
|
||
|
||
addrpair.dstaddr = broadcast ? IPADDR_BCAST : dhcp->dhcpServer;
|
||
addrpair.srcaddr = dhcp->activeaddr;
|
||
addrpair.dstport = DHCP_SERVER_PORT;
|
||
addrpair.srcport = DHCP_CLIENT_PORT;
|
||
|
||
msg = GETPKTDATA(pkt, DhcpMessage);
|
||
msglen = pkt->datalen;
|
||
|
||
// Debug trace
|
||
DhcpDumpMessage(msg, msglen);
|
||
|
||
// Set broadcast flag if necessary
|
||
if (!dhcp->activeaddr || ActiveAutonetAddr(dhcp))
|
||
msg->flags |= HTONS(DHCP_BROADCAST);
|
||
|
||
// Call UDP directly to send out the packet
|
||
UdpSendPacketInternal(pkt, &addrpair, dhcp->ifp);
|
||
|
||
// Calculate the timeout value
|
||
dhcp->retries++;
|
||
DhcpComputeTimeout(dhcp);
|
||
|
||
return NETERR_OK;
|
||
}
|
||
|
||
|
||
//
|
||
// Append the 'parameter request list' option
|
||
// !!! we assume the data buffer is large enough.
|
||
//
|
||
PRIVATE BYTE*
|
||
DhcpAppendParamReqList(
|
||
BYTE* option
|
||
)
|
||
{
|
||
//
|
||
// our default list of option parameters
|
||
//
|
||
static const BYTE defaultParamReqList[] = {
|
||
DHCPOPT_SUBNET_MASK,
|
||
DHCPOPT_ROUTERS,
|
||
DHCPOPT_DNS_SERVERS,
|
||
DHCPOPT_DOMAIN_NAME
|
||
};
|
||
|
||
UINT count = sizeof(defaultParamReqList);
|
||
|
||
option[0] = DHCPOPT_PARAM_REQUEST_LIST;
|
||
option[1] = (BYTE) count;
|
||
option += 2;
|
||
|
||
CopyMem(option, defaultParamReqList, count);
|
||
return option + count;
|
||
}
|
||
|
||
//
|
||
// Append an option whose value is a DWORD
|
||
//
|
||
PRIVATE BYTE*
|
||
DhcpAppendDWordOption(
|
||
BYTE* option,
|
||
INT tag,
|
||
DWORD val
|
||
)
|
||
{
|
||
option[0] = (BYTE) tag;
|
||
option[1] = sizeof(DWORD);
|
||
option += 2;
|
||
|
||
CopyMem(option, &val, sizeof(DWORD));
|
||
return option + sizeof(DWORD);
|
||
}
|
||
|
||
|
||
//
|
||
// Append the 'address lease time' option
|
||
//
|
||
PRIVATE BYTE*
|
||
DhcpAppendLeaseTimeOption(
|
||
BYTE* option
|
||
)
|
||
{
|
||
if (cfgDefaultLeaseTime == 0) return option;
|
||
|
||
return DhcpAppendDWordOption(
|
||
option,
|
||
DHCPOPT_IPADDR_LEASE_TIME,
|
||
HTONL(cfgDefaultLeaseTime));
|
||
}
|
||
|
||
|
||
PRIVATE BYTE*
|
||
DhcpFillMessageHeader(
|
||
DhcpInfo* dhcp,
|
||
BYTE* buf,
|
||
INT msgtype
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Fill in the common header information for
|
||
all outgoing DHCP messages
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
buf - Points to the message buffer
|
||
!!! must be at least DEFAULT_DHCP_BUFSIZE bytes
|
||
msgtype - DHCP message type
|
||
|
||
Return Value:
|
||
|
||
Points to the first byte after the common options:
|
||
magic cookie
|
||
DHCP message type
|
||
client identifier
|
||
|
||
--*/
|
||
|
||
{
|
||
DhcpMessage* msg = (DhcpMessage*) buf;
|
||
BYTE* option = msg->options;
|
||
IfInfo* ifp = dhcp->ifp;
|
||
BYTE hwtype;
|
||
|
||
ZeroMem(buf, DEFAULT_DHCP_BUFSIZE);
|
||
msg->op = BOOTREQUEST;
|
||
msg->htype = hwtype = DhcpMapHwtype(ifp->iftype);
|
||
msg->hlen = (BYTE) ifp->hwaddrlen;
|
||
CopyMem(msg->chaddr, ifp->hwaddr, ifp->hwaddrlen);
|
||
|
||
//
|
||
// Fill in the transaction ID field
|
||
// NOTE: We reuse the same XID for retransmissions.
|
||
//
|
||
msg->xid = HTONL(dhcp->xid);
|
||
|
||
// Number of seconds since we started the address
|
||
// acquisition process.
|
||
msg->secs = (WORD) HTONS(dhcp->secsSinceStart);
|
||
|
||
// Start with the magic cookie
|
||
CopyMem(option, DhcpMagicCookie, DHCPCOOKIELEN);
|
||
option += DHCPCOOKIELEN;
|
||
|
||
// Append the message type option
|
||
option[0] = DHCPOPT_DHCP_MESSAGE_TYPE;
|
||
option[1] = 1;
|
||
option[2] = (BYTE) msgtype;
|
||
option += 3;
|
||
|
||
// Append the client identifier option
|
||
option[0] = DHCPOPT_CLIENTID;
|
||
option[1] = (BYTE) (ifp->hwaddrlen+1);
|
||
option[2] = hwtype;
|
||
option += 3;
|
||
CopyMem(option, ifp->hwaddr, ifp->hwaddrlen);
|
||
option += ifp->hwaddrlen;
|
||
|
||
return option;
|
||
}
|
||
|
||
|
||
PRIVATE VOID
|
||
DhcpComputeSecsSinceStart(
|
||
DhcpInfo* dhcp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Compute the number of seconds since the current
|
||
address acqusition process started.
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
DHCPTIME now = DhcpTime();
|
||
|
||
if (dhcp->retries == 0) dhcp->acqtime = now;
|
||
dhcp->secsSinceStart = now - dhcp->acqtime;
|
||
}
|
||
|
||
|
||
//
|
||
// Common prolog and epilog for SendDhcpXXX functions
|
||
//
|
||
#define SEND_DHCP_MESSAGE_PROLOG() \
|
||
DhcpMessage* msg; \
|
||
BYTE* buf; \
|
||
BYTE* option; \
|
||
Packet* pkt = XnetAllocIpPacket(UDPHDRLEN, DEFAULT_DHCP_BUFSIZE); \
|
||
if (!pkt) return NETERR_MEMORY; \
|
||
buf = pkt->data; \
|
||
msg = (DhcpMessage*) buf
|
||
|
||
#define SEND_DHCP_MESSAGE_RETURN(_bcast) \
|
||
pkt->datalen = option - buf; \
|
||
return DhcpSendMessage(dhcp, pkt, _bcast)
|
||
|
||
|
||
PRIVATE NTSTATUS
|
||
DhcpSendDiscover(
|
||
DhcpInfo* dhcp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Broadcast a DHCPDISCOVER message
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
SEND_DHCP_MESSAGE_PROLOG();
|
||
|
||
ASSERT(dhcp->state == STATE_INIT);
|
||
|
||
DhcpComputeSecsSinceStart(dhcp);
|
||
|
||
// Fill in common header fields
|
||
option = DhcpFillMessageHeader(dhcp, buf, DHCPDISCOVER);
|
||
|
||
// Fill in the parameter request list
|
||
option = DhcpAppendParamReqList(option);
|
||
|
||
// Fill in requested ip address and lease time option
|
||
if (dhcp->dhcpaddr)
|
||
option = DhcpAppendDWordOption(option, DHCPOPT_REQUESTED_IPADDR, dhcp->dhcpaddr);
|
||
|
||
// Fill in the lease time option
|
||
option = DhcpAppendLeaseTimeOption(option);
|
||
*option++ = DHCPOPT_END;
|
||
|
||
// Emit the message
|
||
SEND_DHCP_MESSAGE_RETURN(TRUE);
|
||
}
|
||
|
||
|
||
PRIVATE NTSTATUS
|
||
DhcpSendRequest(
|
||
DhcpInfo* dhcp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Send (broadcast/unicast) a DHCPREQUEST message
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
INT state = dhcp->state;
|
||
SEND_DHCP_MESSAGE_PROLOG();
|
||
|
||
ASSERT(state == STATE_REQUESTING ||
|
||
state == STATE_RENEWING ||
|
||
state == STATE_REBINDING ||
|
||
state == STATE_INIT_REBOOT);
|
||
|
||
//
|
||
// If we're in REQUESTING state, then don't update
|
||
// the secsSinceStart field. This is so that the
|
||
// secs in DHCPREQUEST message will be the same as
|
||
// what's in the original DHCPDISCOVER message.
|
||
//
|
||
if (state != STATE_REQUESTING) {
|
||
DhcpComputeSecsSinceStart(dhcp);
|
||
}
|
||
|
||
// Fill in common header fields
|
||
option = DhcpFillMessageHeader(dhcp, buf, DHCPREQUEST);
|
||
|
||
// Fill in the ciaddr field and the 'requested ip addr' option
|
||
if (state == STATE_RENEWING || state == STATE_REBINDING) {
|
||
msg->ciaddr = dhcp->dhcpaddr;
|
||
} else if (dhcp->dhcpaddr) {
|
||
// state == STATE_INIT_REBOOT || state == STATE_REQUESTING
|
||
option = DhcpAppendDWordOption(option, DHCPOPT_REQUESTED_IPADDR, dhcp->dhcpaddr);
|
||
}
|
||
|
||
// Fill in the parameter request list
|
||
option = DhcpAppendParamReqList(option);
|
||
|
||
// Fill in the lease time option
|
||
option = DhcpAppendLeaseTimeOption(option);
|
||
|
||
// Fill in the server identifier option
|
||
if (state == STATE_REQUESTING)
|
||
option = DhcpAppendDWordOption(option, DHCPOPT_SERVERID, dhcp->dhcpServer);
|
||
|
||
*option++ = DHCPOPT_END;
|
||
|
||
// Emit the message:
|
||
// unicast in RENEWING state, broadcast otherwise
|
||
SEND_DHCP_MESSAGE_RETURN(state != STATE_RENEWING);
|
||
}
|
||
|
||
|
||
PRIVATE NTSTATUS
|
||
DhcpSendInform(
|
||
DhcpInfo* dhcp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Broadcast a DHCPINFORM message
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
SEND_DHCP_MESSAGE_PROLOG();
|
||
|
||
ASSERT(dhcp->state == STATE_STATIC_ADDR);
|
||
|
||
DhcpComputeSecsSinceStart(dhcp);
|
||
|
||
// Fill in common header fields
|
||
option = DhcpFillMessageHeader(dhcp, buf, DHCPINFORM);
|
||
|
||
// Fill in ciaddr field
|
||
msg->ciaddr = dhcp->activeaddr;
|
||
|
||
// Fill in the parameter request list
|
||
option = DhcpAppendParamReqList(option);
|
||
*option++ = DHCPOPT_END;
|
||
|
||
// Emit the message
|
||
SEND_DHCP_MESSAGE_RETURN(TRUE);
|
||
}
|
||
|
||
|
||
#ifdef _ENABLE_SEND_DECLINE
|
||
|
||
PRIVATE NTSTATUS
|
||
DhcpSendDecline(
|
||
DhcpInfo* dhcp,
|
||
IPADDR dhcpServer,
|
||
IPADDR ipaddr
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Broadcast a DHCPDECLINE message
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
dhcpServer - The server that offered the IP address
|
||
ipaddr - The IP address to decline
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
SEND_DHCP_MESSAGE_PROLOG();
|
||
|
||
ASSERT(dhcp->state == STATE_INIT_REBOOT ||
|
||
dhcp->state == STATE_REQUESTING);
|
||
|
||
DhcpChangeState(dhcp, STATE_DECLINING);
|
||
dhcp->secsSinceStart = 0;
|
||
|
||
// Fill in common header fields
|
||
option = DhcpFillMessageHeader(dhcp, buf, DHCPDECLINE);
|
||
|
||
// Fill in 'requested ip addr' and 'server identifier' options
|
||
option = DhcpAppendDWordOption(option, DHCPOPT_REQUESTED_IPADDR, ipaddr);
|
||
option = DhcpAppendDWordOption(option, DHCPOPT_SERVERID, dhcpServer);
|
||
*option++ = DHCPOPT_END;
|
||
|
||
// Emit the message
|
||
SEND_DHCP_MESSAGE_RETURN(TRUE);
|
||
}
|
||
|
||
#endif // _ENABLE_SEND_DECLINE
|
||
|
||
|
||
PRIVATE NTSTATUS
|
||
DhcpSendRelease(
|
||
DhcpInfo* dhcp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Unicast a DHCPRELEASE message to the server
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
SEND_DHCP_MESSAGE_PROLOG();
|
||
|
||
ASSERT(dhcp->state == STATE_BOUND ||
|
||
dhcp->state == STATE_RENEWING ||
|
||
dhcp->state == STATE_REBINDING);
|
||
|
||
DhcpChangeState(dhcp, STATE_NONE);
|
||
dhcp->secsSinceStart = 0;
|
||
|
||
// Fill in common header fields
|
||
option = DhcpFillMessageHeader(dhcp, buf, DHCPRELEASE);
|
||
|
||
// Fill in 'server identifier' option
|
||
option = DhcpAppendDWordOption(option, DHCPOPT_SERVERID, dhcp->dhcpServer);
|
||
*option++ = DHCPOPT_END;
|
||
|
||
// Fill in ciaddr field
|
||
msg->ciaddr = dhcp->dhcpaddr;
|
||
|
||
// Emit the message
|
||
SEND_DHCP_MESSAGE_RETURN(FALSE);
|
||
}
|
||
|
||
|
||
PRIVATE BOOL
|
||
DhcpValidateOffer(
|
||
IPADDR yiaddr,
|
||
DhcpOptionParam* param
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Perform simple sanity check of offered DHCP parameters
|
||
|
||
Arguments:
|
||
|
||
yiaddr - Offered IP address
|
||
param - Other offered parameters
|
||
|
||
Return Value:
|
||
|
||
TRUE if ok, FALSE otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
if (!XnetIsValidUnicastAddr(yiaddr) ||
|
||
!XnetIsValidUnicastAddr(param->dhcpServer) ||
|
||
param->exptime < 8*cfgMinRenewTimeout)
|
||
return FALSE;
|
||
|
||
if (param->dhcpmask == 0)
|
||
param->dhcpmask = XnetGetDefaultSubnetMask(yiaddr);
|
||
|
||
if (param->t1time == 0 ||
|
||
param->t2time == 0 ||
|
||
param->t1time >= param->t2time ||
|
||
param->t2time - param->t1time < cfgMinRenewTimeout ||
|
||
param->t2time >= param->exptime ||
|
||
param->exptime - param->t2time < cfgMinRenewTimeout) {
|
||
param->t1time = param->exptime / 2;
|
||
param->t2time = param->exptime * 7 / 8;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
PRIVATE NTSTATUS
|
||
DhcpProcessOffer(
|
||
DhcpInfo* dhcp,
|
||
IPADDR yiaddr,
|
||
DhcpOptionParam* param
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Process a received DHCPOFFER packet
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
yiaddr - Offered address
|
||
param - Option parameters
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSERT(dhcp->state == STATE_INIT);
|
||
|
||
VERBOSE_("Received DHCPOFFER %s", IPADDRSTR(yiaddr));
|
||
VERBOSE_(" from %s", IPADDRSTR(param->dhcpServer));
|
||
VERBOSE_(" @ time %d", DhcpTime());
|
||
|
||
// Simply sanity check of offered parameters
|
||
if (!DhcpValidateOffer(yiaddr, param))
|
||
return NETERR_PARAM;
|
||
|
||
dhcp->dhcpServer = param->dhcpServer;
|
||
dhcp->dhcpaddr = yiaddr;
|
||
|
||
// Send DHCPREQUEST and ignore error
|
||
DhcpChangeState(dhcp, STATE_REQUESTING);
|
||
return NETERR_OK;
|
||
}
|
||
|
||
|
||
PRIVATE VOID
|
||
DhcpAddOrRemoveGateways(
|
||
DhcpInfo* dhcp,
|
||
BOOL doDelete
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Set or delete default gateways
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
doDelete - Whether to set or delete gateways
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
#define DEFAULT_METRIC 1
|
||
|
||
{
|
||
UINT i;
|
||
IPADDR addr;
|
||
|
||
if (doDelete) {
|
||
for (i=0; i < dhcp->gatewayCount; i++) {
|
||
addr = dhcp->gateways[i];
|
||
TRACE_("Remove gateway: %s", IPADDRSTR(addr));
|
||
IpRemoveDefaultGateway(addr);
|
||
}
|
||
} else {
|
||
for (i=0; i < dhcp->gatewayCount; i++) {
|
||
addr = dhcp->gateways[i];
|
||
TRACE_("Add gateway: %s", IPADDRSTR(addr));
|
||
IpAddDefaultGateway(addr, DEFAULT_METRIC+i, dhcp->ifp);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
DhcpSetDefaultGateways(
|
||
IfInfo* ifp
|
||
)
|
||
{
|
||
KIRQL irql = RaiseToDpc();
|
||
DhcpInfo* dhcp = ifp->dhcpInfo;
|
||
|
||
if (dhcp) {
|
||
DhcpAddOrRemoveGateways(dhcp, FALSE);
|
||
}
|
||
|
||
LowerFromDpc(irql);
|
||
}
|
||
|
||
|
||
PRIVATE VOID
|
||
DhcpUseOptionParams(
|
||
DhcpInfo* dhcp,
|
||
DhcpOptionParam* param
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Make use of the option parameters received from the server
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
param - Optional parameter values
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL resetGateways;
|
||
|
||
//
|
||
// Remember the originating server address and lease info
|
||
//
|
||
dhcp->dhcpServer = param->dhcpServer;
|
||
|
||
if (param->exptime == DHCPTIME_INFINITE) {
|
||
dhcp->t1time =
|
||
dhcp->t2time =
|
||
dhcp->exptime = DHCPTIME_INFINITE;
|
||
} else {
|
||
dhcp->t1time = dhcp->acqtime + param->t1time;
|
||
dhcp->t2time = dhcp->acqtime + param->t2time;
|
||
dhcp->exptime = dhcp->acqtime + param->exptime;
|
||
}
|
||
|
||
//
|
||
// Set gateways in the IP stack
|
||
//
|
||
if (param->gatewayCount == 0 || dhcp->gatewayCount == 0) {
|
||
resetGateways = TRUE;
|
||
} else {
|
||
UINT oldcnt = dhcp->gatewayCount;
|
||
UINT newcnt = param->gatewayCount;
|
||
UINT i, j;
|
||
|
||
for (i=0; i < newcnt; i++) {
|
||
for (j=0; j < oldcnt; j++)
|
||
if (param->gateways[i] != dhcp->gateways[j]) break;
|
||
if (j < oldcnt) break;
|
||
}
|
||
resetGateways = (i < newcnt);
|
||
}
|
||
|
||
if (resetGateways) {
|
||
//
|
||
// Reset gateways if anything has changed
|
||
// first delete existing gateways
|
||
// then set new gateways
|
||
//
|
||
// NOTE: should we ping the new gateways here?
|
||
//
|
||
DhcpAddOrRemoveGateways(dhcp, TRUE);
|
||
|
||
dhcp->gatewayCount = param->gatewayCount;
|
||
CopyMem(dhcp->gateways,
|
||
param->gateways,
|
||
param->gatewayCount * sizeof(IPADDR));
|
||
|
||
DhcpAddOrRemoveGateways(dhcp, FALSE);
|
||
}
|
||
|
||
dhcp->dnsServerCount = param->dnsServerCount;
|
||
CopyMem(dhcp->dnsServers,
|
||
param->dnsServers,
|
||
param->dnsServerCount * sizeof(IPADDR));
|
||
|
||
strcpy(dhcp->domainName, param->domainName);
|
||
}
|
||
|
||
|
||
PRIVATE NTSTATUS
|
||
DhcpProcessAck(
|
||
DhcpInfo* dhcp,
|
||
IPADDR yiaddr,
|
||
DhcpOptionParam* param
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Process a received DHCPACK packet
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
yiaddr - Offered address
|
||
param - Option parameters
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
VERBOSE_("Received DHCPACK %s", IPADDRSTR(yiaddr));
|
||
VERBOSE_(" from %s", IPADDRSTR(param->dhcpServer));
|
||
|
||
ASSERT(dhcp->state == STATE_INIT_REBOOT ||
|
||
dhcp->state == STATE_REQUESTING ||
|
||
dhcp->state == STATE_RENEWING ||
|
||
dhcp->state == STATE_REBINDING);
|
||
|
||
// Simply sanity check of offered parameters
|
||
if (!DhcpValidateOffer(yiaddr, param))
|
||
return NETERR_PARAM;
|
||
|
||
// Note: We're not checking for address conflicts
|
||
// and just assume the offered address is valid.
|
||
|
||
// If we're currently using a different address, give it up
|
||
if ((dhcp->activeaddr != 0) &&
|
||
(dhcp->activeaddr != yiaddr ||
|
||
dhcp->activemask != param->dhcpmask) ||
|
||
ActiveAutonetAddr(dhcp))
|
||
{
|
||
VERBOSE_("Giving up old IP address %s", IPADDRSTR(dhcp->activeaddr));
|
||
DhcpResetInterface(dhcp);
|
||
}
|
||
|
||
// If we got a new address, set it down in the IP stack
|
||
if (dhcp->activeaddr == 0) {
|
||
IfSetIpAddr(dhcp->ifp, yiaddr, param->dhcpmask);
|
||
DhcpSignalAddrEvent(dhcp);
|
||
dhcp->activeaddr = dhcp->dhcpaddr = yiaddr;
|
||
dhcp->activemask = dhcp->dhcpmask = param->dhcpmask;
|
||
dhcp->flags |= FLAG_ACTIVE_DHCPADDR;
|
||
}
|
||
|
||
VERBOSE_("Accepted IP address: %s", IPADDRSTR(yiaddr));
|
||
VERBOSE_("Lease time: %d / %d / %d", param->t1time, param->t2time, param->exptime);
|
||
|
||
//
|
||
// Set other option parameters
|
||
//
|
||
dhcp->initRetryCount = 0;
|
||
DhcpUseOptionParams(dhcp, param);
|
||
|
||
// We're now in bound state.
|
||
// Set timer to expire at T1 time.
|
||
DhcpChangeState(dhcp, STATE_BOUND);
|
||
return NETERR_OK;
|
||
}
|
||
|
||
|
||
PRIVATE NTSTATUS
|
||
DhcpProcessNak(
|
||
DhcpInfo* dhcp,
|
||
IPADDR dhcpServer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Process a received DHCPNAK packet
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
dhcpServer - The originating server address
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
VERBOSE_("Received DHCPNAK from %s", IPADDRSTR(dhcpServer));
|
||
|
||
ASSERT(dhcp->state == STATE_INIT_REBOOT ||
|
||
dhcp->state == STATE_REQUESTING ||
|
||
dhcp->state == STATE_RENEWING ||
|
||
dhcp->state == STATE_REBINDING);
|
||
|
||
// Barf if we got DHCPNAK from an unexpected server
|
||
if (dhcp->state != STATE_INIT_REBOOT && dhcpServer != dhcp->dhcpServer) {
|
||
WARNING_("Random DHCPNAK from %s?", IPADDRSTR(dhcpServer));
|
||
}
|
||
|
||
// If we're using an address, give it up
|
||
if (dhcp->activeaddr) {
|
||
DhcpResetInterface(dhcp);
|
||
}
|
||
|
||
//
|
||
// Go to INIT state to start over again
|
||
// Send DHCPDISCOVER and ignore error
|
||
//
|
||
DhcpChangeState(dhcp, STATE_INIT);
|
||
return NETERR_OK;
|
||
}
|
||
|
||
|
||
PRIVATE NTSTATUS
|
||
DhcpParseOptionParams(
|
||
DhcpOptionParam* param,
|
||
const BYTE* buf,
|
||
UINT buflen,
|
||
BYTE* overload
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Parse option parameters in a received DHCP packet
|
||
|
||
Arguments:
|
||
|
||
param - Where to store the resulting information
|
||
buf - Points to the option data buffer
|
||
buflen - Buffer length
|
||
overload - Returns the overload option value
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
// NOTE: we're assuming little-endian machine here.
|
||
#define EXTRACT_DWORD_OPTION(_result) \
|
||
if (len != 4) goto exit; \
|
||
if (_result == 0) { \
|
||
_result = (((DWORD) buf[0] << 24) | \
|
||
((DWORD) buf[1] << 16) | \
|
||
((DWORD) buf[2] << 8) | \
|
||
((DWORD) buf[3] )); \
|
||
}
|
||
|
||
#define EXTRACT_IPADDR_OPTION(_result) \
|
||
if (len != 4) goto exit; \
|
||
if (_result == 0) _result = *((IPADDR*) buf)
|
||
|
||
{
|
||
while (buflen) {
|
||
UINT tag, len;
|
||
|
||
// Stop after seeing the 'end' option
|
||
if ((tag = buf[0]) == DHCPOPT_END) break;
|
||
|
||
// Skip the 'pad' option
|
||
if (tag == DHCPOPT_PAD) {
|
||
buf++; buflen--;
|
||
continue;
|
||
}
|
||
|
||
// Is the option length field valid?
|
||
if (buflen < 2 || buflen-2 < (len = buf[1])) goto exit;
|
||
buf += 2; buflen -= 2;
|
||
|
||
// Interpret option data
|
||
switch (tag) {
|
||
case DHCPOPT_DHCP_MESSAGE_TYPE:
|
||
if (len != 1) goto exit;
|
||
if (param->recvMsgType == 0)
|
||
param->recvMsgType = *buf;
|
||
break;
|
||
|
||
case DHCPOPT_SERVERID:
|
||
EXTRACT_IPADDR_OPTION(param->dhcpServer);
|
||
break;
|
||
|
||
case DHCPOPT_SUBNET_MASK:
|
||
EXTRACT_IPADDR_OPTION(param->dhcpmask);
|
||
break;
|
||
|
||
case DHCPOPT_ROUTERS:
|
||
case DHCPOPT_DNS_SERVERS: {
|
||
UINT* pcount;
|
||
IPADDR* parray;
|
||
UINT n;
|
||
|
||
if (len == 0 || len % sizeof(IPADDR) != 0) goto exit;
|
||
if (tag == DHCPOPT_ROUTERS) {
|
||
pcount = ¶m->gatewayCount;
|
||
parray = param->gateways;
|
||
n = MAX_DEFAULT_GATEWAYS * sizeof(IPADDR);
|
||
} else {
|
||
pcount = ¶m->dnsServerCount;
|
||
parray = param->dnsServers;
|
||
n = MAX_DEFAULT_DNSSERVERS * sizeof(IPADDR);
|
||
}
|
||
|
||
if (n > len) n = len;
|
||
if (*pcount == 0) {
|
||
*pcount = n / sizeof(IPADDR);
|
||
CopyMem(parray, buf, n);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case DHCPOPT_DOMAIN_NAME:
|
||
if (len < 1 || len >= sizeof(param->domainName)) goto exit;
|
||
if (param->domainName[0] == 0) {
|
||
CopyMem(param->domainName, buf, len);
|
||
param->domainName[len] = 0;
|
||
}
|
||
break;
|
||
|
||
case DHCPOPT_IPADDR_LEASE_TIME:
|
||
EXTRACT_DWORD_OPTION(param->exptime);
|
||
break;
|
||
|
||
case DHCPOPT_T1_INTERVAL:
|
||
EXTRACT_DWORD_OPTION(param->t1time);
|
||
break;
|
||
|
||
case DHCPOPT_T2_INTERVAL:
|
||
EXTRACT_DWORD_OPTION(param->t2time);
|
||
break;
|
||
|
||
case DHCPOPT_FIELD_OVERLOAD:
|
||
if (len != 1) goto exit;
|
||
if (overload && *overload == 0) *overload = *buf;
|
||
break;
|
||
}
|
||
|
||
buf += len; buflen -= len;
|
||
}
|
||
|
||
return NETERR_OK;
|
||
|
||
exit:
|
||
WARNING_("Invalid option data");
|
||
return NETERR_PARAM;
|
||
}
|
||
|
||
|
||
PRIVATE VOID
|
||
DhcpSelectAutonetAddr(
|
||
DhcpInfo* dhcp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Attempt to select an autonet address
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
IPADDR addr;
|
||
|
||
ASSERT(dhcp->state == STATE_SELECT_AUTOADDR);
|
||
|
||
if (++dhcp->initRetryCount > cfgAutonetAddrRetries) {
|
||
//
|
||
// We tried too many autonet addresses without success.
|
||
// Just give up.
|
||
//
|
||
dhcp->initRetryCount = 0;
|
||
DhcpChangeState(dhcp, STATE_NONE);
|
||
|
||
// Signal that Xnet initialization was completed abnormally
|
||
WARNING_("Failed to pick an autonet address.");
|
||
DhcpSignalAddrEvent(dhcp);
|
||
return;
|
||
}
|
||
|
||
// Generate a random autonet address
|
||
addr = AUTONET_ADDRBASE + XnetRandScaled(AUTONET_ADDRRANGE);
|
||
dhcp->autonetaddr = addr = HTONL(addr);
|
||
|
||
VERBOSE_("Trying autonet address: %s", IPADDRSTR(addr));
|
||
|
||
if (dhcp->ifp->flags & IFFLAG_CONNECTED_BOOT) {
|
||
DhcpCheckAddressConflict(dhcp->ifp, addr);
|
||
dhcp->retries = 0;
|
||
DhcpComputeTimeout(dhcp);
|
||
} else {
|
||
//
|
||
// NOTE: if net cable isn't connected at boot time,
|
||
// there is no point trying to check for address conflicts.
|
||
//
|
||
dhcp->retries = cfgConflictCheckRetries;
|
||
DhcpHandleTimeout(dhcp);
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DhcpGetDefaultDnsServers(
|
||
IfInfo* ifp,
|
||
CHAR* domainName,
|
||
UINT namelen,
|
||
IPADDR* serverAddrs,
|
||
UINT* serverCnt
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the default DNS server information
|
||
associated with the specified interface
|
||
|
||
Arguments:
|
||
|
||
ifp - Points to an interface structure
|
||
domainName - Buffer for receiving the default domain name
|
||
namelen - Size of the domain name buffer
|
||
serverAddrs - Buffer for receiving the default server addresses
|
||
serverCnt - Number of default servers
|
||
On entry, it specifies the size of the server address buffer
|
||
On return, it contains the actual number of default servers
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
DhcpInfo* dhcp = ifp->dhcpInfo;
|
||
UINT count;
|
||
|
||
RUNS_AT_DISPATCH_LEVEL
|
||
if (!dhcp) return NETERR_PARAM;
|
||
|
||
//
|
||
// Return the default domain name
|
||
//
|
||
count = dhcp->domainName ? strlen(dhcp->domainName) : 0;
|
||
if (namelen <= count) return NETERR_MEMORY;
|
||
if (count) {
|
||
CopyMem(domainName, dhcp->domainName, count);
|
||
}
|
||
domainName[count] = 0;
|
||
|
||
//
|
||
// Return the default DNS server addresses
|
||
//
|
||
if (dhcp->dnsServerCount) {
|
||
count = min(*serverCnt, dhcp->dnsServerCount);
|
||
*serverCnt = count;
|
||
CopyMem(serverAddrs, dhcp->dnsServers, count*sizeof(IPADDR));
|
||
} else {
|
||
*serverCnt = 0;
|
||
}
|
||
|
||
return NETERR_OK;
|
||
}
|
||
|
||
|
||
PRIVATE IPADDR
|
||
DhcpReadConfigIpAddr(
|
||
ULONG index
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Wrapper function to read an IP address value
|
||
from the xbox configuration sector
|
||
|
||
Arguments:
|
||
|
||
index - Specifies the address of interest
|
||
|
||
Return Value:
|
||
|
||
IP address value, 0 if there is an error
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG type, size, addr;
|
||
INT err = XQueryValue(index, &type, (VOID*) &addr, IPADDRLEN, &size);
|
||
|
||
// If the query call failed, return 0 address.
|
||
if (err != NO_ERROR || type != REG_DWORD || size != IPADDRLEN) {
|
||
VERBOSE_("XQueryValue(%d) failed: %d, %d, %d", index, err, type, size);
|
||
return 0;
|
||
}
|
||
|
||
return addr;
|
||
}
|
||
|
||
|
||
PRIVATE VOID
|
||
DhcpLoadConfigInfo(
|
||
DhcpInfo* dhcp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Load persistent DHCP configuration parameters
|
||
|
||
Arguments:
|
||
|
||
dhcp - Points to the DHCP structure
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Read the static IP address value.
|
||
// Return right away if none is set.
|
||
//
|
||
dhcp->activeaddr = DhcpReadConfigIpAddr(XC_ONLINE_IP_ADDRESS);
|
||
if (dhcp->activeaddr == 0) return;
|
||
|
||
//
|
||
// Read the subnet mask value.
|
||
// If none is set, generate a default mask.
|
||
//
|
||
dhcp->activemask = DhcpReadConfigIpAddr(XC_ONLINE_SUBNET_ADDRESS);
|
||
if (dhcp->activemask == 0) {
|
||
dhcp->activemask = XnetGetDefaultSubnetMask(dhcp->activeaddr);
|
||
}
|
||
|
||
//
|
||
// Read the default gateway address.
|
||
//
|
||
dhcp->gateways[0] = DhcpReadConfigIpAddr(XC_ONLINE_DEFAULT_GATEWAY_ADDRESS);
|
||
if (dhcp->gateways[0] != 0)
|
||
dhcp->gatewayCount = 1;
|
||
}
|
||
|
||
|
||
DWORD
|
||
DhcpGetActiveAddressType(
|
||
IfInfo* ifp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine how we obtained our currently active IP address
|
||
|
||
Arguments:
|
||
|
||
ifp - Points to the interface structure
|
||
|
||
Return Value:
|
||
|
||
Flags (see winsockx.h)
|
||
|
||
--*/
|
||
|
||
{
|
||
DhcpInfo* dhcp;
|
||
DWORD flags = 0;
|
||
|
||
RUNS_AT_DISPATCH_LEVEL
|
||
|
||
if ((dhcp = ifp->dhcpInfo) != NULL) {
|
||
if (ActiveDhcpAddr(dhcp)) {
|
||
flags = XNET_ADDR_DHCP;
|
||
} else if (ActiveAutonetAddr(dhcp)) {
|
||
flags = XNET_ADDR_AUTOIP;
|
||
} else if (dhcp->state == STATE_STATIC_ADDR) {
|
||
flags = XNET_ADDR_STATIC;
|
||
}
|
||
|
||
if (dhcp->gatewayCount)
|
||
flags |= XNET_HAS_GATEWAY;
|
||
}
|
||
|
||
return flags;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DhcpWaitForAddress(
|
||
IfInfo* ifp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Wait for address acquisition process to complete
|
||
|
||
Arguments:
|
||
|
||
ifp - Points to the interface structure
|
||
|
||
Return Value:
|
||
|
||
Status code
|
||
|
||
--*/
|
||
|
||
{
|
||
DhcpInfo* dhcp = ifp->dhcpInfo;
|
||
NTSTATUS status;
|
||
|
||
if (dhcp) {
|
||
status = WaitKernelEventObject(&dhcp->addrEvent, 0);
|
||
|
||
// Check if the acquisition process failed
|
||
if (status == NETERR_OK && dhcp->state == STATE_NONE)
|
||
status = NETERR_NETDOWN;
|
||
} else
|
||
status = NETERR(WSASYSNOTREADY);
|
||
|
||
return status;
|
||
}
|
||
|