2020-09-30 17:17:25 +02:00

1798 lines
49 KiB
C++

// ---------------------------------------------------------------------------------------
// ipdhcp.cpp
//
// Copyright (C) Microsoft Corporation
// ---------------------------------------------------------------------------------------
#include "xnp.h"
#include "xnver.h"
DefineTag(dhcp, 0);
#ifdef XNET_FEATURE_DHCP
//
// DHCP message format
// NOTE: default the options field to 64 bytes
// which is the size of BOOTP vendor-specified area
//
typedef struct _DhcpMessage {
BYTE _op; // message type
BYTE _htype; // hareware address type
BYTE _hlen; // hardware address length
BYTE _hops; // relay hops
DWORD _xid; // transaction ID
WORD _secs; // seconds since address acquisition process began
WORD _flags; // flags
CIpAddr _ciaddr; // client IP address
CIpAddr _yiaddr; // "your" (client) IP address
CIpAddr _siaddr; // server IP address
CIpAddr _giaddr; // relay agent IP address
BYTE _chaddr[16]; // client hardware address
BYTE _sname[64]; // optional server hostname
BYTE _file[128]; // boot filename
BYTE _options[64]; // optional parameters (variable length)
} DhcpMessage;
//
// Our default DHCP packet buffer is 576 bytes
//
#define BOOTP_MESSAGE_SIZE sizeof(DhcpMessage)
#define DEFAULT_DHCP_BUFSIZE 576
#define DHCPHDRLEN offsetof(DhcpMessage, _options)
//
// Minimum and maximum DHCP hardware address length
//
#define MIN_DHCP_HWADDRLEN 1
#define MAX_DHCP_HWADDRLEN 16
//
// BOOTP message type constants
//
#define BOOTREQUEST 1
#define BOOTREPLY 2
//
// Hardware type constants
//
#define HWTYPE_10MB_ETHERNET 1
#define HWTYPE_PPP 8
//
// Constants for DHCP_MESSAGE.flags
//
#define DHCP_BROADCAST 0x8000
//
// DHCP message type constants
//
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPDECLINE 4
#define DHCPACK 5
#define DHCPNAK 6
#define DHCPRELEASE 7
#define DHCPINFORM 8
//
// DHCP option tag constants
//
// Fixed-length options
#define DHCPOPT_PAD 0
#define DHCPOPT_END 255
// Standard options
#define DHCPOPT_SUBNET_MASK 1
#define DHCPOPT_TIME_OFFSET 2
#define DHCPOPT_ROUTERS 3
#define DHCPOPT_TIME_SERVERS 4
#define DHCPOPT_IEN116_SERVERS 5
#define DHCPOPT_DNS_SERVERS 6
#define DHCPOPT_LOG_SERVERS 7
#define DHCPOPT_COOKIE_SERVERS 8
#define DHCPOPT_LPR_SERVERS 9
#define DHCPOPT_IMPRESS_SERVERS 10
#define DHCPOPT_RLP_SERVERS 11
#define DHCPOPT_HOST_NAME 12
#define DHCPOPT_BOOT_FILESIZE 13
#define DHCPOPT_DUMP_FILE 14
#define DHCPOPT_DOMAIN_NAME 15
#define DHCPOPT_SWAP_SERVER 16
#define DHCPOPT_ROOT_PATH 17
#define DHCPOPT_EXTENSIONS_PATH 18
// IP parameters (per host)
#define DHCPOPT_ENABLE_FORWARD 19
#define DHCPOPT_NONLOCAL_SRCROUTING 20
#define DHCPOPT_POLICY_FILTER 21
#define DHCPOPT_MAX_REASSEMBLY_SIZE 22
#define DHCPOPT_DEFAULT_IP_TTL 23
#define DHCPOPT_PMTU_AGING_TIMEOUT 24
#define DHCPOPT_PMTU_PLATEAU_TABLE 25
// IP parameters (per interface)
#define DHCPOPT_INTERFACE_MTU 26
#define DHCPOPT_ALL_SUBNETS_LOCAL 27
#define DHCPOPT_BROADCAST_ADDRESS 28
#define DHCPOPT_MASK_DISCOVERY 29
#define DHCPOPT_MASK_SUPPLIER 30
#define DHCPOPT_ROUTER_DISCOVERY 31
#define DHCPOPT_SOLICIT_ROUTER_ADDR 32
#define DHCPOPT_STATIC_ROUTES 33
// Link layer parameters
#define DHCPOPT_TRAILER_ENCAPS 34
#define DHCPOPT_ARP_CACHE_TIMEOUT 35
#define DHCPOPT_ETHERNET_ENCAPS 36
// TCP parameters
#define DHCPOPT_DEFAULT_TCP_TTL 37
#define DHCPOPT_KEEPALIVE_INTERVAL 38
#define DHCPOPT_KEEPALIVE_GARBAGE 39
// Application parameters
#define DHCPOPT_NIS_DOMAIN 40
#define DHCPOPT_NIS_SERVERS 41
#define DHCPOPT_NTP_SERVERS 42
// DHCP extensions
#define DHCPOPT_REQUESTED_CIpAddr 50
#define DHCPOPT_IPADDR_LEASE_TIME 51
#define DHCPOPT_FIELD_OVERLOAD 52
#define DHCPOPT_TFTP_SERVER_NAME 66
#define DHCPOPT_BOOT_FILENAME 67
#define DHCPOPT_DHCP_MESSAGE_TYPE 53
#define DHCPOPT_SERVERID 54
#define DHCPOPT_PARAM_REQUEST_LIST 55
#define DHCPOPT_MESSAGE 56
#define DHCPOPT_MAX_DHCP_MESSAGESIZE 57
#define DHCPOPT_T1_INTERVAL 58
#define DHCPOPT_T2_INTERVAL 59
#define DHCPOPT_VENDOR_CLASSID 60
#define DHCPOPT_CLIENTID 61
// Vendor-specific information
#define DHCPOPT_VENDOR_INFO 43
#define DHCPOPT_NETBIOS_NAMESERVERS 44
#define DHCPOPT_NETBIOS_DATASERVERS 45
#define DHCPOPT_NETBIOS_NODETYPE 46
#define DHCPOPT_NETBIOS_SCOPE 47
#define DHCPOPT_X11_FONTSERVERS 48
#define DHCPOPT_X11_DISPLAYSERVERS 49
#define DHCPOPT_NIS2_DOMAIN 64
#define DHCPOPT_NIS2_SERVERS 65
#define DHCPOPT_MOBILEIP_HOME_AGENTS 68
#define DHCPOPT_SMTP_SERVERS 69
#define DHCPOPT_POP3_SERVERS 70
#define DHCPOPT_NNTP_SERVERS 71
#define DHCPOPT_WWW_SERVERS 72
#define DHCPOPT_FINGER_SERVERS 73
#define DHCPOPT_IRC_SERVERS 74
#define DHCPOPT_STREETTALK_SERVERS 75
#define DHCPOPT_STREETTALKDA_SERVERS 76
//
// DHCP magic cookie: 99, 130, 83, 99
//
//#define DHCPCOOKIELEN 4
//extern const BYTE DhcpMagicCookie[DHCPCOOKIELEN];
//
// DHCP time value (in seconds)
//
// NOTE: We use 32-bit unsigned interface to represent
// the number of seconds ellapsed since the start of 1/1/2000.
// This should last until year 2136 before overflowing.
//
#define DHCPTIME_INFINITE 0xffffffff
//
// Autonet address range: 169.254/16
// don't use the first 256 and the last 256 addresses
//
#define AUTONET_ADDRMASK 0xffff0000
#define AUTONET_ADDRBASE 0xa9fe0100
#define AUTONET_ADDRRANGE 0x0000fcff
//
// States for an interface
//
enum {
STATE_NONE, // temporary wait state after startup
STATE_STATIC_ADDR, // statically assigned address
STATE_INIT, // sending discover
STATE_REQUESTING, // waiting for DHCPACK after selecting
STATE_INIT_REBOOT, // rebooting with valid lease
STATE_BOUND, // bound with valid address lease
STATE_RENEWING, // renewing after T1 expires
STATE_REBINDING, // rebinding after T2 expires
STATE_DECLINING, // temporary wait state after sending decline
STATE_INIT_AUTOADDR, // waiting for timer to kick off autonet
STATE_SELECT_AUTOADDR // checking autonet address conflicts
};
//
// Constants for the DhcpInfo.flags field:
//
#define FLAG_ACTIVE_DHCPADDR 0x00010000
#define FLAG_ACTIVE_AUTONETADDR 0x00020000
#define FLAG_ACTIVE_NOADDR 0x00040000
#define FLAG_ACTIVE_ADDRMASK (FLAG_ACTIVE_DHCPADDR | FLAG_ACTIVE_AUTONETADDR | FLAG_ACTIVE_NOADDR)
BOOL CXnIp::ActiveDhcpAddr()
{
return (_flags & FLAG_ACTIVE_DHCPADDR);
}
BOOL CXnIp::ActiveAutonetAddr()
{
return (_flags & FLAG_ACTIVE_AUTONETADDR);
}
BOOL CXnIp::ActiveStaticAddr()
{
return(_state == STATE_STATIC_ADDR);
}
BOOL CXnIp::ActiveNoAddr()
{
return(_flags & FLAG_ACTIVE_NOADDR);
}
//
// Dump DHCP message in the debugger
//
#if DBG
VOID DhcpDumpMessage(DhcpMessage* msg, UINT msglen);
#else
#define DhcpDumpMessage(msg, msglen)
#endif
// ---------------------------------------------------------------------------------------
//
// First 4 bytes of the options field in a DHCP message
// must match the following magic cookie
//
const BYTE DhcpMagicCookie[] = { 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;
// Check to see if we're forced to use autonet address
// (without trying to find DHCP servers)
INLINE
BOOL CXnIp::DhcpForceAutonet(CIpAddr addr, CIpAddr mask)
{
ICHECK(IP, USER|UDPC|SDPC);
return (mask == HTONL(AUTONET_ADDRMASK)) &&
(addr & mask) == (HTONL(AUTONET_ADDRBASE) & mask);
}
NTSTATUS CXnIp::DhcpInit()
{
ICHECK(IP, USER);
_state = STATE_NONE;
_timerDhcp.Init((PFNTIMER)DhcpTimer);
_dwXidNext = RandLong();
return(NETERR_OK);
}
INT CXnIp::DhcpConfig(const XNetConfigParams * pxncp)
{
ICHECK(IP, UDPC);
//@@@ Cannot be reconfigured. Need DHCP rewrite.
_activeaddr = pxncp->ina.s_addr;
if (_activeaddr != 0)
{
_activemask = pxncp->inaMask.s_addr;
if (_activemask == 0 || !_activemask.IsValidMask())
_activemask = _activeaddr.DefaultMask();
_options._gatewayCount = 0;
if (pxncp->inaGateway.s_addr != 0)
{
_options._gateways[_options._gatewayCount] = pxncp->inaGateway.s_addr;
_options._gatewayCount += 1;
}
_options._dnsServerCount = 0;
if (pxncp->inaDnsPrimary.s_addr != 0)
{
_options._dnsServers[_options._dnsServerCount] = pxncp->inaDnsPrimary.s_addr;
_options._dnsServerCount += 1;
}
if (pxncp->inaDnsSecondary.s_addr != 0)
{
_options._dnsServers[_options._dnsServerCount] = pxncp->inaDnsSecondary.s_addr;
_options._dnsServerCount += 1;
}
}
if (!TestInitFlag(INITF_CONNECTED_BOOT))
{
TraceSz(Warning, "Ethernet cable not plugged in. No IP address acquired.");
_flags = (_flags & ~FLAG_ACTIVE_ADDRMASK) | FLAG_ACTIVE_NOADDR;
}
#if defined(XNET_FEATURE_INSECURE) && !defined(XNET_FEATURE_SG)
else if (!(cfgFlags & XNET_STARTUP_BYPASS_SECURITY))
{
// When operating in secure mode don't acquire an IP address so we behave
// more like xnets.lib.
_flags = (_flags & ~FLAG_ACTIVE_ADDRMASK) | FLAG_ACTIVE_NOADDR;
}
#endif
else if (_activeaddr != 0 && !DhcpForceAutonet(_activeaddr, _activemask))
{
// If we're using static address, inform the IP stack
IpSetAddress(_activeaddr, _activemask);
DhcpSetDefaultGateways();
DhcpChangeState(STATE_STATIC_ADDR);
}
else if ((cfgFlags & XNET_STARTUP_BYPASS_DHCP) || _activeaddr != 0)
{
TraceSz(Warning, "Forcing autonet...");
_activeaddr = _activemask = 0;
DhcpChangeState(STATE_INIT_AUTOADDR);
}
else
{
// Obtain address via DHCP or Autonet. Be quiet for a while after startup.
DhcpTimerSetRelative(0, 0);
}
return(0);
}
void CXnIp::DhcpTerm()
{
ICHECK(IP, UDPC);
TimerSet(&_timerDhcp, TIMER_INFINITE);
}
void CXnIp::DhcpRecv(CPacket * ppkt, CUdpHdr * pUdpHdr, UINT msglen)
{
ICHECK(IP, UDPC|SDPC);
DhcpMessage * msg = (DhcpMessage *)(pUdpHdr + 1);
CDhcpOptions * dhcpParams = NULL;
BYTE* option;
BYTE overload = 0;
NTSTATUS status = NETERR_NOTIMPL;
if (msglen < DHCPHDRLEN)
{
TraceSz1(pktRecv, "[DISCARD] DHCP packet is too short (%d)", msglen);
return;
}
if (memcmp(msg->_chaddr, _ea._ab, sizeof(CEnetAddr)) != 0)
{
TraceSz(pktRecv, "[DISCARD] DHCP packet not meant for me");
return;
}
if ( msg->_op != BOOTREPLY
|| msg->_htype != HWTYPE_10MB_ETHERNET
|| msg->_hlen != sizeof(CEnetAddr)
|| NTOHL(msg->_xid) != _xid)
{
TraceSz4(pktWarn, "[DISCARD] DHCP packet is invalid (%d,%d,%d,%d)",
msg->_op != BOOTREPLY,
msg->_htype != HWTYPE_10MB_ETHERNET,
msg->_hlen != sizeof(CEnetAddr),
NTOHL(msg->_xid) != _xid);
return;
}
DhcpDumpMessage(msg, msglen);
dhcpParams = (CDhcpOptions *)PoolAllocZ(sizeof(*dhcpParams), PTAG_CDhcpOptions);
if (!dhcpParams)
{
TraceSz(pktWarn, "[DISCARD] Out of memory allocating CDhcpOptions");
status = NETERR_MEMORY;
goto exit;
}
//
// Skip the DHCP magic cookie
//
option = msg->_options;
msglen -= DHCPHDRLEN;
if ( msglen < sizeof(DhcpMagicCookie)
|| memcmp(option, DhcpMagicCookie, sizeof(DhcpMagicCookie)) != 0)
{
TraceSz1(pktWarn, "[DISCARD] DHCP packet has invalid magic cookie (%d)", msglen);
goto exit;
}
msglen -= sizeof(DhcpMagicCookie);
option += sizeof(DhcpMagicCookie);
//
// Parse the regular options
//
status = DhcpParseOptionParams(dhcpParams, option, msglen, &overload);
if (!NT_SUCCESS(status))
{
TraceSz(pktWarn, "[DISCARD] DHCP regular options are invalid");
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))
{
TraceSz(pktWarn, "[DISCARD] DHCP file options are invalid");
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))
{
TraceSz(pktWarn, "[DISCARD] DHCP sname options are invalid");
goto exit;
}
}
//
// The received packet must have a server identifier option
//
if (dhcpParams->_dhcpServer == 0)
{
TraceSz(pktWarn, "[DISCARD] DHCP packet lacks server identifier option");
status = NETERR_PARAM;
goto exit;
}
//
// Now that we've parsed the option data,
// use it appropriately
//
status = NETERR_NOTIMPL;
switch (_state) {
case STATE_INIT:
// Expecting DHCPOFFER...
if (dhcpParams->_recvMsgType == DHCPOFFER)
status = DhcpProcessOffer(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(msg->_yiaddr, dhcpParams);
else if (dhcpParams->_recvMsgType == DHCPNAK)
status = DhcpProcessNak(dhcpParams->_dhcpServer);
break;
}
exit:
if (!NT_SUCCESS(status) && status != NETERR_NOTIMPL)
{
TraceSz1(pktWarn, "[DISCARD] DhcpRecv failed: %08lX", status);
}
PoolFree(dhcpParams);
}
void CXnIp::DhcpNotifyAddressConflict()
{
ICHECK(IP, UDPC|SDPC);
if (_state == STATE_SELECT_AUTOADDR)
{
// the last autonet address we chose was no good
// so we need to find another one
DhcpSelectAutonetAddr();
}
else
{
TraceSz1(Warning, "!!! Address conflict: %s", _activeaddr.Str());
}
}
//
// Number of seconds from start of 1601 to start of 2000
//
static const LONGLONG StartOf2000 = 0x2ee7dd480;
CXnIp::DHCPTIME CXnIp::DhcpTime()
{
ICHECK(IP, UDPC|SDPC);
LARGE_INTEGER currentTime;
// Get the current UTC time
// = number of 100 nanoseconds since 1/1/1601
KeQuerySystemTime(&currentTime);
// Return the number of seconds since the start of 2000
return (DHCPTIME) (currentTime.QuadPart / 10000000 - StartOf2000);
}
void CXnIp::DhcpTimerSetRelative(UINT minWait, UINT maxWait)
{
ICHECK(IP, UDPC|SDPC);
TimerSet(&_timerDhcp, TimerTick() + (minWait + RandScaled(maxWait - minWait)) * TICKS_PER_SECOND);
}
void CXnIp::DhcpTimerSetAbsolute(DHCPTIME dhcptime)
{
ICHECK(IP, UDPC|SDPC);
DWORD dwTick = TIMER_INFINITE;
if (dhcptime != DHCPTIME_INFINITE)
{
DHCPTIME dhcptimeNow = DhcpTime();
if (dhcptime > dhcptimeNow)
dwTick = TimerTick() + (dhcptime - dhcptimeNow) * TICKS_PER_SECOND;
else
dwTick = TimerTick();
}
TimerSet(&_timerDhcp, dwTick);
}
void CXnIp::DhcpComputeTimeout()
{
ICHECK(IP, UDPC|SDPC);
UINT minWait, maxWait;
switch (_state)
{
case STATE_INIT:
if (ActiveAutonetAddr()) {
//
// Special case: we're currently using an Autonet address
// and we're sending out periodic DHCPDISCOVER packets.
//
// DhcpSetIfTimerRelative(
// cfgAutonetDhcpCheckInterval,
// cfgAutonetDhcpCheckInterval);
// NOTE: Non-standard behavior!!!
// We do not send out DHCPDISCOVER messages
// while we're using an active AutoNet address.
DhcpTimerSetAbsolute(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 << _retries;
if (minWait > cfgDhcpRetryMaxTimeoutInSeconds)
minWait = cfgDhcpRetryMaxTimeoutInSeconds;
minWait -= 1;
maxWait = minWait + 2;
DhcpTimerSetRelative(minWait, maxWait);
break;
case STATE_BOUND:
//
// timeout when T1 expires
//
DhcpTimerSetAbsolute(_options._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 = (_state == STATE_RENEWING) ? _options._t2time : _options._exptime;
if (t1 < t2) {
minWait = (t2-t1) / 2;
if (minWait >= cfgDhcpRenewMinTimeoutInSeconds)
t1 += minWait;
else {
t1 += cfgDhcpRenewMinTimeoutInSeconds;
if (t1 > t2) t1 = t2;
}
}
DhcpTimerSetAbsolute(t1);
}
break;
case STATE_DECLINING:
//
// Wait 10 seconds after sending DHCP decline
//
DhcpTimerSetRelative(cfgDhcpDeclineWaitInSeconds, cfgDhcpDeclineWaitInSeconds);
break;
case STATE_SELECT_AUTOADDR:
//
// Wait ~2 seconds for address conflict detection
//
DhcpTimerSetRelative(cfgAutoIpRexmitTimeoutInSeconds, cfgAutoIpRexmitTimeoutInSeconds);
break;
default:
// Should not happen - shut off the timer just in case
Assert(FALSE);
case STATE_NONE:
case STATE_STATIC_ADDR:
DhcpTimerSetAbsolute(DHCPTIME_INFINITE);
break;
}
}
void CXnIp::DhcpTimer(CTimer * pt)
{
ICHECK(IP, UDPC|SDPC);
switch (_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(_dhcpaddr ? STATE_INIT_REBOOT : STATE_INIT);
break;
case STATE_INIT:
//
// We're sending DHCPDISCOVER messages
//
if (_retries >= cfgDhcpRetryCount && !ActiveAutonetAddr())
{
//
// Too many retries, give up and
// start Autonet address acquisition process
//
TraceSz(dhcp, "Couldn't discover DHCP server, trying autonet...");
DhcpChangeState(STATE_SELECT_AUTOADDR);
}
else
{
//
// Send out another DHCPDISCOVER packet
//
TraceSz(dhcp, "Retransmit DHCPDISCOVER");
DhcpSendDiscover();
}
break;
case STATE_INIT_REBOOT:
case STATE_REQUESTING:
//
// We're sending DHCPREQUEST
//
if (_retries >= cfgDhcpRetryCount) {
//
// Too many retries, go back to INIT state
//
TraceSz(dhcp, _state == STATE_INIT_REBOOT ?
"Failed to reuse a prior lease" :
"No ack for an offered address");
DhcpChangeState(STATE_INIT);
} else {
//
// Send out another DHCPREQUEST packet
//
TraceSz(dhcp, "Retransmit DHCPREQUEST");
DhcpSendRequest();
}
break;
case STATE_DECLINING:
//
// We just finished waiting after sending DHCPDECLINE
//
DhcpChangeState(STATE_INIT);
break;
case STATE_BOUND:
//
// T1 expired, start the renewing process
//
TraceSz(dhcp, "Switching to RENEWING state");
DhcpChangeState(STATE_RENEWING);
break;
case STATE_RENEWING:
//
// We're trying to renew a valid address.
// If T2 expired, start the rebinding process.
//
if (DhcpTime() >= _options._t2time) {
TraceSz(dhcp, "Switching to REBINDING state");
DhcpChangeState(STATE_REBINDING);
} else {
// Send out another DHCPREQUEST
TraceSz(dhcp, "Retransmit DHCPREQUEST");
DhcpSendRequest();
}
break;
case STATE_REBINDING:
//
// We're trying to rebind a valid lease
// did our lease expire?
//
if (DhcpTime() >= _options._exptime) {
// Inform IP stack to discard the active address
TraceSz(dhcp, "Address lease expired - start over");
DhcpResetInterface();
// Too bad, go back to INIT state
DhcpChangeState(STATE_INIT);
} else {
// Send out another DHCPREQUEST
TraceSz(dhcp, "Retransmit DHCPREQUEST");
DhcpSendRequest();
}
break;
case STATE_INIT_AUTOADDR:
DhcpChangeState(STATE_SELECT_AUTOADDR);
break;
case STATE_SELECT_AUTOADDR:
if (++_retries > cfgAutoIpRetriesPerAttempt)
{
// We've successfully picked an autonet address.
TraceSz1(dhcp, "Selected autonet address: %s", _autonetaddr.Str());
_activeaddr = _autonetaddr;
_activemask = HTONL(AUTONET_ADDRMASK);
_flags |= FLAG_ACTIVE_AUTONETADDR;
IpSetAddress(_activeaddr, _activemask);
// Switch to INIT state to continue looking
// for a DHCP server
_initRetryCount = 0;
DhcpChangeState(STATE_INIT);
}
else
{
// No response to our previous ARP request.
// Try again just to be sure.
EnetXmitArp(_autonetaddr);
DhcpComputeTimeout();
}
break;
default:
TraceSz(dhcp, "Unexpected timeout");
DhcpTimerSetAbsolute(DHCPTIME_INFINITE);
break;
}
}
void CXnIp::DhcpChangeState(INT state)
{
ICHECK(IP, UDPC|SDPC);
INT oldstate = _state;
_state = state;
_retries = 0;
// Assign a new transaction ID for the next outgoing message.
_xid = _dwXidNext++;
switch (state) {
case STATE_INIT_REBOOT:
case STATE_REQUESTING:
case STATE_RENEWING:
case STATE_REBINDING:
// Send out DHCPREQUEST
DhcpSendRequest();
break;
case STATE_INIT:
if (oldstate == STATE_SELECT_AUTOADDR) {
// We just selected an autonet address.
// Continue to look for a DHCP server.
DhcpComputeTimeout();
}
else if (++_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.
TraceSz(Warning, "Couldn't get a valid DHCP address after many tries");
DhcpChangeState(STATE_NONE);
_flags = (_flags & ~FLAG_ACTIVE_ADDRMASK) | FLAG_ACTIVE_NOADDR;
} else {
// Send out DHCPDISCOVER
DhcpSendDiscover();
}
break;
case STATE_BOUND:
DhcpComputeTimeout();
TraceSz1(dhcp, "Sleep %d seconds till renewal...", _options._t1time - DhcpTime());
break;
case STATE_INIT_AUTOADDR:
DhcpTimerSetAbsolute(0);
break;
case STATE_SELECT_AUTOADDR:
_initRetryCount = 0;
DhcpSelectAutonetAddr();
break;
case STATE_NONE:
case STATE_STATIC_ADDR:
DhcpTimerSetAbsolute(DHCPTIME_INFINITE);
break;
}
}
void CXnIp::DhcpResetInterface()
{
ICHECK(IP, UDPC|SDPC);
IpSetAddress(0, 0);
_flags &= ~FLAG_ACTIVE_ADDRMASK;
if (_state != STATE_STATIC_ADDR)
_activeaddr = 0;
}
NTSTATUS CXnIp::DhcpSendMessage(CPacket * ppkt, UINT msglen, BOOL broadcast)
{
ICHECK(IP, UDPC|SDPC);
ppkt->SetCb(ppkt->GetCb() - DEFAULT_DHCP_BUFSIZE + msglen);
CUdpHdr * pUdpHdr = ppkt->GetUdpHdr();
DhcpMessage * msg = (DhcpMessage *)(pUdpHdr + 1);
// Debug trace
DhcpDumpMessage(msg, msglen);
// Set broadcast flag if necessary
if (!_activeaddr || ActiveAutonetAddr())
msg->_flags |= HTONS(DHCP_BROADCAST);
pUdpHdr->_ipportSrc = DHCP_CLIENT_PORT;
pUdpHdr->_ipportDst = DHCP_SERVER_PORT;
pUdpHdr->_wLen = HTONS(msglen + sizeof(CUdpHdr));
// Call the IP layer to send out the packet
IpFillAndXmit(ppkt, broadcast ? IPADDR_BROADCAST : _options._dhcpServer, IPPROTOCOL_UDP);
// Calculate the timeout value
_retries++;
DhcpComputeTimeout();
return(NETERR_OK);
}
//
// Append the 'parameter request list' option
// !!! we assume the data buffer is large enough.
//
BYTE * DhcpAppendParamReqList(BYTE * option)
{
*option++ = DHCPOPT_PARAM_REQUEST_LIST;
*option++ = 4;
*option++ = DHCPOPT_SUBNET_MASK;
*option++ = DHCPOPT_ROUTERS;
*option++ = DHCPOPT_DNS_SERVERS;
*option++ = DHCPOPT_DOMAIN_NAME;
return(option);
}
//
// Append an option whose value is a DWORD
//
BYTE * DhcpAppendDWordOption(BYTE * option, INT tag, DWORD val)
{
option[0] = (BYTE) tag;
option[1] = sizeof(DWORD);
option += 2;
memcpy(option, &val, sizeof(DWORD));
return option + sizeof(DWORD);
}
//
// Append the 'address lease time' option
//
BYTE * CXnIp::DhcpAppendLeaseTimeOption(BYTE * option)
{
ICHECK(IP, UDPC|SDPC);
DWORD dwLeaseTime = cfgDhcpDefaultLeaseTimeInDays * (24 * 60 * 60);
if (dwLeaseTime == 0)
return option;
return DhcpAppendDWordOption(option, DHCPOPT_IPADDR_LEASE_TIME, HTONL(dwLeaseTime));
}
BYTE * CXnIp::DhcpFillMessageHeader(BYTE * buf, INT msgtype)
{
ICHECK(IP, UDPC|SDPC);
DhcpMessage* msg = (DhcpMessage*) buf;
BYTE* option = msg->_options;
BYTE hwtype;
memset(buf, 0, DEFAULT_DHCP_BUFSIZE);
msg->_op = BOOTREQUEST;
msg->_htype = hwtype = HWTYPE_10MB_ETHERNET;
msg->_hlen = sizeof(CEnetAddr);
memcpy(msg->_chaddr, _ea._ab, sizeof(CEnetAddr));
//
// Fill in the transaction ID field
// NOTE: We reuse the same XID for retransmissions.
//
msg->_xid = HTONL(_xid);
// Number of seconds since we started the address
// acquisition process.
msg->_secs = (WORD) HTONS((WORD)_secsSinceStart);
// Start with the magic cookie
memcpy(option, DhcpMagicCookie, sizeof(DhcpMagicCookie));
option += sizeof(DhcpMagicCookie);
// 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) (sizeof(CEnetAddr)+1);
option[2] = hwtype;
option += 3;
memcpy(option, _ea._ab, sizeof(CEnetAddr));
option += sizeof(CEnetAddr);
return option;
}
void CXnIp::DhcpComputeSecsSinceStart()
{
ICHECK(IP, UDPC|SDPC);
DHCPTIME now = DhcpTime();
if (_retries == 0)
_acqtime = now;
_secsSinceStart = now - _acqtime;
}
//
// Common prolog and epilog for SendDhcpXXX functions
//
#define SEND_DHCP_MESSAGE_PROLOG() \
DhcpMessage* msg; \
BYTE* buf; \
BYTE* option; \
CPacket * pkt = PacketAlloc(PTAG_CDhcpPacket, PKTF_TYPE_UDP|PKTF_XMIT_INSECURE|PKTF_POOLALLOC, DEFAULT_DHCP_BUFSIZE); \
if (!pkt) return NETERR_MEMORY; \
buf = (BYTE *)pkt->GetUdpHdr() + sizeof(CUdpHdr); \
msg = (DhcpMessage*) buf
#define SEND_DHCP_MESSAGE_RETURN(_bcast) \
return DhcpSendMessage(pkt, option - buf, _bcast)
NTSTATUS CXnIp::DhcpSendDiscover()
{
ICHECK(IP, UDPC|SDPC);
SEND_DHCP_MESSAGE_PROLOG();
Assert(_state == STATE_INIT);
DhcpComputeSecsSinceStart();
// Fill in common header fields
option = DhcpFillMessageHeader(buf, DHCPDISCOVER);
// Fill in the parameter request list
option = DhcpAppendParamReqList(option);
// Fill in requested ip address and lease time option
if (_dhcpaddr)
option = DhcpAppendDWordOption(option, DHCPOPT_REQUESTED_CIpAddr, _dhcpaddr);
// Fill in the lease time option
option = DhcpAppendLeaseTimeOption(option);
*option++ = DHCPOPT_END;
// Emit the message
SEND_DHCP_MESSAGE_RETURN(TRUE);
}
NTSTATUS CXnIp::DhcpSendRequest()
{
ICHECK(IP, UDPC|SDPC);
INT state = _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();
}
// Fill in common header fields
option = DhcpFillMessageHeader(buf, DHCPREQUEST);
// Fill in the ciaddr field and the 'requested ip addr' option
if (state == STATE_RENEWING || state == STATE_REBINDING) {
msg->_ciaddr = _dhcpaddr;
} else if (_dhcpaddr) {
// state == STATE_INIT_REBOOT || state == STATE_REQUESTING
option = DhcpAppendDWordOption(option, DHCPOPT_REQUESTED_CIpAddr, _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, _options._dhcpServer);
*option++ = DHCPOPT_END;
// Emit the message:
// unicast in RENEWING state, broadcast otherwise
SEND_DHCP_MESSAGE_RETURN(state != STATE_RENEWING);
}
BOOL CXnIp::DhcpValidateOffer(CIpAddr yiaddr, CDhcpOptions* param)
{
ICHECK(IP, UDPC|SDPC);
if ( !yiaddr.IsValidUnicast()
|| !param->_dhcpServer.IsValidUnicast()
|| param->_exptime < (DHCPTIME)(8*cfgDhcpRenewMinTimeoutInSeconds))
return FALSE;
if (param->_dhcpmask == 0)
param->_dhcpmask = yiaddr.DefaultMask();
if (param->_t1time == 0 ||
param->_t2time == 0 ||
param->_t1time >= param->_t2time ||
param->_t2time - param->_t1time < cfgDhcpRenewMinTimeoutInSeconds ||
param->_t2time >= param->_exptime ||
param->_exptime - param->_t2time < cfgDhcpRenewMinTimeoutInSeconds) {
param->_t1time = param->_exptime / 2;
param->_t2time = param->_exptime * 7 / 8;
}
return TRUE;
}
NTSTATUS CXnIp::DhcpProcessOffer(CIpAddr yiaddr, CDhcpOptions* param)
{
ICHECK(IP, UDPC|SDPC);
Assert(_state == STATE_INIT);
TraceSz3(dhcp, "Received DHCPOFFER %s from %s @ time %d", yiaddr.Str(), param->_dhcpServer.Str(), DhcpTime());
// Simply sanity check of offered parameters
if (!DhcpValidateOffer(yiaddr, param))
return(NETERR_PARAM);
_options._dhcpServer = param->_dhcpServer;
_dhcpaddr = yiaddr;
// Send DHCPREQUEST and ignore error
DhcpChangeState(STATE_REQUESTING);
return(NETERR_OK);
}
void CXnIp::DhcpAddOrRemoveGateways(BOOL fDelete)
{
ICHECK(IP, UDPC|SDPC);
CIpAddr ipa;
UINT i;
for (i = 0; i < _options._gatewayCount; i++)
{
ipa = _options._gateways[i];
TraceSz2(dhcp, "%s gateway: %s", fDelete ? "Remove" : "Add", ipa.Str());
if (fDelete)
RouteDelete(0, 0, ipa);
else
RouteAdd(0, 0, ipa, RTEF_DEFAULT, (WORD)(RTE_DEFAULT_METRIC + i));
}
}
void CXnIp::DhcpSetDefaultGateways()
{
ICHECK(IP, USER|UDPC|SDPC);
RaiseToDpc();
DhcpAddOrRemoveGateways(FALSE);
}
void CXnIp::DhcpUseOptionParams(CDhcpOptions* param)
{
ICHECK(IP, UDPC|SDPC);
BOOL resetGateways;
//
// Remember the originating server address and lease info
//
_options._dhcpServer = param->_dhcpServer;
if (param->_exptime == DHCPTIME_INFINITE) {
_options._t1time =
_options._t2time =
_options._exptime = DHCPTIME_INFINITE;
} else {
_options._t1time = _acqtime + param->_t1time;
_options._t2time = _acqtime + param->_t2time;
_options._exptime = _acqtime + param->_exptime;
}
//
// Set gateways in the IP stack
//
if (param->_gatewayCount == 0 || _options._gatewayCount == 0) {
resetGateways = TRUE;
} else {
UINT oldcnt = _options._gatewayCount;
UINT newcnt = param->_gatewayCount;
UINT i, j;
for (i=0; i < newcnt; i++) {
for (j=0; j < oldcnt; j++)
if (param->_gateways[i] != _options._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(TRUE);
_options._gatewayCount = param->_gatewayCount;
memcpy(_options._gateways,
param->_gateways,
param->_gatewayCount * sizeof(CIpAddr));
DhcpAddOrRemoveGateways(FALSE);
}
_options._dnsServerCount = param->_dnsServerCount;
memcpy(_options._dnsServers,
param->_dnsServers,
param->_dnsServerCount * sizeof(CIpAddr));
#if DBG
UINT i;
TraceSz2(dhcp, "%d gateway%s configured", _options._gatewayCount, _options._gatewayCount == 1 ? "" : "s");
for (i = 0; i < _options._gatewayCount; ++i)
TraceSz1(dhcp, " %s", _options._gateways[i].Str());
TraceSz2(dhcp, "%d DNS server%s configured", _options._dnsServerCount, _options._dnsServerCount == 1 ? "" : "s");
for (i = 0; i < _options._dnsServerCount; ++i)
TraceSz1(dhcp, " %s", _options._dnsServers[i].Str());
#endif
}
NTSTATUS CXnIp::DhcpProcessAck(CIpAddr yiaddr, CDhcpOptions* param)
{
ICHECK(IP, UDPC|SDPC);
TraceSz2(dhcp, "Received DHCPACK %s from %s", yiaddr.Str(), param->_dhcpServer.Str());
Assert(_state == STATE_INIT_REBOOT ||
_state == STATE_REQUESTING ||
_state == STATE_RENEWING ||
_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 ((_activeaddr != 0) &&
(_activeaddr != yiaddr ||
_activemask != param->_dhcpmask) ||
ActiveAutonetAddr())
{
TraceSz1(dhcp, "Giving up old IP address %s", _activeaddr.Str());
DhcpResetInterface();
}
// If we got a new address, set it down in the IP stack
if (_activeaddr == 0)
{
IpSetAddress(yiaddr, param->_dhcpmask);
_activeaddr = _dhcpaddr = yiaddr;
_activemask = _options._dhcpmask = param->_dhcpmask;
_flags |= FLAG_ACTIVE_DHCPADDR;
}
TraceSz2(dhcp, "Accepted IP address: %s from %s", yiaddr.Str(), param->_dhcpServer.Str());
TraceSz3(dhcp, "Lease time: %d / %d / %d", param->_t1time, param->_t2time, param->_exptime);
//
// Set other option parameters
//
_initRetryCount = 0;
DhcpUseOptionParams(param);
// We're now in bound state.
// Set timer to expire at T1 time.
DhcpChangeState(STATE_BOUND);
return NETERR_OK;
}
NTSTATUS CXnIp::DhcpProcessNak(CIpAddr dhcpServer)
{
ICHECK(IP, UDPC|SDPC);
TraceSz1(dhcp, "Received DHCPNAK from %s", dhcpServer.Str());
Assert(_state == STATE_INIT_REBOOT ||
_state == STATE_REQUESTING ||
_state == STATE_RENEWING ||
_state == STATE_REBINDING);
// Barf if we got DHCPNAK from an unexpected server
if (_state != STATE_INIT_REBOOT && dhcpServer != _options._dhcpServer)
{
TraceSz1(Warning, "Random DHCPNAK from %s?", dhcpServer.Str());
}
// If we're using an address, give it up
if (_activeaddr) {
DhcpResetInterface();
}
//
// Go to INIT state to start over again
// Send DHCPDISCOVER and ignore error
//
DhcpChangeState(STATE_INIT);
return NETERR_OK;
}
// 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 = *((CIpAddr*) buf)
NTSTATUS CXnIp::DhcpParseOptionParams(CDhcpOptions* param, const BYTE* buf, UINT buflen, BYTE* overload)
{
ICHECK(IP, UDPC|SDPC);
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;
CIpAddr* parray;
UINT n;
if (len == 0 || len % sizeof(CIpAddr) != 0) goto exit;
if (tag == DHCPOPT_ROUTERS)
{
pcount = &param->_gatewayCount;
parray = param->_gateways;
n = MAX_DEFAULT_GATEWAYS * sizeof(CIpAddr);
}
else
{
pcount = &param->_dnsServerCount;
parray = param->_dnsServers;
n = MAX_DEFAULT_DNSSERVERS * sizeof(CIpAddr);
}
if (n > len) n = len;
if (*pcount == 0) {
*pcount = n / sizeof(CIpAddr);
memcpy(parray, buf, n);
}
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:
TraceSz(Warning, "Invalid option data");
return NETERR_PARAM;
}
void CXnIp::DhcpSelectAutonetAddr()
{
ICHECK(IP, UDPC|SDPC);
CIpAddr addr;
Assert(_state == STATE_SELECT_AUTOADDR);
if (++_initRetryCount > cfgAutoIpMaxAttempts) {
//
// We tried too many autonet addresses without success.
// Just give up.
//
_initRetryCount = 0;
DhcpChangeState(STATE_NONE);
// Signal that Xnet initialization was completed abnormally
_flags = (_flags & ~FLAG_ACTIVE_ADDRMASK) | FLAG_ACTIVE_NOADDR;
TraceSz(Warning, "Failed to pick an autonet address.");
return;
}
// Generate a random autonet address
addr = AUTONET_ADDRBASE + RandScaled(AUTONET_ADDRRANGE);
_autonetaddr = addr = HTONL(addr);
TraceSz1(dhcp, "Trying autonet address: %s", addr.Str());
EnetXmitArp(addr);
_retries = 0;
DhcpComputeTimeout();
}
#if DBG
DefineTag(dhcpDump, 0);
//
// Whether to dump incoming and outgoing DHCP messages
//
VOID DhcpDumpByteArray(const BYTE* buf, UINT buflen)
/*++
Routine Description:
Dump a byte array in hexdecimal format
Arguments:
buf - Points to the data buffer
buflen - Buffer length
Return Value:
NONE
--*/
{
for ( ; buflen--; buf++) {
TraceSz1(dhcpDump, "+%02x+", *buf);
}
}
VOID DhcpDumpOption(IN const BYTE* buf, IN UINT buflen, OUT BYTE* overload)
/*++
Routine Description:
Dump DHCP options
Arguments:
buf - Points to the option data buffer
buflen - Data buffer length
overload - Returns the option overload flags
Return Value:
NONE
--*/
{
static PCSTR dhcpMessageTypeStrs[] = {
"***",
"DHCPDISCOVER",
"DHCPOFFER",
"DHCPREQUEST",
"DHCPDECLINE",
"DHCPACK",
"DHCPNAK",
"DHCPRELEASE",
"DHCPINFORM"
};
UINT tag, len, val;
while (buflen && *buf != DHCPOPT_END) {
// Special case for DHCPOPT_PAD - single byte
if (*buf == DHCPOPT_PAD) {
TraceSz(dhcpDump, "+ PAD");
buflen--, buf++;
continue;
}
// Check option length
if (buflen < 2 || buflen-2 < (len = buf[1])) goto badopt;
switch (tag = buf[0]) {
case DHCPOPT_DHCP_MESSAGE_TYPE:
if (len != 1) goto badopt;
val = buf[2];
if (val > 0 && val < dimensionof(dhcpMessageTypeStrs)) {
TraceSz1(dhcpDump, "+ %s+", dhcpMessageTypeStrs[val]);
} else {
TraceSz1(dhcpDump, "+ Unknown DHCP message type: %d+", val);
}
break;
case DHCPOPT_FIELD_OVERLOAD:
if (len != 1) goto badopt;
TraceSz1(dhcpDump, "+ OVERLOAD: %d+", buf[2]);
if (overload)
*overload = buf[2];
else
TraceSz(dhcpDump, "+ !!!+");
break;
case DHCPOPT_REQUESTED_CIpAddr:
case DHCPOPT_SERVERID:
if (len != 4) goto badopt;
TraceSz5(dhcpDump, "+ %s %d.%d.%d.%d+",
(tag == DHCPOPT_SERVERID) ? "SERVERID" : "REQUEST IP ADDR",
buf[2], buf[3], buf[4], buf[5]);
break;
default:
TraceSz1(dhcpDump, "+ %d - +", tag);
DhcpDumpByteArray(buf+2, len);
break;
}
TraceSz(dhcpDump, "+ ");
buf += len+2;
buflen -= len+2;
}
if (buflen == 0) {
TraceSz(dhcpDump, "!!! Missing 'end' option");
} else {
do {
buflen--, buf++;
} while (buflen && *buf == 0);
if (buflen != 0)
{
TraceSz(dhcpDump, "!!! Extra data after 'end' option");
}
}
return;
badopt:
TraceSz(dhcpDump, "!!! Bad DHCP option data");
}
void DhcpDumpMessage(DhcpMessage* msg, UINT msglen)
/*++
Routine Description:
Dump the content of a DHCP message
Arguments:
msg - Points to the DHCP message
msglen - Message length
Return Value:
NONE
--*/
{
const BYTE* option;
BYTE overload = 0;
if (!Tag(dhcpDump))
return;
//
// Sanity check
//
if (msglen < DHCPHDRLEN) {
TraceSz1(dhcpDump, "!!! DHCP message too small: %d bytes", msglen);
return;
}
//
// Dump out fixed header information
//
TraceSz(dhcpDump, "DHCP message: ");
if (msg->_op == BOOTREQUEST)
TraceSz(dhcpDump, "BOOTREQUEST");
else if (msg->_op == BOOTREPLY)
TraceSz(dhcpDump, "BOOTREPLY");
else
TraceSz1(dhcpDump, "%d", msg->_op);
TraceSz1(dhcpDump, " htype: %d", msg->_htype);
if (msg->_hlen > sizeof(msg->_chaddr)) {
TraceSz1(dhcpDump, "!!! Invalid hardware address length: %d\n", msg->_hlen);
} else if (msg->_hlen) {
TraceSz(dhcpDump, " chaddr: +");
DhcpDumpByteArray(msg->_chaddr, msg->_hlen);
TraceSz(dhcpDump, "+ ");
}
TraceSz1(dhcpDump, " hops: %d", msg->_hops);
TraceSz1(dhcpDump, " xid: 0x%08x", NTOHL(msg->_xid));
TraceSz1(dhcpDump, " secs: %d", NTOHS(msg->_secs));
TraceSz1(dhcpDump, " flags: 0x%04x", NTOHS(msg->_flags));
TraceSz1(dhcpDump, " ciaddr: %s", msg->_ciaddr.Str());
TraceSz1(dhcpDump, " yiaddr: %s", msg->_yiaddr.Str());
TraceSz1(dhcpDump, " siaddr: %s", msg->_siaddr.Str());
TraceSz1(dhcpDump, " giaddr: %s", msg->_giaddr.Str());
//
// Dump options
//
option = msg->_options;
msglen -= DHCPHDRLEN;
if (msglen < sizeof(DhcpMagicCookie) ||
memcmp(option, DhcpMagicCookie, sizeof(DhcpMagicCookie)) != 0) {
TraceSz(dhcpDump, "!!! Invalid DHCP magic cookie");
return;
}
msglen -= sizeof(DhcpMagicCookie);
option += sizeof(DhcpMagicCookie);
TraceSz(dhcpDump, " options:");
DhcpDumpOption(option, msglen, &overload);
if (overload & 1) {
TraceSz(dhcpDump, " overload options (file):");
DhcpDumpOption(msg->_file, sizeof(msg->_file), NULL);
}
if (overload & 2) {
TraceSz(dhcpDump, " overload options (sname):");
DhcpDumpOption(msg->_sname, sizeof(msg->_sname), NULL);
}
}
#endif // DBG
void CXnIp::IpRecvArp(CEnetAddr * pea)
{
ICHECK(IP, UDPC|SDPC);
DhcpNotifyAddressConflict();
}
#endif // XNET_FEATURE_DHCP