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.

3854 lines
121 KiB

  1. /*++
  2. Copyright (c) 1990-2000 Microsoft Corporation
  3. Module Name:
  4. igmp.c - IP multicast routines.
  5. Abstract:
  6. This file contains all the routines related to the Internet Group Management
  7. Protocol (IGMP).
  8. Author:
  9. [Environment:]
  10. kernel mode only
  11. [Notes:]
  12. optional-notes
  13. Revision History:
  14. Feb. 2000 - upgraded to IGMPv3 (DThaler)
  15. --*/
  16. #include "precomp.h"
  17. #include "mdlpool.h"
  18. #include "igmp.h"
  19. #include "icmp.h"
  20. #include "ipxmit.h"
  21. #include "iproute.h"
  22. #if GPC
  23. #include "qos.h"
  24. #include "traffic.h"
  25. #include "gpcifc.h"
  26. #include "ntddtc.h"
  27. extern GPC_HANDLE hGpcClient[];
  28. extern ULONG GpcCfCounts[];
  29. extern GPC_EXPORTED_CALLS GpcEntries;
  30. extern ULONG GPCcfInfo;
  31. #endif
  32. extern uint DisableUserTOS;
  33. extern uint DefaultTOS;
  34. #define IGMP_QUERY 0x11 // Membership query
  35. #define IGMP_REPORT_V1 0x12 // Version 1 membership report
  36. #define IGMP_REPORT_V2 0x16 // Version 2 membership report
  37. #define IGMP_LEAVE 0x17 // Leave Group
  38. #define IGMP_REPORT_V3 0x22 // Version 3 membership report
  39. // IGMPv3 Group Record Types
  40. #define MODE_IS_INCLUDE 1
  41. #define MODE_IS_EXCLUDE 2
  42. #define CHANGE_TO_INCLUDE_MODE 3
  43. #define CHANGE_TO_EXCLUDE_MODE 4
  44. #define ALLOW_NEW_SOURCES 5
  45. #define BLOCK_OLD_SOURCES 6
  46. #define ALL_HOST_MCAST 0x010000E0
  47. #define IGMPV3_RTRS_MCAST 0x160000E0
  48. #define UNSOLICITED_REPORT_INTERVAL 2 // used when sending a report after a
  49. // mcast group has been added. The
  50. // report is sent at a interval of
  51. // 0 msecs to 1 sec. IGMPv3 spec
  52. // changed this from previous value
  53. // of 10 seconds (value 20)
  54. #define DEFAULT_ROBUSTNESS 2
  55. #define MAX_ROBUSTNESS 7
  56. static uchar g_IgmpRobustness = DEFAULT_ROBUSTNESS;
  57. //
  58. // The following values are used to initialize counters that keep time in
  59. // 1/2 a sec.
  60. //
  61. #define DEFAULT_QUERY_RESP_INTERVAL 100 // 10 seconds, note different units from other defines
  62. #define DEFAULT_QUERY_INTERVAL 250 // 125 secs, per spec
  63. // Macro to test whether a source passes the network-layer filter
  64. #define IS_SOURCE_ALLOWED(Grp, Src) \
  65. (((Src)->isa_xrefcnt != (Grp)->iga_grefcnt) || ((Src)->isa_irefcnt != 0))
  66. // Macro to test whether a group should pass the link-layer filter
  67. #define IS_GROUP_ALLOWED(Grp) \
  68. (BOOLEAN) (((Grp)->iga_grefcnt != 0) || ((Grp)->iga_srclist != NULL))
  69. #define IS_SOURCE_DELETABLE(Src) \
  70. (((Src)->isa_irefcnt == 0) && ((Src)->isa_xrefcnt == 0) \
  71. && ((Src)->isa_xmitleft==0) && ((Src)->isa_csmarked == 0))
  72. #define IS_GROUP_DELETABLE(Grp) \
  73. (!IS_GROUP_ALLOWED(Grp) && ((Grp)->iga_xmitleft == 0) \
  74. && ((Grp)->iga_resptimer == 0))
  75. int RandomValue;
  76. int Seed;
  77. // Structure of an IGMPv1/v2 header.
  78. typedef struct IGMPHeader {
  79. uchar igh_vertype; // Type of igmp message
  80. uchar igh_rsvd; // max. resp. time for igmpv2 query;
  81. // max. resp. code for igmpv3 query;
  82. // will be 0 for other messages
  83. ushort igh_xsum;
  84. IPAddr igh_addr;
  85. } IGMPHeader;
  86. #pragma warning(push)
  87. #pragma warning(disable:4200)
  88. typedef struct IGMPv3GroupRecord {
  89. uchar igr_type;
  90. uchar igr_datalen;
  91. ushort igr_numsrc;
  92. IPAddr igr_addr;
  93. IPAddr igr_srclist[0];
  94. } IGMPv3GroupRecord;
  95. #pragma warning(pop)
  96. #define RECORD_SIZE(numsrc, datalen) (sizeof(IGMPv3GroupRecord) + (numsrc) * sizeof(IPAddr) + (datalen * sizeof(ulong)))
  97. typedef struct IGMPv3RecordQueueEntry {
  98. struct IGMPv3RecordQueueEntry *i3qe_next;
  99. IGMPv3GroupRecord *i3qe_buff;
  100. uint i3qe_size;
  101. } IGMPv3RecordQueueEntry;
  102. typedef struct IGMPReportQueueEntry {
  103. struct IGMPReportQueueEntry *iqe_next;
  104. IGMPHeader *iqe_buff;
  105. uint iqe_size;
  106. IPAddr iqe_dest;
  107. } IGMPReportQueueEntry;
  108. typedef struct IGMPv3ReportHeader {
  109. uchar igh_vertype; // Type of igmp message
  110. uchar igh_rsvd;
  111. ushort igh_xsum;
  112. ushort igh_rsvd2;
  113. ushort igh_numrecords;
  114. } IGMPv3ReportHeader;
  115. #pragma warning(push)
  116. #pragma warning(disable:4200) // nonstandard extension used: zero sized array
  117. typedef struct IGMPv3QueryHeader {
  118. uchar igh_vertype; // Type of igmp message
  119. union {
  120. uchar igh_maxresp; // will be 0 for igmpv1 messages
  121. struct {
  122. uchar igh_mrcmant : 4; // MaxRespCode mantissa
  123. uchar igh_mrcexp : 3; // MaxRespCode exponent
  124. uchar igh_mrctype : 1; // MaxRespCode type
  125. };
  126. };
  127. ushort igh_xsum;
  128. IPAddr igh_addr;
  129. uchar igh_qrv : 3;
  130. uchar igh_s : 1;
  131. uchar igh_rsvd2 : 4;
  132. uchar igh_qqic;
  133. ushort igh_numsrc;
  134. IPAddr igh_srclist[0];
  135. } IGMPv3QueryHeader;
  136. #pragma warning(pop)
  137. #define IGMPV3_QUERY_SIZE(NumSrc) \
  138. (sizeof(IGMPv3QueryHeader) + (NumSrc) * sizeof(IPAddr))
  139. #define TOTAL_HEADER_LENGTH \
  140. (sizeof(IPHeader) + ROUTER_ALERT_SIZE + sizeof(IGMPv3ReportHeader))
  141. #define RECORD_MTU(NTE) \
  142. (4 * (((NTE)->nte_if->if_mtu - TOTAL_HEADER_LENGTH) / 4))
  143. typedef struct IGMPBlockStruct {
  144. struct IGMPBlockStruct *ibs_next;
  145. CTEBlockStruc ibs_block;
  146. } IGMPBlockStruct;
  147. void *IGMPProtInfo;
  148. IGMPBlockStruct *IGMPBlockList;
  149. uchar IGMPBlockFlag;
  150. extern BOOLEAN CopyToNdisSafe(PNDIS_BUFFER DestBuf, PNDIS_BUFFER * ppNextBuf,
  151. uchar * SrcBuf, uint Size, uint * StartOffset);
  152. extern NDIS_HANDLE BufferPool;
  153. DEFINE_LOCK_STRUCTURE(IGMPLock)
  154. extern ProtInfo *RawPI; // Raw IP protinfo
  155. //
  156. // the global address for unnumbered interfaces
  157. //
  158. extern IPAddr g_ValidAddr;
  159. extern IP_STATUS IPCopyOptions(uchar *, uint, IPOptInfo *);
  160. extern void IPInitOptions(IPOptInfo *);
  161. extern void *IPRegisterProtocol(uchar Protocol, void *RcvHandler,
  162. void *XmitHandler, void *StatusHandler,
  163. void *RcvCmpltHandler, void *PnPHandler,
  164. void *ElistHandler);
  165. uint IGMPInit(void);
  166. //
  167. // All of the init code can be discarded
  168. //
  169. #ifdef ALLOC_PRAGMA
  170. #pragma alloc_text(INIT, IGMPInit)
  171. #endif // ALLOC_PRAGMA
  172. //** GetIGMPBuffer - Get an IGMP buffer, and allocate an NDIS_BUFFER that maps it.
  173. //
  174. // A routine to allocate an IGMP buffer and map an NDIS_BUFFER to it.
  175. //
  176. // Entry: Size - Size in bytes header buffer should be mapped as.
  177. // Buffer - Pointer to pointer to NDIS_BUFFER to return.
  178. //
  179. // Returns: Pointer to IGMP buffer if allocated, or NULL.
  180. //
  181. __inline
  182. IGMPHeader *
  183. GetIGMPBuffer(uint Size, PNDIS_BUFFER *Buffer)
  184. {
  185. IGMPHeader *Header;
  186. ASSERT(Size);
  187. ASSERT(Buffer);
  188. *Buffer = MdpAllocate(IcmpHeaderPool, &Header);
  189. if (*Buffer) {
  190. NdisAdjustBufferLength(*Buffer, Size);
  191. // Reserve room for the IP Header.
  192. //
  193. Header = (IGMPHeader *)((uchar *)Header + sizeof(IPHeader));
  194. }
  195. return Header;
  196. }
  197. //** FreeIGMPBuffer - Free an IGMP buffer.
  198. //
  199. // This routine puts an IGMP buffer back on our free list.
  200. //
  201. // Entry: Buffer - Pointer to NDIS_BUFFER to be freed.
  202. // Type - IGMP header type
  203. //
  204. // Returns: Nothing.
  205. //
  206. __inline
  207. void
  208. FreeIGMPBuffer(PNDIS_BUFFER Buffer)
  209. {
  210. MdpFree(Buffer);
  211. }
  212. //** IGMPSendComplete - Complete an IGMP send.
  213. //
  214. // This rtn is called when an IGMP send completes. We free the header buffer,
  215. // the data buffer if there is one, and the NDIS_BUFFER chain.
  216. //
  217. // Entry: DataPtr - Pointer to data buffer, if any.
  218. // BufferChain - Pointer to NDIS_BUFFER chain.
  219. //
  220. // Returns: Nothing
  221. //
  222. void
  223. IGMPSendComplete(void *DataPtr, PNDIS_BUFFER BufferChain, IP_STATUS SendStatus)
  224. {
  225. PNDIS_BUFFER DataBuffer;
  226. UNREFERENCED_PARAMETER(SendStatus);
  227. NdisGetNextBuffer(BufferChain, &DataBuffer);
  228. FreeIGMPBuffer(BufferChain);
  229. if (DataBuffer != (PNDIS_BUFFER) NULL) { // We had data with this IGMP send.
  230. CTEFreeMem(DataPtr);
  231. NdisFreeBuffer(DataBuffer);
  232. }
  233. }
  234. //* IGMPRandomTicks - Generate a random value of timer ticks.
  235. //
  236. // A random number routine to generate a random number of timer ticks,
  237. // between 1 and time (in units of half secs) passed. The random number
  238. // algorithm is adapted from the book 'System Simulation' by Geoffrey Gordon.
  239. //
  240. // Input: Nothing.
  241. //
  242. // Returns: A random value between 1 and TimeDelayInHalfSec.
  243. //
  244. uint
  245. IGMPRandomTicks(
  246. IN uint TimeDelayInHalfSec)
  247. {
  248. RandomValue = RandomValue * 1220703125;
  249. if (RandomValue < 0) {
  250. RandomValue += 2147483647; // inefficient, but avoids warnings.
  251. RandomValue++;
  252. }
  253. // Not sure if RandomValue can get to 0, but if it does the algorithm
  254. // degenerates, so fix this if it happens.
  255. if (RandomValue == 0)
  256. RandomValue = ((Seed + (int)CTESystemUpTime()) % 100000000) | 1;
  257. return (uint) (((uint) RandomValue % TimeDelayInHalfSec) + 1);
  258. }
  259. //////////////////////////////////////////////////////////////////////////////
  260. // Routines accessing group entries
  261. //////////////////////////////////////////////////////////////////////////////
  262. //* FindIGMPAddr - Find an mcast entry on an NTE.
  263. //
  264. // Called to search an NTE for an IGMP entry for a given multicast address.
  265. // We walk down the chain on the NTE looking for it. If we find it,
  266. // we return a pointer to it and the one immediately preceding it. If we
  267. // don't find it we return NULL. We assume the caller has taken the lock
  268. // on the NTE before calling us.
  269. //
  270. // Input: NTE - NTE on which to search.
  271. // Addr - Class D address to find.
  272. // PrevPtr - Where to return pointer to preceding entry.
  273. //
  274. // Returns: Pointer to matching IGMPAddr structure if found, or NULL if not
  275. // found.
  276. //
  277. IGMPAddr *
  278. FindIGMPAddr(
  279. IN NetTableEntry *NTE,
  280. IN IPAddr Addr,
  281. OUT IGMPAddr **PrevPtr OPTIONAL)
  282. {
  283. int bucket;
  284. IGMPAddr *Current, *Temp;
  285. IGMPAddr **AddrPtr;
  286. AddrPtr = NTE->nte_igmplist;
  287. if (AddrPtr != NULL) {
  288. bucket = IGMP_HASH(Addr);
  289. Temp = STRUCT_OF(IGMPAddr, &AddrPtr[bucket], iga_next);
  290. Current = AddrPtr[bucket];
  291. while (Current != NULL) {
  292. if (IP_ADDR_EQUAL(Current->iga_addr, Addr)) {
  293. // Found a match, so return it.
  294. if (PrevPtr) {
  295. *PrevPtr = Temp;
  296. }
  297. return Current;
  298. }
  299. Temp = Current;
  300. Current = Current->iga_next;
  301. }
  302. }
  303. return NULL;
  304. }
  305. //* CreateIGMPAddr - Allocate memory and link the new IGMP address in
  306. //
  307. // Input: NTE - NetTableEntry to add group on
  308. // Addr - Group address to add
  309. //
  310. // Output: pAddrPtr - group entry added
  311. // pPrevPtr - previous group entry
  312. //
  313. // Assumes caller holds lock on NTE.
  314. //
  315. IP_STATUS
  316. CreateIGMPAddr(
  317. IN NetTableEntry *NTE,
  318. IN IPAddr Addr,
  319. OUT IGMPAddr **pAddrPtr,
  320. OUT IGMPAddr **pPrevPtr)
  321. {
  322. int bucket;
  323. IGMPAddr *AddrPtr;
  324. // If this is not a multicast address, fail the request.
  325. if (!CLASSD_ADDR(Addr)) {
  326. return IP_BAD_REQ;
  327. }
  328. AddrPtr = CTEAllocMemN(sizeof(IGMPAddr), 'yICT');
  329. if (AddrPtr == NULL) {
  330. return IP_NO_RESOURCES;
  331. }
  332. // See if we added it succesfully. If we did, fill in
  333. // the structure and link it in.
  334. CTEMemSet(AddrPtr, 0, sizeof(IGMPAddr));
  335. AddrPtr->iga_addr = Addr;
  336. // check whether the hash table has been allocated
  337. if (NTE->nte_igmpcount == 0) {
  338. NTE->nte_igmplist = CTEAllocMemN(IGMP_TABLE_SIZE * sizeof(IGMPAddr *),
  339. 'VICT');
  340. if (NTE->nte_igmplist) {
  341. CTEMemSet(NTE->nte_igmplist, 0,
  342. IGMP_TABLE_SIZE * sizeof(IGMPAddr *));
  343. }
  344. }
  345. if (NTE->nte_igmplist == NULL) {
  346. // Alloc failure. Free the memory and fail the request.
  347. CTEFreeMem(AddrPtr);
  348. return IP_NO_RESOURCES;
  349. }
  350. NTE->nte_igmpcount++;
  351. bucket = IGMP_HASH(Addr);
  352. AddrPtr->iga_next = NTE->nte_igmplist[bucket];
  353. NTE->nte_igmplist[bucket] = AddrPtr;
  354. *pAddrPtr = AddrPtr;
  355. *pPrevPtr = STRUCT_OF(IGMPAddr, &NTE->nte_igmplist[bucket], iga_next);
  356. return IP_SUCCESS;
  357. }
  358. //* FindOrCreateIGMPAddr - Find or create a group entry
  359. //
  360. // Input: NTE - NetTableEntry to add group on
  361. // Addr - Group address to add
  362. //
  363. // Output: pGrp - group entry found or added
  364. // pPrevGrp - previous group entry
  365. //
  366. // Assumes caller holds lock on NTE
  367. IP_STATUS
  368. FindOrCreateIGMPAddr(
  369. IN NetTableEntry *NTE,
  370. IN IPAddr Addr,
  371. OUT IGMPAddr **pGrp,
  372. OUT IGMPAddr **pPrevGrp)
  373. {
  374. *pGrp = FindIGMPAddr(NTE, Addr, pPrevGrp);
  375. if (*pGrp)
  376. return IP_SUCCESS;
  377. return CreateIGMPAddr(NTE, Addr, pGrp, pPrevGrp);
  378. }
  379. //* DeleteIGMPAddr - delete a group entry
  380. //
  381. // Input: NTE - NetTableEntry to add group on
  382. // PrevPtr - Previous group entry
  383. // pPtr - Group entry to delete
  384. //
  385. // Output: pPtr - zeroed since group entry is freed
  386. //
  387. // Assumes caller holds lock on NTE
  388. void
  389. DeleteIGMPAddr(
  390. IN NetTableEntry *NTE,
  391. IN IGMPAddr *PrevPtr,
  392. IN OUT IGMPAddr **pPtr)
  393. {
  394. // Make sure all references have been released and retransmissions are done
  395. ASSERT(IS_GROUP_DELETABLE(*pPtr));
  396. // Unlink from the NTE
  397. PrevPtr->iga_next = (*pPtr)->iga_next;
  398. NTE->nte_igmpcount--;
  399. // Free the hash table if needed
  400. if (NTE->nte_igmpcount == 0) {
  401. CTEFreeMem(NTE->nte_igmplist);
  402. NTE->nte_igmplist = NULL;
  403. }
  404. // Free memory
  405. CTEFreeMem(*pPtr);
  406. *pPtr = NULL;
  407. }
  408. //////////////////////////////////////////////////////////////////////////////
  409. // Routines accessing source entries
  410. //////////////////////////////////////////////////////////////////////////////
  411. //* FindIGMPSrcAddr - Find an mcast source entry on a source list.
  412. //
  413. // Called to search an NTE for an IGMP source entry for a given address.
  414. // We walk down the chain on the group entry looking for it. If we find it,
  415. // we return a pointer to it and the one immediately preceding it. If we
  416. // don't find it we return NULL. We assume the caller has taken the lock
  417. // on the NTE before calling us.
  418. //
  419. // Input: IGA - group entry on which to search.
  420. // Addr - source address to find.
  421. // PrevPtr - Where to return pointer to preceding entry.
  422. //
  423. // Returns: Pointer to matching IGMPSrcAddr structure if found, or NULL
  424. // if not found.
  425. //
  426. IGMPSrcAddr *
  427. FindIGMPSrcAddr(
  428. IN IGMPAddr *IGA,
  429. IN IPAddr Addr,
  430. OUT IGMPSrcAddr **PrevPtr OPTIONAL)
  431. {
  432. IGMPSrcAddr *Current, *Temp;
  433. Temp = STRUCT_OF(IGMPSrcAddr, &IGA->iga_srclist, isa_next);
  434. Current = IGA->iga_srclist;
  435. while (Current != NULL) {
  436. if (IP_ADDR_EQUAL(Current->isa_addr, Addr)) {
  437. // Found a match, so return it.
  438. if (PrevPtr) {
  439. *PrevPtr = Temp;
  440. }
  441. return Current;
  442. }
  443. Temp = Current;
  444. Current = Current->isa_next;
  445. }
  446. return NULL;
  447. }
  448. //* CreateIGMPSrcAddr - Allocate memory and link the new source address in
  449. //
  450. // Input: GroupPtr - group entry to add source to.
  451. // SrcAddr - source address to add.
  452. //
  453. // Output: pSrcPtr - source entry added.
  454. // pPrevSrcPtr - previous source entry.
  455. //
  456. // Assumes caller holds lock on NTE.
  457. //
  458. IP_STATUS
  459. CreateIGMPSrcAddr(
  460. IN IGMPAddr *GroupPtr,
  461. IN IPAddr SrcAddr,
  462. OUT IGMPSrcAddr **pSrcPtr,
  463. OUT IGMPSrcAddr **pPrevSrcPtr OPTIONAL)
  464. {
  465. IGMPSrcAddr *SrcAddrPtr;
  466. // If this is a multicast address, fail the request.
  467. if (CLASSD_ADDR(SrcAddr)) {
  468. return IP_BAD_REQ;
  469. }
  470. // Allocate space for the new source entry
  471. SrcAddrPtr = CTEAllocMemN(sizeof(IGMPSrcAddr), 'yICT');
  472. if (SrcAddrPtr == NULL) {
  473. return IP_NO_RESOURCES;
  474. }
  475. // Initialize fields
  476. RtlZeroMemory(SrcAddrPtr, sizeof(IGMPSrcAddr));
  477. SrcAddrPtr->isa_addr = SrcAddr;
  478. // Link it off the group entry
  479. SrcAddrPtr->isa_next = GroupPtr->iga_srclist;
  480. GroupPtr->iga_srclist = SrcAddrPtr;
  481. *pSrcPtr = SrcAddrPtr;
  482. if (pPrevSrcPtr)
  483. *pPrevSrcPtr = STRUCT_OF(IGMPSrcAddr, &GroupPtr->iga_srclist, isa_next);
  484. return IP_SUCCESS;
  485. }
  486. //* FindOrCreateIGMPSrcAddr - Find or create a source entry
  487. //
  488. // Input: GroupPtr - group entry to add source to.
  489. // SrcAddr - source address to add.
  490. //
  491. // Output: pSrcPtr - source entry added.
  492. // pPrevSrcPtr - previous source entry.
  493. //
  494. // Assumes caller holds lock on NTE
  495. IP_STATUS
  496. FindOrCreateIGMPSrcAddr(
  497. IN IGMPAddr *AddrPtr,
  498. IN IPAddr SrcAddr,
  499. OUT IGMPSrcAddr **pSrc,
  500. OUT IGMPSrcAddr **pPrevSrc)
  501. {
  502. *pSrc = FindIGMPSrcAddr(AddrPtr, SrcAddr, pPrevSrc);
  503. if (*pSrc)
  504. return IP_SUCCESS;
  505. return CreateIGMPSrcAddr(AddrPtr, SrcAddr, pSrc, pPrevSrc);
  506. }
  507. //* DeleteIGMPSrcAddr - delete a source entry
  508. //
  509. // Input: pSrcPtr - source entry added.
  510. // PrevSrcPtr - previous source entry.
  511. //
  512. // Output: pSrcPtr - zeroed since source entry is freed.
  513. //
  514. // Caller is responsible for freeing group entry if needed
  515. // Assumes caller holds lock on NTE
  516. void
  517. DeleteIGMPSrcAddr(
  518. IN IGMPSrcAddr *PrevSrcPtr,
  519. IN OUT IGMPSrcAddr **pSrcPtr)
  520. {
  521. // Make sure all references have been released
  522. // and no retransmissions are left
  523. ASSERT(IS_SOURCE_DELETABLE(*pSrcPtr));
  524. // Unlink from the group entry
  525. PrevSrcPtr->isa_next = (*pSrcPtr)->isa_next;
  526. // Free memory
  527. CTEFreeMem(*pSrcPtr);
  528. *pSrcPtr = NULL;
  529. }
  530. //////////////////////////////////////////////////////////////////////////////
  531. // Timer routines
  532. //////////////////////////////////////////////////////////////////////////////
  533. //* ResetGeneralTimer - Reset timer for responding to a General Query in
  534. // IGMPv3 mode
  535. //
  536. // Input: IF - Interface to reset timer on
  537. // MaxRespTimeInHalfSec - Maximum expiration time
  538. void
  539. ResetGeneralTimer(
  540. IN Interface *IF,
  541. IN uint MaxRespTimeInHalfSec)
  542. {
  543. if ((IF->IgmpGeneralTimer == 0) ||
  544. (IF->IgmpGeneralTimer > MaxRespTimeInHalfSec)) {
  545. IF->IgmpGeneralTimer = IGMPRandomTicks(MaxRespTimeInHalfSec);
  546. }
  547. // We could walk all groups here to stop any timers longer
  548. // than IF->IgmpGeneralTimer, but is it really worth it?
  549. }
  550. //* CancelGroupResponseTimer - stop a group timer
  551. //
  552. // Caller is responsible for deleting AddrPtr if no longer needed.
  553. void
  554. CancelGroupResponseTimer(
  555. IN IGMPAddr *AddrPtr)
  556. {
  557. IGMPSrcAddr *Src, *PrevSrc;
  558. AddrPtr->iga_resptimer = 0;
  559. AddrPtr->iga_resptype = NO_RESP;
  560. // Make sure we never violate the invariant:
  561. // iga_resptimer>0 if isa_csmarked=TRUE for any source
  562. PrevSrc = STRUCT_OF(IGMPSrcAddr, &AddrPtr->iga_srclist, isa_next);
  563. for (Src=AddrPtr->iga_srclist; Src; PrevSrc=Src,Src=Src->isa_next) {
  564. Src->isa_csmarked = FALSE;
  565. if (IS_SOURCE_DELETABLE(Src)) {
  566. DeleteIGMPSrcAddr(PrevSrc, &Src);
  567. Src = PrevSrc;
  568. }
  569. }
  570. }
  571. //* ResetGroupResponseTimer - Reset timer for responding to a Group-specific
  572. // Query, or an IGMPv1/v2 General Query.
  573. //
  574. // Input: IF - Interface to reset timer on.
  575. // AddrPtr - Group entry whose timer should be reset.
  576. // MaxRespTimeInHalfSec - Maximum expiration time.
  577. //
  578. // Caller is responsible for deleting AddrPtr if no longer needed.
  579. void
  580. ResetGroupResponseTimer(
  581. IN Interface *IF,
  582. IN IGMPAddr *AddrPtr,
  583. IN uint MaxRespTimeInHalfSec)
  584. {
  585. if ((AddrPtr->iga_resptimer == 0) ||
  586. (AddrPtr->iga_resptimer > MaxRespTimeInHalfSec)) {
  587. AddrPtr->iga_resptimer = IGMPRandomTicks(MaxRespTimeInHalfSec);
  588. }
  589. // Check if superceded by a general query
  590. if ((IF->IgmpGeneralTimer != 0)
  591. && (IF->IgmpGeneralTimer <= AddrPtr->iga_resptimer)) {
  592. CancelGroupResponseTimer(AddrPtr);
  593. return;
  594. }
  595. // Supercede group-source responses
  596. AddrPtr->iga_resptype = GROUP_RESP;
  597. }
  598. //* ResetGroupAndSourceTimer - Reset timer for responding to a
  599. // Group-and-source-specific Query
  600. //
  601. // Input: IF - Interface to reset timer on.
  602. // AddrPtr - Group entry whose timer should be reset.
  603. // MaxRespTimeInHalfSec - Maximum expiration time.
  604. //
  605. // Caller is responsible for deleting AddrPtr if no longer needed
  606. void
  607. ResetGroupAndSourceTimer(
  608. IN Interface *IF,
  609. IN IGMPAddr *AddrPtr,
  610. IN uint MaxRespTimeInHalfSec)
  611. {
  612. if ((AddrPtr->iga_resptimer == 0) ||
  613. (AddrPtr->iga_resptimer > MaxRespTimeInHalfSec)) {
  614. AddrPtr->iga_resptimer = IGMPRandomTicks(MaxRespTimeInHalfSec);
  615. }
  616. // Check if superceded by a general query
  617. if ((IF->IgmpGeneralTimer != 0)
  618. && (IF->IgmpGeneralTimer < AddrPtr->iga_resptimer)) {
  619. CancelGroupResponseTimer(AddrPtr);
  620. return;
  621. }
  622. // Check if superceded by a group-specific responses
  623. if (AddrPtr->iga_resptype == NO_RESP)
  624. AddrPtr->iga_resptype = GROUP_SOURCE_RESP;
  625. }
  626. //////////////////////////////////////////////////////////////////////////////
  627. // Receive routines
  628. //////////////////////////////////////////////////////////////////////////////
  629. //* SetVersion - change the IGMP compatability mode on an interface.
  630. //
  631. // Input: NTE - NetTableEntry on which to set IGMP version.
  632. // Version - IGMP version number to set
  633. //
  634. // Caller is responsible for deleting AddrPtr if no longer needed
  635. void
  636. SetVersion(
  637. IN NetTableEntry *NTE,
  638. IN uint Version)
  639. {
  640. IGMPAddr **HashPtr, *AddrPtr, *PrevPtr;
  641. IGMPSrcAddr *Src, *PrevSrc;
  642. uint i;
  643. DEBUGMSG(DBG_INFO && DBG_IGMP,
  644. (DTEXT("Setting version on interface %d to %d\n"),
  645. NTE->nte_if->if_index, Version));
  646. NTE->nte_if->IgmpVersion = Version;
  647. // Cancel General Timer
  648. NTE->nte_if->IgmpGeneralTimer = 0;
  649. //
  650. // Cancel all Group-Response and Triggered Retransmission timers
  651. //
  652. HashPtr = NTE->nte_igmplist;
  653. for (i = 0; (i < IGMP_TABLE_SIZE) && (NTE->nte_igmplist != NULL); i++) {
  654. PrevPtr = STRUCT_OF(IGMPAddr, &HashPtr[i], iga_next);
  655. for (AddrPtr = HashPtr[i];
  656. (AddrPtr != NULL);
  657. PrevPtr = AddrPtr, AddrPtr = AddrPtr->iga_next)
  658. {
  659. PrevSrc = STRUCT_OF(IGMPSrcAddr, &AddrPtr->iga_srclist, isa_next);
  660. for (Src=AddrPtr->iga_srclist; Src; PrevSrc=Src,Src=Src->isa_next) {
  661. Src->isa_xmitleft = 0;
  662. Src->isa_csmarked = FALSE;
  663. if (IS_SOURCE_DELETABLE(Src)) {
  664. DeleteIGMPSrcAddr(PrevSrc, &Src);
  665. Src = PrevSrc;
  666. }
  667. }
  668. AddrPtr->iga_trtimer = 0;
  669. AddrPtr->iga_changetype = NO_CHANGE;
  670. AddrPtr->iga_xmitleft = 0;
  671. CancelGroupResponseTimer(AddrPtr);
  672. if (IS_GROUP_DELETABLE(AddrPtr)) {
  673. DeleteIGMPAddr(NTE, PrevPtr, &AddrPtr);
  674. AddrPtr = PrevPtr;
  675. }
  676. if (NTE->nte_igmplist == NULL)
  677. break;
  678. }
  679. }
  680. }
  681. //* ProcessGroupQuery - process an IGMP Group-specific query
  682. //
  683. // Caller is responsible for deleting AddrPtr if no longer needed.
  684. void
  685. ProcessGroupQuery(
  686. IN Interface *IF,
  687. IN IGMPAddr *AddrPtr,
  688. IN uint ReportingDelayInHalfSec)
  689. {
  690. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_RX,
  691. (DTEXT("Got group query on interface %d\n"), IF->if_index));
  692. // Ignore query if we won't report anything. This will happen
  693. // right after we leave and have retransmissions pending.
  694. if (!IS_GROUP_ALLOWED(AddrPtr))
  695. return;
  696. ResetGroupResponseTimer(IF, AddrPtr, ReportingDelayInHalfSec);
  697. }
  698. //* ProcessGeneralQuery - Process an IGMP General Query
  699. //
  700. // Assumes caller holds lock on NTE
  701. void
  702. ProcessGeneralQuery(
  703. IN NetTableEntry *NTE,
  704. IN uint ReportingDelayInHalfSec)
  705. {
  706. IGMPAddr **HashPtr, *AddrPtr, *PrevPtr;
  707. uint i;
  708. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_RX,
  709. (DTEXT("Got general query on interface %d\n"),
  710. NTE->nte_if->if_index));
  711. if (NTE->nte_if->IgmpVersion == IGMPV3) {
  712. // IGMPv3 can pack multiple group records into the same report
  713. // and hence does not stagger the timers.
  714. // Create a pending response record
  715. ResetGeneralTimer(NTE->nte_if, ReportingDelayInHalfSec);
  716. } else {
  717. //
  718. // Walk our list and set a random report timer for all those
  719. // multicast addresses (except for the all-hosts address) that
  720. // don't already have one running.
  721. //
  722. HashPtr = NTE->nte_igmplist;
  723. for (i=0; (i < IGMP_TABLE_SIZE) && (NTE->nte_igmplist != NULL); i++) {
  724. PrevPtr = STRUCT_OF(IGMPAddr, &HashPtr[i], iga_next);
  725. for (AddrPtr = HashPtr[i];
  726. (AddrPtr != NULL);
  727. PrevPtr=AddrPtr, AddrPtr = AddrPtr->iga_next)
  728. {
  729. if (IP_ADDR_EQUAL(AddrPtr->iga_addr, ALL_HOST_MCAST))
  730. continue;
  731. ProcessGroupQuery(NTE->nte_if, AddrPtr,
  732. ReportingDelayInHalfSec);
  733. if (IS_GROUP_DELETABLE(AddrPtr)) {
  734. DeleteIGMPAddr(NTE, PrevPtr, &AddrPtr);
  735. AddrPtr = PrevPtr;
  736. }
  737. if (NTE->nte_igmplist == NULL)
  738. break;
  739. }
  740. }
  741. }
  742. }
  743. //* Process an IGMP Group-and-source-specific Query
  744. //
  745. // Caller is responsible for deleting AddrPtr if no longer needed
  746. void
  747. ProcessGroupAndSourceQuery(
  748. IN NetTableEntry *NTE,
  749. IN IGMPv3QueryHeader UNALIGNED *IQH,
  750. IN IGMPAddr *AddrPtr,
  751. IN uint ReportingDelayInHalfSec)
  752. {
  753. uint i, NumSrc;
  754. IGMPSrcAddr *Src;
  755. IP_STATUS Status = IP_SUCCESS;
  756. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_RX,
  757. (DTEXT("Got source query on interface %d\n"),
  758. NTE->nte_if->if_index));
  759. NumSrc = net_short(IQH->igh_numsrc);
  760. ResetGroupAndSourceTimer(NTE->nte_if, AddrPtr, ReportingDelayInHalfSec);
  761. // Mark each source
  762. for (i=0; i<NumSrc; i++) {
  763. Src = FindIGMPSrcAddr(AddrPtr, IQH->igh_srclist[i], NULL);
  764. if (!Src) {
  765. if (AddrPtr->iga_grefcnt == 0)
  766. continue;
  767. // Create temporary source state
  768. Status = CreateIGMPSrcAddr(AddrPtr, IQH->igh_srclist[i],
  769. &Src, NULL);
  770. // If this fails, we have a problem since we won't be
  771. // able to override the leave and a temporary black
  772. // hole would result. To avoid this, we pretend we
  773. // just got a group-specific query instead.
  774. if (Status != IP_SUCCESS) {
  775. ProcessGroupQuery(NTE->nte_if, AddrPtr,
  776. ReportingDelayInHalfSec);
  777. break;
  778. }
  779. }
  780. // Mark source for current-state report inclusion
  781. Src->isa_csmarked = TRUE;
  782. }
  783. }
  784. //* Process an IGMP Query message
  785. //
  786. // Entry: NTE - Pointer to NTE on which IGMP message was received.
  787. // Dest - IPAddr of destination (should be a Class D address).
  788. // IPHdr - Pointer to the IP Header.
  789. // IPHdrLength - Bytes in IPHeader.
  790. // IQH - Pointer to IGMP Query received.
  791. // Size - Size in bytes of IGMP message.
  792. //
  793. // Assumes caller holds lock on NTE
  794. void
  795. IGMPRcvQuery(
  796. IN NetTableEntry *NTE,
  797. IN IPAddr Dest,
  798. IN IPHeader UNALIGNED *IPHdr,
  799. IN uint IPHdrLength,
  800. IN IGMPv3QueryHeader UNALIGNED *IQH,
  801. IN uint Size)
  802. {
  803. uint ReportingDelayInHalfSec, MaxResp, NumSrc;
  804. IGMPAddr *AddrPtr, *PrevPtr;
  805. uchar QRV;
  806. DBG_UNREFERENCED_PARAMETER(Dest);
  807. // Make sure we're running at least level 2 of IGMP support.
  808. if (IGMPLevel != 2)
  809. return;
  810. NumSrc = (Size >= 12)? net_short(IQH->igh_numsrc) : 0;
  811. QRV = (Size >= 12)? IQH->igh_qrv : 0;
  812. // Update Robustness to match querier's robustness variable
  813. if (QRV > MAX_ROBUSTNESS) {
  814. QRV = MAX_ROBUSTNESS;
  815. }
  816. g_IgmpRobustness = (QRV)? QRV : DEFAULT_ROBUSTNESS;
  817. //
  818. // If it is an older-version General Query, set the timer value for
  819. // staying in older-version mode.
  820. //
  821. if ((Size == 8) && (IQH->igh_maxresp == 0)) {
  822. MaxResp = DEFAULT_QUERY_RESP_INTERVAL;
  823. if (IQH->igh_addr == 0) {
  824. if (NTE->nte_if->IgmpVersion > IGMPV1) {
  825. SetVersion(NTE, IGMPV1);
  826. }
  827. NTE->nte_if->IgmpVer1Timeout = g_IgmpRobustness * DEFAULT_QUERY_INTERVAL
  828. + (MaxResp+4)/5;
  829. }
  830. } else if ((Size == 8) && (IQH->igh_maxresp != 0)) {
  831. MaxResp = IQH->igh_maxresp;
  832. if (IQH->igh_addr == 0) {
  833. if (NTE->nte_if->IgmpVersion > IGMPV2) {
  834. SetVersion(NTE, IGMPV2);
  835. }
  836. NTE->nte_if->IgmpVer2Timeout = g_IgmpRobustness * DEFAULT_QUERY_INTERVAL
  837. + (MaxResp+4)/5;
  838. }
  839. } else if ((Size < 12) || (IQH->igh_rsvd2 != 0)) {
  840. // must silently ignore
  841. DEBUGMSG(DBG_WARN && DBG_IGMP,
  842. (DTEXT("Dropping IGMPv3 query with unrecognized version\n")));
  843. return;
  844. } else {
  845. // IGMPv3
  846. uchar* ptr = ((uchar*)IPHdr) + sizeof(IPHeader);
  847. int len = IPHdrLength - sizeof(IPHeader);
  848. uchar temp;
  849. BOOLEAN bRtrAlertFound = FALSE;
  850. // drop it if size is too short for advertised # sources
  851. if (Size < IGMPV3_QUERY_SIZE(NumSrc)) {
  852. DEBUGMSG(DBG_WARN && DBG_IGMP,
  853. (DTEXT("Dropping IGMPv3 query due to size too short\n")));
  854. return;
  855. }
  856. // drop it if it didn't have router alert
  857. while (!bRtrAlertFound && len>=2) {
  858. if (ptr[0] == IP_OPT_ROUTER_ALERT) {
  859. bRtrAlertFound = TRUE;
  860. break;
  861. }
  862. temp = ptr[1]; // length
  863. ptr += temp;
  864. len -= temp;
  865. }
  866. if (!bRtrAlertFound) {
  867. DEBUGMSG(DBG_WARN && DBG_IGMP,
  868. (DTEXT("Dropping IGMPv3 query due to lack of Router Alert option\n")));
  869. return;
  870. }
  871. if (IQH->igh_mrctype == 0) {
  872. MaxResp = IQH->igh_maxresp;
  873. } else {
  874. MaxResp = ((((uint)IQH->igh_mrcmant) + 16) << (((uint)IQH->igh_mrcexp) + 3));
  875. }
  876. }
  877. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_RX,
  878. (DTEXT("IGMPRcvQuery: Max response time = %d.%d seconds\n"),
  879. MaxResp/10, MaxResp%10));
  880. //
  881. // MaxResp has time in 100 msec (1/10 sec) units. Convert
  882. // to 500 msec units. If the time is < 500 msec, use 1.
  883. //
  884. ReportingDelayInHalfSec = ((MaxResp > 5) ? (MaxResp / 5) : 1);
  885. if (IQH->igh_addr == 0) {
  886. // General Query
  887. ProcessGeneralQuery(NTE, ReportingDelayInHalfSec);
  888. } else {
  889. // If all-hosts address, ignore it
  890. if (IP_ADDR_EQUAL(IQH->igh_addr, ALL_HOST_MCAST)) {
  891. DEBUGMSG(DBG_WARN && DBG_IGMP,
  892. (DTEXT("Dropping IGMPv3 query for the All-Hosts group\n")));
  893. return;
  894. }
  895. // Don't need to do anything if we have no group state for the group
  896. AddrPtr = FindIGMPAddr(NTE, IQH->igh_addr, &PrevPtr);
  897. if (!AddrPtr)
  898. return;
  899. if (NumSrc == 0) {
  900. // Group-specific query
  901. ProcessGroupQuery(NTE->nte_if, AddrPtr, ReportingDelayInHalfSec);
  902. } else {
  903. // Group-and-source-specific query
  904. ProcessGroupAndSourceQuery(NTE, IQH, AddrPtr,
  905. ReportingDelayInHalfSec);
  906. }
  907. // Delete group if no longer needed
  908. if (IS_GROUP_DELETABLE(AddrPtr))
  909. DeleteIGMPAddr(NTE, PrevPtr, &AddrPtr);
  910. }
  911. }
  912. //** IGMPRcv - Receive an IGMP datagram.
  913. //
  914. // Called by IP when we receive an IGMP datagram. We validate it to make
  915. // sure it's reasonable. Then if it it's a query for a group to which we
  916. // belong we'll start a response timer. If it's a report to a group to
  917. // which we belong we'll stop any running timer.
  918. //
  919. // The IGMP header is only 8 bytes long, and so should always fit in
  920. // exactly one IP rcv buffer. We check this to make sure, and if it
  921. // takes multiple buffers we discard it.
  922. //
  923. // Entry: NTE - Pointer to NTE on which IGMP message was received.
  924. // Dest - IPAddr of destination (should be a Class D address).
  925. // Src - IPAddr of source
  926. // LocalAddr - Local address of network which caused this to be
  927. // received.
  928. // SrcAddr - Address of local interface which received the
  929. // packet
  930. // IPHdr - Pointer to the IP Header.
  931. // IPHdrLength - Bytes in IPHeader.
  932. // RcvBuf - Pointer to IP receive buffer chain.
  933. // Size - Size in bytes of IGMP message.
  934. // IsBCast - Boolean indicator of whether or not this came in
  935. // as a bcast (should always be true).
  936. // Protocol - Protocol this came in on.
  937. // OptInfo - Pointer to info structure for received options.
  938. //
  939. // Returns: Status of reception
  940. IP_STATUS
  941. IGMPRcv(
  942. IN NetTableEntry * NTE,
  943. IN IPAddr Dest,
  944. IN IPAddr Src,
  945. IN IPAddr LocalAddr,
  946. IN IPAddr SrcAddr,
  947. IN IPHeader UNALIGNED * IPHdr,
  948. IN uint IPHdrLength,
  949. IN IPRcvBuf * RcvBuf,
  950. IN uint Size,
  951. IN uchar IsBCast,
  952. IN uchar Protocol,
  953. IN IPOptInfo * OptInfo)
  954. {
  955. IGMPHeader UNALIGNED *IGH;
  956. CTELockHandle Handle;
  957. IGMPAddr *AddrPtr, *PrevPtr;
  958. uchar DType;
  959. uint PromiscuousMode = 0;
  960. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_RX,
  961. (DTEXT("IGMPRcv entered\n")));
  962. PromiscuousMode = NTE->nte_if->if_promiscuousmode;
  963. // ASSERT(CLASSD_ADDR(Dest));
  964. // ASSERT(IsBCast);
  965. // Discard packets with invalid or broadcast source addresses.
  966. DType = GetAddrType(Src);
  967. if (DType == DEST_INVALID || IS_BCAST_DEST(DType)) {
  968. return IP_SUCCESS;
  969. }
  970. // Now get the pointer to the header, and validate the xsum.
  971. IGH = (IGMPHeader UNALIGNED *) RcvBuf->ipr_buffer;
  972. //
  973. // For mtrace like programs, use the entire IGMP packet to generate the xsum.
  974. //
  975. if ((Size < sizeof(IGMPHeader)) || (XsumRcvBuf(0, RcvBuf) != 0xffff)) {
  976. // Bad checksum, so fail.
  977. return IP_SUCCESS;
  978. }
  979. // OK, we may need to process this. See if we are a member of the
  980. // destination group. If we aren't, there's no need to proceed further.
  981. //
  982. // Since for any interface we always get notified with
  983. // same NTE, locking the NTE is fine. We don't have to
  984. // lock the interface structure
  985. //
  986. CTEGetLock(&NTE->nte_lock, &Handle);
  987. {
  988. if (!(NTE->nte_flags & NTE_VALID)) {
  989. CTEFreeLock(&NTE->nte_lock, Handle);
  990. return IP_SUCCESS;
  991. }
  992. //
  993. // The NTE is valid. Demux on type.
  994. //
  995. switch (IGH->igh_vertype) {
  996. case IGMP_QUERY:
  997. IGMPRcvQuery(NTE, Dest, IPHdr, IPHdrLength,
  998. (IGMPv3QueryHeader UNALIGNED *)IGH, Size);
  999. break;
  1000. case IGMP_REPORT_V1:
  1001. case IGMP_REPORT_V2:
  1002. // Make sure we're running at least level 2 of IGMP support.
  1003. if (IGMPLevel != 2) {
  1004. CTEFreeLock(&NTE->nte_lock, Handle);
  1005. return IP_SUCCESS;
  1006. }
  1007. //
  1008. // This is a report. Check its validity and see if we have a
  1009. // response timer running for that address. If we do, stop it.
  1010. // Make sure the destination address matches the address in the
  1011. // IGMP header.
  1012. //
  1013. if (IP_ADDR_EQUAL(Dest, IGH->igh_addr)) {
  1014. // The addresses match. See if we have a membership in this
  1015. // group.
  1016. AddrPtr = FindIGMPAddr(NTE, IGH->igh_addr, &PrevPtr);
  1017. if (AddrPtr != NULL) {
  1018. // We found a matching multicast address. Stop the response
  1019. // timer for any Group-specific or Group-and-source-
  1020. // specific queries.
  1021. CancelGroupResponseTimer(AddrPtr);
  1022. if (IS_GROUP_DELETABLE(AddrPtr))
  1023. DeleteIGMPAddr(NTE, PrevPtr, &AddrPtr);
  1024. }
  1025. }
  1026. break;
  1027. default:
  1028. break;
  1029. }
  1030. }
  1031. CTEFreeLock(&NTE->nte_lock, Handle);
  1032. //
  1033. // Pass the packet up to the raw layer if applicable.
  1034. // If promiscuous mode is set then we will anyway call rawrcv later
  1035. //
  1036. if ((RawPI != NULL) && (!PromiscuousMode)) {
  1037. if (RawPI->pi_rcv != NULL) {
  1038. (*(RawPI->pi_rcv)) (NTE, Dest, Src, LocalAddr, SrcAddr, IPHdr,
  1039. IPHdrLength, RcvBuf, Size, IsBCast, Protocol, OptInfo);
  1040. }
  1041. }
  1042. return IP_SUCCESS;
  1043. }
  1044. //////////////////////////////////////////////////////////////////////////////
  1045. // Send routines
  1046. //////////////////////////////////////////////////////////////////////////////
  1047. //* IGMPTransmit - transmit an IGMP message
  1048. IP_STATUS
  1049. IGMPTransmit(
  1050. IN PNDIS_BUFFER Buffer,
  1051. IN PVOID Body,
  1052. IN uint Size,
  1053. IN IPAddr SrcAddr,
  1054. IN IPAddr DestAddr)
  1055. {
  1056. uchar RtrAlertOpt[4] = { IP_OPT_ROUTER_ALERT, 4, 0, 0 };
  1057. IPOptInfo OptInfo; // Options for this transmit.
  1058. IP_STATUS Status;
  1059. RouteCacheEntry *RCE;
  1060. ushort MSS;
  1061. uchar DestType;
  1062. IPAddr Src;
  1063. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_TX,
  1064. (DTEXT("IGMPTransmit: Buffer=%x Body=%x Size=%d SrcAddr=%x\n"),
  1065. Buffer, Body, Size, SrcAddr));
  1066. IPInitOptions(&OptInfo);
  1067. OptInfo.ioi_ttl = 1;
  1068. OptInfo.ioi_options = (uchar *) RtrAlertOpt;
  1069. OptInfo.ioi_optlength = ROUTER_ALERT_SIZE;
  1070. Src = OpenRCE(DestAddr, SrcAddr, &RCE, &DestType, &MSS, &OptInfo);
  1071. if (IP_ADDR_EQUAL(Src,NULL_IP_ADDR)) {
  1072. IGMPSendComplete(Body, Buffer, IP_SUCCESS);
  1073. return IP_DEST_HOST_UNREACHABLE;
  1074. }
  1075. #if GPC
  1076. if (DisableUserTOS) {
  1077. OptInfo.ioi_tos = (uchar) DefaultTOS;
  1078. }
  1079. if (GPCcfInfo) {
  1080. //
  1081. // we'll fall into here only if the GPC client is there
  1082. // and there is at least one CF_INFO_QOS installed
  1083. // (counted by GPCcfInfo).
  1084. //
  1085. GPC_STATUS status = STATUS_SUCCESS;
  1086. struct QosCfTransportInfo TransportInfo = {0, 0};
  1087. GPC_IP_PATTERN Pattern;
  1088. CLASSIFICATION_HANDLE GPCHandle;
  1089. Pattern.SrcAddr = SrcAddr;
  1090. Pattern.DstAddr = DestAddr;
  1091. Pattern.ProtocolId = PROT_IGMP;
  1092. Pattern.gpcSrcPort = 0;
  1093. Pattern.gpcDstPort = 0;
  1094. Pattern.InterfaceId.InterfaceId = 0;
  1095. Pattern.InterfaceId.LinkId = 0;
  1096. GPCHandle = 0;
  1097. GetIFAndLink(RCE,
  1098. &Pattern.InterfaceId.InterfaceId,
  1099. &Pattern.InterfaceId.LinkId
  1100. );
  1101. status = GpcEntries.GpcClassifyPatternHandler(
  1102. hGpcClient[GPC_CF_QOS],
  1103. GPC_PROTOCOL_TEMPLATE_IP,
  1104. &Pattern,
  1105. NULL, // context
  1106. &GPCHandle,
  1107. 0,
  1108. NULL,
  1109. FALSE);
  1110. OptInfo.ioi_GPCHandle = (int)GPCHandle;
  1111. //
  1112. // Only if QOS patterns exist, we get the TOS bits out.
  1113. //
  1114. if (NT_SUCCESS(status) && GpcCfCounts[GPC_CF_QOS]) {
  1115. status = GpcEntries.GpcGetUlongFromCfInfoHandler(
  1116. hGpcClient[GPC_CF_QOS],
  1117. OptInfo.ioi_GPCHandle,
  1118. FIELD_OFFSET(CF_INFO_QOS, TransportInformation),
  1119. (PULONG)&TransportInfo);
  1120. //
  1121. // It is likely that the pattern has gone by now (Removed or
  1122. // whatever) and the handle that we are caching is INVALID.
  1123. // We need to pull up a new handle and get the
  1124. // TOS bit again.
  1125. //
  1126. if (STATUS_NOT_FOUND == status) {
  1127. GPCHandle = 0;
  1128. status = GpcEntries.GpcClassifyPatternHandler(
  1129. hGpcClient[GPC_CF_QOS],
  1130. GPC_PROTOCOL_TEMPLATE_IP,
  1131. &Pattern,
  1132. NULL, // context
  1133. &GPCHandle,
  1134. 0,
  1135. NULL,
  1136. FALSE);
  1137. OptInfo.ioi_GPCHandle = (int)GPCHandle;
  1138. //
  1139. // Only if QOS patterns exist, we get the TOS bits out.
  1140. //
  1141. if (NT_SUCCESS(status)) {
  1142. status = GpcEntries.GpcGetUlongFromCfInfoHandler(
  1143. hGpcClient[GPC_CF_QOS],
  1144. OptInfo.ioi_GPCHandle,
  1145. FIELD_OFFSET(CF_INFO_QOS, TransportInformation),
  1146. (PULONG)&TransportInfo);
  1147. }
  1148. }
  1149. }
  1150. if (status == STATUS_SUCCESS) {
  1151. OptInfo.ioi_tos = (OptInfo.ioi_tos & TOS_MASK) |
  1152. (UCHAR)TransportInfo.ToSValue;
  1153. }
  1154. } // if (GPCcfInfo)
  1155. #endif
  1156. Status = IPTransmit(IGMPProtInfo, Body, Buffer, Size,
  1157. DestAddr, SrcAddr, &OptInfo, RCE, PROT_IGMP, NULL);
  1158. CloseRCE(RCE);
  1159. if (Status != IP_PENDING)
  1160. IGMPSendComplete(Body, Buffer, IP_SUCCESS);
  1161. return Status;
  1162. }
  1163. //* GetAllowRecord - allocate and fill in an IGMPv3 ALLOW record for a group
  1164. //
  1165. // Caller is responsible for freeing pointer returned
  1166. IGMPv3GroupRecord *
  1167. GetAllowRecord(
  1168. IN IGMPAddr *AddrPtr,
  1169. IN uint *RecSize)
  1170. {
  1171. IGMPSrcAddr *Src, *PrevSrc;
  1172. IGMPv3GroupRecord *Rec;
  1173. ushort Count = 0;
  1174. // Count sources to include
  1175. for (Src=AddrPtr->iga_srclist; Src; Src=Src->isa_next) {
  1176. if (Src->isa_xmitleft == 0)
  1177. continue;
  1178. if (!IS_SOURCE_ALLOWED(AddrPtr, Src))
  1179. continue;
  1180. Count++;
  1181. }
  1182. if (Count == 0) {
  1183. *RecSize = 0;
  1184. return NULL;
  1185. }
  1186. Rec = CTEAllocMemN(RECORD_SIZE(Count,0), 'qICT');
  1187. //
  1188. // We need to walk the source list regardless of whether the
  1189. // allocation succeeded, so that we preserve the invariant that
  1190. // iga_xmitleft >= isa_xmitleft for all sources.
  1191. //
  1192. Count = 0;
  1193. PrevSrc = STRUCT_OF(IGMPSrcAddr, &AddrPtr->iga_srclist, isa_next);
  1194. for (Src=AddrPtr->iga_srclist; Src; PrevSrc=Src,Src=Src->isa_next) {
  1195. if (Src->isa_xmitleft == 0)
  1196. continue;
  1197. if (!IS_SOURCE_ALLOWED(AddrPtr, Src))
  1198. continue;
  1199. if (Rec)
  1200. Rec->igr_srclist[Count++] = Src->isa_addr;
  1201. Src->isa_xmitleft--;
  1202. if (IS_SOURCE_DELETABLE(Src)) {
  1203. DeleteIGMPSrcAddr(PrevSrc, &Src);
  1204. Src = PrevSrc;
  1205. }
  1206. }
  1207. if (Rec == NULL) {
  1208. *RecSize = 0;
  1209. return NULL;
  1210. }
  1211. Rec->igr_type = ALLOW_NEW_SOURCES;
  1212. Rec->igr_datalen = 0;
  1213. Rec->igr_numsrc = net_short(Count);
  1214. Rec->igr_addr = AddrPtr->iga_addr;
  1215. *RecSize = RECORD_SIZE(Count,Rec->igr_datalen);
  1216. return Rec;
  1217. }
  1218. // Count a state-change report as going out, and preserve the invariant
  1219. // that iga_xmitleft>0 if iga_changetype!=NO_CHANGE
  1220. //
  1221. VOID
  1222. IgmpDecXmitLeft(
  1223. IN IGMPAddr *AddrPtr)
  1224. {
  1225. AddrPtr->iga_xmitleft--;
  1226. if (!AddrPtr->iga_xmitleft) {
  1227. AddrPtr->iga_changetype = NO_CHANGE;
  1228. }
  1229. }
  1230. //* GetBlockRecord - allocate and fill in an IGMPv3 BLOCK record for a group
  1231. //
  1232. // Caller is responsible for freeing pointer returned
  1233. IGMPv3GroupRecord *
  1234. GetBlockRecord(
  1235. IN IGMPAddr *AddrPtr,
  1236. IN uint *RecSize)
  1237. {
  1238. IGMPSrcAddr *Src, *PrevSrc;
  1239. IGMPv3GroupRecord *Rec;
  1240. ushort Count = 0;
  1241. // We now need to decrement the retransmission count on the group.
  1242. // This must be done exactly once for every pair of ALLOW/BLOCK
  1243. // records possibly generated. We centralize this code in one place
  1244. // by putting it in either GetAllowRecord or GetBlockRecord (which
  1245. // are always called together). We arbitrarily choose to put it
  1246. // in GetBlockRecord, rather than GetAllowRecord (which isn't currently
  1247. // called from LeaveAllIGMPAddr).
  1248. //
  1249. IgmpDecXmitLeft(AddrPtr);
  1250. // Count sources to include
  1251. for (Src=AddrPtr->iga_srclist; Src; Src=Src->isa_next) {
  1252. if (Src->isa_xmitleft == 0)
  1253. continue;
  1254. if (IS_SOURCE_ALLOWED(AddrPtr, Src))
  1255. continue;
  1256. Count++;
  1257. }
  1258. if (Count == 0) {
  1259. *RecSize = 0;
  1260. return NULL;
  1261. }
  1262. // Allocate record
  1263. Rec = CTEAllocMemN(RECORD_SIZE(Count,0), 'qICT');
  1264. //
  1265. // We need to walk the source list regardless of whether the
  1266. // allocation succeeded, so that we preserve the invariant that
  1267. // iga_xmitleft >= isa_xmitleft for all sources.
  1268. //
  1269. Count = 0;
  1270. PrevSrc = STRUCT_OF(IGMPSrcAddr, &AddrPtr->iga_srclist, isa_next);
  1271. for (Src=AddrPtr->iga_srclist; Src; PrevSrc=Src,Src=Src->isa_next) {
  1272. if (Src->isa_xmitleft == 0)
  1273. continue;
  1274. if (IS_SOURCE_ALLOWED(AddrPtr, Src))
  1275. continue;
  1276. if (Rec)
  1277. Rec->igr_srclist[Count++] = Src->isa_addr;
  1278. Src->isa_xmitleft--;
  1279. if (IS_SOURCE_DELETABLE(Src)) {
  1280. DeleteIGMPSrcAddr(PrevSrc, &Src);
  1281. Src = PrevSrc;
  1282. }
  1283. }
  1284. if (Rec == NULL) {
  1285. *RecSize = 0;
  1286. return NULL;
  1287. }
  1288. Rec->igr_type = BLOCK_OLD_SOURCES;
  1289. Rec->igr_datalen = 0;
  1290. Rec->igr_numsrc = net_short(Count);
  1291. Rec->igr_addr = AddrPtr->iga_addr;
  1292. *RecSize = RECORD_SIZE(Count,Rec->igr_datalen);
  1293. return Rec;
  1294. }
  1295. //* GetGSIsInRecord - allocate and fill in an IGMPv3 IS_IN record for a
  1296. // group-and-source query response.
  1297. //
  1298. // Caller is responsible for freeing pointer returned
  1299. IGMPv3GroupRecord *
  1300. GetGSIsInRecord(
  1301. IN IGMPAddr *AddrPtr,
  1302. IN uint *RecSize)
  1303. {
  1304. IGMPSrcAddr *Src, *PrevSrc;
  1305. IGMPv3GroupRecord *Rec;
  1306. ushort Count = 0;
  1307. // Count sources marked and included
  1308. for (Src=AddrPtr->iga_srclist; Src; Src=Src->isa_next) {
  1309. if (!IS_SOURCE_ALLOWED(AddrPtr, Src))
  1310. continue;
  1311. if (!Src->isa_csmarked)
  1312. continue;
  1313. Count++;
  1314. }
  1315. // Allocate record
  1316. Rec = CTEAllocMemN(RECORD_SIZE(Count,0), 'qICT');
  1317. if (Rec == NULL) {
  1318. *RecSize = 0;
  1319. return NULL;
  1320. }
  1321. Count = 0;
  1322. PrevSrc = STRUCT_OF(IGMPSrcAddr, &AddrPtr->iga_srclist, isa_next);
  1323. for (Src=AddrPtr->iga_srclist; Src; PrevSrc=Src,Src=Src->isa_next) {
  1324. if (!IS_SOURCE_ALLOWED(AddrPtr, Src))
  1325. continue;
  1326. if (!Src->isa_csmarked)
  1327. continue;
  1328. Rec->igr_srclist[Count++] = Src->isa_addr;
  1329. Src->isa_csmarked = FALSE;
  1330. if (IS_SOURCE_DELETABLE(Src)) {
  1331. DeleteIGMPSrcAddr(PrevSrc, &Src);
  1332. Src = PrevSrc;
  1333. }
  1334. }
  1335. Rec->igr_type = MODE_IS_INCLUDE;
  1336. Rec->igr_datalen = 0;
  1337. Rec->igr_numsrc = net_short(Count);
  1338. Rec->igr_addr = AddrPtr->iga_addr;
  1339. *RecSize = RECORD_SIZE(Count,Rec->igr_datalen);
  1340. return Rec;
  1341. }
  1342. //* GetInclRecord - allocate and fill in an IGMPv3 TO_IN or IS_IN record for
  1343. // a group
  1344. //
  1345. // Caller is responsible for freeing pointer returned
  1346. IGMPv3GroupRecord *
  1347. GetInclRecord(
  1348. IN IGMPAddr *AddrPtr,
  1349. IN uint *RecSize,
  1350. IN uchar Type)
  1351. {
  1352. IGMPSrcAddr *Src, *PrevSrc;
  1353. IGMPv3GroupRecord *Rec;
  1354. ushort Count = 0;
  1355. // Count sources
  1356. for (Src=AddrPtr->iga_srclist; Src; Src=Src->isa_next) {
  1357. if (!IS_SOURCE_ALLOWED(AddrPtr, Src))
  1358. continue;
  1359. Count++;
  1360. }
  1361. // Allocate record
  1362. Rec = CTEAllocMemN(RECORD_SIZE(Count,0), 'qICT');
  1363. if (Rec == NULL) {
  1364. *RecSize = 0;
  1365. return NULL;
  1366. }
  1367. //
  1368. // Walk the source list, making sure to preserve the invariants:
  1369. // iga_xmitleft >= isa_xmitleft for all sources, and
  1370. // iga_resptimer>0 whenever isa_csmarked is TRUE.
  1371. //
  1372. Count = 0;
  1373. PrevSrc = STRUCT_OF(IGMPSrcAddr, &AddrPtr->iga_srclist, isa_next);
  1374. for (Src=AddrPtr->iga_srclist; Src; PrevSrc=Src,Src=Src->isa_next) {
  1375. if ((Type == CHANGE_TO_INCLUDE_MODE) && (Src->isa_xmitleft > 0))
  1376. Src->isa_xmitleft--;
  1377. if (IS_SOURCE_ALLOWED(AddrPtr, Src)) {
  1378. Rec->igr_srclist[Count++] = Src->isa_addr;
  1379. Src->isa_csmarked = FALSE;
  1380. }
  1381. if (IS_SOURCE_DELETABLE(Src)) {
  1382. DeleteIGMPSrcAddr(PrevSrc, &Src);
  1383. Src = PrevSrc;
  1384. }
  1385. }
  1386. Rec->igr_type = Type;
  1387. Rec->igr_datalen = 0;
  1388. Rec->igr_numsrc = net_short(Count);
  1389. Rec->igr_addr = AddrPtr->iga_addr;
  1390. if (Type == CHANGE_TO_INCLUDE_MODE) {
  1391. IgmpDecXmitLeft(AddrPtr);
  1392. }
  1393. *RecSize = RECORD_SIZE(Count,Rec->igr_datalen);
  1394. return Rec;
  1395. }
  1396. #define GetIsInRecord(Grp, RecSz) \
  1397. GetInclRecord(Grp, RecSz, MODE_IS_INCLUDE)
  1398. #define GetToInRecord(Grp, RecSz) \
  1399. GetInclRecord(Grp, RecSz, CHANGE_TO_INCLUDE_MODE)
  1400. //* GetExclRecord - allocate and fill in an IGMPv3 TO_EX or IS_EX record for
  1401. // a group
  1402. //
  1403. // Caller is responsible for freeing pointer returned
  1404. IGMPv3GroupRecord *
  1405. GetExclRecord(
  1406. IN IGMPAddr *AddrPtr,
  1407. IN uint *RecSize,
  1408. IN uint BodyMTU,
  1409. IN uchar Type)
  1410. {
  1411. IGMPSrcAddr *Src, *PrevSrc;
  1412. IGMPv3GroupRecord *Rec;
  1413. ushort Count = 0;
  1414. // Count sources
  1415. for (Src=AddrPtr->iga_srclist; Src; Src=Src->isa_next) {
  1416. if (IS_SOURCE_ALLOWED(AddrPtr, Src))
  1417. continue;
  1418. Count++;
  1419. }
  1420. // Allocate record
  1421. Rec = CTEAllocMemN(RECORD_SIZE(Count,0), 'qICT');
  1422. if (Rec == NULL) {
  1423. *RecSize = 0;
  1424. return NULL;
  1425. }
  1426. //
  1427. // Walk the source list, making sure to preserve the invariants:
  1428. // iga_xmitleft <= isa_xmitleft for all sources, and
  1429. // iga_resptimer>0 whenever isa_csmarked is TRUE.
  1430. //
  1431. Count = 0;
  1432. PrevSrc = STRUCT_OF(IGMPSrcAddr, &AddrPtr->iga_srclist, isa_next);
  1433. for (Src=AddrPtr->iga_srclist; Src; PrevSrc=Src,Src=Src->isa_next) {
  1434. if ((Type == CHANGE_TO_EXCLUDE_MODE) && (Src->isa_xmitleft > 0))
  1435. Src->isa_xmitleft--;
  1436. if (!IS_SOURCE_ALLOWED(AddrPtr, Src)) {
  1437. Rec->igr_srclist[Count++] = Src->isa_addr;
  1438. Src->isa_csmarked = FALSE;
  1439. }
  1440. if (IS_SOURCE_DELETABLE(Src)) {
  1441. DeleteIGMPSrcAddr(PrevSrc, &Src);
  1442. Src = PrevSrc;
  1443. }
  1444. }
  1445. Rec->igr_type = Type;
  1446. Rec->igr_datalen = 0;
  1447. Rec->igr_numsrc = net_short(Count);
  1448. Rec->igr_addr = AddrPtr->iga_addr;
  1449. if (Type == CHANGE_TO_EXCLUDE_MODE) {
  1450. IgmpDecXmitLeft(AddrPtr);
  1451. }
  1452. *RecSize = RECORD_SIZE(Count,Rec->igr_datalen);
  1453. // Truncate at MTU boundary
  1454. if (*RecSize > BodyMTU) {
  1455. *RecSize = BodyMTU;
  1456. }
  1457. return Rec;
  1458. }
  1459. #define GetIsExRecord(Grp, RecSz, BodyMTU) \
  1460. GetExclRecord(Grp, RecSz, BodyMTU, MODE_IS_EXCLUDE)
  1461. #define GetToExRecord(Grp, RecSz, BodyMTU) \
  1462. GetExclRecord(Grp, RecSz, BodyMTU, CHANGE_TO_EXCLUDE_MODE)
  1463. //* QueueRecord - Queue an IGMPv3 group record for transmission.
  1464. // If the record cannot be queued, the record is dropped and the
  1465. // memory freed.
  1466. //
  1467. // Input: pCurr = pointer to last queue entry
  1468. // Record = record to append to end of queue
  1469. // RecSize = size of record to queue
  1470. //
  1471. // Output: pCurr = pointer to new queue entry
  1472. // Record = zeroed if queue failed and record was freed
  1473. //
  1474. // Returns: status
  1475. //
  1476. IP_STATUS
  1477. QueueRecord(
  1478. IN OUT IGMPv3RecordQueueEntry **pCurr,
  1479. IN OUT IGMPv3GroupRecord **pRecord,
  1480. IN uint RecSize)
  1481. {
  1482. IGMPv3RecordQueueEntry *rqe;
  1483. IGMPv3GroupRecord *Record = *pRecord;
  1484. IP_STATUS Status;
  1485. if (!Record) {
  1486. return IP_SUCCESS;
  1487. }
  1488. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_TX,
  1489. (DTEXT("QueueRecord: Record=%x Type=%d Group=%x NumSrc=%d\n"),
  1490. Record, Record->igr_type, Record->igr_addr,
  1491. net_short(Record->igr_numsrc)));
  1492. //
  1493. // Make sure we never add a record for the all-hosts mcast address.
  1494. //
  1495. if (IP_ADDR_EQUAL(Record->igr_addr, ALL_HOST_MCAST)) {
  1496. Status = IP_BAD_REQ;
  1497. goto Error;
  1498. }
  1499. // Allocate a queue entry
  1500. rqe = CTEAllocMemN(sizeof(IGMPv3RecordQueueEntry), 'qICT');
  1501. if (rqe == NULL) {
  1502. Status = IP_NO_RESOURCES;
  1503. goto Error;
  1504. }
  1505. rqe->i3qe_next = NULL;
  1506. rqe->i3qe_buff = Record;
  1507. rqe->i3qe_size = RecSize;
  1508. // Append to queue
  1509. (*pCurr)->i3qe_next = rqe;
  1510. *pCurr = rqe;
  1511. return IP_SUCCESS;
  1512. Error:
  1513. // Free buffers
  1514. CTEFreeMem(Record);
  1515. *pRecord = NULL;
  1516. return Status;
  1517. }
  1518. VOID
  1519. FlushIGMPv3Queue(
  1520. IN IGMPv3RecordQueueEntry *Head)
  1521. {
  1522. IGMPv3RecordQueueEntry *Rqe;
  1523. while ((Rqe = Head) != NULL) {
  1524. // Remove entry from queue
  1525. Head = Rqe->i3qe_next;
  1526. Rqe->i3qe_next = NULL;
  1527. // Free queued record
  1528. CTEFreeMem(Rqe->i3qe_buff);
  1529. CTEFreeMem(Rqe);
  1530. }
  1531. }
  1532. //* SendIGMPv3Reports - send pending IGMPv3 reports
  1533. //
  1534. // Input: Head - queue of IGMPv3 records to transmit
  1535. // SrcAddr - source address to send with
  1536. // BodyMTU - message payload size available to pack records in
  1537. IP_STATUS
  1538. SendIGMPv3Reports(
  1539. IN IGMPv3RecordQueueEntry *Head,
  1540. IN IPAddr SrcAddr,
  1541. IN uint BodyMTU)
  1542. {
  1543. PNDIS_BUFFER HdrBuffer;
  1544. uint HdrSize;
  1545. IGMPv3ReportHeader *IGH;
  1546. PNDIS_BUFFER BodyBuffer;
  1547. uint BodySize;
  1548. uchar* Body;
  1549. IP_STATUS Status = IP_SUCCESS;
  1550. NDIS_STATUS NdisStatus;
  1551. uint NumRecords;
  1552. ushort NumOldSources, NumNewSources;
  1553. IGMPv3RecordQueueEntry *Rqe;
  1554. IGMPv3GroupRecord *Rec, *HeadRec;
  1555. ulong csum;
  1556. while (Head != NULL) {
  1557. // Get header buffer
  1558. HdrSize = sizeof(IGMPv3ReportHeader);
  1559. IGH = (IGMPv3ReportHeader*) GetIGMPBuffer(HdrSize, &HdrBuffer);
  1560. if (IGH == NULL) {
  1561. FlushIGMPv3Queue(Head);
  1562. return IP_NO_RESOURCES;
  1563. }
  1564. // We got the buffer. Fill it in and send it.
  1565. IGH->igh_vertype = (UCHAR) IGMP_REPORT_V3;
  1566. IGH->igh_rsvd = 0;
  1567. IGH->igh_rsvd2 = 0;
  1568. // Compute optimum body size
  1569. for (;;) {
  1570. NumRecords = 0;
  1571. BodySize = 0;
  1572. for (Rqe=Head; Rqe; Rqe=Rqe->i3qe_next) {
  1573. if (BodySize + Rqe->i3qe_size > BodyMTU)
  1574. break;
  1575. BodySize += Rqe->i3qe_size;
  1576. NumRecords++;
  1577. }
  1578. // Make sure we fit at least one record
  1579. if (NumRecords > 0)
  1580. break;
  1581. //
  1582. // No records fit. Let's split the first record and try again.
  1583. // Note that igr_datalen is always 0 today. If there is data
  1584. // later, then splitting will need to know whether to copy
  1585. // the data or not. Today we assume not.
  1586. //
  1587. HeadRec = Head->i3qe_buff;
  1588. #pragma warning(push)
  1589. #pragma warning(disable:4267) // conversion from 'size_t' to 'ushort'
  1590. NumOldSources = (ushort) ((BodyMTU - sizeof(IGMPv3GroupRecord)) /
  1591. sizeof(IPAddr));
  1592. #pragma warning(pop)
  1593. NumNewSources = net_short(HeadRec->igr_numsrc) - NumOldSources;
  1594. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_TX,
  1595. (DTEXT("SendIGMPv3Reports: Splitting queue entry %x Srcs=%d+%d\n"),
  1596. HeadRec, NumOldSources, NumNewSources));
  1597. // Truncate head
  1598. HeadRec->igr_numsrc = net_short(NumOldSources);
  1599. Head->i3qe_size = RECORD_SIZE(NumOldSources, HeadRec->igr_datalen);
  1600. // Special case for IS_EX/TO_EX: just truncate or else the router
  1601. // will end up forwarding all the sources we exclude in messages
  1602. // other than the last one.
  1603. if (HeadRec->igr_type == MODE_IS_EXCLUDE
  1604. || HeadRec->igr_type == CHANGE_TO_EXCLUDE_MODE) {
  1605. continue;
  1606. }
  1607. // Create a new record with NumNewSources sources
  1608. Rec = CTEAllocMemN(RECORD_SIZE(NumNewSources,0), 'qICT');
  1609. if (Rec == NULL) {
  1610. // Forget the continuation, just send the truncated original.
  1611. continue;
  1612. }
  1613. Rec->igr_type = HeadRec->igr_type;
  1614. Rec->igr_datalen = 0;
  1615. Rec->igr_numsrc = net_short(NumNewSources);
  1616. Rec->igr_addr = HeadRec->igr_addr;
  1617. RtlCopyMemory(Rec->igr_srclist,
  1618. &HeadRec->igr_srclist[NumOldSources],
  1619. NumNewSources * sizeof(IPAddr));
  1620. // Append it
  1621. Rqe = Head;
  1622. QueueRecord(&Rqe, &Rec, RECORD_SIZE(NumNewSources,
  1623. Rec->igr_datalen));
  1624. }
  1625. // Get another ndis buffer for the body
  1626. Body = CTEAllocMemN(BodySize, 'bICT');
  1627. if (Body == NULL) {
  1628. FreeIGMPBuffer(HdrBuffer);
  1629. FlushIGMPv3Queue(Head);
  1630. return IP_NO_RESOURCES;
  1631. }
  1632. NdisAllocateBuffer(&NdisStatus, &BodyBuffer, BufferPool, Body, BodySize);
  1633. if (NdisStatus != NDIS_STATUS_SUCCESS) {
  1634. CTEFreeMem(Body);
  1635. FreeIGMPBuffer(HdrBuffer);
  1636. FlushIGMPv3Queue(Head);
  1637. return IP_NO_RESOURCES;
  1638. }
  1639. NDIS_BUFFER_LINKAGE(HdrBuffer) = BodyBuffer;
  1640. // Fill in records
  1641. NumRecords = 0;
  1642. BodySize = 0;
  1643. csum = 0;
  1644. while ((Rqe = Head) != NULL) {
  1645. if (BodySize + Rqe->i3qe_size > BodyMTU)
  1646. break;
  1647. // Remove from queue
  1648. Head = Rqe->i3qe_next;
  1649. Rqe->i3qe_next = NULL;
  1650. // update checksum
  1651. csum += xsum((uchar *)Rqe->i3qe_buff, Rqe->i3qe_size);
  1652. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_TX,
  1653. (DTEXT("SendRecord: Record=%x RecSize=%d Type=%d Group=%x Body=%x Offset=%d\n"),
  1654. Rqe->i3qe_buff, Rqe->i3qe_size, Rqe->i3qe_buff->igr_type,
  1655. Rqe->i3qe_buff->igr_addr, Body, BodySize));
  1656. RtlCopyMemory(Body + BodySize, (uchar *)Rqe->i3qe_buff,
  1657. Rqe->i3qe_size);
  1658. BodySize += Rqe->i3qe_size;
  1659. NumRecords++;
  1660. CTEFreeMem(Rqe->i3qe_buff);
  1661. CTEFreeMem(Rqe);
  1662. }
  1663. // Finish header
  1664. IGH->igh_xsum = 0;
  1665. IGH->igh_numrecords = net_short(NumRecords);
  1666. csum += xsum(IGH, sizeof(IGMPv3ReportHeader));
  1667. // Fold the checksum down.
  1668. csum = (csum >> 16) + (csum & 0xffff);
  1669. csum += (csum >> 16);
  1670. IGH->igh_xsum = (ushort)~csum;
  1671. Status = IGMPTransmit(HdrBuffer, Body, HdrSize + BodySize, SrcAddr,
  1672. IGMPV3_RTRS_MCAST);
  1673. }
  1674. return Status;
  1675. }
  1676. //* QueueIGMPv3GeneralResponse - compose and queue IGMPv3 responses to general
  1677. // query
  1678. IP_STATUS
  1679. QueueIGMPv3GeneralResponse(
  1680. IN IGMPv3RecordQueueEntry **pCurr,
  1681. IN NetTableEntry *NTE)
  1682. {
  1683. IGMPAddr **HashPtr, *AddrPtr;
  1684. uint i;
  1685. IGMPv3GroupRecord *StateRec;
  1686. uint StateRecSize;
  1687. uint BodyMTU;
  1688. BodyMTU = RECORD_MTU(NTE);
  1689. //
  1690. // Walk our list and set a random report timer for all those
  1691. // multicast addresses (except for the all-hosts address) that
  1692. // don't already have one running.
  1693. //
  1694. HashPtr = NTE->nte_igmplist;
  1695. if (HashPtr != NULL) {
  1696. for (i = 0; i < IGMP_TABLE_SIZE; i++) {
  1697. for (AddrPtr = HashPtr[i];
  1698. AddrPtr != NULL;
  1699. AddrPtr = AddrPtr->iga_next)
  1700. {
  1701. if (IP_ADDR_EQUAL(AddrPtr->iga_addr, ALL_HOST_MCAST))
  1702. continue;
  1703. if (AddrPtr->iga_grefcnt == 0)
  1704. StateRec = GetIsInRecord(AddrPtr, &StateRecSize);
  1705. else
  1706. StateRec = GetIsExRecord(AddrPtr, &StateRecSize, BodyMTU);
  1707. QueueRecord(pCurr, &StateRec, StateRecSize);
  1708. }
  1709. }
  1710. }
  1711. return IP_SUCCESS;
  1712. }
  1713. //* QueueOldReport - create and queue an IGMPv1/v2 membership report to be sent
  1714. IP_STATUS
  1715. QueueOldReport(
  1716. IN IGMPReportQueueEntry **pCurr,
  1717. IN uint ChangeType,
  1718. IN uint IgmpVersion,
  1719. IN IPAddr Group)
  1720. {
  1721. IGMPReportQueueEntry *rqe;
  1722. IGMPHeader *IGH;
  1723. uint ReportType, Size;
  1724. IPAddr Dest;
  1725. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_TX,
  1726. (DTEXT("QueueOldReport: Type=%d Vers=%d Group=%x\n"),
  1727. ChangeType, IgmpVersion, Group));
  1728. //
  1729. // Make sure we never queue a report for the all-hosts mcast address.
  1730. //
  1731. if (IP_ADDR_EQUAL(Group, ALL_HOST_MCAST)) {
  1732. return IP_BAD_REQ;
  1733. }
  1734. //
  1735. // If the report to be sent is a "Leave Group" report but we have
  1736. // detected an igmp v1 router on this net, do not send the report
  1737. //
  1738. if (IgmpVersion == IGMPV1) {
  1739. if (ChangeType == IGMP_DELETE) {
  1740. return IP_SUCCESS;
  1741. } else {
  1742. ReportType = IGMP_REPORT_V1;
  1743. Dest = Group;
  1744. }
  1745. } else {
  1746. if (ChangeType == IGMP_DELETE) {
  1747. ReportType = IGMP_LEAVE;
  1748. Dest = ALL_ROUTER_MCAST;
  1749. } else {
  1750. ReportType = IGMP_REPORT_V2;
  1751. Dest = Group;
  1752. }
  1753. }
  1754. // Allocate an IGMP report
  1755. Size = sizeof(IGMPHeader);
  1756. IGH = (IGMPHeader *) CTEAllocMemN(Size, 'hICT');
  1757. if (IGH == NULL) {
  1758. return IP_NO_RESOURCES;
  1759. }
  1760. IGH->igh_vertype = (UCHAR) ReportType;
  1761. IGH->igh_rsvd = 0;
  1762. IGH->igh_xsum = 0;
  1763. IGH->igh_addr = Group;
  1764. IGH->igh_xsum = ~xsum(IGH, Size);
  1765. // Allocate a queue entry
  1766. rqe = (IGMPReportQueueEntry *) CTEAllocMemN(sizeof(IGMPReportQueueEntry),
  1767. 'qICT');
  1768. if (rqe == NULL) {
  1769. CTEFreeMem(IGH);
  1770. return IP_NO_RESOURCES;
  1771. }
  1772. rqe->iqe_next = NULL;
  1773. rqe->iqe_buff = IGH;
  1774. rqe->iqe_size = Size;
  1775. rqe->iqe_dest = Dest;
  1776. ASSERT((IGH != NULL) && (Size > 0));
  1777. // Append to queue
  1778. (*pCurr)->iqe_next = rqe;
  1779. *pCurr = rqe;
  1780. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_TX,
  1781. (DTEXT("QueueOldReport: added rqe=%x buff=%x size=%d\n"),
  1782. rqe, rqe->iqe_buff, rqe->iqe_size));
  1783. return IP_SUCCESS;
  1784. }
  1785. //* SendOldReport - send an IGMPv1/v2 membership report
  1786. IP_STATUS
  1787. SendOldReport(
  1788. IN IGMPReportQueueEntry *Rqe,
  1789. IN IPAddr SrcAddr)
  1790. {
  1791. PNDIS_BUFFER Buffer;
  1792. uint Size;
  1793. IGMPHeader *IGH;
  1794. uchar *IGH2;
  1795. IPAddr DestAddr;
  1796. //ASSERT(!IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR));
  1797. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_TX,
  1798. (DTEXT("SendOldReport: rqe=%x buff=%x size=%x\n"),
  1799. Rqe, Rqe->iqe_buff, Rqe->iqe_size));
  1800. IGH = Rqe->iqe_buff;
  1801. ASSERT(IGH != NULL);
  1802. Size = Rqe->iqe_size;
  1803. ASSERT(Size > 0);
  1804. DestAddr = Rqe->iqe_dest;
  1805. IGH2 = (uchar*)GetIGMPBuffer(Size, &Buffer);
  1806. if (IGH2 == NULL) {
  1807. CTEFreeMem(IGH);
  1808. Rqe->iqe_buff = NULL;
  1809. return IP_NO_RESOURCES;
  1810. }
  1811. RtlCopyMemory(IGH2, (uchar *)IGH, Size);
  1812. CTEFreeMem(IGH);
  1813. Rqe->iqe_buff = NULL;
  1814. return IGMPTransmit(Buffer, NULL, Size, SrcAddr, DestAddr);
  1815. }
  1816. //* SendOldReports - send pending IGMPv1/v2 membership reports
  1817. void
  1818. SendOldReports(
  1819. IN IGMPReportQueueEntry *Head,
  1820. IN IPAddr SrcAddr)
  1821. {
  1822. IGMPReportQueueEntry *rqe;
  1823. while ((rqe = Head) != NULL) {
  1824. // Remove from queue
  1825. Head = rqe->iqe_next;
  1826. rqe->iqe_next = NULL;
  1827. SendOldReport(rqe, SrcAddr);
  1828. CTEFreeMem(rqe);
  1829. }
  1830. }
  1831. //////////////////////////////////////////////////////////////////////////////
  1832. // Mark changes for triggered reports
  1833. //////////////////////////////////////////////////////////////////////////////
  1834. // Should only be called for leaves if in IGMPv3 mode,
  1835. // but should be called for joins always.
  1836. void
  1837. MarkGroup(
  1838. IN IGMPAddr *Grp)
  1839. {
  1840. // No reports are sent for the ALL_HOST_MCAST group
  1841. if (IP_ADDR_EQUAL(Grp->iga_addr, ALL_HOST_MCAST)) {
  1842. return;
  1843. }
  1844. Grp->iga_changetype = MODE_CHANGE;
  1845. Grp->iga_xmitleft = g_IgmpRobustness;
  1846. }
  1847. // Should only be called if in IGMPv3 mode
  1848. void
  1849. MarkSource(
  1850. IN IGMPAddr *Grp,
  1851. IN IGMPSrcAddr *Src)
  1852. {
  1853. // No reports are sent for the ALL_HOST_MCAST group
  1854. if (IP_ADDR_EQUAL(Grp->iga_addr, ALL_HOST_MCAST)) {
  1855. return;
  1856. }
  1857. Src->isa_xmitleft = g_IgmpRobustness;
  1858. Grp->iga_xmitleft = g_IgmpRobustness;
  1859. if (Grp->iga_changetype == NO_CHANGE) {
  1860. Grp->iga_changetype = SOURCE_CHANGE;
  1861. }
  1862. }
  1863. //* IGMPDelExclList - delete sources from an internal source exclude list
  1864. //
  1865. // This never affects link-layer filters.
  1866. // Assumes caller holds lock on NTE
  1867. void
  1868. IGMPDelExclList(
  1869. IN NetTableEntry *NTE,
  1870. IN IGMPAddr *PrevAddrPtr,
  1871. IN OUT IGMPAddr **pAddrPtr,
  1872. IN uint NumDelSources,
  1873. IN IPAddr *DelSourceList,
  1874. IN BOOLEAN AllowMsg)
  1875. {
  1876. uint i;
  1877. IGMPSrcAddr *Src, *PrevSrc;
  1878. DEBUGMSG(DBG_TRACE && DBG_IGMP,
  1879. (DTEXT("IGMPDelExclList: AddrPtr=%x NumDelSources=%d DelSourceList=%x\n"),
  1880. *pAddrPtr, NumDelSources, DelSourceList));
  1881. for (i=0; i<NumDelSources; i++) {
  1882. // Find the source entry
  1883. Src = FindIGMPSrcAddr(*pAddrPtr, DelSourceList[i], &PrevSrc);
  1884. // Break if not there or xrefcnt=0
  1885. ASSERT(Src && (Src->isa_xrefcnt!=0));
  1886. if (AllowMsg && (NTE->nte_if->IgmpVersion == IGMPV3)) {
  1887. // If all sockets exclude and no sockets include, add source
  1888. // to IGMP ALLOW message
  1889. if (!IS_SOURCE_ALLOWED(*pAddrPtr, Src)) {
  1890. // Add source to ALLOW message
  1891. MarkSource(*pAddrPtr, Src);
  1892. }
  1893. }
  1894. // Decrement the xrefcnt
  1895. Src->isa_xrefcnt--;
  1896. // If irefcnt and xrefcnt are both 0 and no rexmits left,
  1897. // delete the source entry
  1898. if (IS_SOURCE_DELETABLE(Src))
  1899. DeleteIGMPSrcAddr(PrevSrc, &Src);
  1900. // If the group refcount=0, and srclist is null, delete group entry
  1901. if (IS_GROUP_DELETABLE(*pAddrPtr))
  1902. DeleteIGMPAddr(NTE, PrevAddrPtr, pAddrPtr);
  1903. }
  1904. }
  1905. //* IGMPDelInclList - delete sources from an internal source include list
  1906. //
  1907. // Assumes caller holds lock on NTE
  1908. void
  1909. IGMPDelInclList(
  1910. IN CTELockHandle *pHandle,
  1911. IN NetTableEntry *NTE,
  1912. IN IGMPAddr **pPrevAddrPtr,
  1913. IN OUT IGMPAddr **pAddrPtr,
  1914. IN uint NumDelSources,
  1915. IN IPAddr *DelSourceList,
  1916. IN BOOLEAN BlockMsg)
  1917. {
  1918. uint i;
  1919. IGMPSrcAddr *Src, *PrevSrc;
  1920. BOOLEAN GroupWasAllowed;
  1921. BOOLEAN GroupNowAllowed;
  1922. IPAddr Addr;
  1923. DEBUGMSG(DBG_TRACE && DBG_IGMP,
  1924. (DTEXT("IGMPDelInclList: AddrPtr=%x NumDelSources=%d DelSourceList=%x\n"),
  1925. *pAddrPtr, NumDelSources, DelSourceList));
  1926. Addr = (*pAddrPtr)->iga_addr;
  1927. GroupWasAllowed = IS_GROUP_ALLOWED(*pAddrPtr);
  1928. for (i=0; i<NumDelSources; i++) {
  1929. // Find the source entry
  1930. Src = FindIGMPSrcAddr(*pAddrPtr, DelSourceList[i], &PrevSrc);
  1931. // Break if not there or irefcnt=0
  1932. ASSERT(Src && (Src->isa_irefcnt!=0));
  1933. // Decrement the irefcnt
  1934. Src->isa_irefcnt--;
  1935. if (Src->isa_irefcnt == 0) {
  1936. (*pAddrPtr)->iga_isrccnt--;
  1937. }
  1938. if (BlockMsg && (NTE->nte_if->IgmpVersion == IGMPV3)) {
  1939. // If all sockets exclude and no sockets include, add source
  1940. // to IGMP BLOCK message
  1941. if (!IS_SOURCE_ALLOWED(*pAddrPtr, Src)) {
  1942. // Add source to BLOCK message
  1943. MarkSource(*pAddrPtr, Src);
  1944. }
  1945. }
  1946. // If irefcnt and xrefcnt are both 0 and no rexmits left,
  1947. // delete the source entry
  1948. if (IS_SOURCE_DELETABLE(Src))
  1949. DeleteIGMPSrcAddr(PrevSrc, &Src);
  1950. // If the group refcount=0, and srclist is null, delete group entry
  1951. if (IS_GROUP_DELETABLE(*pAddrPtr))
  1952. DeleteIGMPAddr(NTE, *pPrevAddrPtr, pAddrPtr);
  1953. }
  1954. GroupNowAllowed = (BOOLEAN) ((*pAddrPtr != NULL) && IS_GROUP_ALLOWED(*pAddrPtr));
  1955. if (GroupWasAllowed && !GroupNowAllowed) {
  1956. if (*pAddrPtr) {
  1957. // Cancel response timer if running
  1958. CancelGroupResponseTimer(*pAddrPtr);
  1959. if (IS_GROUP_DELETABLE(*pAddrPtr))
  1960. DeleteIGMPAddr(NTE, *pPrevAddrPtr, pAddrPtr);
  1961. }
  1962. // update link-layer filter
  1963. CTEFreeLock(&NTE->nte_lock, *pHandle);
  1964. {
  1965. (*NTE->nte_if->if_deladdr) (NTE->nte_if->if_lcontext,
  1966. LLIP_ADDR_MCAST, Addr, 0);
  1967. }
  1968. CTEGetLock(&NTE->nte_lock, pHandle);
  1969. // Revalidate NTE, AddrPtr, PrevPtr
  1970. if (!(NTE->nte_flags & NTE_VALID)) {
  1971. *pAddrPtr = *pPrevAddrPtr = NULL;
  1972. return;
  1973. }
  1974. *pAddrPtr = FindIGMPAddr(NTE, Addr, pPrevAddrPtr);
  1975. }
  1976. }
  1977. //* IGMPAddExclList - add sources to an internal source exclude list
  1978. //
  1979. // This never affects link-layer filters.
  1980. // Assumes caller holds lock on NTE
  1981. // If failure results, the source list will be unchanged afterwards
  1982. // but the group entry may have been deleted.
  1983. IP_STATUS
  1984. IGMPAddExclList(
  1985. IN NetTableEntry *NTE,
  1986. IN IGMPAddr *PrevAddrPtr,
  1987. IN OUT IGMPAddr **pAddrPtr,
  1988. IN uint NumAddSources,
  1989. IN IPAddr *AddSourceList)
  1990. {
  1991. uint i;
  1992. IGMPSrcAddr *Src, *PrevSrc;
  1993. IP_STATUS Status = IP_SUCCESS;
  1994. DEBUGMSG(DBG_TRACE && DBG_IGMP,
  1995. (DTEXT("IGMPAddExclList: AddrPtr=%x NumAddSources=%d AddSourceList=%x\n"),
  1996. *pAddrPtr, NumAddSources, AddSourceList));
  1997. for (i=0; i<NumAddSources; i++) {
  1998. // If an IGMPSrcAddr entry for the source doesn't exist, create one.
  1999. Status = FindOrCreateIGMPSrcAddr(*pAddrPtr, AddSourceList[i], &Src,
  2000. &PrevSrc);
  2001. if (Status != IP_SUCCESS) {
  2002. break;
  2003. }
  2004. // Bump the xrefcnt on the source entry
  2005. Src->isa_xrefcnt++;
  2006. // If all sockets exclude and no sockets include, add source
  2007. // to IGMP BLOCK message
  2008. if (!IS_SOURCE_ALLOWED(*pAddrPtr, Src)
  2009. && (NTE->nte_if->IgmpVersion == IGMPV3)) {
  2010. // Add source to BLOCK message
  2011. MarkSource(*pAddrPtr, Src);
  2012. }
  2013. }
  2014. if (Status == IP_SUCCESS)
  2015. return Status;
  2016. // undo previous
  2017. IGMPDelExclList(NTE, PrevAddrPtr, pAddrPtr, i, AddSourceList, FALSE);
  2018. return Status;
  2019. }
  2020. //* IGMPAddInclList - add sources to an internal source include list
  2021. //
  2022. // Assumes caller holds lock on NTE
  2023. //
  2024. // If failure results, the source list will be unchanged afterwards
  2025. // but the group entry may have been deleted.
  2026. IP_STATUS
  2027. IGMPAddInclList(
  2028. IN CTELockHandle *pHandle,
  2029. IN NetTableEntry *NTE,
  2030. IN IGMPAddr **pPrevAddrPtr,
  2031. IN OUT IGMPAddr **pAddrPtr,
  2032. IN uint NumAddSources,
  2033. IN IPAddr *AddSourceList)
  2034. {
  2035. uint i, AddrAdded;
  2036. IGMPSrcAddr *Src, *PrevSrc;
  2037. IP_STATUS Status = IP_SUCCESS;
  2038. BOOLEAN GroupWasAllowed;
  2039. BOOLEAN GroupNowAllowed;
  2040. IPAddr Addr;
  2041. DEBUGMSG(DBG_TRACE && DBG_IGMP,
  2042. (DTEXT("IGMPAddInclList: AddrPtr=%x NumAddSources=%d AddSourceList=%x\n"),
  2043. *pAddrPtr, NumAddSources, AddSourceList));
  2044. Addr = (*pAddrPtr)->iga_addr;
  2045. GroupWasAllowed = IS_GROUP_ALLOWED(*pAddrPtr);
  2046. for (i=0; i<NumAddSources; i++) {
  2047. // If an IGMPSrcAddr entry for the source doesn't exist, create one.
  2048. Status = FindOrCreateIGMPSrcAddr(*pAddrPtr, AddSourceList[i], &Src,
  2049. &PrevSrc);
  2050. if (Status != IP_SUCCESS) {
  2051. break;
  2052. }
  2053. // If all sockets exclude and no sockets include, add source
  2054. // to IGMP ALLOW message
  2055. if (!IS_SOURCE_ALLOWED(*pAddrPtr, Src)
  2056. && (NTE->nte_if->IgmpVersion == IGMPV3)) {
  2057. // Add source to ALLOW message
  2058. MarkSource(*pAddrPtr, Src);
  2059. }
  2060. // Bump the irefcnt on the source entry
  2061. if (Src->isa_irefcnt == 0) {
  2062. (*pAddrPtr)->iga_isrccnt++;
  2063. }
  2064. Src->isa_irefcnt++;
  2065. }
  2066. GroupNowAllowed = IS_GROUP_ALLOWED(*pAddrPtr);
  2067. if (!GroupWasAllowed && GroupNowAllowed) {
  2068. // update link-layer filter
  2069. CTEFreeLock(&NTE->nte_lock, *pHandle);
  2070. {
  2071. AddrAdded = (*NTE->nte_if->if_addaddr) (NTE->nte_if->if_lcontext,
  2072. LLIP_ADDR_MCAST, Addr, 0, NULL);
  2073. }
  2074. CTEGetLock(&NTE->nte_lock, pHandle);
  2075. // Revalidate NTE, AddrPtr, PrevPtr
  2076. if (!(NTE->nte_flags & NTE_VALID)) {
  2077. Status = IP_BAD_REQ;
  2078. } else {
  2079. // Find the IGMPAddr entry
  2080. *pAddrPtr = FindIGMPAddr(NTE, Addr, pPrevAddrPtr);
  2081. if (!*pAddrPtr) {
  2082. Status = IP_BAD_REQ;
  2083. }
  2084. }
  2085. if (!AddrAdded) {
  2086. Status = IP_NO_RESOURCES;
  2087. }
  2088. }
  2089. if (Status == IP_SUCCESS)
  2090. return Status;
  2091. // undo previous
  2092. IGMPDelInclList(pHandle, NTE, pPrevAddrPtr, pAddrPtr, i, AddSourceList,
  2093. FALSE);
  2094. return Status;
  2095. }
  2096. //* IGMPInclChange - update source inclusion list
  2097. //
  2098. // On failure, inclusion list will be unchanged
  2099. IP_STATUS
  2100. IGMPInclChange(
  2101. IN NetTableEntry *NTE,
  2102. IN IPAddr Addr,
  2103. IN uint NumAddSources,
  2104. IN IPAddr *AddSourceList,
  2105. IN uint NumDelSources,
  2106. IN IPAddr *DelSourceList)
  2107. {
  2108. CTELockHandle Handle;
  2109. IGMPAddr *AddrPtr, *PrevPtr;
  2110. IP_STATUS Status;
  2111. Interface *IF;
  2112. IGMPBlockStruct Block;
  2113. IGMPBlockStruct *BlockPtr;
  2114. uint IgmpVersion = 0, BodyMTU = 0;
  2115. IPAddr SrcAddr = 0;
  2116. IGMPv3GroupRecord *AllowRec = NULL, *BlockRec = NULL;
  2117. uint AllowRecSize = 0, BlockRecSize = 0;
  2118. BOOLEAN GroupWasAllowed = FALSE;
  2119. BOOLEAN GroupNowAllowed = FALSE;
  2120. // First make sure we're at level 2 of IGMP support.
  2121. if (IGMPLevel != 2)
  2122. return IP_BAD_REQ;
  2123. // Make sure addlist and dellist aren't both empty
  2124. ASSERT((NumAddSources > 0) || (NumDelSources > 0));
  2125. if (NTE->nte_flags & NTE_VALID) {
  2126. //
  2127. // If this is an unnumbered interface
  2128. //
  2129. if ((NTE->nte_if->if_flags & IF_FLAGS_NOIPADDR) &&
  2130. IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
  2131. SrcAddr = g_ValidAddr;
  2132. if (IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR)) {
  2133. return IP_BAD_REQ;
  2134. }
  2135. } else {
  2136. SrcAddr = NTE->nte_addr;
  2137. }
  2138. }
  2139. CTEInitBlockStruc(&Block.ibs_block);
  2140. // Make sure we're the only ones in this routine. If someone else is
  2141. // already here, block.
  2142. CTEGetLock(&IGMPLock, &Handle);
  2143. if (IGMPBlockFlag) {
  2144. // Someone else is already here. Walk down the block list, and
  2145. // put ourselves on the end. Then free the lock and block on our
  2146. // IGMPBlock structure.
  2147. BlockPtr = STRUCT_OF(IGMPBlockStruct, &IGMPBlockList, ibs_next);
  2148. while (BlockPtr->ibs_next != NULL)
  2149. BlockPtr = BlockPtr->ibs_next;
  2150. Block.ibs_next = NULL;
  2151. BlockPtr->ibs_next = &Block;
  2152. CTEFreeLock(&IGMPLock, Handle);
  2153. CTEBlock(&Block.ibs_block);
  2154. } else {
  2155. // Noone else here, set the flag so noone else gets in and free the
  2156. // lock.
  2157. IGMPBlockFlag = 1;
  2158. CTEFreeLock(&IGMPLock, Handle);
  2159. }
  2160. // Now we're in the routine, and we won't be reentered here by another
  2161. // thread of execution. Make sure everything's valid, and figure out
  2162. // what to do.
  2163. Status = IP_SUCCESS;
  2164. // Now get the lock on the NTE and make sure it's valid.
  2165. CTEGetLock(&NTE->nte_lock, &Handle);
  2166. {
  2167. if (!(NTE->nte_flags & NTE_VALID)) {
  2168. Status = IP_BAD_REQ;
  2169. goto Done;
  2170. }
  2171. IF = NTE->nte_if;
  2172. BodyMTU = RECORD_MTU(NTE);
  2173. IgmpVersion = IF->IgmpVersion;
  2174. // If an IGMPAddr entry for the group on the interface doesn't
  2175. // exist, create one.
  2176. Status = FindOrCreateIGMPAddr(NTE, Addr, &AddrPtr, &PrevPtr);
  2177. if (Status != IP_SUCCESS) {
  2178. goto Done;
  2179. }
  2180. GroupWasAllowed = IS_GROUP_ALLOWED(AddrPtr);
  2181. // Perform IADDLIST
  2182. Status = IGMPAddInclList(&Handle, NTE, &PrevPtr, &AddrPtr,
  2183. NumAddSources, AddSourceList);
  2184. if (Status != IP_SUCCESS) {
  2185. goto Done;
  2186. }
  2187. // Perform IDELLLIST
  2188. IGMPDelInclList(&Handle, NTE, &PrevPtr, &AddrPtr,
  2189. NumDelSources, DelSourceList, TRUE);
  2190. if (AddrPtr == NULL) {
  2191. GroupNowAllowed = FALSE;
  2192. goto Done;
  2193. } else {
  2194. GroupNowAllowed = IS_GROUP_ALLOWED(AddrPtr);
  2195. }
  2196. if (IgmpVersion == IGMPV3) {
  2197. // Get ALLOC/BLOCK records
  2198. AllowRec = GetAllowRecord(AddrPtr, &AllowRecSize);
  2199. BlockRec = GetBlockRecord(AddrPtr, &BlockRecSize);
  2200. // Set retransmission timer
  2201. AddrPtr->iga_trtimer = IGMPRandomTicks(UNSOLICITED_REPORT_INTERVAL);
  2202. } else if (!GroupWasAllowed && GroupNowAllowed) {
  2203. // Set retransmission timer only for joins, not leaves
  2204. MarkGroup(AddrPtr);
  2205. AddrPtr->iga_trtimer = IGMPRandomTicks(UNSOLICITED_REPORT_INTERVAL);
  2206. }
  2207. }
  2208. Done:
  2209. CTEFreeLock(&NTE->nte_lock, Handle);
  2210. if (IgmpVersion == IGMPV3) {
  2211. IGMPv3RecordQueueEntry *Head = NULL, *rqe;
  2212. rqe = STRUCT_OF(IGMPv3RecordQueueEntry, &Head, i3qe_next);
  2213. // Send IGMP ALLOW/BLOCK messages if non-empty
  2214. QueueRecord(&rqe, &AllowRec, AllowRecSize);
  2215. QueueRecord(&rqe, &BlockRec, BlockRecSize);
  2216. SendIGMPv3Reports(Head, SrcAddr, BodyMTU);
  2217. } else if (!GroupWasAllowed && GroupNowAllowed) {
  2218. IGMPReportQueueEntry *Head = NULL, *rqe;
  2219. rqe = STRUCT_OF(IGMPReportQueueEntry, &Head, iqe_next);
  2220. QueueOldReport(&rqe, IGMP_ADD, IgmpVersion, Addr);
  2221. SendOldReports(Head, SrcAddr);
  2222. } else if (GroupWasAllowed && !GroupNowAllowed) {
  2223. IGMPReportQueueEntry *Head = NULL, *rqe;
  2224. rqe = STRUCT_OF(IGMPReportQueueEntry, &Head, iqe_next);
  2225. QueueOldReport(&rqe, IGMP_DELETE, IgmpVersion, Addr);
  2226. SendOldReports(Head, SrcAddr);
  2227. }
  2228. // We finished the request, and Status contains the completion status.
  2229. // If there are any pending blocks for this routine, signal the next
  2230. // one now. Otherwise clear the block flag.
  2231. CTEGetLock(&IGMPLock, &Handle);
  2232. if ((BlockPtr = IGMPBlockList) != NULL) {
  2233. // Someone is blocking. Pull him from the list and signal him.
  2234. IGMPBlockList = BlockPtr->ibs_next;
  2235. CTEFreeLock(&IGMPLock, Handle);
  2236. CTESignal(&BlockPtr->ibs_block, IP_SUCCESS);
  2237. } else {
  2238. // No one blocking, just clear the flag.
  2239. IGMPBlockFlag = 0;
  2240. CTEFreeLock(&IGMPLock, Handle);
  2241. }
  2242. return Status;
  2243. }
  2244. //* IGMPExclChange - update source exclusion list
  2245. //
  2246. // On failure, exclusion list will be unchanged
  2247. IP_STATUS
  2248. IGMPExclChange(
  2249. IN NetTableEntry * NTE,
  2250. IN IPAddr Addr,
  2251. IN uint NumAddSources,
  2252. IN IPAddr * AddSourceList,
  2253. IN uint NumDelSources,
  2254. IN IPAddr * DelSourceList)
  2255. {
  2256. CTELockHandle Handle;
  2257. IGMPAddr *AddrPtr, *PrevPtr;
  2258. IP_STATUS Status;
  2259. Interface *IF;
  2260. IGMPBlockStruct Block;
  2261. IGMPBlockStruct *BlockPtr;
  2262. uint IgmpVersion = 0, BodyMTU = 0;
  2263. IPAddr SrcAddr = 0;
  2264. IGMPv3GroupRecord *AllowRec = NULL, *BlockRec = NULL;
  2265. uint AllowRecSize = 0, BlockRecSize = 0;
  2266. // First make sure we're at level 2 of IGMP support.
  2267. if (IGMPLevel != 2)
  2268. return IP_BAD_REQ;
  2269. // Make sure addlist and dellist aren't both empty
  2270. ASSERT((NumAddSources > 0) || (NumDelSources > 0));
  2271. if (NTE->nte_flags & NTE_VALID) {
  2272. //
  2273. // If this is an unnumbered interface
  2274. //
  2275. if ((NTE->nte_if->if_flags & IF_FLAGS_NOIPADDR) &&
  2276. IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
  2277. SrcAddr = g_ValidAddr;
  2278. if (IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR)) {
  2279. return IP_BAD_REQ;
  2280. }
  2281. } else {
  2282. SrcAddr = NTE->nte_addr;
  2283. }
  2284. }
  2285. CTEInitBlockStruc(&Block.ibs_block);
  2286. // Make sure we're the only ones in this routine. If someone else is
  2287. // already here, block.
  2288. CTEGetLock(&IGMPLock, &Handle);
  2289. if (IGMPBlockFlag) {
  2290. // Someone else is already here. Walk down the block list, and
  2291. // put ourselves on the end. Then free the lock and block on our
  2292. // IGMPBlock structure.
  2293. BlockPtr = STRUCT_OF(IGMPBlockStruct, &IGMPBlockList, ibs_next);
  2294. while (BlockPtr->ibs_next != NULL)
  2295. BlockPtr = BlockPtr->ibs_next;
  2296. Block.ibs_next = NULL;
  2297. BlockPtr->ibs_next = &Block;
  2298. CTEFreeLock(&IGMPLock, Handle);
  2299. CTEBlock(&Block.ibs_block);
  2300. } else {
  2301. // No one else here, set the flag so no one else gets in and free the
  2302. // lock.
  2303. IGMPBlockFlag = 1;
  2304. CTEFreeLock(&IGMPLock, Handle);
  2305. }
  2306. // Now we're in the routine, and we won't be reentered here by another
  2307. // thread of execution. Make sure everything's valid, and figure out
  2308. // what to do.
  2309. Status = IP_SUCCESS;
  2310. // Now get the lock on the NTE and make sure it's valid.
  2311. CTEGetLock(&NTE->nte_lock, &Handle);
  2312. {
  2313. if (!(NTE->nte_flags & NTE_VALID)) {
  2314. Status = IP_BAD_REQ;
  2315. goto Done;
  2316. }
  2317. IF = NTE->nte_if;
  2318. BodyMTU = RECORD_MTU(NTE);
  2319. IgmpVersion = IF->IgmpVersion;
  2320. // Find the IGMPAddr entry
  2321. AddrPtr = FindIGMPAddr(NTE, Addr, &PrevPtr);
  2322. // Break if not there or refcount=0
  2323. ASSERT(AddrPtr && (AddrPtr->iga_grefcnt!=0));
  2324. // Perform XADDLIST
  2325. Status = IGMPAddExclList(NTE, PrevPtr, &AddrPtr, NumAddSources,
  2326. AddSourceList);
  2327. if (Status != IP_SUCCESS) {
  2328. goto Done;
  2329. }
  2330. // Perform XDELLLIST
  2331. IGMPDelExclList(NTE, PrevPtr, &AddrPtr, NumDelSources, DelSourceList,
  2332. TRUE);
  2333. // Don't need to reget AddrPtr here since the NTE lock is never
  2334. // released while modifying the exclusion list above, since the
  2335. // linklayer filter is unaffected.
  2336. if (IgmpVersion == IGMPV3) {
  2337. AllowRec = GetAllowRecord(AddrPtr, &AllowRecSize);
  2338. BlockRec = GetBlockRecord(AddrPtr, &BlockRecSize);
  2339. // Set retransmission timer
  2340. AddrPtr->iga_trtimer = IGMPRandomTicks(UNSOLICITED_REPORT_INTERVAL);
  2341. }
  2342. }
  2343. Done:
  2344. CTEFreeLock(&NTE->nte_lock, Handle);
  2345. // Since AddrPtr->iga_grefcnt cannot be zero, and is unchanged by
  2346. // this function, we never need to update the link-layer filter.
  2347. // Send IGMP ALLOW/BLOCK messages if non-empty
  2348. // Note that we never need to do anything here in IGMPv1/v2 mode.
  2349. if (IgmpVersion == IGMPV3) {
  2350. IGMPv3RecordQueueEntry *Head = NULL, *rqe;
  2351. rqe = STRUCT_OF(IGMPv3RecordQueueEntry, &Head, i3qe_next);
  2352. QueueRecord(&rqe, &AllowRec, AllowRecSize);
  2353. QueueRecord(&rqe, &BlockRec, BlockRecSize);
  2354. SendIGMPv3Reports(Head, SrcAddr, BodyMTU);
  2355. }
  2356. // We finished the request, and Status contains the completion status.
  2357. // If there are any pending blocks for this routine, signal the next
  2358. // one now. Otherwise clear the block flag.
  2359. CTEGetLock(&IGMPLock, &Handle);
  2360. if ((BlockPtr = IGMPBlockList) != NULL) {
  2361. // Someone is blocking. Pull him from the list and signal him.
  2362. IGMPBlockList = BlockPtr->ibs_next;
  2363. CTEFreeLock(&IGMPLock, Handle);
  2364. CTESignal(&BlockPtr->ibs_block, IP_SUCCESS);
  2365. } else {
  2366. // No one blocking, just clear the flag.
  2367. IGMPBlockFlag = 0;
  2368. CTEFreeLock(&IGMPLock, Handle);
  2369. }
  2370. return Status;
  2371. }
  2372. //* JoinIGMPAddr - add a membership reference to an entire group, and
  2373. // update associated source list refcounts.
  2374. //
  2375. // On failure, state will remain unchanged.
  2376. IP_STATUS
  2377. JoinIGMPAddr(
  2378. IN NetTableEntry *NTE,
  2379. IN IPAddr Addr,
  2380. IN uint NumExclSources,
  2381. IN OUT IPAddr *ExclSourceList, // volatile
  2382. IN uint NumInclSources,
  2383. IN IPAddr *InclSourceList,
  2384. IN IPAddr SrcAddr)
  2385. {
  2386. IGMPAddr *AddrPtr, *PrevPtr;
  2387. IGMPSrcAddr *SrcAddrPtr, *PrevSrc;
  2388. Interface *IF;
  2389. uint IgmpVersion = 0, i, AddrAdded, BodyMTU = 0;
  2390. IP_STATUS Status;
  2391. CTELockHandle Handle;
  2392. IGMPv3GroupRecord *ToExRec = NULL, *AllowRec = NULL, *BlockRec = NULL;
  2393. uint ToExRecSize = 0, AllowRecSize = 0, BlockRecSize = 0;
  2394. BOOLEAN GroupWasAllowed = FALSE;
  2395. uint InitialRefOnIgmpAddr;
  2396. Status = IP_SUCCESS;
  2397. CTEGetLock(&NTE->nte_lock, &Handle);
  2398. {
  2399. if (!(NTE->nte_flags & NTE_VALID)) {
  2400. Status = IP_BAD_REQ;
  2401. goto Done;
  2402. }
  2403. IF = NTE->nte_if;
  2404. IgmpVersion = IF->IgmpVersion;
  2405. BodyMTU = RECORD_MTU(NTE);
  2406. // If no group entry exists, create one in exclusion mode
  2407. Status = FindOrCreateIGMPAddr(NTE, Addr, &AddrPtr, &PrevPtr);
  2408. if (Status != IP_SUCCESS) {
  2409. goto Done;
  2410. }
  2411. // Store the ref count at this point in a local variable.
  2412. InitialRefOnIgmpAddr = AddrPtr->iga_grefcnt;
  2413. GroupWasAllowed = IS_GROUP_ALLOWED(AddrPtr);
  2414. if (!GroupWasAllowed) {
  2415. // We have to be careful not to release the lock while
  2416. // IS_GROUP_DELETABLE() is true, or else it might be
  2417. // deleted by IGMPTimer(). So before releasing the lock,
  2418. // we bump the join refcount (which we want to do anyway
  2419. // later on, so it won't hurt anything now).
  2420. (AddrPtr->iga_grefcnt)++;
  2421. // Update link-layer filter
  2422. CTEFreeLock(&NTE->nte_lock, Handle);
  2423. {
  2424. AddrAdded = (*IF->if_addaddr) (IF->if_lcontext,
  2425. LLIP_ADDR_MCAST, Addr, 0, NULL);
  2426. }
  2427. CTEGetLock(&NTE->nte_lock, &Handle);
  2428. // Revalidate NTE, AddrPtr, PrevPtr
  2429. if (!(NTE->nte_flags & NTE_VALID)) {
  2430. // Don't need to undo any refcount here as the refcount
  2431. // was blown away by StopIGMPForNTE.
  2432. Status = IP_BAD_REQ;
  2433. goto Done;
  2434. }
  2435. // Find the IGMPAddr entry
  2436. AddrPtr = FindIGMPAddr(NTE, Addr, &PrevPtr);
  2437. if (!AddrPtr) {
  2438. Status = IP_BAD_REQ;
  2439. goto Done;
  2440. }
  2441. // Now release the refcount we grabbed above
  2442. // so the rest of the logic is the same for
  2443. // all cases.
  2444. (AddrPtr->iga_grefcnt)--;
  2445. if (!AddrAdded) {
  2446. if (IS_GROUP_DELETABLE(AddrPtr))
  2447. DeleteIGMPAddr(NTE, PrevPtr, &AddrPtr);
  2448. Status = IP_NO_RESOURCES;
  2449. goto Done;
  2450. }
  2451. }
  2452. // For each existing source entry,
  2453. // If not in {xaddlist}, xrefcnt=refcount, irefcnt=0
  2454. // Add source to ALLOW message
  2455. // If in {xaddlist},
  2456. // Increment xrefcnt and remove from {xaddlist}
  2457. for (SrcAddrPtr = AddrPtr->iga_srclist;
  2458. SrcAddrPtr;
  2459. SrcAddrPtr = SrcAddrPtr->isa_next) {
  2460. for (i=0; i<NumExclSources; i++) {
  2461. if (IP_ADDR_EQUAL(SrcAddrPtr->isa_addr, ExclSourceList[i])) {
  2462. (SrcAddrPtr->isa_xrefcnt)++;
  2463. ExclSourceList[i] = ExclSourceList[--NumExclSources];
  2464. break;
  2465. }
  2466. }
  2467. if ((i == NumExclSources)
  2468. && !IS_SOURCE_ALLOWED(AddrPtr, SrcAddrPtr)
  2469. && (NTE->nte_if->IgmpVersion == IGMPV3)) {
  2470. // Add source to ALLOW message
  2471. MarkSource(AddrPtr, SrcAddrPtr);
  2472. }
  2473. }
  2474. // The purpose of this check is to mark this Address 'only the first time'.
  2475. // To take care of race conditions, this has to be stored in a local variable.
  2476. if (InitialRefOnIgmpAddr == 0) {
  2477. MarkGroup(AddrPtr);
  2478. }
  2479. // Bump the refcount on the group entry
  2480. (AddrPtr->iga_grefcnt)++;
  2481. // For each entry left in {xaddlist}
  2482. // Add source entry and increment xrefcnt
  2483. for (i=0; i<NumExclSources; i++) {
  2484. Status = CreateIGMPSrcAddr(AddrPtr, ExclSourceList[i],
  2485. &SrcAddrPtr, &PrevSrc);
  2486. if (Status != IP_SUCCESS) {
  2487. break;
  2488. }
  2489. (SrcAddrPtr->isa_xrefcnt)++;
  2490. }
  2491. if (Status != IP_SUCCESS) {
  2492. // undo source adds
  2493. IGMPDelExclList(NTE, PrevPtr, &AddrPtr, i, ExclSourceList, FALSE);
  2494. // undo group join
  2495. (AddrPtr->iga_grefcnt)--;
  2496. if (IS_GROUP_DELETABLE(AddrPtr))
  2497. DeleteIGMPAddr(NTE, PrevPtr, &AddrPtr);
  2498. goto Done;
  2499. }
  2500. // Perform IDELLIST
  2501. IGMPDelInclList(&Handle, NTE, &PrevPtr, &AddrPtr,
  2502. NumInclSources, InclSourceList, TRUE);
  2503. // Make sure AddrPtr didn't go away somehow
  2504. if (AddrPtr == NULL) {
  2505. Status = IP_BAD_REQ;
  2506. goto Done;
  2507. }
  2508. // No reports are sent for the ALL_HOST_MCAST group
  2509. if (!IP_ADDR_EQUAL(AddrPtr->iga_addr, ALL_HOST_MCAST)) {
  2510. if (IgmpVersion == IGMPV3) {
  2511. // If filter mode was inclusion,
  2512. // Send TO_EX with list of sources where irefcnt=0,xrefcnt=refcnt
  2513. // Else
  2514. // Send ALLOW/BLOCK messages if non-empty
  2515. if (AddrPtr->iga_grefcnt == 1) {
  2516. ToExRec = GetToExRecord( AddrPtr, &ToExRecSize, BodyMTU);
  2517. } else {
  2518. AllowRec = GetAllowRecord(AddrPtr, &AllowRecSize);
  2519. BlockRec = GetBlockRecord(AddrPtr, &BlockRecSize);
  2520. }
  2521. // set triggered group retransmission timer
  2522. AddrPtr->iga_trtimer = IGMPRandomTicks(UNSOLICITED_REPORT_INTERVAL);
  2523. } else if (!GroupWasAllowed) {
  2524. // Set retransmission timer
  2525. AddrPtr->iga_trtimer = IGMPRandomTicks(UNSOLICITED_REPORT_INTERVAL);
  2526. }
  2527. }
  2528. }
  2529. Done:
  2530. CTEFreeLock(&NTE->nte_lock, Handle);
  2531. if (Status != IP_SUCCESS) {
  2532. return Status;
  2533. }
  2534. if (IP_ADDR_EQUAL(Addr, ALL_HOST_MCAST))
  2535. return Status;
  2536. if (IgmpVersion == IGMPV3) {
  2537. IGMPv3RecordQueueEntry *Head = NULL, *rqe;
  2538. rqe = STRUCT_OF(IGMPv3RecordQueueEntry, &Head, i3qe_next);
  2539. QueueRecord(&rqe, &ToExRec, ToExRecSize);
  2540. QueueRecord(&rqe, &AllowRec, AllowRecSize);
  2541. QueueRecord(&rqe, &BlockRec, BlockRecSize);
  2542. SendIGMPv3Reports(Head, SrcAddr, BodyMTU);
  2543. } else if (!GroupWasAllowed) {
  2544. IGMPReportQueueEntry *Head = NULL, *rqe;
  2545. rqe = STRUCT_OF(IGMPReportQueueEntry, &Head, iqe_next);
  2546. QueueOldReport(&rqe, IGMP_ADD, IgmpVersion, Addr);
  2547. SendOldReports(Head, SrcAddr);
  2548. }
  2549. return Status;
  2550. }
  2551. //* LeaveIGMPAddr - remove a membership reference to an entire group, and
  2552. // update associated source list refcounts.
  2553. IP_STATUS
  2554. LeaveIGMPAddr(
  2555. IN NetTableEntry *NTE,
  2556. IN IPAddr Addr,
  2557. IN uint NumExclSources,
  2558. IN OUT IPAddr *ExclSourceList, // volatile
  2559. IN uint NumInclSources,
  2560. IN IPAddr *InclSourceList,
  2561. IN IPAddr SrcAddr)
  2562. {
  2563. IGMPAddr *AddrPtr, *PrevPtr;
  2564. IGMPSrcAddr *Src, *PrevSrc;
  2565. IP_STATUS Status;
  2566. CTELockHandle Handle;
  2567. Interface *IF = NULL;
  2568. uint IgmpVersion = 0, i, BodyMTU = 0;
  2569. BOOLEAN GroupNowAllowed = TRUE;
  2570. IGMPv3GroupRecord *ToInRec = NULL, *AllowRec = NULL, *BlockRec = NULL;
  2571. uint ToInRecSize = 0, AllowRecSize = 0, BlockRecSize = 0;
  2572. Status = IP_SUCCESS;
  2573. DEBUGMSG(DBG_TRACE && DBG_IGMP,
  2574. (DTEXT("LeaveIGMPAddr NTE=%x Addr=%x NumExcl=%d ExclSList=%x NumIncl=%d InclSList=%x SrcAddr=%x\n"),
  2575. NTE, Addr, NumExclSources, ExclSourceList, NumInclSources,
  2576. InclSourceList, SrcAddr));
  2577. // Now get the lock on the NTE and make sure it's valid.
  2578. CTEGetLock(&NTE->nte_lock, &Handle);
  2579. {
  2580. if (!(NTE->nte_flags & NTE_VALID)) {
  2581. Status = IP_BAD_REQ;
  2582. goto Done;
  2583. }
  2584. IF = NTE->nte_if;
  2585. IgmpVersion = IF->IgmpVersion;
  2586. BodyMTU = RECORD_MTU(NTE);
  2587. // The NTE is valid. Try to find an existing IGMPAddr structure
  2588. // that matches the input address.
  2589. AddrPtr = FindIGMPAddr(NTE, Addr, &PrevPtr);
  2590. // This is a delete request. If we didn't find the requested
  2591. // address, fail the request.
  2592. // For now, if the ref count is 0, we will treat it as equivalent to
  2593. // not-found. This is done to take care of the ref count on an
  2594. // IGMPAddr going bad because of a race condition between the
  2595. // invalidation and revalidation of an NTE and deletion and creation
  2596. // of an IGMPAddr.
  2597. if ((AddrPtr == NULL) || (AddrPtr->iga_grefcnt == 0)) {
  2598. Status = IP_BAD_REQ;
  2599. goto Done;
  2600. }
  2601. // Don't let the all-hosts mcast address go away.
  2602. if (IP_ADDR_EQUAL(Addr, ALL_HOST_MCAST)) {
  2603. goto Done;
  2604. }
  2605. // Perform IADDLIST
  2606. Status = IGMPAddInclList(&Handle, NTE, &PrevPtr, &AddrPtr,
  2607. NumInclSources, InclSourceList);
  2608. if (Status != IP_SUCCESS) {
  2609. goto Done;
  2610. }
  2611. // Decrement the refcount
  2612. ASSERT(AddrPtr->iga_grefcnt > 0);
  2613. AddrPtr->iga_grefcnt--;
  2614. if ((AddrPtr->iga_grefcnt == 0)
  2615. && (NTE->nte_if->IgmpVersion == IGMPV3)) {
  2616. // Leaves are only retransmitted in IGMPv3
  2617. MarkGroup(AddrPtr);
  2618. }
  2619. // For each existing source entry:
  2620. // If entry is not in {xdellist}, xrefcnt=refcnt, irefcnt=0,
  2621. // Add source to BLOCK message
  2622. // If entry is in {xdellist},
  2623. // Decrement xrefcnt and remove from {xdellist}
  2624. // If xrefcnt=irefcnt=0, delete entry
  2625. PrevSrc = STRUCT_OF(IGMPSrcAddr, &AddrPtr->iga_srclist, isa_next);
  2626. for (Src = AddrPtr->iga_srclist; Src; PrevSrc=Src,Src = Src->isa_next) {
  2627. for (i=0; i<NumExclSources; i++) {
  2628. if (IP_ADDR_EQUAL(Src->isa_addr, ExclSourceList[i])) {
  2629. (Src->isa_xrefcnt)--;
  2630. ExclSourceList[i] = ExclSourceList[--NumExclSources];
  2631. break;
  2632. }
  2633. }
  2634. if ((i == NumExclSources)
  2635. && !IS_SOURCE_ALLOWED(AddrPtr, Src)
  2636. && (NTE->nte_if->IgmpVersion == IGMPV3)) {
  2637. // Add source to BLOCK message
  2638. MarkSource(AddrPtr, Src);
  2639. }
  2640. if (IS_SOURCE_DELETABLE(Src)) {
  2641. DeleteIGMPSrcAddr(PrevSrc, &Src);
  2642. Src = PrevSrc;
  2643. }
  2644. }
  2645. // Break if {xdellist} is not empty
  2646. ASSERT(NumExclSources == 0);
  2647. if (IgmpVersion == IGMPV3) {
  2648. // If refcnt is 0
  2649. // Send TO_IN(null)
  2650. // Else
  2651. // Send ALLOW/BLOCK messages if non-empty
  2652. if (AddrPtr->iga_grefcnt == 0) {
  2653. ToInRec = GetToInRecord(AddrPtr, &ToInRecSize);
  2654. } else {
  2655. AllowRec = GetAllowRecord(AddrPtr, &AllowRecSize);
  2656. BlockRec = GetBlockRecord(AddrPtr, &BlockRecSize);
  2657. }
  2658. // set triggered group retransmission timer
  2659. if (ToInRec || AllowRec || BlockRec) {
  2660. AddrPtr->iga_trtimer = IGMPRandomTicks(UNSOLICITED_REPORT_INTERVAL);
  2661. }
  2662. }
  2663. // Note: IGMPv2 leaves are not retransmitted, hence no timer set.
  2664. GroupNowAllowed = IS_GROUP_ALLOWED(AddrPtr);
  2665. if (!GroupNowAllowed)
  2666. CancelGroupResponseTimer(AddrPtr);
  2667. // Delete the group entry if it's no longer needed
  2668. if (IS_GROUP_DELETABLE(AddrPtr))
  2669. DeleteIGMPAddr(NTE, PrevPtr, &AddrPtr);
  2670. }
  2671. Done:
  2672. CTEFreeLock(&NTE->nte_lock, Handle);
  2673. if (Status != IP_SUCCESS) {
  2674. return Status;
  2675. }
  2676. // Update link-layer filter
  2677. if (!GroupNowAllowed) {
  2678. (*IF->if_deladdr) (IF->if_lcontext, LLIP_ADDR_MCAST, Addr, 0);
  2679. }
  2680. if (IgmpVersion == IGMPV3) {
  2681. IGMPv3RecordQueueEntry *Head = NULL, *rqe;
  2682. rqe = STRUCT_OF(IGMPv3RecordQueueEntry, &Head, i3qe_next);
  2683. QueueRecord(&rqe, &ToInRec, ToInRecSize);
  2684. QueueRecord(&rqe, &AllowRec, AllowRecSize);
  2685. QueueRecord(&rqe, &BlockRec, BlockRecSize);
  2686. SendIGMPv3Reports(Head, SrcAddr, BodyMTU);
  2687. } else if (!GroupNowAllowed) {
  2688. IGMPReportQueueEntry *Head = NULL, *rqe;
  2689. rqe = STRUCT_OF(IGMPReportQueueEntry, &Head, iqe_next);
  2690. QueueOldReport(&rqe, IGMP_DELETE, IgmpVersion, Addr);
  2691. SendOldReports(Head, SrcAddr);
  2692. }
  2693. return Status;
  2694. }
  2695. //* LeaveAllIGMPAddr - remove all group references on an interface
  2696. IP_STATUS
  2697. LeaveAllIGMPAddr(
  2698. IN NetTableEntry *NTE,
  2699. IN IPAddr SrcAddr)
  2700. {
  2701. IGMPAddr **HashPtr, *Prev, *Next, *Curr;
  2702. IGMPSrcAddr *PrevSrc, *CurrSrc;
  2703. int i, Grefcnt;
  2704. IP_STATUS Status;
  2705. CTELockHandle Handle;
  2706. Interface *IF;
  2707. uint IgmpVersion = 0, BodyMTU = 0;
  2708. IPAddr Addr;
  2709. IGMPv3RecordQueueEntry *I3Head = NULL, *i3qe;
  2710. IGMPReportQueueEntry *OldHead = NULL, *iqe;
  2711. IGMPv3GroupRecord *Rec;
  2712. uint RecSize;
  2713. i3qe = STRUCT_OF(IGMPv3RecordQueueEntry, &I3Head, i3qe_next);
  2714. iqe = STRUCT_OF(IGMPReportQueueEntry, &OldHead, iqe_next);
  2715. // We've been called to delete all of the addresses,
  2716. // regardless of their reference count. This should only
  2717. // happen when the NTE is going away.
  2718. Status = IP_SUCCESS;
  2719. CTEGetLock(&NTE->nte_lock, &Handle);
  2720. {
  2721. HashPtr = NTE->nte_igmplist;
  2722. if (HashPtr == NULL) {
  2723. goto Done;
  2724. }
  2725. IF = NTE->nte_if;
  2726. BodyMTU = RECORD_MTU(NTE);
  2727. IgmpVersion = IF->IgmpVersion;
  2728. for (i = 0; (i < IGMP_TABLE_SIZE) && (NTE->nte_igmplist != NULL); i++) {
  2729. Curr = STRUCT_OF(IGMPAddr, &HashPtr[i], iga_next);
  2730. Next = HashPtr[i];
  2731. for (Prev=Curr,Curr=Next;
  2732. Curr && (NTE->nte_igmplist != NULL);
  2733. Prev=Curr,Curr=Next) {
  2734. Next = Curr->iga_next;
  2735. Grefcnt = Curr->iga_grefcnt;
  2736. Addr = Curr->iga_addr;
  2737. // Leave all sources
  2738. PrevSrc = STRUCT_OF(IGMPSrcAddr, &Curr->iga_srclist, isa_next);
  2739. for(CurrSrc=PrevSrc->isa_next;
  2740. CurrSrc;
  2741. PrevSrc=CurrSrc,CurrSrc=CurrSrc->isa_next) {
  2742. if (Grefcnt && IS_SOURCE_ALLOWED(Curr, CurrSrc)
  2743. && (IgmpVersion == IGMPV3)) {
  2744. // Add source to BLOCK message
  2745. MarkSource(Curr, CurrSrc);
  2746. }
  2747. // Force leave
  2748. CurrSrc->isa_irefcnt = 0;
  2749. CurrSrc->isa_xrefcnt = Curr->iga_grefcnt;
  2750. //
  2751. // We may be able to delete the source now,
  2752. // but not if it's marked for inclusion in a block
  2753. // message to be sent below.
  2754. //
  2755. if (IS_SOURCE_DELETABLE(CurrSrc)) {
  2756. DeleteIGMPSrcAddr(PrevSrc, &CurrSrc);
  2757. CurrSrc = PrevSrc;
  2758. }
  2759. }
  2760. // Force group leave
  2761. if (Grefcnt > 0) {
  2762. Curr->iga_grefcnt = 0;
  2763. // Leaves are only retransmitted in IGMPv3, where
  2764. // state will actually be deleted once retransmissions
  2765. // are complete.
  2766. if (IgmpVersion == IGMPV3)
  2767. MarkGroup(Curr);
  2768. CancelGroupResponseTimer(Curr);
  2769. //
  2770. // We may be able to delete the group now,
  2771. // but not if it's marked for inclusion in an IGMPv3
  2772. // leave to be sent below.
  2773. //
  2774. if (IS_GROUP_DELETABLE(Curr))
  2775. DeleteIGMPAddr(NTE, Prev, &Curr);
  2776. }
  2777. // Queue triggered messages
  2778. if (!IP_ADDR_EQUAL(Addr, ALL_HOST_MCAST)) {
  2779. if (IgmpVersion < IGMPV3) {
  2780. QueueOldReport(&iqe, IGMP_DELETE, IgmpVersion,Addr);
  2781. } else if (Grefcnt > 0) {
  2782. // queue TO_IN
  2783. Rec = GetToInRecord(Curr, &RecSize);
  2784. QueueRecord(&i3qe, &Rec, RecSize);
  2785. } else {
  2786. // queue BLOCK
  2787. Rec = GetBlockRecord(Curr, &RecSize);
  2788. QueueRecord(&i3qe, &Rec, RecSize);
  2789. }
  2790. }
  2791. // If we haven't deleted the group yet, delete it now
  2792. if (Curr != NULL) {
  2793. // Delete any leftover sources
  2794. PrevSrc = STRUCT_OF(IGMPSrcAddr, &Curr->iga_srclist,
  2795. isa_next);
  2796. while (Curr->iga_srclist != NULL) {
  2797. CurrSrc = Curr->iga_srclist;
  2798. CurrSrc->isa_irefcnt = CurrSrc->isa_xrefcnt = 0;
  2799. CurrSrc->isa_xmitleft = CurrSrc->isa_csmarked = 0;
  2800. DeleteIGMPSrcAddr(PrevSrc, &CurrSrc);
  2801. }
  2802. Curr->iga_xmitleft = 0;
  2803. DeleteIGMPAddr(NTE, Prev, &Curr);
  2804. }
  2805. Curr = Prev;
  2806. CTEFreeLock(&NTE->nte_lock, Handle);
  2807. {
  2808. // Update link-layer filter
  2809. (*IF->if_deladdr) (IF->if_lcontext, LLIP_ADDR_MCAST,
  2810. Addr, 0);
  2811. }
  2812. CTEGetLock(&NTE->nte_lock, &Handle);
  2813. }
  2814. }
  2815. ASSERT(NTE->nte_igmplist == NULL);
  2816. ASSERT(NTE->nte_igmpcount == 0);
  2817. }
  2818. Done:
  2819. CTEFreeLock(&NTE->nte_lock, Handle);
  2820. if (IgmpVersion == IGMPV3)
  2821. SendIGMPv3Reports(I3Head, SrcAddr, BodyMTU);
  2822. else
  2823. SendOldReports(OldHead, SrcAddr);
  2824. return Status;
  2825. }
  2826. //* IGMPAddrChange - Change the IGMP address list on an NTE.
  2827. //
  2828. // Called to add or delete an IGMP address. We're given the relevant NTE,
  2829. // the address, and the action to be performed. We validate the NTE, the
  2830. // address, and the IGMP level, and then attempt to perform the action.
  2831. //
  2832. // There are a bunch of strange race conditions that can occur during
  2833. // adding/deleting addresses, related to trying to add the same address
  2834. // twice and having it fail, or adding and deleting the same address
  2835. // simultaneously. Most of these happen because we have to free the lock
  2836. // to call the interface, and the call to the interface can fail. To
  2837. // prevent this we serialize all access to this routine. Only one thread
  2838. // of execution can go through here at a time, all others are blocked.
  2839. //
  2840. // Input: NTE - NTE with list to be altered.
  2841. // Addr - Address affected.
  2842. // ChangeType - Type of change - IGMP_ADD, IGMP_DELETE,
  2843. // IGMP_DELETE_ALL.
  2844. // ExclSourceList - list of exclusion sources (volatile)
  2845. //
  2846. // Returns: IP_STATUS of attempt to perform action.
  2847. //
  2848. IP_STATUS
  2849. IGMPAddrChange(
  2850. IN NetTableEntry *NTE,
  2851. IN IPAddr Addr,
  2852. IN uint ChangeType,
  2853. IN uint NumExclSources,
  2854. IN OUT IPAddr *ExclSourceList,
  2855. IN uint NumInclSources,
  2856. IN IPAddr *InclSourceList)
  2857. {
  2858. CTELockHandle Handle;
  2859. IP_STATUS Status;
  2860. IGMPBlockStruct Block;
  2861. IGMPBlockStruct *BlockPtr;
  2862. IPAddr SrcAddr = 0;
  2863. // First make sure we're at level 2 of IGMP support.
  2864. if (IGMPLevel != 2)
  2865. return IP_BAD_REQ;
  2866. if (NTE->nte_flags & NTE_VALID) {
  2867. //
  2868. // If this is an unnumbered interface
  2869. //
  2870. if ((NTE->nte_if->if_flags & IF_FLAGS_NOIPADDR) &&
  2871. IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
  2872. SrcAddr = g_ValidAddr;
  2873. if (IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR)) {
  2874. return IP_BAD_REQ;
  2875. }
  2876. } else {
  2877. SrcAddr = NTE->nte_addr;
  2878. }
  2879. }
  2880. CTEInitBlockStruc(&Block.ibs_block);
  2881. // Make sure we're the only ones in this routine. If someone else is
  2882. // already here, block.
  2883. CTEGetLock(&IGMPLock, &Handle);
  2884. if (IGMPBlockFlag) {
  2885. // Someone else is already here. Walk down the block list, and
  2886. // put ourselves on the end. Then free the lock and block on our
  2887. // IGMPBlock structure.
  2888. BlockPtr = STRUCT_OF(IGMPBlockStruct, &IGMPBlockList, ibs_next);
  2889. while (BlockPtr->ibs_next != NULL)
  2890. BlockPtr = BlockPtr->ibs_next;
  2891. Block.ibs_next = NULL;
  2892. BlockPtr->ibs_next = &Block;
  2893. CTEFreeLock(&IGMPLock, Handle);
  2894. CTEBlock(&Block.ibs_block);
  2895. } else {
  2896. // Noone else here, set the flag so noone else gets in and free the
  2897. // lock.
  2898. IGMPBlockFlag = 1;
  2899. CTEFreeLock(&IGMPLock, Handle);
  2900. }
  2901. // Now we're in the routine, and we won't be reentered here by another
  2902. // thread of execution. Make sure everything's valid, and figure out
  2903. // what to do.
  2904. Status = IP_SUCCESS;
  2905. // Now figure out the action to be performed.
  2906. switch (ChangeType) {
  2907. case IGMP_ADD:
  2908. Status = JoinIGMPAddr(NTE, Addr, NumExclSources, ExclSourceList,
  2909. NumInclSources, InclSourceList,
  2910. SrcAddr);
  2911. break;
  2912. case IGMP_DELETE:
  2913. Status = LeaveIGMPAddr(NTE, Addr, NumExclSources, ExclSourceList,
  2914. NumInclSources, InclSourceList,
  2915. SrcAddr);
  2916. break;
  2917. case IGMP_DELETE_ALL:
  2918. Status = LeaveAllIGMPAddr(NTE, SrcAddr);
  2919. break;
  2920. default:
  2921. DEBUGCHK;
  2922. break;
  2923. }
  2924. // We finished the request, and Status contains the completion status.
  2925. // If there are any pending blocks for this routine, signal the next
  2926. // one now. Otherwise clear the block flag.
  2927. CTEGetLock(&IGMPLock, &Handle);
  2928. if ((BlockPtr = IGMPBlockList) != NULL) {
  2929. // Someone is blocking. Pull him from the list and signal him.
  2930. IGMPBlockList = BlockPtr->ibs_next;
  2931. CTEFreeLock(&IGMPLock, Handle);
  2932. CTESignal(&BlockPtr->ibs_block, IP_SUCCESS);
  2933. } else {
  2934. // No one blocking, just clear the flag.
  2935. IGMPBlockFlag = 0;
  2936. CTEFreeLock(&IGMPLock, Handle);
  2937. }
  2938. return Status;
  2939. }
  2940. //* GroupResponseTimeout - Called when group-response timer expires
  2941. // Assumes caller holds lock on NTE
  2942. // Caller is responsible for deleting AddrPtr if no longer needed
  2943. void
  2944. GroupResponseTimeout(
  2945. IN OUT IGMPv3RecordQueueEntry **pI3qe,
  2946. IN OUT IGMPReportQueueEntry **pIqe,
  2947. IN NetTableEntry *NTE,
  2948. IN IGMPAddr *AddrPtr)
  2949. {
  2950. uint IgmpVersion, BodyMTU, StateRecSize = 0;
  2951. IGMPv3GroupRecord *StateRec = NULL;
  2952. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_TX,
  2953. (DTEXT("GroupResponseTimeout\n")));
  2954. IgmpVersion = NTE->nte_if->IgmpVersion;
  2955. BodyMTU = RECORD_MTU(NTE);
  2956. if (IgmpVersion < IGMPV3) {
  2957. QueueOldReport(pIqe, IGMP_ADD, IgmpVersion, AddrPtr->iga_addr);
  2958. return;
  2959. }
  2960. if (AddrPtr->iga_resptype == GROUP_SOURCE_RESP) {
  2961. StateRec = GetGSIsInRecord(AddrPtr, &StateRecSize);
  2962. } else {
  2963. // Group-specific response
  2964. if (AddrPtr->iga_grefcnt == 0) {
  2965. StateRec = GetIsInRecord(AddrPtr, &StateRecSize);
  2966. } else {
  2967. StateRec = GetIsExRecord(AddrPtr, &StateRecSize, BodyMTU);
  2968. }
  2969. }
  2970. QueueRecord(pI3qe, &StateRec, StateRecSize);
  2971. CancelGroupResponseTimer(AddrPtr);
  2972. }
  2973. //* RetransmissionTimeout - called when retransmission timer expires
  2974. //
  2975. // Caller is responsible for deleting Grp afterwards if no longer needed
  2976. void
  2977. RetransmissionTimeout(
  2978. IN OUT IGMPv3RecordQueueEntry **pI3qe,
  2979. IN OUT IGMPReportQueueEntry **pIqe,
  2980. IN NetTableEntry *NTE,
  2981. IN IGMPAddr *Grp)
  2982. {
  2983. IGMPv3GroupRecord *Rec = NULL;
  2984. uint RecSize = 0;
  2985. uint IgmpVersion, BodyMTU;
  2986. DEBUGMSG(DBG_TRACE && DBG_IGMP && DBG_TX,
  2987. (DTEXT("RetransmissionTimeout\n")));
  2988. IgmpVersion = NTE->nte_if->IgmpVersion;
  2989. BodyMTU = RECORD_MTU(NTE);
  2990. if (IgmpVersion < IGMPV3) {
  2991. // We decrement the counter here since the same function
  2992. // is used to respond to queries.
  2993. IgmpDecXmitLeft(Grp);
  2994. QueueOldReport(pIqe, IGMP_ADD, IgmpVersion, Grp->iga_addr);
  2995. } else {
  2996. if (Grp->iga_changetype == MODE_CHANGE) {
  2997. if (Grp->iga_grefcnt == 0) {
  2998. Rec = GetToInRecord(Grp, &RecSize);
  2999. } else {
  3000. Rec = GetToExRecord(Grp, &RecSize, BodyMTU);
  3001. }
  3002. QueueRecord(pI3qe, &Rec, RecSize);
  3003. } else {
  3004. Rec = GetAllowRecord(Grp, &RecSize);
  3005. QueueRecord(pI3qe, &Rec, RecSize);
  3006. Rec = GetBlockRecord(Grp, &RecSize);
  3007. QueueRecord(pI3qe, &Rec, RecSize);
  3008. }
  3009. }
  3010. if (Grp->iga_xmitleft > 0) {
  3011. Grp->iga_trtimer = IGMPRandomTicks(UNSOLICITED_REPORT_INTERVAL);
  3012. }
  3013. }
  3014. //* IGMPTimer - Handle an IGMP timer event.
  3015. //
  3016. // This function is called every 500 ms. by IP. If we're at level 2 of
  3017. // IGMP functionality we run down the NTE looking for running timers. If
  3018. // we find one, we see if it has expired and if so we send an
  3019. // IGMP report.
  3020. //
  3021. // Input: NTE - Pointer to NTE to check.
  3022. //
  3023. // Returns: Nothing.
  3024. //
  3025. void
  3026. IGMPTimer(
  3027. IN NetTableEntry * NTE)
  3028. {
  3029. CTELockHandle Handle;
  3030. IGMPAddr *AddrPtr, *PrevPtr;
  3031. uint IgmpVersion = 0, BodyMTU = 0, i;
  3032. IPAddr SrcAddr = 0;
  3033. IGMPAddr **HashPtr;
  3034. IGMPv3RecordQueueEntry *I3Head = NULL, *i3qe;
  3035. IGMPReportQueueEntry *OldHead = NULL, *iqe;
  3036. i3qe = STRUCT_OF(IGMPv3RecordQueueEntry, &I3Head, i3qe_next);
  3037. iqe = STRUCT_OF(IGMPReportQueueEntry, &OldHead, iqe_next);
  3038. if (IGMPLevel != 2) {
  3039. return;
  3040. }
  3041. // We are doing IGMP. Run down the addresses active on this NTE.
  3042. CTEGetLock(&NTE->nte_lock, &Handle);
  3043. if (NTE->nte_flags & NTE_VALID) {
  3044. //
  3045. // If we haven't heard any query from an older version
  3046. // router during timeout period, revert to newer version.
  3047. // No need to check whether NTE is valid or not
  3048. //
  3049. if ((NTE->nte_if->IgmpVer2Timeout != 0)
  3050. && (--(NTE->nte_if->IgmpVer2Timeout) == 0)) {
  3051. NTE->nte_if->IgmpVersion = IGMPV3;
  3052. }
  3053. if ((NTE->nte_if->IgmpVer1Timeout != 0)
  3054. && (--(NTE->nte_if->IgmpVer1Timeout) == 0)) {
  3055. NTE->nte_if->IgmpVersion = IGMPV3;
  3056. }
  3057. if (NTE->nte_if->IgmpVer2Timeout != 0)
  3058. NTE->nte_if->IgmpVersion = IGMPV2;
  3059. if (NTE->nte_if->IgmpVer1Timeout != 0)
  3060. NTE->nte_if->IgmpVersion = IGMPV1;
  3061. if ((NTE->nte_if->if_flags & IF_FLAGS_NOIPADDR) &&
  3062. IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) {
  3063. SrcAddr = g_ValidAddr;
  3064. if (IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR)) {
  3065. CTEFreeLock(&NTE->nte_lock, Handle);
  3066. return;
  3067. }
  3068. } else {
  3069. SrcAddr = NTE->nte_addr;
  3070. }
  3071. BodyMTU = RECORD_MTU(NTE);
  3072. IgmpVersion = NTE->nte_if->IgmpVersion;
  3073. HashPtr = NTE->nte_igmplist;
  3074. for (i=0; (i<IGMP_TABLE_SIZE) && (NTE->nte_igmplist!=NULL); i++) {
  3075. PrevPtr = STRUCT_OF(IGMPAddr, &HashPtr[i], iga_next);
  3076. AddrPtr = PrevPtr->iga_next;
  3077. while (AddrPtr != NULL) {
  3078. // Hande group response timer
  3079. if (AddrPtr->iga_resptimer != 0) {
  3080. AddrPtr->iga_resptimer--;
  3081. if ((AddrPtr->iga_resptimer == 0)
  3082. && (NTE->nte_flags & NTE_VALID)) {
  3083. GroupResponseTimeout(&i3qe, &iqe, NTE, AddrPtr);
  3084. }
  3085. }
  3086. // Handle triggered retransmission timer
  3087. if (AddrPtr->iga_trtimer != 0) {
  3088. AddrPtr->iga_trtimer--;
  3089. if ((AddrPtr->iga_trtimer == 0)
  3090. && (NTE->nte_flags & NTE_VALID)) {
  3091. RetransmissionTimeout(&i3qe, &iqe, NTE, AddrPtr);
  3092. }
  3093. }
  3094. // Delete group if no longer needed
  3095. if (IS_GROUP_DELETABLE(AddrPtr)) {
  3096. DeleteIGMPAddr(NTE, PrevPtr, &AddrPtr);
  3097. AddrPtr = PrevPtr;
  3098. }
  3099. if (NTE->nte_igmplist == NULL) {
  3100. // PrevPtr is gone
  3101. break;
  3102. }
  3103. //
  3104. // Go on to the next one.
  3105. //
  3106. PrevPtr = AddrPtr;
  3107. AddrPtr = AddrPtr->iga_next;
  3108. }
  3109. }
  3110. // Check general query timer
  3111. if ((NTE->nte_if->IgmpGeneralTimer != 0)
  3112. && (--(NTE->nte_if->IgmpGeneralTimer) == 0)) {
  3113. QueueIGMPv3GeneralResponse(&i3qe, NTE);
  3114. }
  3115. } //nte_valid
  3116. CTEFreeLock(&NTE->nte_lock, Handle);
  3117. if (IgmpVersion == IGMPV3)
  3118. SendIGMPv3Reports(I3Head, SrcAddr, BodyMTU);
  3119. else
  3120. SendOldReports(OldHead, SrcAddr);
  3121. }
  3122. //* IsMCastSourceAllowed - check if incoming packet passes interface filter
  3123. //
  3124. // Returns: DEST_MCAST if allowed, DEST_LOCAL if not.
  3125. uchar
  3126. IsMCastSourceAllowed(
  3127. IN IPAddr Dest,
  3128. IN IPAddr Source,
  3129. IN uchar Protocol,
  3130. IN NetTableEntry *NTE)
  3131. {
  3132. CTELockHandle Handle;
  3133. uchar Result = DEST_LOCAL;
  3134. IGMPAddr *AddrPtr = NULL;
  3135. IGMPSrcAddr *SrcPtr = NULL;
  3136. if (IGMPLevel != 2) {
  3137. return DEST_LOCAL;
  3138. }
  3139. // IGMP Queries must be immune to source filters or else
  3140. // we might not be able to respond to group-specific queries
  3141. // from the querier and hence lose data.
  3142. if (Protocol == PROT_IGMP) {
  3143. return DEST_MCAST;
  3144. }
  3145. CTEGetLock(&NTE->nte_lock, &Handle);
  3146. {
  3147. AddrPtr = FindIGMPAddr(NTE, Dest, NULL);
  3148. if (AddrPtr != NULL) {
  3149. SrcPtr = FindIGMPSrcAddr(AddrPtr, Source, NULL);
  3150. if (SrcPtr) {
  3151. if (IS_SOURCE_ALLOWED(AddrPtr, SrcPtr))
  3152. Result = DEST_MCAST;
  3153. } else {
  3154. if (IS_GROUP_ALLOWED(AddrPtr))
  3155. Result = DEST_MCAST;
  3156. }
  3157. }
  3158. }
  3159. CTEFreeLock(&NTE->nte_lock, Handle);
  3160. return Result;
  3161. }
  3162. //* InitIGMPForNTE - Called to do per-NTE initialization.
  3163. //
  3164. // Called when an NTE becomes valid. If we're at level 2, we put the
  3165. // all-host mcast on the list and add the address to the interface.
  3166. //
  3167. // Input: NTE - NTE on which to act.
  3168. //
  3169. // Returns: Nothing.
  3170. //
  3171. void
  3172. InitIGMPForNTE(
  3173. IN NetTableEntry * NTE)
  3174. {
  3175. if (IGMPLevel == 2) {
  3176. IGMPAddrChange(NTE, ALL_HOST_MCAST, IGMP_ADD, 0, NULL, 0, NULL);
  3177. }
  3178. if (Seed == 0) {
  3179. // No random seed yet.
  3180. Seed = (int)NTE->nte_addr;
  3181. // Make sure the inital value is odd, and less than 9 decimal digits.
  3182. RandomValue = ((Seed + (int)CTESystemUpTime()) % 100000000) | 1;
  3183. }
  3184. }
  3185. //* StopIGMPForNTE - Called to do per-NTE shutdown.
  3186. //
  3187. // Called when we're shutting down an NTE, and want to stop IGMP on it,
  3188. //
  3189. // Input: NTE - NTE on which to act.
  3190. //
  3191. // Returns: Nothing.
  3192. //
  3193. void
  3194. StopIGMPForNTE(
  3195. IN NetTableEntry * NTE)
  3196. {
  3197. if (IGMPLevel == 2) {
  3198. IGMPAddrChange(NTE, NULL_IP_ADDR, IGMP_DELETE_ALL,
  3199. 0, NULL, 0, NULL);
  3200. }
  3201. }
  3202. #pragma BEGIN_INIT
  3203. //** IGMPInit - Initialize IGMP.
  3204. //
  3205. // This bit of code initializes IGMP generally. There is also some amount
  3206. // of work done on a per-NTE basis that we do when each one is initialized.
  3207. //
  3208. // Input: Nothing.
  3209. ///
  3210. // Returns: TRUE if we init, FALSE if we don't.
  3211. //
  3212. uint
  3213. IGMPInit(void)
  3214. {
  3215. DEBUGMSG(DBG_INFO && DBG_IGMP,
  3216. (DTEXT("Initializing IGMP\n")));
  3217. if (IGMPLevel != 2)
  3218. return TRUE;
  3219. CTEInitLock(&IGMPLock);
  3220. IGMPBlockList = NULL;
  3221. IGMPBlockFlag = 0;
  3222. Seed = 0;
  3223. IGMPProtInfo = IPRegisterProtocol(PROT_IGMP, IGMPRcv, IGMPSendComplete,
  3224. NULL, NULL, NULL, NULL);
  3225. if (IGMPProtInfo != NULL)
  3226. return TRUE;
  3227. else
  3228. return FALSE;
  3229. }
  3230. #pragma END_INIT