diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/tcpip/ip/igmp.c | 799 |
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 |