1815 lines
44 KiB
C
1815 lines
44 KiB
C
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
net\routing\ip\rtrmgr\rtrdisc.c
|
|
|
|
Abstract:
|
|
Router Discover code
|
|
|
|
Revision History:
|
|
|
|
Amritansh Raghav 20th March 1996 Created
|
|
|
|
--*/
|
|
|
|
#include "allinc.h"
|
|
|
|
VOID
|
|
DeleteSockets(
|
|
IN PICB picb
|
|
);
|
|
|
|
|
|
VOID
|
|
InitializeRouterDiscoveryInfo(
|
|
IN PICB picb,
|
|
IN PRTR_INFO_BLOCK_HEADER pInfoHdr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Sets router discovery information passed in the ICB. If none is passed,
|
|
sets to RFC 1256 defaults
|
|
|
|
Locks
|
|
|
|
Must be called with ICB_LIST lock held as WRITER
|
|
|
|
Arguments
|
|
|
|
picb The ICB of the interface for whom the router discovery
|
|
related variables have to be set
|
|
|
|
Return Value
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PROUTER_DISC_CB pDiscCb;
|
|
PRTR_DISC_INFO pInfo = NULL;
|
|
PRTR_TOC_ENTRY pToc;
|
|
LONG lMilliSec;
|
|
|
|
TraceEnter("InitializeRouterDiscovery");
|
|
|
|
pToc = GetPointerToTocEntry(IP_ROUTER_DISC_INFO,
|
|
pInfoHdr);
|
|
|
|
if(!pToc)
|
|
{
|
|
//
|
|
// Leave things as they are
|
|
//
|
|
|
|
TraceLeave("InitializeRouterDiscoveryInfo");
|
|
|
|
return;
|
|
}
|
|
|
|
pDiscCb = &picb->rdcRtrDiscInfo;
|
|
|
|
//
|
|
// First set everything to defaults, then if any info is valid, reset
|
|
// those fields
|
|
//
|
|
|
|
pDiscCb->wMaxAdvtInterval = DEFAULT_MAX_ADVT_INTERVAL;
|
|
|
|
pDiscCb->lPrefLevel = DEFAULT_PREF_LEVEL;
|
|
pDiscCb->dwNumAdvtsSent = pDiscCb->dwNumSolicitationsSeen = 0;
|
|
|
|
pDiscCb->liMaxMinDiff.HighPart =
|
|
pDiscCb->liMaxMinDiff.LowPart = 0;
|
|
|
|
pDiscCb->liMinAdvtIntervalInSysUnits.HighPart =
|
|
pDiscCb->liMinAdvtIntervalInSysUnits.LowPart = 0;
|
|
|
|
pDiscCb->pRtrDiscSockets = NULL;
|
|
|
|
//
|
|
// RFC 1256 says default should be true, so there will be traffic on the
|
|
// net if the user does not explicitly set this to false. We diverge from
|
|
// the RFC and default to FALSE
|
|
//
|
|
|
|
pDiscCb->bAdvertise = FALSE;
|
|
pDiscCb->bActive = FALSE;
|
|
|
|
if(pToc and (pToc->InfoSize > 0) and (pToc->Count > 0))
|
|
{
|
|
pInfo = (PRTR_DISC_INFO) GetInfoFromTocEntry(pInfoHdr,pToc);
|
|
|
|
if(pInfo isnot NULL)
|
|
{
|
|
//
|
|
// Ok, so we were passed some info
|
|
//
|
|
|
|
if((pInfo->wMaxAdvtInterval >= MIN_MAX_ADVT_INTERVAL) and
|
|
(pInfo->wMaxAdvtInterval <= MAX_MAX_ADVT_INTERVAL))
|
|
{
|
|
pDiscCb->wMaxAdvtInterval = pInfo->wMaxAdvtInterval;
|
|
}
|
|
|
|
if((pInfo->wMinAdvtInterval >= MIN_MIN_ADVT_INTERVAL) and
|
|
(pInfo->wMinAdvtInterval <= pDiscCb->wMaxAdvtInterval))
|
|
{
|
|
pDiscCb->liMinAdvtIntervalInSysUnits = SecsToSysUnits(pInfo->wMinAdvtInterval);
|
|
}
|
|
else
|
|
{
|
|
lMilliSec = (LONG)(DEFAULT_MIN_ADVT_INTERVAL_RATIO * pDiscCb->wMaxAdvtInterval * 1000);
|
|
|
|
pDiscCb->liMinAdvtIntervalInSysUnits = RtlEnlargedIntegerMultiply(lMilliSec,10000);
|
|
}
|
|
|
|
if((pInfo->wAdvtLifetime >= pDiscCb->wMaxAdvtInterval) and
|
|
(pInfo->wAdvtLifetime <= MAX_ADVT_LIFETIME))
|
|
{
|
|
pDiscCb->wAdvtLifetime = pInfo->wAdvtLifetime;
|
|
}
|
|
else
|
|
{
|
|
pDiscCb->wAdvtLifetime = DEFAULT_ADVT_LIFETIME_RATIO * pDiscCb->wMaxAdvtInterval;
|
|
}
|
|
|
|
pDiscCb->bAdvertise = pInfo->bAdvertise;
|
|
|
|
pDiscCb->lPrefLevel = pInfo->lPrefLevel;
|
|
}
|
|
}
|
|
|
|
if(pInfo is NULL)
|
|
{
|
|
//
|
|
// default case, in case no router disc. info. is specified.
|
|
//
|
|
|
|
lMilliSec = (LONG)(DEFAULT_MIN_ADVT_INTERVAL_RATIO * pDiscCb->wMaxAdvtInterval * 1000);
|
|
|
|
pDiscCb->liMinAdvtIntervalInSysUnits = RtlEnlargedIntegerMultiply(lMilliSec,10000);
|
|
|
|
pDiscCb->wAdvtLifetime = DEFAULT_ADVT_LIFETIME_RATIO * pDiscCb->wMaxAdvtInterval;
|
|
}
|
|
|
|
pDiscCb->liMaxMinDiff = RtlLargeIntegerSubtract(SecsToSysUnits(pDiscCb->wMaxAdvtInterval),
|
|
pDiscCb->liMinAdvtIntervalInSysUnits);
|
|
|
|
TraceLeave("InitializeRouterDiscovery");
|
|
}
|
|
|
|
VOID
|
|
SetRouterDiscoveryInfo(
|
|
IN PICB picb,
|
|
IN PRTR_INFO_BLOCK_HEADER pInfoHdr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Sets router discovery information passed in the ICB. If none is passed,
|
|
sets to RFC 1256 defaults
|
|
|
|
Locks
|
|
|
|
Must be called with ICB_LIST lock held as WRITER
|
|
|
|
Arguments
|
|
|
|
picb The ICB of the interface for whom the router discovery related
|
|
variables have to be set
|
|
pInfoHdr Interface Info header
|
|
|
|
Return Value
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PROUTER_DISC_CB pDiscCb;
|
|
PRTR_DISC_INFO pInfo;
|
|
PRTR_TOC_ENTRY pToc;
|
|
LONG lMilliSec;
|
|
BOOL bOriginalStatus;
|
|
|
|
TraceEnter("SetRouterDiscoveryInfo");
|
|
|
|
pDiscCb = &picb->rdcRtrDiscInfo;
|
|
|
|
pToc = GetPointerToTocEntry(IP_ROUTER_DISC_INFO,
|
|
pInfoHdr);
|
|
|
|
if(!pToc)
|
|
{
|
|
//
|
|
// Leave things as they are
|
|
//
|
|
|
|
TraceLeave("SetRouterDiscoveryInfo");
|
|
|
|
return;
|
|
}
|
|
|
|
pInfo = (PRTR_DISC_INFO) GetInfoFromTocEntry(pInfoHdr,pToc);
|
|
|
|
bOriginalStatus = pDiscCb->bAdvertise;
|
|
|
|
if((pToc->InfoSize is 0) or (pInfo is NULL))
|
|
{
|
|
//
|
|
// If the size is zero, stop advertising
|
|
//
|
|
|
|
DeActivateRouterDiscovery(picb);
|
|
|
|
//
|
|
// Instead of returning, we go through and set defaults so that the info
|
|
// looks good when someone does a get info
|
|
//
|
|
}
|
|
|
|
|
|
//
|
|
// First set everything to defaults, then if any info is valid, reset
|
|
// those fields
|
|
//
|
|
|
|
pDiscCb->wMaxAdvtInterval = DEFAULT_MAX_ADVT_INTERVAL;
|
|
|
|
pDiscCb->lPrefLevel = DEFAULT_PREF_LEVEL;
|
|
|
|
//
|
|
// We reset the counters
|
|
//
|
|
|
|
pDiscCb->dwNumAdvtsSent = pDiscCb->dwNumSolicitationsSeen = 0;
|
|
|
|
pDiscCb->liMaxMinDiff.HighPart =
|
|
pDiscCb->liMaxMinDiff.LowPart = 0;
|
|
|
|
pDiscCb->liMinAdvtIntervalInSysUnits.HighPart =
|
|
pDiscCb->liMinAdvtIntervalInSysUnits.LowPart = 0;
|
|
|
|
//
|
|
// We DONT mess with the sockets
|
|
//
|
|
|
|
|
|
if((pToc->InfoSize) and (pInfo isnot NULL))
|
|
{
|
|
if(!pInfo->bAdvertise)
|
|
{
|
|
DeActivateRouterDiscovery(picb);
|
|
}
|
|
|
|
if((pInfo->wMaxAdvtInterval > MIN_MAX_ADVT_INTERVAL) and
|
|
(pInfo->wMaxAdvtInterval < MAX_MAX_ADVT_INTERVAL))
|
|
{
|
|
pDiscCb->wMaxAdvtInterval = pInfo->wMaxAdvtInterval;
|
|
}
|
|
|
|
if((pInfo->wMinAdvtInterval > MIN_MIN_ADVT_INTERVAL) and
|
|
(pInfo->wMinAdvtInterval < pDiscCb->wMaxAdvtInterval))
|
|
{
|
|
pDiscCb->liMinAdvtIntervalInSysUnits =
|
|
SecsToSysUnits(pInfo->wMinAdvtInterval);
|
|
}
|
|
else
|
|
{
|
|
lMilliSec = (LONG)(DEFAULT_MIN_ADVT_INTERVAL_RATIO * pDiscCb->wMaxAdvtInterval * 1000);
|
|
|
|
pDiscCb->liMinAdvtIntervalInSysUnits =
|
|
RtlEnlargedIntegerMultiply(lMilliSec,10000);
|
|
}
|
|
|
|
if((pInfo->wAdvtLifetime > pDiscCb->wMaxAdvtInterval) and
|
|
(pInfo->wAdvtLifetime < MAX_ADVT_LIFETIME))
|
|
{
|
|
pDiscCb->wAdvtLifetime = pInfo->wAdvtLifetime;
|
|
}
|
|
else
|
|
{
|
|
pDiscCb->wAdvtLifetime =
|
|
DEFAULT_ADVT_LIFETIME_RATIO * pDiscCb->wMaxAdvtInterval;
|
|
}
|
|
|
|
pDiscCb->bAdvertise = pInfo->bAdvertise;
|
|
|
|
pDiscCb->lPrefLevel = pInfo->lPrefLevel;
|
|
}
|
|
else
|
|
{
|
|
lMilliSec = (LONG)(DEFAULT_MIN_ADVT_INTERVAL_RATIO * pDiscCb->wMaxAdvtInterval * 1000);
|
|
|
|
pDiscCb->liMinAdvtIntervalInSysUnits =
|
|
RtlEnlargedIntegerMultiply(lMilliSec,10000);
|
|
|
|
pDiscCb->wAdvtLifetime =
|
|
DEFAULT_ADVT_LIFETIME_RATIO * pDiscCb->wMaxAdvtInterval;
|
|
}
|
|
|
|
if(pDiscCb->bAdvertise is TRUE)
|
|
{
|
|
if(bOriginalStatus is FALSE)
|
|
{
|
|
//
|
|
// If we were originally not advertising but are advertising now,
|
|
// then start router discovery
|
|
//
|
|
|
|
ActivateRouterDiscovery(picb);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Else just update the fields in the advertisement
|
|
//
|
|
|
|
UpdateAdvertisement(picb);
|
|
}
|
|
}
|
|
|
|
TraceLeave("SetRouterDiscoveryInfo");
|
|
}
|
|
|
|
DWORD
|
|
GetInterfaceRouterDiscoveryInfo(
|
|
PICB picb,
|
|
PRTR_TOC_ENTRY pToc,
|
|
PBYTE pbDataPtr,
|
|
PRTR_INFO_BLOCK_HEADER pInfoHdr,
|
|
PDWORD pdwSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Gets router discovery information related to an interface
|
|
|
|
Locks
|
|
|
|
Called with ICB_LIST lock held as READER
|
|
|
|
Arguments
|
|
|
|
picb The ICB of the interface whose router discovery information is
|
|
being retrieved
|
|
pToc Pointer to TOC for router discovery info
|
|
pbDataPtr Pointer to start of data buffer
|
|
pInfoHdr Pointer to the header of the whole info
|
|
pdwSize [IN] Size of data buffer
|
|
[OUT] Size of buffer consumed
|
|
|
|
Return Value
|
|
|
|
--*/
|
|
|
|
{
|
|
PRTR_DISC_INFO pInfo;
|
|
DWORD dwRem;
|
|
LARGE_INTEGER liQuotient;
|
|
|
|
TraceEnter("GetInterfaceRouterDiscoveryInfo");
|
|
|
|
if(*pdwSize < sizeof(RTR_DISC_INFO))
|
|
{
|
|
*pdwSize = sizeof(RTR_DISC_INFO);
|
|
|
|
TraceLeave("GetInterfaceRouterDiscoveryInfo");
|
|
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
*pdwSize = pToc->InfoSize = sizeof(RTR_DISC_INFO);
|
|
|
|
//pToc->InfoVersion = IP_ROUTER_DISC_INFO;
|
|
pToc->InfoType = IP_ROUTER_DISC_INFO;
|
|
pToc->Count = 1;
|
|
pToc->Offset = (ULONG)(pbDataPtr - (PBYTE) pInfoHdr);
|
|
|
|
pInfo = (PRTR_DISC_INFO)pbDataPtr;
|
|
|
|
pInfo->wMaxAdvtInterval = picb->rdcRtrDiscInfo.wMaxAdvtInterval;
|
|
|
|
liQuotient = RtlExtendedLargeIntegerDivide(picb->rdcRtrDiscInfo.liMinAdvtIntervalInSysUnits,
|
|
SYS_UNITS_IN_1_SEC,
|
|
&dwRem);
|
|
|
|
pInfo->wMinAdvtInterval = LOWORD(liQuotient.LowPart);
|
|
pInfo->wAdvtLifetime = picb->rdcRtrDiscInfo.wAdvtLifetime;
|
|
pInfo->bAdvertise = picb->rdcRtrDiscInfo.bAdvertise;
|
|
pInfo->lPrefLevel = picb->rdcRtrDiscInfo.lPrefLevel;
|
|
|
|
TraceLeave("GetInterfaceRouterDiscoveryInfo");
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
ActivateRouterDiscovery(
|
|
IN PICB picb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Activates router discovery messages on an interface. The interface must
|
|
already be bound.
|
|
|
|
Locks
|
|
|
|
Called with the ICB_LIST lock held as WRITER
|
|
|
|
Arguments
|
|
|
|
picb The ICB of the interface to activate
|
|
|
|
Return Value
|
|
|
|
NO_ERROR or some error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PROUTER_DISC_CB pDiscCb;
|
|
PROUTER_DISC_CB pDiscCb2;
|
|
DWORD dwResult,i,dwNumAddrs,dwNumOldAddrs,dwSize;
|
|
LARGE_INTEGER liTimer;
|
|
PTIMER_QUEUE_ITEM pTimer;
|
|
BOOL bReset;
|
|
|
|
TraceEnter("ActivateRouterDiscovery");
|
|
|
|
//
|
|
// The SetInterfaceInfo call takes into account whether our
|
|
// admin state is UP or down. Here we only check if our oper
|
|
// state allows us to come up
|
|
//
|
|
|
|
if(picb->dwOperationalState < IF_OPER_STATUS_CONNECTING)
|
|
{
|
|
TraceLeave("ActivateRouterDiscovery");
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
pDiscCb = &picb->rdcRtrDiscInfo;
|
|
|
|
if(!pDiscCb->bAdvertise)
|
|
{
|
|
//
|
|
// Since we dont have to advertise on this interface, we are done
|
|
//
|
|
|
|
TraceLeave("ActivateRouterDiscovery");
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
dwResult = CreateSockets(picb);
|
|
|
|
if(dwResult isnot NO_ERROR)
|
|
{
|
|
Trace2(ERR,
|
|
"ActivateRouterDiscovery: Couldnt create sockets for interface %S. Error %d",
|
|
picb->pwszName,
|
|
dwResult);
|
|
|
|
TraceLeave("ActivateRouterDiscovery");
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
dwResult = UpdateAdvertisement(picb);
|
|
|
|
if(dwResult isnot NO_ERROR)
|
|
{
|
|
DeleteSockets(picb);
|
|
|
|
Trace1(ERR,
|
|
"ActivateRouterDiscovery: Couldnt update Icmp Advt. Error %d",
|
|
dwResult);
|
|
|
|
TraceLeave("ActivateRouterDiscovery");
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
//
|
|
// Ok so we have a valid CB and we have the sockets all set up.
|
|
//
|
|
|
|
bReset = SetFiringTimeForAdvt(picb);
|
|
|
|
if(bReset)
|
|
{
|
|
if(!SetWaitableTimer(g_hRtrDiscTimer,
|
|
&(pDiscCb->tqiTimer.liFiringTime),
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
FALSE))
|
|
{
|
|
dwResult = GetLastError();
|
|
|
|
Trace1(ERR,
|
|
"ActivateRouterDiscovery: Error %d setting timer",
|
|
dwResult);
|
|
|
|
DeleteSockets(picb);
|
|
|
|
TraceLeave("ActivateRouterDiscovery");
|
|
|
|
return ERROR_CAN_NOT_COMPLETE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Yes we are active
|
|
//
|
|
|
|
pDiscCb->bActive = TRUE;
|
|
|
|
TraceLeave("ActivateRouterDiscovery");
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
UpdateAdvertisement(
|
|
IN PICB picb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Updates the Router discovery advertisement. If none exists, creates one
|
|
|
|
Locks
|
|
|
|
ICB lock as writer
|
|
|
|
Arguments
|
|
|
|
picb ICB to update
|
|
|
|
Return Value
|
|
|
|
NO_ERROR
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwResult,i;
|
|
PROUTER_DISC_CB pDiscCb;
|
|
|
|
|
|
TraceEnter("UpdateAdvertisement");
|
|
|
|
pDiscCb = &picb->rdcRtrDiscInfo;
|
|
|
|
if(picb->pRtrDiscAdvt)
|
|
{
|
|
//
|
|
// If we have an old advertisement
|
|
//
|
|
|
|
if(picb->dwRtrDiscAdvtSize < SIZEOF_RTRDISC_ADVT(picb->dwNumAddresses))
|
|
{
|
|
//
|
|
// Too small, cannot reuse the old advert
|
|
//
|
|
|
|
HeapFree(IPRouterHeap,
|
|
0,
|
|
picb->pRtrDiscAdvt);
|
|
|
|
picb->pRtrDiscAdvt = NULL;
|
|
|
|
picb->dwRtrDiscAdvtSize = 0;
|
|
}
|
|
}
|
|
|
|
if(!picb->pRtrDiscAdvt)
|
|
{
|
|
picb->pRtrDiscAdvt = HeapAlloc(IPRouterHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
SIZEOF_RTRDISC_ADVT(picb->dwNumAddresses));
|
|
|
|
if(picb->pRtrDiscAdvt is NULL)
|
|
{
|
|
//
|
|
// Set advertise to FALSE so that no one uses the
|
|
// NULL pointer by mistake
|
|
//
|
|
|
|
pDiscCb->bAdvertise = FALSE;
|
|
picb->dwRtrDiscAdvtSize = 0;
|
|
|
|
Trace1(ERR,
|
|
"UpdateAdvertisement: Cant allocate %d bytes for Icmp Msg",
|
|
SIZEOF_RTRDISC_ADVT(picb->dwNumAddresses));
|
|
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
picb->dwRtrDiscAdvtSize = SIZEOF_RTRDISC_ADVT(picb->dwNumAddresses);
|
|
}
|
|
|
|
picb->pRtrDiscAdvt->byType = ICMP_ROUTER_DISCOVERY_TYPE;
|
|
picb->pRtrDiscAdvt->byCode = ICMP_ROUTER_DISCOVERY_CODE;
|
|
picb->pRtrDiscAdvt->wLifeTime = htons(pDiscCb->wAdvtLifetime);
|
|
picb->pRtrDiscAdvt->byAddrEntrySize = ICMP_ROUTER_DISCOVERY_ADDR_SIZE;
|
|
picb->pRtrDiscAdvt->wXSum = 0;
|
|
|
|
//
|
|
// Add the interface's addresses to the advertisement.
|
|
//
|
|
|
|
picb->wsAdvtWSABuffer.buf = (PBYTE)picb->pRtrDiscAdvt;
|
|
picb->wsAdvtWSABuffer.len = SIZEOF_RTRDISC_ADVT(picb->dwNumAddresses);
|
|
|
|
picb->pRtrDiscAdvt->byNumAddrs = LOBYTE(LOWORD(picb->dwNumAddresses));
|
|
|
|
for(i = 0; i < picb->dwNumAddresses; i++)
|
|
{
|
|
picb->pRtrDiscAdvt->iaAdvt[i].dwRtrIpAddr = picb->pibBindings[i].dwAddress;
|
|
picb->pRtrDiscAdvt->iaAdvt[i].lPrefLevel =
|
|
htonl(picb->rdcRtrDiscInfo.lPrefLevel);
|
|
}
|
|
|
|
picb->pRtrDiscAdvt->wXSum = Compute16BitXSum((PVOID)picb->pRtrDiscAdvt,
|
|
SIZEOF_RTRDISC_ADVT(picb->dwNumAddresses));
|
|
|
|
//
|
|
// Note: TBD: If the advertising times have changed we should
|
|
// change the timer queue. However we let the timer fire and the
|
|
// next time we set the timer we will pick up the correct
|
|
// time
|
|
//
|
|
|
|
TraceLeave("UpdateAdvertisement");
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
SetFiringTimeForAdvt(
|
|
IN PICB picb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
|
|
|
|
Locks
|
|
|
|
ICB_LIST lock must be held as WRITER
|
|
|
|
Arguments
|
|
|
|
picb The ICB of the interface to activate
|
|
|
|
Return Value
|
|
|
|
TRUE if calling the function caused the timer to be reset
|
|
|
|
--*/
|
|
|
|
{
|
|
PROUTER_DISC_CB pDiscCb;
|
|
DWORD dwResult;
|
|
LARGE_INTEGER liCurrentTime, liRandomTime;
|
|
INT iRand;
|
|
ULONG ulRem;
|
|
PLIST_ENTRY pleNode;
|
|
PTIMER_QUEUE_ITEM pOldTime;
|
|
|
|
TraceEnter("SetFiringTimeForAdvt");
|
|
|
|
//
|
|
// Figure out the next time this interface should advertise
|
|
//
|
|
|
|
iRand = rand();
|
|
|
|
pDiscCb = &picb->rdcRtrDiscInfo;
|
|
|
|
liRandomTime = RtlExtendedLargeIntegerDivide(RtlExtendedIntegerMultiply(pDiscCb->liMaxMinDiff,
|
|
iRand),
|
|
RAND_MAX,
|
|
&ulRem);
|
|
|
|
liRandomTime = RtlLargeIntegerAdd(liRandomTime,
|
|
pDiscCb->liMinAdvtIntervalInSysUnits);
|
|
|
|
if((pDiscCb->dwNumAdvtsSent <= MAX_INITIAL_ADVTS) and
|
|
RtlLargeIntegerGreaterThan(liRandomTime,SecsToSysUnits(MAX_INITIAL_ADVT_TIME)))
|
|
{
|
|
liRandomTime = SecsToSysUnits(MAX_INITIAL_ADVT_TIME);
|
|
}
|
|
|
|
NtQuerySystemTime(&liCurrentTime);
|
|
|
|
picb->rdcRtrDiscInfo.tqiTimer.liFiringTime = RtlLargeIntegerAdd(liCurrentTime,liRandomTime);
|
|
|
|
//
|
|
// Insert into sorted list
|
|
//
|
|
|
|
for(pleNode = g_leTimerQueueHead.Flink;
|
|
pleNode isnot &g_leTimerQueueHead;
|
|
pleNode = pleNode->Flink)
|
|
{
|
|
pOldTime = CONTAINING_RECORD(pleNode,TIMER_QUEUE_ITEM,leTimerLink);
|
|
|
|
if(RtlLargeIntegerGreaterThan(pOldTime->liFiringTime,
|
|
picb->rdcRtrDiscInfo.tqiTimer.liFiringTime))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now pleNode points to first Node whose time is greater than ours, so
|
|
// we insert ourselves before pleNode. Since RTL doesnt supply us with
|
|
// an InsertAfter function, we go back on and use the previous node as a
|
|
// list head and call InsertHeadList
|
|
//
|
|
|
|
pleNode = pleNode->Blink;
|
|
|
|
InsertHeadList(pleNode,
|
|
&(picb->rdcRtrDiscInfo.tqiTimer.leTimerLink));
|
|
|
|
if(pleNode is &g_leTimerQueueHead)
|
|
{
|
|
//
|
|
// We inserted ourselves at the head of the queue
|
|
//
|
|
|
|
TraceLeave("SetFiringTimeForAdvt");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
TraceLeave("SetFiringTimeForAdvt");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
CreateSockets(
|
|
IN PICB picb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Activates router discovery messages on an interface. The interface must
|
|
already be bound
|
|
|
|
Locks
|
|
|
|
ICB_LIST lock must be held as WRITER
|
|
|
|
Arguments
|
|
|
|
picb The ICB of the interface for which the sockets have to be created
|
|
|
|
Return Value
|
|
|
|
NO_ERROR or some error code
|
|
|
|
--*/
|
|
|
|
{
|
|
PROUTER_DISC_CB pDiscCb;
|
|
DWORD i, dwResult, dwBytesReturned;
|
|
struct linger lingerOption;
|
|
BOOL bOption, bLoopback;
|
|
SOCKADDR_IN sinSockAddr;
|
|
struct ip_mreq imOption;
|
|
INT iScope;
|
|
|
|
TraceEnter("CreateSockets");
|
|
|
|
dwResult = NO_ERROR;
|
|
|
|
if(picb->dwNumAddresses is 0)
|
|
{
|
|
Trace1(ERR,
|
|
"CreateSockets: Can not activate router discovery on %S as it has no addresses",
|
|
picb->pwszName);
|
|
|
|
TraceLeave("CreateSockets");
|
|
|
|
return ERROR_CAN_NOT_COMPLETE;
|
|
}
|
|
|
|
//
|
|
// Create the sockets for the interface
|
|
//
|
|
|
|
pDiscCb = &(picb->rdcRtrDiscInfo);
|
|
|
|
pDiscCb->pRtrDiscSockets = HeapAlloc(IPRouterHeap,
|
|
0,
|
|
(picb->dwNumAddresses) * sizeof(SOCKET));
|
|
|
|
if(pDiscCb->pRtrDiscSockets is NULL)
|
|
{
|
|
Trace1(ERR,
|
|
"CreateSockets: Error allocating %d bytes for sockets",
|
|
(picb->dwNumAddresses) * sizeof(SOCKET));
|
|
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
for(i = 0; i < picb->dwNumAddresses; i++)
|
|
{
|
|
pDiscCb->pRtrDiscSockets[i] = INVALID_SOCKET;
|
|
}
|
|
|
|
for(i = 0; i < picb->dwNumAddresses; i++)
|
|
{
|
|
pDiscCb->pRtrDiscSockets[i] = WSASocket(AF_INET,
|
|
SOCK_RAW,
|
|
IPPROTO_ICMP,
|
|
NULL,
|
|
0,
|
|
RTR_DISC_SOCKET_FLAGS);
|
|
|
|
if(pDiscCb->pRtrDiscSockets[i] is INVALID_SOCKET)
|
|
{
|
|
dwResult = WSAGetLastError();
|
|
|
|
Trace3(ERR,
|
|
"CreateSockets: Couldnt create socket number %d on %S. Error %d",
|
|
i,
|
|
picb->pwszName,
|
|
dwResult);
|
|
|
|
continue;
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// Set to SO_DONTLINGER
|
|
//
|
|
|
|
bOption = TRUE;
|
|
|
|
if(setsockopt(pDiscCb->pRtrDiscSockets[i],
|
|
SOL_SOCKET,
|
|
SO_DONTLINGER,
|
|
(const char FAR*)&bOption,
|
|
sizeof(BOOL)) is SOCKET_ERROR)
|
|
{
|
|
Trace1(ERR,
|
|
"CreateSockets: Couldnt set linger option - continuing. Error %d",
|
|
WSAGetLastError());
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Set to SO_REUSEADDR
|
|
//
|
|
|
|
bOption = TRUE;
|
|
|
|
if(setsockopt(pDiscCb->pRtrDiscSockets[i],
|
|
SOL_SOCKET,
|
|
SO_REUSEADDR,
|
|
(const char FAR*)&bOption,
|
|
sizeof(BOOL)) is SOCKET_ERROR)
|
|
{
|
|
Trace1(ERR,
|
|
"CreateSockets: Couldnt set reuse option - continuing. Error %d",
|
|
WSAGetLastError());
|
|
}
|
|
|
|
if(WSAEventSelect(pDiscCb->pRtrDiscSockets[i],
|
|
g_hRtrDiscSocketEvent,
|
|
FD_READ) is SOCKET_ERROR)
|
|
{
|
|
Trace2(ERR,
|
|
"CreateSockets: WSAEventSelect() failed for socket on %S.Error %d",
|
|
picb->pwszName,
|
|
WSAGetLastError());
|
|
|
|
closesocket(pDiscCb->pRtrDiscSockets[i]);
|
|
|
|
pDiscCb->pRtrDiscSockets[i] = INVALID_SOCKET;
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// TBD: Set scope/TTL to 1 since we always multicast the responses
|
|
// Also set Loopback to ignore self generated packets
|
|
//
|
|
|
|
//
|
|
// Bind to the addresses on the interface
|
|
//
|
|
|
|
sinSockAddr.sin_family = AF_INET;
|
|
sinSockAddr.sin_addr.s_addr = picb->pibBindings[i].dwAddress;
|
|
sinSockAddr.sin_port = 0;
|
|
|
|
if(bind(pDiscCb->pRtrDiscSockets[i],
|
|
(const struct sockaddr FAR*)&sinSockAddr,
|
|
sizeof(SOCKADDR_IN)) is SOCKET_ERROR)
|
|
{
|
|
dwResult = WSAGetLastError();
|
|
|
|
Trace3(ERR,
|
|
"CreateSockets: Couldnt bind to %d.%d.%d.%d on interface %S. Error %d",
|
|
PRINT_IPADDR(picb->pibBindings[i].dwAddress),
|
|
picb->pwszName,
|
|
dwResult);
|
|
|
|
closesocket(pDiscCb->pRtrDiscSockets[i]);
|
|
|
|
pDiscCb->pRtrDiscSockets[i] = INVALID_SOCKET;
|
|
|
|
continue;
|
|
}
|
|
|
|
bLoopback = FALSE;
|
|
|
|
dwResult = WSAIoctl(pDiscCb->pRtrDiscSockets[i],
|
|
SIO_MULTIPOINT_LOOPBACK,
|
|
(PVOID)&bLoopback,
|
|
sizeof(BOOL),
|
|
NULL,
|
|
0,
|
|
&dwBytesReturned,
|
|
NULL,
|
|
NULL);
|
|
|
|
if(dwResult is SOCKET_ERROR)
|
|
{
|
|
Trace1(ERR,
|
|
"CreateSockets: Error %d setting loopback to FALSE",
|
|
WSAGetLastError());
|
|
}
|
|
|
|
iScope = 1;
|
|
|
|
dwResult = WSAIoctl(pDiscCb->pRtrDiscSockets[i],
|
|
SIO_MULTICAST_SCOPE,
|
|
(PVOID)&iScope,
|
|
sizeof(INT),
|
|
NULL,
|
|
0,
|
|
&dwBytesReturned,
|
|
NULL,
|
|
NULL);
|
|
|
|
if(dwResult is SOCKET_ERROR)
|
|
{
|
|
Trace1(ERR,
|
|
"CreateSockets: Error %d setting multicast scope to 1",
|
|
WSAGetLastError());
|
|
}
|
|
|
|
|
|
#if 0
|
|
|
|
//
|
|
// Join the multicast session on ALL_ROUTERS_MULTICAST
|
|
//
|
|
|
|
sinSockAddr.sin_family = AF_INET;
|
|
sinSockAddr.sin_addr.s_addr = ALL_ROUTERS_MULTICAST_GROUP;
|
|
sinSockAddr.sin_port = 0;
|
|
|
|
if(WSAJoinLeaf(pDiscCb->pRtrDiscSockets[i],
|
|
(const struct sockaddr FAR*)&sinSockAddr,
|
|
sizeof(SOCKADDR_IN),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
JL_BOTH) is INVALID_SOCKET)
|
|
{
|
|
dwResult = WSAGetLastError();
|
|
|
|
Trace2(ERR,
|
|
"CreateSockets: Couldnt join multicast group on socket for %d.%d.%d.%d on %S",
|
|
PRINT_IPADDR(picb->pibBindings[i].dwAddress)),
|
|
picb->pwszName);
|
|
|
|
closesocket(pDiscCb->pRtrDiscSockets[i]);
|
|
|
|
pDiscCb->pRtrDiscSockets[i] = INVALID_SOCKET;
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Join the multicast session on ALL_SYSTEMS_MULTICAST
|
|
//
|
|
|
|
sinSockAddr.sin_family = AF_INET;
|
|
sinSockAddr.sin_addr.s_addr = ALL_SYSTEMS_MULTICAST_GROUP;
|
|
sinSockAddr.sin_port = 0;
|
|
|
|
if(WSAJoinLeaf(pDiscCb->pRtrDiscSockets[i],
|
|
(const struct sockaddr FAR*)&sinSockAddr,
|
|
sizeof(SOCKADDR_IN),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
JL_BOTH) is INVALID_SOCKET)
|
|
{
|
|
dwResult = WSAGetLastError();
|
|
|
|
Trace2(ERR,
|
|
"CreateSockets: Couldnt join all systems multicast group on socket for %d.%d.%d.%d on %S",
|
|
PRINT_IPADDR(picb->pibBindings[i].dwAddress),
|
|
picb->pwszName);
|
|
}
|
|
|
|
#endif
|
|
|
|
sinSockAddr.sin_addr.s_addr = picb->pibBindings[i].dwAddress;
|
|
|
|
if(setsockopt(pDiscCb->pRtrDiscSockets[i],
|
|
IPPROTO_IP,
|
|
IP_MULTICAST_IF,
|
|
(PBYTE)&sinSockAddr.sin_addr,
|
|
sizeof(IN_ADDR)) is SOCKET_ERROR)
|
|
{
|
|
dwResult = WSAGetLastError();
|
|
|
|
Trace2(ERR,
|
|
"CreateSockets: Couldnt join multicast group on socket for %d.%d.%d.%d on %S",
|
|
PRINT_IPADDR(picb->pibBindings[i].dwAddress),
|
|
picb->pwszName);
|
|
|
|
closesocket(pDiscCb->pRtrDiscSockets[i]);
|
|
|
|
pDiscCb->pRtrDiscSockets[i] = INVALID_SOCKET;
|
|
|
|
continue;
|
|
}
|
|
|
|
Trace2(RTRDISC,
|
|
"CreateSockets: Joining ALL_ROUTERS on %d.%d.%d.%d over %S",
|
|
PRINT_IPADDR(picb->pibBindings[i].dwAddress),
|
|
picb->pwszName);
|
|
|
|
imOption.imr_multiaddr.s_addr = ALL_ROUTERS_MULTICAST_GROUP;
|
|
imOption.imr_interface.s_addr = picb->pibBindings[i].dwAddress;
|
|
|
|
if(setsockopt(pDiscCb->pRtrDiscSockets[i],
|
|
IPPROTO_IP,
|
|
IP_ADD_MEMBERSHIP,
|
|
(PBYTE)&imOption,
|
|
sizeof(imOption)) is SOCKET_ERROR)
|
|
{
|
|
dwResult = WSAGetLastError();
|
|
|
|
Trace2(ERR,
|
|
"CreateSockets: Couldnt join multicast group on socket for %d.%d.%d.%d on %S",
|
|
PRINT_IPADDR(picb->pibBindings[i].dwAddress),
|
|
picb->pwszName);
|
|
|
|
closesocket(pDiscCb->pRtrDiscSockets[i]);
|
|
|
|
pDiscCb->pRtrDiscSockets[i] = INVALID_SOCKET;
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
TraceLeave("CreateSockets");
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
VOID
|
|
DeleteSockets(
|
|
IN PICB picb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Deletes the sockets (if any) created for running Router Discovery
|
|
|
|
Locks
|
|
|
|
|
|
Arguments
|
|
|
|
picb The interface whose sockets need to be deleted
|
|
|
|
Return Value
|
|
|
|
--*/
|
|
|
|
{
|
|
PROUTER_DISC_CB pDiscCb;
|
|
DWORD i;
|
|
|
|
//
|
|
// Cloese the sockets, free the memory
|
|
//
|
|
|
|
pDiscCb = &(picb->rdcRtrDiscInfo);
|
|
|
|
for(i = 0; i < picb->dwNumAddresses; i++)
|
|
{
|
|
if(pDiscCb->pRtrDiscSockets[i] isnot INVALID_SOCKET)
|
|
{
|
|
closesocket(pDiscCb->pRtrDiscSockets[i]);
|
|
}
|
|
}
|
|
|
|
HeapFree(IPRouterHeap,
|
|
0,
|
|
pDiscCb->pRtrDiscSockets);
|
|
|
|
pDiscCb->pRtrDiscSockets = NULL;
|
|
|
|
}
|
|
|
|
VOID
|
|
HandleRtrDiscTimer(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Processes the firing of the Router Discovery Timer for an interface
|
|
|
|
Locks
|
|
|
|
Called with ICB_LIST held as WRITER. This is needed because ICB_LIST
|
|
protects timer queue, etc. This may seem inefficient, but the Timer will
|
|
go off once in 5 minutes or so, so its worth reducing Kernel Mode footprint
|
|
by reusing the lock
|
|
|
|
Arguments
|
|
|
|
None
|
|
|
|
Return Value
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER liCurrentTime;
|
|
PICB picb;
|
|
PLIST_ENTRY pleNode;
|
|
PTIMER_QUEUE_ITEM pTimer;
|
|
BOOL bReset;
|
|
DWORD dwResult;
|
|
PROUTER_DISC_CB pInfo;
|
|
|
|
TraceEnter("HandleRtrDiscTimer");
|
|
|
|
while(!IsListEmpty(&g_leTimerQueueHead))
|
|
{
|
|
pleNode = g_leTimerQueueHead.Flink;
|
|
|
|
pTimer = CONTAINING_RECORD(pleNode, TIMER_QUEUE_ITEM, leTimerLink);
|
|
|
|
NtQuerySystemTime(&liCurrentTime);
|
|
|
|
if(RtlLargeIntegerGreaterThan(pTimer->liFiringTime,liCurrentTime))
|
|
{
|
|
break;
|
|
}
|
|
|
|
RemoveHeadList(&g_leTimerQueueHead);
|
|
|
|
//
|
|
// We have the pointer to the timer element. From that
|
|
// we get the pointer to the Router Discovery Info container
|
|
//
|
|
|
|
pInfo = CONTAINING_RECORD(pTimer, ROUTER_DISC_CB, tqiTimer);
|
|
|
|
//
|
|
// From the rtrdisc info we get to the ICB which contains it
|
|
//
|
|
|
|
picb = CONTAINING_RECORD(pInfo, ICB, rdcRtrDiscInfo);
|
|
|
|
//
|
|
// Send advt for this interface
|
|
//
|
|
|
|
if((picb->rdcRtrDiscInfo.bAdvertise is FALSE) or
|
|
(picb->dwAdminState is IF_ADMIN_STATUS_DOWN) or
|
|
(picb->dwOperationalState < CONNECTED))
|
|
{
|
|
Trace3(ERR,
|
|
"HandleRtrDiscTimer: Router Discovery went off for interface %S, but current state (%d/%d) doesnt allow tx",
|
|
picb->pwszName,
|
|
picb->dwAdminState,
|
|
picb->dwOperationalState);
|
|
|
|
continue;
|
|
}
|
|
|
|
IpRtAssert(picb->pRtrDiscAdvt);
|
|
|
|
AdvertiseInterface(picb);
|
|
|
|
//
|
|
// Insert the next time this interface needs to advt into the queue.
|
|
// Reset the timer
|
|
//
|
|
|
|
SetFiringTimeForAdvt(picb);
|
|
|
|
}
|
|
|
|
//
|
|
// We have to reset the timer
|
|
//
|
|
|
|
if(!IsListEmpty(&g_leTimerQueueHead))
|
|
{
|
|
pleNode = g_leTimerQueueHead.Flink;
|
|
|
|
pTimer = CONTAINING_RECORD(pleNode, TIMER_QUEUE_ITEM, leTimerLink);
|
|
|
|
if(!SetWaitableTimer(g_hRtrDiscTimer,
|
|
&pTimer->liFiringTime,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
FALSE))
|
|
{
|
|
dwResult = GetLastError();
|
|
|
|
Trace1(ERR,
|
|
"HandleRtrDiscTimer: Couldnt set waitable timer",
|
|
dwResult);
|
|
}
|
|
}
|
|
|
|
TraceLeave("HandleRtrDiscTimer");
|
|
}
|
|
|
|
VOID
|
|
AdvertiseInterface(
|
|
IN PICB picb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
|
|
Locks
|
|
|
|
|
|
|
|
Arguments
|
|
|
|
picb ICB of the interface on which to send out the advertisement
|
|
|
|
|
|
Return Value
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i,dwResult,dwNumBytesSent;
|
|
|
|
TraceEnter("AdvertiseInterface");
|
|
|
|
//
|
|
// If any replies were pending, they are deemed to be sent even if
|
|
// the send fails
|
|
//
|
|
|
|
picb->rdcRtrDiscInfo.bReplyPending = FALSE;
|
|
|
|
for(i = 0; i < picb->dwNumAddresses; i++)
|
|
{
|
|
|
|
#if DBG
|
|
|
|
Trace2(RTRDISC,
|
|
"Advertising from %d.%d.%d.%d on %S",
|
|
PRINT_IPADDR(picb->pibBindings[i].dwAddress),
|
|
picb->pwszName);
|
|
|
|
TraceDumpEx(TraceHandle,
|
|
IPRTRMGR_TRACE_RTRDISC,
|
|
picb->wsAdvtWSABuffer.buf,
|
|
picb->wsAdvtWSABuffer.len,
|
|
4,
|
|
FALSE,
|
|
"ICMP Advt");
|
|
#endif
|
|
|
|
if(WSASendTo(picb->rdcRtrDiscInfo.pRtrDiscSockets[i],
|
|
&picb->wsAdvtWSABuffer,
|
|
1,
|
|
&dwNumBytesSent,
|
|
MSG_DONTROUTE,
|
|
(const struct sockaddr FAR*)&g_sinAllSystemsAddr,
|
|
sizeof(SOCKADDR_IN),
|
|
NULL,
|
|
NULL
|
|
) is SOCKET_ERROR)
|
|
{
|
|
dwResult = WSAGetLastError();
|
|
|
|
Trace3(ERR,
|
|
"AdvertiseInterface: Couldnt send from %d.%d.%d.%d on %S. Error %d",
|
|
PRINT_IPADDR(picb->pibBindings[i].dwAddress),
|
|
picb->pwszName,
|
|
dwResult);
|
|
}
|
|
else
|
|
{
|
|
picb->rdcRtrDiscInfo.dwNumAdvtsSent++;
|
|
}
|
|
}
|
|
|
|
TraceLeave("AdvertiseInterface");
|
|
}
|
|
|
|
DWORD
|
|
DeActivateRouterDiscovery(
|
|
IN PICB picb
|
|
)
|
|
{
|
|
PROUTER_DISC_CB pDiscCb;
|
|
DWORD i;
|
|
PTIMER_QUEUE_ITEM pTimer;
|
|
PLIST_ENTRY pleNode;
|
|
|
|
|
|
TraceEnter("DeActivateRouterDiscovery");
|
|
|
|
pDiscCb = &(picb->rdcRtrDiscInfo);
|
|
|
|
if(!pDiscCb->bActive)
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DeleteSockets(picb);
|
|
|
|
//
|
|
// Check to see if our advertisement is in the front of the queue
|
|
//
|
|
|
|
if(&(pDiscCb->tqiTimer.leTimerLink) is &g_leTimerQueueHead)
|
|
{
|
|
RemoveHeadList(&g_leTimerQueueHead);
|
|
|
|
if(!IsListEmpty(&g_leTimerQueueHead))
|
|
{
|
|
pleNode = g_leTimerQueueHead.Flink;
|
|
|
|
pTimer = CONTAINING_RECORD(pleNode, TIMER_QUEUE_ITEM, leTimerLink);
|
|
|
|
if(!SetWaitableTimer(g_hRtrDiscTimer,
|
|
&pTimer->liFiringTime,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
FALSE))
|
|
{
|
|
Trace1(ERR,
|
|
"DeActivateRouterDiscovery: Couldnt set waitable timer",
|
|
GetLastError());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Just remove the timer element from the queue
|
|
//
|
|
|
|
RemoveEntryList(&(pDiscCb->tqiTimer.leTimerLink));
|
|
}
|
|
|
|
pDiscCb->bActive = FALSE;
|
|
|
|
TraceLeave("DeActivateRouterDiscovery");
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
HandleSolicitations(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
|
|
Locks
|
|
|
|
|
|
Arguments
|
|
|
|
|
|
Return Value
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY pleNode;
|
|
PICB picb;
|
|
DWORD i, dwResult, dwRcvAddrLen, dwSizeOfHeader, dwBytesRead, dwFlags;
|
|
WSANETWORKEVENTS wsaNetworkEvents;
|
|
SOCKADDR_IN sinFrom;
|
|
PICMP_ROUTER_SOL_MSG pIcmpMsg;
|
|
|
|
TraceEnter("HandleSolicitations");
|
|
|
|
for(pleNode = ICBList.Flink;
|
|
pleNode isnot &ICBList;
|
|
pleNode = pleNode->Flink)
|
|
{
|
|
picb = CONTAINING_RECORD(pleNode, ICB, leIfLink);
|
|
|
|
//
|
|
// If the interface has no bindings, or isnot involved in Router Discovery, we wouldnt have
|
|
// opened a socket on it so the FD_READ notification cant be for it
|
|
//
|
|
|
|
if((picb->dwNumAddresses is 0) or
|
|
(picb->rdcRtrDiscInfo.bActive is FALSE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for(i = 0; i < picb->dwNumAddresses; i++)
|
|
{
|
|
if(picb->rdcRtrDiscInfo.pRtrDiscSockets[i] is INVALID_SOCKET)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(WSAEnumNetworkEvents(picb->rdcRtrDiscInfo.pRtrDiscSockets[i],
|
|
NULL,
|
|
&wsaNetworkEvents) is SOCKET_ERROR)
|
|
{
|
|
dwResult = GetLastError();
|
|
|
|
Trace1(ERR,
|
|
"HandleSolicitations: WSAEnumNetworkEvents() returned %d",
|
|
dwResult);
|
|
|
|
continue;
|
|
}
|
|
|
|
if(!(wsaNetworkEvents.lNetworkEvents & FD_READ))
|
|
{
|
|
//
|
|
// Read bit isnot set and we arent interested in anything else
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
if(wsaNetworkEvents.iErrorCode[FD_READ_BIT] isnot NO_ERROR)
|
|
{
|
|
Trace3(ERR,
|
|
"HandleSolicitations: Error %d associated with socket %s on %S for FD_READ",
|
|
wsaNetworkEvents.iErrorCode[FD_READ_BIT],
|
|
PRINT_IPADDR(picb->pibBindings[i].dwAddress),
|
|
picb->pwszName);
|
|
|
|
continue;
|
|
}
|
|
|
|
dwRcvAddrLen = sizeof(SOCKADDR_IN);
|
|
dwFlags = 0;
|
|
|
|
dwResult = WSARecvFrom(picb->rdcRtrDiscInfo.pRtrDiscSockets[i],
|
|
&g_wsaIpRcvBuf,
|
|
1,
|
|
&dwBytesRead,
|
|
&dwFlags,
|
|
(struct sockaddr FAR*)&sinFrom,
|
|
&dwRcvAddrLen,
|
|
NULL,
|
|
NULL);
|
|
|
|
if(dwResult is SOCKET_ERROR)
|
|
{
|
|
dwResult = WSAGetLastError();
|
|
|
|
Trace4(ERR,
|
|
"HandleSolicitations: Error %d in WSARecvFrom on socket %d.%d.%d.%d over %S. Bytes read %d",
|
|
dwResult,
|
|
PRINT_IPADDR(picb->pibBindings[i].dwAddress),
|
|
picb->pwszName,
|
|
dwBytesRead);
|
|
|
|
continue;
|
|
}
|
|
|
|
Trace2(RTRDISC,
|
|
"HandleSolicitations: Received %d bytes on %d.%d.%d.%d",
|
|
dwBytesRead,
|
|
PRINT_IPADDR(picb->pibBindings[i].dwAddress));
|
|
|
|
if(picb->rdcRtrDiscInfo.bReplyPending)
|
|
{
|
|
//
|
|
// Well the reply is pending so we dont need to do anything other than go
|
|
// through the sockets for this interface and do a recvfrom to clear out the
|
|
// FD_READ bit
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
dwSizeOfHeader = ((g_pIpHeader->byVerLen)&0x0f)<<2;
|
|
|
|
pIcmpMsg = (PICMP_ROUTER_SOL_MSG)(((PBYTE)g_pIpHeader) + dwSizeOfHeader);
|
|
|
|
#if DBG
|
|
|
|
Trace6(RTRDISC,
|
|
"HandleSolicitations: Type is %d, code %d. IP Length is %d. \n\t\tHeader Length is %d Src is %d.%d.%d.%d dest is %d.%d.%d.%d",
|
|
(DWORD)pIcmpMsg->byType,
|
|
(DWORD)pIcmpMsg->byCode,
|
|
ntohs(g_pIpHeader->wLength),
|
|
(DWORD)dwSizeOfHeader,
|
|
PRINT_IPADDR(g_pIpHeader->dwSrc),
|
|
PRINT_IPADDR(g_pIpHeader->dwDest));
|
|
#endif
|
|
|
|
if((pIcmpMsg->byType isnot 0xA) or
|
|
(pIcmpMsg->byCode isnot 0x0))
|
|
{
|
|
//
|
|
// Can not be a valid ICMP Router Solicitation packet
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
if((ntohs(g_pIpHeader->wLength) - dwSizeOfHeader) < 8)
|
|
{
|
|
Trace0(RTRDISC,
|
|
"HandleSolicitations: Received ICMP packet of length less than 8, discarding");
|
|
|
|
continue;
|
|
}
|
|
|
|
if(Compute16BitXSum((PVOID)pIcmpMsg,
|
|
8) isnot 0x0000)
|
|
{
|
|
Trace0(ERR,
|
|
"HandleSolicitations: ICMP packet checksum wrong");
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check for valid neighbour
|
|
//
|
|
|
|
if((g_pIpHeader->dwSrc isnot 0) and
|
|
((g_pIpHeader->dwSrc & picb->pibBindings[i].dwMask) isnot
|
|
(picb->pibBindings[i].dwAddress & picb->pibBindings[i].dwMask)))
|
|
{
|
|
Trace1(ERR,
|
|
"HandleSolicitations: Received ICMP solicitation from invalid neigbour %d.%d.%d.%d",
|
|
PRINT_IPADDR(g_pIpHeader->dwDest));
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Since we always Multicast, if the destination addresses was a
|
|
// broadcast, log an error
|
|
//
|
|
|
|
if((g_pIpHeader->dwDest is 0xFFFFFFFF) or
|
|
(g_pIpHeader->dwDest is (picb->pibBindings[i].dwMask | ~picb->pibBindings[i].dwMask)))
|
|
{
|
|
Trace0(ERR,
|
|
"HandleSolicitations: Received a broadcast ICMP solicitation");
|
|
}
|
|
|
|
//
|
|
// So insert a reply for this interface. We multicast the replies
|
|
// too.
|
|
//
|
|
|
|
picb->rdcRtrDiscInfo.bReplyPending = TRUE;
|
|
|
|
SetFiringTimeForReply(picb);
|
|
}
|
|
}
|
|
|
|
TraceLeave("HandleSolicitations");
|
|
}
|
|
|
|
|
|
VOID
|
|
SetFiringTimeForReply(
|
|
IN PICB picb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
|
|
Locks
|
|
|
|
|
|
Arguments
|
|
|
|
|
|
Return Value
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER liCurrentTime, liRandomTime;
|
|
INT iRand;
|
|
ULONG ulRem;
|
|
PLIST_ENTRY pleNode;
|
|
PTIMER_QUEUE_ITEM pOldTime;
|
|
BOOL bReset = FALSE;
|
|
DWORD dwResult;
|
|
|
|
TraceEnter("SetFiringTimeForReply");
|
|
|
|
//
|
|
// We remove the timer the interface has queued up
|
|
//
|
|
|
|
if(g_leTimerQueueHead.Flink is &(picb->rdcRtrDiscInfo.tqiTimer.leTimerLink))
|
|
{
|
|
//
|
|
// Since this timer was the first one, it determined the firing time of the timer.
|
|
//
|
|
|
|
bReset = TRUE;
|
|
}
|
|
|
|
RemoveEntryList(&(picb->rdcRtrDiscInfo.tqiTimer.leTimerLink));
|
|
|
|
iRand = rand();
|
|
|
|
liRandomTime = RtlExtendedLargeIntegerDivide(SecsToSysUnits(RESPONSE_DELAY_INTERVAL * iRand),
|
|
RAND_MAX,
|
|
&ulRem);
|
|
|
|
liRandomTime = RtlLargeIntegerAdd(liRandomTime,SecsToSysUnits(MIN_RESPONSE_DELAY));
|
|
|
|
NtQuerySystemTime(&liCurrentTime);
|
|
|
|
picb->rdcRtrDiscInfo.tqiTimer.liFiringTime = RtlLargeIntegerAdd(liCurrentTime,liRandomTime);
|
|
|
|
//
|
|
// Insert into sorted list
|
|
//
|
|
|
|
for(pleNode = g_leTimerQueueHead.Flink;
|
|
pleNode isnot &g_leTimerQueueHead;
|
|
pleNode = pleNode->Flink)
|
|
{
|
|
pOldTime = CONTAINING_RECORD(pleNode,TIMER_QUEUE_ITEM,leTimerLink);
|
|
|
|
if(RtlLargeIntegerGreaterThan(pOldTime->liFiringTime,
|
|
picb->rdcRtrDiscInfo.tqiTimer.liFiringTime))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
pleNode = pleNode->Blink;
|
|
|
|
InsertHeadList(pleNode,
|
|
&(picb->rdcRtrDiscInfo.tqiTimer.leTimerLink));
|
|
|
|
if((pleNode is &g_leTimerQueueHead) or bReset)
|
|
{
|
|
//
|
|
// We inserted ourselves at the head of the queue, or took the timer off the front of the
|
|
// queue
|
|
//
|
|
|
|
pOldTime = CONTAINING_RECORD(g_leTimerQueueHead.Flink,TIMER_QUEUE_ITEM,leTimerLink);
|
|
|
|
if(!SetWaitableTimer(g_hRtrDiscTimer,
|
|
&pOldTime->liFiringTime,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
FALSE))
|
|
{
|
|
dwResult = GetLastError();
|
|
|
|
Trace1(ERR,
|
|
"SetFiringTimeForReply: Error %d setting waitable timer",
|
|
dwResult);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
WORD
|
|
Compute16BitXSum(
|
|
IN VOID UNALIGNED *pvData,
|
|
IN DWORD dwNumBytes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
|
|
Locks
|
|
|
|
|
|
Arguments
|
|
|
|
|
|
Return Value
|
|
|
|
16 Bit one's complement of the one's complement sum of dwNumBytes starting
|
|
at pData
|
|
|
|
--*/
|
|
|
|
{
|
|
REGISTER WORD UNALIGNED *pwStart;
|
|
REGISTER DWORD dwNumWords,i;
|
|
REGISTER DWORD dwSum = 0;
|
|
|
|
pwStart = (PWORD)pvData;
|
|
|
|
//
|
|
// If there are odd numbered bytes, that has to be handled differently
|
|
// However we can never have odd numbered bytes in our case so we optimize.
|
|
//
|
|
|
|
|
|
dwNumWords = dwNumBytes/2;
|
|
|
|
for(i = 0; i < dwNumWords; i++)
|
|
{
|
|
dwSum += pwStart[i];
|
|
}
|
|
|
|
//
|
|
// Add any carry
|
|
//
|
|
|
|
dwSum = (dwSum & 0x0000FFFF) + (dwSum >> 16);
|
|
dwSum = dwSum + (dwSum >> 16);
|
|
|
|
return LOWORD((~(DWORD_PTR)dwSum));
|
|
}
|