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.

711 lines
21 KiB

  1. // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
  2. //
  3. // Copyright (c) 1985-2000 Microsoft Corporation
  4. //
  5. // This file is part of the Microsoft Research IPv6 Network Protocol Stack.
  6. // You should have received a copy of the Microsoft End-User License Agreement
  7. // for this software along with this release; see the file "license.txt".
  8. // If not, please see http://www.research.microsoft.com/msripv6/license.htm,
  9. // or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
  10. //
  11. // Abstract:
  12. //
  13. // Multicast Listener Discovery for Internet Protocol Version 6.
  14. // See draft-ietf-ipngwg-mld-00.txt for details.
  15. //
  16. #include "oscfg.h"
  17. #include "ndis.h"
  18. #include "ip6imp.h"
  19. #include "ip6def.h"
  20. #include "icmp.h"
  21. #include "mld.h"
  22. #include "ntddip6.h"
  23. #include "route.h"
  24. #include "alloca.h"
  25. #include "info.h"
  26. //
  27. // The QueryListLock may be taken while holding an Interface lock.
  28. //
  29. KSPIN_LOCK QueryListLock;
  30. MulticastAddressEntry *QueryList;
  31. //* AddToQueryList
  32. //
  33. // Add an MAE to the front of the QueryList.
  34. // The caller should already have the QueryList and the IF locked.
  35. //
  36. void
  37. AddToQueryList(MulticastAddressEntry *MAE)
  38. {
  39. MAE->NextQL = QueryList;
  40. QueryList = MAE;
  41. }
  42. //* RemoveFromQueryList
  43. //
  44. // Remove an MAE from the QueryList.
  45. // The caller should already have the QueryList and the IF locked.
  46. //
  47. void
  48. RemoveFromQueryList(MulticastAddressEntry *MAE)
  49. {
  50. MulticastAddressEntry **PrevMAE, *ThisMAE;
  51. for (PrevMAE = &QueryList; ; PrevMAE = &ThisMAE->NextQL) {
  52. ThisMAE = *PrevMAE;
  53. ASSERT(ThisMAE != NULL);
  54. if (ThisMAE == MAE) {
  55. //
  56. // Remove the entry.
  57. //
  58. *PrevMAE = ThisMAE->NextQL;
  59. break;
  60. }
  61. }
  62. }
  63. //* MLDQueryReceive - Process the receipt of a Group Query MLD message.
  64. //
  65. // Queries for a specific group should be sent to the group address
  66. // in question. General queries are sent to the all nodes address, and
  67. // have the group address set to zero.
  68. // Here we need to add the group to the list of groups waiting to send
  69. // membership reports. Then set the timer value in the ADE entry to a
  70. // random value determines by the incoming query.
  71. //
  72. void
  73. MLDQueryReceive(IPv6Packet *Packet)
  74. {
  75. Interface *IF = Packet->NTEorIF->IF;
  76. MLDMessage *Message;
  77. MulticastAddressEntry *MAE;
  78. uint MaxResponseDelay;
  79. //
  80. // Verify that the packet has a link-local source address.
  81. //
  82. if (!IsLinkLocal(AlignAddr(&Packet->IP->Source))) {
  83. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
  84. "MLDQueryReceive: non-link-local source\n"));
  85. return;
  86. }
  87. //
  88. // Verify that we have enough contiguous data to overlay a MLDMessage
  89. // structure on the incoming packet. Then do so.
  90. //
  91. if (! PacketPullup(Packet, sizeof(MLDMessage),
  92. __builtin_alignof(MLDMessage), 0)) {
  93. // Pullup failed.
  94. if (Packet->TotalSize < sizeof(MLDMessage))
  95. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
  96. "MLDQueryReceive: too small to contain MLD message\n"));
  97. return;
  98. }
  99. Message = (MLDMessage *)Packet->Data;
  100. //
  101. // Get the maximum response value from the received MLD message.
  102. //
  103. MaxResponseDelay = net_short(Message->MaxResponseDelay); // Milliseconds.
  104. MaxResponseDelay = ConvertMillisToTicks(MaxResponseDelay);
  105. KeAcquireSpinLockAtDpcLevel(&IF->Lock);
  106. //
  107. // Loop through the ADE list and update the timer for the desired
  108. // groups. Note that a general query uses the unspecified address, and
  109. // sets the timer for all groups.
  110. //
  111. for (MAE = (MulticastAddressEntry *)IF->ADE;
  112. MAE != NULL;
  113. MAE = (MulticastAddressEntry *)MAE->Next) {
  114. if ((MAE->Type == ADE_MULTICAST) &&
  115. (MAE->MCastFlags & MAE_REPORTABLE) &&
  116. (IP6_ADDR_EQUAL(AlignAddr(&Message->GroupAddr),
  117. &UnspecifiedAddr) ||
  118. IP6_ADDR_EQUAL(AlignAddr(&Message->GroupAddr),
  119. &MAE->Address))) {
  120. //
  121. // If the timer is currently off or if the maximum requested
  122. // response delay is less than the current timer value, draw a
  123. // random value on the interval(0, MaxResponseDelay) and update
  124. // the timer to reflect this value.
  125. //
  126. KeAcquireSpinLockAtDpcLevel(&QueryListLock);
  127. //
  128. // Add this MAE to the QueryList, if not already present.
  129. //
  130. if (MAE->MCastTimer == 0) {
  131. AddToQueryList(MAE);
  132. goto UpdateTimerValue;
  133. }
  134. if (MaxResponseDelay <= MAE->MCastTimer) {
  135. UpdateTimerValue:
  136. //
  137. // Update the timer value.
  138. //
  139. if (MaxResponseDelay == 0)
  140. MAE->MCastTimer = 0;
  141. else
  142. MAE->MCastTimer = (ushort)
  143. RandomNumber(0, MaxResponseDelay);
  144. //
  145. // We add 1 because MLDTimeout predecrements.
  146. // We must maintain the invariant that ADEs on
  147. // the query list have a non-zero timer value.
  148. //
  149. MAE->MCastTimer += 1;
  150. }
  151. KeReleaseSpinLockFromDpcLevel(&QueryListLock);
  152. }
  153. }
  154. KeReleaseSpinLockFromDpcLevel(&IF->Lock);
  155. }
  156. //* MLDReportReceive - Process the receipt of a Group Report MLD message.
  157. //
  158. // When another host on the local link sends a group report, we receive
  159. // a copy if we also belong to the group. If we have a timer running for
  160. // this group, we can turn it off now.
  161. //
  162. // Callable from DPC context, not from thread context.
  163. //
  164. void
  165. MLDReportReceive(IPv6Packet *Packet)
  166. {
  167. Interface *IF = Packet->NTEorIF->IF;
  168. MLDMessage *Message;
  169. MulticastAddressEntry *MAE;
  170. //
  171. // Verify that the packet has a link-local source address.
  172. // An unspecified source address can also happen during initialization.
  173. //
  174. if (!(IsLinkLocal(AlignAddr(&Packet->IP->Source)) ||
  175. IsUnspecified(AlignAddr(&Packet->IP->Source)))) {
  176. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
  177. "MLDReportReceive: non-link-local source\n"));
  178. return;
  179. }
  180. //
  181. // Verify that we have enough contiguous data to overlay a MLDMessage
  182. // structure on the incoming packet. Then do so.
  183. //
  184. if (! PacketPullup(Packet, sizeof(MLDMessage),
  185. __builtin_alignof(MLDMessage), 0)) {
  186. // Pullup failed.
  187. if (Packet->TotalSize < sizeof(MLDMessage))
  188. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
  189. "MLDReportReceive: too small to contain MLD message\n"));
  190. return;
  191. }
  192. Message = (MLDMessage *)Packet->Data;
  193. KeAcquireSpinLockAtDpcLevel(&IF->Lock);
  194. //
  195. // Search for the MAE for this group address.
  196. //
  197. MAE = (MulticastAddressEntry *)
  198. *FindADE(IF, AlignAddr(&Message->GroupAddr));
  199. if ((MAE != NULL) && (MAE->Type == ADE_MULTICAST)) {
  200. KeAcquireSpinLockAtDpcLevel(&QueryListLock);
  201. //
  202. // We ignore the report unless
  203. // we are in the "Delaying Listener" state.
  204. //
  205. if (MAE->MCastTimer != 0) {
  206. //
  207. // Stop our timer and clear the last-reporter flag.
  208. // Note that we only clear the last-reporter flag
  209. // if our timer is running, as called for in the spec.
  210. // Although it would make sense to clear the flag
  211. // when we do not have a timer running.
  212. //
  213. MAE->MCastTimer = 0;
  214. MAE->MCastFlags &= ~MAE_LAST_REPORTER;
  215. RemoveFromQueryList(MAE);
  216. }
  217. KeReleaseSpinLockFromDpcLevel(&QueryListLock);
  218. }
  219. KeReleaseSpinLockFromDpcLevel(&IF->Lock);
  220. }
  221. //* MLDMessageSend
  222. //
  223. // Primitive function for sending MLD messages.
  224. //
  225. // Note that we can not use RouteToDestination to get an RCE.
  226. // There might be no valid source addresses on the sending interface.
  227. // We could use IPv6SendND, but it doesn't make sense because
  228. // we can't pass in a valid DiscoveryAddress. And it's not needed.
  229. //
  230. void
  231. MLDMessageSend(
  232. Interface *IF,
  233. const IPv6Addr *GroupAddr,
  234. const IPv6Addr *Dest,
  235. uchar Type)
  236. {
  237. PNDIS_PACKET Packet;
  238. IPv6Header UNALIGNED *IP;
  239. ICMPv6Header UNALIGNED *ICMP;
  240. MLDMessage UNALIGNED *MLD;
  241. MLDRouterAlertOption UNALIGNED *RA;
  242. uint Offset;
  243. uint PayloadLength;
  244. uint MemLen;
  245. uchar *Mem;
  246. void *LLDest;
  247. IP_STATUS Status;
  248. ICMPv6OutStats.icmps_msgs++;
  249. ASSERT(IsMulticast(Dest));
  250. //
  251. // Calculate the packet size.
  252. //
  253. Offset = IF->LinkHeaderSize;
  254. PayloadLength = sizeof(MLDRouterAlertOption) + sizeof(ICMPv6Header)
  255. + sizeof(MLDMessage);
  256. MemLen = Offset + sizeof(IPv6Header) + PayloadLength;
  257. //
  258. // Allocate the packet.
  259. //
  260. Status = IPv6AllocatePacket(MemLen, &Packet, &Mem);
  261. if (Status != NDIS_STATUS_SUCCESS) {
  262. ICMPv6OutStats.icmps_errors++;
  263. return;
  264. }
  265. //
  266. // Prepare the IP header.
  267. //
  268. IP = (IPv6Header UNALIGNED *)(Mem + Offset);
  269. IP->VersClassFlow = IP_VERSION;
  270. IP->PayloadLength = net_short((ushort)PayloadLength);
  271. IP->NextHeader = IP_PROTOCOL_HOP_BY_HOP;
  272. IP->HopLimit = 1;
  273. IP->Dest = *Dest;
  274. //
  275. // This will give us the unspecified address
  276. // if our link-local address is not valid.
  277. // (For example if it is still tentative pending DAD.)
  278. //
  279. (void) GetLinkLocalAddress(IF, AlignAddr(&IP->Source));
  280. //
  281. // Prepare the router alert option.
  282. //
  283. RA = (MLDRouterAlertOption UNALIGNED *)(IP + 1);
  284. RA->Header.NextHeader = IP_PROTOCOL_ICMPv6;
  285. RA->Header.HeaderExtLength = 0;
  286. RA->Option.Type = OPT6_ROUTER_ALERT;
  287. RA->Option.Length = 2;
  288. RA->Option.Value = MLD_ROUTER_ALERT_OPTION_TYPE;
  289. RA->Pad.Type = 1;
  290. RA->Pad.DataLength = 0;
  291. //
  292. // Prepare the ICMP header.
  293. //
  294. ICMP = (ICMPv6Header UNALIGNED *)(RA + 1);
  295. ICMP->Type = Type;
  296. ICMP->Code = 0;
  297. ICMP->Checksum = 0; // Calculated below.
  298. //
  299. // Prepare the MLD message.
  300. //
  301. MLD = (MLDMessage UNALIGNED *)(ICMP + 1);
  302. MLD->MaxResponseDelay = 0;
  303. MLD->Unused = 0;
  304. MLD->GroupAddr = *GroupAddr;
  305. //
  306. // Calculate the ICMP checksum.
  307. //
  308. ICMP->Checksum = ChecksumPacket(Packet,
  309. Offset + sizeof(IPv6Header) + sizeof(MLDRouterAlertOption),
  310. NULL,
  311. sizeof(ICMPv6Header) + sizeof(MLDMessage),
  312. AlignAddr(&IP->Source), AlignAddr(&IP->Dest),
  313. IP_PROTOCOL_ICMPv6);
  314. //
  315. // Convert the IP-level multicast destination address
  316. // to a link-layer multicast address.
  317. //
  318. LLDest = alloca(IF->LinkAddressLength);
  319. (*IF->ConvertAddr)(IF->LinkContext, Dest, LLDest);
  320. PC(Packet)->Flags = NDIS_FLAGS_MULTICAST_PACKET | NDIS_FLAGS_DONT_LOOPBACK;
  321. //
  322. // Transmit the packet.
  323. //
  324. ICMPv6OutStats.icmps_typecount[Type]++;
  325. IPv6SendLL(IF, Packet, Offset, LLDest);
  326. }
  327. //* MLDReportSend - Send an MLD membership report.
  328. //
  329. // This function is called either when a host first joins a multicast group or
  330. // at some point after a membership query message was received, and the timer
  331. // for this host has expired.
  332. //
  333. void
  334. MLDReportSend(Interface *IF, const IPv6Addr *GroupAddr)
  335. {
  336. MLDMessageSend(IF, GroupAddr, GroupAddr,
  337. ICMPv6_MULTICAST_LISTENER_REPORT);
  338. }
  339. //* MLDDoneSend - Send an MLD done message.
  340. //
  341. // This function is called when a host quits a multicast group AND this was
  342. // the last host on the local link to report interest in the group. A host
  343. // quits when either the upper layer explicitly quits or when the interface
  344. // is deleted.
  345. //
  346. void
  347. MLDDoneSend(Interface *IF, const IPv6Addr *GroupAddr)
  348. {
  349. MLDMessageSend(IF, GroupAddr, &AllRoutersOnLinkAddr,
  350. ICMPv6_MULTICAST_LISTENER_DONE);
  351. }
  352. //* MLDAddMCastAddr - Add a multicast group to the specified interface.
  353. //
  354. // This function is called when a user level program has asked to join a
  355. // multicast group.
  356. //
  357. // The Interface number can be supplied as zero,
  358. // in which we try to pick a reasonable interface
  359. // and then return the interface number that we picked.
  360. //
  361. // Callable from thread context, not from DPC context.
  362. // Called with no locks held.
  363. //
  364. IP_STATUS
  365. MLDAddMCastAddr(uint *pInterfaceNo, const IPv6Addr *Addr)
  366. {
  367. uint InterfaceNo = *pInterfaceNo;
  368. Interface *IF;
  369. MulticastAddressEntry *MAE;
  370. IP_STATUS status;
  371. KIRQL OldIrql;
  372. if (!IsMulticast(Addr)) {
  373. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
  374. "MLDAddMCastAddr: Not mcast addr\n"));
  375. return IP_PARAMETER_PROBLEM;
  376. }
  377. if (InterfaceNo == 0) {
  378. RouteCacheEntry *RCE;
  379. //
  380. // We must pick an interface to use for this multicast address.
  381. // Look for a multicast route in the routing table.
  382. //
  383. status = RouteToDestination(Addr, 0, NULL, RTD_FLAG_NORMAL, &RCE);
  384. if (status != IP_SUCCESS) {
  385. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
  386. "MLDAddMCastAddr - no route\n"));
  387. return status;
  388. }
  389. //
  390. // Use the interface associated with the RCE.
  391. //
  392. IF = RCE->NTE->IF;
  393. *pInterfaceNo = IF->Index;
  394. AddRefIF(IF);
  395. ReleaseRCE(RCE);
  396. }
  397. else {
  398. //
  399. // Use the interface requested by the application.
  400. //
  401. IF = FindInterfaceFromIndex(InterfaceNo);
  402. if (IF == NULL)
  403. return IP_PARAMETER_PROBLEM;
  404. }
  405. //
  406. // Will this interface support multicast addresses?
  407. //
  408. if (!(IF->Flags & IF_FLAG_MULTICAST)) {
  409. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
  410. "MLDAddMCastAddr: IF cannot add a mcast addr\n"));
  411. ReleaseIF(IF);
  412. return IP_PARAMETER_PROBLEM;
  413. }
  414. //
  415. // The real work is all in FindOrCreateMAE.
  416. //
  417. KeAcquireSpinLock(&IF->Lock, &OldIrql);
  418. MAE = FindOrCreateMAE(IF, Addr, NULL);
  419. if (IsMCastSyncNeeded(IF))
  420. DeferSynchronizeMulticastAddresses(IF);
  421. KeReleaseSpinLock(&IF->Lock, OldIrql);
  422. ReleaseIF(IF);
  423. return (MAE == NULL) ? IP_NO_RESOURCES : IP_SUCCESS;
  424. }
  425. //* MLDDropMCastAddr - remove a multicast address from an interface.
  426. //
  427. // This function is called when a user has indicated that they are no
  428. // longer interested in a multicast group.
  429. //
  430. // Callable from thread context, not from DPC context.
  431. // Called with no locks held.
  432. //
  433. IP_STATUS
  434. MLDDropMCastAddr(uint InterfaceNo, const IPv6Addr *Addr)
  435. {
  436. Interface *IF;
  437. MulticastAddressEntry *MAE;
  438. IP_STATUS status;
  439. KIRQL OldIrql;
  440. //
  441. // Unlike MLDAddMCastAddr, no need to check
  442. // if the address is multicast. If it is not,
  443. // FindAndReleaseMAE will fail to find it.
  444. //
  445. if (InterfaceNo == 0) {
  446. RouteCacheEntry *RCE;
  447. //
  448. // We must pick an interface to use for this multicast address.
  449. // Look for a multicast route in the routing table.
  450. //
  451. status = RouteToDestination(Addr, 0, NULL, RTD_FLAG_NORMAL, &RCE);
  452. if (status != IP_SUCCESS) {
  453. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
  454. "MLDDropMCastAddr - no route\n"));
  455. return status;
  456. }
  457. //
  458. // Use the interface associated with the RCE.
  459. //
  460. IF = RCE->NTE->IF;
  461. AddRefIF(IF);
  462. ReleaseRCE(RCE);
  463. }
  464. else {
  465. //
  466. // Use the interface requested by the application.
  467. //
  468. IF = FindInterfaceFromIndex(InterfaceNo);
  469. if (IF == NULL)
  470. return IP_PARAMETER_PROBLEM;
  471. }
  472. //
  473. // Unlike MLDAddMCastAddr, no need to check IF_FLAG_MULTICAST.
  474. // If the interface does not support multicast addresses,
  475. // FindAndReleaseMAE will fail to find the address.
  476. //
  477. //
  478. // All the real work is in FindAndReleaseMAE.
  479. //
  480. KeAcquireSpinLock(&IF->Lock, &OldIrql);
  481. MAE = FindAndReleaseMAE(IF, Addr);
  482. if (IsMCastSyncNeeded(IF))
  483. DeferSynchronizeMulticastAddresses(IF);
  484. KeReleaseSpinLock(&IF->Lock, OldIrql);
  485. ReleaseIF(IF);
  486. return (MAE == NULL) ? IP_PARAMETER_PROBLEM : IP_SUCCESS;
  487. }
  488. //* MLDTimeout - Handle MLD timer events.
  489. //
  490. // This function is called periodically by IPv6Timeout.
  491. // We decrement the timer value in each MAE on the query list.
  492. // If the timer reaches zero, we send a group membership report.
  493. // If the timer is already zero, that means we should send
  494. // a Done message and then free the MAE. In this case, the MAE
  495. // holds an interface reference. See DeleteMAE.
  496. //
  497. void
  498. MLDTimeout(void)
  499. {
  500. typedef struct MLDReportRequest {
  501. struct MLDReportRequest *Next;
  502. Interface *IF;
  503. IPv6Addr GroupAddr;
  504. } MLDReportRequest;
  505. MulticastAddressEntry **PrevMAE, *MAE;
  506. MLDReportRequest *ReportList = NULL;
  507. MLDReportRequest *Request;
  508. MulticastAddressEntry *DoneList = NULL;
  509. //
  510. // Lock the QueryList so we can traverse it and decrement timers.
  511. // But we avoid sending messages while holding any locks
  512. // by building a list of requested reports.
  513. //
  514. KeAcquireSpinLockAtDpcLevel(&QueryListLock);
  515. PrevMAE = &QueryList;
  516. while ((MAE = *PrevMAE) != NULL) {
  517. ASSERT(MAE->Type == ADE_MULTICAST);
  518. if (MAE->MCastTimer == 0) {
  519. //
  520. // We need to send a Done message.
  521. // Remove this MAE from the QueryList
  522. // and put it on a temporary list.
  523. //
  524. *PrevMAE = MAE->NextQL;
  525. MAE->NextQL = DoneList;
  526. DoneList = MAE;
  527. continue;
  528. }
  529. else if (--MAE->MCastTimer == 0) {
  530. //
  531. // This entry has expired, we need to send a Report.
  532. //
  533. Request = ExAllocatePool(NonPagedPool, sizeof *Request);
  534. if (Request != NULL) {
  535. Request->Next = ReportList;
  536. ReportList = Request;
  537. Request->IF = MAE->NTEorIF->IF;
  538. AddRefIF(Request->IF);
  539. Request->GroupAddr = MAE->Address;
  540. //
  541. // Set the flag indicating we sent the last report
  542. // on the link.
  543. //
  544. MAE->MCastFlags |= MAE_LAST_REPORTER;
  545. }
  546. if (MAE->MCastCount != 0) {
  547. if (MAE->NTEorIF->IF->Flags & IF_FLAG_PERIODICMLD) {
  548. //
  549. // On tunnels to 6to4 relays, we continue to generate
  550. // periodic reports since queries cannot be sent over
  551. // an NBMA interface.
  552. //
  553. MAE->MCastTimer = MLD_QUERY_INTERVAL;
  554. }
  555. else {
  556. //
  557. // If we are sending unsolicited reports,
  558. // then leave the MAE on the query list
  559. // and set a new timer value.
  560. //
  561. if (--MAE->MCastCount == 0)
  562. goto Remove;
  563. MAE->MCastTimer = (ushort)
  564. RandomNumber(0, MLD_UNSOLICITED_REPORT_INTERVAL) + 1;
  565. }
  566. }
  567. else {
  568. Remove:
  569. //
  570. // Remove the MAE from the query list.
  571. //
  572. *PrevMAE = MAE->NextQL;
  573. continue;
  574. }
  575. }
  576. //
  577. // Go on to the next MAE.
  578. //
  579. PrevMAE = &MAE->NextQL;
  580. }
  581. KeReleaseSpinLockFromDpcLevel(&QueryListLock);
  582. //
  583. // Send MLD Report messages.
  584. //
  585. while ((Request = ReportList) != NULL) {
  586. ReportList = Request->Next;
  587. //
  588. // Send the MLD Report message.
  589. //
  590. MLDReportSend(Request->IF, &Request->GroupAddr);
  591. //
  592. // Free this structure.
  593. //
  594. ReleaseIF(Request->IF);
  595. ExFreePool(Request);
  596. }
  597. //
  598. // Send MLD Done messages.
  599. //
  600. while ((MAE = DoneList) != NULL) {
  601. Interface *IF = MAE->IF;
  602. DoneList = MAE->NextQL;
  603. //
  604. // Send the MLD Done message.
  605. //
  606. MLDDoneSend(IF, &MAE->Address);
  607. //
  608. // Free this structure.
  609. //
  610. ExFreePool(MAE);
  611. ReleaseIF(IF);
  612. }
  613. }
  614. //* MLDInit - Initialize MLD.
  615. //
  616. // Initialize MLD global data structures.
  617. //
  618. void
  619. MLDInit(void)
  620. {
  621. KeInitializeSpinLock(&QueryListLock);
  622. QueryList = NULL;
  623. }