Leaked source code of windows server 2003
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.

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