windows-nt/Source/XPSP1/NT/net/rras/ip/rtrmgr/mhrtbt.c

944 lines
21 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
net\routing\ip\rtrmgr\mhrtbt.c
Abstract:
Multicast heartbeat
Revision History:
Amritansh Raghav
--*/
#include "allinc.h"
HANDLE g_hMHbeatSocketEvent;
DWORD
SetMHeartbeatInfo(
IN PICB picb,
IN PRTR_INFO_BLOCK_HEADER pInfoHdr
)
/*++
Routine Description
Sets multicast heartbeat information passed to the ICB.
Locks
Must be called with ICB_LIST lock held as WRITER
Arguments
picb The ICB of the interface for whom the multicast hearbeat
related variables have to be set
pInfoHdr Interface Info header
Return Value
None
--*/
{
PMCAST_HBEAT_INFO pInfo;
PRTR_TOC_ENTRY pToc;
DWORD dwResult;
PMCAST_HBEAT_CB pHbeatCb;
TraceEnter("SetMHeartbeatInfo");
pHbeatCb = &picb->mhcHeartbeatInfo;
pToc = GetPointerToTocEntry(IP_MCAST_HEARBEAT_INFO,
pInfoHdr);
if(!pToc)
{
//
// Leave things as they are
//
TraceLeave("SetMHeartbeatInfo");
return NO_ERROR;
}
pInfo = (PMCAST_HBEAT_INFO)GetInfoFromTocEntry(pInfoHdr,
pToc);
if((pToc->InfoSize is 0) or (pInfo is NULL))
{
//
// If the size is zero, stop detecting
//
DeActivateMHeartbeat(picb);
//
// Also, blow away any old info
//
ZeroMemory(pHbeatCb,
sizeof(MCAST_HBEAT_CB));
//
// Set the socket to invalid
//
pHbeatCb->sHbeatSocket = INVALID_SOCKET;
return NO_ERROR;
}
//
// Set the info present. We dont care if resolution is in progress
// because it will find that the name has changed or that detection has
// been deactivated and will not do anything
//
//
// If the address protocol or port changes deactivate the heartbeat
//
if((pInfo->bActive is FALSE) or
(wcsncmp(pInfo->pwszGroup,
pHbeatCb->pwszGroup,
MAX_GROUP_LEN) isnot 0) or
(pInfo->byProtocol isnot pHbeatCb->byProtocol) or
(pInfo->wPort isnot pHbeatCb->wPort))
{
DeActivateMHeartbeat(picb);
}
//
// Copy out the info
//
wcsncpy(pHbeatCb->pwszGroup,
pInfo->pwszGroup,
MAX_GROUP_LEN);
pHbeatCb->pwszGroup[MAX_GROUP_LEN - 1] = UNICODE_NULL;
pHbeatCb->wPort = pInfo->wPort;
pHbeatCb->byProtocol = pInfo->byProtocol;
pHbeatCb->ullDeadInterval =
(ULONGLONG)(60 * SYS_UNITS_IN_1_SEC * pInfo->ulDeadInterval);
//
// Leave the group and socket as they are
//
//
// If the info says that detection should be on, but the i/f is not
// detecting, either it is being switched on or it was deactivated due
// to a info change and needs to be on
//
dwResult = NO_ERROR;
if((pHbeatCb->bActive is FALSE) and
(pInfo->bActive is TRUE))
{
pHbeatCb->bActive = TRUE;
dwResult = ActivateMHeartbeat(picb);
if(dwResult isnot NO_ERROR)
{
Trace2(ERR,
"SetMHeartbeatInfo: Error %d activating hbeat for %S",
GetLastError(),
picb->pwszName);
ZeroMemory(pHbeatCb,
sizeof(MCAST_HBEAT_CB));
}
}
TraceLeave("SetMHeartbeatInfo");
return dwResult;
}
DWORD
GetMHeartbeatInfo(
PICB picb,
PRTR_TOC_ENTRY pToc,
PBYTE pbDataPtr,
PRTR_INFO_BLOCK_HEADER pInfoHdr,
PDWORD pdwSize
)
/*++
Routine Description
Gets the multicast hearbeat info related to the interface
Locks
Called with ICB_LIST lock held as READER
Arguments
picb The ICB of the interface whose multicast heartbeat 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
--*/
{
PMCAST_HBEAT_INFO pInfo;
PMCAST_HBEAT_CB pHbeatCb;
TraceEnter("GetMHeartbeatInfo");
if(*pdwSize < sizeof(MCAST_HBEAT_INFO))
{
*pdwSize = sizeof(MCAST_HBEAT_INFO);
TraceLeave("GetMHeartbeatInfo");
return ERROR_INSUFFICIENT_BUFFER;
}
*pdwSize = pToc->InfoSize = sizeof(MCAST_HBEAT_INFO);
//pToc->InfoVersion IP_MCAST_HEARBEAT_INFO;
pToc->InfoType = IP_MCAST_HEARBEAT_INFO;
pToc->Count = 1;
pToc->Offset = (ULONG)(pbDataPtr - (PBYTE) pInfoHdr);
pInfo = (PMCAST_HBEAT_INFO)pbDataPtr;
pHbeatCb = &picb->mhcHeartbeatInfo;
wcsncpy(pHbeatCb->pwszGroup,
pInfo->pwszGroup,
MAX_GROUP_LEN);
pHbeatCb->pwszGroup[MAX_GROUP_LEN - 1] = UNICODE_NULL;
pInfo->bActive = pHbeatCb->bActive;
pInfo->byProtocol = pHbeatCb->byProtocol;
pInfo->wPort = pHbeatCb->wPort;
pInfo->ulDeadInterval =
(ULONG)(pHbeatCb->ullDeadInterval/(60 * SYS_UNITS_IN_1_SEC));
TraceLeave("GetMHeartbeatInfo");
return NO_ERROR;
}
DWORD
ActivateMHeartbeat(
PICB picb
)
/*++
Routine Description
Function to activate heartbeat detection. If there is no info or the
detection is configured to be inactive, we quit. We try to get the
group address. If a name is given we queue a worker to resolve the
group name, otherwise we start detection
Locks
ICB_LIST lock held as WRITER
Arguments
picb ICB of the interface to activate
Return Value
--*/
{
PMCAST_HBEAT_CB pHbeatCb;
CHAR pszGroup[MAX_GROUP_LEN];
PHEARTBEAT_CONTEXT pContext;
DWORD dwResult;
TraceEnter("ActivateMHeartbeat");
pHbeatCb = &picb->mhcHeartbeatInfo;
if((pHbeatCb->bActive is FALSE) or
(pHbeatCb->bResolutionInProgress is TRUE))
{
return NO_ERROR;
}
//
// Convert to ansi
//
WideCharToMultiByte(CP_ACP,
0,
pHbeatCb->pwszGroup,
-1,
pszGroup,
MAX_GROUP_LEN,
NULL,
NULL);
pHbeatCb->dwGroup = inet_addr((CONST CHAR *)pszGroup);
if(pHbeatCb->dwGroup is INADDR_NONE)
{
//
// we need to resolve the name. This will be done in a
// worker function. Create a context for the function and
// queue it
//
pContext = HeapAlloc(IPRouterHeap,
0,
sizeof(HEARTBEAT_CONTEXT));
if(pContext is NULL)
{
Trace2(ERR,
"SetMHeartbeatInfo: Error %d allocating context for %S",
GetLastError(),
picb->pwszName);
return ERROR_NOT_ENOUGH_MEMORY;
}
pContext->dwIfIndex = picb->dwIfIndex;
pContext->picb = picb;
CopyMemory(&pContext->pwszGroup,
pHbeatCb->pwszGroup,
sizeof(MCAST_HBEAT_INFO));
dwResult = QueueAsyncFunction(ResolveHbeatName,
pContext,
FALSE);
if(dwResult isnot NO_ERROR)
{
HeapFree(IPRouterHeap,
0,
pContext);
Trace2(ERR,
"SetMHeartbeatInfo: Error %d queuing worker for %S",
GetLastError(),
picb->pwszName);
return dwResult;
}
pHbeatCb->bResolutionInProgress = TRUE;
return NO_ERROR;
}
//
// No need to do name resultion. Just start
//
dwResult = StartMHeartbeat(picb);
if(dwResult isnot NO_ERROR)
{
Trace2(ERR,
"SetMHeartbeatInfo: Error %d starting hbeat for %S",
dwResult,
picb->pwszName);
}
return dwResult;
}
DWORD
StartMHeartbeat(
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
--*/
{
PMCAST_HBEAT_CB pHbeatCb;
DWORD dwResult;
TraceEnter("ActivateMHeartbeat");
if((picb->dwAdminState isnot IF_ADMIN_STATUS_UP) or
(picb->dwOperationalState < IF_OPER_STATUS_CONNECTING))
{
TraceLeave("ActivateMHeartbeat");
return NO_ERROR;
}
pHbeatCb = &picb->mhcHeartbeatInfo;
dwResult = CreateHbeatSocket(picb);
if(dwResult isnot NO_ERROR)
{
Trace2(ERR,
"ActivateMHeartbeat: Couldnt create socket for %S. Error %d",
picb->pwszName,
dwResult);
TraceLeave("ActivateMHeartbeat");
return dwResult;
}
//
// Yes we are active
//
pHbeatCb->bActive = TRUE;
TraceLeave("ActivateMHeartbeat");
return NO_ERROR;
}
DWORD
CreateHbeatSocket(
IN PICB picb
)
/*++
Routine Description
Creates a socket to listen to multicast hearbeat messages
Locks
ICB_LIST lock must be held as WRITER
Arguments
picb The ICB of the interface for which the socket has to be created
Return Value
NO_ERROR or some error code
--*/
{
PMCAST_HBEAT_CB pHbeatCb;
DWORD i, dwResult, dwBytesReturned;
struct linger lingerOption;
BOOL bOption, bLoopback;
SOCKADDR_IN sinSockAddr;
struct ip_mreq imOption;
TraceEnter("CreateHbeatSocket");
if(picb->bBound)
{
Trace1(ERR,
"CreateHbeatSocket: Can not activate heartbeat on %S as it is not bound",
picb->pwszName);
TraceLeave("CreateHbeatSocket");
return ERROR_CAN_NOT_COMPLETE;
}
//
// Create the sockets for the interface
//
pHbeatCb = &(picb->mhcHeartbeatInfo);
pHbeatCb->sHbeatSocket = INVALID_SOCKET;
if(pHbeatCb->byProtocol is IPPROTO_RAW)
{
//
// If we are raw proto, then the port number contains protocol
//
pHbeatCb->sHbeatSocket = WSASocket(AF_INET,
SOCK_RAW,
LOBYTE(pHbeatCb->wPort),
NULL,
0,
MHBEAT_SOCKET_FLAGS);
}
else
{
IpRtAssert(pHbeatCb->byProtocol is IPPROTO_UDP);
pHbeatCb->sHbeatSocket = WSASocket(AF_INET,
SOCK_DGRAM,
IPPROTO_UDP,
NULL,
0,
MHBEAT_SOCKET_FLAGS);
}
if(pHbeatCb->sHbeatSocket is INVALID_SOCKET)
{
dwResult = WSAGetLastError();
Trace2(ERR,
"CreateHbeatSocket: Couldnt create socket on %S. Error %d",
picb->pwszName,
dwResult);
TraceLeave("CreateHbeatSocket");
return dwResult;
}
#if 0
//
// Set to SO_DONTLINGER
//
bOption = TRUE;
if(setsockopt(pHbeatCb->sHbeatSocket,
SOL_SOCKET,
SO_DONTLINGER,
(const char FAR*)&bOption,
sizeof(BOOL)) is SOCKET_ERROR)
{
Trace1(ERR,
"CreateHbeatSocket: Couldnt set linger option - continuing. Error %d",
WSAGetLastError());
}
#endif
//
// Set to SO_REUSEADDR
//
bOption = TRUE;
if(setsockopt(pHbeatCb->sHbeatSocket,
SOL_SOCKET,
SO_REUSEADDR,
(const char FAR*)&bOption,
sizeof(BOOL)) is SOCKET_ERROR)
{
Trace1(ERR,
"CreateHbeatSocket: Couldnt set reuse option - continuing. Error %d",
WSAGetLastError());
}
//
// we are interested in READ events only and want the event to be set
// for those
//
if(WSAEventSelect(pHbeatCb->sHbeatSocket,
g_hMHbeatSocketEvent,
FD_READ) is SOCKET_ERROR)
{
dwResult = WSAGetLastError();
Trace2(ERR,
"CreateHbeatSocket: WSAEventSelect() failed for socket on %S.Error %d",
picb->pwszName,
dwResult);
closesocket(pHbeatCb->sHbeatSocket);
pHbeatCb->sHbeatSocket = INVALID_SOCKET;
return dwResult;
}
//
// Bind to one of the addresses on the interface. We just bind to the
// first address (and the port if specified)
//
sinSockAddr.sin_family = AF_INET;
sinSockAddr.sin_addr.s_addr = picb->pibBindings[0].dwAddress;
if(pHbeatCb->byProtocol is IPPROTO_UDP)
{
sinSockAddr.sin_port = pHbeatCb->wPort;
}
else
{
sinSockAddr.sin_port = 0;
}
if(bind(pHbeatCb->sHbeatSocket,
(const struct sockaddr FAR*)&sinSockAddr,
sizeof(SOCKADDR_IN)) is SOCKET_ERROR)
{
dwResult = WSAGetLastError();
Trace3(ERR,
"CreateHbeatSocket: Couldnt bind to %s on interface %S. Error %d",
inet_ntoa(*(PIN_ADDR)&(picb->pibBindings[0].dwAddress)),
picb->pwszName,
dwResult);
closesocket(pHbeatCb->sHbeatSocket);
pHbeatCb->sHbeatSocket = INVALID_SOCKET;
return dwResult;
}
#if 0
//
// Join the multicast session
//
sinSockAddr.sin_family = AF_INET;
sinSockAddr.sin_addr.s_addr = pHbeatCb->dwGroup;
sinSockAddr.sin_port = 0;
if(WSAJoinLeaf(pHbeatCb->sHbeatSocket,
(const struct sockaddr FAR*)&sinSockAddr,
sizeof(SOCKADDR_IN),
NULL,
NULL,
NULL,
NULL,
JL_BOTH) is INVALID_SOCKET)
{
dwResult = WSAGetLastError();
Trace2(ERR,
"CreateHbeatSocket: Couldnt join multicast group over %s on %S",
inet_ntoa(*(PIN_ADDR)&(picb->pibBindings[i].dwAddress)),
picb->pwszName);
closesocket(pHbeatCb->sHbeatSocket);
pHbeatCb->sHbeatSocket = INVALID_SOCKET;
return dwResult;
}
#else
sinSockAddr.sin_addr.s_addr = picb->pibBindings[0].dwAddress;
if(setsockopt(pHbeatCb->sHbeatSocket,
IPPROTO_IP,
IP_MULTICAST_IF,
(PBYTE)&sinSockAddr.sin_addr,
sizeof(IN_ADDR)) is SOCKET_ERROR)
{
dwResult = WSAGetLastError();
Trace2(ERR,
"CreateHbeatSocket: Couldnt enable mcast over %s on %S",
inet_ntoa(*(PIN_ADDR)&(picb->pibBindings[0].dwAddress)),
picb->pwszName);
closesocket(pHbeatCb->sHbeatSocket);
pHbeatCb->sHbeatSocket = INVALID_SOCKET;
return dwResult;
}
imOption.imr_multiaddr.s_addr = pHbeatCb->dwGroup;
imOption.imr_interface.s_addr = picb->pibBindings[0].dwAddress;
if(setsockopt(pHbeatCb->sHbeatSocket,
IPPROTO_IP,
IP_ADD_MEMBERSHIP,
(PBYTE)&imOption,
sizeof(imOption)) is SOCKET_ERROR)
{
dwResult = WSAGetLastError();
Trace3(ERR,
"CreateHbeatSocket: Couldnt join %d.%d.%d.%d on socket over %s on %S",
PRINT_IPADDR(pHbeatCb->dwGroup),
inet_ntoa(*(PIN_ADDR)&(picb->pibBindings[0].dwAddress)),
picb->pwszName);
closesocket(pHbeatCb->sHbeatSocket);
pHbeatCb->sHbeatSocket = INVALID_SOCKET;
return dwResult;
}
#endif
TraceLeave("CreateHbeatSocket");
return NO_ERROR;
}
VOID
DeleteHbeatSocket(
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
--*/
{
PMCAST_HBEAT_CB pHbeatCb;
DWORD i;
pHbeatCb = &(picb->mhcHeartbeatInfo);
if(pHbeatCb->sHbeatSocket isnot INVALID_SOCKET)
{
closesocket(pHbeatCb->sHbeatSocket);
}
pHbeatCb->sHbeatSocket = INVALID_SOCKET;
}
DWORD
DeActivateMHeartbeat(
IN PICB picb
)
{
PMCAST_HBEAT_CB pHbeatCb;
TraceEnter("DeActivateMHeartbeat");
pHbeatCb = &(picb->mhcHeartbeatInfo);
if(!pHbeatCb->bActive)
{
return NO_ERROR;
}
DeleteHbeatSocket(picb);
pHbeatCb->bActive = FALSE;
TraceLeave("DeActivateMHeartbeat");
return NO_ERROR;
}
VOID
HandleMHeartbeatMessages(
VOID
)
/*++
Routine Description
Locks
Arguments
Return Value
--*/
{
PLIST_ENTRY pleNode;
PICB picb;
DWORD i, dwResult, dwRcvAddrLen, dwSizeOfHeader;
DWORD dwBytesRead, dwFlags;
WSANETWORKEVENTS wsaNetworkEvents;
SOCKADDR_IN sinFrom;
WSABUF wsaRcvBuf;
SYSTEMTIME stSysTime;
ULARGE_INTEGER uliTime;
wsaRcvBuf.len = 0;
wsaRcvBuf.buf = NULL;
GetSystemTime(&stSysTime);
SystemTimeToFileTime(&stSysTime,
(PFILETIME)&uliTime);
TraceEnter("HandleMHeartbeatMessages");
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
// multicast heartbeat detection, we wouldnt have
// opened a socket on it so the FD_READ notification cant be for it
//
if((picb->bBound is FALSE) or
(picb->mhcHeartbeatInfo.bActive is FALSE))
{
continue;
}
if(picb->mhcHeartbeatInfo.sHbeatSocket is INVALID_SOCKET)
{
continue;
}
if(WSAEnumNetworkEvents(picb->mhcHeartbeatInfo.sHbeatSocket,
NULL,
&wsaNetworkEvents) is SOCKET_ERROR)
{
dwResult = GetLastError();
Trace1(ERR,
"HandleMHeartbeatMessages: 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)
{
Trace2(ERR,
"HandleMHeartbeatMessages: Error %d associated with socket on %S for FD_READ",
wsaNetworkEvents.iErrorCode[FD_READ_BIT],
picb->pwszName);
continue;
}
dwRcvAddrLen = sizeof(SOCKADDR_IN);
dwFlags = 0;
//
// We dont want the data, we just want to clear out the read
// notification
//
dwResult = WSARecvFrom(picb->mhcHeartbeatInfo.sHbeatSocket,
&wsaRcvBuf,
1,
&dwBytesRead,
&dwFlags,
(struct sockaddr FAR*)&sinFrom,
&dwRcvAddrLen,
NULL,
NULL);
if(dwResult is SOCKET_ERROR)
{
dwResult = WSAGetLastError();
if(dwResult isnot WSAEMSGSIZE)
{
Trace3(ERR,
"HandleMHeartbeatMessages: Error %d in WSARecvFrom on %S. Bytes read %d",
dwResult,
picb->pwszName,
dwBytesRead);
continue;
}
}
//
// If the message is on the group we need to hear from
// then update the last heard time
//
picb->mhcHeartbeatInfo.ullLastHeard = uliTime.QuadPart;
}
TraceLeave("HandleMHeartbeatMessages");
}