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

  1. //=============================================================================
  2. // Copyright (c) 1997 Microsoft Corporation
  3. // File: work.c
  4. //
  5. // Abstract:
  6. // Implements the work items that are queued by igmp routines.
  7. //
  8. // Author: K.S.Lokesh (lokeshs@) 11-1-97
  9. //
  10. // Revision History:
  11. //=============================================================================
  12. #include "pchigmp.h"
  13. #pragma hdrstop
  14. //
  15. // should each packet be queued to another work item again
  16. //
  17. #define BQUEUE_WORK_ITEM_FOR_PACKET 1
  18. //------------------------------------------------------------------------------
  19. // _WT_ProcessInputEvent
  20. // called in the wait worker thread when the packet event is set.
  21. // Queues: _WF_ProcessInputEvent()
  22. // Runs in: WaitServerThread context
  23. //------------------------------------------------------------------------------
  24. VOID
  25. WT_ProcessInputEvent(
  26. PVOID pContext, // psee entry. the entry might have been deleted.
  27. BOOLEAN NotUsed
  28. )
  29. {
  30. HANDLE WaitHandle ;
  31. //
  32. // set the InputWaitEvent to NULL so that UnregisterWaitEx is not called.
  33. // psee will be valid here, but might not be once queued to the worker Fn.
  34. //
  35. PSOCKET_EVENT_ENTRY psee = (PSOCKET_EVENT_ENTRY) pContext;
  36. if (!EnterIgmpApi())
  37. return;
  38. Trace0(WORKER, "_WF_ProcessInputEvent queued by WaitThread");
  39. // make a non-blocking UnregisterWaitEx call
  40. WaitHandle = InterlockedExchangePointer(&psee->InputWaitEvent, NULL);
  41. if (WaitHandle)
  42. UnregisterWaitEx( WaitHandle, NULL ) ;
  43. QueueIgmpWorker(WF_ProcessInputEvent, pContext);
  44. LeaveIgmpApi();
  45. return;
  46. }
  47. //------------------------------------------------------------------------------
  48. // _WF_ProcessInputEvent
  49. // Called by: _WT_ProcessInputEvent()
  50. // Locks:
  51. // Acquire socketsLockShared. Either queue processing the packet to
  52. // _WF_ProcessPacket() or take shared interface lock and process the packet.
  53. //------------------------------------------------------------------------------
  54. VOID
  55. WF_ProcessInputEvent (
  56. PVOID pContext
  57. )
  58. {
  59. DWORD Error = NO_ERROR;
  60. PIF_TABLE_ENTRY pite;
  61. PLIST_ENTRY ple, pHead;
  62. WSANETWORKEVENTS wsane;
  63. PSOCKET_EVENT_ENTRY psee = (PSOCKET_EVENT_ENTRY) pContext,
  64. pseeTmp;
  65. PSOCKET_ENTRY pse;
  66. if (!EnterIgmpWorker()) return;
  67. Trace0(ENTER1, "Entering _WF_ProcessInputEvent");
  68. ACQUIRE_SOCKETS_LOCK_SHARED("_WF_ProcessInputEvent");
  69. //
  70. // make sure that the psee entry still exists
  71. //
  72. pHead = &g_ListOfSocketEvents;
  73. for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
  74. pseeTmp = CONTAINING_RECORD(ple, SOCKET_EVENT_ENTRY, LinkBySocketEvents);
  75. if (pseeTmp==psee)
  76. break;
  77. }
  78. if (ple==pHead) {
  79. RELEASE_SOCKETS_LOCK_SHARED("_WF_ProcessInputEvent");
  80. Trace0(ERR, "Input Event received on deleted SocketEvent. not an error");
  81. LeaveIgmpWorker();
  82. return;
  83. }
  84. //
  85. // go through the list of active interfaces
  86. // processing sockets which have input packets
  87. //
  88. pHead = &psee->ListOfInterfaces;
  89. for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
  90. pse = CONTAINING_RECORD(ple, SOCKET_ENTRY, LinkByInterfaces);
  91. pite = CONTAINING_RECORD(pse, IF_TABLE_ENTRY, SocketEntry);
  92. //
  93. // process only activated interfaces. (Proxy wont be on this list)
  94. //
  95. if (!IS_IF_ACTIVATED(pite))
  96. continue;
  97. //
  98. // process input event
  99. //
  100. BEGIN_BREAKOUT_BLOCK1 {
  101. if (pse->Socket == INVALID_SOCKET)
  102. GOTO_END_BLOCK1;
  103. //
  104. // enumerate network events to see whether
  105. // any packets have arrived on this interface
  106. //
  107. Error = WSAEnumNetworkEvents(pse->Socket, NULL, &wsane);
  108. if (Error != NO_ERROR) {
  109. Trace3(RECEIVE,
  110. "error %d checking for input on interface %d (%d.%d.%d.%d)",
  111. Error, pite->IfIndex, PRINT_IPADDR(pite->IpAddr));
  112. Logwarn1(ENUM_NETWORK_EVENTS_FAILED, "%I", pite->IpAddr, Error);
  113. GOTO_END_BLOCK1;
  114. }
  115. if (!(wsane.lNetworkEvents & FD_READ))
  116. GOTO_END_BLOCK1;
  117. //
  118. // the input flag is set, now see if there was an error
  119. //
  120. if (wsane.iErrorCode[FD_READ_BIT] != NO_ERROR) {
  121. Trace3(RECEIVE,
  122. "error %d in input record for interface %d (%d.%d.%d.%d)",
  123. wsane.iErrorCode[FD_READ_BIT], pite->IfIndex,
  124. PRINT_IPADDR(pite->IpAddr)
  125. );
  126. Logwarn1(INPUT_RECORD_ERROR, "%I", pite->IpAddr, Error);
  127. GOTO_END_BLOCK1;
  128. }
  129. //
  130. // Process the packet received on the interface
  131. //
  132. ProcessInputOnInterface(pite);
  133. } END_BREAKOUT_BLOCK1;
  134. } //for loop: for each interface
  135. //
  136. // register the event with the wait thread for future receives
  137. //
  138. if (g_RunningStatus!=IGMP_STATUS_STOPPING) {
  139. DWORD dwRetval;
  140. if (! RegisterWaitForSingleObject(
  141. &psee->InputWaitEvent,
  142. psee->InputEvent,
  143. WT_ProcessInputEvent,
  144. (VOID*)psee,
  145. INFINITE,
  146. (WT_EXECUTEINWAITTHREAD)|(WT_EXECUTEONLYONCE)
  147. ))
  148. {
  149. dwRetval = GetLastError();
  150. Trace1(ERR, "error %d RtlRegisterWait", dwRetval);
  151. IgmpAssertOnError(FALSE);
  152. }
  153. }
  154. RELEASE_SOCKETS_LOCK_SHARED("_WF_ProcessInputEvent");
  155. LeaveIgmpWorker();
  156. Trace0(LEAVE1, "leaving _WF_ProcessInputEvent()\n");
  157. Trace0(LEAVE, ""); //putting a newline
  158. return;
  159. } //end _WF_ProcessInputEvent
  160. //------------------------------------------------------------------------------
  161. // _ProcessInputOnInterface
  162. // Does some minimal checking of packet length, etc. We can either queue to
  163. // work item(_WF_ProcessPacket) or run it here itself.
  164. //
  165. // Called by: _WF_ProcessInputEvent()
  166. // Locks: Assumes socket lock. Either queues the packet to _WF_ProcessPacket or
  167. // takes shared interface lock and processes it here itself.
  168. //------------------------------------------------------------------------------
  169. VOID
  170. ProcessInputOnInterface(
  171. PIF_TABLE_ENTRY pite
  172. )
  173. {
  174. WSABUF WsaBuf;
  175. DWORD dwNumBytes, dwFlags, dwAddrLen;
  176. SOCKADDR_IN saSrcAddr;
  177. DWORD dwSrcAddr, DstnMcastAddr;
  178. DWORD Error = NO_ERROR;
  179. UCHAR *pPacket;
  180. UCHAR IpHdrLen;
  181. PIP_HEADER pIpHdr;
  182. BOOL bRtrAlertSet = FALSE;
  183. PBYTE Buffer;
  184. WsaBuf.len = pite->Info.PacketSize;
  185. WsaBuf.buf = IGMP_ALLOC(WsaBuf.len, 0x800040, pite->IfIndex);
  186. PROCESS_ALLOC_FAILURE2(WsaBuf.buf,
  187. "error %d allocating %d bytes for input packet",
  188. Error, WsaBuf.len,
  189. return);
  190. Buffer = WsaBuf.buf;
  191. BEGIN_BREAKOUT_BLOCK1 {
  192. //
  193. // read the incoming packet
  194. //
  195. dwAddrLen = sizeof(SOCKADDR_IN);
  196. dwAddrLen = sizeof (saSrcAddr);
  197. dwFlags = 0;
  198. Error = WSARecvFrom(pite->SocketEntry.Socket, &WsaBuf, 1, &dwNumBytes,
  199. &dwFlags, (SOCKADDR FAR *)&saSrcAddr, &dwAddrLen,
  200. NULL, NULL);
  201. // check if any error in reading packet
  202. if ((Error!=0)||(dwNumBytes == 0)) {
  203. Error = WSAGetLastError();
  204. Trace2(RECEIVE, "error %d receiving packet on interface %d)",
  205. Error, pite->IfIndex);
  206. Logerr1(RECVFROM_FAILED, "%I", pite->IpAddr, Error);
  207. GOTO_END_BLOCK1;
  208. }
  209. //
  210. // dont ignore the packet even if it is from a local address
  211. //
  212. //
  213. // set packet ptr, IpHdr ptr, dwNumBytes, SrcAddr, DstnMcastAddr
  214. //
  215. // set source addr of packet
  216. dwSrcAddr = saSrcAddr.sin_addr.s_addr;
  217. IpHdrLen = (Buffer[0]&0x0F)*4;
  218. pPacket = &Buffer[IpHdrLen];
  219. dwNumBytes -= IpHdrLen;
  220. pIpHdr = (PIP_HEADER)Buffer;
  221. DstnMcastAddr = (ULONG)pIpHdr->Dstn.s_addr;
  222. //
  223. // verify that the packet has igmp type
  224. //
  225. if (pIpHdr->Protocol!=0x2) {
  226. Trace5(RECEIVE,
  227. "Packet received with IpDstnAddr(%d.%d.%d.%d) %d.%d.%d.%d from(%s) on "
  228. "IF:%0x is not of Igmp type(%d)",
  229. PRINT_IPADDR(pIpHdr->Dstn.s_addr), PRINT_IPADDR(pIpHdr->Src.s_addr),
  230. PRINT_IPADDR(dwSrcAddr), pite->IfIndex, pIpHdr->Protocol
  231. );
  232. Error = ERROR_CAN_NOT_COMPLETE;
  233. GOTO_END_BLOCK1;
  234. }
  235. //
  236. // check if packet has router alert option
  237. //
  238. {
  239. PBYTE pOption = (PBYTE)(pIpHdr+1);
  240. UCHAR i;
  241. for (i=0; i<IpHdrLen-20; i+=4) {
  242. if ( (pOption[0]==148) && (pOption[1]==4) ) {
  243. bRtrAlertSet = TRUE;
  244. break;
  245. }
  246. }
  247. }
  248. if (BQUEUE_WORK_ITEM_FOR_PACKET) {
  249. PACKET_CONTEXT UNALIGNED *pPktContext;
  250. //
  251. // allocate and initialize a packet-context
  252. //
  253. CREATE_PACKET_CONTEXT(pPktContext, dwNumBytes, Error);
  254. if (Error!=NO_ERROR)
  255. GOTO_END_BLOCK1;
  256. pPktContext->IfIndex = pite->IfIndex;
  257. pPktContext->DstnMcastAddr = DstnMcastAddr;
  258. pPktContext->InputSrc = dwSrcAddr;
  259. pPktContext->Length = dwNumBytes;
  260. pPktContext->Flags = bRtrAlertSet;
  261. CopyMemory(pPktContext->Packet, pPacket, dwNumBytes);
  262. //
  263. // enqueue the work-item to process the packet
  264. //
  265. Error = QueueIgmpWorker(WF_ProcessPacket, (PVOID)pPktContext);
  266. Trace2(WORKER, "Queuing IgmpWorker function: %s in %s",
  267. "WF_ProcessPacket:", "ProcessInputOnInterface");
  268. if (Error != NO_ERROR) {
  269. Trace1(ERR, "error %d queueing work-item for packet", Error);
  270. Logerr0(QUEUE_WORKER_FAILED, Error);
  271. IGMP_FREE(pPktContext);
  272. GOTO_END_BLOCK1;
  273. }
  274. }
  275. //
  276. // process the packet here itself
  277. //
  278. else {
  279. ACQUIRE_IF_LOCK_SHARED(pite->IfIndex, "_ProcessInputOnInterface");
  280. ProcessPacket(pite, dwSrcAddr, DstnMcastAddr, dwNumBytes, pPacket,
  281. bRtrAlertSet);
  282. RELEASE_IF_LOCK_SHARED(pite->IfIndex, "_ProcessInputOnInterface");
  283. }
  284. } END_BREAKOUT_BLOCK1;
  285. IGMP_FREE(WsaBuf.buf);
  286. return;
  287. } //end _ProcessInputOnInterface
  288. //------------------------------------------------------------------------------
  289. // _WF_ProcessPacket
  290. // Queued by: _ProcessInputOnInterface()
  291. // Locks: takes shared interface lock
  292. // Calls: _ProcessPacket()
  293. //------------------------------------------------------------------------------
  294. VOID
  295. WF_ProcessPacket (
  296. PVOID pvContext
  297. )
  298. {
  299. PPACKET_CONTEXT pPktContext = (PPACKET_CONTEXT)pvContext;
  300. DWORD IfIndex = pPktContext->IfIndex;
  301. PIF_TABLE_ENTRY pite;
  302. if (!EnterIgmpWorker()) { return; }
  303. Trace0(ENTER1, "Entering _WF_ProcessPacket()");
  304. ACQUIRE_IF_LOCK_SHARED(IfIndex, "_WF_ProcessPacket");
  305. BEGIN_BREAKOUT_BLOCK1 {
  306. //
  307. // retrieve the interface
  308. //
  309. pite = GetIfByIndex(IfIndex);
  310. if (pite == NULL) {
  311. Trace1(ERR, "_WF_ProcessPacket: interface %d not found", IfIndex);
  312. GOTO_END_BLOCK1;
  313. }
  314. //
  315. // make sure that the interface is activated
  316. //
  317. if (!(IS_IF_ACTIVATED(pite))) {
  318. Trace1(ERR,"_WF_ProcessPacket() called for inactive IfIndex(%0x)",
  319. IfIndex);
  320. GOTO_END_BLOCK1;
  321. }
  322. //
  323. // process the packet
  324. //
  325. ProcessPacket (pite, pPktContext->InputSrc, pPktContext->DstnMcastAddr,
  326. pPktContext->Length, pPktContext->Packet, pPktContext->Flags);
  327. } END_BREAKOUT_BLOCK1;
  328. RELEASE_IF_LOCK_SHARED(IfIndex, "_WF_ProcessPacket");
  329. IGMP_FREE(pPktContext);
  330. Trace0(LEAVE1, "Leaving _WF_ProcessPacket()");
  331. LeaveIgmpWorker();
  332. return;
  333. } //end _WF_ProcessPacket
  334. #define RETURN_FROM_PROCESS_PACKET() {\
  335. if (DEBUG_TIMER_PACKET&&bPrintTimerDebug) {\
  336. if (Error==NO_ERROR) {\
  337. Trace0(TIMER1, " ");\
  338. Trace0(TIMER1, "Printing Timer Queue after _ProcessPacket");\
  339. DebugPrintTimerQueue();\
  340. }\
  341. }\
  342. if (ExitLockRelease&IF_LOCK) \
  343. RELEASE_IF_LOCK_SHARED(IfIndex, "_ProcessPacket"); \
  344. if (ExitLockRelease&GROUP_LOCK) \
  345. RELEASE_GROUP_LOCK(Group, "_ProcessPacket"); \
  346. if (ExitLockRelease&TIMER_LOCK) \
  347. RELEASE_TIMER_LOCK("_ProcessPacket");\
  348. Trace0(LEAVE1, "Leaving _ProcessPacket1()\n"); \
  349. return; \
  350. }
  351. //------------------------------------------------------------------------------
  352. // _ProcessPacket
  353. //
  354. // Processes a packet received on an interface
  355. //
  356. // Locks: Assumes either shared Interface lock
  357. // or shared Socket Lock.
  358. // if ras interface, this procedure takes read lock on the ras table.
  359. // Called by: _ProcessInputOnInterface() or _WF_ProcessPacket()
  360. //------------------------------------------------------------------------------
  361. VOID
  362. ProcessPacket (
  363. PIF_TABLE_ENTRY pite,
  364. DWORD InputSrcAddr,
  365. DWORD DstnMcastAddr,
  366. DWORD NumBytes,
  367. PBYTE pPacketData, // igmp packet hdr. data following it ignored
  368. BOOL bRtrAlertSet
  369. )
  370. {
  371. DWORD Error = NO_ERROR;
  372. DWORD IfIndex = pite->IfIndex, Group=0, IfVersion;
  373. IGMP_HEADER UNALIGNED *pHdr;
  374. PIF_INFO pInfo = &pite->Info;
  375. PIGMP_IF_CONFIG pConfig = &pite->Config;
  376. PRAS_TABLE prt;
  377. PRAS_TABLE_ENTRY prte;
  378. PRAS_CLIENT_INFO pRasInfo;
  379. BOOL bRasStats = FALSE, bPrintTimerDebug=TRUE;
  380. LONGLONG llCurTime = GetCurrentIgmpTime();
  381. INT cmp;
  382. CHAR szPacketType[30];
  383. enum {
  384. NO_LOCK=0,
  385. IF_LOCK=0x1,
  386. RAS_LOCK=0x2,
  387. GROUP_LOCK=0x4,
  388. TIMER_LOCK=0x8
  389. } ExitLockRelease;
  390. ExitLockRelease = 0;
  391. IfVersion = IS_IF_VER1(pite)? 1: (IS_IF_VER2(pite)?2:3);
  392. Trace2(ENTER1, "Entering _ProcessPacket() IfIndex(%0x) DstnMcastAddr(%d.%d.%d.%d)",
  393. IfIndex, PRINT_IPADDR(DstnMcastAddr)
  394. );
  395. //
  396. // the packet must be at least some minimum length
  397. //
  398. if (NumBytes < MIN_PACKET_SIZE) {
  399. Trace4(RECEIVE,
  400. "%d-byte packet from %d.%d.%d.%d on If %0x (%d.%d.%d.%d) is too small",
  401. NumBytes, PRINT_IPADDR(InputSrcAddr), IfIndex, pite->IpAddr
  402. );
  403. Logwarn2(PACKET_TOO_SMALL, "%I%I", pite->IpAddr, InputSrcAddr, NO_ERROR);
  404. InterlockedIncrement(&pite->Info.ShortPacketsReceived);
  405. //todo: implement ras stats
  406. /*if (bRasStats)
  407. InterlockedIncrement(&pRasInfo->ShortPacketsReceived);
  408. */
  409. bPrintTimerDebug = FALSE;
  410. RETURN_FROM_PROCESS_PACKET();
  411. }
  412. //
  413. // initialize packet fields
  414. //
  415. pHdr = (IGMP_HEADER UNALIGNED *) pPacketData;
  416. Group = pHdr->Group;
  417. //
  418. // Verify packet version
  419. //
  420. if ( (pHdr->Vertype==IGMP_QUERY)||(pHdr->Vertype==IGMP_REPORT_V1)
  421. || (pHdr->Vertype==IGMP_REPORT_V2) || (pHdr->Vertype==IGMP_REPORT_V3)
  422. || (pHdr->Vertype==IGMP_LEAVE) )
  423. {
  424. InterlockedIncrement(&pInfo->TotalIgmpPacketsForRouter);
  425. //if (bRasStats)
  426. // InterlockedIncrement(&pRasInfo->TotalIgmpPacketsForRouter);
  427. }
  428. else {
  429. bPrintTimerDebug = FALSE;
  430. RETURN_FROM_PROCESS_PACKET();
  431. }
  432. switch(pHdr->Vertype) {
  433. case IGMP_QUERY:
  434. lstrcpy(szPacketType, "igmp-query"); break;
  435. case IGMP_REPORT_V1:
  436. lstrcpy(szPacketType, "igmp-report-v1"); break;
  437. case IGMP_REPORT_V2:
  438. lstrcpy(szPacketType, "igmp-report-v2"); break;
  439. case IGMP_REPORT_V3:
  440. lstrcpy(szPacketType, "igmp-report-v3"); break;
  441. case IGMP_LEAVE:
  442. lstrcpy(szPacketType, "igmp-leave"); break;
  443. };
  444. //
  445. // check for router alert option
  446. //
  447. if (!bRtrAlertSet) {
  448. InterlockedIncrement(&pInfo->PacketsWithoutRtrAlert);
  449. if (pite->Config.Flags&IGMP_ACCEPT_RTRALERT_PACKETS_ONLY) {
  450. Trace3(RECEIVE,
  451. "%s packet from %d ignored on IfIndex(%d%) due to no "
  452. "RtrAlert option",
  453. szPacketType, PRINT_IPADDR(InputSrcAddr), IfIndex
  454. );
  455. bPrintTimerDebug = FALSE;
  456. RETURN_FROM_PROCESS_PACKET();
  457. }
  458. }
  459. //
  460. // Make sure that the DstnMcastAddr is a valid multicast addr
  461. // or the unicast address of the router
  462. //
  463. if (!IS_MCAST_ADDR(DstnMcastAddr) && DstnMcastAddr!=pite->IpAddr) {
  464. Trace2(ERR,
  465. "Error! Igmp router received packet from Src(%d.%d.%d.%d) with "
  466. "dstn addr(%d.%d.%d.%d) which is not valid",
  467. PRINT_IPADDR(InputSrcAddr), PRINT_IPADDR(DstnMcastAddr)
  468. );
  469. IgmpAssertOnError(FALSE);
  470. bPrintTimerDebug = FALSE;
  471. RETURN_FROM_PROCESS_PACKET();
  472. }
  473. //
  474. // make sure that the interface is activated
  475. //
  476. if (!(IS_IF_ACTIVATED(pite))) {
  477. Trace1(ERR,"ProcessPacket() called for inactive IfIndex(%0x)",
  478. IfIndex);
  479. bPrintTimerDebug = FALSE;
  480. RETURN_FROM_PROCESS_PACKET();
  481. }
  482. //
  483. //if ras-server, then get lock on ras table.
  484. //
  485. if ( IS_RAS_SERVER_IF(pite->IfType) ) {
  486. prt = pite->pRasTable;
  487. //
  488. // retrieve ras client by addr
  489. //
  490. prte = GetRasClientByAddr(InputSrcAddr, prt);
  491. if (prte==NULL) {
  492. Trace3(ERR,
  493. "Got Igmp packet from an unknown ras client(%d.%d.%d.%d) on "
  494. "IF(%d:%d.%d.%d.%d)",
  495. PRINT_IPADDR(InputSrcAddr), IfIndex, PRINT_IPADDR(pite->IpAddr)
  496. );
  497. bPrintTimerDebug = FALSE;
  498. RETURN_FROM_PROCESS_PACKET();
  499. }
  500. #if 0
  501. // if the ras-client is not active, then return
  502. if (prte->Status&DELETED_FLAG)
  503. RETURN_FROM_PROCESS_PACKET();
  504. #endif
  505. // should I update ras client stats
  506. bRasStats = g_Config.RasClientStats;
  507. pRasInfo = &prte->Info;
  508. }
  509. //
  510. // increment count of total igmp packets received
  511. //
  512. InterlockedIncrement(&pInfo->TotalIgmpPacketsReceived);
  513. if (bRasStats)
  514. InterlockedIncrement(&pRasInfo->TotalIgmpPacketsReceived);
  515. //
  516. // long packet received. print trace if not v3. But it is not an error
  517. //
  518. if ( (NumBytes > MIN_PACKET_SIZE) && !IS_CONFIG_IGMP_V3(&pite->Config)) {
  519. Trace4( RECEIVE,
  520. "%d-byte packet from %d.%d.%d.%d on If %d (%d.%d.%d.%d) is too large",
  521. NumBytes, PRINT_IPADDR(InputSrcAddr), IfIndex,
  522. PRINT_IPADDR(pite->IpAddr)
  523. );
  524. InterlockedIncrement(&pite->Info.LongPacketsReceived);
  525. if (bRasStats)
  526. InterlockedIncrement(&pRasInfo->LongPacketsReceived);
  527. }
  528. //
  529. // Verify Igmp checksum
  530. //
  531. if (xsum(pHdr, NumBytes) != 0xffff) {
  532. Trace0(RECEIVE, "Wrong checksum packet received");
  533. InterlockedIncrement(&pInfo->WrongChecksumPackets);
  534. if (bRasStats)
  535. InterlockedIncrement(&pRasInfo->WrongChecksumPackets);
  536. RETURN_FROM_PROCESS_PACKET();
  537. }
  538. switch (pHdr->Vertype) {
  539. //////////////////////////////////////////////////////////////////
  540. // IGMP-QUERY //
  541. //////////////////////////////////////////////////////////////////
  542. case IGMP_QUERY :
  543. {
  544. //
  545. // ignore the query if it came from this interface
  546. //
  547. if (MatchIpAddrBinding(pite, InputSrcAddr)) {
  548. /*
  549. Trace3(RECEIVE,
  550. "received query packet sent by myself: IfIndex(%0x)"
  551. "IpAddr(%d.%d.%d.%d) DstnMcastAddr(%d.%d.%d.%d)",
  552. IfIndex, PRINT_IPADDR(InputSrcAddr), PRINT_IPADDR(DstnMcastAddr)
  553. );
  554. */
  555. bPrintTimerDebug = FALSE;
  556. RETURN_FROM_PROCESS_PACKET();
  557. }
  558. //
  559. // Error if interface type is IGMP_IF_RAS_SERVER. can be
  560. // IGMP_IF_RAS_ROUTER or IS_NOT_RAS_IF
  561. //
  562. if (! ( (IS_NOT_RAS_IF(pite->IfType))||(IS_RAS_ROUTER_IF(pite->IfType) ) )
  563. )
  564. {
  565. Trace3(ERR,
  566. "Error received Query on IfIndex(%d: %d.%d.%d.%d) from "
  567. "Ras client(%d.%d.%d.%d)",
  568. IfIndex, PRINT_IPADDR(pite->IpAddr), PRINT_IPADDR(InputSrcAddr)
  569. );
  570. IgmpAssertOnError(FALSE);
  571. bPrintTimerDebug = FALSE;
  572. RETURN_FROM_PROCESS_PACKET();
  573. }
  574. //////////////////////////////////////////////////////////////////
  575. // General Query
  576. //////////////////////////////////////////////////////////////////
  577. if (pHdr->Group==0) {
  578. DWORD Version,//Min(interface,pkt vertion)
  579. RealVersion;//pkt version
  580. // get versions
  581. Version = ((pHdr->ResponseTime==0)||IS_IF_VER1(pite))
  582. ? 1
  583. : ( (NumBytes==sizeof(IGMP_HEADER)||IS_IF_VER2(pite)) ? 2 : 3);
  584. RealVersion = (pHdr->ResponseTime==0)
  585. ? 1
  586. : (NumBytes==sizeof(IGMP_HEADER) ? 2 : 3);
  587. Trace3(RECEIVE,
  588. "General Query Version:%d received on interface(%d) from %d.%d.%d.%d",
  589. IfIndex, RealVersion, PRINT_IPADDR(InputSrcAddr));
  590. if (Version!=RealVersion){
  591. Trace2(RECEIVE, "Processing the Version:%d packet as Version:%d",
  592. RealVersion, RealVersion);
  593. }
  594. //
  595. // check that the dstn addr was AllHostsAddr
  596. //
  597. if (DstnMcastAddr!=ALL_HOSTS_MCAST) {
  598. Trace3(RECEIVE,
  599. "received query packet not on AllHostsGroup: IfIndex(%0x)"
  600. "SrcAddr(%d.%d.%d.%d) DstnMcastAddr(%d.%d.%d.%d)",
  601. IfIndex, PRINT_IPADDR(InputSrcAddr),
  602. PRINT_IPADDR(DstnMcastAddr)
  603. );
  604. RETURN_FROM_PROCESS_PACKET();
  605. }
  606. //
  607. // acquire timer lock
  608. //
  609. ACQUIRE_TIMER_LOCK("_ProcessPacket");
  610. ExitLockRelease |= TIMER_LOCK;
  611. //
  612. // log warning if incorrect version query received
  613. //
  614. if ( ((RealVersion==1)&&(!IS_PROTOCOL_TYPE_IGMPV1(pite)))
  615. || (RealVersion==2 && !IS_PROTOCOL_TYPE_IGMPV2(pite))
  616. || (RealVersion==3 && IS_PROTOCOL_TYPE_IGMPV3(pite)) )
  617. {
  618. // get warn interval in system time
  619. LONGLONG llWarnInterval = OTHER_VER_ROUTER_WARN_INTERVAL*60*1000;
  620. InterlockedIncrement(&pInfo->WrongVersionQueries);
  621. //
  622. // check if warn interval time has passed since last warning
  623. // I check if OtherVerPresentTimeWarn>llCurTime to take care
  624. // of timer resets
  625. //
  626. if ( (pInfo->OtherVerPresentTimeWarn+llWarnInterval<llCurTime)
  627. || (pInfo->OtherVerPresentTimeWarn>llCurTime) )
  628. {
  629. if (pHdr->ResponseTime==0) {
  630. Trace3(RECEIVE,
  631. "Detected ver-%d router(%d.%d.%d.%d) on "
  632. "interface(%d.%d.%d.%d)",
  633. Version, PRINT_IPADDR(InputSrcAddr),
  634. PRINT_IPADDR(pite->IpAddr));
  635. Logwarn2(VERSION_QUERY, "%I%I", InputSrcAddr,
  636. pite->IpAddr, NO_ERROR);
  637. }
  638. pInfo->OtherVerPresentTimeWarn = llCurTime;
  639. }
  640. }
  641. if (Version==1)
  642. pite->Info.V1QuerierPresentTime = llCurTime
  643. + CONFIG_TO_SYSTEM_TIME(IGMP_VER1_RTR_PRESENT_TIMEOUT);
  644. //
  645. // if IpAddress less than my address then I become NonQuerier
  646. // even if I am in Startup Mode
  647. //
  648. if (INET_CMP(InputSrcAddr, pite->IpAddr, cmp) <0) {
  649. DWORD QQIC=0,QRV=0;
  650. // last querier is being changed from myself to B, or from A to B.
  651. if (InputSrcAddr != pite->Info.QuerierIpAddr)
  652. pite->Info.LastQuerierChangeTime = llCurTime;
  653. //
  654. // if (version 3, change robustness variable and query interval
  655. // if required) (only if I am not querier. else it will be
  656. // changed when I change to non-querier
  657. //
  658. if (Version==3 && !IS_QUERIER(pite)
  659. &&(INET_CMP(InputSrcAddr, pite->Info.QuerierIpAddr, cmp)<=0))
  660. {
  661. PIGMP_HEADER_V3_EXT pSourcesQuery;
  662. pSourcesQuery = (PIGMP_HEADER_V3_EXT)
  663. ((PBYTE)pHdr+sizeof(IGMP_HEADER));
  664. if (pSourcesQuery->QRV!=0) {
  665. if (pite->Config.RobustnessVariable!=pSourcesQuery->QRV)
  666. {
  667. Trace3(CONFIG,
  668. "Changing Robustness variable from %d to %d. "
  669. "Querier:%d.%d.%d.%d",
  670. pite->Config.RobustnessVariable,
  671. pSourcesQuery->QRV,
  672. PRINT_IPADDR(InputSrcAddr)
  673. );
  674. pite->Config.RobustnessVariable = pSourcesQuery->QRV;
  675. }
  676. }
  677. QQIC = GET_QQIC_FROM_CODE(pSourcesQuery->QQIC)*1000;
  678. if (pSourcesQuery->QQIC!=0 && pite->Config.GenQueryMaxResponseTime < QQIC) {
  679. if (pite->Config.GenQueryInterval!=QQIC)
  680. {
  681. Trace3(CONFIG,
  682. "Changing General-Query-Interval from %d to %d. "
  683. "Querier:%d.%d.%d.%d",
  684. pite->Config.GenQueryInterval/1000,
  685. QQIC/1000,
  686. PRINT_IPADDR(InputSrcAddr)
  687. );
  688. pite->Config.GenQueryInterval
  689. = QQIC;
  690. }
  691. }
  692. pite->Config.GroupMembershipTimeout =
  693. pite->Config.RobustnessVariable*pite->Config.GenQueryInterval
  694. + pite->Config.GenQueryMaxResponseTime;
  695. pite->Config.OtherQuerierPresentInterval
  696. = pite->Config.RobustnessVariable*pite->Config.GenQueryInterval
  697. + (pite->Config.GenQueryMaxResponseTime)/2;
  698. }
  699. // change from querier to non-querier
  700. if (IS_QUERIER(pite)) {
  701. PQUERIER_CONTEXT pwi = IGMP_ALLOC(sizeof(QUERIER_CONTEXT),
  702. 0x800080,pite->IfIndex);
  703. if (pwi==NULL)
  704. RETURN_FROM_PROCESS_PACKET();
  705. pwi->IfIndex = IfIndex;
  706. pwi->QuerierIpAddr = InputSrcAddr;
  707. pwi->NewRobustnessVariable = QRV;
  708. pwi->NewGenQueryInterval = QQIC;
  709. // I have to queue a work item as I have to take an If write lock
  710. QueueIgmpWorker(WF_BecomeNonQuerier, (PVOID)pwi);
  711. Trace2(RECEIVE, "_ProcessPacket queued _WF_BecomeNonQuerier "
  712. "on If:%0x Querier(%d.%d.%d.%d)",
  713. IfIndex, PRINT_IPADDR(InputSrcAddr));
  714. }
  715. // I am non-querier already
  716. else {
  717. InterlockedExchange(&pite->Info.QuerierIpAddr, InputSrcAddr);
  718. #if DEBUG_TIMER_TIMERID
  719. SET_TIMER_ID(&pite->NonQueryTimer, 211, pite->IfIndex, 0, 0);
  720. #endif
  721. UpdateLocalTimer(&pite->NonQueryTimer,
  722. pite->Config.OtherQuerierPresentInterval, DBG_N);
  723. // not using interlockedExchange
  724. pite->Info.QuerierPresentTimeout = llCurTime
  725. + CONFIG_TO_SYSTEM_TIME(pite->Config.OtherQuerierPresentInterval);
  726. }
  727. }
  728. //
  729. // Ignore query from querier with higher IpAddr
  730. //
  731. else {
  732. }
  733. RELEASE_TIMER_LOCK("_ProcessPacket");
  734. ExitLockRelease &= ~TIMER_LOCK;
  735. RETURN_FROM_PROCESS_PACKET();
  736. } //end general query
  737. //////////////////////////////////////////////////////////////////
  738. // Group Specific Query
  739. //////////////////////////////////////////////////////////////////
  740. else {
  741. Error = ProcessGroupQuery(pite, pHdr, NumBytes, InputSrcAddr, DstnMcastAddr);
  742. RETURN_FROM_PROCESS_PACKET();
  743. }
  744. break;
  745. } //end query (groupSpecific or general)
  746. //////////////////////////////////////////////////////////////////
  747. // IGMP_REPORT_V1, IGMP_REPORT_V2, IGMP_REPORT_V3 //
  748. //////////////////////////////////////////////////////////////////
  749. case IGMP_REPORT_V1 :
  750. case IGMP_REPORT_V2 :
  751. case IGMP_REPORT_V3 :
  752. {
  753. Error = ProcessReport(pite, pHdr, NumBytes, InputSrcAddr, DstnMcastAddr);
  754. RETURN_FROM_PROCESS_PACKET();
  755. }
  756. //////////////////////////////////////////////////////////////////
  757. // IGMP_LEAVE //
  758. //////////////////////////////////////////////////////////////////
  759. case IGMP_LEAVE :
  760. {
  761. PGROUP_TABLE_ENTRY pge; //group table entry
  762. PGI_ENTRY pgie; //group interface entry
  763. Trace3(RECEIVE,
  764. "IGMP Leave for group(%d.%d.%d.%d) on IfIndex(%0x) from "
  765. "SrcAddr(%d.%d.%d.%d)",
  766. PRINT_IPADDR(Group), IfIndex, PRINT_IPADDR(InputSrcAddr)
  767. );
  768. //
  769. // the multicast group should not be 224.0.0.x
  770. //
  771. if (LOCAL_MCAST_GROUP(DstnMcastAddr)) {
  772. Trace2(RECEIVE,
  773. "Leave Report received from %d.%d.%d.%d for "
  774. "Local group(%d.%d.%d.%d)",
  775. PRINT_IPADDR(InputSrcAddr), PRINT_IPADDR(DstnMcastAddr));
  776. RETURN_FROM_PROCESS_PACKET();
  777. }
  778. //
  779. // check that the dstn addr was AllRoutersAddr
  780. // or dstn addr must match the group field
  781. //
  782. if ( (DstnMcastAddr!=ALL_ROUTERS_MCAST)&&(DstnMcastAddr!=Group) ) {
  783. Trace3(RECEIVE,
  784. "received IGMP Leave packet not on AllRoutersGroup: IfIndex(%0x)"
  785. "SrcAddr(%d.%d.%d.%d) DstnMcastAddr(%d.%d.%d.%d)",
  786. IfIndex, PRINT_IPADDR(InputSrcAddr), PRINT_IPADDR(DstnMcastAddr)
  787. );
  788. RETURN_FROM_PROCESS_PACKET();
  789. }
  790. //
  791. // check that the Group field is a valid multicast addr
  792. //
  793. if ( !IS_MCAST_ADDR(Group) ) {
  794. Trace4(RECEIVE,
  795. "received IGMP Leave packet with illegal Group(%d.%d.%d.%d) field: "
  796. "IfIndex(%0x) SrcAddr(%d.%d.%d.%d) DstnMcastAddr(%d.%d.%d.%d)",
  797. PRINT_IPADDR(Group), IfIndex, PRINT_IPADDR(InputSrcAddr),
  798. PRINT_IPADDR(DstnMcastAddr)
  799. );
  800. RETURN_FROM_PROCESS_PACKET();
  801. }
  802. //
  803. // update statistics
  804. //
  805. InterlockedIncrement(&pite->Info.LeavesReceived);
  806. if (bRasStats)
  807. InterlockedIncrement(&pRasInfo->LeavesReceived);
  808. //
  809. // if Leave processing not enabled or not querier then ignore Leave.
  810. //
  811. if ( !((IS_IF_VER2(pite)||IS_IF_VER3(pite)) && (IS_QUERIER(pite))) ) {
  812. Trace0(RECEIVE,"Ignoring the Leave Packet");
  813. break;
  814. }
  815. //
  816. // Lock the group table
  817. //
  818. ACQUIRE_GROUP_LOCK(Group, "_ProcessPacket");
  819. ExitLockRelease |= GROUP_LOCK;
  820. //
  821. // find the group entry. If entry not found then ignore the leave messg
  822. //
  823. pge = GetGroupFromGroupTable(Group, NULL, llCurTime);
  824. if (pge==NULL) {
  825. Error = ERROR_CAN_NOT_COMPLETE;
  826. Trace2(ERR, "Leave received for nonexisting group(%d.%d.%d.%d) on IfIndex(%0x)",
  827. PRINT_IPADDR(Group), pite->IfIndex);
  828. RETURN_FROM_PROCESS_PACKET();
  829. }
  830. //
  831. // find the GI entry. If GI entry does not exist or has deletedFlag then
  832. // ignore the leave
  833. //
  834. pgie = GetGIFromGIList(pge, pite, InputSrcAddr, NOT_STATIC_GROUP, NULL, llCurTime);
  835. if ( (pgie==NULL)||(pgie->Status&DELETED_FLAG) ) {
  836. Error = ERROR_CAN_NOT_COMPLETE;
  837. Trace2(ERR, "leave received for nonexisting group(%d.%d.%d.%d) on IfIndex(%0x). Not member",
  838. PRINT_IPADDR(Group), IfIndex);
  839. RETURN_FROM_PROCESS_PACKET();
  840. }
  841. // ignore leave if it is not in ver 2 mode
  842. if (pgie->Version!=2)
  843. RETURN_FROM_PROCESS_PACKET();
  844. // if static group, ignore leave
  845. if (pgie->bStaticGroup) {
  846. Trace2(ERR,
  847. "Leave not processed for group(%d.%d.%d.%d) on IfIndex(%0x): "
  848. "Static group",
  849. PRINT_IPADDR(Group), IfIndex
  850. );
  851. RETURN_FROM_PROCESS_PACKET();
  852. }
  853. //
  854. // if v1-query received recently for that group, then ignore leaves
  855. //
  856. //
  857. if (pgie->Version==1)
  858. {
  859. Error = ERROR_CAN_NOT_COMPLETE;
  860. Trace2(ERR,
  861. "Leave not processed for group(%d.%d.%d.%d) on IfIndex(%0x)"
  862. "(recent v1 report)",
  863. PRINT_IPADDR(Group), IfIndex
  864. );
  865. bPrintTimerDebug = FALSE;
  866. RETURN_FROM_PROCESS_PACKET();
  867. }
  868. //
  869. // if ras server interface, then delete the group entry and I am done.
  870. // GroupSpecific Query is not sent to ras clients
  871. //
  872. // if pConfig->LastMemQueryCount==0 then the group is expected to be
  873. // deleted immediately
  874. //
  875. if ( IS_RAS_SERVER_IF(pite->IfType) || pConfig->LastMemQueryCount==0) {
  876. DeleteGIEntry(pgie, TRUE, TRUE); //updateStats, CallMgm
  877. RETURN_FROM_PROCESS_PACKET();
  878. }
  879. ACQUIRE_TIMER_LOCK("_ProcessPacket");
  880. ExitLockRelease |= TIMER_LOCK;
  881. //
  882. // if timer already expired return.
  883. // Leave the group deletion to Membership timer
  884. //
  885. if ( !(pgie->GroupMembershipTimer.Status&TIMER_STATUS_ACTIVE)
  886. ||(pgie->GroupMembershipTimer.Timeout<llCurTime) )
  887. {
  888. RETURN_FROM_PROCESS_PACKET();
  889. }
  890. //
  891. // if currently processing a leave then exit.
  892. //
  893. if (pgie->LastMemQueryCount>0) {
  894. RETURN_FROM_PROCESS_PACKET();
  895. }
  896. //
  897. // in almost all places, I have to do this check.
  898. // change the way insert and update timers' timeout is set
  899. //
  900. // set a new leave timer. Set the new LastMemQueryCount left
  901. //
  902. if (pConfig->LastMemQueryCount) {
  903. pgie->LastMemQueryCount = pConfig->LastMemQueryCount - 1;
  904. #if DEBUG_TIMER_TIMERID
  905. SET_TIMER_ID(&pgie->LastMemQueryTimer, 410, pite->IfIndex,
  906. Group, 0);
  907. #endif
  908. InsertTimer(&pgie->LastMemQueryTimer, pConfig->LastMemQueryInterval, TRUE, DBG_Y);
  909. }
  910. //
  911. // set membership timer to
  912. // min{currentValue,LastMemQueryInterval*LastMemQueryCount}
  913. //
  914. if (pgie->GroupMembershipTimer.Timeout >
  915. (llCurTime+(pConfig->LastMemQueryCount
  916. *CONFIG_TO_SYSTEM_TIME(pConfig->LastMemQueryInterval)))
  917. )
  918. {
  919. #if DEBUG_TIMER_TIMERID
  920. pgie->GroupMembershipTimer.Id = 340;
  921. pgie->GroupMembershipTimer.Id2 = TimerId++;
  922. #endif
  923. UpdateLocalTimer(&pgie->GroupMembershipTimer,
  924. pConfig->LastMemQueryCount*pConfig->LastMemQueryInterval,
  925. DBG_N);
  926. // update GroupExpiryTime so that correct stats are displayed
  927. pgie->Info.GroupExpiryTime = llCurTime
  928. + CONFIG_TO_SYSTEM_TIME(pConfig->LastMemQueryCount
  929. *pConfig->LastMemQueryInterval);
  930. }
  931. //
  932. //release timer and groupBucket locks
  933. //I still have read lock on the IfTable/RasTable
  934. //
  935. RELEASE_TIMER_LOCK("_ProcessPacket");
  936. RELEASE_GROUP_LOCK(Group, "_ProcessPacket");
  937. ExitLockRelease &= ~TIMER_LOCK;
  938. ExitLockRelease &= ~GROUP_LOCK;
  939. //
  940. // send group specific query only if I am a querier
  941. //
  942. if (IS_QUERIER(pite))
  943. SEND_GROUP_QUERY_V2(pite, Group);
  944. //releae ifLock/RasLock and exit
  945. RETURN_FROM_PROCESS_PACKET();
  946. }//igmp leave
  947. default :
  948. {
  949. Error = ERROR_CAN_NOT_COMPLETE;
  950. Trace3(ERR,
  951. "Incorrect Igmp type(%d) packet received on IfIndex(%d%) Group(%d.%d.%d.5d)",
  952. pHdr->Vertype, IfIndex, PRINT_IPADDR(Group)
  953. );
  954. IgmpAssertOnError(FALSE);
  955. RETURN_FROM_PROCESS_PACKET();
  956. }
  957. }
  958. RETURN_FROM_PROCESS_PACKET();
  959. } //end _ProcessPacket
  960. //------------------------------------------------------------------------------
  961. // _T_LastVer1ReportTimer
  962. //
  963. // For this GI entry, the last ver-1 report has timed out. Change to ver-2 if
  964. // the interface is set to ver-2.
  965. // Locks: Assumes timer lock.
  966. //
  967. // be careful as only timer lock held. make sure that the worker fn checks
  968. // everything. recheck igmp version, etc.
  969. //------------------------------------------------------------------------------
  970. DWORD
  971. T_LastVer1ReportTimer (
  972. PVOID pvContext
  973. )
  974. {
  975. PIGMP_TIMER_ENTRY pTimer; //ptr to timer entry
  976. PGI_ENTRY pgie; //group interface entry
  977. PIF_TABLE_ENTRY pite;
  978. LONGLONG llCurTime = GetCurrentIgmpTime();
  979. Trace0(ENTER1, "Entering _T_LastVer1ReportTimer()");
  980. //
  981. // get pointer to LastMemQueryTimer, GI entry, pite
  982. //
  983. pTimer = CONTAINING_RECORD( pvContext, IGMP_TIMER_ENTRY, Context);
  984. pgie = CONTAINING_RECORD( pTimer, GI_ENTRY, LastVer1ReportTimer);
  985. pite = pgie->pIfTableEntry;
  986. //
  987. // if IfTable not activated, then break
  988. //
  989. if (!IS_IF_ACTIVATED(pite) || (pgie->Status&DELETED_FLAG))
  990. return NO_ERROR;
  991. Trace2(TIMER, "T_LastVer1ReportTimer() called for If(%0x), Group(%d.%d.%d.%d)",
  992. pite->IfIndex, PRINT_IPADDR(pgie->pGroupTableEntry->Group));
  993. // set the state to ver-2 unless the interface is ver-1, in which case
  994. // set the version-1 timer again.
  995. if (IS_PROTOCOL_TYPE_IGMPV2(pite)) {
  996. pgie->Version = 2;
  997. }
  998. else if (IS_PROTOCOL_TYPE_IGMPV3(pite)) {
  999. if (IS_TIMER_ACTIVE(pgie->LastVer2ReportTimer))
  1000. pgie->Version = 2;
  1001. else {
  1002. PWORK_CONTEXT pWorkContext;
  1003. DWORD Error=NO_ERROR;
  1004. //
  1005. // queue work item for shifting to v3 for that group
  1006. //
  1007. CREATE_WORK_CONTEXT(pWorkContext, Error);
  1008. if (Error!=NO_ERROR) {
  1009. return ERROR_CAN_NOT_COMPLETE;
  1010. }
  1011. pWorkContext->IfIndex = pite->IfIndex;
  1012. pWorkContext->Group = pgie->pGroupTableEntry->Group; //ptrs usage safe
  1013. pWorkContext->NHAddr = pgie->NHAddr; //valid only for ras: should i us
  1014. pWorkContext->WorkType = SHIFT_TO_V3;
  1015. Trace0(WORKER, "Queueing WF_TimerProcessing() to shift to v3");
  1016. QueueIgmpWorker(WF_TimerProcessing, (PVOID)pWorkContext);
  1017. }
  1018. }
  1019. Trace0(LEAVE1, "Leaving _T_LastVer1ReportTimer()");
  1020. return 0;
  1021. }
  1022. //------------------------------------------------------------------------------
  1023. // _T_LastMemQueryTimer
  1024. // called when LastMemQueryTimer() has expired. This timer is not used to
  1025. // time out memberships (GroupMembershipTimer is used for that). It is only
  1026. // used to send GroupSpecific Queries.
  1027. //
  1028. // Queues: WF_TimerProcessing() to send group specific query.
  1029. // Note: WT_ProcessTimerEvent() makes sure the protocol is not stopp-ing/ed
  1030. // Locks: Assumes timer lock. does not need any other lock.
  1031. // be careful as only timer lock held. make sure that the worker fn checks
  1032. // everything. recheck igmp version, etc.
  1033. //------------------------------------------------------------------------------
  1034. DWORD
  1035. T_LastMemQueryTimer (
  1036. PVOID pvContext
  1037. )
  1038. {
  1039. DWORD Error=NO_ERROR;
  1040. PIGMP_TIMER_ENTRY pTimer; //ptr to timer entry
  1041. PGI_ENTRY pgie; //group interface entry
  1042. PWORK_CONTEXT pWorkContext;
  1043. PIF_TABLE_ENTRY pite;
  1044. PRAS_TABLE_ENTRY prte;
  1045. BOOL bCompleted = FALSE; //if false, set count to 0
  1046. Trace0(ENTER1, "Entering _T_LastMemQueryTimer()");
  1047. //
  1048. // get pointer to LastMemQueryTimer, GI entry, pite, prte
  1049. //
  1050. pTimer = CONTAINING_RECORD( pvContext, IGMP_TIMER_ENTRY, Context);
  1051. pgie = CONTAINING_RECORD( pTimer, GI_ENTRY, LastMemQueryTimer);
  1052. pite = pgie->pIfTableEntry;
  1053. prte = pgie->pRasTableEntry;
  1054. //
  1055. // if IfTable not activated, then break
  1056. //
  1057. if (!IS_IF_ACTIVATED(pite))
  1058. return NO_ERROR;
  1059. Trace2(TIMER, "_T_LastMemQueryTimer() called for If(%0x), Group(%d.%d.%d.%d)",
  1060. pite->IfIndex, PRINT_IPADDR(pgie->pGroupTableEntry->Group));
  1061. BEGIN_BREAKOUT_BLOCK1 {
  1062. //
  1063. // if GI or pite or prte has flag already set, then exit
  1064. //
  1065. if ( (pgie->Status&DELETED_FLAG) || (pite->Status&DELETED_FLAG) )
  1066. GOTO_END_BLOCK1;
  1067. if ( (prte!=NULL) && (prte->Status&DELETED_FLAG) )
  1068. GOTO_END_BLOCK1;
  1069. if (pgie->Version!=3) {
  1070. //
  1071. // if LeaveEnabled FALSE then return
  1072. //
  1073. if (!GI_PROCESS_GRPQUERY(pite, pgie))
  1074. GOTO_END_BLOCK1;
  1075. }
  1076. //
  1077. // have sent the last GroupSpecific query. GroupMembershipTimer will take care
  1078. // of deleting this GI entry
  1079. //
  1080. if (pgie->LastMemQueryCount==0) {
  1081. bCompleted = TRUE;
  1082. GOTO_END_BLOCK1;
  1083. }
  1084. //
  1085. // decrement count.
  1086. //
  1087. if (InterlockedDecrement(&pgie->LastMemQueryCount) == (ULONG)-1) {
  1088. pgie->LastMemQueryCount = 0;
  1089. }
  1090. //
  1091. // if count==0, dont insert timer again, but send the last groupSp Query
  1092. //
  1093. if (pgie->LastMemQueryCount>0) {
  1094. //reinsert the timer to send the next GroupSpQuery
  1095. #if DEBUG_TIMER_TIMERID
  1096. SET_TIMER_ID(&pgie->LastMemQueryTimer, 420, pite->IfIndex,
  1097. pgie->pGroupTableEntry->Group, 0);
  1098. #endif
  1099. InsertTimer(&pgie->LastMemQueryTimer,
  1100. pite->Config.LastMemQueryInterval, FALSE, DBG_Y);
  1101. }
  1102. //
  1103. // queue work item for sending the GroupSp query even if the router
  1104. // is not a Querier
  1105. //
  1106. CREATE_WORK_CONTEXT(pWorkContext, Error);
  1107. if (Error!=NO_ERROR) {
  1108. GOTO_END_BLOCK1;
  1109. }
  1110. pWorkContext->IfIndex = pite->IfIndex;
  1111. pWorkContext->Group = pgie->pGroupTableEntry->Group;
  1112. pWorkContext->NHAddr = pgie->NHAddr; //valid only for ras: should i use it?
  1113. pWorkContext->WorkType = (pgie->Version==3) ? MSG_GROUP_QUERY_V3
  1114. : MSG_GROUP_QUERY_V2;
  1115. Trace0(WORKER, "Queueing WF_TimerProcessing() to send GroupSpQuery:");
  1116. QueueIgmpWorker(WF_TimerProcessing, (PVOID)pWorkContext);
  1117. bCompleted = TRUE;
  1118. } END_BREAKOUT_BLOCK1;
  1119. // there was some error somewhere. so set the LastMemQueryCount to 0
  1120. if (!bCompleted)
  1121. InterlockedExchange(&pgie->LastMemQueryCount, 0);
  1122. Trace0(LEAVE1, "Leaving _T_LastMemQueryTimer()");
  1123. return 0;
  1124. } //end _T_LastMemQueryTimer
  1125. //------------------------------------------------------------------------------
  1126. // _T_MembershipTimer
  1127. //
  1128. // lock: has TimerLock
  1129. // called when the GroupMembershipTimer is fired
  1130. // delete the GI entry if it exists.
  1131. //
  1132. // be careful as only timer lock held. make sure that the worker fn checks
  1133. // everything. recheck igmp version, etc.
  1134. //------------------------------------------------------------------------------
  1135. DWORD
  1136. T_MembershipTimer (
  1137. PVOID pvContext
  1138. )
  1139. {
  1140. DWORD Error=NO_ERROR;
  1141. PIGMP_TIMER_ENTRY pTimer; //ptr to timer entry
  1142. PGI_ENTRY pgie; //group interface entry
  1143. PWORK_CONTEXT pWorkContext;
  1144. PIF_TABLE_ENTRY pite;
  1145. PRAS_TABLE_ENTRY prte;
  1146. Trace0(ENTER1, "Entering _T_MembershipTimer()");
  1147. BEGIN_BREAKOUT_BLOCK1 {
  1148. //
  1149. // get pointer to Membership Timer, GI entry, pite,
  1150. //
  1151. pTimer = CONTAINING_RECORD( pvContext, IGMP_TIMER_ENTRY, Context);
  1152. pgie = CONTAINING_RECORD( pTimer, GI_ENTRY, GroupMembershipTimer);
  1153. pite = pgie->pIfTableEntry;
  1154. prte = pgie->pRasTableEntry;
  1155. //
  1156. // if IfTable not activated, then break
  1157. //
  1158. if (!IS_IF_ACTIVATED(pite))
  1159. GOTO_END_BLOCK1;
  1160. Trace2(TIMER, "_T_MembershipTimer() called for If(%0x), Group(%d.%d.%d.%d)",
  1161. pite->IfIndex, PRINT_IPADDR(pgie->pGroupTableEntry->Group));
  1162. //
  1163. // if GI or pite or prte has deleted flag already set, then exit
  1164. //
  1165. if ( (pgie->Status&DELETED_FLAG) || (pite->Status&DELETED_FLAG) ) {
  1166. GOTO_END_BLOCK1;
  1167. }
  1168. //
  1169. // if Ras, and ras table being deleted then break
  1170. //
  1171. if ( (prte!=NULL) && (prte->Status&DELETED_FLAG) )
  1172. GOTO_END_BLOCK1;
  1173. //
  1174. // if IfTable not activated, then break
  1175. //
  1176. if (!IS_IF_ACTIVATED(pite))
  1177. GOTO_END_BLOCK1;
  1178. //
  1179. // if LastMemTimer is active, remove it(cant remove it in this function
  1180. // as it is being processed by the timer queue simultaneously.
  1181. if (pgie->LastMemQueryCount>0)
  1182. pgie->LastMemQueryCount = 0;
  1183. //
  1184. // queue work item to delete the GI entry
  1185. //
  1186. CREATE_WORK_CONTEXT(pWorkContext, Error);
  1187. if (Error!=NO_ERROR)
  1188. GOTO_END_BLOCK1;
  1189. pWorkContext->IfIndex = pite->IfIndex;
  1190. pWorkContext->NHAddr = pgie->NHAddr;
  1191. pWorkContext->Group = pgie->pGroupTableEntry->Group;
  1192. pWorkContext->WorkType = DELETE_MEMBERSHIP;
  1193. Trace0(WORKER, "_T_MembershipTimer queued _WF_TimerProcessing:");
  1194. QueueIgmpWorker(WF_TimerProcessing, (PVOID)pWorkContext);
  1195. } END_BREAKOUT_BLOCK1;
  1196. Trace0(LEAVE1, "Leaving _T_MembershipTimer()");
  1197. return 0;
  1198. } //end _T_MembershipTimer
  1199. //------------------------------------------------------------------------------
  1200. // _T_QueryTimer
  1201. // fired when a general query timer is fired. Sends a general query.
  1202. // The timer queue is currently locked
  1203. //
  1204. // be careful as only timer lock held. make sure that the worker fn checks
  1205. // everything. recheck igmp version, etc.
  1206. //------------------------------------------------------------------------------
  1207. DWORD
  1208. T_QueryTimer (
  1209. PVOID pvContext
  1210. )
  1211. {
  1212. DWORD Error=NO_ERROR;
  1213. PIGMP_TIMER_ENTRY pTimer; //ptr to timer entry
  1214. PWORK_CONTEXT pWorkContext;
  1215. PIF_INFO pInfo;
  1216. PIF_TABLE_ENTRY pite;
  1217. static ULONG Seed = 123456;
  1218. ULONG ulTimeout;
  1219. BOOL bRandomize = FALSE; // [0,GenQueryInterval] for 1st gen query after startup.
  1220. Trace0(ENTER1, "Entering _T_QueryTimer()");
  1221. pTimer = CONTAINING_RECORD( pvContext, IGMP_TIMER_ENTRY, Context);
  1222. pite = CONTAINING_RECORD( pTimer, IF_TABLE_ENTRY, QueryTimer);
  1223. pInfo = &pite->Info;
  1224. //
  1225. // make sure that the interface is activated
  1226. //
  1227. if (!(IS_IF_ACTIVATED(pite))) {
  1228. Trace2(ERR, "T_QueryTimer() called for inactive IfIndex(%0x), IfType(%d)",
  1229. pite->IfIndex, pite->IfType);
  1230. return 0;
  1231. }
  1232. Trace2(TIMER, "Processing T_QueryTimer() for IfIndex(%0x), IfType(%d)",
  1233. pite->IfIndex, pite->IfType);
  1234. //
  1235. // check if still in startup Mode.
  1236. //
  1237. if (pInfo->StartupQueryCountCurrent>0) {
  1238. InterlockedDecrement(&pInfo->StartupQueryCountCurrent);
  1239. bRandomize = (pInfo->StartupQueryCountCurrent == 0);
  1240. }
  1241. // if non-querier, then done if I have sent startupQueries
  1242. if ( !IS_QUERIER(pite) && (pInfo->StartupQueryCountCurrent<=0) )
  1243. return 0;
  1244. // set the next query time
  1245. ulTimeout = (pInfo->StartupQueryCountCurrent>0)
  1246. ? pite->Config.StartupQueryInterval
  1247. : (bRandomize )
  1248. ? (DWORD) ((RtlRandom(&Seed)/(FLOAT)MAXLONG)
  1249. *pite->Config.GenQueryInterval)
  1250. : pite->Config.GenQueryInterval;
  1251. #if DEBUG_TIMER_TIMERID
  1252. SET_TIMER_ID(&pite->QueryTimer, 120, pite->IfIndex, 0, 0);
  1253. #endif
  1254. InsertTimer(&pite->QueryTimer, ulTimeout, FALSE, DBG_Y);
  1255. //
  1256. // queue work item for sending the general query
  1257. //
  1258. CREATE_WORK_CONTEXT(pWorkContext, Error);
  1259. if (Error!=NO_ERROR)
  1260. return 0;
  1261. pWorkContext->IfIndex = pite->IfIndex;
  1262. pWorkContext->WorkType = MSG_GEN_QUERY;
  1263. QueueIgmpWorker(WF_TimerProcessing, (PVOID)pWorkContext);
  1264. Trace0(WORKER,
  1265. "_T_QueryTimer queued _WF_TimerProcessing: Querier State");
  1266. Trace0(LEAVE1, "Leaving _T_QueryTimer()");
  1267. return 0;
  1268. } //end _T_QueryTimer
  1269. //------------------------------------------------------------------------------
  1270. // _T_NonQueryTimer
  1271. // fired when it is in non-querier Mode and hasnt heard a query for a long time
  1272. //
  1273. // be careful as only timer lock held. make sure that the worker fn checks
  1274. // everything. recheck igmp version, etc.
  1275. //------------------------------------------------------------------------------
  1276. DWORD
  1277. T_NonQueryTimer (
  1278. PVOID pvContext
  1279. )
  1280. {
  1281. DWORD Error=NO_ERROR;
  1282. PIGMP_TIMER_ENTRY pTimer; //ptr to timer entry
  1283. PIF_TABLE_ENTRY pite;
  1284. Trace0(ENTER1, "Entering _T_NonQueryTimer()");
  1285. pTimer = CONTAINING_RECORD( pvContext, IGMP_TIMER_ENTRY, Context);
  1286. pite = CONTAINING_RECORD( pTimer, IF_TABLE_ENTRY, NonQueryTimer);
  1287. //
  1288. // make sure that the interface is activated
  1289. //
  1290. if (!(IS_IF_ACTIVATED(pite))) {
  1291. /*Trace2(ERR, "T_NonQueryTimer() called for inactive IfIndex(%0x), IfType(%d)",
  1292. pite->IfIndex, pite->IfType);
  1293. IgmpAssertOnError(FALSE);*/
  1294. return 0;
  1295. }
  1296. Trace2(TIMER, "Processing T_NonQueryTimer() for IfIndex(%0x), IfType(%d)",
  1297. pite->IfIndex, pite->IfType);
  1298. //
  1299. // if non-querier, then queue work item to become querier
  1300. //
  1301. if (!IS_QUERIER(pite)) {
  1302. QueueIgmpWorker(WF_BecomeQuerier, (PVOID)(DWORD_PTR)pite->IfIndex);
  1303. Trace1(WORKER, "_T_NonQueryTimer queued _WF_BecomeQuerier on If:%0x",
  1304. pite->IfIndex);
  1305. }
  1306. Trace0(LEAVE1, "Leaving _T_NonQueryTimer()");
  1307. return 0;
  1308. }
  1309. VOID
  1310. WF_BecomeQuerier(
  1311. PVOID pvIfIndex
  1312. )
  1313. //Called by T_NonQueryTimer
  1314. {
  1315. ChangeQuerierState(PtrToUlong(pvIfIndex), QUERIER_FLAG, 0, 0, 0);
  1316. }
  1317. VOID
  1318. WF_BecomeNonQuerier(
  1319. PVOID pvContext
  1320. )
  1321. {
  1322. PQUERIER_CONTEXT pwi = (PQUERIER_CONTEXT)pvContext;
  1323. ChangeQuerierState(pwi->IfIndex, NON_QUERIER_FLAG, pwi->QuerierIpAddr,
  1324. pwi->NewRobustnessVariable, pwi->NewGenQueryInterval);
  1325. IGMP_FREE(pwi);
  1326. }
  1327. VOID
  1328. ChangeQuerierState(
  1329. DWORD IfIndex,
  1330. DWORD Flag, //QUERIER_CHANGE_V1_ONLY,QUERIER_FLAG,NON_QUERIER_FLAG
  1331. DWORD QuerierIpAddr, // only when changing from querier-->nonquerier
  1332. DWORD NewRobustnessVariable, //only for v3:querier->non-querier
  1333. DWORD NewGenQueryInterval //only for v3:querier->non-querier
  1334. )
  1335. {
  1336. PIF_TABLE_ENTRY pite;
  1337. BOOL bPrevCanAddGroupsToMgm;
  1338. if (!EnterIgmpWorker()) return;
  1339. Trace0(ENTER1, "Entering _ChangeQuerierState");
  1340. ACQUIRE_IF_LOCK_EXCLUSIVE(IfIndex, "_ChangeQuerierState");
  1341. BEGIN_BREAKOUT_BLOCK1 {
  1342. //
  1343. // retrieve the interface entry
  1344. //
  1345. pite = GetIfByIndex(IfIndex);
  1346. //
  1347. // return error if interface does not exist, or it is not activated
  1348. // or is already in that state
  1349. //
  1350. if ( (pite == NULL)||(!IS_IF_ACTIVATED(pite)) ) {
  1351. Trace1(ERR,
  1352. "Warning: worker fn could not change querier state for If:%0x",
  1353. IfIndex
  1354. );
  1355. GOTO_END_BLOCK1;
  1356. }
  1357. //
  1358. // if it is supposed to be a V1 interface, make sure that it is
  1359. //
  1360. if ( (Flag & QUERIER_CHANGE_V1_ONLY)
  1361. && (!IS_PROTOCOL_TYPE_IGMPV1(pite)) )
  1362. {
  1363. GOTO_END_BLOCK1;
  1364. }
  1365. bPrevCanAddGroupsToMgm = CAN_ADD_GROUPS_TO_MGM(pite);
  1366. //
  1367. // changing from non querier to querier
  1368. //
  1369. if (Flag & QUERIER_FLAG) {
  1370. // if already querier, then done
  1371. if (IS_QUERIER(pite))
  1372. GOTO_END_BLOCK1;
  1373. SET_QUERIER_STATE_QUERIER(pite->Info.QuerierState);
  1374. Trace2(QUERIER,
  1375. "NonQuerier --> Querier. IfIndex(%0x), IpAddr(%d.%d.%d.%d) ",
  1376. IfIndex, PRINT_IPADDR(pite->IpAddr)
  1377. );
  1378. // copy back the old robustness, genquery, etc values. for v3
  1379. // interface
  1380. if (IS_IF_VER3(pite)) {
  1381. pite->Config.RobustnessVariable = pite->Config.RobustnessVariableOld;
  1382. pite->Config.GenQueryInterval = pite->Config.GenQueryIntervalOld;
  1383. pite->Config.OtherQuerierPresentInterval
  1384. = pite->Config.OtherQuerierPresentIntervalOld;
  1385. pite->Config.GroupMembershipTimeout = pite->Config.GroupMembershipTimeoutOld;
  1386. }
  1387. // register all groups with MGM if I wasnt doing earlier
  1388. if (CAN_ADD_GROUPS_TO_MGM(pite) && !bPrevCanAddGroupsToMgm) {
  1389. RefreshMgmIgmprtrGroups(pite, ADD_FLAG);
  1390. Trace1(MGM,
  1391. "Igmp Router start propagating groups to MGM on If:%0x",
  1392. pite->IfIndex
  1393. );
  1394. }
  1395. // I am the querier again. Set the addr in Info.
  1396. InterlockedExchange(&pite->Info.QuerierIpAddr, pite->IpAddr);
  1397. // update the time when querier was last changed
  1398. pite->Info.LastQuerierChangeTime = GetCurrentIgmpTime();
  1399. //
  1400. // set the GenQuery timer and remove NonQueryTimer if set.
  1401. //
  1402. ACQUIRE_TIMER_LOCK("_ChangeQuerierState");
  1403. #if DEBUG_TIMER_TIMERID
  1404. SET_TIMER_ID(&pite->QueryTimer, 220, pite->IfIndex, 0, 0);
  1405. #endif
  1406. if (!IS_TIMER_ACTIVE(pite->QueryTimer))
  1407. InsertTimer(&pite->QueryTimer, pite->Config.GenQueryInterval, FALSE, DBG_Y);
  1408. if (IS_TIMER_ACTIVE(pite->NonQueryTimer))
  1409. RemoveTimer(&pite->NonQueryTimer, DBG_Y);
  1410. RELEASE_TIMER_LOCK("_ChangeQuerierState");
  1411. // send general query
  1412. SEND_GEN_QUERY(pite);
  1413. }
  1414. //
  1415. // changing from querier to non querier
  1416. //
  1417. else {
  1418. LONGLONG llCurTime = GetCurrentIgmpTime();
  1419. BOOL bPrevAddGroupsToMgm;
  1420. // if already non querier, then done
  1421. if (!IS_QUERIER(pite))
  1422. GOTO_END_BLOCK1;
  1423. // change querier state
  1424. SET_QUERIER_STATE_NON_QUERIER(pite->Info.QuerierState);
  1425. Trace2(QUERIER,
  1426. "Querier --> NonQuerier. IfIndex(%0x), IpAddr(%d.%d.%d.%d) ",
  1427. IfIndex, PRINT_IPADDR(pite->IpAddr)
  1428. );
  1429. InterlockedExchange(&pite->Info.QuerierIpAddr, QuerierIpAddr);
  1430. //
  1431. // if previously, groups were propagated to MGM, but should
  1432. // not be propagated now, then deregister the groups from MGM
  1433. //
  1434. if (!CAN_ADD_GROUPS_TO_MGM(pite) && bPrevCanAddGroupsToMgm) {
  1435. RefreshMgmIgmprtrGroups(pite, DELETE_FLAG);
  1436. Trace1(MGM,
  1437. "Igmp Router stop propagating groups to MGM on If:%0x",
  1438. pite->IfIndex
  1439. );
  1440. }
  1441. if (IS_IF_VER3(pite)) {
  1442. if (NewRobustnessVariable==0)
  1443. NewRobustnessVariable = pite->Config.RobustnessVariableOld;
  1444. if (NewGenQueryInterval==0)
  1445. NewGenQueryInterval = pite->Config.GenQueryIntervalOld;
  1446. if (pite->Config.GenQueryMaxResponseTime > NewGenQueryInterval)
  1447. NewGenQueryInterval = pite->Config.GenQueryIntervalOld;
  1448. if (NewRobustnessVariable != pite->Config.RobustnessVariable
  1449. || NewGenQueryInterval != pite->Config.RobustnessVariable
  1450. ) {
  1451. pite->Config.RobustnessVariable = NewRobustnessVariable;
  1452. pite->Config.GenQueryInterval = NewGenQueryInterval;
  1453. pite->Config.OtherQuerierPresentInterval
  1454. = NewRobustnessVariable*NewGenQueryInterval
  1455. + (pite->Config.GenQueryMaxResponseTime)/2;
  1456. pite->Config.GroupMembershipTimeout = NewRobustnessVariable*NewGenQueryInterval
  1457. + pite->Config.GenQueryMaxResponseTime;
  1458. Trace3(CONFIG,
  1459. "Querier->NonQuerier: Robustness:%d GenQueryInterval:%d "
  1460. "GroupMembershipTimeout:%d. ",
  1461. NewRobustnessVariable, NewGenQueryInterval/1000,
  1462. pite->Config.GroupMembershipTimeout/1000
  1463. );
  1464. }
  1465. }
  1466. //
  1467. // set other querier present timer, and remove querier timer if not
  1468. // in startup query Mode
  1469. //
  1470. ACQUIRE_TIMER_LOCK("_ChangeQuerierState");
  1471. #if DEBUG_TIMER_TIMERID
  1472. SET_TIMER_ID(&pite->NonQueryTimer, 210, pite->IfIndex, 0, 0);
  1473. #endif
  1474. if (!IS_TIMER_ACTIVE(pite->NonQueryTimer)) {
  1475. InsertTimer(&pite->NonQueryTimer,
  1476. pite->Config.OtherQuerierPresentInterval, TRUE, DBG_Y);
  1477. }
  1478. if (IS_TIMER_ACTIVE(pite->QueryTimer) &&
  1479. (pite->Info.StartupQueryCountCurrent<=0) )
  1480. {
  1481. RemoveTimer(&pite->QueryTimer, DBG_Y);
  1482. }
  1483. pite->Info.QuerierPresentTimeout = llCurTime
  1484. + CONFIG_TO_SYSTEM_TIME(pite->Config.OtherQuerierPresentInterval);
  1485. RELEASE_TIMER_LOCK("_ChangeQuerierState");
  1486. }
  1487. } END_BREAKOUT_BLOCK1;
  1488. RELEASE_IF_LOCK_EXCLUSIVE(IfIndex, "_ChangeQuerierState");
  1489. Trace0(LEAVE1, "leaving _ChangeQuerierState\n");
  1490. LeaveIgmpWorker();
  1491. return;
  1492. }//end _ChangeQuerierState
  1493. //------------------------------------------------------------------------------
  1494. // _WF_TimerProcessing
  1495. //------------------------------------------------------------------------------
  1496. VOID
  1497. WF_TimerProcessing (
  1498. PVOID pvContext
  1499. )
  1500. {
  1501. DWORD IfIndex;
  1502. PWORK_CONTEXT pWorkContext = (PWORK_CONTEXT)pvContext;
  1503. PIF_TABLE_ENTRY pite;
  1504. DWORD Error = NO_ERROR;
  1505. DWORD Group = pWorkContext->Group;
  1506. BOOL bCreate;
  1507. PRAS_TABLE prt;
  1508. PRAS_TABLE_ENTRY prte;
  1509. PGROUP_TABLE_ENTRY pge;
  1510. PGI_ENTRY pgie; //group interface entry
  1511. enum {
  1512. NO_LOCK=0x0,
  1513. IF_LOCK=0x1,
  1514. RAS_LOCK=0x2,
  1515. GROUP_LOCK=0x4,
  1516. TIMER_LOCK=0x8
  1517. } ExitLockRelease;
  1518. ExitLockRelease = NO_LOCK;
  1519. //todo: remove the read lock get/release
  1520. //used only for DELETE_MEMBERSHIP
  1521. #define RETURN_FROM_TIMER_PROCESSING() {\
  1522. IGMP_FREE(pvContext); \
  1523. if (ExitLockRelease&IF_LOCK) \
  1524. RELEASE_IF_LOCK_SHARED(IfIndex, "_WF_TimerProcessing"); \
  1525. if (ExitLockRelease&GROUP_LOCK) \
  1526. RELEASE_GROUP_LOCK(Group, "_WF_TimerProcessing"); \
  1527. if (ExitLockRelease&TIMER_LOCK) \
  1528. RELEASE_TIMER_LOCK("_WF_TimerProcessing"); \
  1529. Trace0(LEAVE1, "Leaving _WF_TimerProcessing()\n");\
  1530. LeaveIgmpWorker();\
  1531. return;\
  1532. }
  1533. if (!EnterIgmpWorker()) { return; }
  1534. Trace0(ENTER1, "Entering _WF_TimerProcessing");
  1535. // take shared interface lock
  1536. IfIndex = pWorkContext->IfIndex;
  1537. ACQUIRE_IF_LOCK_SHARED(IfIndex, "_WF_TimerProcessing");
  1538. ExitLockRelease |= IF_LOCK;
  1539. BEGIN_BREAKOUT_BLOCK1 {
  1540. //
  1541. // retrieve the interface
  1542. //
  1543. pite = GetIfByIndex(IfIndex);
  1544. if (pite == NULL) {
  1545. Trace1(IF, "_WF_TimerProcessing: interface %d not found", IfIndex);
  1546. Error = ERROR_CAN_NOT_COMPLETE;
  1547. GOTO_END_BLOCK1;
  1548. }
  1549. //
  1550. // exit quitely if the interface is not activated
  1551. //
  1552. if ( !(IS_IF_ACTIVATED(pite)) ) {
  1553. Trace1(ERR, "Trying to send packet on inactive interface(%d)",
  1554. pite->IfIndex);
  1555. Error = ERROR_CAN_NOT_COMPLETE;
  1556. GOTO_END_BLOCK1;
  1557. }
  1558. switch (pWorkContext->WorkType) {
  1559. //-----------------------------------------
  1560. // GENERAL QUERY
  1561. //-----------------------------------------
  1562. case MSG_GEN_QUERY:
  1563. {
  1564. Trace2(TIMER,
  1565. "Timer fired leads to General-query being sent on If(%0x)"
  1566. "Group(%d.%d.%d.%d)",
  1567. pite->IfIndex, PRINT_IPADDR(pWorkContext->Group));
  1568. SEND_GEN_QUERY(pite);
  1569. break;
  1570. }
  1571. //-----------------------------------------
  1572. // GROUP SPECIFIC QUERY
  1573. //-----------------------------------------
  1574. case MSG_GROUP_QUERY_V2 :
  1575. {
  1576. Trace2(TIMER,
  1577. "Timer fired leads to group query being sent on If(%0x)"
  1578. "Group(%d.%d.%d.%d)",
  1579. pite->IfIndex, PRINT_IPADDR(pWorkContext->Group)
  1580. );
  1581. SEND_GROUP_QUERY_V2(pite, pWorkContext->Group);
  1582. break;
  1583. }
  1584. //
  1585. // MEMBERSHIP TIMED OUT
  1586. //
  1587. case DELETE_MEMBERSHIP:
  1588. {
  1589. //
  1590. // Lock the group table bucket
  1591. //
  1592. ACQUIRE_GROUP_LOCK(Group, "_WF_TimerProcessing");
  1593. ExitLockRelease |= GROUP_LOCK;
  1594. //
  1595. // find the group entry. If entry not found then ignore the timer
  1596. //
  1597. pge = GetGroupFromGroupTable(Group, NULL, 0); //llCurTime not req
  1598. if (pge==NULL) {
  1599. RETURN_FROM_TIMER_PROCESSING();
  1600. }
  1601. //
  1602. // find the GI entry. If GI entry does not exist or has deletedFlag
  1603. // or is static group, then ignore the timer
  1604. //
  1605. pgie = GetGIFromGIList(pge, pite, pWorkContext->NHAddr, FALSE, NULL, 0);
  1606. if ( (pgie==NULL)||(pgie->bStaticGroup) ) {
  1607. RETURN_FROM_TIMER_PROCESSING();
  1608. }
  1609. // gi entry might be deleted here
  1610. if (pgie->Version==3 && pgie->FilterType==EXCLUSION) {
  1611. if (pgie->bStaticGroup) {
  1612. PLIST_ENTRY pHead, ple;
  1613. //
  1614. // remove all sources in exclusion list
  1615. //
  1616. pHead = &pgie->V3ExclusionList;
  1617. for (ple=pHead->Flink; ple!=pHead; ) {
  1618. PGI_SOURCE_ENTRY pSourceEntry;
  1619. pSourceEntry = CONTAINING_RECORD(ple, GI_SOURCE_ENTRY,
  1620. LinkSources);
  1621. ple = ple->Flink;
  1622. // dont have to call mgm as it will remain in -ve mfe
  1623. if (!pSourceEntry->bStaticSource) {
  1624. RemoveEntryList(&pSourceEntry->LinkSources);
  1625. IGMP_FREE(pSourceEntry);
  1626. }
  1627. }
  1628. break;
  1629. }
  1630. Trace2(TIMER,
  1631. "Timer fired leads to group filter mode change If(%0x) "
  1632. "Group(%d.%d.%d.%d)",
  1633. pite->IfIndex, PRINT_IPADDR(pWorkContext->Group)
  1634. );
  1635. ChangeGroupFilterMode(pgie, INCLUSION);
  1636. }
  1637. else if (pgie->Version!=3) {
  1638. if (pgie->bStaticGroup)
  1639. break;
  1640. Trace2(TIMER,
  1641. "Timer fired leads to membership being timed out If(%0x) "
  1642. "Group(%d.%d.%d.%d)",
  1643. pite->IfIndex, PRINT_IPADDR(pWorkContext->Group)
  1644. );
  1645. //
  1646. // finally delete the entry
  1647. //
  1648. Error = DeleteGIEntry(pgie, TRUE, TRUE); //updateStats, CallMgm
  1649. }
  1650. break;
  1651. } //end case:DELETE_MEMBERSHIP
  1652. //
  1653. // SOURCE TIMED OUT
  1654. //
  1655. case DELETE_SOURCE:
  1656. case MSG_SOURCES_QUERY:
  1657. case MSG_GROUP_QUERY_V3:
  1658. case SHIFT_TO_V3:
  1659. case MOVE_SOURCE_TO_EXCL:
  1660. {
  1661. PGI_SOURCE_ENTRY pSourceEntry;
  1662. if ((pWorkContext->WorkType)==DELETE_SOURCE){
  1663. Trace3(TIMER,
  1664. "Timer fired leads to membership being timed out If(%0x) "
  1665. "Group(%d.%d.%d.%d) Source(%d.%d.%d.%d)",
  1666. pite->IfIndex, PRINT_IPADDR(pWorkContext->Group),
  1667. PRINT_IPADDR(pWorkContext->Source)
  1668. );
  1669. }
  1670. else if ((pWorkContext->WorkType)==MSG_SOURCES_QUERY){
  1671. Trace2(TIMER,
  1672. "Timer fired leads to sources specific msg being sent If(%0x) "
  1673. "Group(%d.%d.%d.%d)",
  1674. pite->IfIndex, PRINT_IPADDR(pWorkContext->Group));
  1675. }
  1676. else if ((pWorkContext->WorkType)==MSG_GROUP_QUERY_V3){
  1677. Trace2(TIMER,
  1678. "Timer fired leads to group query being sent on If(%0x) "
  1679. "Group(%d.%d.%d.%d)",
  1680. pite->IfIndex, PRINT_IPADDR(pWorkContext->Group)
  1681. );
  1682. }
  1683. else if ((pWorkContext->WorkType)==SHIFT_TO_V3){
  1684. Trace2(TIMER,
  1685. "Timer fired leads to group shifting to v3 mode If(%0x) "
  1686. "Group(%d.%d.%d.%d)",
  1687. pite->IfIndex, PRINT_IPADDR(pWorkContext->Group)
  1688. );
  1689. }
  1690. else if (pWorkContext->WorkType==MOVE_SOURCE_TO_EXCL){
  1691. Trace3(TIMER,
  1692. "Timer fired leads to source shifting to exclList If(%0x) "
  1693. "Group(%d.%d.%d.%d) Source(%d.%d.%d.%d)",
  1694. pite->IfIndex, PRINT_IPADDR(pWorkContext->Group),
  1695. PRINT_IPADDR(pWorkContext->Source)
  1696. );
  1697. }
  1698. //
  1699. // Lock the group table bucket
  1700. //
  1701. ACQUIRE_GROUP_LOCK(Group, "_WF_TimerProcessing");
  1702. ExitLockRelease |= GROUP_LOCK;
  1703. //
  1704. // find the group entry. If entry not found then ignore the timer
  1705. //
  1706. pge = GetGroupFromGroupTable(Group, NULL, 0); //llCurTime not req
  1707. if (pge==NULL) {
  1708. RETURN_FROM_TIMER_PROCESSING();
  1709. }
  1710. //
  1711. // find the GI entry. If GI entry does not exist or has deletedFlag
  1712. // or is static group, then ignore the timer
  1713. //
  1714. pgie = GetGIFromGIList(pge, pite, pWorkContext->NHAddr, FALSE, NULL, 0);
  1715. if ( (pgie==NULL)||(pgie->bStaticGroup) ) {
  1716. RETURN_FROM_TIMER_PROCESSING();
  1717. }
  1718. ACQUIRE_TIMER_LOCK("_WF_Timer_Processing");
  1719. ExitLockRelease |= TIMER_LOCK;
  1720. //
  1721. // if changeSourceMode to excl, but filtertype is not excl
  1722. // then delete the source
  1723. //
  1724. if ( (pWorkContext->WorkType==MOVE_SOURCE_TO_EXCL)
  1725. && (pgie->FilterType != EXCLUSION)
  1726. ) {
  1727. pWorkContext->WorkType = DELETE_SOURCE;
  1728. Trace2(TIMER, "DeleteSource instead of moving to excl "
  1729. "Group(%d.%d.%d.%d) Source(%d.%d.%d.%d)",
  1730. PRINT_IPADDR(pWorkContext->Group),
  1731. PRINT_IPADDR(pWorkContext->Source)
  1732. );
  1733. }
  1734. if ((pWorkContext->WorkType)==DELETE_SOURCE){
  1735. //
  1736. // get the source entry from inclusion list
  1737. //
  1738. pSourceEntry = GetSourceEntry(pgie, pWorkContext->Source,
  1739. INCLUSION, NULL, 0, 0);
  1740. if (pSourceEntry==NULL) {
  1741. Trace1(TIMER, "Source %d.%d.%d.%d not found",
  1742. PRINT_IPADDR(pWorkContext->Source));
  1743. RETURN_FROM_TIMER_PROCESSING();
  1744. }
  1745. if (!pSourceEntry->bStaticSource) {
  1746. DeleteSourceEntry(pSourceEntry, TRUE); //process mgm
  1747. if (pgie->NumSources==0) {
  1748. DeleteGIEntry(pgie, TRUE, TRUE);
  1749. }
  1750. }
  1751. }
  1752. else if ((pWorkContext->WorkType)==MSG_SOURCES_QUERY) {
  1753. SEND_SOURCES_QUERY(pgie);
  1754. }
  1755. else if ((pWorkContext->WorkType)==MSG_GROUP_QUERY_V3) {
  1756. SendV3GroupQuery(pgie);
  1757. }
  1758. else if ((pWorkContext->WorkType)==SHIFT_TO_V3) {
  1759. // make sure that version has not changed
  1760. if (pgie->Version != 3 && IS_IF_VER3(pite)) {
  1761. // shift to v3 exclusion mode
  1762. // membership timer already running
  1763. pgie->Version = 3;
  1764. pgie->FilterType = EXCLUSION;
  1765. // dont have to join to mgm as already joined in v1,v2
  1766. }
  1767. }
  1768. else if (pWorkContext->WorkType==MOVE_SOURCE_TO_EXCL) {
  1769. pSourceEntry = GetSourceEntry(pgie, pWorkContext->Source,
  1770. INCLUSION, NULL, 0, 0);
  1771. if (pSourceEntry==NULL) {
  1772. Trace1(TIMER, "Source %d.%d.%d.%d not found",
  1773. PRINT_IPADDR(pWorkContext->Source));
  1774. RETURN_FROM_TIMER_PROCESSING();
  1775. }
  1776. if (pSourceEntry->bInclusionList==TRUE) {
  1777. if (pSourceEntry==NULL)
  1778. RETURN_FROM_TIMER_PROCESSING();
  1779. ChangeSourceFilterMode(pgie, pSourceEntry);
  1780. }
  1781. }
  1782. break;
  1783. } //end case:DELETE_SOURCE,MSG_SOURCES_QUERY
  1784. } //end switch There should not be any code between here and
  1785. //endBreakout block
  1786. } END_BREAKOUT_BLOCK1;
  1787. RETURN_FROM_TIMER_PROCESSING();
  1788. return;
  1789. } //end _WF_TimerProcessing
  1790. //------------------------------------------------------------------------------
  1791. // DeleteRasClient
  1792. //
  1793. // Takes the if_group list lock and deletes all the GI entries associated with
  1794. // the ras client.
  1795. // Then takes write lock on the ras table and decrements the refCount. The
  1796. // ras table and interface entries are deleted if the deleted flag is set on pite.
  1797. // also releases the ras client from MGM.
  1798. //
  1799. // Queued by:
  1800. // DisconnectRasClient(), DeActivateInterfaceComplete() for ras server
  1801. // Locks:
  1802. // Initially runs in IF_GROUP_LIST_LOCK
  1803. // then runs in exclusive ras table lock.
  1804. // assumes if exclusive lock
  1805. // May call: _CompleteIfDeletion()
  1806. //------------------------------------------------------------------------------
  1807. VOID
  1808. DeleteRasClient (
  1809. PRAS_TABLE_ENTRY prte
  1810. )
  1811. {
  1812. PLIST_ENTRY pHead, ple;
  1813. PGI_ENTRY pgie;
  1814. PIF_TABLE_ENTRY pite = prte->IfTableEntry;
  1815. PRAS_TABLE prt = prte->IfTableEntry->pRasTable;
  1816. DWORD Error = NO_ERROR, IfIndex=pite->IfIndex;
  1817. //
  1818. // take exclusive lock on the If_Group List and remove all timers
  1819. //
  1820. ACQUIRE_IF_GROUP_LIST_LOCK(IfIndex, "_WF_DeleteRasClient");
  1821. //
  1822. // Remove all timers associtated with that ras client's GI list
  1823. //
  1824. ACQUIRE_TIMER_LOCK("_WF_DeleteRasClient");
  1825. pHead = &prte->ListOfSameClientGroups;
  1826. DeleteAllTimers(pHead, RAS_CLIENT);
  1827. RELEASE_TIMER_LOCK("_WF_DeleteRasClient");
  1828. RELEASE_IF_GROUP_LIST_LOCK(IfIndex, "_WF_DeleteRasClient");
  1829. //
  1830. // revisit the list and delete all GI entries. Need to take
  1831. // exclusive lock on the group bucket before deleting the GI entry
  1832. // No need to lock the If-Group list as no one can access it anymore
  1833. //
  1834. for (ple=pHead->Flink; ple!=pHead; ) {
  1835. DWORD dwGroup;
  1836. pgie = CONTAINING_RECORD(ple, GI_ENTRY, LinkBySameClientGroups);
  1837. ple=ple->Flink;
  1838. dwGroup = pgie->pGroupTableEntry->Group;
  1839. ACQUIRE_GROUP_LOCK(dwGroup, "_WF_DeleteRasClient");
  1840. DeleteGIEntryFromIf(pgie);
  1841. RELEASE_GROUP_LOCK(dwGroup, "_WF_DeleteRasClient");
  1842. }
  1843. //
  1844. // Take exclusive lock on the interface. If deleted flag set on interface
  1845. // and refcount==0 then delete the ras table and pite, else just decrement
  1846. // the refcount
  1847. //
  1848. // decrement Refcount
  1849. prt->RefCount --;
  1850. //
  1851. // if deleted flag set and Refcount ==0 then delete Ras server completely
  1852. //
  1853. if ( (pite->Status&IF_DELETED_FLAG) &&(prt->RefCount==0) ){
  1854. CompleteIfDeletion(pite);
  1855. }
  1856. IGMP_FREE(prte);
  1857. return;
  1858. } //end _WF_DeleteRasClient
  1859. //------------------------------------------------------------------------------
  1860. // _WF_CompleteIfDeactivateDelete
  1861. //
  1862. // Completes deactivation an activated interface.
  1863. //
  1864. // Locking:
  1865. // does not require any lock on IfTable, as it is already removed from
  1866. // global interface lists.
  1867. // takes lock on Sockets list, as socket is getting deactivated
  1868. // Calls:
  1869. // DeActivateInterfaceComplete(). That function will also call
  1870. // _CompleteIfDeletion if the delete flag is set.
  1871. // Called by:
  1872. // _DeleteIfEntry() after it has called _DeActivationDeregisterFromMgm
  1873. // _UnbindIfEntry() after it has called _DeActivateInterfaceInitial
  1874. // _DisableIfEntry() after it has called _DeActivateInterfaceInitial
  1875. //------------------------------------------------------------------------------
  1876. VOID
  1877. CompleteIfDeactivateDelete (
  1878. PIF_TABLE_ENTRY pite
  1879. )
  1880. {
  1881. DWORD IfIndex = pite->IfIndex;
  1882. Trace1(ENTER1, "Entering _WF_CompleteIfDeactivateDelete(%d)", IfIndex);
  1883. ACQUIRE_SOCKETS_LOCK_EXCLUSIVE("_WF_CompleteIfDeactivateDelete");
  1884. DeActivateInterfaceComplete(pite);
  1885. RELEASE_SOCKETS_LOCK_EXCLUSIVE("_WF_CompleteIfDeactivateDelete");
  1886. // dont have to call _CompleteIfDeletion as it will be called in
  1887. // DeactivateInterface() as the delete flag is set.
  1888. Trace1(LEAVE1, "Leaving _WF_CompleteIfDeactivateDelete(%d)", IfIndex);
  1889. return;
  1890. } //end _WF_CompleteIfDeactivateDelete