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.
2903 lines
94 KiB
2903 lines
94 KiB
//=============================================================================
|
|
// Copyright (c) 1997 Microsoft Corporation
|
|
// File: table.c
|
|
//
|
|
// Abstract:
|
|
// This module implements some of the routines associated with creating,
|
|
// initializing, deleting timers, GI entries, table entries etc
|
|
//
|
|
// Author: K.S.Lokesh (lokeshs@) 11-1-97
|
|
//
|
|
// Revision History:
|
|
//=============================================================================
|
|
|
|
#include "pchigmp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _CreateIfSockets
|
|
//
|
|
// Creates the sockets.
|
|
// for proxy: a raw IPPROTO_IP socket so that igmp host functionality will
|
|
// take over for all groups added on that interface.
|
|
// for router: a raw IPPROTO_IGMP socket so that it receives all igmp packets
|
|
// A router never does Add Memberships
|
|
//
|
|
// Called by: _ActivateInterface()
|
|
// Locks: assumes exclusive lock on the interface, and socketsList
|
|
//------------------------------------------------------------------------------
|
|
|
|
DWORD
|
|
CreateIfSockets (
|
|
PIF_TABLE_ENTRY pite
|
|
)
|
|
{
|
|
DWORD Error = NO_ERROR, dwRetval, SockType;
|
|
DWORD IpAddr = pite->IpAddr;
|
|
DWORD IfIndex = pite->IfIndex;
|
|
SOCKADDR_IN saLocalIf;
|
|
BOOL bProxy = IS_PROTOCOL_TYPE_PROXY(pite);
|
|
PSOCKET_ENTRY pse = &pite->SocketEntry;
|
|
|
|
|
|
|
|
BEGIN_BREAKOUT_BLOCK1 {
|
|
|
|
//
|
|
// for proxy, create a IPPROTO_IP socket so that igmp host functionality
|
|
// takes over.
|
|
// for igmp router, create a raw IPPROTO_IGMP socket
|
|
//
|
|
SockType = (bProxy)? IPPROTO_IP : IPPROTO_IGMP;
|
|
|
|
|
|
//
|
|
// create input socket
|
|
//
|
|
pse->Socket = WSASocket(AF_INET, SOCK_RAW, SockType, NULL, 0, 0);
|
|
|
|
if (pse->Socket == INVALID_SOCKET) {
|
|
Error = WSAGetLastError();
|
|
Trace3(IF,
|
|
"error %d creating socket for interface %0x (%d.%d.%d.%d)",
|
|
Error, IfIndex, PRINT_IPADDR(IpAddr));
|
|
Logerr1(CREATE_SOCKET_FAILED_2, "%I", IpAddr, Error);
|
|
|
|
GOTO_END_BLOCK1;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// bind socket to local interface. If I dont bind multicast may
|
|
// not work.
|
|
//
|
|
|
|
ZeroMemory(&saLocalIf, sizeof(saLocalIf));
|
|
saLocalIf.sin_family = PF_INET;
|
|
saLocalIf.sin_addr.s_addr = IpAddr;
|
|
saLocalIf.sin_port = 0; //port shouldnt matter
|
|
|
|
// bind the input socket
|
|
|
|
Error = bind(pse->Socket, (SOCKADDR FAR *)&saLocalIf, sizeof(SOCKADDR));
|
|
|
|
if (Error == SOCKET_ERROR) {
|
|
Error = WSAGetLastError();
|
|
Trace3(IF, "error %d binding on socket for interface %0x (%d.%d.%d.%d)",
|
|
Error, IfIndex, PRINT_IPADDR(IpAddr));
|
|
Logerr1(BIND_FAILED, "%I", IpAddr, Error);
|
|
|
|
GOTO_END_BLOCK1;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// A proxy never sends/receives any packets. It is just expected to enable
|
|
// igmp host functionality to take over for the groups on which it joins.
|
|
//
|
|
|
|
//------------------------------
|
|
// if proxy then done
|
|
//------------------------------
|
|
|
|
if (bProxy)
|
|
GOTO_END_BLOCK1;
|
|
|
|
|
|
|
|
//------------------------------
|
|
// NOT PROXY INTERFACE
|
|
//------------------------------
|
|
|
|
if (!bProxy) {
|
|
|
|
// set ttl to 1: not required as it is set to 1 by default.
|
|
|
|
McastSetTtl(pse->Socket, 1);
|
|
|
|
|
|
//
|
|
// disable multicast packets from being loopedback.
|
|
// This may not work due to promiscuous mode,
|
|
// so you still have to check the input packets
|
|
//
|
|
|
|
{
|
|
BOOL bLoopBack = FALSE;
|
|
|
|
dwRetval = setsockopt(pse->Socket, IPPROTO_IP, IP_MULTICAST_LOOP,
|
|
(char *)&bLoopBack, sizeof(BOOL));
|
|
|
|
if (dwRetval==SOCKET_ERROR) {
|
|
Trace2(ERR, "error %d disabling multicast loopBack on IfIndex %0x",
|
|
WSAGetLastError(), IfIndex);
|
|
IgmpAssertOnError(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// if RasServerInterface, then activate hdrInclude option so that I can
|
|
// send GenQuery to all RAS clients
|
|
//
|
|
if (IS_RAS_SERVER_IF(pite->IfType)) {
|
|
|
|
INT iSetHdrIncl = 1;
|
|
|
|
Error = setsockopt( pse->Socket, IPPROTO_IP, IP_HDRINCL,
|
|
(char *) &iSetHdrIncl, sizeof(INT));
|
|
|
|
if (Error!=NO_ERROR) {
|
|
Error = WSAGetLastError();
|
|
Trace2(ERR, "error %d unable to set IP_HDRINCL option on interface %0x",
|
|
Error, IfIndex);
|
|
IgmpAssertOnError(FALSE);
|
|
Logerr1(SET_HDRINCL_FAILED, "%I", pite->IpAddr, Error);
|
|
GOTO_END_BLOCK1;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// set the interface on which multicasts must be sent
|
|
// set only for non rasserver (not internal) interfaces
|
|
//
|
|
|
|
dwRetval = setsockopt(pse->Socket, IPPROTO_IP, IP_MULTICAST_IF,
|
|
(PBYTE)&saLocalIf.sin_addr, sizeof(IN_ADDR));
|
|
|
|
if (dwRetval == SOCKET_ERROR) {
|
|
Error = WSAGetLastError();
|
|
Trace3(IF, "error %d setting interface %0x (%d.%d.%d.%d) to send multicast",
|
|
Error, IfIndex, PRINT_IPADDR(pite->IpAddr));
|
|
Logerr1(SET_MCAST_IF_FAILED, "%I", pite->IpAddr, Error);
|
|
Error = SOCKET_ERROR;
|
|
GOTO_END_BLOCK1;
|
|
}
|
|
|
|
{
|
|
//
|
|
// set router alert option for packets sent. dont have to set it for
|
|
// RasServerInterface where I do hdrInclude.
|
|
//
|
|
|
|
u_char Router_alert[4] = {148, 4, 0, 0};
|
|
|
|
dwRetval = setsockopt(pse->Socket, IPPROTO_IP, IP_OPTIONS,
|
|
(void *)Router_alert, sizeof(Router_alert));
|
|
|
|
if (dwRetval!=0) {
|
|
dwRetval = WSAGetLastError();
|
|
Trace2(ERR,
|
|
"error %d unable to set router alert option on interface %0x",
|
|
dwRetval, IfIndex
|
|
);
|
|
IgmpAssertOnError(FALSE);
|
|
Logerr1(SET_ROUTER_ALERT_FAILED, "%I", pite->IpAddr, dwRetval);
|
|
Error = dwRetval;
|
|
GOTO_END_BLOCK1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// set the interface in promiscuous igmp multicast mode.
|
|
//
|
|
|
|
{
|
|
DWORD dwEnable = 1;
|
|
DWORD dwNum;
|
|
|
|
dwRetval = WSAIoctl(pse->Socket, SIO_RCVALL_IGMPMCAST, (char *)&dwEnable,
|
|
sizeof(dwEnable), NULL , 0, &dwNum, NULL, NULL);
|
|
|
|
if (dwRetval !=0) {
|
|
dwRetval = WSAGetLastError();
|
|
Trace2(IF, "error %d setting interface %0x as promiscuous multicast",
|
|
dwRetval, IfIndex);
|
|
Logerr1(SET_ROUTER_ALERT_FAILED, "%I", pite->IpAddr, dwRetval);
|
|
Error = dwRetval;
|
|
GOTO_END_BLOCK1;
|
|
}
|
|
else {
|
|
Trace1(IF, "promiscuous igmp multicast enabled on If (%d)",
|
|
IfIndex);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Router doesnt have to join any group as it is in promiscuous mode
|
|
//
|
|
|
|
|
|
//
|
|
// create entry in the SocketsEvents list
|
|
//
|
|
{
|
|
BOOLEAN bCreateNewEntry;
|
|
PLIST_ENTRY ple, pHead = &g_ListOfSocketEvents;
|
|
PSOCKET_EVENT_ENTRY psee;
|
|
|
|
|
|
bCreateNewEntry = TRUE;
|
|
|
|
|
|
//
|
|
// see if a new socket-event entry has to be created
|
|
//
|
|
if (g_pIfTable->NumInterfaces>NUM_SINGLE_SOCKET_EVENTS) {
|
|
|
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
|
|
|
psee = CONTAINING_RECORD(ple, SOCKET_EVENT_ENTRY,
|
|
LinkBySocketEvents);
|
|
|
|
if (psee->NumInterfaces < MAX_SOCKETS_PER_EVENT) {
|
|
bCreateNewEntry = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// create a new socket-event entry and insert in the list
|
|
// register the event entry with the wait thread
|
|
//
|
|
if (bCreateNewEntry) {
|
|
|
|
psee = IGMP_ALLOC(sizeof(SOCKET_EVENT_ENTRY),
|
|
0x80000,pite->IfIndex);
|
|
|
|
PROCESS_ALLOC_FAILURE2(psee,
|
|
"error %d allocating %d bytes for SocketEventEntry",
|
|
Error, sizeof(SOCKET_EVENT_ENTRY),
|
|
GOTO_END_BLOCK1);
|
|
|
|
InitializeListHead(&psee->ListOfInterfaces);
|
|
psee->NumInterfaces = 0;
|
|
InsertHeadList(pHead, &psee->LinkBySocketEvents);
|
|
|
|
|
|
psee->InputEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
|
|
if (psee->InputEvent == NULL) {
|
|
Error = GetLastError();
|
|
Trace1(ERR,
|
|
"error %d creating InputEvent in CreateIfSockets",
|
|
Error);
|
|
IgmpAssertOnError(FALSE);
|
|
Logerr0(CREATE_EVENT_FAILED, Error);
|
|
GOTO_END_BLOCK1;
|
|
}
|
|
|
|
|
|
if (! RegisterWaitForSingleObject(
|
|
&psee->InputWaitEvent,
|
|
psee->InputEvent,
|
|
WT_ProcessInputEvent, psee,
|
|
INFINITE,
|
|
(WT_EXECUTEINWAITTHREAD)|(WT_EXECUTEONLYONCE)
|
|
))
|
|
{
|
|
Error = GetLastError();
|
|
Trace1(ERR, "error %d RegisterWaitForSingleObject", Error);
|
|
IgmpAssertOnError(FALSE);
|
|
GOTO_END_BLOCK1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// put the socketEntry in the list
|
|
//
|
|
InsertTailList(&psee->ListOfInterfaces, &pse->LinkByInterfaces);
|
|
pse->pSocketEventsEntry = psee;
|
|
|
|
|
|
|
|
//
|
|
// if the socket-event entry cannot take any more sockets,
|
|
// then put it at end of list
|
|
//
|
|
if (++psee->NumInterfaces==MAX_SOCKETS_PER_EVENT) {
|
|
RemoveEntryList(&psee->LinkBySocketEvents);
|
|
InsertTailList(pHead, &psee->LinkBySocketEvents);
|
|
}
|
|
} //end:create entry in the sockets list
|
|
|
|
//
|
|
// create socket for static joins.
|
|
//
|
|
{
|
|
pite->StaticGroupSocket =
|
|
WSASocket(AF_INET, SOCK_RAW, IPPROTO_IP, NULL, 0, 0);
|
|
if (pite->StaticGroupSocket == INVALID_SOCKET) {
|
|
Error = WSAGetLastError();
|
|
Trace3(IF,
|
|
"error %d creating static group socket for interface "
|
|
"%d (%d.%d.%d.%d)",
|
|
Error, pite->IfIndex, PRINT_IPADDR(pite->IpAddr));
|
|
Logerr1(CREATE_SOCKET_FAILED_2, "%I", pite->IpAddr, Error);
|
|
|
|
GOTO_END_BLOCK1;
|
|
}
|
|
//
|
|
// bind socket to local interface. If I dont bind multicast may
|
|
// not work.
|
|
//
|
|
saLocalIf.sin_family = PF_INET;
|
|
saLocalIf.sin_addr.s_addr = pite->IpAddr;
|
|
saLocalIf.sin_port = 0; //port shouldnt matter
|
|
|
|
Error = bind(pite->StaticGroupSocket, (SOCKADDR FAR *)&saLocalIf,
|
|
sizeof(SOCKADDR));
|
|
}
|
|
} // end: not proxy interface
|
|
|
|
|
|
} END_BREAKOUT_BLOCK1;
|
|
|
|
if (Error!=NO_ERROR)
|
|
DeleteIfSockets(pite);
|
|
|
|
return Error;
|
|
|
|
} //end _CreateIfSockets
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _DeleteIfSockets
|
|
//
|
|
// Called by: _DeActivateInterfaceComplete()
|
|
//------------------------------------------------------------------------------
|
|
VOID
|
|
DeleteIfSockets (
|
|
PIF_TABLE_ENTRY pite
|
|
)
|
|
{
|
|
PSOCKET_ENTRY pse = &pite->SocketEntry;
|
|
BOOL bProxy = IS_PROTOCOL_TYPE_PROXY(pite);
|
|
|
|
|
|
// close input socket
|
|
|
|
if (pse->Socket!=INVALID_SOCKET) {
|
|
|
|
if (closesocket(pse->Socket) == SOCKET_ERROR) {
|
|
Trace1(IF, "error %d closing socket", WSAGetLastError());
|
|
}
|
|
|
|
pse->Socket = INVALID_SOCKET;
|
|
}
|
|
|
|
//
|
|
// if router interface. delete socket from socketEventList
|
|
// and free the socketEventEntry only if they were initialized.
|
|
//
|
|
if ((!bProxy)&&(pse->pSocketEventsEntry!=NULL)) {
|
|
|
|
PSOCKET_EVENT_ENTRY psee = pse->pSocketEventsEntry;
|
|
|
|
RemoveEntryList(&pse->LinkByInterfaces);
|
|
|
|
if (--psee->NumInterfaces==0) {
|
|
|
|
if (psee->InputWaitEvent) {
|
|
|
|
HANDLE WaitHandle ;
|
|
|
|
WaitHandle = InterlockedExchangePointer(&psee->InputWaitEvent, NULL);
|
|
|
|
if (WaitHandle)
|
|
UnregisterWaitEx( WaitHandle, NULL ) ;
|
|
}
|
|
CloseHandle(psee->InputEvent);
|
|
|
|
RemoveEntryList(&psee->LinkBySocketEvents);
|
|
IGMP_FREE(psee);
|
|
}
|
|
if (pite->StaticGroupSocket!=INVALID_SOCKET) {
|
|
closesocket(pite->StaticGroupSocket);
|
|
pite->StaticGroupSocket = INVALID_SOCKET;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _DeleteAllTimers
|
|
//
|
|
// Deletes all timers associated with a GI entry.
|
|
//
|
|
// Called by: _DeActivateInterfaceComplete()
|
|
// Locks: Assumes Timer lock and GroupBucket lock.
|
|
//------------------------------------------------------------------------------
|
|
|
|
VOID
|
|
DeleteAllTimers (
|
|
PLIST_ENTRY pHead,
|
|
DWORD bEntryType
|
|
)
|
|
{
|
|
PLIST_ENTRY ple;
|
|
PGI_ENTRY pgie;
|
|
|
|
Trace0(ENTER1, "entering _DeleteAllTimers()");
|
|
|
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
|
|
|
|
|
pgie = (bEntryType==RAS_CLIENT)
|
|
? CONTAINING_RECORD(ple, GI_ENTRY, LinkBySameClientGroups)
|
|
: CONTAINING_RECORD(ple, GI_ENTRY, LinkBySameIfGroups);
|
|
|
|
if (IS_TIMER_ACTIVE(pgie->GroupMembershipTimer))
|
|
RemoveTimer(&pgie->GroupMembershipTimer, DBG_N);
|
|
|
|
|
|
if (IS_TIMER_ACTIVE(pgie->LastMemQueryTimer))
|
|
RemoveTimer(&pgie->LastMemQueryTimer, DBG_N);
|
|
|
|
if (IS_TIMER_ACTIVE(pgie->LastVer1ReportTimer))
|
|
RemoveTimer(&pgie->LastVer1ReportTimer, DBG_N);
|
|
|
|
if (IS_TIMER_ACTIVE(pgie->LastVer2ReportTimer))
|
|
RemoveTimer(&pgie->LastVer2ReportTimer, DBG_N);
|
|
|
|
if (IS_TIMER_ACTIVE(pgie->V3SourcesQueryTimer))
|
|
RemoveTimer(&pgie->V3SourcesQueryTimer, DBG_N);
|
|
|
|
|
|
// delete all sources timers
|
|
|
|
if (pgie->Version==3) {
|
|
PLIST_ENTRY pleSrc, pHeadSrc;
|
|
|
|
pHeadSrc = &pgie->V3InclusionListSorted;
|
|
for (pleSrc=pHeadSrc->Flink; pleSrc!=pHeadSrc; pleSrc=pleSrc->Flink){
|
|
PGI_SOURCE_ENTRY pSourceEntry;
|
|
pSourceEntry = CONTAINING_RECORD(pleSrc, GI_SOURCE_ENTRY, LinkSourcesInclListSorted);
|
|
if (IS_TIMER_ACTIVE(pSourceEntry->SourceExpTimer))
|
|
RemoveTimer(&pSourceEntry->SourceExpTimer, DBG_Y);
|
|
}
|
|
pHeadSrc = &pgie->V3ExclusionList;
|
|
for (pleSrc=pHeadSrc->Flink; pleSrc!=pHeadSrc; pleSrc=pleSrc->Flink){
|
|
PGI_SOURCE_ENTRY pSourceEntry;
|
|
pSourceEntry = CONTAINING_RECORD(pleSrc, GI_SOURCE_ENTRY, LinkSources);
|
|
if (IS_TIMER_ACTIVE(pSourceEntry->SourceExpTimer))
|
|
RemoveTimer(&pSourceEntry->SourceExpTimer, DBG_Y);
|
|
}
|
|
}
|
|
}
|
|
|
|
Trace0(LEAVE1, "Leaving _DeleteAllTimers()");
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _DeleteGIEntry
|
|
//
|
|
// Locks: Assumes lock on the group bucket. takes lock on IfGroup list.
|
|
// takes lock on groupList if group being deleted.
|
|
//------------------------------------------------------------------------------
|
|
|
|
DWORD
|
|
DeleteGIEntry (
|
|
PGI_ENTRY pgie, //group interface entry
|
|
BOOL bUpdateStats,
|
|
BOOL bCallMgm
|
|
)
|
|
{
|
|
PIF_TABLE_ENTRY pite = pgie->pIfTableEntry;
|
|
PRAS_TABLE_ENTRY prte = pgie->pRasTableEntry;
|
|
PGROUP_TABLE_ENTRY pge = pgie->pGroupTableEntry;
|
|
PGI_ENTRY pgieCur;
|
|
BOOL bRas = (prte!=NULL);
|
|
DWORD NHAddr;
|
|
DWORD dwRetval;
|
|
|
|
|
|
Trace0(ENTER1, "Entering _DeleteGIEntry()");
|
|
|
|
NHAddr = (bRas) ? prte->NHAddr : 0;
|
|
|
|
Trace4(GROUP, "Deleting group(%d.%d.%d.%d) on Interface(%0x)(%d.%d.%d.%d) "
|
|
"NHAddr(%d.%d.%d.%d)",
|
|
PRINT_IPADDR(pge->Group), pite->IfIndex,
|
|
PRINT_IPADDR(pite->IpAddr), PRINT_IPADDR(NHAddr));
|
|
|
|
|
|
bCallMgm = bCallMgm && (CAN_ADD_GROUPS_TO_MGM(pite));
|
|
|
|
//
|
|
// exclusion mode. remove all exclusion entries
|
|
// dont have to call MGM as it still has to be excluded
|
|
//
|
|
if (pgie->Version==3) {
|
|
PLIST_ENTRY pleSrc, pHeadSrc;
|
|
|
|
pHeadSrc = &pgie->V3InclusionListSorted;
|
|
for (pleSrc=pHeadSrc->Flink; pleSrc!=pHeadSrc; ){
|
|
PGI_SOURCE_ENTRY pSourceEntry;
|
|
pSourceEntry = CONTAINING_RECORD(pleSrc, GI_SOURCE_ENTRY, LinkSources);
|
|
pleSrc=pleSrc->Flink;
|
|
DeleteSourceEntry(pSourceEntry, bCallMgm);
|
|
}
|
|
|
|
pHeadSrc = &pgie->V3ExclusionList;
|
|
for (pleSrc=pHeadSrc->Flink; pleSrc!=pHeadSrc; ){
|
|
PGI_SOURCE_ENTRY pSourceEntry;
|
|
pSourceEntry = CONTAINING_RECORD(pleSrc, GI_SOURCE_ENTRY, LinkSources);
|
|
pleSrc=pleSrc->Flink;
|
|
DeleteSourceEntry(pSourceEntry, bCallMgm);
|
|
}
|
|
}
|
|
|
|
//
|
|
// call mgm to remove this group
|
|
//
|
|
|
|
if ( bCallMgm ) {
|
|
if ( (pgie->Version==3 && pgie->FilterType==EXCLUSION)
|
|
|| pgie->Version!=3)
|
|
{
|
|
MGM_DELETE_GROUP_MEMBERSHIP_ENTRY(pite, NHAddr,
|
|
0, 0,
|
|
pge->Group, 0xffffffff, MGM_JOIN_STATE_FLAG);
|
|
}
|
|
}
|
|
|
|
//
|
|
// remove all timers
|
|
//
|
|
|
|
ACQUIRE_TIMER_LOCK("_DeleteGIEntry");
|
|
|
|
|
|
if (IS_TIMER_ACTIVE(pgie->GroupMembershipTimer))
|
|
RemoveTimer(&pgie->GroupMembershipTimer, DBG_N);
|
|
|
|
|
|
if (IS_TIMER_ACTIVE(pgie->LastMemQueryTimer))
|
|
RemoveTimer(&pgie->LastMemQueryTimer, DBG_N);
|
|
|
|
if (IS_TIMER_ACTIVE(pgie->LastVer1ReportTimer))
|
|
RemoveTimer(&pgie->LastVer1ReportTimer, DBG_N);
|
|
|
|
if (IS_TIMER_ACTIVE(pgie->LastVer2ReportTimer))
|
|
RemoveTimer(&pgie->LastVer2ReportTimer, DBG_N);
|
|
|
|
if (IS_TIMER_ACTIVE(pgie->V3SourcesQueryTimer))
|
|
RemoveTimer(&pgie->V3SourcesQueryTimer, DBG_Y);
|
|
|
|
RELEASE_TIMER_LOCK("_DeleteGIEntry");
|
|
|
|
|
|
|
|
//
|
|
// Remove from IfGroupList. needs lock on IfGroupList
|
|
//
|
|
ACQUIRE_IF_GROUP_LIST_LOCK(pite->IfIndex, "_DeleteGIEntry");
|
|
|
|
// if interface being deleted, then return from here
|
|
|
|
if (IS_IF_DELETED(pgie->pIfTableEntry)) {
|
|
RELEASE_IF_GROUP_LIST_LOCK(pite->IfIndex, "_DeleteGIEntry");
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// remove GI entry from group's GI list
|
|
|
|
RemoveEntryList(&pgie->LinkByGI);
|
|
|
|
|
|
//
|
|
// remove entry from interface list
|
|
//
|
|
RemoveEntryList(&pgie->LinkBySameIfGroups);
|
|
if (bRas)
|
|
RemoveEntryList(&pgie->LinkBySameClientGroups);
|
|
|
|
|
|
RELEASE_IF_GROUP_LIST_LOCK(pite->IfIndex, "_DeleteGIEntry");
|
|
|
|
|
|
|
|
//
|
|
// decrement the number of virtual interfaces. I have to do
|
|
// interlocked decrement as I dont take group_list lock
|
|
//
|
|
InterlockedDecrement(&pge->NumVifs);
|
|
|
|
//
|
|
// if group has no more interfaces hanging from it, then delete it
|
|
// and update statistics
|
|
//
|
|
if (IsListEmpty(&pge->ListOfGIs)) {
|
|
|
|
// take groupList lock before deleting it from the group list
|
|
|
|
ACQUIRE_GROUP_LIST_LOCK("_DeleteGIEntry");
|
|
RemoveEntryList(&pge->LinkByGroup);
|
|
RELEASE_GROUP_LIST_LOCK("_DeleteGIEntry");
|
|
|
|
|
|
|
|
// remove group entry from the group hash table
|
|
|
|
RemoveEntryList(&pge->HTLinkByGroup);
|
|
|
|
|
|
IGMP_FREE(pge);
|
|
pge = NULL;
|
|
|
|
|
|
//global stats (has to be updated even if bUpdateStats==FALSE)
|
|
|
|
InterlockedDecrement(&g_Info.CurrentGroupMemberships);
|
|
|
|
#if DBG
|
|
DebugPrintGroupsList(1);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// update statistics
|
|
//
|
|
if (bUpdateStats) {
|
|
|
|
//
|
|
// ras interface statistics (decrement only if last GI for ras)
|
|
//
|
|
if (bRas) {
|
|
|
|
// see if GI entry exists for that ras client. very inefficient
|
|
if (pge!=NULL) {
|
|
PLIST_ENTRY pHead, ple;
|
|
pHead = &pge->ListOfGIs;
|
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
|
pgieCur = CONTAINING_RECORD(ple, GI_ENTRY, LinkByGI);
|
|
if (pgieCur->IfIndex>=pite->IfIndex)
|
|
break;
|
|
}
|
|
if ( (ple==pHead)||(pgieCur->IfIndex!=pite->IfIndex) ) {
|
|
InterlockedDecrement(&pite->Info.CurrentGroupMemberships);
|
|
}
|
|
}
|
|
|
|
// last GI entry
|
|
else {
|
|
InterlockedDecrement(&pite->Info.CurrentGroupMemberships);
|
|
}
|
|
|
|
// update ras client stats
|
|
if (g_Config.RasClientStats) {
|
|
InterlockedDecrement(&prte->Info.CurrentGroupMemberships);
|
|
}
|
|
}
|
|
|
|
// not ras interace
|
|
else {
|
|
InterlockedDecrement(&pite->Info.CurrentGroupMemberships);
|
|
}
|
|
}
|
|
|
|
IGMP_FREE_NOT_NULL(pgie->V3InclusionList);
|
|
IGMP_FREE(pgie);
|
|
|
|
Trace0(LEAVE1, "Leaving _DeleteGIEntry");
|
|
return NO_ERROR;
|
|
|
|
}//end _DeleteGIEntry
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _DeleteAllGIEntries
|
|
//
|
|
// Repeatedly calls _DeleteGIEntryFromIf() to delete each GI entry from the list.
|
|
// If there are a lot of GI entries, optimizes on the GroupBucket locks
|
|
// by grouping all GI entries hashing to the same bucket and then acquiring
|
|
// GroupBucket locks to delete them.
|
|
//
|
|
// Locks: interface_group_list lock not req. exclusive interface lock not req.
|
|
// as the interface has been removed from external lists.
|
|
// Calls: Repeatedly calls _DeleteGIEntryFromIf() to remove each GI entry
|
|
// Called by: _DeActivateInterfaceComplete()
|
|
//------------------------------------------------------------------------------
|
|
|
|
VOID
|
|
DeleteAllGIEntries(
|
|
PIF_TABLE_ENTRY pite
|
|
)
|
|
{
|
|
PLIST_ENTRY pHead, ple, pleOld;
|
|
PGI_ENTRY pgie;
|
|
DWORD dwGroup;
|
|
|
|
|
|
//
|
|
// concatenate ListOfSameIfGroupsNew at the end of ListOfSameIfGroups so
|
|
// that I have to delete only one list
|
|
//
|
|
|
|
CONCATENATE_LISTS(pite->ListOfSameIfGroups, pite->ListOfSameIfGroupsNew);
|
|
|
|
// if ras interface then return as list will be deleted through RAS clients
|
|
|
|
if (IS_RAS_SERVER_IF(pite->IfType))
|
|
return;
|
|
|
|
|
|
pHead = &pite->ListOfSameIfGroups;
|
|
if (IsListEmpty(pHead))
|
|
return;
|
|
|
|
|
|
//--------------------------------------------------------
|
|
// do optimization only if there are lots of GI entries
|
|
//--------------------------------------------------------
|
|
|
|
if (pite->Info.CurrentGroupMemberships > GROUP_HASH_TABLE_SZ*2) {
|
|
|
|
DWORD i;
|
|
LIST_ENTRY TmpGroupTable[GROUP_HASH_TABLE_SZ];
|
|
|
|
|
|
// initialize the temp group table
|
|
|
|
for (i=0; i<GROUP_HASH_TABLE_SZ; i++) {
|
|
|
|
InitializeListHead(&TmpGroupTable[i]);
|
|
}
|
|
|
|
|
|
// move the GI entries to the temp group table using LinkBySameIfGroups
|
|
// LinkBySameIfGroups is not used anymore
|
|
|
|
pHead = &pite->ListOfSameIfGroups;
|
|
|
|
for (ple=pHead->Flink; ple!=pHead; ) {
|
|
|
|
// remove from old list
|
|
pleOld = ple;
|
|
ple = ple->Flink;
|
|
|
|
RemoveEntryList(pleOld);
|
|
|
|
|
|
pgie = CONTAINING_RECORD(pleOld, GI_ENTRY, LinkBySameIfGroups);
|
|
|
|
dwGroup = pgie->pGroupTableEntry->Group;
|
|
|
|
|
|
// put in appropriate bucket
|
|
|
|
InsertHeadList(&TmpGroupTable[GROUP_HASH_VALUE(dwGroup)],
|
|
&pgie->LinkBySameIfGroups);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// now delete GI entries going by all groups which hash to same bucket
|
|
//
|
|
for (i=0; i<GROUP_HASH_TABLE_SZ; i++) {
|
|
|
|
if (IsListEmpty(&TmpGroupTable[i]))
|
|
continue;
|
|
|
|
|
|
//
|
|
// LOCK GROUP BUCKET (done use ACQUIRE_GROUP_LOCK macros)
|
|
//
|
|
ACQUIRE_GROUP_LOCK(i, "_DeleteAllGIEntries");
|
|
|
|
pHead = &TmpGroupTable[i];
|
|
|
|
|
|
// delete all GI entries that hash to that bucket
|
|
|
|
for (ple=pHead->Flink; ple!=pHead; ) {
|
|
|
|
pgie = CONTAINING_RECORD(ple, GI_ENTRY, LinkBySameIfGroups);
|
|
ple=ple->Flink;
|
|
|
|
//
|
|
// remove the entry from the group's GI list and update
|
|
// statistics. If group's GI list becomes empty, removes group
|
|
//
|
|
DeleteGIEntryFromIf(pgie);
|
|
}
|
|
|
|
|
|
RELEASE_GROUP_LOCK(i, "_DeleteAllGIEntries");
|
|
|
|
}
|
|
|
|
InitializeListHead(&pite->ListOfSameIfGroups);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// NO OPTIMIZATION
|
|
//-----------------------------------------------------------
|
|
|
|
pHead = &pite->ListOfSameIfGroups;
|
|
|
|
//
|
|
// delete all GI entries hanging from that interface.
|
|
//
|
|
for (ple=pHead->Flink; ple!=pHead; ) {
|
|
|
|
|
|
pgie = CONTAINING_RECORD(ple, GI_ENTRY, LinkBySameIfGroups);
|
|
ple=ple->Flink;
|
|
|
|
dwGroup = pgie->pGroupTableEntry->Group;
|
|
|
|
|
|
// LOCK GROUP BUCKET
|
|
|
|
ACQUIRE_GROUP_LOCK(dwGroup,
|
|
"_DeActivateInterfaceComplete");
|
|
|
|
DeleteGIEntryFromIf(pgie);
|
|
|
|
|
|
RELEASE_GROUP_LOCK(dwGroup, "_DeActivateInterfaceComplete");
|
|
|
|
}
|
|
|
|
|
|
InitializeListHead(&pite->ListOfSameIfGroups);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _DeleteGIEntryFromIf
|
|
//
|
|
// Called to delete a GI entry when an interface/RAS client is being deleted.
|
|
// The GI entries cannot be accessed from anywhere except through enumeration of
|
|
// group list.
|
|
//
|
|
// Locks: Assumes lock on the group bucket. lock on IfGroup list not req.
|
|
// Called by: _DeleteAllGIEntries() which in turn called by
|
|
// _DeActivateInterfaceComplete().
|
|
//------------------------------------------------------------------------------
|
|
|
|
VOID
|
|
DeleteGIEntryFromIf (
|
|
PGI_ENTRY pgie //group interface entry
|
|
)
|
|
{
|
|
PIF_TABLE_ENTRY pite = pgie->pIfTableEntry;
|
|
PGROUP_TABLE_ENTRY pge = pgie->pGroupTableEntry;
|
|
PLIST_ENTRY pHead, ple;
|
|
DWORD IfIndex = pite->IfIndex;
|
|
|
|
|
|
|
|
Trace1(ENTER1, "Entering _DeleteGIEntryFromIf(): IfIndex(%0x)", IfIndex);
|
|
|
|
|
|
//
|
|
// delete sources
|
|
//
|
|
if (pgie->Version==3) {
|
|
PLIST_ENTRY pleSrc, pHeadSrc;
|
|
|
|
pHeadSrc = &pgie->V3InclusionListSorted;
|
|
for (pleSrc=pHeadSrc->Flink; pleSrc!=pHeadSrc; ){
|
|
PGI_SOURCE_ENTRY pSourceEntry;
|
|
pSourceEntry = CONTAINING_RECORD(pleSrc, GI_SOURCE_ENTRY, LinkSourcesInclListSorted);
|
|
pleSrc=pleSrc->Flink;
|
|
IGMP_FREE(pSourceEntry);
|
|
}
|
|
|
|
pHeadSrc = &pgie->V3ExclusionList;
|
|
for (pleSrc=pHeadSrc->Flink; pleSrc!=pHeadSrc; ){
|
|
PGI_SOURCE_ENTRY pSourceEntry;
|
|
pSourceEntry = CONTAINING_RECORD(pleSrc, GI_SOURCE_ENTRY, LinkSources);
|
|
pleSrc=pleSrc->Flink;
|
|
IGMP_FREE(pSourceEntry);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove pgie from gi list. Dont have to remove from ListBySameIfGroups
|
|
//
|
|
RemoveEntryList(&pgie->LinkByGI);
|
|
|
|
InterlockedDecrement(&pge->NumVifs);
|
|
|
|
|
|
//
|
|
// if group has no more interfaces hanging from it, then delete it
|
|
// and update statistics
|
|
//
|
|
if (IsListEmpty(&pge->ListOfGIs)) {
|
|
|
|
|
|
// have to lock the group-list before deleting any group
|
|
|
|
ACQUIRE_GROUP_LIST_LOCK("_DeleteGIEntryFromIf");
|
|
|
|
RemoveEntryList(&pge->LinkByGroup);
|
|
|
|
RELEASE_GROUP_LIST_LOCK("_DeleteGIEntryFromIf");
|
|
|
|
|
|
RemoveEntryList(&pge->HTLinkByGroup);
|
|
|
|
IGMP_FREE(pge);
|
|
|
|
InterlockedDecrement(&g_Info.CurrentGroupMemberships);
|
|
}
|
|
|
|
IGMP_FREE_NOT_NULL(pgie->V3InclusionList);
|
|
IGMP_FREE(pgie);
|
|
|
|
Trace1(LEAVE1, "Leaving _DeleteGIEntryFromIf(%0x)", IfIndex);
|
|
|
|
return;
|
|
|
|
}//end _DeleteGIEntryFromIf
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// DebugPrintIfConfig
|
|
//------------------------------------------------------------------------------
|
|
VOID
|
|
DebugPrintIfConfig (
|
|
PIGMP_MIB_IF_CONFIG pConfigExt,
|
|
DWORD IfIndex
|
|
)
|
|
{
|
|
DWORD i;
|
|
PCHAR StaticGroupStr[5];
|
|
BOOL bVersion3 = IS_CONFIG_IGMP_V3(pConfigExt);
|
|
|
|
|
|
Trace1(CONFIG, "Printing Config Info for interface(%0x)", IfIndex);
|
|
Trace1(CONFIG, "Version: 0x%0x", pConfigExt->Version);
|
|
Trace1(CONFIG, "IfType: %d", pConfigExt->IfType);
|
|
|
|
|
|
{
|
|
CHAR str[150];
|
|
|
|
strcpy(str, "");
|
|
if (pConfigExt->Flags&IGMP_INTERFACE_ENABLED_IN_CONFIG)
|
|
strcat(str, "IF_ENABLED ");
|
|
else
|
|
strcat(str, "IF_DISABLED ");
|
|
|
|
if (pConfigExt->Flags&IGMP_ACCEPT_RTRALERT_PACKETS_ONLY)
|
|
strcat(str, "RTRALERT_PACKETS_ONLY ");
|
|
|
|
Trace1(CONFIG, "Flags: %s", str);
|
|
|
|
}
|
|
|
|
Trace1(CONFIG, "IgmpProtocolType: %d", pConfigExt->IgmpProtocolType);
|
|
Trace1(CONFIG, "RobustnessVariable: %d", pConfigExt->RobustnessVariable);
|
|
Trace1(CONFIG, "StartupQueryInterval: %d",
|
|
pConfigExt->StartupQueryInterval);
|
|
Trace1(CONFIG, "StartupQueryCount : %d",
|
|
pConfigExt->StartupQueryCount);
|
|
Trace1(CONFIG, "GenQueryInterval: %d", pConfigExt->GenQueryInterval);
|
|
Trace1(CONFIG, "GenQueryMaxResponseTime: %d",
|
|
pConfigExt->GenQueryMaxResponseTime);
|
|
Trace1(CONFIG, "LastMemQueryInterval: %d (ms)",
|
|
pConfigExt->LastMemQueryInterval);
|
|
Trace1(CONFIG, "LastMemQueryCount: %d", pConfigExt->LastMemQueryCount);
|
|
Trace1(CONFIG, "OtherQuerierPresentInterval:%d",
|
|
pConfigExt->OtherQuerierPresentInterval);
|
|
Trace1(CONFIG, "GroupMembershipTimeout: %d",
|
|
pConfigExt->GroupMembershipTimeout);
|
|
|
|
|
|
if (pConfigExt->NumStaticGroups>0) {
|
|
|
|
PIGMP_STATIC_GROUP pStaticGroup;
|
|
PSTATIC_GROUP_V3 pStaticGroupV3;
|
|
|
|
Trace1(CONFIG, "NumStaticGroups: %d",
|
|
pConfigExt->NumStaticGroups);
|
|
|
|
pStaticGroup = GET_FIRST_IGMP_STATIC_GROUP(pConfigExt);
|
|
if (bVersion3) {
|
|
pStaticGroupV3 = GET_FIRST_STATIC_GROUP_V3(pConfigExt);
|
|
|
|
Trace0(CONFIG, " Group Mode(Host/MGM) Filter(In/Ex) "
|
|
"NumSources");
|
|
}
|
|
else
|
|
Trace0(CONFIG, " Group Mode(Host/MGM)");
|
|
|
|
for (i=0; i<pConfigExt->NumStaticGroups; i++){
|
|
|
|
if (bVersion3) {
|
|
DWORD j;
|
|
Trace5(CONFIG, "%d. %15s %d %d %d",
|
|
i+1, INET_NTOA(pStaticGroupV3->GroupAddr),
|
|
pStaticGroupV3->Mode, pStaticGroupV3->FilterType,
|
|
pStaticGroupV3->NumSources
|
|
);
|
|
for (j=0; j<pStaticGroupV3->NumSources; j++) {
|
|
Trace1(CONFIG, " %d.%d.%d.%d",
|
|
PRINT_IPADDR(pStaticGroupV3->Sources[j]));
|
|
}
|
|
pStaticGroupV3 = (PSTATIC_GROUP_V3)
|
|
GET_NEXT_STATIC_GROUP_V3(pStaticGroupV3);
|
|
}
|
|
else {
|
|
Trace3(CONFIG, "%d. %15s Mode:%d",
|
|
i+1, INET_NTOA(pStaticGroup->GroupAddr),
|
|
pStaticGroup->Mode
|
|
);
|
|
pStaticGroup++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Trace0(CONFIG, "");
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// CopyinIfConfigAndUpdate
|
|
//
|
|
// Copies the if config struct passed by mib to igmp and update the timers
|
|
// and does static joins if req.
|
|
// Called when the interface is activated
|
|
//------------------------------------------------------------------------------
|
|
DWORD
|
|
CopyinIfConfigAndUpdate (
|
|
PIF_TABLE_ENTRY pite,
|
|
PIGMP_MIB_IF_CONFIG pConfigExt,
|
|
ULONG IfIndex
|
|
)
|
|
{
|
|
PIGMP_IF_CONFIG pConfig = &pite->Config;
|
|
BOOL bGroupMembershipTimer=FALSE, bLastMemQueryTimer=FALSE;
|
|
ULONG NewStartupQueryInterval, NewGenQueryInterval,
|
|
NewGenQueryMaxResponseTime, NewLastMemQueryInterval,
|
|
NewOtherQuerierPresentInterval, NewGroupMembershipTimeout;
|
|
BOOL bFound;
|
|
DWORD Error=NO_ERROR;
|
|
DWORD bVer3=IS_CONFIG_IGMP_V3(pConfigExt);
|
|
|
|
|
|
NewStartupQueryInterval
|
|
= CONFIG_TO_INTERNAL_TIME(pConfigExt->StartupQueryInterval);
|
|
NewGenQueryInterval
|
|
= CONFIG_TO_INTERNAL_TIME(pConfigExt->GenQueryInterval);
|
|
NewGenQueryMaxResponseTime
|
|
= CONFIG_TO_INTERNAL_TIME(pConfigExt->GenQueryMaxResponseTime);
|
|
NewOtherQuerierPresentInterval
|
|
= CONFIG_TO_INTERNAL_TIME(pConfigExt->OtherQuerierPresentInterval);
|
|
NewLastMemQueryInterval = pConfigExt->LastMemQueryInterval; //already in ms
|
|
NewGroupMembershipTimeout
|
|
= CONFIG_TO_INTERNAL_TIME(pConfigExt->GroupMembershipTimeout);
|
|
|
|
|
|
//
|
|
// used only in ver3
|
|
//
|
|
pConfig->RobustnessVariableOld = pConfigExt->RobustnessVariable;
|
|
pConfig->GenQueryIntervalOld = NewGenQueryInterval;
|
|
pConfig->OtherQuerierPresentIntervalOld
|
|
= NewOtherQuerierPresentInterval;
|
|
pConfig->GroupMembershipTimeoutOld = NewGroupMembershipTimeout;
|
|
|
|
|
|
|
|
//
|
|
// update values only if it is ver1,ver2 or ver3&&Querier
|
|
//
|
|
|
|
if (!IS_IF_VER3(pite) || (IS_IF_VER3(pite) && IS_QUERIER(pite)) ){
|
|
|
|
|
|
ACQUIRE_TIMER_LOCK("_CopyinIfConfigAndUpdate");
|
|
|
|
|
|
//
|
|
// change Info.StartupQueryCountCurrent if it was set to some very high value.
|
|
// During startup, Info.StartupQueryCountCurrent is used, and not the Config value.
|
|
//
|
|
if (pConfigExt->StartupQueryCount < pite->Info.StartupQueryCountCurrent)
|
|
InterlockedExchange(&pite->Info.StartupQueryCountCurrent,
|
|
pConfigExt->StartupQueryCount);
|
|
|
|
|
|
|
|
// in startup mode. StartupQueryInterval active and to be reduced
|
|
if (pite->Info.StartupQueryCountCurrent>0) {
|
|
|
|
if ( (NewStartupQueryInterval < pConfig->StartupQueryInterval)
|
|
&& (IS_TIMER_ACTIVE(pite->QueryTimer)) )
|
|
{
|
|
UpdateLocalTimer(&pite->QueryTimer, NewStartupQueryInterval, DBG_Y);
|
|
}
|
|
|
|
}
|
|
|
|
// in querier mode. GenQueryInterval is active and to be updated
|
|
else {
|
|
|
|
if ( (NewGenQueryInterval < pConfig->GenQueryInterval)
|
|
&& (IS_TIMER_ACTIVE(pite->QueryTimer)) )
|
|
{
|
|
UpdateLocalTimer(&pite->QueryTimer, NewGenQueryInterval, DBG_Y);
|
|
}
|
|
|
|
}
|
|
|
|
// OtherQuerierPresentInterval active and to be updated
|
|
|
|
if ( (NewOtherQuerierPresentInterval<pConfig->OtherQuerierPresentInterval)
|
|
&& (IS_TIMER_ACTIVE(pite->NonQueryTimer)) )
|
|
{
|
|
UpdateLocalTimer(&pite->NonQueryTimer, NewOtherQuerierPresentInterval, DBG_Y);
|
|
}
|
|
|
|
|
|
// NewLastMemQueryInterval is to be processed only if in ver-2 mode and not
|
|
// server
|
|
if ( (pConfigExt->IgmpProtocolType==IGMP_ROUTER_V2)
|
|
&& (pite->IfType!=IGMP_IF_RAS_SERVER) )
|
|
{
|
|
if (NewLastMemQueryInterval < pConfig->LastMemQueryInterval)
|
|
bLastMemQueryTimer = TRUE;
|
|
}
|
|
|
|
|
|
// check if GroupMembership timeout is reduced
|
|
if (NewGroupMembershipTimeout < pConfig->GroupMembershipTimeout)
|
|
bGroupMembershipTimer = TRUE;
|
|
|
|
|
|
|
|
//
|
|
// Go through the GI list for that interface (all ras clients) and update
|
|
// their timers if they are higher
|
|
//
|
|
if ( ((bLastMemQueryTimer||bGroupMembershipTimer)&&(!IS_RAS_SERVER_IF(pite->IfType)))
|
|
|| ((bGroupMembershipTimer)&&(IS_RAS_SERVER_IF(pite->IfType))) )
|
|
{
|
|
PLIST_ENTRY pHead, ple;
|
|
PGI_ENTRY pgie;
|
|
LONGLONG llNewLastMemQueryInterval, llNewGroupMembershipTimeout;
|
|
LONGLONG llMaxTime, llCurTime = GetCurrentIgmpTime();
|
|
|
|
|
|
//
|
|
// get the absolute timeout values
|
|
//
|
|
llNewLastMemQueryInterval = llCurTime
|
|
+ CONFIG_TO_SYSTEM_TIME(NewLastMemQueryInterval);
|
|
llNewGroupMembershipTimeout = llCurTime
|
|
+ CONFIG_TO_SYSTEM_TIME(NewGroupMembershipTimeout);
|
|
|
|
|
|
// if not ras interface, then go through the list from interface
|
|
if ( !IS_RAS_SERVER_IF(pite->IfType)) {
|
|
|
|
|
|
// merge the IfGroup lists
|
|
MergeIfGroupsLists(pite);
|
|
|
|
|
|
pHead = &pite->ListOfSameIfGroups;
|
|
|
|
|
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
|
|
|
pgie = CONTAINING_RECORD(ple, GI_ENTRY, LinkBySameIfGroups);
|
|
|
|
|
|
// update LastMemQueryTimer/V3SourcesQueryTimer if it is active and has a higher value
|
|
|
|
if (bLastMemQueryTimer && IS_TIMER_ACTIVE(pgie->LastMemQueryTimer)
|
|
&& (llNewLastMemQueryInterval<pgie->LastMemQueryTimer.Timeout))
|
|
{
|
|
UpdateLocalTimer(&pgie->LastMemQueryTimer,
|
|
NewLastMemQueryInterval, DBG_Y);
|
|
}
|
|
if (bLastMemQueryTimer)
|
|
pgie->LastMemQueryTimer.Timeout = llNewLastMemQueryInterval;
|
|
|
|
|
|
if (bLastMemQueryTimer && IS_TIMER_ACTIVE(pgie->V3SourcesQueryTimer)
|
|
&& (llNewLastMemQueryInterval<pgie->V3SourcesQueryTimer.Timeout))
|
|
{
|
|
UpdateLocalTimer(&pgie->V3SourcesQueryTimer,
|
|
NewLastMemQueryInterval, DBG_Y);
|
|
}
|
|
if (bLastMemQueryTimer)
|
|
pgie->V3SourcesQueryTimer.Timeout = llNewLastMemQueryInterval;
|
|
|
|
// update GroupMembershipTimeout if it is active and has a higher value
|
|
|
|
if (bGroupMembershipTimer
|
|
&& IS_TIMER_ACTIVE(pgie->GroupMembershipTimer)
|
|
&& (llNewGroupMembershipTimeout<pgie->GroupMembershipTimer.Timeout))
|
|
{
|
|
UpdateLocalTimer(&pgie->GroupMembershipTimer,
|
|
NewGroupMembershipTimeout, DBG_Y);
|
|
}
|
|
if (bGroupMembershipTimer)
|
|
{
|
|
pgie->GroupMembershipTimer.Timeout = llNewGroupMembershipTimeout;
|
|
pgie->LastVer1ReportTimer.Timeout = llNewGroupMembershipTimeout;
|
|
pgie->LastVer2ReportTimer.Timeout = llNewGroupMembershipTimeout;
|
|
}
|
|
|
|
// update LastVer1ReportTimer/LastVer2ReportTimer if it is active and has a higher value
|
|
// LastVer1ReportTimeout is set to GroupMembershipTimeout
|
|
|
|
if (bGroupMembershipTimer
|
|
&& IS_TIMER_ACTIVE(pgie->LastVer1ReportTimer)
|
|
&& (llNewGroupMembershipTimeout<pgie->LastVer1ReportTimer.Timeout))
|
|
{
|
|
UpdateLocalTimer(&pgie->LastVer1ReportTimer,
|
|
NewGroupMembershipTimeout, DBG_Y);
|
|
}
|
|
if (bGroupMembershipTimer
|
|
&& IS_TIMER_ACTIVE(pgie->LastVer2ReportTimer)
|
|
&& (llNewGroupMembershipTimeout<pgie->LastVer2ReportTimer.Timeout))
|
|
{
|
|
UpdateLocalTimer(&pgie->LastVer2ReportTimer,
|
|
NewGroupMembershipTimeout, DBG_Y);
|
|
}
|
|
}
|
|
}
|
|
|
|
// IS_RAS_SERVER_IF: process for all clients. have to process
|
|
// GroupMembershipTimeout only
|
|
else {
|
|
|
|
PLIST_ENTRY pHeadClient, pleClient;
|
|
PRAS_TABLE_ENTRY prte;
|
|
PRAS_TABLE prt = pite->pRasTable;
|
|
|
|
//
|
|
// process GI list of each ras client
|
|
//
|
|
pHeadClient = &pite->pRasTable->ListByAddr;
|
|
|
|
for (pleClient=pHeadClient->Flink; pleClient!=pHeadClient;
|
|
pleClient=pleClient->Flink)
|
|
{
|
|
prte = CONTAINING_RECORD(pleClient, RAS_TABLE_ENTRY, LinkByAddr);
|
|
|
|
|
|
pHead = &prte->ListOfSameClientGroups;
|
|
|
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
|
|
|
pgie = CONTAINING_RECORD(ple, GI_ENTRY, LinkBySameClientGroups);
|
|
|
|
if (IS_TIMER_ACTIVE(pgie->GroupMembershipTimer)
|
|
&& (llNewGroupMembershipTimeout
|
|
<pgie->GroupMembershipTimer.Timeout))
|
|
{
|
|
UpdateLocalTimer(&pgie->GroupMembershipTimer,
|
|
NewGroupMembershipTimeout, DBG_Y);
|
|
}
|
|
pgie->GroupMembershipTimer.Timeout = llNewGroupMembershipTimeout;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RELEASE_TIMER_LOCK("_CopyinIfConfigAndUpdate");
|
|
|
|
|
|
//
|
|
// finally copy the new values
|
|
//
|
|
CopyMemory(pConfig, pConfigExt, sizeof(IGMP_MIB_IF_CONFIG));
|
|
|
|
pConfig->StartupQueryInterval = NewStartupQueryInterval;
|
|
pConfig->GenQueryInterval = NewGenQueryInterval;
|
|
pConfig->GenQueryMaxResponseTime = NewGenQueryMaxResponseTime;
|
|
pConfig->LastMemQueryInterval = NewLastMemQueryInterval;
|
|
pConfig->OtherQuerierPresentInterval = NewOtherQuerierPresentInterval;
|
|
pConfig->GroupMembershipTimeout = NewGroupMembershipTimeout;
|
|
pConfig->IfIndex = IfIndex;
|
|
|
|
}
|
|
|
|
pConfig->NumStaticGroups = 0;
|
|
|
|
return Error;
|
|
#if 0
|
|
{
|
|
PSTATIC_GROUP_V3 pStaticGroupExt;
|
|
PIF_STATIC_GROUP pStaticGroup;
|
|
PLIST_ENTRY ple, pHead;
|
|
DWORD i, GroupAddr;
|
|
SOCKADDR_IN saLocalIf;
|
|
|
|
|
|
//
|
|
// delete all static groups which are different in the old config
|
|
//
|
|
|
|
pHead = &pite->Config.ListOfStaticGroups;
|
|
for (ple=pHead->Flink; ple!=pHead; ) {
|
|
|
|
pStaticGroup = CONTAINING_RECORD(ple, IF_STATIC_GROUP, Link);
|
|
GroupAddr = pStaticGroup->GroupAddr;
|
|
ple = ple->Flink;
|
|
|
|
bFound = FALSE;
|
|
pStaticGroupExt = GET_FIRST_STATIC_GROUP_V3(pConfigExt);
|
|
for (i=0; i<pConfigExt->NumStaticGroups; i++) {
|
|
|
|
if ( (GroupAddr == pStaticGroupExt->GroupAddr)
|
|
&& (pStaticGroup->Mode == pStaticGroupExt->Mode) )
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
pStaticGroupExt = GET_NEXT_STATIC_GROUP_V3(pStaticGroupExt);
|
|
}
|
|
|
|
|
|
//
|
|
// group exists in old and new config. check for changes in sources.
|
|
//
|
|
if (bFound && bVer3) {
|
|
|
|
if (pStaticGroupExt->NumSources==0) {
|
|
//delete all static sources
|
|
}
|
|
|
|
if (pStaticGroupExt->NumSources==0 &&
|
|
pStaticGroupExt->FilterType!=EXCLUSION) {
|
|
|
|
// delete the static group
|
|
|
|
}
|
|
|
|
// check for differences in sources
|
|
}
|
|
|
|
// if old static group not found in new list, delete it
|
|
|
|
|
|
// Router
|
|
|
|
if (IS_CONFIG_IGMPRTR(pConfig)) {
|
|
|
|
if (pStaticGroup->Mode==IGMP_HOST_JOIN) {
|
|
LeaveMulticastGroup(pite->StaticGroupSocket, GroupAddr,
|
|
pite->IfIndex, pite->IpAddr, 0 );
|
|
}
|
|
else {
|
|
PGROUP_TABLE_ENTRY pge;
|
|
PGI_ENTRY pgie;
|
|
|
|
ACQUIRE_GROUP_LOCK(GroupAddr, "_CopyinIfConfigAndUpdate");
|
|
|
|
pge = GetGroupFromGroupTable(GroupAddr, NULL, 0);
|
|
pgie = GetGIFromGIList(pge, pite, 0, STATIC_GROUP, NULL, 0);
|
|
|
|
pgie->bStaticGroup = 0;
|
|
|
|
if (bVer3) {
|
|
PLIST_ENTRY pHead, ple;
|
|
PGI_SOURCE_ENTRY pSourceEntry;
|
|
|
|
//
|
|
// delete all static source entries
|
|
//
|
|
|
|
pHead = &pgie->V3ExclusionList;
|
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
|
pSourceEntry = CONTAINING_RECORD(ple, GI_SOURCE_ENTRY,LinkSources);
|
|
// delete source entry (forward the packets)
|
|
if (pSourceEntry->bStaticSource) {
|
|
DeleteSourceEntry(pSourceEntry, MGM_YES);
|
|
}
|
|
}
|
|
pHead = &pgie->V3InclusionListSorted;
|
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
|
pSourceEntry = CONTAINING_RECORD(ple, GI_SOURCE_ENTRY,LinkSourcesInclListSorted);
|
|
if (pSourceEntry->bStaticSource) {
|
|
pSourceEntry->bStaticSource = FALSE;
|
|
if (!IS_TIMER_ACTIVE(pSourceEntry->SourceExpTimer))
|
|
DeleteSourceEntry(pSourceEntry, MGM_YES);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (!(pgie->GroupMembershipTimer.Status&TIMER_STATUS_ACTIVE))
|
|
DeleteGIEntry(pgie, TRUE, TRUE);
|
|
}
|
|
|
|
RELEASE_GROUP_LOCK(GroupAddr, "_CopyinIfConfigAndUpdate");
|
|
}
|
|
}
|
|
|
|
// Proxy Interface
|
|
else {
|
|
if (bVer3){
|
|
for (i=0; i<pStaticGroup->NumSources; i++){
|
|
ProcessProxyGroupChange(pStaticGroup->Sources[i],
|
|
GroupAddr, DELETE_FLAG, STATIC_GROUP);
|
|
}
|
|
}
|
|
if (!bVer3 || pStaticGroup->NumSources==0)
|
|
ProcessProxyGroupChange(0,GroupAddr, DELETE_FLAG, STATIC_GROUP);
|
|
}
|
|
|
|
RemoveEntryList(&pStaticGroup->Link);
|
|
IGMP_FREE(pStaticGroup);
|
|
}
|
|
|
|
|
|
//
|
|
// for all new static groups, if not in old list, create it
|
|
//
|
|
pStaticGroupExt = GET_FIRST_STATIC_GROUP_V3(pConfigExt);
|
|
for (i=0; i<pConfigExt->NumStaticGroups; i++,pStaticGroupExt++) {
|
|
|
|
pHead = &pite->Config.ListOfStaticGroups;
|
|
bFound = FALSE;
|
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
|
pStaticGroup = CONTAINING_RECORD(ple, IF_STATIC_GROUP, Link);
|
|
if (pStaticGroup->GroupAddr==pStaticGroupExt->GroupAddr) {
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// not found: create the new static group
|
|
if (!bFound) {
|
|
|
|
pStaticGroup = IGMP_ALLOC(
|
|
IGMP_MIB_STATIC_GROUP_SIZE(pConfigExt,
|
|
pStaticGroupExt), 0x100000,IfIndex);
|
|
|
|
PROCESS_ALLOC_FAILURE3(pStaticGroup,
|
|
"error %d allocating %d bytes for static group for IF:%0x",
|
|
Error, sizeof(IF_STATIC_GROUP), pite->IfIndex,
|
|
return Error);
|
|
|
|
memcpy(pStaticGroup, pStaticGroupExt,
|
|
IGMP_MIB_STATIC_GROUP_SIZE(pConfigExt, pStaticGroupExt));
|
|
|
|
|
|
InsertHeadList(&pConfig->ListOfStaticGroups, &pStaticGroup->Link);
|
|
|
|
if (IS_IF_ACTIVATED(pite)) {
|
|
|
|
// if proxy
|
|
if (IS_CONFIG_IGMPPROXY(pConfig)) {
|
|
if (pStaticGroup->NumSources==0)
|
|
ProcessProxyGroupChange(0, pStaticGroup->GroupAddr,
|
|
ADD_FLAG, STATIC_GROUP);
|
|
else {
|
|
for (i=0; i<pStaticGroup->NumSources; i++) {
|
|
ProcessProxyGroupChange(pStaticGroup->Sources[i],
|
|
pStaticGroup->GroupAddr,
|
|
ADD_FLAG, STATIC_GROUP);
|
|
}
|
|
}
|
|
}
|
|
// Add static group to Router
|
|
else {
|
|
if (pStaticGroup->Mode==IGMP_HOST_JOIN) {
|
|
|
|
if (!bVer3) {
|
|
JoinMulticastGroup(pite->StaticGroupSocket,
|
|
pStaticGroup->GroupAddr,
|
|
pite->IfIndex,
|
|
pite->IpAddr,
|
|
0
|
|
);
|
|
}
|
|
else {
|
|
// include filter
|
|
if (pStaticGroup->FilterType==INCLUSION) {
|
|
|
|
if (pStaticGroup->NumSources==0) {
|
|
JoinMulticastGroup(pite->StaticGroupSocket,
|
|
pStaticGroup->GroupAddr,
|
|
pite->IfIndex,
|
|
pite->IpAddr,
|
|
0
|
|
);
|
|
}
|
|
for (i=0; i<pStaticGroup->NumSources; i++) {
|
|
JoinMulticastGroup(pite->StaticGroupSocket,
|
|
pStaticGroup->GroupAddr,
|
|
pite->IfIndex,
|
|
pite->IpAddr,
|
|
pStaticGroup->Sources[i]
|
|
);
|
|
}
|
|
}
|
|
// exclude filter
|
|
else {
|
|
if (pStaticGroup->NumSources==0) {
|
|
JoinMulticastGroup(pite->StaticGroupSocket,
|
|
pStaticGroup->GroupAddr,
|
|
pite->IfIndex,
|
|
pite->IpAddr,
|
|
0
|
|
);
|
|
}
|
|
for (i=0; i<pStaticGroup->NumSources; i++) {
|
|
BlockSource(pite->StaticGroupSocket,
|
|
pStaticGroup->GroupAddr,
|
|
pite->IfIndex,
|
|
pite->IpAddr,
|
|
pStaticGroup->Sources[i]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// IGMPRTR_MGM_ONLY
|
|
|
|
else {
|
|
PGROUP_TABLE_ENTRY pge;
|
|
PGI_ENTRY pgie;
|
|
BOOL bCreate;
|
|
PGI_SOURCE_ENTRY pSourceEntry;
|
|
|
|
|
|
GroupAddr = pStaticGroup->GroupAddr;
|
|
|
|
ACQUIRE_GROUP_LOCK(GroupAddr,
|
|
"_CopyinIfConfigAndUpdate");
|
|
|
|
bCreate = TRUE;
|
|
pge = GetGroupFromGroupTable(GroupAddr, &bCreate, 0);
|
|
|
|
bCreate = TRUE;
|
|
pgie = GetGIFromGIList(pge, pite, 0,
|
|
(pStaticGroup->NumSources==0)
|
|
?STATIC_GROUP:NOT_STATIC_GROUP,
|
|
&bCreate, 0);
|
|
for (i=0; i<pStaticGroup->NumSources; i++) {
|
|
|
|
if (pStaticGroup->FilterType!=pgie->FilterType) {
|
|
|
|
pSourceEntry = GetSourceEntry(pgie,
|
|
pStaticGroup->Sources[i],
|
|
pStaticGroup->FilterType ^ 1,
|
|
NULL, 0, 0);
|
|
|
|
if (pSourceEntry) {
|
|
pSourceEntry->bStaticSource = TRUE;
|
|
ChangeSourceFilterMode(pgie,
|
|
pSourceEntry);
|
|
|
|
continue;
|
|
}
|
|
}
|
|
bCreate = TRUE;
|
|
pSourceEntry = GetSourceEntry(pgie,
|
|
pStaticGroup->Sources[i],
|
|
pStaticGroup->FilterType,
|
|
&bCreate, STATIC, MGM_YES);
|
|
}
|
|
RELEASE_GROUP_LOCK(GroupAddr,
|
|
"_CopyinIfConfigAndUpdate");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Error;
|
|
#endif
|
|
} //end _CopyinIfConfigAndUpdate
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _CopyinIfConfig
|
|
// Copies the if config struct passed by mib to igmp.
|
|
// called after the interface is in disabled state
|
|
//------------------------------------------------------------------------------
|
|
DWORD
|
|
CopyinIfConfig (
|
|
PIGMP_IF_CONFIG pConfig,
|
|
PIGMP_MIB_IF_CONFIG pConfigExt,
|
|
ULONG IfIndex
|
|
)
|
|
{
|
|
DWORD Error=NO_ERROR;
|
|
|
|
CopyMemory(pConfig, pConfigExt, sizeof(IGMP_MIB_IF_CONFIG));
|
|
CONV_CONFIG_TO_INTERNAL_TIME(pConfig->StartupQueryInterval);
|
|
CONV_CONFIG_TO_INTERNAL_TIME(pConfig->GenQueryInterval);
|
|
CONV_CONFIG_TO_INTERNAL_TIME(pConfig->GenQueryMaxResponseTime);
|
|
// already in ms
|
|
//CONV_CONFIG_TO_INTERNAL_TIME(pConfig->LastMemQueryInterval);
|
|
CONV_CONFIG_TO_INTERNAL_TIME(pConfig->OtherQuerierPresentInterval);
|
|
CONV_CONFIG_TO_INTERNAL_TIME(pConfig->GroupMembershipTimeout);
|
|
|
|
pConfig->RobustnessVariableOld = pConfig->RobustnessVariable;
|
|
pConfig->GenQueryIntervalOld = pConfig->GenQueryInterval;
|
|
pConfig->OtherQuerierPresentIntervalOld
|
|
= pConfig->OtherQuerierPresentInterval;
|
|
pConfig->GroupMembershipTimeoutOld = pConfig->GroupMembershipTimeout;
|
|
|
|
pConfig->ExtSize = IgmpMibIfConfigSize(pConfigExt);
|
|
|
|
pConfig->IfIndex = IfIndex ;
|
|
|
|
|
|
pConfig->NumStaticGroups = 0;
|
|
|
|
return Error;
|
|
#if 0
|
|
{
|
|
PIGMP_STATIC_GROUP pStaticGroupExt;
|
|
PSTATIC_GROUP_V3 pStaticGroupExtV3;
|
|
PIF_STATIC_GROUP pStaticGroup;
|
|
DWORD i;
|
|
PLIST_ENTRY ple;
|
|
BOOL bVersion3=IS_CONFIG_IGMP_V3(pConfigExt);
|
|
|
|
// delete all old static groups
|
|
|
|
for (ple=pConfig->ListOfStaticGroups.Flink;
|
|
ple!=&pConfig->ListOfStaticGroups; )
|
|
{
|
|
pStaticGroup = CONTAINING_RECORD(ple, IF_STATIC_GROUP, Link);
|
|
ple = ple->Flink;
|
|
IGMP_FREE(pStaticGroup);
|
|
}
|
|
|
|
|
|
// copy all static groups
|
|
|
|
InitializeListHead(&pConfig->ListOfStaticGroups);
|
|
if (bVersion3)
|
|
pStaticGroupExtV3 = GET_FIRST_STATIC_GROUP_V3(pConfigExt);
|
|
else
|
|
pStaticGroupExt = GET_FIRST_IGMP_STATIC_GROUP(pConfigExt);
|
|
|
|
for (i=0; i<pConfig->NumStaticGroups; i++) {
|
|
|
|
DWORD Size = IGMP_MIB_STATIC_GROUP_SIZE(pConfigExt, pStaticGroupExtV3);
|
|
|
|
pStaticGroup = IGMP_ALLOC(Size, 0x200000,IfIndex);
|
|
|
|
PROCESS_ALLOC_FAILURE2(pStaticGroup,
|
|
"error %d allocating %d bytes for static group for IF:%0x",
|
|
Error, Size,return Error);
|
|
|
|
if (!bVersion3) {
|
|
pStaticGroup->GroupAddr = pStaticGroupExt->GroupAddr;
|
|
pStaticGroup->Mode = pStaticGroupExt->Mode;
|
|
pStaticGroupExt++;
|
|
}
|
|
else {
|
|
memcpy(pStaticGroup, pStaticGroupExtV3, Size);
|
|
|
|
pStaticGroupExtV3 = (PSTATIC_GROUP_V3)
|
|
((PCHAR)pStaticGroupExtV3 + Size);
|
|
}
|
|
|
|
InsertHeadList(&pConfig->ListOfStaticGroups, &pStaticGroup->Link);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _CopyoutIfConfig
|
|
//------------------------------------------------------------------------------
|
|
VOID
|
|
CopyoutIfConfig (
|
|
PIGMP_MIB_IF_CONFIG pConfigMib,
|
|
PIF_TABLE_ENTRY pite
|
|
)
|
|
{
|
|
PIGMP_IF_CONFIG pConfig = &pite->Config;
|
|
BOOL bVersion3 = IS_CONFIG_IGMP_V3(pConfig);
|
|
|
|
//
|
|
// the initial IGMP_MIB_IF_CONFIG size of struct is common
|
|
//
|
|
CopyMemory(pConfigMib, pConfig, sizeof(IGMP_MIB_IF_CONFIG));
|
|
|
|
|
|
CONV_INTERNAL_TO_CONFIG_TIME(pConfigMib->StartupQueryInterval);
|
|
CONV_INTERNAL_TO_CONFIG_TIME(pConfigMib->GenQueryInterval);
|
|
CONV_INTERNAL_TO_CONFIG_TIME(pConfigMib->GenQueryMaxResponseTime);
|
|
// keep in ms
|
|
//CONV_INTERNAL_TO_CONFIG_TIME(pConfigMib->LastMemQueryInterval);
|
|
CONV_INTERNAL_TO_CONFIG_TIME(pConfigMib->OtherQuerierPresentInterval);
|
|
CONV_INTERNAL_TO_CONFIG_TIME(pConfigMib->GroupMembershipTimeout);
|
|
pConfigMib->IfIndex = pite->IfIndex;
|
|
pConfigMib->IpAddr = pite->IpAddr;
|
|
|
|
// have to convert the Iftype to external type
|
|
pConfigMib->IfType = GET_EXTERNAL_IF_TYPE(pite);
|
|
|
|
|
|
{
|
|
PLIST_ENTRY pHead, ple;
|
|
PIGMP_STATIC_GROUP pStaticGroupExt;
|
|
PSTATIC_GROUP_V3 pStaticGroupExtV3;
|
|
PIF_STATIC_GROUP pStaticGroup;
|
|
|
|
if (bVersion3)
|
|
pStaticGroupExtV3 = GET_FIRST_STATIC_GROUP_V3(pConfigMib);
|
|
else
|
|
pStaticGroupExt = GET_FIRST_IGMP_STATIC_GROUP(pConfigMib);
|
|
|
|
pHead = &pConfig->ListOfStaticGroups;
|
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
|
|
|
pStaticGroup = CONTAINING_RECORD(ple, IF_STATIC_GROUP, Link);
|
|
|
|
if (bVersion3) {
|
|
memcpy(pStaticGroupExtV3,
|
|
(PCHAR)pStaticGroup+FIELD_OFFSET(IF_STATIC_GROUP,GroupAddr),
|
|
sizeof(STATIC_GROUP_V3)+pStaticGroup->NumSources*sizeof(IPADDR)
|
|
);
|
|
pStaticGroupExtV3 = GET_NEXT_STATIC_GROUP_V3(pStaticGroupExtV3);
|
|
}
|
|
else {
|
|
pStaticGroupExt->GroupAddr = pStaticGroup->GroupAddr;
|
|
pStaticGroupExt->Mode = pStaticGroup->Mode;
|
|
pStaticGroupExt++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _ValidateIfConfig
|
|
//
|
|
// Corrects some values, and returns error for some others.
|
|
// Return: ERROR_INVALID_DATA, NO_ERROR
|
|
//------------------------------------------------------------------------------
|
|
DWORD
|
|
ValidateIfConfig (
|
|
PIGMP_MIB_IF_CONFIG pConfigExt,
|
|
DWORD IfIndex,
|
|
DWORD IfType,
|
|
ULONG ulStructureVersion,
|
|
ULONG ulStructureSize
|
|
)
|
|
{
|
|
DWORD Error = NO_ERROR, i, Size;
|
|
BOOL bVersion3;
|
|
|
|
|
|
//
|
|
// verify config size
|
|
//
|
|
/*kslksl
|
|
if (ulStructureSize<sizeof(IGMP_MIB_IF_CONFIG)) {
|
|
Trace2(ERR, "IGMP config size %d very small. Expected:%d", ulStructureSize,
|
|
sizeof(IGMP_MIB_IF_CONFIG));
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
*/
|
|
bVersion3 = IS_IGMP_VERSION_3(pConfigExt->Version);
|
|
|
|
{
|
|
if (!bVersion3) {
|
|
Size = sizeof(IGMP_MIB_IF_CONFIG)
|
|
+ pConfigExt->NumStaticGroups*sizeof(IGMP_STATIC_GROUP);
|
|
}
|
|
else {
|
|
PSTATIC_GROUP_V3 pStaticGroupV3 = GET_FIRST_STATIC_GROUP_V3(pConfigExt);
|
|
|
|
Size = sizeof(IGMP_MIB_IF_CONFIG)
|
|
+sizeof(STATIC_GROUP_V3)*pConfigExt->NumStaticGroups;
|
|
|
|
for (i=0; i<pConfigExt->NumStaticGroups; i++) {
|
|
Size += pStaticGroupV3->NumSources*sizeof(IPADDR);
|
|
if (ulStructureSize<Size)
|
|
break;
|
|
pStaticGroupV3 = GET_NEXT_STATIC_GROUP_V3(pStaticGroupV3);
|
|
}
|
|
}
|
|
|
|
/*kslksl
|
|
if (ulStructureSize!=Size) {
|
|
Trace0(ERR, "Invalid IGMP structure size");
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
// DebugPrintIfConfig
|
|
|
|
DebugPrintIfConfig(pConfigExt, IfIndex);
|
|
|
|
// check version
|
|
|
|
if (pConfigExt->Version >= IGMP_VERSION_3_5) {
|
|
|
|
Trace1(ERR, "Invalid version in interface config.\n"
|
|
"Create the Igmp configuration again", pConfigExt->Version);
|
|
IgmpAssertOnError(FALSE);
|
|
Logerr0(INVALID_VERSION, ERROR_INVALID_DATA);
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
|
|
//
|
|
// check the proxy/router common fields, and then check the router fields
|
|
//
|
|
|
|
|
|
//
|
|
// check the protocolType
|
|
//
|
|
switch (pConfigExt->IgmpProtocolType) {
|
|
case IGMP_ROUTER_V1 :
|
|
case IGMP_ROUTER_V2 :
|
|
{
|
|
if ( (pConfigExt->Version<IGMP_VERSION_1_2)
|
|
||(pConfigExt->Version>=IGMP_VERSION_1_2_5) )
|
|
{
|
|
Trace1(ERR, "IGMP v1/v2 should have version %0x", IGMP_VERSION_1_2);
|
|
IgmpAssertOnError(FALSE);
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
break;
|
|
}
|
|
case IGMP_ROUTER_V3:
|
|
{
|
|
if (pConfigExt->Version<IGMP_VERSION_3 || pConfigExt->Version>=IGMP_VERSION_3_5) {
|
|
Trace1(ERR, "IGMP v3 should have version %0x", IGMP_VERSION_3);
|
|
IgmpAssertOnError(FALSE);
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
break;
|
|
}
|
|
case IGMP_PROXY :
|
|
case IGMP_PROXY_V3 :
|
|
break;
|
|
|
|
// if none of above, then return error
|
|
default : {
|
|
Trace2(ERR,
|
|
"Error: IGMP protocol type(%d) for interface(%0x) invalid",
|
|
pConfigExt->IgmpProtocolType, IfIndex);
|
|
IgmpAssertOnError(FALSE);
|
|
Logerr2(INVALID_PROTOTYPE, "%d%d", pConfigExt->IgmpProtocolType,
|
|
IfIndex, ERROR_INVALID_DATA);
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
|
|
|
|
// cannot configure a proxy on a ras server interface
|
|
|
|
if (IS_RAS_SERVER_IF(IfType) && IS_CONFIG_IGMPPROXY(pConfigExt)) {
|
|
Trace1(ERR,
|
|
"Error: Cannot configure Proxy on RAS server interface:%0x",
|
|
IfIndex);
|
|
IgmpAssertOnError(FALSE);
|
|
Logerr1(PROXY_ON_RAS_SERVER, "%d",IfIndex, ERROR_INVALID_DATA);
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
//
|
|
// check for static joins
|
|
//
|
|
|
|
if (pConfigExt->NumStaticGroups>0) {
|
|
|
|
PIGMP_STATIC_GROUP pStaticGroup = GET_FIRST_IGMP_STATIC_GROUP(pConfigExt);
|
|
PSTATIC_GROUP_V3 pStaticGroupV3
|
|
= GET_FIRST_STATIC_GROUP_V3(pConfigExt);
|
|
|
|
for (i=0; i<pConfigExt->NumStaticGroups; i++) {
|
|
|
|
//
|
|
// make sure that the static group is a multicast address
|
|
//
|
|
if (!IS_MCAST_ADDR(pStaticGroup->GroupAddr)) {
|
|
Trace2(ERR,
|
|
"Error: Static group:%d.%d.%d.%d on IF:%0x not a multicast address",
|
|
PRINT_IPADDR(pStaticGroup->GroupAddr), IfIndex);
|
|
IgmpAssertOnError(FALSE);
|
|
Logerr2(INVALID_STATIC_GROUP, "%I%d", pStaticGroup->GroupAddr,
|
|
IfIndex, ERROR_INVALID_DATA);
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
|
|
//
|
|
// make sure that the mode of the static group is correct
|
|
//
|
|
|
|
if ( (pStaticGroup->Mode!=IGMP_HOST_JOIN
|
|
&& pStaticGroup->Mode!=IGMPRTR_JOIN_MGM_ONLY)
|
|
||(IS_CONFIG_IGMPPROXY(pConfigExt)
|
|
&& pStaticGroup->Mode!=IGMP_HOST_JOIN) )
|
|
{
|
|
Trace2(ERR,
|
|
"Error: Invalid mode for static group:%d.%d.%d.%d on IF:%0x",
|
|
PRINT_IPADDR(pStaticGroup->GroupAddr), IfIndex);
|
|
IgmpAssertOnError(FALSE);
|
|
Logerr2(INVALID_STATIC_MODE, "%I%d", pStaticGroup->GroupAddr,
|
|
IfIndex, ERROR_INVALID_DATA);
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
if (bVersion3) {
|
|
|
|
DWORD EntrySize = sizeof(STATIC_GROUP_V3)
|
|
+ pStaticGroupV3->NumSources*sizeof(IPADDR);
|
|
|
|
// check filter mode
|
|
|
|
if ( (pStaticGroupV3->FilterType!=INCLUSION)
|
|
&& (pStaticGroupV3->FilterType!=EXCLUSION))
|
|
{
|
|
Trace2(ERR,
|
|
"Error: Invalid filter type for static group:%d.%d.%d.%d on IF:%0x",
|
|
PRINT_IPADDR(pStaticGroup->GroupAddr), IfIndex);
|
|
IgmpAssertOnError(FALSE);
|
|
Logerr2(INVALID_STATIC_FILTER, "%I%d", pStaticGroup->GroupAddr,
|
|
IfIndex, ERROR_INVALID_DATA);
|
|
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
// not checking source addresses
|
|
|
|
pStaticGroupV3 = (PSTATIC_GROUP_V3)
|
|
((PCHAR)pStaticGroupV3 + EntrySize);
|
|
pStaticGroup = (PIGMP_STATIC_GROUP)pStaticGroupV3;
|
|
}
|
|
else {
|
|
pStaticGroup ++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// if it is a proxy interface, then none of the config variables other than
|
|
// static group is used. I return no_error
|
|
//
|
|
if (IS_CONFIG_IGMPPROXY(pConfigExt))
|
|
return NO_ERROR;
|
|
|
|
|
|
|
|
// robustness variable must be greater than 0
|
|
|
|
if (pConfigExt->RobustnessVariable<=0) {
|
|
Trace1(ERR, "Error RobustnessVariable for Interface(%0x) cannot be 0.",
|
|
IfIndex);
|
|
Logerr2(INVALID_ROBUSTNESS, "%d%d", pConfigExt->RobustnessVariable,
|
|
IfIndex, ERROR_INVALID_DATA);
|
|
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
// if robustness variable == 1, then log a warning
|
|
|
|
if (pConfigExt->RobustnessVariable==1) {
|
|
Trace1(ERR,
|
|
"Warning: Robustness variable for interface (%d) being set to 1",
|
|
IfIndex);
|
|
Logwarn0(ROBUSTNESS_VARIABLE_EQUAL_1, NO_ERROR);
|
|
}
|
|
|
|
|
|
// if robustness variable > 7, then I correct it to 7 and log a warning
|
|
|
|
if (pConfigExt->RobustnessVariable>7) {
|
|
Trace2(ERR, "RobustnessVariable for Interface(%0x) too high(%d)."
|
|
"Being set to 7", IfIndex, pConfigExt->RobustnessVariable);
|
|
Logwarn2(INVALID_ROBUSTNESS, "%d%d", pConfigExt->RobustnessVariable,
|
|
IfIndex, NO_ERROR);
|
|
|
|
pConfigExt->RobustnessVariable = 7;
|
|
}
|
|
|
|
|
|
|
|
// default value of GenQueryInterval is 125 sec. I force a minimum
|
|
// value of 10 secs to prevent trashing the network.
|
|
// max of 31744 as possible by exp value
|
|
|
|
if (pConfigExt->GenQueryInterval<10) {
|
|
Trace2(ERR, "GetQueryInterval for Interface(%0x) too low(%d)."
|
|
"Being set to 10", IfIndex, pConfigExt->GenQueryInterval);
|
|
|
|
pConfigExt->GenQueryInterval = 10;
|
|
}
|
|
|
|
if (pConfigExt->GenQueryInterval>31744) {
|
|
Trace2(ERR, "GetQueryInterval for Interface(%0x) too high(%d)."
|
|
"Being set to 31744", IfIndex, pConfigExt->GenQueryInterval);
|
|
|
|
pConfigExt->GenQueryInterval = 31744;
|
|
}
|
|
|
|
|
|
//
|
|
// StartupQueryInterval: default is 1/4 of GenQueryInterval
|
|
// I enforce a minimum of 1 sec and a max of GenQueryInterval
|
|
//
|
|
if (pConfigExt->StartupQueryInterval<1) {
|
|
Trace2(ERR, "StartupQueryInterval for Interface(%0x) too low(%d)."
|
|
"Being set to 1 sec", IfIndex, pConfigExt->StartupQueryInterval);
|
|
|
|
pConfigExt->StartupQueryInterval = 1;
|
|
}
|
|
|
|
if (pConfigExt->StartupQueryInterval>pConfigExt->GenQueryInterval) {
|
|
Trace3(ERR, "StartupQueryInterval(%d) for Interface(%0x) "
|
|
"higher than GenQueryInterval(%d). StartupQueryInterval set "
|
|
"to GenQueryInterval", pConfigExt->StartupQueryInterval, IfIndex,
|
|
pConfigExt->GenQueryInterval
|
|
);
|
|
|
|
pConfigExt->StartupQueryInterval = pConfigExt->GenQueryInterval;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// StartupQueryCount: default is Robustness variable
|
|
// I enforce a max of 7. (I am allowing someone to set it to 0??)
|
|
//
|
|
if (pConfigExt->StartupQueryCount>7) {
|
|
Trace2(ERR, "StartupQueryCount for IF(%0x) too high(%d). "
|
|
"Being set to 7.", IfIndex, pConfigExt->StartupQueryCount);
|
|
Logerr2(INVALID_STARTUPQUERYCOUNT, "%d%d",
|
|
pConfigExt->StartupQueryCount, IfIndex, ERROR_INVALID_DATA);
|
|
pConfigExt->StartupQueryCount = 7;
|
|
}
|
|
|
|
|
|
if ((int)pConfigExt->StartupQueryCount<0) {
|
|
Trace2(ERR,
|
|
"Error: StartupQueryCount(%d) for IF(%0x) cannot be < than 0.",
|
|
pConfigExt->StartupQueryCount, IfIndex);
|
|
Logerr2(INVALID_STARTUPQUERYCOUNT, "%d%d",
|
|
pConfigExt->StartupQueryCount, IfIndex, ERROR_INVALID_DATA);
|
|
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// GenQueryMaxResponseTime: default is 10.
|
|
// Absurd if value is greater than GenQueryInterval.
|
|
// I correct the values, if required
|
|
//
|
|
if (pConfigExt->GenQueryMaxResponseTime > pConfigExt->GenQueryInterval) {
|
|
Trace3(ERR, "GenQueryMaxResponseTime(%d) for IF(%0x) "
|
|
"higher than GenQueryInterval(%d). GenQueryMaxResponseTime "
|
|
"set to GenQueryInterval", pConfigExt->GenQueryMaxResponseTime,
|
|
IfIndex, pConfigExt->GenQueryInterval);
|
|
|
|
pConfigExt->GenQueryMaxResponseTime = pConfigExt->GenQueryInterval;
|
|
}
|
|
if (pConfigExt->GenQueryMaxResponseTime > 3174) {
|
|
Trace2(ERR, "GenQueryMaxResponseTime(%d) for IF(%0x) "
|
|
"higher than 3174 "
|
|
"set to 1sec", pConfigExt->GenQueryMaxResponseTime,
|
|
IfIndex);
|
|
|
|
pConfigExt->GenQueryMaxResponseTime = 1;
|
|
}
|
|
|
|
if (pConfigExt->GenQueryMaxResponseTime <= 0) {
|
|
Trace2(ERR, "Error. GenQueryMaxResponseTime(%d) for Interface(%0x) "
|
|
"should be greater than 0.", pConfigExt->GenQueryMaxResponseTime,
|
|
IfIndex);
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
|
|
//
|
|
// check LastMemQueryCount and LastMemQueryInterval only if
|
|
// protocol type is not IGMP-Router-ver1 and it is not a ras server interface
|
|
//
|
|
if ( (pConfigExt->IgmpProtocolType!=IGMP_ROUTER_V1) && (!IS_RAS_SERVER_IF(IfType)) ) {
|
|
|
|
// LastMemQueryCount can be 0
|
|
|
|
// set max LastMemQueryCount to 7
|
|
if (pConfigExt->LastMemQueryCount>7) {
|
|
Trace2(ERR, "Warning. LastMemQueryCount(%d) for IF(%0x) "
|
|
"is too high. Resetting it to 10.", pConfigExt->LastMemQueryCount,
|
|
IfIndex);
|
|
pConfigExt->LastMemQueryCount = 10;
|
|
}
|
|
|
|
|
|
// limit LastMemQueryInterval(in ms) to GroupMembershipTimeout(in sec)
|
|
if (pConfigExt->LastMemQueryInterval>pConfigExt->GroupMembershipTimeout*1000) {
|
|
Trace3(ERR,
|
|
"Warning. LastMemberQueryInterval(%d) for IF(%0x) "
|
|
"is too high. Resetting it to GroupMembershipTimeout(%d ms).",
|
|
pConfigExt->LastMemQueryCount, IfIndex,
|
|
pConfigExt->GroupMembershipTimeout*1000
|
|
);
|
|
pConfigExt->LastMemQueryInterval = pConfigExt->GroupMembershipTimeout*1000;
|
|
}
|
|
// limit LastMemQueryInterval(in ms) to 3174(in sec)
|
|
if (pConfigExt->LastMemQueryInterval>3174*1000) {
|
|
Trace2(ERR,
|
|
"Warning. LastMemberQueryInterval(%d) for IF(%0x) "
|
|
"is too high. Resetting it to 1000ms).",
|
|
pConfigExt->LastMemQueryCount, IfIndex
|
|
);
|
|
pConfigExt->LastMemQueryInterval = 1000;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// check the value of OtherQuerierPresentInterval
|
|
|
|
if (pConfigExt->OtherQuerierPresentInterval !=
|
|
pConfigExt->RobustnessVariable*pConfigExt->GenQueryInterval
|
|
+ (pConfigExt->GenQueryMaxResponseTime)/2
|
|
)
|
|
{
|
|
pConfigExt->OtherQuerierPresentInterval =
|
|
pConfigExt->RobustnessVariable*pConfigExt->GenQueryInterval
|
|
+ (pConfigExt->GenQueryMaxResponseTime)/2;
|
|
|
|
Trace0(ERR, "Warning: OtherQuerierPresentInterval's value should be "
|
|
"RobustnessVariable*GenQueryInterval + (GenQueryMaxResponseTime)/2");
|
|
}
|
|
|
|
|
|
// check the value of GroupMembershipTimeout
|
|
|
|
if (pConfigExt->GroupMembershipTimeout !=
|
|
(pConfigExt->RobustnessVariable*pConfigExt->GenQueryInterval
|
|
+ pConfigExt->GenQueryMaxResponseTime) )
|
|
{
|
|
pConfigExt->GroupMembershipTimeout =
|
|
pConfigExt->RobustnessVariable*pConfigExt->GenQueryInterval
|
|
+ pConfigExt->GenQueryMaxResponseTime;
|
|
|
|
Trace0(ERR, "Warning: GroupMembershipTimeout's value should be "
|
|
"RobustnessVariable*GenQueryInterval + GenQueryMaxResponseTime");
|
|
}
|
|
|
|
|
|
return Error;
|
|
|
|
} // _ValidateIfConfig
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// InitializeIfTable
|
|
// Creates the Interface table. The interface table size is dynamic
|
|
//------------------------------------------------------------------------------
|
|
DWORD
|
|
InitializeIfTable(
|
|
)
|
|
{
|
|
DWORD Error = NO_ERROR;
|
|
PIGMP_IF_TABLE pTable;
|
|
DWORD NumBuckets, i;
|
|
|
|
|
|
BEGIN_BREAKOUT_BLOCK1 {
|
|
|
|
// set the initial size of the interface table to IF_HASHTABLE_SZ1
|
|
|
|
NumBuckets = IF_HASHTABLE_SZ1;
|
|
|
|
|
|
|
|
//
|
|
// allocate memory for the interface table
|
|
//
|
|
g_pIfTable = IGMP_ALLOC(sizeof(IGMP_IF_TABLE), 0x400000,0);
|
|
|
|
PROCESS_ALLOC_FAILURE2(g_pIfTable,
|
|
"error %d allocating %d bytes for interface table",
|
|
Error, sizeof(IGMP_IF_TABLE),
|
|
GOTO_END_BLOCK1);
|
|
|
|
pTable = g_pIfTable;
|
|
|
|
|
|
// initialize NumBuckets and NumInterfaces
|
|
|
|
pTable->NumBuckets = NumBuckets;
|
|
pTable->NumInterfaces = 0;
|
|
|
|
|
|
//
|
|
// Initialize the IfTable lists
|
|
//
|
|
InitializeListHead(&pTable->ListByIndex);
|
|
|
|
InitializeListHead(&pTable->ListByAddr);
|
|
|
|
|
|
|
|
//
|
|
// Initialize the list CS and proxyAlertCS
|
|
//
|
|
try {
|
|
InitializeCriticalSection(&pTable->IfLists_CS);
|
|
InitializeCriticalSection(&g_ProxyAlertCS);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Error = GetExceptionCode();
|
|
Trace1(ANY,
|
|
"exception %d initializing critical section in InitIfTable",
|
|
Error);
|
|
Logerr0(INIT_CRITSEC_FAILED, Error);
|
|
|
|
GOTO_END_BLOCK1;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// allocate memory for the different buckets
|
|
//
|
|
pTable->HashTableByIndex = IGMP_ALLOC(sizeof(LIST_ENTRY)*NumBuckets,
|
|
0x800000,0);
|
|
|
|
PROCESS_ALLOC_FAILURE2(pTable->HashTableByIndex,
|
|
"error %d allocating %d bytes for interface table",
|
|
Error, sizeof(LIST_ENTRY)*NumBuckets,
|
|
GOTO_END_BLOCK1);
|
|
|
|
|
|
//
|
|
// allocate memory for the array of pointers to dynamic RWLs
|
|
//
|
|
pTable->aIfBucketDRWL
|
|
= IGMP_ALLOC(sizeof(PDYNAMIC_RW_LOCK)*NumBuckets, 0x800001,0);
|
|
|
|
PROCESS_ALLOC_FAILURE2(pTable->aIfBucketDRWL,
|
|
"error %d allocating %d bytes for interface table",
|
|
Error, sizeof(PDYNAMIC_RW_LOCK)*NumBuckets,
|
|
GOTO_END_BLOCK1);
|
|
|
|
|
|
|
|
//
|
|
// allocate memory for the array of pointers to dynamic CSs
|
|
//
|
|
pTable->aIfBucketDCS
|
|
= IGMP_ALLOC(sizeof(PDYNAMIC_CS_LOCK)*NumBuckets, 0x800002,0);
|
|
|
|
PROCESS_ALLOC_FAILURE2(pTable->aIfBucketDCS,
|
|
"error %d allocating %d bytes for interface table",
|
|
Error, sizeof(PDYNAMIC_CS_LOCK)*NumBuckets,
|
|
GOTO_END_BLOCK1);
|
|
|
|
|
|
//
|
|
// init locks to NULL, implying that the dynamic locks have not been
|
|
// allocated. and initialize the list heads.
|
|
//
|
|
for (i=0; i<NumBuckets; i++) {
|
|
|
|
InitializeListHead(&pTable->HashTableByIndex[i]);
|
|
|
|
pTable->aIfBucketDRWL[i] = NULL;
|
|
|
|
pTable->aIfBucketDCS[i] = NULL;
|
|
}
|
|
|
|
|
|
pTable->Status = 0;
|
|
|
|
} END_BREAKOUT_BLOCK1;
|
|
|
|
return Error;
|
|
|
|
} //end _InitializeIfTable
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _DeInitializeIfTable
|
|
//------------------------------------------------------------------------------
|
|
VOID
|
|
DeInitializeIfTable(
|
|
)
|
|
{
|
|
PIGMP_IF_TABLE pTable = g_pIfTable;
|
|
PLIST_ENTRY pHead, ple;
|
|
PIF_TABLE_ENTRY pite;
|
|
DWORD i, dwRetval;
|
|
|
|
|
|
if (pTable==NULL)
|
|
return;
|
|
|
|
|
|
//
|
|
// for each active interface call deregister MGM.
|
|
//
|
|
|
|
// go through the list of active interfaces ordered by IpAddr
|
|
|
|
pHead = &g_pIfTable->ListByAddr;
|
|
|
|
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
|
|
|
|
pite = CONTAINING_RECORD(ple, IF_TABLE_ENTRY, LinkByAddr);
|
|
|
|
|
|
// if not activated then continue
|
|
|
|
if (!IS_IF_ACTIVATED(pite))
|
|
continue;
|
|
|
|
|
|
// deregister all interfaces, ras clients and proxy protocol from mgm
|
|
DeActivationDeregisterFromMgm(pite);
|
|
}
|
|
|
|
|
|
|
|
// delete the IfLists CS
|
|
DeleteCriticalSection(&pTable->IfLists_CS);
|
|
|
|
IGMP_FREE_NOT_NULL(pTable->aIfBucketDCS);
|
|
IGMP_FREE_NOT_NULL(pTable->aIfBucketDRWL);
|
|
IGMP_FREE_NOT_NULL(pTable->HashTableByIndex);
|
|
IGMP_FREE_NOT_NULL(g_pIfTable);
|
|
|
|
// I dont delete the different dynamic locks. They should have been deleted
|
|
// by now
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _InitializeGroupTable //
|
|
//------------------------------------------------------------------------------
|
|
DWORD
|
|
InitializeGroupTable (
|
|
)
|
|
{
|
|
BOOL bErr = TRUE;
|
|
DWORD Error = NO_ERROR;
|
|
PGROUP_TABLE pGroupTable;
|
|
DWORD i;
|
|
|
|
|
|
BEGIN_BREAKOUT_BLOCK1 {
|
|
|
|
//
|
|
// allocate space for the group table
|
|
//
|
|
|
|
g_pGroupTable = IGMP_ALLOC(sizeof(GROUP_TABLE), 0x800004,0);
|
|
|
|
PROCESS_ALLOC_FAILURE2(g_pGroupTable,
|
|
"error %d allocating %d bytes for Group table",
|
|
Error, sizeof(GROUP_TABLE),
|
|
GOTO_END_BLOCK1);
|
|
|
|
|
|
pGroupTable = g_pGroupTable;
|
|
|
|
|
|
//
|
|
// initialize group tables' dynamically locked lists
|
|
//
|
|
|
|
for (i=0; i<GROUP_HASH_TABLE_SZ; i++) {
|
|
InitDynamicCSLockedList(&pGroupTable->HashTableByGroup[i]);
|
|
}
|
|
|
|
|
|
//
|
|
// initialize list of all groups
|
|
//
|
|
try {
|
|
CREATE_LOCKED_LIST(&pGroupTable->ListByGroup);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Error = GetExceptionCode();
|
|
Trace1(ERR, "Exception %d creating locked list for Group Table",
|
|
Error);
|
|
Logerr0(INIT_CRITSEC_FAILED, Error);
|
|
GOTO_END_BLOCK1;
|
|
}
|
|
|
|
//
|
|
// initialize the list of new groups
|
|
//
|
|
InitializeListHead(&pGroupTable->ListByGroupNew);
|
|
pGroupTable->NumGroupsInNewList = 0;
|
|
|
|
|
|
pGroupTable->Status = 0;
|
|
|
|
bErr = FALSE;
|
|
|
|
} END_BREAKOUT_BLOCK1;
|
|
|
|
if (!bErr) {
|
|
return Error==NO_ERROR ? ERROR_CAN_NOT_COMPLETE: Error;
|
|
}
|
|
else {
|
|
return Error;
|
|
}
|
|
|
|
} //end _InitializeGroupTable
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// DeInitializeGroupTable //
|
|
// Just delete the critical sections //
|
|
//------------------------------------------------------------------------------
|
|
VOID
|
|
DeInitializeGroupTable (
|
|
)
|
|
{
|
|
PGROUP_TABLE pGroupTable = g_pGroupTable;
|
|
DWORD i;
|
|
|
|
|
|
if (pGroupTable==NULL)
|
|
return;
|
|
|
|
//
|
|
// I dont try to delete the dynamically allocated locks as they should
|
|
// have all been deleted by the last thread executing in that lock
|
|
//
|
|
|
|
|
|
DeleteCriticalSection(&pGroupTable->ListByGroup.Lock);
|
|
IGMP_FREE_NOT_NULL(pGroupTable);
|
|
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _InitializeRasTable
|
|
// creates ras table and initializes the fields.
|
|
// called by _DeActivateInterfaceInitial() _AddIfEntry()
|
|
// The interface table is created during _AddIfEntry, as _ConnectRasClients can
|
|
// be called even when the ras server interface is not activated
|
|
//------------------------------------------------------------------------------
|
|
DWORD
|
|
InitializeRasTable(
|
|
DWORD IfIndex,
|
|
PIF_TABLE_ENTRY pite
|
|
)
|
|
{
|
|
DWORD Error = NO_ERROR, i;
|
|
PRAS_TABLE prt;
|
|
|
|
|
|
//
|
|
// allocate Ras table
|
|
//
|
|
prt = IGMP_ALLOC(sizeof(RAS_TABLE), 0x800008,IfIndex);
|
|
|
|
PROCESS_ALLOC_FAILURE2(prt, "error %d allocating %d bytes for Ras Table",
|
|
Error, sizeof(RAS_TABLE),
|
|
return Error);
|
|
|
|
|
|
//set the ras table entry in pite
|
|
pite->pRasTable = prt;
|
|
|
|
|
|
// initialize list pointing to Ras Clients ordered by IpAddr
|
|
InitializeListHead(&prt->ListByAddr);
|
|
|
|
|
|
// initialize hash table containing lists pointing to Ras Clients
|
|
// hashed on IpAddr
|
|
|
|
for (i=0; i<RAS_HASH_TABLE_SZ; i++)
|
|
InitializeListHead(&prt->HashTableByAddr[i]);
|
|
|
|
|
|
// set backpointer to the interface table entry
|
|
prt->pIfTable = pite;
|
|
|
|
|
|
// set RefCount and Status
|
|
prt->RefCount = 1;
|
|
prt->Status = IF_CREATED_FLAG;
|
|
|
|
|
|
return NO_ERROR;
|
|
|
|
} //end _InitializeRasTable
|
|
|
|
|
|
//todo:remove
|
|
//------------------------------------------------------------------------------
|
|
// DeInitializeRasTable
|
|
//------------------------------------------------------------------------------
|
|
VOID
|
|
DeInitializeRasTable (
|
|
PIF_TABLE_ENTRY pite,
|
|
BOOL bFullCleanup
|
|
)
|
|
{
|
|
|
|
PRAS_TABLE prt = pite->pRasTable;
|
|
PRAS_TABLE_ENTRY prte;
|
|
PLIST_ENTRY pHeadRas, pleRas;
|
|
|
|
pHeadRas = &prt->ListByAddr;
|
|
for (pleRas=pHeadRas->Flink; pleRas!=pHeadRas; pleRas=pleRas->Flink) {
|
|
prte = CONTAINING_RECORD(pleRas, RAS_TABLE_ENTRY, LinkByAddr);
|
|
|
|
if (prte->CreationFlags & TAKEN_INTERFACE_OWNERSHIP_WITH_MGM)
|
|
{
|
|
MgmReleaseInterfaceOwnership(g_MgmIgmprtrHandle, pite->IfIndex,
|
|
prte->NHAddr);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _MergeIfGroupsLists
|
|
//
|
|
// Merges the new GI list with the main GI list.
|
|
// Locks: Assumes the IF-GI list to be locked.
|
|
//------------------------------------------------------------------------------
|
|
VOID
|
|
MergeIfGroupsLists(
|
|
PIF_TABLE_ENTRY pite
|
|
)
|
|
{
|
|
// sentinel is set at the end of the Main list so that all entries is inserted
|
|
// before it. its group value is set to all 1's.
|
|
GROUP_TABLE_ENTRY pgeSentinel;
|
|
GI_ENTRY giSentinel;
|
|
PGI_ENTRY giNew, giMain;
|
|
PLIST_ENTRY pHeadNew, pHeadMain, pleMain, pleNew;
|
|
|
|
Trace1(ENTER1, "Entering _MergeIfGroupLists(): IfIndex:%0x", pite->IfIndex);
|
|
|
|
pHeadNew = &pite->ListOfSameIfGroupsNew;
|
|
pHeadMain = &pite->ListOfSameIfGroups;
|
|
|
|
|
|
//
|
|
// if main list is empty, then just move the new list to main list
|
|
// and I am done
|
|
//
|
|
if (IsListEmpty(pHeadMain)) {
|
|
|
|
// insert pHeadMain into new list
|
|
InsertHeadList(pHeadNew, pHeadMain);
|
|
|
|
// remove new list header
|
|
RemoveEntryList(pHeadNew);
|
|
|
|
InitializeListHead(pHeadNew);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// insert the sentinel at the end of the main list
|
|
//
|
|
pgeSentinel.GroupLittleEndian = ~0;
|
|
giSentinel.pGroupTableEntry = &pgeSentinel;
|
|
InsertTailList(pHeadMain, &giSentinel.LinkBySameIfGroups);
|
|
|
|
|
|
|
|
pleMain = pHeadMain->Flink;
|
|
giMain = CONTAINING_RECORD(pleMain, GI_ENTRY, LinkBySameIfGroups);
|
|
|
|
|
|
|
|
// merge the lists by inserting the entries from new list into main list.
|
|
|
|
for (pleNew=pHeadNew->Flink; pleNew!=pHeadNew; ) {
|
|
|
|
giNew = CONTAINING_RECORD(pleNew, GI_ENTRY, LinkBySameIfGroups);
|
|
pleNew=pleNew->Flink;
|
|
|
|
|
|
while (giNew->pGroupTableEntry->GroupLittleEndian >
|
|
giMain->pGroupTableEntry->GroupLittleEndian)
|
|
{
|
|
pleMain = pleMain->Flink;
|
|
|
|
giMain = CONTAINING_RECORD(pleMain, GI_ENTRY, LinkBySameIfGroups);
|
|
}
|
|
|
|
InsertTailList(pleMain, &giNew->LinkBySameIfGroups);
|
|
}
|
|
|
|
//
|
|
// reinitialize the New list
|
|
//
|
|
pite->NumGIEntriesInNewList = 0;
|
|
InitializeListHead(&pite->ListOfSameIfGroupsNew);
|
|
|
|
|
|
// remove the sentinel entry from the main list
|
|
|
|
RemoveEntryList(&giSentinel.LinkBySameIfGroups);
|
|
|
|
//DebugPrintIfGroups(pite, 0); //deldel
|
|
|
|
Trace0(LEAVE1, "Leaving _MergeIfGroupsLists");
|
|
return;
|
|
|
|
} //end _MergeIfGroupsLists
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _MergeProxyLists
|
|
//
|
|
// Merges the new GI list with the main GI list.
|
|
// Locks: Assumes the IF-GI list to be locked.
|
|
//------------------------------------------------------------------------------
|
|
|
|
VOID
|
|
MergeProxyLists(
|
|
PIF_TABLE_ENTRY pite
|
|
)
|
|
{
|
|
// sentinel is set at the end of the Main list so that all entries is inserted
|
|
// before it. its group value is set to all 1's.
|
|
PROXY_GROUP_ENTRY ProxySentinel, *pProxyNew, *pProxyMain;
|
|
PLIST_ENTRY pHeadNew, pHeadMain, pleMain, pleNew;
|
|
|
|
Trace1(ENTER1, "Entering MergeProxyLists(): IfIndex:%0x", pite->IfIndex);
|
|
|
|
pHeadNew = &pite->ListOfSameIfGroupsNew;
|
|
pHeadMain = &pite->ListOfSameIfGroups;
|
|
|
|
|
|
//
|
|
// if main list is empty, then just move the new list to main list
|
|
// and I am done
|
|
//
|
|
if (IsListEmpty(pHeadMain)) {
|
|
|
|
CONCATENATE_LISTS(pite->ListOfSameIfGroups, pite->ListOfSameIfGroupsNew);
|
|
|
|
pite->NumGIEntriesInNewList = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// insert the sentinel at the end of the main list
|
|
//
|
|
ProxySentinel.GroupLittleEndian = ~0;
|
|
InsertTailList(pHeadMain, &ProxySentinel.LinkBySameIfGroups);
|
|
|
|
|
|
|
|
pleMain = pHeadMain->Flink;
|
|
pProxyMain = CONTAINING_RECORD(pleMain, PROXY_GROUP_ENTRY,
|
|
LinkBySameIfGroups);
|
|
|
|
|
|
|
|
// merge the lists by inserting the entries from new list into main list.
|
|
|
|
for (pleNew=pHeadNew->Flink; pleNew!=pHeadNew; ) {
|
|
|
|
pProxyNew = CONTAINING_RECORD(pleNew, PROXY_GROUP_ENTRY,
|
|
LinkBySameIfGroups);
|
|
pleNew=pleNew->Flink;
|
|
|
|
|
|
while (pProxyNew->GroupLittleEndian > pProxyMain->GroupLittleEndian)
|
|
{
|
|
pleMain = pleMain->Flink;
|
|
|
|
pProxyMain = CONTAINING_RECORD(pleMain, PROXY_GROUP_ENTRY,
|
|
LinkBySameIfGroups);
|
|
}
|
|
|
|
InsertTailList(pleMain, &pProxyNew->LinkBySameIfGroups);
|
|
}
|
|
|
|
//
|
|
// reinitialize the New list
|
|
//
|
|
pite->NumGIEntriesInNewList = 0;
|
|
InitializeListHead(&pite->ListOfSameIfGroupsNew);
|
|
|
|
|
|
// remove the sentinel entry from the main list
|
|
|
|
RemoveEntryList(&ProxySentinel.LinkBySameIfGroups);
|
|
|
|
Trace0(LEAVE1, "Leaving _MergeProxyLists");
|
|
return;
|
|
|
|
} //end _MergeProxyLists
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// _MergeGroupLists
|
|
//
|
|
// Merges the new group list with the main group list.
|
|
//
|
|
// Locks: Assumes the group list to be locked.
|
|
// Called by: MibGetInternalGroupIfsInfo() or InsertInGroupsList()
|
|
//------------------------------------------------------------------------------
|
|
VOID
|
|
MergeGroupLists(
|
|
)
|
|
{
|
|
// sentinel is set at the end of the Main list so that all entries is inserted
|
|
// before it. its group value is set to all 1's.
|
|
GROUP_TABLE_ENTRY pgeSentinel;
|
|
PGROUP_TABLE_ENTRY pgeNew, pgeMain;
|
|
PLIST_ENTRY pHeadNew, pHeadMain, pleMain, pleNew;
|
|
|
|
Trace0(ENTER1, "Entering _MergeGroupLists()");
|
|
|
|
#if DBG
|
|
DebugPrintGroupsList(1);
|
|
#endif
|
|
|
|
pHeadNew = &g_pGroupTable->ListByGroupNew;
|
|
pHeadMain = &g_pGroupTable->ListByGroup.Link;
|
|
|
|
|
|
//
|
|
// if main list is empty, then just move the new list to main list
|
|
// and I am done
|
|
//
|
|
if (IsListEmpty(pHeadMain)) {
|
|
|
|
// insert pHeadMain into new list
|
|
InsertHeadList(pHeadNew, pHeadMain);
|
|
|
|
// remove new list header
|
|
RemoveEntryList(pHeadNew);
|
|
|
|
InitializeListHead(pHeadNew);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// insert the sentinel at the end of the main list
|
|
//
|
|
pgeSentinel.GroupLittleEndian = ~0;
|
|
InsertTailList(pHeadMain, &pgeSentinel.LinkByGroup);
|
|
|
|
pleMain = pHeadMain->Flink;
|
|
pgeMain = CONTAINING_RECORD(pleMain, GROUP_TABLE_ENTRY, LinkByGroup);
|
|
|
|
|
|
// merge the lists by inserting the entries from new list into main list.
|
|
|
|
for (pleNew=pHeadNew->Flink; pleNew!=pHeadNew; ) {
|
|
|
|
pgeNew = CONTAINING_RECORD(pleNew, GROUP_TABLE_ENTRY, LinkByGroup);
|
|
pleNew=pleNew->Flink;
|
|
|
|
|
|
while (pgeNew->GroupLittleEndian > pgeMain->GroupLittleEndian) {
|
|
|
|
pleMain = pleMain->Flink;
|
|
|
|
pgeMain = CONTAINING_RECORD(pleMain, GROUP_TABLE_ENTRY,
|
|
LinkByGroup);
|
|
}
|
|
|
|
InsertTailList(pleMain, &pgeNew->LinkByGroup);
|
|
}
|
|
|
|
//
|
|
// reinitialize the New list
|
|
//
|
|
g_pGroupTable->NumGroupsInNewList = 0;
|
|
InitializeListHead(&g_pGroupTable->ListByGroupNew);
|
|
|
|
|
|
// remove the sentinel entry from the main list
|
|
|
|
RemoveEntryList(&pgeSentinel.LinkByGroup);
|
|
|
|
|
|
return;
|
|
|
|
} //end _MergeGroupLists
|
|
|
|
|