| ..back |
/*****************************************************************************/
/** Microsoft Windows **/
/** Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved. **/
/*****************************************************************************/
/*
autonet.c
DESCRIPTION:
Automatic IP configuration functions
*/
#include "dhcpp.h"
#include "dhcp.h"
#include "protocol.h"
#include "icmpif.h"
#define DHCP_IPAUTOCONFIGURATION_ATTEMPTS 20
void ProcessAutoIP(DhcpInfo *pDhcp);
extern DhcpInfo **_FindDhcp(DhcpInfo *pDhcp, PTSTR pName); // dhcp.c
extern void FreeDhcpInfo(DhcpInfo *pDhcp); // dhcp.c
extern void CloseDhcpSocket(DhcpInfo *pDhcp); // dhcp.c
extern STATUS SetDhcpConfig(DhcpInfo *pDhcp); // dhcp.c
extern STATUS PutNetUp(DhcpInfo *pDhcp); // dhcp.c
extern BOOL CanUseCachedLease(DhcpInfo * pDhcp); // dhcp.c
extern void NotifyXxxChange(HANDLE hEvent); // dhcp.c
extern HANDLE g_hAddrChangeEvent; // dhcp.c
#define DHCP_ICMP_WAIT_TIME 1000
#define DHCP_ICMP_RCV_BUF_SIZE 0x1000
#define DHCP_ICMP_SEND_MESSAGE "DHCPC"
typedef ULONG (* PFN_VXDECHOREQUEST)(void * InBuf, ulong * InBufLen, void * OutBuf, ulong * OutBufLen);
//
// Ping the specified IP address with "DHCPC".
//
BOOL
PingDhcp(
DWORD dwIPAddr
)
{
DWORD inlen;
DWORD outlen;
DWORD status;
BYTE ReqBuffer[sizeof(ICMP_ECHO_REQUEST)+8];
PICMP_ECHO_REQUEST pReq;
BYTE ReplyBuffer[DHCP_ICMP_RCV_BUF_SIZE];
PICMP_ECHO_REPLY EchoReplies;
PFN_VXDECHOREQUEST pfnEchoRequest;
HANDLE hTcpstk;
BOOL bRet;
#ifdef DEBUG
TCHAR Addr[32];
TCHAR Reply[32];
#endif
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:+PingDhcp\n"));
hTcpstk = LoadLibrary(L"tcpstk.dll");
if (hTcpstk == NULL) {
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:-PingDhcp: LoadLibrary(tcpstk.dll) failed\n"));
return FALSE;
}
bRet = FALSE;
pfnEchoRequest = (PFN_VXDECHOREQUEST)GetProcAddress(hTcpstk, L"VXDEchoRequest");
if (pfnEchoRequest == NULL) {
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:PingDhcp: GetProcAddress(VXDEchoRequest) failed\n"));
goto pd_exit;
}
pReq = (PICMP_ECHO_REQUEST)ReqBuffer;
pReq->Address = dwIPAddr;
pReq->Timeout = DHCP_ICMP_WAIT_TIME;
pReq->DataOffset = sizeof(ICMP_ECHO_REQUEST);
pReq->DataSize = strlen(DHCP_ICMP_SEND_MESSAGE);
strcpy(ReqBuffer+sizeof(ICMP_ECHO_REQUEST), DHCP_ICMP_SEND_MESSAGE);
pReq->OptionsValid = 0;
pReq->Ttl = 1;
pReq->Tos = 0;
pReq->Flags = 0;
pReq->OptionsOffset = 0;
pReq->OptionsSize = 0;
inlen = sizeof(ReqBuffer);
outlen = sizeof(ReplyBuffer);
status = pfnEchoRequest(
(void *)ReqBuffer,
&inlen,
(void *)ReplyBuffer,
&outlen
);
if (status) {
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:PingDhcp: VXDEchoRequest failed %d\n", status));
goto pd_exit;
}
EchoReplies = (PICMP_ECHO_REPLY)ReplyBuffer;
if (EchoReplies->Status) {
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:PingDhcp: Echo status = %d\n", EchoReplies->Status));
goto pd_exit;
}
if (EchoReplies->Address != dwIPAddr) {
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:PingDhcp: Echo IPAddr(%s) != Request IPAddr(%s)\n",
AddrToString(EchoReplies->Status, Reply), AddrToString(dwIPAddr, Addr)));
goto pd_exit;
}
bRet = TRUE;
pd_exit:
FreeLibrary(hTcpstk);
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:-PingDhcp returning %s\n", bRet ? L"TRUE" : L"FALSE"));
return bRet;
} // PingDhcp
//
// If there were no DHCP servers and this interface has a leased IP addr, then ping
// the default gateway to make sure we are on the same subnet. This will avoid going
// to auto IP prematurely.
//
// This function will bring up the interface and leave it up if the default gateway
// could be ping'd.
//
BOOL
CouldPingGateway(
DhcpInfo * pDhcp
)
{
#ifdef DEBUG
TCHAR Addr[32];
TCHAR Gateway[32];
#endif
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:+CouldPingGateway\n"));
if ((pDhcp->IPAddr == 0) || (pDhcp->Gateway == 0)) {
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:-CouldPingGateway: IPAddr = %s, Gateway = %s\n",
AddrToString(pDhcp->IPAddr, Addr), AddrToString(pDhcp->Gateway, Gateway)));
return FALSE;
}
if (!CanUseCachedLease(pDhcp)) {
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:-CouldPingGateway: In T2 stage!\n"));
return FALSE;
}
//CallNetMsgBox(NULL, NMB_FL_OK, NETUI_GETNETSTR_CACHED_LEASE);
if (PutNetUp(pDhcp) != DHCP_SUCCESS) {
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:-CouldPingGateway: PutNetUp(%s) failed\n",
AddrToString(pDhcp->IPAddr, Addr)));
return FALSE;
}
if (PingDhcp(pDhcp->Gateway)) {
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:-CouldPingGateway: Received response from default gateway\n"));
return TRUE;
}
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:-CouldPingGateway: No response to ping\n"));
TakeNetDown(pDhcp, FALSE, TRUE); // retain IPAddr
return FALSE;
} // CouldPingGateway
//
// Schedule a CTE timer so ProcessAutoIP can look for a DHCP server again.
//
void
StartAutoIPTimer(
DhcpInfo * pDhcp
)
{
FILETIME Ft;
FILETIME CurTime;
Ft.dwLowDateTime = pDhcp->AutoInterval;
Ft.dwHighDateTime = 0;
mul64_32_64(&Ft, TEN_M, &Ft);
GetCurrentFT(&CurTime);
add64_64_64(&CurTime, &Ft, &Ft);
CTEStartFTimer(&pDhcp->Timer, Ft, (CTEEventRtn)ProcessAutoIP, pDhcp);
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:StartAutoIPTimer - AutoIP event scheduled\n"));
}
//
// When we autoconfigure our IP address we must continuously look for a DHCP server appearing on our net.
// The default interval is 5 minutes.
//
void
ProcessAutoIP(
DhcpInfo *pDhcp
)
{
int cPkt;
STATUS Status;
DhcpPkt Pkt;
uint IPAddr;
DhcpInfo **ppDhcp;
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:+ProcessAutoIP\n"));
Status = DHCP_SUCCESS;
if (*(ppDhcp = _FindDhcp(pDhcp, NULL))) {
if (SetDHCPNTE(pDhcp)) {
if (DHCP_SUCCESS == (Status = DhcpInitSock(pDhcp, 0))) {
//
// Save current auto config IP address and set dhcp IP address to 0, to follow spec.
// Neither ciaddr nor DHCP_REQ_IP_OP should reflect the auto config IP address; they
// should both be 0.
//
IPAddr = pDhcp->IPAddr;
pDhcp->IPAddr = 0;
BuildDhcpPkt(pDhcp, &Pkt, DHCPDISCOVER, NEW_PKT_FL, pDhcp->ReqOptions, &cPkt);
pDhcp->IPAddr = IPAddr;
Status = SendDhcpPkt(pDhcp, &Pkt, cPkt, DHCPOFFER, ONCE_FL|BCAST_FL);
if (DHCP_SUCCESS == Status) {
//
// If the OFFER is from a DHCP allocator, then we should request our auto config IP address
// It is a DHCP allocator if it is on the same subnet as auto config.
//
if ((pDhcp->IPAddr & pDhcp->SubnetMask) == pDhcp->AutoSubnet) {
pDhcp->IPAddr = IPAddr;
BuildDhcpPkt(pDhcp, &Pkt, DHCPREQUEST, RIP_PKT_FL, pDhcp->ReqOptions, &cPkt);
Status = SendDhcpPkt(pDhcp, &Pkt, cPkt, DHCPACK,
BCAST_FL | DFT_LOOP_FL);
if (DHCP_SUCCESS == Status) {
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:ProcessAutoIP - Leased auto cfg IP addr with ICS DHCP allocater/server.\n"));
ClearDHCPNTE(pDhcp);
CloseDhcpSocket(pDhcp);
CTEFreeLock(&pDhcp->Lock, 0);
return;
} else {
Status = DHCP_SUCCESS; // Error, fall through to switch-over case
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:ProcessAutoIP - Trouble with ICS DHCP allocater/server.\n"));
}
}
}
ClearDHCPNTE(pDhcp);
CloseDhcpSocket(pDhcp);
if (DHCP_SUCCESS == Status) {
//
// A DHCP server has appeared on the net. We need to discard the auto IP address and DHCP for one.
//
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:ProcessAutoIP - Saw a DHCP server, discarding auto IP\n"));
*ppDhcp = pDhcp->pNext;
CTEFreeLock(&v_GlobalListLock, 0);
//
// Remember what was in this DHCPOFFER so RequestDHCPAddr doesn't send another DISCOVER
//
SetDhcpConfig(pDhcp);
pDhcp->IPAddr = IPAddr;
TakeNetDown(pDhcp, FALSE, TRUE); // FALSE == retain IPAddr from the DHCPOFFER
RequestDHCPAddr(pDhcp->Name, pDhcp->Nte, pDhcp->NteContext,
pDhcp->PhysAddr, ETH_ADDR_LEN);
CTEFreeLock(&pDhcp->Lock, 0);
FreeDhcpInfo(pDhcp);
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:-ProcessAutoIP\n"));
return;
} else {
//
// No DHCP server on this net, reschedule this timer to keep checking.
//
StartAutoIPTimer(pDhcp);
}
} else {
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:ProcessAutoIP - DhcpInitSock failed!\n"));
}
} else {
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:ProcessAutoIP - SetDHCPNTE failed!\n"));
}
CTEFreeLock(&v_GlobalListLock, 0);
CTEFreeLock(&pDhcp->Lock, 0);
} else {
CTEFreeLock(&v_GlobalListLock, 0);
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:ProcessAutoIP - FindDhcp failed!\n"));
}
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:-ProcessAutoIP\n"));
} // ProcessAutoIP
extern DhcpInfo *v_pCurrent;
//
// This function is called by the IP component to indicate the status of a gratuitous ARP
// (i.e. "no response" is good. A response usually implies an IP address conflict)
//
void
ARPResult(
IPAddr IPAddr,
uint Result
)
{
DhcpInfo *pDhcp;
#ifdef DEBUG
TCHAR Addr[32];
#endif
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:+ARPResult\n"));
CTEGetLock(&v_GlobalListLock, 0);
if (pDhcp = v_pCurrent) {
if (pDhcp->IPAddr == IPAddr) {
pDhcp->ARPResult = Result;
if (pDhcp->ARPEvent) {
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:ARPResult - setting event\n"));
SetEvent(pDhcp->ARPEvent);
}
} else {
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:ARPResult - IPAddress %s != Current IPAddress\n", AddrToString(IPAddr, Addr)));
}
} else {
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:ARPResult - v_pCurrent == NULL\n"));
}
CTEFreeLock(&v_GlobalListLock, 0);
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:-ARPResult\n"));
} // AutoIP_ARPResult
#define GRAND_HASHING_RETRIES 5
//================================================================================
// GrandHashing does all the work. It updates seed and returns an ip address
// is in the specified subnet (with required mask) based on the random # and the
// hardware address.
//================================================================================
uint // Ip address o/p after hashing
GrandHashing(
IN uchar * HwAddress, // Hardware addres of the card
IN uint HwLen, // Hardware length
IN OUT uint * Seed, // In: orig value, Out: final value
IN uint Mask, // Subnet mask to generate ip addr in
IN uint Subnet // Subnet address to generate ip addr in
)
{
DWORD Hash, Shift;
uint Result;
int cRetries;
uchar * Addr;
uint Len;
if (*Seed == 0) {
*Seed = (uint)CeGetRandomSeed();
}
for (cRetries = GRAND_HASHING_RETRIES; cRetries; cRetries--) {
Addr = HwAddress;
Len = HwLen;
*Seed = (*Seed)*1103515245 + 12345 ;// Random # generator as in K&R
Hash = (*Seed) >> 16 ;
Hash <<= 16;
*Seed = (*Seed)*1103515245 + 12345 ;// Random # generator as in K&R
Hash += (*Seed) >> 16;
// Now Hash contains the 32 bit Random # we need.
Shift = Hash % sizeof(uint) ; // Decide the # of bytes to shift hw-address
while( Len -- ) {
Hash += (*Addr++) << (8 * Shift);
Shift = (Shift + 1 )% sizeof(uint);
}
Result = Hash & ~Mask;
// Check for bcast addresses
if (Result && (Result != ~Mask)) {
return Result | Subnet; // Now restrict hash to be in required subnet
}
}
// In the highly unlikely case that 5 consecutive hashes yielded bcast addresses,
// return a non-bcast address
return (GRAND_HASHING_RETRIES & ~Mask) | Subnet;
}
//
// This function is called when the DHCP client has not received any response from a DHCP server
//
STATUS
AutoIP(
DhcpInfo *pDhcp
)
{
uint i;
uint Result;
uint Seed;
uint HwLen;
uint AttemptedAddress;
uint Mask;
uint Subnet;
uchar * HwAddress;
uint SaveIPAddr;
uint AutoInterval;
#ifdef DEBUG
TCHAR Addr[32];
#endif
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:+AutoIP\n"));
pDhcp->SFlags &= ~DHCPSTATE_AUTO_CFG_IP; // IPAddr not autoconfigured.
// If there is no DHCP server out there and we have a valid IPAddr, ping the default
// gateway to make sure we are still on the same subnet.
if (CouldPingGateway(pDhcp)) {
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:-AutoIP - Still on DHCP server subnet\n"));
return DHCP_SUCCESS;
}
Mask = pDhcp->AutoMask;
Subnet = pDhcp->AutoSubnet;
HwAddress = pDhcp->PhysAddr;
HwLen = sizeof(pDhcp->PhysAddr);
Seed = pDhcp->AutoSeed;
SaveIPAddr = pDhcp->IPAddr;
Result = ERROR_GEN_FAILURE;
//
// Tell tcpstk we are doing DHCP + AutoCfg
//
if (!(*pfnSetDHCPNTE)(pDhcp->NteContext, NULL, NULL, &Seed)) {
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:AutoIP - SetDHCPNTE failed!\n"));
goto ai_exit;
}
if (pDhcp->AutoIPAddr) {
//
// Use our previously autoconfigured IP address
//
AttemptedAddress = pDhcp->AutoIPAddr;
} else {
AttemptedAddress = GrandHashing( HwAddress, HwLen, &Seed, Mask, Subnet );
}
i = DHCP_IPAUTOCONFIGURATION_ATTEMPTS;
do {
if (pDhcp->SFlags & DHCPSTATE_SAW_DHCP_SRV) {
//
// If we ever see a DHCP server on our net, then abort IP autoconfiguration immediately
//
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:AutoIP:DHCP server on net. Aborting autoconfig\n"));
goto ai_exit;
}
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:AutoIP:Trying autoconfig address: %s\n", AddrToString(AttemptedAddress, Addr)));
if ((AttemptedAddress & DHCP_RESERVED_AUTOCFG_MASK) == DHCP_RESERVED_AUTOCFG_SUBNET) {
// address is in reserved range, dont use it..
goto ai_next;
}
pDhcp->ARPResult = ERROR_SUCCESS;
pDhcp->IPAddr = AttemptedAddress;
ResetEvent(pDhcp->ARPEvent);
//
// Among other things, setting the NTE addr causes ARP to probe for existence of this IP addr.
// Just wait a few seconds to see if it is a dup.
//
if ((*pfnIPSetNTEAddr)((ushort)pDhcp->NteContext, NULL, pDhcp->IPAddr, Mask, 0)) {
WaitForSingleObject(pDhcp->ARPEvent, ARP_RESPONSE_WAIT);
if (ERROR_SUCCESS == pDhcp->ARPResult) {
AutoInterval = pDhcp->AutoInterval; // Remember interval
pDhcp->AutoInterval = 2; // Send an additional DHCP discover right away.
pDhcp->AutoIPAddr = AttemptedAddress;
pDhcp->AutoSeed = Seed;
pDhcp->SubnetMask = Mask;
pDhcp->SFlags |= DHCPSTATE_AUTO_CFG_IP; // IPAddr is autoconfigured.
StartAutoIPTimer(pDhcp);
pDhcp->AutoInterval = AutoInterval; // Restore interval
CallAfd(AddInterface)(pDhcp->Name, pDhcp->Nte,
pDhcp->NteContext, 0, pDhcp->IPAddr,
pDhcp->SubnetMask, 0, pDhcp->DNS, 0, pDhcp->WinsServer);
NotifyXxxChange(g_hAddrChangeEvent);
Result = DHCP_SUCCESS;
break;
} else {
//
// Tell IP to stop using the address and mark this NTE for another try
//
(*pfnIPSetNTEAddr)((ushort)pDhcp->NteContext, NULL, 0, 0, 0);
(*pfnSetDHCPNTE)(pDhcp->NteContext, NULL, NULL, &Seed);
}
} else {
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:AutoIP - IPSetNTEAddr failed!\n"));
goto ai_exit;
}
ai_next:
AttemptedAddress = GrandHashing( HwAddress, HwLen, &Seed, Mask, Subnet );
i --;
} while (i);
if (0 == i) { // Tried everything and still could not match
(*pfnIPSetNTEAddr)((ushort)pDhcp->NteContext, NULL, 0, 0, 0);
DEBUGMSG(ZONE_AUTOIP, (L"DHCP:AutoIP - Gave up autoconfiguration\n"));
}
ai_exit:
if (Result) {
pDhcp->IPAddr = SaveIPAddr;
}
ClearDHCPNTE(pDhcp);
DEBUGMSG(ZONE_FUNCTION|ZONE_AUTOIP, (L"DHCP:-AutoIP %d\n", Result));
return Result;
} // AutoIP
Legal Declaration: it is for studying wince(MicroSoft Windows CE) only! : http://www.vxworks6.com