summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/tcpip/ip/igmp.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/tcpip/ip/igmp.c799
1 files changed, 799 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/ip/igmp.c b/private/ntos/tdi/tcpip/ip/igmp.c
new file mode 100644
index 000000000..340f0380f
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/igmp.c
@@ -0,0 +1,799 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** igmp.c - IP multicast routines.
+//
+// This file contains all the routines related to the IGMP protocol.
+
+#include "oscfg.h"
+#include "cxport.h"
+#include "ndis.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "igmp.h"
+#include "icmp.h"
+#include "ipxmit.h"
+#include "llipif.h"
+#include "iproute.h"
+
+
+#define IGMP_QUERY 0x11 //Membership query
+#define IGMP_REPORT_V1 0x12 //Version 1 membership report
+#define IGMP_REPORT_V2 0x16 //Version 2 membership report
+#define IGMP_LEAVE 0x17 //Leave Group
+
+
+#define IGMPV1 2 //IGMP version 1
+#define IGMPV2 3 //IGMP version 2
+
+//
+// undefine for 4.0 sp2
+//
+#undef IGMPV2
+
+#define ALL_HOST_MCAST 0x010000E0
+
+#define MAX_DELAY_TICKS 20 //used when sending a report after a
+ //mcast group has been added. The
+ //report is sent at a interval of
+ //500 msecs to 9.5 secs
+
+//
+// The following values are used to initialize counters that keep time in
+// 1/2 a sec.
+//
+#define MAX_DELAY_IGMPV1_QUERY_RESP 20 //10 secs
+
+//
+// The amount of time we stay in the "IGMPV1 Router Present" state in the
+// absence of an IGMPV1 query
+//
+#define VERSION1_ROUTER_TIMEOUT 800 //400 secs
+
+int RandomValue;
+int Seed;
+
+// Structure of an IGMP header.
+typedef struct IGMPHeader {
+ uchar igh_vertype; // Type of igmp message
+ uchar igh_rsvd; // max. resp. time for igmpv2 messages; will be 0
+ // for igmpv1 messages
+ ushort igh_xsum;
+ IPAddr igh_addr;
+} IGMPHeader;
+
+
+typedef struct IGMPBlockStruct {
+ struct IGMPBlockStruct *ibs_next;
+ CTEBlockStruc ibs_block;
+} IGMPBlockStruct;
+
+void *IGMPProtInfo;
+
+IGMPBlockStruct *IGMPBlockList;
+uchar IGMPBlockFlag;
+
+DEFINE_LOCK_STRUCTURE(IGMPLock)
+
+extern ProtInfo *RawPI; // Raw IP protinfo
+
+extern IP_STATUS IPCopyOptions(uchar *, uint, IPOptInfo *);
+extern void IPInitOptions(IPOptInfo *);
+extern void *IPRegisterProtocol(uchar Protocol, void *RcvHandler,
+ void *XmitHandler, void *StatusHandler, void *RcvCmpltHandler);
+
+
+#ifdef NT
+
+//
+// All of the init code can be discarded
+//
+#ifdef ALLOC_PRAGMA
+
+uint IGMPInit(void);
+
+#pragma alloc_text(INIT, IGMPInit)
+
+#endif // ALLOC_PRAGMA
+
+#endif // NT
+
+//* IGMPRandomTicks - Generate a random value of timer ticks.
+//
+// A random number routine to generate a random number of timer ticks,
+// between 1 and time (in units of half secs) passed. The random number
+// algorithm is adapted from the book 'System Simulation' by Geoffrey Gordon.
+//
+// Input: Nothing.
+//
+// Returns: A random value between 1 and TimeDelayInHalfSec.
+//
+uint
+IGMPRandomTicks(uint TimeDelayInHalfSec)
+{
+
+ RandomValue = RandomValue * 1220703125;
+
+ if (RandomValue < 0) {
+ RandomValue += 2147483647; // inefficient, but avoids warnings.
+ RandomValue++;
+ }
+
+ // Not sure if RandomValue can get to 0, but if it does the algorithm
+ // degenerates, so fix this if it happens.
+ if (RandomValue == 0)
+ RandomValue = ((Seed + (int)CTESystemUpTime()) % 100000000) | 1;
+
+ return (uint)(((uint)RandomValue % TimeDelayInHalfSec) + 1);
+}
+
+
+//* FindIGMPAddr - Find an mcast entry on an NTE.
+//
+// Called to search an NTE for an IGMP entry for a given class D address.
+// We walk down the chain on the NTE looking for it. If we find it,
+// we return a pointer to it and the one immediately preceding it. If we
+// don't find it we return NULL. We assume the caller has taken the lock
+// on the NTE before calling us.
+//
+// Input: NTE - NTE on which to search.
+// Addr - Class D address to find.
+// PrevPtr - Where to return pointer to preceding entry.
+//
+// Returns: Pointer to matching IGMPAddr structure if found, or NULL if not
+// found.
+//
+IGMPAddr *
+FindIGMPAddr(NetTableEntry *NTE, IPAddr Addr, IGMPAddr **PrevPtr)
+{
+ IGMPAddr *Current, *Temp;
+
+ Temp = STRUCT_OF(IGMPAddr, &NTE->nte_igmplist, iga_next);
+ Current = NTE->nte_igmplist;
+
+ while (Current != NULL) {
+ if (IP_ADDR_EQUAL(Current->iga_addr, Addr)) {
+ // Found a match, so return it.
+ *PrevPtr = Temp;
+ break;
+ }
+ Temp = Current;
+ Current = Current->iga_next;
+ }
+
+ return Current;
+
+}
+
+//** IGMPRcv - Receive an IGMP datagram.
+//
+// Called by IP when we receive an IGMP datagram. We validate it to make sure
+// it's reasonable. Then if it it's a query for a group to which we belong
+// we'll start a response timer. If it's a report to a group to which we belong
+// we'll stop any running timer.
+//
+// The IGMP header is only 8 bytes long, and so should always fit in exactly
+// one IP rcv buffer. We check this to make sure, and if it takes multiple
+// buffers we discard it.
+//
+// Entry: NTE - Pointer to NTE on which IGMP message was received.
+// Dest - IPAddr of destination (should be a Class D address).
+// Src - IPAddr of source
+// LocalAddr - Local address of network which caused this to be
+// received.
+// SrcAddr - Address of local interface which received the
+// packet
+// IPHdr - Pointer to the IP Header.
+// IPHdrLength - Bytes in IPHeader.
+// RcvBuf - Pointer to IP receive buffer chain.
+// Size - Size in bytes of IGMP message.
+// IsBCast - Boolean indicator of whether or not this came in
+// as a bcast (should always be true).
+// Protocol - Protocol this came in on.
+// OptInfo - Pointer to info structure for received options.
+//
+// Returns: Status of reception
+IP_STATUS
+IGMPRcv(NetTableEntry *NTE, IPAddr Dest, IPAddr Src, IPAddr LocalAddr,
+ IPAddr SrcAddr, IPHeader UNALIGNED *IPHdr, uint IPHdrLength,
+ IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, uchar Protocol,
+ IPOptInfo *OptInfo)
+{
+ IGMPHeader UNALIGNED *IGH;
+ CTELockHandle Handle;
+ IGMPAddr *AddrPtr, *PrevPtr;
+ uchar DType;
+ uint ReportingDelayInHalfSec;
+
+
+ CTEAssert(CLASSD_ADDR(Dest));
+ CTEAssert(IsBCast);
+
+ // Make sure we're running at least level 2 of IGMP support.
+ if (IGMPLevel != 2)
+ return IP_SUCCESS;
+
+ // Discard packets with invalid or broadcast source addresses.
+ DType = GetAddrType(Src);
+ if (DType == DEST_INVALID || IS_BCAST_DEST(DType))
+ return IP_SUCCESS;
+
+ // Check the size to make sure it's valid.
+ if (Size != sizeof(IGMPHeader) || RcvBuf->ipr_size != sizeof(IGMPHeader))
+ return IP_SUCCESS;
+
+ // Now get the pointer to the header, and validate the xsum.
+ IGH = (IGMPHeader UNALIGNED *)RcvBuf->ipr_buffer;
+
+ if (xsum(IGH, sizeof(IGMPHeader)) != 0xffff) {
+ // Bad checksum, so fail.
+ return IP_SUCCESS;
+ }
+
+ // If we sent it, don't process this message.
+ if (IP_ADDR_EQUAL(Src, LocalAddr))
+ return IP_SUCCESS;
+
+ // OK, we may need to process this. See if we are a member of the
+ // destination group. If we aren't, there's no need to proceed further.
+ CTEGetLock(&NTE->nte_lock, &Handle);
+
+ if (NTE->nte_flags & NTE_VALID) {
+ //
+ // The NTE is valid. Demux on type.
+ //
+ switch (IGH->igh_vertype) {
+
+ case IGMP_QUERY:
+
+ //
+ // If it is an IGMPV1 query, set the timer value for staying in
+ // igmpv1 mode
+ //
+#ifdef IGMPV2
+ if (IGH->igh_rsvd == 0) {
+ //
+ // Since for any interface we always get notified with
+ // same NTE, locking the NTE is fine. We don't have to
+ // lock the interface structure
+ //
+ if (NTE->nte_if->IgmpVersion == IGMPV2) {
+ NTE->nte_if->IgmpVersion = IGMPV1;
+ }
+ NTE->nte_if->IgmpVer1Timeout = VERSION1_ROUTER_TIMEOUT;
+ ReportingDelayInHalfSec = MAX_DELAY_IGMPV1_QUERY_RESP;
+ }
+ else {
+ ReportingDelayInHalfSec = IGH->igh_rsvd * 5; //field's unit are in 100ms
+ }
+#else
+ ReportingDelayInHalfSec = MAX_DELAY_IGMPV1_QUERY_RESP;
+#endif
+
+ //
+ // This is a query. Walk our list and set a random report timer for
+ // all those class D addresses that don't already have one running
+ // (except for the all host's address).
+ //
+ for (AddrPtr = NTE->nte_igmplist; AddrPtr != NULL; AddrPtr = AddrPtr->iga_next) {
+ if (!IP_ADDR_EQUAL(AddrPtr->iga_addr, ALL_HOST_MCAST)) {
+ if (AddrPtr->iga_timer == 0) {
+ AddrPtr->iga_timer = IGMPRandomTicks(ReportingDelayInHalfSec);
+ }
+ }
+ }
+
+ break;
+
+ case IGMP_REPORT_V1:
+ case IGMP_REPORT_V2:
+ //
+ // This is a report. Check it's validity and see if we have a
+ // report timer running for that address. If we do, stop it.
+ // Make sure the destination address matches the address in the
+ // IGMP header.
+ //
+ if (IP_ADDR_EQUAL(Dest, IGH->igh_addr)) {
+ // The addresses match. See if we have a membership in this
+ // group.
+ AddrPtr = FindIGMPAddr(NTE, IGH->igh_addr, &PrevPtr);
+ if (AddrPtr != NULL) {
+ // We found a matching class D address. Stop the timer.
+ AddrPtr->iga_timer = 0;
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ //
+ // Pass the packet up to the raw layer if applicable.
+ //
+ if (RawPI != NULL) {
+ if (RawPI->pi_rcv != NULL) {
+ (*(RawPI->pi_rcv))(NTE, Dest, Src, LocalAddr, SrcAddr, IPHdr,
+ IPHdrLength, RcvBuf, Size, IsBCast, Protocol, OptInfo);
+ }
+ }
+
+ return IP_SUCCESS;
+ }
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ return IP_SUCCESS;
+}
+
+//* SendIGMPReport - Send an IGMP report.
+//
+// Called when we want to send an IGMP report for some reason. For this
+// purpose we steal ICMP buffers. What we'll do is get one, fill it in,
+// and send it.
+//
+// Input: Dest - Destination to send to.
+// Src - Source to send from.
+//
+// Returns: Nothing.
+//
+void
+SendIGMPReport(uint ChangeType, uint IgmpVersion, IPAddr Dest, IPAddr Src)
+{
+ IGMPHeader *IGH;
+ PNDIS_BUFFER Buffer;
+ IPOptInfo OptInfo; // Options for this transmit.
+ IP_STATUS Status;
+ int ReportType;
+
+ CTEAssert(CLASSD_ADDR(Dest));
+ CTEAssert(!IP_ADDR_EQUAL(Src, NULL_IP_ADDR));
+
+ // Make sure we never send a report for the all-hosts mcast address.
+ if (IP_ADDR_EQUAL(Dest, ALL_HOST_MCAST)) {
+ DEBUGCHK;
+ return;
+ }
+ //
+ // If the report to be sent is a "Leave Group" report but we have
+ // detected an igmp v1 router on this net, do not send the report
+ //
+#ifdef IGMPV2
+ if (IgmpVersion == IGMPV1) {
+ if (ChangeType == IGMP_DELETE) {
+ return;
+ } else {
+#endif
+ ReportType = IGMP_REPORT_V1;
+#ifdef IGMPV2
+ }
+ } else {
+ if (ChangeType == IGMP_DELETE) {
+ ReportType = IGMP_LEAVE;
+ } else {
+ ReportType = IGMP_REPORT_V2;
+ }
+ }
+#endif
+
+ IGH = (IGMPHeader *)GetICMPBuffer(sizeof(IGMPHeader), &Buffer);
+ if (IGH != NULL) {
+ // We got the buffer. Fill it in and send it.
+ IGH->igh_vertype = ReportType;
+ IGH->igh_rsvd = 0;
+ IGH->igh_xsum = 0;
+ IGH->igh_addr = Dest;
+ IGH->igh_xsum = ~xsum(IGH, sizeof(IGMPHeader));
+
+ IPInitOptions(&OptInfo);
+ OptInfo.ioi_ttl = 1;
+
+ Status = IPTransmit(IGMPProtInfo, NULL, Buffer, sizeof(IGMPHeader),
+ Dest, Src, &OptInfo, NULL, PROT_IGMP);
+
+ if (Status != IP_PENDING)
+ ICMPSendComplete(NULL, Buffer);
+ }
+
+}
+
+//* IGMPAddrChange - Change the IGMP address list on an NTE.
+//
+// Called to add or delete an IGMP address. We're given the relevant NTE,
+// the address, and the action to be performed. We validate the NTE, the
+// address, and the IGMP level, and then attempt to perform the action.
+//
+// There are a bunch of strange race conditions that can occur during adding/
+// deleting addresses, related to trying to add the same address twice and
+// having it fail, or adding and deleting the same address simultaneously. Most
+// of these happen because we have to free the lock to call the interface,
+// and the call to the interface can fail. To prevent this we serialize all
+// access to this routine. Only one thread of execution can go through here
+// at a time, all others are blocked.
+//
+// Input: NTE - NTE with list to be altered.
+// Addr - Address affected.
+// ChangeType - Type of change - IGMP_ADD, IGMP_DELETE,
+// IGMP_DELETE_ALL.
+//
+// Returns: IP_STATUS of attempt to perform action.
+//
+IP_STATUS
+IGMPAddrChange(NetTableEntry *NTE, IPAddr Addr, uint ChangeType)
+{
+ CTELockHandle Handle;
+ IGMPAddr *AddrPtr, *PrevPtr;
+ IP_STATUS Status;
+ Interface *IF;
+ uint AddrAdded;
+ IGMPBlockStruct Block;
+ IGMPBlockStruct *BlockPtr;
+ uint IgmpVersion;
+
+ // First make sure we're at level 2 of IGMP support.
+
+ if (IGMPLevel != 2)
+ return IP_BAD_REQ;
+
+ CTEInitBlockStruc(&Block.ibs_block);
+
+ // Make sure we're the only ones in this routine. If someone else is
+ // already here, block.
+
+ CTEGetLock(&IGMPLock, &Handle);
+ if (IGMPBlockFlag) {
+
+ // Someone else is already here. Walk down the block list, and
+ // put ourselves on the end. Then free the lock and block on our
+ // IGMPBlock structure.
+ BlockPtr = STRUCT_OF(IGMPBlockStruct, &IGMPBlockList, ibs_next);
+ while (BlockPtr->ibs_next != NULL)
+ BlockPtr = BlockPtr->ibs_next;
+
+ Block.ibs_next = NULL;
+ BlockPtr->ibs_next = &Block;
+ CTEFreeLock(&IGMPLock, Handle);
+ CTEBlock(&Block.ibs_block);
+ } else {
+ // Noone else here, set the flag so noone else gets in and free the
+ // lock.
+ IGMPBlockFlag = 1;
+ CTEFreeLock(&IGMPLock, Handle);
+ }
+
+ // Now we're in the routine, and we won't be reentered here by another
+ // thread of execution. Make sure everything's valid, and figure out
+ // what to do.
+
+ Status = IP_SUCCESS;
+
+ // Now get the lock on the NTE and make sure it's valid.
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ if ((NTE->nte_flags & NTE_VALID) || ChangeType == IGMP_DELETE_ALL) {
+ // The NTE is valid. Try to find an existing IGMPAddr structure
+ // that matches the input address.
+ AddrPtr = FindIGMPAddr(NTE, Addr, &PrevPtr);
+ IF = NTE->nte_if;
+
+#ifdef IGMPV2
+ IgmpVersion = IF->IgmpVersion;
+#else
+ IgmpVersion = IGMPV1;
+#endif
+ // Now figure out the action to be performed.
+ switch (ChangeType) {
+
+ case IGMP_ADD:
+
+ // We're to add this. If AddrPtr is NULL, we'll need to
+ // allocate memory and link the new IGMP address in. Otherwise
+ // we can just increment the reference count on the existing
+ // address structure.
+ if (AddrPtr == NULL) {
+ // AddrPtr is NULL, i.e. the address doesn't currently
+ // exist. Allocate memory for it, then try to add the
+ // address locally.
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ // If this is not a class D address, fail the request.
+ if (!CLASSD_ADDR(Addr)) {
+ Status = IP_BAD_REQ;
+ break;
+ }
+
+ AddrPtr = CTEAllocMem(sizeof(IGMPAddr));
+ if (AddrPtr != NULL) {
+
+ // Got memory. Try to add the address locally.
+ AddrAdded = (*IF->if_addaddr)(IF->if_lcontext,
+ LLIP_ADDR_MCAST, Addr, 0, NULL);
+
+ // See if we added it succesfully. If we did, fill in
+ // the stucture and link it in.
+
+ if (AddrAdded) {
+
+ AddrPtr->iga_addr = Addr;
+ AddrPtr->iga_refcnt = 1;
+ AddrPtr->iga_timer = 0;
+
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ AddrPtr->iga_next = NTE->nte_igmplist;
+ NTE->nte_igmplist = AddrPtr;
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ if (!IP_ADDR_EQUAL(Addr, ALL_HOST_MCAST)) {
+ // This isn't the all host address, so send a
+ // report for it.
+ AddrPtr->iga_timer = IGMPRandomTicks(MAX_DELAY_TICKS);
+ SendIGMPReport(ChangeType, IgmpVersion, Addr,
+ NTE->nte_addr);
+ }
+ } else {
+ // Couldn't add the local address. Free the memory
+ // and fail the request.
+ CTEFreeMem(AddrPtr);
+ Status = IP_NO_RESOURCES;
+ }
+
+ } else {
+ Status = IP_NO_RESOURCES;
+ }
+ } else {
+ // Already have this one. Bump his count.
+ (AddrPtr->iga_refcnt)++;
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ }
+ break;
+ case IGMP_DELETE:
+
+ // This is a delete request. If we didn't find the requested
+ // address, fail the request. Otherwise dec his refcnt, and if
+ // it goes to 0 delete the address locally.
+ if (AddrPtr != NULL) {
+ // Have one. We won't let the all-hosts mcast address go
+ // away, but for other's we'll check to see if it's time
+ // to delete them.
+ if (!IP_ADDR_EQUAL(Addr, ALL_HOST_MCAST) &&
+ --(AddrPtr->iga_refcnt) == 0) {
+ // This one is to be deleted. Pull him from the
+ // list, and call the lower interface to delete him.
+ PrevPtr->iga_next = AddrPtr->iga_next;
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ CTEFreeMem(AddrPtr);
+ (*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_MCAST,
+ Addr, 0);
+ //
+ // Send a report to indicate that we are leaving the
+ // group
+ //
+
+#ifdef IGMPV2
+ SendIGMPReport(ChangeType, IgmpVersion, Addr,
+ NTE->nte_addr);
+#endif
+ } else
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ } else {
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ Status = IP_BAD_REQ;
+ }
+ break;
+ case IGMP_DELETE_ALL:
+ // We've been called to delete all of this addresses,
+ // regardless of their reference count. This should only
+ // happen when the NTE is going away.
+ AddrPtr = NTE->nte_igmplist;
+ NTE->nte_igmplist = NULL;
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ // Walk down the list, deleteing each one.
+ while (AddrPtr != NULL) {
+ (*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_MCAST,
+ AddrPtr->iga_addr, 0);
+#ifdef IGMPV2
+ if (!IP_ADDR_EQUAL(AddrPtr->iga_addr, ALL_HOST_MCAST)) {
+ SendIGMPReport(IGMP_DELETE, IgmpVersion, AddrPtr->iga_addr, NTE->nte_addr);
+ }
+#endif
+ PrevPtr = AddrPtr;
+ AddrPtr = AddrPtr->iga_next;
+ CTEFreeMem(PrevPtr);
+ }
+
+ // All done.
+ break;
+ default:
+ DEBUGCHK;
+ break;
+ }
+ } else {
+ // NTE isn't valid.
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ Status = IP_BAD_REQ;
+ }
+
+
+ // We finished the request, and Status contains the completion status.
+ // If there are any pending blocks for this routine, signal the next
+ // one now. Otherwise clear the block flag.
+ CTEGetLock(&IGMPLock, &Handle);
+ if ((BlockPtr = IGMPBlockList) != NULL) {
+ // Someone is blocking. Pull him from the list and signal him.
+ IGMPBlockList = BlockPtr->ibs_next;
+ CTEFreeLock(&IGMPLock, Handle);
+
+ CTESignal(&BlockPtr->ibs_block, IP_SUCCESS);
+ } else {
+ // No one blocking, just clear the flag.
+ IGMPBlockFlag = 0;
+ CTEFreeLock(&IGMPLock, Handle);
+ }
+
+ return Status;
+
+}
+
+//* IGMPTimer - Handle an IGMP timer event.
+//
+// This function is called every 500 ms. by IP. If we're at level 2 of
+// IGMP functionality we run down the NTE looking for running timers. If
+// we find one, we see if it has expired and if so we send an
+// IGMP report.
+//
+// Input: NTE - Pointer to NTE to check.
+//
+// Returns: Nothing.
+//
+void
+IGMPTimer(NetTableEntry *NTE)
+{
+ CTELockHandle Handle;
+ IGMPAddr *AddrPtr, *PrevPtr;
+ uint IgmpVersion;
+
+ if (IGMPLevel == 2) {
+ // We are doing IGMP. Run down the addresses active on this NTE.
+ CTEGetLock(&NTE->nte_lock, &Handle);
+
+ //
+ // if we haven't heard any query or report from an igmpv1 router or
+ // host during timeout period, revert to igmpv2. No need to check
+ // whether NTE is valid or not
+ //
+#ifdef IGMPV2
+ if ((NTE->nte_if->IgmpVer1Timeout != 0) && (--(NTE->nte_if->IgmpVer1Timeout) == 0)) {
+ NTE->nte_if->IgmpVersion = IGMPV2;
+ }
+#endif
+ PrevPtr = STRUCT_OF(IGMPAddr, &NTE->nte_igmplist, iga_next);
+ AddrPtr = PrevPtr->iga_next;
+ while (AddrPtr != NULL) {
+ // We have one. See if it's running.
+ if (AddrPtr->iga_timer != 0) {
+ // It's running. See if it's expired.
+ if (--(AddrPtr->iga_timer) == 0 && NTE->nte_flags & NTE_VALID) {
+ // It's expired. Increment the ref count so it
+ // doesn't go away while we're here, and send a report.
+ AddrPtr->iga_refcnt++;
+#ifdef IGMPV2
+ IgmpVersion = NTE->nte_if->IgmpVersion;
+#else
+ IgmpVersion = IGMPV1;
+#endif
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+
+ SendIGMPReport(IGMP_ADD, IgmpVersion, AddrPtr->iga_addr,
+ NTE->nte_addr);
+ // Now get the lock, and decrement the refcnt. If it goes
+ // to 0, it's been deleted so we need to free it.
+ CTEGetLock(&NTE->nte_lock, &Handle);
+ if (--(AddrPtr->iga_refcnt) == 0) {
+ // It's been deleted.
+ PrevPtr->iga_next = AddrPtr->iga_next;
+ CTEFreeMem(AddrPtr);
+ AddrPtr = PrevPtr->iga_next;
+ continue;
+ }
+ }
+ }
+ // Either the timer isn't running or hasn't fired. Try the next
+ // one.
+ PrevPtr = AddrPtr;
+ AddrPtr = AddrPtr->iga_next;
+ }
+
+ CTEFreeLock(&NTE->nte_lock, Handle);
+ }
+
+}
+
+//* InitIGMPForNTE - Called to do per-NTE initialization.
+//
+// Called when an NTE becomes valid. If we're at level 2, we put the all-host
+// mcast on the list and add the address to the interface.
+//
+// Input: NTE - NTE on which to act.
+//
+// Returns: Nothing.
+//
+void
+InitIGMPForNTE(NetTableEntry *NTE)
+{
+ if (IGMPLevel == 2) {
+ IGMPAddrChange(NTE, ALL_HOST_MCAST, IGMP_ADD);
+ if (NTE->nte_rtrdiscovery && (NTE->nte_rtrdiscaddr == ALL_ROUTER_MCAST)) {
+ IGMPAddrChange(NTE, ALL_ROUTER_MCAST, IGMP_ADD);
+ }
+ }
+ if (Seed == 0) {
+ // No random seed yet.
+ Seed = (int)NTE->nte_addr;
+
+ // Make sure the inital value is odd, and less than 9 decimal digits.
+ RandomValue = ((Seed + (int)CTESystemUpTime()) % 100000000) | 1;
+ }
+}
+
+//* StopIGMPForNTE - Called to do per-NTE shutdown.
+//
+// Called when we're shutting down and NTE, and want to stop IGMP on hi,
+//
+// Input: NTE - NTE on which to act.
+//
+// Returns: Nothing.
+//
+void
+StopIGMPForNTE(NetTableEntry *NTE)
+{
+ if (IGMPLevel == 2) {
+ IGMPAddrChange(NTE, NULL_IP_ADDR, IGMP_DELETE_ALL);
+ }
+}
+
+#pragma BEGIN_INIT
+
+//** IGMPInit - Initialize IGMP.
+//
+// This bit of code initializes IGMP generally. There is also some amount
+// of work done on a per-NTE basis that we do when each one is initialized.
+//
+// Input: Nothing.
+///
+// Returns: TRUE if we init, FALSE if we don't.
+//
+uint
+IGMPInit(void)
+{
+
+ if (IGMPLevel != 2)
+ return TRUE;
+
+ CTEInitLock(&IGMPLock);
+ IGMPBlockList = NULL;
+ IGMPBlockFlag = 0;
+ Seed = 0;
+
+ // We fake things a little bit. We register our receive handler, but
+ // since we steal buffers from ICMP we register the ICMP send complete
+ // handler.
+ IGMPProtInfo = IPRegisterProtocol(PROT_IGMP, IGMPRcv, ICMPSendComplete,
+ NULL, NULL);
+
+ if (IGMPProtInfo != NULL)
+ return TRUE;
+ else
+ return FALSE;
+
+}
+
+#pragma END_INIT