295 lines
5.8 KiB
C
295 lines
5.8 KiB
C
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
igmp.c
|
|
|
|
Abstract:
|
|
|
|
Handle IGMP protocol version 2
|
|
|
|
Revision History:
|
|
|
|
05/31/2000 davidx
|
|
Created it.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
VOID
|
|
IgmpSendMessage(
|
|
IfInfo* ifp,
|
|
IPADDR dstaddr,
|
|
BYTE type,
|
|
IPADDR groupaddr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send out an IGMP report or leave message
|
|
|
|
Arguments:
|
|
|
|
ifp - Points to the interface structure
|
|
dstaddr - Specifies the message's destination address
|
|
type - Message type
|
|
groupaddr - Address of the multicast group in question
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
Packet* pkt;
|
|
IgmpMessage* igmpmsg;
|
|
IpHeader* iphdr;
|
|
|
|
pkt = XnetAllocIpPacket(0, IGMPHDRLEN);
|
|
if (!pkt) return;
|
|
|
|
igmpmsg = GETPKTDATA(pkt, IgmpMessage);
|
|
igmpmsg->type = type;
|
|
igmpmsg->maxresptime = 0;
|
|
igmpmsg->groupaddr = groupaddr;
|
|
COMPUTE_CHECKSUM(igmpmsg->checksum, igmpmsg, IGMPHDRLEN);
|
|
|
|
iphdr = GETPKTIPHDR(pkt);
|
|
pkt->data -= IPHDRLEN;
|
|
pkt->datalen += IPHDRLEN;
|
|
pkt->nexthop = dstaddr;
|
|
|
|
FILL_IPHEADER(
|
|
iphdr,
|
|
IPHDRLEN,
|
|
0,
|
|
pkt->datalen,
|
|
0,
|
|
1,
|
|
IPPROTOCOL_IGMP,
|
|
ifp->ipaddr,
|
|
dstaddr);
|
|
|
|
// Queue up the packet for transmission
|
|
IfEnqueuePacket(ifp, pkt);
|
|
ifp->StartOutput(ifp);
|
|
}
|
|
|
|
|
|
PRIVATE VOID
|
|
IgmpSetQueryReportTimer(
|
|
IfInfo* ifp,
|
|
IfMcastGroup* mcastgrp,
|
|
UINT maxresptime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the query report timer on a multicast group we belong to.
|
|
|
|
Arguments:
|
|
|
|
ifp - Points to the interface structure
|
|
mcastgrp - Points to the multicast group in question
|
|
maxresptime - Specifies the maximum response time in 1/10 of a second
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
// If existing timer value is less than the new max response time,
|
|
// then we just leave the existing timer alone.
|
|
if (mcastgrp->reportTimer && mcastgrp->reportTimer*10 <= maxresptime)
|
|
return;
|
|
|
|
// Randomize the response timer
|
|
maxresptime = XnetRandScaled(maxresptime) / 10;
|
|
|
|
if ((mcastgrp->reportTimer = maxresptime) == 0) {
|
|
IgmpSendReport(ifp, mcastgrp);
|
|
}
|
|
}
|
|
|
|
|
|
PRIVATE VOID
|
|
IgmpProcessMessage(
|
|
IfInfo* ifp,
|
|
IgmpMessage* igmpmsg,
|
|
IPADDR dstaddr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process a received IGMP message
|
|
|
|
Arguments:
|
|
|
|
ifp - Points to the interface structure
|
|
igmpmsg - Points to the received IGMP message
|
|
dstaddr - Destination group the message is addressed to
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
Note:
|
|
|
|
This function will only be called when the interface
|
|
belongs to the message's destination address group.
|
|
|
|
--*/
|
|
|
|
#define LOCATE_MCASTGROUP() \
|
|
while (count--) { \
|
|
if (mcastgrp->mcastaddr == dstaddr) break; \
|
|
mcastgrp++; \
|
|
}
|
|
|
|
{
|
|
IfMcastData* ifmcast = ifp->mcastData;
|
|
IfMcastGroup* mcastgrp = ifmcast->mcastGroups;
|
|
UINT count = ifmcast->groupCount;
|
|
IPADDR grpaddr = igmpmsg->groupaddr;
|
|
|
|
switch (igmpmsg->type) {
|
|
case IGMPTYPE_MEMBER_QUERY:
|
|
if (grpaddr == 0) {
|
|
UINT maxresptime;
|
|
|
|
// General query must be directed to all-hosts address
|
|
if (dstaddr != IPADDR_ALLHOSTS) return;
|
|
|
|
if (igmpmsg->maxresptime == 0) {
|
|
ifmcast->hasV1Querier = TRUE;
|
|
ifmcast->v1QuerierTimer = IGMP_V1_QUERIER_TIMER;
|
|
maxresptime = IGMP_V1_QUERY_RESPONSE*10;
|
|
} else
|
|
maxresptime = igmpmsg->maxresptime;
|
|
|
|
while (count--) {
|
|
IgmpSetQueryReportTimer(ifp, mcastgrp, maxresptime);
|
|
mcastgrp++;
|
|
}
|
|
} else {
|
|
// Group-specific query must directed to that group address
|
|
if (dstaddr != grpaddr) return;
|
|
|
|
LOCATE_MCASTGROUP();
|
|
IgmpSetQueryReportTimer(ifp, mcastgrp, igmpmsg->maxresptime);
|
|
}
|
|
break;
|
|
|
|
case IGMPTYPE_MEMBER_REPORT_1:
|
|
case IGMPTYPE_MEMBER_REPORT_2:
|
|
// Membership report must be sent to the specific group
|
|
if (dstaddr != grpaddr) return;
|
|
|
|
LOCATE_MCASTGROUP();
|
|
mcastgrp->reportTimer = 0;
|
|
mcastgrp->sentLastReport = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
IgmpReceivePacket(
|
|
Packet* pkt
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Receive an IGMP packet
|
|
|
|
Arguments:
|
|
|
|
pkt - Points to the received IGMP message
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
RUNS_AT_DISPATCH_LEVEL
|
|
|
|
// Verify packet length and checksum
|
|
if (pkt->datalen < IGMPHDRLEN ||
|
|
tcpipxsum(0, pkt->data, pkt->datalen) != 0xffff) {
|
|
XnetCompletePacket(pkt, NETERR_DISCARDED);
|
|
return;
|
|
}
|
|
|
|
// We're only interested in packets addressed
|
|
// to the all-host multicast group here.
|
|
if (pkt->recvifp->mcastData) {
|
|
IgmpProcessMessage(
|
|
pkt->recvifp,
|
|
GETPKTDATA(pkt, IgmpMessage),
|
|
GETPKTIPHDR(pkt)->dstaddr);
|
|
}
|
|
|
|
// Pass the packet upstream for further processing
|
|
RawReceivePacket(pkt);
|
|
}
|
|
|
|
|
|
VOID
|
|
IgmpTimerProc(
|
|
IfInfo* ifp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IGMP timer procedure
|
|
|
|
Arguments:
|
|
|
|
ifp - Points to the interface structure
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
IfMcastData* ifmcast = ifp->mcastData;
|
|
IfMcastGroup* mcastgrp;
|
|
UINT count;
|
|
|
|
ASSERT(ifmcast != NULL);
|
|
|
|
if (ifmcast->v1QuerierTimer && --(ifmcast->v1QuerierTimer) == 0)
|
|
ifmcast->hasV1Querier = FALSE;
|
|
|
|
mcastgrp = ifmcast->mcastGroups;
|
|
count = ifmcast->groupCount;
|
|
while (count--) {
|
|
if (mcastgrp->reportTimer && --(mcastgrp->reportTimer) == 0) {
|
|
IgmpSendReport(ifp, mcastgrp);
|
|
}
|
|
mcastgrp++;
|
|
}
|
|
}
|
|
|