2577 lines
78 KiB
C
2577 lines
78 KiB
C
|
//=============================================================================
|
||
|
// Copyright (c) 1997 Microsoft Corporation
|
||
|
// File: work.c
|
||
|
//
|
||
|
// Abstract:
|
||
|
// Implements the work items that are queued by igmp routines.
|
||
|
//
|
||
|
// Author: K.S.Lokesh (lokeshs@) 11-1-97
|
||
|
//
|
||
|
// Revision History:
|
||
|
//=============================================================================
|
||
|
|
||
|
|
||
|
#include "pchigmp.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
|
||
|
//
|
||
|
// should each packet be queued to another work item again
|
||
|
//
|
||
|
#define BQUEUE_WORK_ITEM_FOR_PACKET 1
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _WT_ProcessInputEvent
|
||
|
// called in the wait worker thread when the packet event is set.
|
||
|
// Queues: _WF_ProcessInputEvent()
|
||
|
// Runs in: WaitServerThread context
|
||
|
//------------------------------------------------------------------------------
|
||
|
VOID
|
||
|
WT_ProcessInputEvent(
|
||
|
PVOID pContext, // psee entry. the entry might have been deleted.
|
||
|
BOOLEAN NotUsed
|
||
|
)
|
||
|
{
|
||
|
HANDLE WaitHandle ;
|
||
|
|
||
|
//
|
||
|
// set the InputWaitEvent to NULL so that UnregisterWaitEx is not called.
|
||
|
// psee will be valid here, but might not be once queued to the worker Fn.
|
||
|
//
|
||
|
|
||
|
PSOCKET_EVENT_ENTRY psee = (PSOCKET_EVENT_ENTRY) pContext;
|
||
|
|
||
|
if (!EnterIgmpApi())
|
||
|
return;
|
||
|
|
||
|
Trace0(WORKER, "_WF_ProcessInputEvent queued by WaitThread");
|
||
|
|
||
|
|
||
|
// make a non-blocking UnregisterWaitEx call
|
||
|
|
||
|
WaitHandle = InterlockedExchangePointer(&psee->InputWaitEvent, NULL);
|
||
|
|
||
|
if (WaitHandle)
|
||
|
UnregisterWaitEx( WaitHandle, NULL ) ;
|
||
|
|
||
|
|
||
|
QueueIgmpWorker(WF_ProcessInputEvent, pContext);
|
||
|
|
||
|
LeaveIgmpApi();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _WF_ProcessInputEvent
|
||
|
// Called by: _WT_ProcessInputEvent()
|
||
|
// Locks:
|
||
|
// Acquire socketsLockShared. Either queue processing the packet to
|
||
|
// _WF_ProcessPacket() or take shared interface lock and process the packet.
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
WF_ProcessInputEvent (
|
||
|
PVOID pContext
|
||
|
)
|
||
|
{
|
||
|
DWORD Error = NO_ERROR;
|
||
|
PIF_TABLE_ENTRY pite;
|
||
|
PLIST_ENTRY ple, pHead;
|
||
|
WSANETWORKEVENTS wsane;
|
||
|
PSOCKET_EVENT_ENTRY psee = (PSOCKET_EVENT_ENTRY) pContext,
|
||
|
pseeTmp;
|
||
|
PSOCKET_ENTRY pse;
|
||
|
|
||
|
|
||
|
if (!EnterIgmpWorker()) return;
|
||
|
Trace0(ENTER1, "Entering _WF_ProcessInputEvent");
|
||
|
|
||
|
|
||
|
ACQUIRE_SOCKETS_LOCK_SHARED("_WF_ProcessInputEvent");
|
||
|
|
||
|
//
|
||
|
// make sure that the psee entry still exists
|
||
|
//
|
||
|
pHead = &g_ListOfSocketEvents;
|
||
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
||
|
pseeTmp = CONTAINING_RECORD(ple, SOCKET_EVENT_ENTRY, LinkBySocketEvents);
|
||
|
if (pseeTmp==psee)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (ple==pHead) {
|
||
|
RELEASE_SOCKETS_LOCK_SHARED("_WF_ProcessInputEvent");
|
||
|
Trace0(ERR, "Input Event received on deleted SocketEvent. not an error");
|
||
|
LeaveIgmpWorker();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// go through the list of active interfaces
|
||
|
// processing sockets which have input packets
|
||
|
//
|
||
|
|
||
|
pHead = &psee->ListOfInterfaces;
|
||
|
|
||
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
||
|
|
||
|
pse = CONTAINING_RECORD(ple, SOCKET_ENTRY, LinkByInterfaces);
|
||
|
pite = CONTAINING_RECORD(pse, IF_TABLE_ENTRY, SocketEntry);
|
||
|
|
||
|
//
|
||
|
// process only activated interfaces. (Proxy wont be on this list)
|
||
|
//
|
||
|
if (!IS_IF_ACTIVATED(pite))
|
||
|
continue;
|
||
|
|
||
|
|
||
|
//
|
||
|
// process input event
|
||
|
//
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
|
||
|
if (pse->Socket == INVALID_SOCKET)
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
|
||
|
//
|
||
|
// enumerate network events to see whether
|
||
|
// any packets have arrived on this interface
|
||
|
//
|
||
|
Error = WSAEnumNetworkEvents(pse->Socket, NULL, &wsane);
|
||
|
|
||
|
if (Error != NO_ERROR) {
|
||
|
Trace3(RECEIVE,
|
||
|
"error %d checking for input on interface %d (%d.%d.%d.%d)",
|
||
|
Error, pite->IfIndex, PRINT_IPADDR(pite->IpAddr));
|
||
|
Logwarn1(ENUM_NETWORK_EVENTS_FAILED, "%I", pite->IpAddr, Error);
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
if (!(wsane.lNetworkEvents & FD_READ))
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
|
||
|
//
|
||
|
// the input flag is set, now see if there was an error
|
||
|
//
|
||
|
|
||
|
if (wsane.iErrorCode[FD_READ_BIT] != NO_ERROR) {
|
||
|
Trace3(RECEIVE,
|
||
|
"error %d in input record for interface %d (%d.%d.%d.%d)",
|
||
|
wsane.iErrorCode[FD_READ_BIT], pite->IfIndex,
|
||
|
PRINT_IPADDR(pite->IpAddr)
|
||
|
);
|
||
|
Logwarn1(INPUT_RECORD_ERROR, "%I", pite->IpAddr, Error);
|
||
|
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Process the packet received on the interface
|
||
|
//
|
||
|
|
||
|
ProcessInputOnInterface(pite);
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
} //for loop: for each interface
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// register the event with the wait thread for future receives
|
||
|
//
|
||
|
|
||
|
if (g_RunningStatus!=IGMP_STATUS_STOPPING) {
|
||
|
|
||
|
DWORD dwRetval;
|
||
|
|
||
|
if (! RegisterWaitForSingleObject(
|
||
|
&psee->InputWaitEvent,
|
||
|
psee->InputEvent,
|
||
|
WT_ProcessInputEvent,
|
||
|
(VOID*)psee,
|
||
|
INFINITE,
|
||
|
(WT_EXECUTEINWAITTHREAD)|(WT_EXECUTEONLYONCE)
|
||
|
))
|
||
|
{
|
||
|
dwRetval = GetLastError();
|
||
|
Trace1(ERR, "error %d RtlRegisterWait", dwRetval);
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
RELEASE_SOCKETS_LOCK_SHARED("_WF_ProcessInputEvent");
|
||
|
|
||
|
|
||
|
LeaveIgmpWorker();
|
||
|
|
||
|
Trace0(LEAVE1, "leaving _WF_ProcessInputEvent()\n");
|
||
|
Trace0(LEAVE, ""); //putting a newline
|
||
|
return;
|
||
|
|
||
|
} //end _WF_ProcessInputEvent
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _ProcessInputOnInterface
|
||
|
// Does some minimal checking of packet length, etc. We can either queue to
|
||
|
// work item(_WF_ProcessPacket) or run it here itself.
|
||
|
//
|
||
|
// Called by: _WF_ProcessInputEvent()
|
||
|
// Locks: Assumes socket lock. Either queues the packet to _WF_ProcessPacket or
|
||
|
// takes shared interface lock and processes it here itself.
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
ProcessInputOnInterface(
|
||
|
PIF_TABLE_ENTRY pite
|
||
|
)
|
||
|
{
|
||
|
WSABUF WsaBuf;
|
||
|
DWORD dwNumBytes, dwFlags, dwAddrLen;
|
||
|
SOCKADDR_IN saSrcAddr;
|
||
|
DWORD dwSrcAddr, DstnMcastAddr;
|
||
|
DWORD Error = NO_ERROR;
|
||
|
UCHAR *pPacket;
|
||
|
UCHAR IpHdrLen;
|
||
|
PIP_HEADER pIpHdr;
|
||
|
BOOL bRtrAlertSet = FALSE;
|
||
|
PBYTE Buffer;
|
||
|
|
||
|
|
||
|
WsaBuf.len = pite->Info.PacketSize;
|
||
|
WsaBuf.buf = IGMP_ALLOC(WsaBuf.len, 0x800040, pite->IfIndex);
|
||
|
PROCESS_ALLOC_FAILURE2(WsaBuf.buf,
|
||
|
"error %d allocating %d bytes for input packet",
|
||
|
Error, WsaBuf.len,
|
||
|
return);
|
||
|
Buffer = WsaBuf.buf;
|
||
|
|
||
|
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
|
||
|
//
|
||
|
// read the incoming packet
|
||
|
//
|
||
|
|
||
|
dwAddrLen = sizeof(SOCKADDR_IN);
|
||
|
|
||
|
dwAddrLen = sizeof (saSrcAddr);
|
||
|
dwFlags = 0;
|
||
|
|
||
|
|
||
|
Error = WSARecvFrom(pite->SocketEntry.Socket, &WsaBuf, 1, &dwNumBytes,
|
||
|
&dwFlags, (SOCKADDR FAR *)&saSrcAddr, &dwAddrLen,
|
||
|
NULL, NULL);
|
||
|
|
||
|
|
||
|
// check if any error in reading packet
|
||
|
|
||
|
if ((Error!=0)||(dwNumBytes == 0)) {
|
||
|
Error = WSAGetLastError();
|
||
|
Trace2(RECEIVE, "error %d receiving packet on interface %d)",
|
||
|
Error, pite->IfIndex);
|
||
|
Logerr1(RECVFROM_FAILED, "%I", pite->IpAddr, Error);
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// dont ignore the packet even if it is from a local address
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// set packet ptr, IpHdr ptr, dwNumBytes, SrcAddr, DstnMcastAddr
|
||
|
//
|
||
|
|
||
|
// set source addr of packet
|
||
|
dwSrcAddr = saSrcAddr.sin_addr.s_addr;
|
||
|
|
||
|
IpHdrLen = (Buffer[0]&0x0F)*4;
|
||
|
|
||
|
pPacket = &Buffer[IpHdrLen];
|
||
|
dwNumBytes -= IpHdrLen;
|
||
|
pIpHdr = (PIP_HEADER)Buffer;
|
||
|
DstnMcastAddr = (ULONG)pIpHdr->Dstn.s_addr;
|
||
|
|
||
|
|
||
|
//
|
||
|
// verify that the packet has igmp type
|
||
|
//
|
||
|
if (pIpHdr->Protocol!=0x2) {
|
||
|
Trace5(RECEIVE,
|
||
|
"Packet received with IpDstnAddr(%d.%d.%d.%d) %d.%d.%d.%d from(%s) on "
|
||
|
"IF:%0x is not of Igmp type(%d)",
|
||
|
PRINT_IPADDR(pIpHdr->Dstn.s_addr), PRINT_IPADDR(pIpHdr->Src.s_addr),
|
||
|
PRINT_IPADDR(dwSrcAddr), pite->IfIndex, pIpHdr->Protocol
|
||
|
);
|
||
|
Error = ERROR_CAN_NOT_COMPLETE;
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// check if packet has router alert option
|
||
|
//
|
||
|
{
|
||
|
PBYTE pOption = (PBYTE)(pIpHdr+1);
|
||
|
UCHAR i;
|
||
|
|
||
|
for (i=0; i<IpHdrLen-20; i+=4) {
|
||
|
|
||
|
if ( (pOption[0]==148) && (pOption[1]==4) ) {
|
||
|
bRtrAlertSet = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (BQUEUE_WORK_ITEM_FOR_PACKET) {
|
||
|
|
||
|
PACKET_CONTEXT UNALIGNED *pPktContext;
|
||
|
|
||
|
//
|
||
|
// allocate and initialize a packet-context
|
||
|
//
|
||
|
CREATE_PACKET_CONTEXT(pPktContext, dwNumBytes, Error);
|
||
|
if (Error!=NO_ERROR)
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
|
||
|
pPktContext->IfIndex = pite->IfIndex;
|
||
|
pPktContext->DstnMcastAddr = DstnMcastAddr;
|
||
|
pPktContext->InputSrc = dwSrcAddr;
|
||
|
pPktContext->Length = dwNumBytes;
|
||
|
pPktContext->Flags = bRtrAlertSet;
|
||
|
CopyMemory(pPktContext->Packet, pPacket, dwNumBytes);
|
||
|
|
||
|
|
||
|
//
|
||
|
// enqueue the work-item to process the packet
|
||
|
//
|
||
|
|
||
|
Error = QueueIgmpWorker(WF_ProcessPacket, (PVOID)pPktContext);
|
||
|
|
||
|
Trace2(WORKER, "Queuing IgmpWorker function: %s in %s",
|
||
|
"WF_ProcessPacket:", "ProcessInputOnInterface");
|
||
|
|
||
|
if (Error != NO_ERROR) {
|
||
|
Trace1(ERR, "error %d queueing work-item for packet", Error);
|
||
|
Logerr0(QUEUE_WORKER_FAILED, Error);
|
||
|
IGMP_FREE(pPktContext);
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// process the packet here itself
|
||
|
//
|
||
|
else {
|
||
|
|
||
|
ACQUIRE_IF_LOCK_SHARED(pite->IfIndex, "_ProcessInputOnInterface");
|
||
|
|
||
|
ProcessPacket(pite, dwSrcAddr, DstnMcastAddr, dwNumBytes, pPacket,
|
||
|
bRtrAlertSet);
|
||
|
|
||
|
RELEASE_IF_LOCK_SHARED(pite->IfIndex, "_ProcessInputOnInterface");
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
IGMP_FREE(WsaBuf.buf);
|
||
|
|
||
|
return;
|
||
|
|
||
|
} //end _ProcessInputOnInterface
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _WF_ProcessPacket
|
||
|
// Queued by: _ProcessInputOnInterface()
|
||
|
// Locks: takes shared interface lock
|
||
|
// Calls: _ProcessPacket()
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
WF_ProcessPacket (
|
||
|
PVOID pvContext
|
||
|
)
|
||
|
{
|
||
|
PPACKET_CONTEXT pPktContext = (PPACKET_CONTEXT)pvContext;
|
||
|
DWORD IfIndex = pPktContext->IfIndex;
|
||
|
PIF_TABLE_ENTRY pite;
|
||
|
|
||
|
|
||
|
if (!EnterIgmpWorker()) { return; }
|
||
|
Trace0(ENTER1, "Entering _WF_ProcessPacket()");
|
||
|
|
||
|
ACQUIRE_IF_LOCK_SHARED(IfIndex, "_WF_ProcessPacket");
|
||
|
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
|
||
|
//
|
||
|
// retrieve the interface
|
||
|
//
|
||
|
pite = GetIfByIndex(IfIndex);
|
||
|
if (pite == NULL) {
|
||
|
Trace1(ERR, "_WF_ProcessPacket: interface %d not found", IfIndex);
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// make sure that the interface is activated
|
||
|
//
|
||
|
if (!(IS_IF_ACTIVATED(pite))) {
|
||
|
Trace1(ERR,"_WF_ProcessPacket() called for inactive IfIndex(%0x)",
|
||
|
IfIndex);
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// process the packet
|
||
|
//
|
||
|
ProcessPacket (pite, pPktContext->InputSrc, pPktContext->DstnMcastAddr,
|
||
|
pPktContext->Length, pPktContext->Packet, pPktContext->Flags);
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
|
||
|
RELEASE_IF_LOCK_SHARED(IfIndex, "_WF_ProcessPacket");
|
||
|
|
||
|
IGMP_FREE(pPktContext);
|
||
|
|
||
|
|
||
|
Trace0(LEAVE1, "Leaving _WF_ProcessPacket()");
|
||
|
LeaveIgmpWorker();
|
||
|
|
||
|
return;
|
||
|
|
||
|
} //end _WF_ProcessPacket
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#define RETURN_FROM_PROCESS_PACKET() {\
|
||
|
if (DEBUG_TIMER_PACKET&&bPrintTimerDebug) {\
|
||
|
if (Error==NO_ERROR) {\
|
||
|
Trace0(TIMER1, " ");\
|
||
|
Trace0(TIMER1, "Printing Timer Queue after _ProcessPacket");\
|
||
|
DebugPrintTimerQueue();\
|
||
|
}\
|
||
|
}\
|
||
|
if (ExitLockRelease&IF_LOCK) \
|
||
|
RELEASE_IF_LOCK_SHARED(IfIndex, "_ProcessPacket"); \
|
||
|
if (ExitLockRelease&GROUP_LOCK) \
|
||
|
RELEASE_GROUP_LOCK(Group, "_ProcessPacket"); \
|
||
|
if (ExitLockRelease&TIMER_LOCK) \
|
||
|
RELEASE_TIMER_LOCK("_ProcessPacket");\
|
||
|
Trace0(LEAVE1, "Leaving _ProcessPacket1()\n"); \
|
||
|
return; \
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _ProcessPacket
|
||
|
//
|
||
|
// Processes a packet received on an interface
|
||
|
//
|
||
|
// Locks: Assumes either shared Interface lock
|
||
|
// or shared Socket Lock.
|
||
|
// if ras interface, this procedure takes read lock on the ras table.
|
||
|
// Called by: _ProcessInputOnInterface() or _WF_ProcessPacket()
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
ProcessPacket (
|
||
|
PIF_TABLE_ENTRY pite,
|
||
|
DWORD InputSrcAddr,
|
||
|
DWORD DstnMcastAddr,
|
||
|
DWORD NumBytes,
|
||
|
PBYTE pPacketData, // igmp packet hdr. data following it ignored
|
||
|
BOOL bRtrAlertSet
|
||
|
)
|
||
|
{
|
||
|
DWORD Error = NO_ERROR;
|
||
|
DWORD IfIndex = pite->IfIndex, Group=0, IfVersion;
|
||
|
IGMP_HEADER UNALIGNED *pHdr;
|
||
|
PIF_INFO pInfo = &pite->Info;
|
||
|
PIGMP_IF_CONFIG pConfig = &pite->Config;
|
||
|
|
||
|
PRAS_TABLE prt;
|
||
|
PRAS_TABLE_ENTRY prte;
|
||
|
PRAS_CLIENT_INFO pRasInfo;
|
||
|
BOOL bRasStats = FALSE, bPrintTimerDebug=TRUE;
|
||
|
LONGLONG llCurTime = GetCurrentIgmpTime();
|
||
|
INT cmp;
|
||
|
CHAR szPacketType[30];
|
||
|
|
||
|
|
||
|
enum {
|
||
|
NO_LOCK=0,
|
||
|
IF_LOCK=0x1,
|
||
|
RAS_LOCK=0x2,
|
||
|
GROUP_LOCK=0x4,
|
||
|
TIMER_LOCK=0x8
|
||
|
} ExitLockRelease;
|
||
|
|
||
|
ExitLockRelease = 0;
|
||
|
|
||
|
IfVersion = IS_IF_VER1(pite)? 1: (IS_IF_VER2(pite)?2:3);
|
||
|
|
||
|
Trace2(ENTER1, "Entering _ProcessPacket() IfIndex(%0x) DstnMcastAddr(%d.%d.%d.%d)",
|
||
|
IfIndex, PRINT_IPADDR(DstnMcastAddr)
|
||
|
);
|
||
|
|
||
|
|
||
|
//
|
||
|
// the packet must be at least some minimum length
|
||
|
//
|
||
|
if (NumBytes < MIN_PACKET_SIZE) {
|
||
|
|
||
|
Trace4(RECEIVE,
|
||
|
"%d-byte packet from %d.%d.%d.%d on If %0x (%d.%d.%d.%d) is too small",
|
||
|
NumBytes, PRINT_IPADDR(InputSrcAddr), IfIndex, pite->IpAddr
|
||
|
);
|
||
|
Logwarn2(PACKET_TOO_SMALL, "%I%I", pite->IpAddr, InputSrcAddr, NO_ERROR);
|
||
|
|
||
|
|
||
|
InterlockedIncrement(&pite->Info.ShortPacketsReceived);
|
||
|
|
||
|
//todo: implement ras stats
|
||
|
/*if (bRasStats)
|
||
|
InterlockedIncrement(&pRasInfo->ShortPacketsReceived);
|
||
|
*/
|
||
|
bPrintTimerDebug = FALSE;
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// initialize packet fields
|
||
|
//
|
||
|
pHdr = (IGMP_HEADER UNALIGNED *) pPacketData;
|
||
|
Group = pHdr->Group;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Verify packet version
|
||
|
//
|
||
|
|
||
|
if ( (pHdr->Vertype==IGMP_QUERY)||(pHdr->Vertype==IGMP_REPORT_V1)
|
||
|
|| (pHdr->Vertype==IGMP_REPORT_V2) || (pHdr->Vertype==IGMP_REPORT_V3)
|
||
|
|| (pHdr->Vertype==IGMP_LEAVE) )
|
||
|
{
|
||
|
InterlockedIncrement(&pInfo->TotalIgmpPacketsForRouter);
|
||
|
//if (bRasStats)
|
||
|
// InterlockedIncrement(&pRasInfo->TotalIgmpPacketsForRouter);
|
||
|
}
|
||
|
else {
|
||
|
bPrintTimerDebug = FALSE;
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
switch(pHdr->Vertype) {
|
||
|
case IGMP_QUERY:
|
||
|
lstrcpy(szPacketType, "igmp-query"); break;
|
||
|
case IGMP_REPORT_V1:
|
||
|
lstrcpy(szPacketType, "igmp-report-v1"); break;
|
||
|
case IGMP_REPORT_V2:
|
||
|
lstrcpy(szPacketType, "igmp-report-v2"); break;
|
||
|
case IGMP_REPORT_V3:
|
||
|
lstrcpy(szPacketType, "igmp-report-v3"); break;
|
||
|
case IGMP_LEAVE:
|
||
|
lstrcpy(szPacketType, "igmp-leave"); break;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// check for router alert option
|
||
|
//
|
||
|
if (!bRtrAlertSet) {
|
||
|
|
||
|
InterlockedIncrement(&pInfo->PacketsWithoutRtrAlert);
|
||
|
|
||
|
if (pite->Config.Flags&IGMP_ACCEPT_RTRALERT_PACKETS_ONLY) {
|
||
|
Trace3(RECEIVE,
|
||
|
"%s packet from %d ignored on IfIndex(%d%) due to no "
|
||
|
"RtrAlert option",
|
||
|
szPacketType, PRINT_IPADDR(InputSrcAddr), IfIndex
|
||
|
);
|
||
|
|
||
|
bPrintTimerDebug = FALSE;
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Make sure that the DstnMcastAddr is a valid multicast addr
|
||
|
// or the unicast address of the router
|
||
|
//
|
||
|
|
||
|
if (!IS_MCAST_ADDR(DstnMcastAddr) && DstnMcastAddr!=pite->IpAddr) {
|
||
|
Trace2(ERR,
|
||
|
"Error! Igmp router received packet from Src(%d.%d.%d.%d) with "
|
||
|
"dstn addr(%d.%d.%d.%d) which is not valid",
|
||
|
PRINT_IPADDR(InputSrcAddr), PRINT_IPADDR(DstnMcastAddr)
|
||
|
);
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
|
||
|
bPrintTimerDebug = FALSE;
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// make sure that the interface is activated
|
||
|
//
|
||
|
if (!(IS_IF_ACTIVATED(pite))) {
|
||
|
|
||
|
Trace1(ERR,"ProcessPacket() called for inactive IfIndex(%0x)",
|
||
|
IfIndex);
|
||
|
bPrintTimerDebug = FALSE;
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
//if ras-server, then get lock on ras table.
|
||
|
//
|
||
|
if ( IS_RAS_SERVER_IF(pite->IfType) ) {
|
||
|
|
||
|
prt = pite->pRasTable;
|
||
|
|
||
|
|
||
|
//
|
||
|
// retrieve ras client by addr
|
||
|
//
|
||
|
prte = GetRasClientByAddr(InputSrcAddr, prt);
|
||
|
|
||
|
if (prte==NULL) {
|
||
|
Trace3(ERR,
|
||
|
"Got Igmp packet from an unknown ras client(%d.%d.%d.%d) on "
|
||
|
"IF(%d:%d.%d.%d.%d)",
|
||
|
PRINT_IPADDR(InputSrcAddr), IfIndex, PRINT_IPADDR(pite->IpAddr)
|
||
|
);
|
||
|
bPrintTimerDebug = FALSE;
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
// if the ras-client is not active, then return
|
||
|
if (prte->Status&DELETED_FLAG)
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
#endif
|
||
|
|
||
|
// should I update ras client stats
|
||
|
bRasStats = g_Config.RasClientStats;
|
||
|
pRasInfo = &prte->Info;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// increment count of total igmp packets received
|
||
|
//
|
||
|
InterlockedIncrement(&pInfo->TotalIgmpPacketsReceived);
|
||
|
if (bRasStats)
|
||
|
InterlockedIncrement(&pRasInfo->TotalIgmpPacketsReceived);
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// long packet received. print trace if not v3. But it is not an error
|
||
|
//
|
||
|
if ( (NumBytes > MIN_PACKET_SIZE) && !IS_CONFIG_IGMP_V3(&pite->Config)) {
|
||
|
|
||
|
Trace4( RECEIVE,
|
||
|
"%d-byte packet from %d.%d.%d.%d on If %d (%d.%d.%d.%d) is too large",
|
||
|
NumBytes, PRINT_IPADDR(InputSrcAddr), IfIndex,
|
||
|
PRINT_IPADDR(pite->IpAddr)
|
||
|
);
|
||
|
|
||
|
InterlockedIncrement(&pite->Info.LongPacketsReceived);
|
||
|
if (bRasStats)
|
||
|
InterlockedIncrement(&pRasInfo->LongPacketsReceived);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Verify Igmp checksum
|
||
|
//
|
||
|
if (xsum(pHdr, NumBytes) != 0xffff) {
|
||
|
Trace0(RECEIVE, "Wrong checksum packet received");
|
||
|
|
||
|
InterlockedIncrement(&pInfo->WrongChecksumPackets);
|
||
|
if (bRasStats)
|
||
|
InterlockedIncrement(&pRasInfo->WrongChecksumPackets);
|
||
|
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
switch (pHdr->Vertype) {
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
// IGMP-QUERY //
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
case IGMP_QUERY :
|
||
|
{
|
||
|
//
|
||
|
// ignore the query if it came from this interface
|
||
|
//
|
||
|
if (MatchIpAddrBinding(pite, InputSrcAddr)) {
|
||
|
|
||
|
/*
|
||
|
Trace3(RECEIVE,
|
||
|
"received query packet sent by myself: IfIndex(%0x)"
|
||
|
"IpAddr(%d.%d.%d.%d) DstnMcastAddr(%d.%d.%d.%d)",
|
||
|
IfIndex, PRINT_IPADDR(InputSrcAddr), PRINT_IPADDR(DstnMcastAddr)
|
||
|
);
|
||
|
*/
|
||
|
bPrintTimerDebug = FALSE;
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Error if interface type is IGMP_IF_RAS_SERVER. can be
|
||
|
// IGMP_IF_RAS_ROUTER or IS_NOT_RAS_IF
|
||
|
//
|
||
|
if (! ( (IS_NOT_RAS_IF(pite->IfType))||(IS_RAS_ROUTER_IF(pite->IfType) ) )
|
||
|
)
|
||
|
{
|
||
|
|
||
|
Trace3(ERR,
|
||
|
"Error received Query on IfIndex(%d: %d.%d.%d.%d) from "
|
||
|
"Ras client(%d.%d.%d.%d)",
|
||
|
IfIndex, PRINT_IPADDR(pite->IpAddr), PRINT_IPADDR(InputSrcAddr)
|
||
|
);
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
|
||
|
bPrintTimerDebug = FALSE;
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
// General Query
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
|
||
|
if (pHdr->Group==0) {
|
||
|
|
||
|
DWORD Version,//Min(interface,pkt vertion)
|
||
|
RealVersion;//pkt version
|
||
|
|
||
|
// get versions
|
||
|
Version = ((pHdr->ResponseTime==0)||IS_IF_VER1(pite))
|
||
|
? 1
|
||
|
: ( (NumBytes==sizeof(IGMP_HEADER)||IS_IF_VER2(pite)) ? 2 : 3);
|
||
|
|
||
|
RealVersion = (pHdr->ResponseTime==0)
|
||
|
? 1
|
||
|
: (NumBytes==sizeof(IGMP_HEADER) ? 2 : 3);
|
||
|
|
||
|
Trace3(RECEIVE,
|
||
|
"General Query Version:%d received on interface(%d) from %d.%d.%d.%d",
|
||
|
IfIndex, RealVersion, PRINT_IPADDR(InputSrcAddr));
|
||
|
if (Version!=RealVersion){
|
||
|
Trace2(RECEIVE, "Processing the Version:%d packet as Version:%d",
|
||
|
RealVersion, RealVersion);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// check that the dstn addr was AllHostsAddr
|
||
|
//
|
||
|
if (DstnMcastAddr!=ALL_HOSTS_MCAST) {
|
||
|
Trace3(RECEIVE,
|
||
|
"received query packet not on AllHostsGroup: IfIndex(%0x)"
|
||
|
"SrcAddr(%d.%d.%d.%d) DstnMcastAddr(%d.%d.%d.%d)",
|
||
|
IfIndex, PRINT_IPADDR(InputSrcAddr),
|
||
|
PRINT_IPADDR(DstnMcastAddr)
|
||
|
);
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// acquire timer lock
|
||
|
//
|
||
|
|
||
|
ACQUIRE_TIMER_LOCK("_ProcessPacket");
|
||
|
ExitLockRelease |= TIMER_LOCK;
|
||
|
|
||
|
|
||
|
//
|
||
|
// log warning if incorrect version query received
|
||
|
//
|
||
|
|
||
|
|
||
|
if ( ((RealVersion==1)&&(!IS_PROTOCOL_TYPE_IGMPV1(pite)))
|
||
|
|| (RealVersion==2 && !IS_PROTOCOL_TYPE_IGMPV2(pite))
|
||
|
|| (RealVersion==3 && IS_PROTOCOL_TYPE_IGMPV3(pite)) )
|
||
|
{
|
||
|
// get warn interval in system time
|
||
|
|
||
|
LONGLONG llWarnInterval = OTHER_VER_ROUTER_WARN_INTERVAL*60*1000;
|
||
|
|
||
|
InterlockedIncrement(&pInfo->WrongVersionQueries);
|
||
|
|
||
|
//
|
||
|
// check if warn interval time has passed since last warning
|
||
|
// I check if OtherVerPresentTimeWarn>llCurTime to take care
|
||
|
// of timer resets
|
||
|
//
|
||
|
if ( (pInfo->OtherVerPresentTimeWarn+llWarnInterval<llCurTime)
|
||
|
|| (pInfo->OtherVerPresentTimeWarn>llCurTime) )
|
||
|
{
|
||
|
if (pHdr->ResponseTime==0) {
|
||
|
Trace3(RECEIVE,
|
||
|
"Detected ver-%d router(%d.%d.%d.%d) on "
|
||
|
"interface(%d.%d.%d.%d)",
|
||
|
Version, PRINT_IPADDR(InputSrcAddr),
|
||
|
PRINT_IPADDR(pite->IpAddr));
|
||
|
Logwarn2(VERSION_QUERY, "%I%I", InputSrcAddr,
|
||
|
pite->IpAddr, NO_ERROR);
|
||
|
}
|
||
|
pInfo->OtherVerPresentTimeWarn = llCurTime;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Version==1)
|
||
|
pite->Info.V1QuerierPresentTime = llCurTime
|
||
|
+ CONFIG_TO_SYSTEM_TIME(IGMP_VER1_RTR_PRESENT_TIMEOUT);
|
||
|
|
||
|
|
||
|
//
|
||
|
// if IpAddress less than my address then I become NonQuerier
|
||
|
// even if I am in Startup Mode
|
||
|
//
|
||
|
|
||
|
if (INET_CMP(InputSrcAddr, pite->IpAddr, cmp) <0) {
|
||
|
|
||
|
DWORD QQIC=0,QRV=0;
|
||
|
|
||
|
// last querier is being changed from myself to B, or from A to B.
|
||
|
if (InputSrcAddr != pite->Info.QuerierIpAddr)
|
||
|
pite->Info.LastQuerierChangeTime = llCurTime;
|
||
|
|
||
|
//
|
||
|
// if (version 3, change robustness variable and query interval
|
||
|
// if required) (only if I am not querier. else it will be
|
||
|
// changed when I change to non-querier
|
||
|
//
|
||
|
if (Version==3 && !IS_QUERIER(pite)
|
||
|
&&(INET_CMP(InputSrcAddr, pite->Info.QuerierIpAddr, cmp)<=0))
|
||
|
{
|
||
|
PIGMP_HEADER_V3_EXT pSourcesQuery;
|
||
|
|
||
|
pSourcesQuery = (PIGMP_HEADER_V3_EXT)
|
||
|
((PBYTE)pHdr+sizeof(IGMP_HEADER));
|
||
|
|
||
|
if (pSourcesQuery->QRV!=0) {
|
||
|
if (pite->Config.RobustnessVariable!=pSourcesQuery->QRV)
|
||
|
{
|
||
|
Trace3(CONFIG,
|
||
|
"Changing Robustness variable from %d to %d. "
|
||
|
"Querier:%d.%d.%d.%d",
|
||
|
pite->Config.RobustnessVariable,
|
||
|
pSourcesQuery->QRV,
|
||
|
PRINT_IPADDR(InputSrcAddr)
|
||
|
);
|
||
|
pite->Config.RobustnessVariable = pSourcesQuery->QRV;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QQIC = GET_QQIC_FROM_CODE(pSourcesQuery->QQIC)*1000;
|
||
|
if (pSourcesQuery->QQIC!=0 && pite->Config.GenQueryMaxResponseTime < QQIC) {
|
||
|
|
||
|
if (pite->Config.GenQueryInterval!=QQIC)
|
||
|
{
|
||
|
Trace3(CONFIG,
|
||
|
"Changing General-Query-Interval from %d to %d. "
|
||
|
"Querier:%d.%d.%d.%d",
|
||
|
pite->Config.GenQueryInterval/1000,
|
||
|
QQIC/1000,
|
||
|
PRINT_IPADDR(InputSrcAddr)
|
||
|
);
|
||
|
pite->Config.GenQueryInterval
|
||
|
= QQIC;
|
||
|
}
|
||
|
}
|
||
|
pite->Config.GroupMembershipTimeout =
|
||
|
pite->Config.RobustnessVariable*pite->Config.GenQueryInterval
|
||
|
+ pite->Config.GenQueryMaxResponseTime;
|
||
|
|
||
|
pite->Config.OtherQuerierPresentInterval
|
||
|
= pite->Config.RobustnessVariable*pite->Config.GenQueryInterval
|
||
|
+ (pite->Config.GenQueryMaxResponseTime)/2;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// change from querier to non-querier
|
||
|
if (IS_QUERIER(pite)) {
|
||
|
|
||
|
PQUERIER_CONTEXT pwi = IGMP_ALLOC(sizeof(QUERIER_CONTEXT),
|
||
|
0x800080,pite->IfIndex);
|
||
|
if (pwi==NULL)
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
|
||
|
pwi->IfIndex = IfIndex;
|
||
|
pwi->QuerierIpAddr = InputSrcAddr;
|
||
|
pwi->NewRobustnessVariable = QRV;
|
||
|
pwi->NewGenQueryInterval = QQIC;
|
||
|
|
||
|
// I have to queue a work item as I have to take an If write lock
|
||
|
QueueIgmpWorker(WF_BecomeNonQuerier, (PVOID)pwi);
|
||
|
|
||
|
Trace2(RECEIVE, "_ProcessPacket queued _WF_BecomeNonQuerier "
|
||
|
"on If:%0x Querier(%d.%d.%d.%d)",
|
||
|
IfIndex, PRINT_IPADDR(InputSrcAddr));
|
||
|
}
|
||
|
// I am non-querier already
|
||
|
else {
|
||
|
InterlockedExchange(&pite->Info.QuerierIpAddr, InputSrcAddr);
|
||
|
|
||
|
#if DEBUG_TIMER_TIMERID
|
||
|
SET_TIMER_ID(&pite->NonQueryTimer, 211, pite->IfIndex, 0, 0);
|
||
|
#endif
|
||
|
|
||
|
UpdateLocalTimer(&pite->NonQueryTimer,
|
||
|
pite->Config.OtherQuerierPresentInterval, DBG_N);
|
||
|
|
||
|
|
||
|
// not using interlockedExchange
|
||
|
pite->Info.QuerierPresentTimeout = llCurTime
|
||
|
+ CONFIG_TO_SYSTEM_TIME(pite->Config.OtherQuerierPresentInterval);
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Ignore query from querier with higher IpAddr
|
||
|
//
|
||
|
else {
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
RELEASE_TIMER_LOCK("_ProcessPacket");
|
||
|
ExitLockRelease &= ~TIMER_LOCK;
|
||
|
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
|
||
|
} //end general query
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
// Group Specific Query
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
|
||
|
else {
|
||
|
Error = ProcessGroupQuery(pite, pHdr, NumBytes, InputSrcAddr, DstnMcastAddr);
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
break;
|
||
|
|
||
|
} //end query (groupSpecific or general)
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
// IGMP_REPORT_V1, IGMP_REPORT_V2, IGMP_REPORT_V3 //
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
|
||
|
case IGMP_REPORT_V1 :
|
||
|
case IGMP_REPORT_V2 :
|
||
|
case IGMP_REPORT_V3 :
|
||
|
{
|
||
|
Error = ProcessReport(pite, pHdr, NumBytes, InputSrcAddr, DstnMcastAddr);
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
// IGMP_LEAVE //
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
|
||
|
case IGMP_LEAVE :
|
||
|
{
|
||
|
PGROUP_TABLE_ENTRY pge; //group table entry
|
||
|
PGI_ENTRY pgie; //group interface entry
|
||
|
|
||
|
|
||
|
Trace3(RECEIVE,
|
||
|
"IGMP Leave for group(%d.%d.%d.%d) on IfIndex(%0x) from "
|
||
|
"SrcAddr(%d.%d.%d.%d)",
|
||
|
PRINT_IPADDR(Group), IfIndex, PRINT_IPADDR(InputSrcAddr)
|
||
|
);
|
||
|
|
||
|
|
||
|
//
|
||
|
// the multicast group should not be 224.0.0.x
|
||
|
//
|
||
|
if (LOCAL_MCAST_GROUP(DstnMcastAddr)) {
|
||
|
Trace2(RECEIVE,
|
||
|
"Leave Report received from %d.%d.%d.%d for "
|
||
|
"Local group(%d.%d.%d.%d)",
|
||
|
PRINT_IPADDR(InputSrcAddr), PRINT_IPADDR(DstnMcastAddr));
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// check that the dstn addr was AllRoutersAddr
|
||
|
// or dstn addr must match the group field
|
||
|
//
|
||
|
if ( (DstnMcastAddr!=ALL_ROUTERS_MCAST)&&(DstnMcastAddr!=Group) ) {
|
||
|
Trace3(RECEIVE,
|
||
|
"received IGMP Leave packet not on AllRoutersGroup: IfIndex(%0x)"
|
||
|
"SrcAddr(%d.%d.%d.%d) DstnMcastAddr(%d.%d.%d.%d)",
|
||
|
IfIndex, PRINT_IPADDR(InputSrcAddr), PRINT_IPADDR(DstnMcastAddr)
|
||
|
);
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// check that the Group field is a valid multicast addr
|
||
|
//
|
||
|
if ( !IS_MCAST_ADDR(Group) ) {
|
||
|
Trace4(RECEIVE,
|
||
|
"received IGMP Leave packet with illegal Group(%d.%d.%d.%d) field: "
|
||
|
"IfIndex(%0x) SrcAddr(%d.%d.%d.%d) DstnMcastAddr(%d.%d.%d.%d)",
|
||
|
PRINT_IPADDR(Group), IfIndex, PRINT_IPADDR(InputSrcAddr),
|
||
|
PRINT_IPADDR(DstnMcastAddr)
|
||
|
);
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// update statistics
|
||
|
//
|
||
|
InterlockedIncrement(&pite->Info.LeavesReceived);
|
||
|
if (bRasStats)
|
||
|
InterlockedIncrement(&pRasInfo->LeavesReceived);
|
||
|
|
||
|
|
||
|
//
|
||
|
// if Leave processing not enabled or not querier then ignore Leave.
|
||
|
//
|
||
|
if ( !((IS_IF_VER2(pite)||IS_IF_VER3(pite)) && (IS_QUERIER(pite))) ) {
|
||
|
Trace0(RECEIVE,"Ignoring the Leave Packet");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Lock the group table
|
||
|
//
|
||
|
ACQUIRE_GROUP_LOCK(Group, "_ProcessPacket");
|
||
|
ExitLockRelease |= GROUP_LOCK;
|
||
|
|
||
|
|
||
|
//
|
||
|
// find the group entry. If entry not found then ignore the leave messg
|
||
|
//
|
||
|
pge = GetGroupFromGroupTable(Group, NULL, llCurTime);
|
||
|
if (pge==NULL) {
|
||
|
Error = ERROR_CAN_NOT_COMPLETE;
|
||
|
Trace2(ERR, "Leave received for nonexisting group(%d.%d.%d.%d) on IfIndex(%0x)",
|
||
|
PRINT_IPADDR(Group), pite->IfIndex);
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// find the GI entry. If GI entry does not exist or has deletedFlag then
|
||
|
// ignore the leave
|
||
|
//
|
||
|
pgie = GetGIFromGIList(pge, pite, InputSrcAddr, NOT_STATIC_GROUP, NULL, llCurTime);
|
||
|
if ( (pgie==NULL)||(pgie->Status&DELETED_FLAG) ) {
|
||
|
Error = ERROR_CAN_NOT_COMPLETE;
|
||
|
Trace2(ERR, "leave received for nonexisting group(%d.%d.%d.%d) on IfIndex(%0x). Not member",
|
||
|
PRINT_IPADDR(Group), IfIndex);
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
// ignore leave if it is not in ver 2 mode
|
||
|
if (pgie->Version!=2)
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
|
||
|
|
||
|
|
||
|
// if static group, ignore leave
|
||
|
if (pgie->bStaticGroup) {
|
||
|
Trace2(ERR,
|
||
|
"Leave not processed for group(%d.%d.%d.%d) on IfIndex(%0x): "
|
||
|
"Static group",
|
||
|
PRINT_IPADDR(Group), IfIndex
|
||
|
);
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// if v1-query received recently for that group, then ignore leaves
|
||
|
//
|
||
|
//
|
||
|
if (pgie->Version==1)
|
||
|
{
|
||
|
Error = ERROR_CAN_NOT_COMPLETE;
|
||
|
Trace2(ERR,
|
||
|
"Leave not processed for group(%d.%d.%d.%d) on IfIndex(%0x)"
|
||
|
"(recent v1 report)",
|
||
|
PRINT_IPADDR(Group), IfIndex
|
||
|
);
|
||
|
|
||
|
bPrintTimerDebug = FALSE;
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// if ras server interface, then delete the group entry and I am done.
|
||
|
// GroupSpecific Query is not sent to ras clients
|
||
|
//
|
||
|
// if pConfig->LastMemQueryCount==0 then the group is expected to be
|
||
|
// deleted immediately
|
||
|
//
|
||
|
|
||
|
if ( IS_RAS_SERVER_IF(pite->IfType) || pConfig->LastMemQueryCount==0) {
|
||
|
|
||
|
DeleteGIEntry(pgie, TRUE, TRUE); //updateStats, CallMgm
|
||
|
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
ACQUIRE_TIMER_LOCK("_ProcessPacket");
|
||
|
ExitLockRelease |= TIMER_LOCK;
|
||
|
|
||
|
|
||
|
//
|
||
|
// if timer already expired return.
|
||
|
// Leave the group deletion to Membership timer
|
||
|
//
|
||
|
if ( !(pgie->GroupMembershipTimer.Status&TIMER_STATUS_ACTIVE)
|
||
|
||(pgie->GroupMembershipTimer.Timeout<llCurTime) )
|
||
|
{
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// if currently processing a leave then exit.
|
||
|
//
|
||
|
if (pgie->LastMemQueryCount>0) {
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// in almost all places, I have to do this check.
|
||
|
// change the way insert and update timers' timeout is set
|
||
|
|
||
|
|
||
|
//
|
||
|
// set a new leave timer. Set the new LastMemQueryCount left
|
||
|
//
|
||
|
if (pConfig->LastMemQueryCount) {
|
||
|
|
||
|
pgie->LastMemQueryCount = pConfig->LastMemQueryCount - 1;
|
||
|
|
||
|
#if DEBUG_TIMER_TIMERID
|
||
|
SET_TIMER_ID(&pgie->LastMemQueryTimer, 410, pite->IfIndex,
|
||
|
Group, 0);
|
||
|
#endif
|
||
|
|
||
|
InsertTimer(&pgie->LastMemQueryTimer, pConfig->LastMemQueryInterval, TRUE, DBG_Y);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// set membership timer to
|
||
|
// min{currentValue,LastMemQueryInterval*LastMemQueryCount}
|
||
|
//
|
||
|
|
||
|
if (pgie->GroupMembershipTimer.Timeout >
|
||
|
(llCurTime+(pConfig->LastMemQueryCount
|
||
|
*CONFIG_TO_SYSTEM_TIME(pConfig->LastMemQueryInterval)))
|
||
|
)
|
||
|
{
|
||
|
#if DEBUG_TIMER_TIMERID
|
||
|
pgie->GroupMembershipTimer.Id = 340;
|
||
|
pgie->GroupMembershipTimer.Id2 = TimerId++;
|
||
|
#endif
|
||
|
|
||
|
UpdateLocalTimer(&pgie->GroupMembershipTimer,
|
||
|
pConfig->LastMemQueryCount*pConfig->LastMemQueryInterval,
|
||
|
DBG_N);
|
||
|
|
||
|
// update GroupExpiryTime so that correct stats are displayed
|
||
|
pgie->Info.GroupExpiryTime = llCurTime
|
||
|
+ CONFIG_TO_SYSTEM_TIME(pConfig->LastMemQueryCount
|
||
|
*pConfig->LastMemQueryInterval);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
//release timer and groupBucket locks
|
||
|
//I still have read lock on the IfTable/RasTable
|
||
|
//
|
||
|
RELEASE_TIMER_LOCK("_ProcessPacket");
|
||
|
RELEASE_GROUP_LOCK(Group, "_ProcessPacket");
|
||
|
|
||
|
ExitLockRelease &= ~TIMER_LOCK;
|
||
|
ExitLockRelease &= ~GROUP_LOCK;
|
||
|
|
||
|
|
||
|
//
|
||
|
// send group specific query only if I am a querier
|
||
|
//
|
||
|
if (IS_QUERIER(pite))
|
||
|
SEND_GROUP_QUERY_V2(pite, Group);
|
||
|
|
||
|
|
||
|
//releae ifLock/RasLock and exit
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
|
||
|
}//igmp leave
|
||
|
|
||
|
default :
|
||
|
{
|
||
|
Error = ERROR_CAN_NOT_COMPLETE;
|
||
|
Trace3(ERR,
|
||
|
"Incorrect Igmp type(%d) packet received on IfIndex(%d%) Group(%d.%d.%d.5d)",
|
||
|
pHdr->Vertype, IfIndex, PRINT_IPADDR(Group)
|
||
|
);
|
||
|
IgmpAssertOnError(FALSE);
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
RETURN_FROM_PROCESS_PACKET();
|
||
|
|
||
|
|
||
|
} //end _ProcessPacket
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _T_LastVer1ReportTimer
|
||
|
//
|
||
|
// For this GI entry, the last ver-1 report has timed out. Change to ver-2 if
|
||
|
// the interface is set to ver-2.
|
||
|
// Locks: Assumes timer lock.
|
||
|
//
|
||
|
// be careful as only timer lock held. make sure that the worker fn checks
|
||
|
// everything. recheck igmp version, etc.
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
DWORD
|
||
|
T_LastVer1ReportTimer (
|
||
|
PVOID pvContext
|
||
|
)
|
||
|
{
|
||
|
PIGMP_TIMER_ENTRY pTimer; //ptr to timer entry
|
||
|
PGI_ENTRY pgie; //group interface entry
|
||
|
PIF_TABLE_ENTRY pite;
|
||
|
LONGLONG llCurTime = GetCurrentIgmpTime();
|
||
|
|
||
|
|
||
|
Trace0(ENTER1, "Entering _T_LastVer1ReportTimer()");
|
||
|
|
||
|
//
|
||
|
// get pointer to LastMemQueryTimer, GI entry, pite
|
||
|
//
|
||
|
pTimer = CONTAINING_RECORD( pvContext, IGMP_TIMER_ENTRY, Context);
|
||
|
pgie = CONTAINING_RECORD( pTimer, GI_ENTRY, LastVer1ReportTimer);
|
||
|
pite = pgie->pIfTableEntry;
|
||
|
|
||
|
//
|
||
|
// if IfTable not activated, then break
|
||
|
//
|
||
|
if (!IS_IF_ACTIVATED(pite) || (pgie->Status&DELETED_FLAG))
|
||
|
return NO_ERROR;
|
||
|
|
||
|
|
||
|
Trace2(TIMER, "T_LastVer1ReportTimer() called for If(%0x), Group(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pgie->pGroupTableEntry->Group));
|
||
|
|
||
|
|
||
|
// set the state to ver-2 unless the interface is ver-1, in which case
|
||
|
// set the version-1 timer again.
|
||
|
|
||
|
if (IS_PROTOCOL_TYPE_IGMPV2(pite)) {
|
||
|
pgie->Version = 2;
|
||
|
}
|
||
|
else if (IS_PROTOCOL_TYPE_IGMPV3(pite)) {
|
||
|
|
||
|
if (IS_TIMER_ACTIVE(pgie->LastVer2ReportTimer))
|
||
|
pgie->Version = 2;
|
||
|
else {
|
||
|
|
||
|
PWORK_CONTEXT pWorkContext;
|
||
|
DWORD Error=NO_ERROR;
|
||
|
|
||
|
//
|
||
|
// queue work item for shifting to v3 for that group
|
||
|
//
|
||
|
|
||
|
CREATE_WORK_CONTEXT(pWorkContext, Error);
|
||
|
if (Error!=NO_ERROR) {
|
||
|
return ERROR_CAN_NOT_COMPLETE;
|
||
|
}
|
||
|
pWorkContext->IfIndex = pite->IfIndex;
|
||
|
pWorkContext->Group = pgie->pGroupTableEntry->Group; //ptrs usage safe
|
||
|
pWorkContext->NHAddr = pgie->NHAddr; //valid only for ras: should i us
|
||
|
pWorkContext->WorkType = SHIFT_TO_V3;
|
||
|
|
||
|
Trace0(WORKER, "Queueing WF_TimerProcessing() to shift to v3");
|
||
|
QueueIgmpWorker(WF_TimerProcessing, (PVOID)pWorkContext);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
Trace0(LEAVE1, "Leaving _T_LastVer1ReportTimer()");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _T_LastMemQueryTimer
|
||
|
// called when LastMemQueryTimer() has expired. This timer is not used to
|
||
|
// time out memberships (GroupMembershipTimer is used for that). It is only
|
||
|
// used to send GroupSpecific Queries.
|
||
|
//
|
||
|
// Queues: WF_TimerProcessing() to send group specific query.
|
||
|
// Note: WT_ProcessTimerEvent() makes sure the protocol is not stopp-ing/ed
|
||
|
// Locks: Assumes timer lock. does not need any other lock.
|
||
|
// be careful as only timer lock held. make sure that the worker fn checks
|
||
|
// everything. recheck igmp version, etc.
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
DWORD
|
||
|
T_LastMemQueryTimer (
|
||
|
PVOID pvContext
|
||
|
)
|
||
|
{
|
||
|
DWORD Error=NO_ERROR;
|
||
|
PIGMP_TIMER_ENTRY pTimer; //ptr to timer entry
|
||
|
PGI_ENTRY pgie; //group interface entry
|
||
|
PWORK_CONTEXT pWorkContext;
|
||
|
PIF_TABLE_ENTRY pite;
|
||
|
PRAS_TABLE_ENTRY prte;
|
||
|
BOOL bCompleted = FALSE; //if false, set count to 0
|
||
|
|
||
|
|
||
|
Trace0(ENTER1, "Entering _T_LastMemQueryTimer()");
|
||
|
|
||
|
|
||
|
//
|
||
|
// get pointer to LastMemQueryTimer, GI entry, pite, prte
|
||
|
//
|
||
|
pTimer = CONTAINING_RECORD( pvContext, IGMP_TIMER_ENTRY, Context);
|
||
|
pgie = CONTAINING_RECORD( pTimer, GI_ENTRY, LastMemQueryTimer);
|
||
|
pite = pgie->pIfTableEntry;
|
||
|
prte = pgie->pRasTableEntry;
|
||
|
|
||
|
//
|
||
|
// if IfTable not activated, then break
|
||
|
//
|
||
|
if (!IS_IF_ACTIVATED(pite))
|
||
|
return NO_ERROR;
|
||
|
|
||
|
|
||
|
Trace2(TIMER, "_T_LastMemQueryTimer() called for If(%0x), Group(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pgie->pGroupTableEntry->Group));
|
||
|
|
||
|
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
|
||
|
//
|
||
|
// if GI or pite or prte has flag already set, then exit
|
||
|
//
|
||
|
if ( (pgie->Status&DELETED_FLAG) || (pite->Status&DELETED_FLAG) )
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
if ( (prte!=NULL) && (prte->Status&DELETED_FLAG) )
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
|
||
|
if (pgie->Version!=3) {
|
||
|
//
|
||
|
// if LeaveEnabled FALSE then return
|
||
|
//
|
||
|
if (!GI_PROCESS_GRPQUERY(pite, pgie))
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// have sent the last GroupSpecific query. GroupMembershipTimer will take care
|
||
|
// of deleting this GI entry
|
||
|
//
|
||
|
if (pgie->LastMemQueryCount==0) {
|
||
|
bCompleted = TRUE;
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// decrement count.
|
||
|
//
|
||
|
if (InterlockedDecrement(&pgie->LastMemQueryCount) == (ULONG)-1) {
|
||
|
pgie->LastMemQueryCount = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// if count==0, dont insert timer again, but send the last groupSp Query
|
||
|
//
|
||
|
|
||
|
if (pgie->LastMemQueryCount>0) {
|
||
|
|
||
|
//reinsert the timer to send the next GroupSpQuery
|
||
|
|
||
|
#if DEBUG_TIMER_TIMERID
|
||
|
SET_TIMER_ID(&pgie->LastMemQueryTimer, 420, pite->IfIndex,
|
||
|
pgie->pGroupTableEntry->Group, 0);
|
||
|
#endif
|
||
|
|
||
|
InsertTimer(&pgie->LastMemQueryTimer,
|
||
|
pite->Config.LastMemQueryInterval, FALSE, DBG_Y);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// queue work item for sending the GroupSp query even if the router
|
||
|
// is not a Querier
|
||
|
//
|
||
|
|
||
|
CREATE_WORK_CONTEXT(pWorkContext, Error);
|
||
|
if (Error!=NO_ERROR) {
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
pWorkContext->IfIndex = pite->IfIndex;
|
||
|
pWorkContext->Group = pgie->pGroupTableEntry->Group;
|
||
|
pWorkContext->NHAddr = pgie->NHAddr; //valid only for ras: should i use it?
|
||
|
pWorkContext->WorkType = (pgie->Version==3) ? MSG_GROUP_QUERY_V3
|
||
|
: MSG_GROUP_QUERY_V2;
|
||
|
|
||
|
Trace0(WORKER, "Queueing WF_TimerProcessing() to send GroupSpQuery:");
|
||
|
QueueIgmpWorker(WF_TimerProcessing, (PVOID)pWorkContext);
|
||
|
|
||
|
|
||
|
bCompleted = TRUE;
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
|
||
|
// there was some error somewhere. so set the LastMemQueryCount to 0
|
||
|
|
||
|
if (!bCompleted)
|
||
|
InterlockedExchange(&pgie->LastMemQueryCount, 0);
|
||
|
|
||
|
|
||
|
Trace0(LEAVE1, "Leaving _T_LastMemQueryTimer()");
|
||
|
return 0;
|
||
|
|
||
|
} //end _T_LastMemQueryTimer
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _T_MembershipTimer
|
||
|
//
|
||
|
// lock: has TimerLock
|
||
|
// called when the GroupMembershipTimer is fired
|
||
|
// delete the GI entry if it exists.
|
||
|
//
|
||
|
// be careful as only timer lock held. make sure that the worker fn checks
|
||
|
// everything. recheck igmp version, etc.
|
||
|
//------------------------------------------------------------------------------
|
||
|
DWORD
|
||
|
T_MembershipTimer (
|
||
|
PVOID pvContext
|
||
|
)
|
||
|
{
|
||
|
DWORD Error=NO_ERROR;
|
||
|
PIGMP_TIMER_ENTRY pTimer; //ptr to timer entry
|
||
|
PGI_ENTRY pgie; //group interface entry
|
||
|
|
||
|
PWORK_CONTEXT pWorkContext;
|
||
|
PIF_TABLE_ENTRY pite;
|
||
|
PRAS_TABLE_ENTRY prte;
|
||
|
|
||
|
|
||
|
Trace0(ENTER1, "Entering _T_MembershipTimer()");
|
||
|
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
|
||
|
//
|
||
|
// get pointer to Membership Timer, GI entry, pite,
|
||
|
//
|
||
|
pTimer = CONTAINING_RECORD( pvContext, IGMP_TIMER_ENTRY, Context);
|
||
|
pgie = CONTAINING_RECORD( pTimer, GI_ENTRY, GroupMembershipTimer);
|
||
|
pite = pgie->pIfTableEntry;
|
||
|
prte = pgie->pRasTableEntry;
|
||
|
|
||
|
//
|
||
|
// if IfTable not activated, then break
|
||
|
//
|
||
|
if (!IS_IF_ACTIVATED(pite))
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
|
||
|
Trace2(TIMER, "_T_MembershipTimer() called for If(%0x), Group(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pgie->pGroupTableEntry->Group));
|
||
|
|
||
|
//
|
||
|
// if GI or pite or prte has deleted flag already set, then exit
|
||
|
//
|
||
|
if ( (pgie->Status&DELETED_FLAG) || (pite->Status&DELETED_FLAG) ) {
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// if Ras, and ras table being deleted then break
|
||
|
//
|
||
|
if ( (prte!=NULL) && (prte->Status&DELETED_FLAG) )
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
//
|
||
|
// if IfTable not activated, then break
|
||
|
//
|
||
|
if (!IS_IF_ACTIVATED(pite))
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// if LastMemTimer is active, remove it(cant remove it in this function
|
||
|
// as it is being processed by the timer queue simultaneously.
|
||
|
if (pgie->LastMemQueryCount>0)
|
||
|
pgie->LastMemQueryCount = 0;
|
||
|
|
||
|
|
||
|
//
|
||
|
// queue work item to delete the GI entry
|
||
|
//
|
||
|
|
||
|
CREATE_WORK_CONTEXT(pWorkContext, Error);
|
||
|
if (Error!=NO_ERROR)
|
||
|
GOTO_END_BLOCK1;
|
||
|
pWorkContext->IfIndex = pite->IfIndex;
|
||
|
pWorkContext->NHAddr = pgie->NHAddr;
|
||
|
pWorkContext->Group = pgie->pGroupTableEntry->Group;
|
||
|
pWorkContext->WorkType = DELETE_MEMBERSHIP;
|
||
|
|
||
|
Trace0(WORKER, "_T_MembershipTimer queued _WF_TimerProcessing:");
|
||
|
|
||
|
QueueIgmpWorker(WF_TimerProcessing, (PVOID)pWorkContext);
|
||
|
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
Trace0(LEAVE1, "Leaving _T_MembershipTimer()");
|
||
|
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
} //end _T_MembershipTimer
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _T_QueryTimer
|
||
|
// fired when a general query timer is fired. Sends a general query.
|
||
|
// The timer queue is currently locked
|
||
|
//
|
||
|
// be careful as only timer lock held. make sure that the worker fn checks
|
||
|
// everything. recheck igmp version, etc.
|
||
|
//------------------------------------------------------------------------------
|
||
|
DWORD
|
||
|
T_QueryTimer (
|
||
|
PVOID pvContext
|
||
|
)
|
||
|
{
|
||
|
DWORD Error=NO_ERROR;
|
||
|
PIGMP_TIMER_ENTRY pTimer; //ptr to timer entry
|
||
|
PWORK_CONTEXT pWorkContext;
|
||
|
PIF_INFO pInfo;
|
||
|
PIF_TABLE_ENTRY pite;
|
||
|
static ULONG Seed = 123456;
|
||
|
ULONG ulTimeout;
|
||
|
BOOL bRandomize = FALSE; // [0,GenQueryInterval] for 1st gen query after startup.
|
||
|
|
||
|
|
||
|
Trace0(ENTER1, "Entering _T_QueryTimer()");
|
||
|
|
||
|
|
||
|
pTimer = CONTAINING_RECORD( pvContext, IGMP_TIMER_ENTRY, Context);
|
||
|
|
||
|
pite = CONTAINING_RECORD( pTimer, IF_TABLE_ENTRY, QueryTimer);
|
||
|
pInfo = &pite->Info;
|
||
|
|
||
|
|
||
|
//
|
||
|
// make sure that the interface is activated
|
||
|
//
|
||
|
if (!(IS_IF_ACTIVATED(pite))) {
|
||
|
Trace2(ERR, "T_QueryTimer() called for inactive IfIndex(%0x), IfType(%d)",
|
||
|
pite->IfIndex, pite->IfType);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
Trace2(TIMER, "Processing T_QueryTimer() for IfIndex(%0x), IfType(%d)",
|
||
|
pite->IfIndex, pite->IfType);
|
||
|
|
||
|
|
||
|
//
|
||
|
// check if still in startup Mode.
|
||
|
//
|
||
|
if (pInfo->StartupQueryCountCurrent>0) {
|
||
|
InterlockedDecrement(&pInfo->StartupQueryCountCurrent);
|
||
|
bRandomize = (pInfo->StartupQueryCountCurrent == 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// if non-querier, then done if I have sent startupQueries
|
||
|
if ( !IS_QUERIER(pite) && (pInfo->StartupQueryCountCurrent<=0) )
|
||
|
return 0;
|
||
|
|
||
|
|
||
|
// set the next query time
|
||
|
ulTimeout = (pInfo->StartupQueryCountCurrent>0)
|
||
|
? pite->Config.StartupQueryInterval
|
||
|
: (bRandomize )
|
||
|
? (DWORD) ((RtlRandom(&Seed)/(FLOAT)MAXLONG)
|
||
|
*pite->Config.GenQueryInterval)
|
||
|
: pite->Config.GenQueryInterval;
|
||
|
|
||
|
|
||
|
#if DEBUG_TIMER_TIMERID
|
||
|
SET_TIMER_ID(&pite->QueryTimer, 120, pite->IfIndex, 0, 0);
|
||
|
#endif
|
||
|
|
||
|
InsertTimer(&pite->QueryTimer, ulTimeout, FALSE, DBG_Y);
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// queue work item for sending the general query
|
||
|
//
|
||
|
|
||
|
CREATE_WORK_CONTEXT(pWorkContext, Error);
|
||
|
if (Error!=NO_ERROR)
|
||
|
return 0;
|
||
|
pWorkContext->IfIndex = pite->IfIndex;
|
||
|
pWorkContext->WorkType = MSG_GEN_QUERY;
|
||
|
QueueIgmpWorker(WF_TimerProcessing, (PVOID)pWorkContext);
|
||
|
|
||
|
Trace0(WORKER,
|
||
|
"_T_QueryTimer queued _WF_TimerProcessing: Querier State");
|
||
|
|
||
|
|
||
|
|
||
|
Trace0(LEAVE1, "Leaving _T_QueryTimer()");
|
||
|
return 0;
|
||
|
|
||
|
} //end _T_QueryTimer
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _T_NonQueryTimer
|
||
|
// fired when it is in non-querier Mode and hasnt heard a query for a long time
|
||
|
//
|
||
|
// be careful as only timer lock held. make sure that the worker fn checks
|
||
|
// everything. recheck igmp version, etc.
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
DWORD
|
||
|
T_NonQueryTimer (
|
||
|
PVOID pvContext
|
||
|
)
|
||
|
{
|
||
|
DWORD Error=NO_ERROR;
|
||
|
PIGMP_TIMER_ENTRY pTimer; //ptr to timer entry
|
||
|
PIF_TABLE_ENTRY pite;
|
||
|
|
||
|
|
||
|
Trace0(ENTER1, "Entering _T_NonQueryTimer()");
|
||
|
|
||
|
|
||
|
pTimer = CONTAINING_RECORD( pvContext, IGMP_TIMER_ENTRY, Context);
|
||
|
|
||
|
pite = CONTAINING_RECORD( pTimer, IF_TABLE_ENTRY, NonQueryTimer);
|
||
|
|
||
|
|
||
|
//
|
||
|
// make sure that the interface is activated
|
||
|
//
|
||
|
if (!(IS_IF_ACTIVATED(pite))) {
|
||
|
/*Trace2(ERR, "T_NonQueryTimer() called for inactive IfIndex(%0x), IfType(%d)",
|
||
|
pite->IfIndex, pite->IfType);
|
||
|
IgmpAssertOnError(FALSE);*/
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
Trace2(TIMER, "Processing T_NonQueryTimer() for IfIndex(%0x), IfType(%d)",
|
||
|
pite->IfIndex, pite->IfType);
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// if non-querier, then queue work item to become querier
|
||
|
//
|
||
|
|
||
|
if (!IS_QUERIER(pite)) {
|
||
|
|
||
|
QueueIgmpWorker(WF_BecomeQuerier, (PVOID)(DWORD_PTR)pite->IfIndex);
|
||
|
|
||
|
Trace1(WORKER, "_T_NonQueryTimer queued _WF_BecomeQuerier on If:%0x",
|
||
|
pite->IfIndex);
|
||
|
}
|
||
|
|
||
|
Trace0(LEAVE1, "Leaving _T_NonQueryTimer()");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
WF_BecomeQuerier(
|
||
|
PVOID pvIfIndex
|
||
|
)
|
||
|
//Called by T_NonQueryTimer
|
||
|
{
|
||
|
ChangeQuerierState(PtrToUlong(pvIfIndex), QUERIER_FLAG, 0, 0, 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
WF_BecomeNonQuerier(
|
||
|
PVOID pvContext
|
||
|
)
|
||
|
{
|
||
|
PQUERIER_CONTEXT pwi = (PQUERIER_CONTEXT)pvContext;
|
||
|
|
||
|
ChangeQuerierState(pwi->IfIndex, NON_QUERIER_FLAG, pwi->QuerierIpAddr,
|
||
|
pwi->NewRobustnessVariable, pwi->NewGenQueryInterval);
|
||
|
|
||
|
IGMP_FREE(pwi);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ChangeQuerierState(
|
||
|
DWORD IfIndex,
|
||
|
DWORD Flag, //QUERIER_CHANGE_V1_ONLY,QUERIER_FLAG,NON_QUERIER_FLAG
|
||
|
DWORD QuerierIpAddr, // only when changing from querier-->nonquerier
|
||
|
DWORD NewRobustnessVariable, //only for v3:querier->non-querier
|
||
|
DWORD NewGenQueryInterval //only for v3:querier->non-querier
|
||
|
)
|
||
|
{
|
||
|
PIF_TABLE_ENTRY pite;
|
||
|
BOOL bPrevCanAddGroupsToMgm;
|
||
|
|
||
|
|
||
|
if (!EnterIgmpWorker()) return;
|
||
|
Trace0(ENTER1, "Entering _ChangeQuerierState");
|
||
|
|
||
|
|
||
|
ACQUIRE_IF_LOCK_EXCLUSIVE(IfIndex, "_ChangeQuerierState");
|
||
|
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
|
||
|
//
|
||
|
// retrieve the interface entry
|
||
|
//
|
||
|
pite = GetIfByIndex(IfIndex);
|
||
|
|
||
|
//
|
||
|
// return error if interface does not exist, or it is not activated
|
||
|
// or is already in that state
|
||
|
//
|
||
|
if ( (pite == NULL)||(!IS_IF_ACTIVATED(pite)) ) {
|
||
|
Trace1(ERR,
|
||
|
"Warning: worker fn could not change querier state for If:%0x",
|
||
|
IfIndex
|
||
|
);
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// if it is supposed to be a V1 interface, make sure that it is
|
||
|
//
|
||
|
if ( (Flag & QUERIER_CHANGE_V1_ONLY)
|
||
|
&& (!IS_PROTOCOL_TYPE_IGMPV1(pite)) )
|
||
|
{
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
bPrevCanAddGroupsToMgm = CAN_ADD_GROUPS_TO_MGM(pite);
|
||
|
|
||
|
|
||
|
//
|
||
|
// changing from non querier to querier
|
||
|
//
|
||
|
|
||
|
if (Flag & QUERIER_FLAG) {
|
||
|
|
||
|
|
||
|
// if already querier, then done
|
||
|
if (IS_QUERIER(pite))
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
|
||
|
SET_QUERIER_STATE_QUERIER(pite->Info.QuerierState);
|
||
|
|
||
|
Trace2(QUERIER,
|
||
|
"NonQuerier --> Querier. IfIndex(%0x), IpAddr(%d.%d.%d.%d) ",
|
||
|
IfIndex, PRINT_IPADDR(pite->IpAddr)
|
||
|
);
|
||
|
|
||
|
|
||
|
// copy back the old robustness, genquery, etc values. for v3
|
||
|
// interface
|
||
|
|
||
|
if (IS_IF_VER3(pite)) {
|
||
|
pite->Config.RobustnessVariable = pite->Config.RobustnessVariableOld;
|
||
|
pite->Config.GenQueryInterval = pite->Config.GenQueryIntervalOld;
|
||
|
pite->Config.OtherQuerierPresentInterval
|
||
|
= pite->Config.OtherQuerierPresentIntervalOld;
|
||
|
pite->Config.GroupMembershipTimeout = pite->Config.GroupMembershipTimeoutOld;
|
||
|
}
|
||
|
|
||
|
|
||
|
// register all groups with MGM if I wasnt doing earlier
|
||
|
|
||
|
if (CAN_ADD_GROUPS_TO_MGM(pite) && !bPrevCanAddGroupsToMgm) {
|
||
|
|
||
|
RefreshMgmIgmprtrGroups(pite, ADD_FLAG);
|
||
|
|
||
|
Trace1(MGM,
|
||
|
"Igmp Router start propagating groups to MGM on If:%0x",
|
||
|
pite->IfIndex
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// I am the querier again. Set the addr in Info.
|
||
|
InterlockedExchange(&pite->Info.QuerierIpAddr, pite->IpAddr);
|
||
|
|
||
|
// update the time when querier was last changed
|
||
|
pite->Info.LastQuerierChangeTime = GetCurrentIgmpTime();
|
||
|
|
||
|
//
|
||
|
// set the GenQuery timer and remove NonQueryTimer if set.
|
||
|
//
|
||
|
|
||
|
ACQUIRE_TIMER_LOCK("_ChangeQuerierState");
|
||
|
|
||
|
#if DEBUG_TIMER_TIMERID
|
||
|
SET_TIMER_ID(&pite->QueryTimer, 220, pite->IfIndex, 0, 0);
|
||
|
#endif
|
||
|
|
||
|
if (!IS_TIMER_ACTIVE(pite->QueryTimer))
|
||
|
InsertTimer(&pite->QueryTimer, pite->Config.GenQueryInterval, FALSE, DBG_Y);
|
||
|
|
||
|
if (IS_TIMER_ACTIVE(pite->NonQueryTimer))
|
||
|
RemoveTimer(&pite->NonQueryTimer, DBG_Y);
|
||
|
|
||
|
RELEASE_TIMER_LOCK("_ChangeQuerierState");
|
||
|
|
||
|
|
||
|
|
||
|
// send general query
|
||
|
|
||
|
SEND_GEN_QUERY(pite);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// changing from querier to non querier
|
||
|
//
|
||
|
else {
|
||
|
|
||
|
LONGLONG llCurTime = GetCurrentIgmpTime();
|
||
|
BOOL bPrevAddGroupsToMgm;
|
||
|
|
||
|
|
||
|
// if already non querier, then done
|
||
|
if (!IS_QUERIER(pite))
|
||
|
GOTO_END_BLOCK1;
|
||
|
|
||
|
|
||
|
|
||
|
// change querier state
|
||
|
|
||
|
SET_QUERIER_STATE_NON_QUERIER(pite->Info.QuerierState);
|
||
|
|
||
|
Trace2(QUERIER,
|
||
|
"Querier --> NonQuerier. IfIndex(%0x), IpAddr(%d.%d.%d.%d) ",
|
||
|
IfIndex, PRINT_IPADDR(pite->IpAddr)
|
||
|
);
|
||
|
|
||
|
InterlockedExchange(&pite->Info.QuerierIpAddr, QuerierIpAddr);
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// if previously, groups were propagated to MGM, but should
|
||
|
// not be propagated now, then deregister the groups from MGM
|
||
|
//
|
||
|
|
||
|
if (!CAN_ADD_GROUPS_TO_MGM(pite) && bPrevCanAddGroupsToMgm) {
|
||
|
|
||
|
RefreshMgmIgmprtrGroups(pite, DELETE_FLAG);
|
||
|
|
||
|
Trace1(MGM,
|
||
|
"Igmp Router stop propagating groups to MGM on If:%0x",
|
||
|
pite->IfIndex
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
if (IS_IF_VER3(pite)) {
|
||
|
if (NewRobustnessVariable==0)
|
||
|
NewRobustnessVariable = pite->Config.RobustnessVariableOld;
|
||
|
if (NewGenQueryInterval==0)
|
||
|
NewGenQueryInterval = pite->Config.GenQueryIntervalOld;
|
||
|
|
||
|
if (pite->Config.GenQueryMaxResponseTime > NewGenQueryInterval)
|
||
|
NewGenQueryInterval = pite->Config.GenQueryIntervalOld;
|
||
|
|
||
|
if (NewRobustnessVariable != pite->Config.RobustnessVariable
|
||
|
|| NewGenQueryInterval != pite->Config.RobustnessVariable
|
||
|
) {
|
||
|
pite->Config.RobustnessVariable = NewRobustnessVariable;
|
||
|
pite->Config.GenQueryInterval = NewGenQueryInterval;
|
||
|
|
||
|
pite->Config.OtherQuerierPresentInterval
|
||
|
= NewRobustnessVariable*NewGenQueryInterval
|
||
|
+ (pite->Config.GenQueryMaxResponseTime)/2;
|
||
|
|
||
|
pite->Config.GroupMembershipTimeout = NewRobustnessVariable*NewGenQueryInterval
|
||
|
+ pite->Config.GenQueryMaxResponseTime;
|
||
|
|
||
|
Trace3(CONFIG,
|
||
|
"Querier->NonQuerier: Robustness:%d GenQueryInterval:%d "
|
||
|
"GroupMembershipTimeout:%d. ",
|
||
|
NewRobustnessVariable, NewGenQueryInterval/1000,
|
||
|
pite->Config.GroupMembershipTimeout/1000
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// set other querier present timer, and remove querier timer if not
|
||
|
// in startup query Mode
|
||
|
//
|
||
|
|
||
|
ACQUIRE_TIMER_LOCK("_ChangeQuerierState");
|
||
|
|
||
|
#if DEBUG_TIMER_TIMERID
|
||
|
SET_TIMER_ID(&pite->NonQueryTimer, 210, pite->IfIndex, 0, 0);
|
||
|
#endif
|
||
|
if (!IS_TIMER_ACTIVE(pite->NonQueryTimer)) {
|
||
|
InsertTimer(&pite->NonQueryTimer,
|
||
|
pite->Config.OtherQuerierPresentInterval, TRUE, DBG_Y);
|
||
|
}
|
||
|
|
||
|
if (IS_TIMER_ACTIVE(pite->QueryTimer) &&
|
||
|
(pite->Info.StartupQueryCountCurrent<=0) )
|
||
|
{
|
||
|
RemoveTimer(&pite->QueryTimer, DBG_Y);
|
||
|
}
|
||
|
|
||
|
pite->Info.QuerierPresentTimeout = llCurTime
|
||
|
+ CONFIG_TO_SYSTEM_TIME(pite->Config.OtherQuerierPresentInterval);
|
||
|
|
||
|
|
||
|
RELEASE_TIMER_LOCK("_ChangeQuerierState");
|
||
|
}
|
||
|
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
|
||
|
RELEASE_IF_LOCK_EXCLUSIVE(IfIndex, "_ChangeQuerierState");
|
||
|
|
||
|
|
||
|
Trace0(LEAVE1, "leaving _ChangeQuerierState\n");
|
||
|
LeaveIgmpWorker();
|
||
|
return;
|
||
|
|
||
|
}//end _ChangeQuerierState
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _WF_TimerProcessing
|
||
|
//------------------------------------------------------------------------------
|
||
|
VOID
|
||
|
WF_TimerProcessing (
|
||
|
PVOID pvContext
|
||
|
)
|
||
|
{
|
||
|
DWORD IfIndex;
|
||
|
PWORK_CONTEXT pWorkContext = (PWORK_CONTEXT)pvContext;
|
||
|
PIF_TABLE_ENTRY pite;
|
||
|
DWORD Error = NO_ERROR;
|
||
|
DWORD Group = pWorkContext->Group;
|
||
|
BOOL bCreate;
|
||
|
PRAS_TABLE prt;
|
||
|
PRAS_TABLE_ENTRY prte;
|
||
|
PGROUP_TABLE_ENTRY pge;
|
||
|
PGI_ENTRY pgie; //group interface entry
|
||
|
|
||
|
enum {
|
||
|
NO_LOCK=0x0,
|
||
|
IF_LOCK=0x1,
|
||
|
RAS_LOCK=0x2,
|
||
|
GROUP_LOCK=0x4,
|
||
|
TIMER_LOCK=0x8
|
||
|
} ExitLockRelease;
|
||
|
|
||
|
ExitLockRelease = NO_LOCK;
|
||
|
|
||
|
//todo: remove the read lock get/release
|
||
|
|
||
|
//used only for DELETE_MEMBERSHIP
|
||
|
#define RETURN_FROM_TIMER_PROCESSING() {\
|
||
|
IGMP_FREE(pvContext); \
|
||
|
if (ExitLockRelease&IF_LOCK) \
|
||
|
RELEASE_IF_LOCK_SHARED(IfIndex, "_WF_TimerProcessing"); \
|
||
|
if (ExitLockRelease&GROUP_LOCK) \
|
||
|
RELEASE_GROUP_LOCK(Group, "_WF_TimerProcessing"); \
|
||
|
if (ExitLockRelease&TIMER_LOCK) \
|
||
|
RELEASE_TIMER_LOCK("_WF_TimerProcessing"); \
|
||
|
Trace0(LEAVE1, "Leaving _WF_TimerProcessing()\n");\
|
||
|
LeaveIgmpWorker();\
|
||
|
return;\
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
if (!EnterIgmpWorker()) { return; }
|
||
|
Trace0(ENTER1, "Entering _WF_TimerProcessing");
|
||
|
|
||
|
|
||
|
// take shared interface lock
|
||
|
IfIndex = pWorkContext->IfIndex;
|
||
|
|
||
|
|
||
|
ACQUIRE_IF_LOCK_SHARED(IfIndex, "_WF_TimerProcessing");
|
||
|
ExitLockRelease |= IF_LOCK;
|
||
|
|
||
|
|
||
|
BEGIN_BREAKOUT_BLOCK1 {
|
||
|
//
|
||
|
// retrieve the interface
|
||
|
//
|
||
|
pite = GetIfByIndex(IfIndex);
|
||
|
if (pite == NULL) {
|
||
|
Trace1(IF, "_WF_TimerProcessing: interface %d not found", IfIndex);
|
||
|
Error = ERROR_CAN_NOT_COMPLETE;
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// exit quitely if the interface is not activated
|
||
|
//
|
||
|
if ( !(IS_IF_ACTIVATED(pite)) ) {
|
||
|
Trace1(ERR, "Trying to send packet on inactive interface(%d)",
|
||
|
pite->IfIndex);
|
||
|
|
||
|
Error = ERROR_CAN_NOT_COMPLETE;
|
||
|
GOTO_END_BLOCK1;
|
||
|
}
|
||
|
|
||
|
|
||
|
switch (pWorkContext->WorkType) {
|
||
|
|
||
|
//-----------------------------------------
|
||
|
// GENERAL QUERY
|
||
|
//-----------------------------------------
|
||
|
|
||
|
case MSG_GEN_QUERY:
|
||
|
{
|
||
|
Trace2(TIMER,
|
||
|
"Timer fired leads to General-query being sent on If(%0x)"
|
||
|
"Group(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pWorkContext->Group));
|
||
|
SEND_GEN_QUERY(pite);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------
|
||
|
// GROUP SPECIFIC QUERY
|
||
|
//-----------------------------------------
|
||
|
|
||
|
case MSG_GROUP_QUERY_V2 :
|
||
|
{
|
||
|
Trace2(TIMER,
|
||
|
"Timer fired leads to group query being sent on If(%0x)"
|
||
|
"Group(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pWorkContext->Group)
|
||
|
);
|
||
|
|
||
|
SEND_GROUP_QUERY_V2(pite, pWorkContext->Group);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// MEMBERSHIP TIMED OUT
|
||
|
//
|
||
|
case DELETE_MEMBERSHIP:
|
||
|
{
|
||
|
//
|
||
|
// Lock the group table bucket
|
||
|
//
|
||
|
ACQUIRE_GROUP_LOCK(Group, "_WF_TimerProcessing");
|
||
|
ExitLockRelease |= GROUP_LOCK;
|
||
|
|
||
|
|
||
|
//
|
||
|
// find the group entry. If entry not found then ignore the timer
|
||
|
//
|
||
|
pge = GetGroupFromGroupTable(Group, NULL, 0); //llCurTime not req
|
||
|
if (pge==NULL) {
|
||
|
RETURN_FROM_TIMER_PROCESSING();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// find the GI entry. If GI entry does not exist or has deletedFlag
|
||
|
// or is static group, then ignore the timer
|
||
|
//
|
||
|
pgie = GetGIFromGIList(pge, pite, pWorkContext->NHAddr, FALSE, NULL, 0);
|
||
|
if ( (pgie==NULL)||(pgie->bStaticGroup) ) {
|
||
|
|
||
|
RETURN_FROM_TIMER_PROCESSING();
|
||
|
}
|
||
|
|
||
|
// gi entry might be deleted here
|
||
|
if (pgie->Version==3 && pgie->FilterType==EXCLUSION) {
|
||
|
|
||
|
if (pgie->bStaticGroup) {
|
||
|
|
||
|
PLIST_ENTRY pHead, ple;
|
||
|
|
||
|
//
|
||
|
// remove all sources in exclusion list
|
||
|
//
|
||
|
pHead = &pgie->V3ExclusionList;
|
||
|
|
||
|
for (ple=pHead->Flink; ple!=pHead; ) {
|
||
|
|
||
|
PGI_SOURCE_ENTRY pSourceEntry;
|
||
|
pSourceEntry = CONTAINING_RECORD(ple, GI_SOURCE_ENTRY,
|
||
|
LinkSources);
|
||
|
ple = ple->Flink;
|
||
|
|
||
|
// dont have to call mgm as it will remain in -ve mfe
|
||
|
if (!pSourceEntry->bStaticSource) {
|
||
|
RemoveEntryList(&pSourceEntry->LinkSources);
|
||
|
IGMP_FREE(pSourceEntry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Trace2(TIMER,
|
||
|
"Timer fired leads to group filter mode change If(%0x) "
|
||
|
"Group(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pWorkContext->Group)
|
||
|
);
|
||
|
|
||
|
ChangeGroupFilterMode(pgie, INCLUSION);
|
||
|
}
|
||
|
else if (pgie->Version!=3) {
|
||
|
if (pgie->bStaticGroup)
|
||
|
break;
|
||
|
|
||
|
Trace2(TIMER,
|
||
|
"Timer fired leads to membership being timed out If(%0x) "
|
||
|
"Group(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pWorkContext->Group)
|
||
|
);
|
||
|
|
||
|
|
||
|
//
|
||
|
// finally delete the entry
|
||
|
//
|
||
|
Error = DeleteGIEntry(pgie, TRUE, TRUE); //updateStats, CallMgm
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
} //end case:DELETE_MEMBERSHIP
|
||
|
|
||
|
//
|
||
|
// SOURCE TIMED OUT
|
||
|
//
|
||
|
case DELETE_SOURCE:
|
||
|
case MSG_SOURCES_QUERY:
|
||
|
case MSG_GROUP_QUERY_V3:
|
||
|
case SHIFT_TO_V3:
|
||
|
case MOVE_SOURCE_TO_EXCL:
|
||
|
{
|
||
|
PGI_SOURCE_ENTRY pSourceEntry;
|
||
|
|
||
|
if ((pWorkContext->WorkType)==DELETE_SOURCE){
|
||
|
Trace3(TIMER,
|
||
|
"Timer fired leads to membership being timed out If(%0x) "
|
||
|
"Group(%d.%d.%d.%d) Source(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pWorkContext->Group),
|
||
|
PRINT_IPADDR(pWorkContext->Source)
|
||
|
);
|
||
|
}
|
||
|
else if ((pWorkContext->WorkType)==MSG_SOURCES_QUERY){
|
||
|
Trace2(TIMER,
|
||
|
"Timer fired leads to sources specific msg being sent If(%0x) "
|
||
|
"Group(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pWorkContext->Group));
|
||
|
|
||
|
}
|
||
|
else if ((pWorkContext->WorkType)==MSG_GROUP_QUERY_V3){
|
||
|
Trace2(TIMER,
|
||
|
"Timer fired leads to group query being sent on If(%0x) "
|
||
|
"Group(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pWorkContext->Group)
|
||
|
);
|
||
|
}
|
||
|
else if ((pWorkContext->WorkType)==SHIFT_TO_V3){
|
||
|
Trace2(TIMER,
|
||
|
"Timer fired leads to group shifting to v3 mode If(%0x) "
|
||
|
"Group(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pWorkContext->Group)
|
||
|
);
|
||
|
}
|
||
|
else if (pWorkContext->WorkType==MOVE_SOURCE_TO_EXCL){
|
||
|
Trace3(TIMER,
|
||
|
"Timer fired leads to source shifting to exclList If(%0x) "
|
||
|
"Group(%d.%d.%d.%d) Source(%d.%d.%d.%d)",
|
||
|
pite->IfIndex, PRINT_IPADDR(pWorkContext->Group),
|
||
|
PRINT_IPADDR(pWorkContext->Source)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Lock the group table bucket
|
||
|
//
|
||
|
ACQUIRE_GROUP_LOCK(Group, "_WF_TimerProcessing");
|
||
|
ExitLockRelease |= GROUP_LOCK;
|
||
|
|
||
|
|
||
|
//
|
||
|
// find the group entry. If entry not found then ignore the timer
|
||
|
//
|
||
|
pge = GetGroupFromGroupTable(Group, NULL, 0); //llCurTime not req
|
||
|
if (pge==NULL) {
|
||
|
RETURN_FROM_TIMER_PROCESSING();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// find the GI entry. If GI entry does not exist or has deletedFlag
|
||
|
// or is static group, then ignore the timer
|
||
|
//
|
||
|
pgie = GetGIFromGIList(pge, pite, pWorkContext->NHAddr, FALSE, NULL, 0);
|
||
|
if ( (pgie==NULL)||(pgie->bStaticGroup) ) {
|
||
|
|
||
|
RETURN_FROM_TIMER_PROCESSING();
|
||
|
}
|
||
|
|
||
|
|
||
|
ACQUIRE_TIMER_LOCK("_WF_Timer_Processing");
|
||
|
ExitLockRelease |= TIMER_LOCK;
|
||
|
|
||
|
//
|
||
|
// if changeSourceMode to excl, but filtertype is not excl
|
||
|
// then delete the source
|
||
|
//
|
||
|
|
||
|
if ( (pWorkContext->WorkType==MOVE_SOURCE_TO_EXCL)
|
||
|
&& (pgie->FilterType != EXCLUSION)
|
||
|
) {
|
||
|
pWorkContext->WorkType = DELETE_SOURCE;
|
||
|
Trace2(TIMER, "DeleteSource instead of moving to excl "
|
||
|
"Group(%d.%d.%d.%d) Source(%d.%d.%d.%d)",
|
||
|
PRINT_IPADDR(pWorkContext->Group),
|
||
|
PRINT_IPADDR(pWorkContext->Source)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if ((pWorkContext->WorkType)==DELETE_SOURCE){
|
||
|
|
||
|
//
|
||
|
// get the source entry from inclusion list
|
||
|
//
|
||
|
pSourceEntry = GetSourceEntry(pgie, pWorkContext->Source,
|
||
|
INCLUSION, NULL, 0, 0);
|
||
|
if (pSourceEntry==NULL) {
|
||
|
Trace1(TIMER, "Source %d.%d.%d.%d not found",
|
||
|
PRINT_IPADDR(pWorkContext->Source));
|
||
|
RETURN_FROM_TIMER_PROCESSING();
|
||
|
}
|
||
|
|
||
|
if (!pSourceEntry->bStaticSource) {
|
||
|
DeleteSourceEntry(pSourceEntry, TRUE); //process mgm
|
||
|
|
||
|
if (pgie->NumSources==0) {
|
||
|
DeleteGIEntry(pgie, TRUE, TRUE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if ((pWorkContext->WorkType)==MSG_SOURCES_QUERY) {
|
||
|
SEND_SOURCES_QUERY(pgie);
|
||
|
}
|
||
|
else if ((pWorkContext->WorkType)==MSG_GROUP_QUERY_V3) {
|
||
|
SendV3GroupQuery(pgie);
|
||
|
}
|
||
|
else if ((pWorkContext->WorkType)==SHIFT_TO_V3) {
|
||
|
|
||
|
// make sure that version has not changed
|
||
|
|
||
|
if (pgie->Version != 3 && IS_IF_VER3(pite)) {
|
||
|
|
||
|
// shift to v3 exclusion mode
|
||
|
// membership timer already running
|
||
|
pgie->Version = 3;
|
||
|
pgie->FilterType = EXCLUSION;
|
||
|
// dont have to join to mgm as already joined in v1,v2
|
||
|
}
|
||
|
}
|
||
|
else if (pWorkContext->WorkType==MOVE_SOURCE_TO_EXCL) {
|
||
|
|
||
|
pSourceEntry = GetSourceEntry(pgie, pWorkContext->Source,
|
||
|
INCLUSION, NULL, 0, 0);
|
||
|
if (pSourceEntry==NULL) {
|
||
|
Trace1(TIMER, "Source %d.%d.%d.%d not found",
|
||
|
PRINT_IPADDR(pWorkContext->Source));
|
||
|
RETURN_FROM_TIMER_PROCESSING();
|
||
|
}
|
||
|
if (pSourceEntry->bInclusionList==TRUE) {
|
||
|
if (pSourceEntry==NULL)
|
||
|
RETURN_FROM_TIMER_PROCESSING();
|
||
|
|
||
|
ChangeSourceFilterMode(pgie, pSourceEntry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
} //end case:DELETE_SOURCE,MSG_SOURCES_QUERY
|
||
|
|
||
|
} //end switch There should not be any code between here and
|
||
|
//endBreakout block
|
||
|
|
||
|
} END_BREAKOUT_BLOCK1;
|
||
|
|
||
|
|
||
|
RETURN_FROM_TIMER_PROCESSING();
|
||
|
|
||
|
return;
|
||
|
|
||
|
} //end _WF_TimerProcessing
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// DeleteRasClient
|
||
|
//
|
||
|
// Takes the if_group list lock and deletes all the GI entries associated with
|
||
|
// the ras client.
|
||
|
// Then takes write lock on the ras table and decrements the refCount. The
|
||
|
// ras table and interface entries are deleted if the deleted flag is set on pite.
|
||
|
// also releases the ras client from MGM.
|
||
|
//
|
||
|
// Queued by:
|
||
|
// DisconnectRasClient(), DeActivateInterfaceComplete() for ras server
|
||
|
// Locks:
|
||
|
// Initially runs in IF_GROUP_LIST_LOCK
|
||
|
// then runs in exclusive ras table lock.
|
||
|
// assumes if exclusive lock
|
||
|
// May call: _CompleteIfDeletion()
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
DeleteRasClient (
|
||
|
PRAS_TABLE_ENTRY prte
|
||
|
)
|
||
|
{
|
||
|
PLIST_ENTRY pHead, ple;
|
||
|
PGI_ENTRY pgie;
|
||
|
PIF_TABLE_ENTRY pite = prte->IfTableEntry;
|
||
|
PRAS_TABLE prt = prte->IfTableEntry->pRasTable;
|
||
|
DWORD Error = NO_ERROR, IfIndex=pite->IfIndex;
|
||
|
|
||
|
|
||
|
//
|
||
|
// take exclusive lock on the If_Group List and remove all timers
|
||
|
//
|
||
|
|
||
|
ACQUIRE_IF_GROUP_LIST_LOCK(IfIndex, "_WF_DeleteRasClient");
|
||
|
|
||
|
//
|
||
|
// Remove all timers associtated with that ras client's GI list
|
||
|
//
|
||
|
ACQUIRE_TIMER_LOCK("_WF_DeleteRasClient");
|
||
|
|
||
|
pHead = &prte->ListOfSameClientGroups;
|
||
|
DeleteAllTimers(pHead, RAS_CLIENT);
|
||
|
|
||
|
RELEASE_TIMER_LOCK("_WF_DeleteRasClient");
|
||
|
|
||
|
|
||
|
RELEASE_IF_GROUP_LIST_LOCK(IfIndex, "_WF_DeleteRasClient");
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// revisit the list and delete all GI entries. Need to take
|
||
|
// exclusive lock on the group bucket before deleting the GI entry
|
||
|
// No need to lock the If-Group list as no one can access it anymore
|
||
|
//
|
||
|
for (ple=pHead->Flink; ple!=pHead; ) {
|
||
|
|
||
|
DWORD dwGroup;
|
||
|
|
||
|
|
||
|
pgie = CONTAINING_RECORD(ple, GI_ENTRY, LinkBySameClientGroups);
|
||
|
ple=ple->Flink;
|
||
|
|
||
|
dwGroup = pgie->pGroupTableEntry->Group;
|
||
|
|
||
|
|
||
|
ACQUIRE_GROUP_LOCK(dwGroup, "_WF_DeleteRasClient");
|
||
|
|
||
|
DeleteGIEntryFromIf(pgie);
|
||
|
|
||
|
RELEASE_GROUP_LOCK(dwGroup, "_WF_DeleteRasClient");
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Take exclusive lock on the interface. If deleted flag set on interface
|
||
|
// and refcount==0 then delete the ras table and pite, else just decrement
|
||
|
// the refcount
|
||
|
//
|
||
|
|
||
|
|
||
|
// decrement Refcount
|
||
|
|
||
|
prt->RefCount --;
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// if deleted flag set and Refcount ==0 then delete Ras server completely
|
||
|
//
|
||
|
if ( (pite->Status&IF_DELETED_FLAG) &&(prt->RefCount==0) ){
|
||
|
|
||
|
CompleteIfDeletion(pite);
|
||
|
}
|
||
|
|
||
|
|
||
|
IGMP_FREE(prte);
|
||
|
|
||
|
|
||
|
return;
|
||
|
|
||
|
} //end _WF_DeleteRasClient
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// _WF_CompleteIfDeactivateDelete
|
||
|
//
|
||
|
// Completes deactivation an activated interface.
|
||
|
//
|
||
|
// Locking:
|
||
|
// does not require any lock on IfTable, as it is already removed from
|
||
|
// global interface lists.
|
||
|
// takes lock on Sockets list, as socket is getting deactivated
|
||
|
// Calls:
|
||
|
// DeActivateInterfaceComplete(). That function will also call
|
||
|
// _CompleteIfDeletion if the delete flag is set.
|
||
|
// Called by:
|
||
|
// _DeleteIfEntry() after it has called _DeActivationDeregisterFromMgm
|
||
|
// _UnbindIfEntry() after it has called _DeActivateInterfaceInitial
|
||
|
// _DisableIfEntry() after it has called _DeActivateInterfaceInitial
|
||
|
//------------------------------------------------------------------------------
|
||
|
VOID
|
||
|
CompleteIfDeactivateDelete (
|
||
|
PIF_TABLE_ENTRY pite
|
||
|
)
|
||
|
{
|
||
|
DWORD IfIndex = pite->IfIndex;
|
||
|
|
||
|
Trace1(ENTER1, "Entering _WF_CompleteIfDeactivateDelete(%d)", IfIndex);
|
||
|
|
||
|
|
||
|
ACQUIRE_SOCKETS_LOCK_EXCLUSIVE("_WF_CompleteIfDeactivateDelete");
|
||
|
|
||
|
DeActivateInterfaceComplete(pite);
|
||
|
|
||
|
RELEASE_SOCKETS_LOCK_EXCLUSIVE("_WF_CompleteIfDeactivateDelete");
|
||
|
|
||
|
|
||
|
|
||
|
// dont have to call _CompleteIfDeletion as it will be called in
|
||
|
// DeactivateInterface() as the delete flag is set.
|
||
|
|
||
|
|
||
|
Trace1(LEAVE1, "Leaving _WF_CompleteIfDeactivateDelete(%d)", IfIndex);
|
||
|
|
||
|
return;
|
||
|
|
||
|
} //end _WF_CompleteIfDeactivateDelete
|
||
|
|