/*++ 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++; } }