Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2576 lines
78 KiB

//=============================================================================
// 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