1798 lines
49 KiB
C++
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(¤tTime);
|
||
|
|
||
|
// 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 = ¶m->_gatewayCount;
|
||
|
parray = param->_gateways;
|
||
|
n = MAX_DEFAULT_GATEWAYS * sizeof(CIpAddr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pcount = ¶m->_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
|