mirror of https://github.com/lianthony/NT4.0
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.
799 lines
24 KiB
799 lines
24 KiB
/********************************************************************/
|
|
/** 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
|