windows-nt/Source/XPSP1/NT/net/rras/ip/rtrmgr/rtrdisc.c
2020-09-26 16:20:57 +08:00

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));
}