//============================================================================= // Copyright (c) 1997 Microsoft Corporation // File: packet.c // // Abstract: // This module defines SendPacket, JoinMulticastGroup, LeaveMulticastGroup, // and xsum. // // Author: K.S.Lokesh (lokeshs@) 11-1-97 // // Revision History: //============================================================================= #include "pchigmp.h" #pragma hdrstop UCHAR GetMaxRespCode( PIF_TABLE_ENTRY pite, DWORD val ); UCHAR GetQqic ( DWORD val ); //------------------------------------------------------------------------------ // _SendPacket // // Sends the packet. Called for ras server interfaces only for general queries. // Locks: assumes IfRead lock // for ver2 group specific query, send packet irrespective of pgie state. //------------------------------------------------------------------------------ DWORD SendPacket ( PIF_TABLE_ENTRY pite, PGI_ENTRY pgie, //null for gen query, and group_query_v2 DWORD PacketType, //MSG_GEN_QUERY, //MSG_GROUP_QUERY_V2,MSG_GROUP_QUERY_V3 // MSG_SOURCES_QUERY DWORD Group //destination McastGrp ) { DWORD Error = NO_ERROR; SOCKADDR_IN saSrcAddr, saDstnAddr; BYTE *SendBufPtr; DWORD SendBufLen, IpHdrLen=0, NumSources, Count; IGMP_HEADER UNALIGNED *IgmpHeader; INT iLength; BOOL bHdrIncl = IS_RAS_SERVER_IF(pite->IfType); UCHAR RouterAlert[4] = {148, 4, 0, 0}; //MSG_SOURCES_QUERY PIGMP_HEADER_V3_EXT pSourcesQuery; LONGLONG llCurTime; DWORD Version; Trace0(ENTER1, "Entering _SendPacket()"); if (PacketType==MSG_GEN_QUERY) Version = GET_IF_VERSION(pite); else if (PacketType==MSG_GROUP_QUERY_V2) Version =2; else Version = pgie->Version; // // make sure that the pgie->version has not meanwhile changed // if ( ((PacketType==MSG_SOURCES_QUERY)||(PacketType==MSG_GROUP_QUERY_V3)) && pgie->Version!=3 ) { return NO_ERROR; } if ( (PacketType==MSG_GROUP_QUERY_V2) && pgie!=NULL && pgie->Version!=2) return NO_ERROR; //source query and list is empty if (PacketType==MSG_SOURCES_QUERY && IsListEmpty(&pgie->V3SourcesQueryList)) return NO_ERROR; SendBufLen = sizeof(IGMP_HEADER) + ((Version==3)?sizeof(IGMP_HEADER_V3_EXT):0); IpHdrLen = (bHdrIncl) ? sizeof(IP_HEADER) + sizeof(RouterAlert) : 0; if (PacketType==MSG_SOURCES_QUERY) { SendBufPtr = (PBYTE) IGMP_ALLOC(SendBufLen + IpHdrLen + sizeof(IPADDR)*pgie->V3SourcesQueryCount, 0x4000,pite->IfIndex); } else { SendBufPtr = (PBYTE) IGMP_ALLOC(SendBufLen+IpHdrLen, 0x8000,pite->IfIndex); } if (!SendBufPtr) { return ERROR_NOT_ENOUGH_MEMORY; } // // set destination multicast addr (general queries sent to ALL_HOSTS_MCAST // and group specific queries sent to the GroupAddr // ZeroMemory((PVOID)&saDstnAddr, sizeof(saDstnAddr)); saDstnAddr.sin_family = AF_INET; saDstnAddr.sin_port = 0; saDstnAddr.sin_addr.s_addr = (PacketType==MSG_GEN_QUERY) ? ALL_HOSTS_MCAST : Group; // // set igmp header // IgmpHeader = (IGMP_HEADER UNALIGNED*) (bHdrIncl ? &SendBufPtr[IpHdrLen] : SendBufPtr); IgmpHeader->Vertype = IGMP_QUERY; // have to divide GenQueryInterval by 100, as it should be in units of 100ms if (PacketType==MSG_GEN_QUERY) { // for gen query, set response time if the IF-Ver2 else set it to 0 IgmpHeader->ResponseTime = GetMaxRespCode(pite, pite->Config.GenQueryMaxResponseTime/100); } else { IgmpHeader->ResponseTime = GetMaxRespCode(pite, pite->Config.LastMemQueryInterval/100); } if (Version==3) { llCurTime = GetCurrentIgmpTime(); pSourcesQuery = (PIGMP_HEADER_V3_EXT) ((PBYTE)IgmpHeader+sizeof(IGMP_HEADER)); pSourcesQuery->Reserved = 0; pSourcesQuery->QRV = (UCHAR)pite->Config.RobustnessVariable; pSourcesQuery->QQIC = GetQqic(pite->Config.GenQueryInterval); pSourcesQuery->NumSources = 0; if (PacketType==MSG_GROUP_QUERY_V3) { pSourcesQuery->SFlag = (QueryRemainingTime(&pgie->GroupMembershipTimer, llCurTime) <=pite->Config.LastMemQueryInterval) ? 0 : 1; } // first send gen query and 1st sources query packet without suppress bit else { pSourcesQuery->SFlag = 0; } } // twice in loop for sources query Count = (PacketType==MSG_SOURCES_QUERY)? 0 : 1; for ( ; Count<2; Count++) { IgmpHeader->Xsum = 0; if (PacketType==MSG_SOURCES_QUERY) { PLIST_ENTRY pHead, ple; if (Count==1 && (PacketType==MSG_SOURCES_QUERY)) pSourcesQuery->SFlag = 1; pHead = &pgie->V3SourcesQueryList; for (NumSources=0,ple=pHead->Flink; ple!=pHead; ) { PGI_SOURCE_ENTRY pSourceEntry = CONTAINING_RECORD(ple, GI_SOURCE_ENTRY, V3SourcesQueryList); ple = ple->Flink; if ( (pSourcesQuery->SFlag &&(QueryRemainingTime(&pSourceEntry->SourceExpTimer, llCurTime) >GET_IF_CONFIG_FOR_SOURCE(pSourceEntry).LastMemQueryInterval)) || (!pSourcesQuery->SFlag &&(QueryRemainingTime(&pSourceEntry->SourceExpTimer, llCurTime) <=GET_IF_CONFIG_FOR_SOURCE(pSourceEntry).LastMemQueryInterval)) ) { if (NumSources==0) { Trace4(SEND, "Sent Sources Query on IfIndex(%0x) IpAddr(%d.%d.%d.%d) " "for Group(%d.%d.%d.%d) SFlag:%d", pite->IfIndex, PRINT_IPADDR(pite->IpAddr), PRINT_IPADDR(Group),pSourcesQuery->SFlag ); } pSourcesQuery->Sources[NumSources++] = pSourceEntry->IpAddr; Trace1(SEND, " Source:%d.%d.%d.%d", PRINT_IPADDR(pSourceEntry->IpAddr)); if (--pSourceEntry->V3SourcesQueryLeft==0) { RemoveEntryList(&pSourceEntry->V3SourcesQueryList); pSourceEntry->bInV3SourcesQueryList = FALSE; pgie->V3SourcesQueryCount--; } } } if (NumSources==0) continue; pSourcesQuery->NumSources = htons((USHORT)NumSources); SendBufLen += sizeof(IPADDR)*NumSources; } IgmpHeader->Group = (PacketType==MSG_GEN_QUERY) ? 0 : Group; IgmpHeader->Xsum = ~xsum((PVOID)IgmpHeader, SendBufLen); // // send the packet // if (!bHdrIncl) { Error = NO_ERROR; iLength = sendto(pite->SocketEntry.Socket, SendBufPtr, SendBufLen+IpHdrLen, 0, (PSOCKADDR) &saDstnAddr, sizeof(SOCKADDR_IN) ); // // error messages and statistics updates // if ( (iLength==SOCKET_ERROR) || ((DWORD)iLengthIfIndex, PRINT_IPADDR(pite->IpAddr)); IgmpAssertOnError(FALSE); Logwarn2(SENDTO_FAILED, "%I%I", pite->IpAddr, saDstnAddr.sin_addr, Error); } } // // for RAS server interface, use HDRINCL option. Build up the ip header and // send the packet to all RAS clients. // else { PIP_HEADER IpHdr; DWORD IpHdrLen = sizeof(IP_HEADER) + sizeof(RouterAlert); // // igmp follows the ip header containing the routerAlert option // IpHdr = (IP_HEADER *)((PBYTE)SendBufPtr); #define wordsof(x) (((x)+3)/4) /* Number of 32-bit words */ // Set IP version (4) and IP header length IpHdr->Hl = (UCHAR) (IPVERSION * 16 + wordsof(sizeof(IP_HEADER) + sizeof(RouterAlert))); // No TOS bits are set IpHdr->Tos = 0; // Total IP length is set in host order IpHdr->Len = (USHORT)(IpHdrLen+sizeof(IGMP_HEADER)); // Stack will fill in the ID IpHdr->Id = 0; // No offset IpHdr->Offset = 0; // Set the TTL to 1 IpHdr->Ttl = 1; // Protocol is IGMP IpHdr->Protocol = IPPROTO_IGMP; // Checksum is set by stack IpHdr->Xsum = 0; // Set source and destination address IpHdr->Src.s_addr = pite->IpAddr; IpHdr->Dstn.s_addr = ALL_HOSTS_MCAST; // set the router alert option, but still set it memcpy( (void *)((UCHAR *)IpHdr + sizeof(IP_HEADER)), (void *)RouterAlert, sizeof(RouterAlert)); // send packet to all RAS clients, with the destination address in sendto // set to the unicast addr of the client { PLIST_ENTRY pHead, ple; PRAS_TABLE_ENTRY prte; pHead = &pite->pRasTable->ListByAddr; Error = NO_ERROR; for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) { // get address of ras client prte = CONTAINING_RECORD(ple, RAS_TABLE_ENTRY, LinkByAddr); saDstnAddr.sin_addr.s_addr = prte->NHAddr; // send the packet to the client iLength = sendto(pite->SocketEntry.Socket, SendBufPtr, SendBufLen+IpHdrLen, 0, (PSOCKADDR) &saDstnAddr, sizeof(SOCKADDR_IN) ); // print error if sendto failed if ((iLength==SOCKET_ERROR) || ((DWORD)iLengthIfIndex, PRINT_IPADDR(pite->IpAddr) ); IgmpAssertOnError(FALSE); Logwarn2(SENDTO_FAILED, "%I%I", pite->IpAddr, saDstnAddr.sin_addr.s_addr, Error); } else { Trace1(SEND, "sent general query to ras client: %d.%d.%d.%d", PRINT_IPADDR(prte->NHAddr)); } }//for loop:sent packet to a RAS client }//sent packets to all ras clients }//created IPHeader and sent packets to all ras clients }; //for loop. send both packets if (PacketType==MSG_SOURCES_QUERY) { pgie->bV3SourcesQueryNow = FALSE; // set timer for next sources query if (pgie->V3SourcesQueryCount>0) { ACQUIRE_TIMER_LOCK("_SendPacket"); #if DEBUG_TIMER_TIMERID SET_TIMER_ID(&pgie->V3SourcesQueryTimer,1001, pite->IfIndex, Group, 0); #endif if (IS_TIMER_ACTIVE(pgie->V3SourcesQueryTimer)) { UpdateLocalTimer(&pgie->V3SourcesQueryTimer, (pite->Config.LastMemQueryInterval/pite->Config.LastMemQueryCount), DBG_Y); } else { InsertTimer(&pgie->V3SourcesQueryTimer, (pite->Config.LastMemQueryInterval/pite->Config.LastMemQueryCount), TRUE, DBG_Y); } RELEASE_TIMER_LOCK("_SendPacket"); } } // // if successful, print trace and update statistics // if (Error==NO_ERROR) { if (PacketType==MSG_GEN_QUERY) { Trace2(SEND, "Sent GenQuery on IfIndex(%0x) IpAddr(%d.%d.%d.%d)", pite->IfIndex, PRINT_IPADDR(pite->IpAddr)); InterlockedIncrement(&pite->Info.GenQueriesSent); } else if (PacketType==MSG_GROUP_QUERY_V2 || PacketType==MSG_GROUP_QUERY_V3) { Trace3(SEND, "Sent Group Query on IfIndex(%0x) IpAddr(%d.%d.%d.%d) " "for Group(%d.%d.%d.%d)", pite->IfIndex, PRINT_IPADDR(pite->IpAddr), PRINT_IPADDR(Group)); InterlockedIncrement(&pite->Info.GroupQueriesSent); } } IGMP_FREE_NOT_NULL(SendBufPtr); Trace0(LEAVE1, "Leaving _SendPacket()"); return Error; } //end _SendPacket DWORD BlockSource ( SOCKET Sock, DWORD dwGroup, DWORD IfIndex, IPADDR IpAddr, IPADDR Source ) { struct ip_mreq imOption; DWORD Error = NO_ERROR; DWORD dwRetval; struct ip_mreq_source imr; imr.imr_multiaddr.s_addr = dwGroup; imr.imr_sourceaddr.s_addr = Source; imr.imr_interface.s_addr = IpAddr; dwRetval = setsockopt(Sock, IPPROTO_IP, IP_BLOCK_SOURCE, (PCHAR)&imr, sizeof(imr)); if (dwRetval == SOCKET_ERROR) { Error = WSAGetLastError(); Trace5(ERR, "ERROR %d BLOCKING MULTICAST GROUP(%d.%d.%d.%d) " "Source:%d.%d.%d.%d ON INTERFACE (%d) %d.%d.%d.%d", Error, PRINT_IPADDR(dwGroup), PRINT_IPADDR(Source), IfIndex, PRINT_IPADDR(IpAddr)); IgmpAssertOnError(FALSE); } Trace2(MGM, "Blocking MCAST: (%d.%d.%d.%d) SOURCE (%d.%d.%d.%d)", PRINT_IPADDR(dwGroup), PRINT_IPADDR(Source)); return Error; } DWORD UnBlockSource ( SOCKET Sock, DWORD dwGroup, DWORD IfIndex, IPADDR IpAddr, IPADDR Source ) { struct ip_mreq imOption; DWORD Error = NO_ERROR; DWORD dwRetval; struct ip_mreq_source imr; imr.imr_multiaddr.s_addr = dwGroup; imr.imr_sourceaddr.s_addr = Source; imr.imr_interface.s_addr = IpAddr; dwRetval = setsockopt(Sock, IPPROTO_IP, IP_UNBLOCK_SOURCE, (PCHAR)&imr, sizeof(imr)); if (dwRetval == SOCKET_ERROR) { Error = WSAGetLastError(); Trace5(ERR, "ERROR %d UN-BLOCKING MULTICAST GROUP(%d.%d.%d.%d) " "Source:%d.%d.%d.%d ON INTERFACE (%d) %d.%d.%d.%d", Error, PRINT_IPADDR(dwGroup), PRINT_IPADDR(Source), IfIndex, PRINT_IPADDR(IpAddr)); IgmpAssertOnError(FALSE); } Trace2(MGM, "UnBlocking MCAST: (%d.%d.%d.%d) SOURCE (%d.%d.%d.%d)", PRINT_IPADDR(dwGroup), PRINT_IPADDR(Source)); return Error; } //------------------------------------------------------------------------------ // _JoinMulticastGroup //------------------------------------------------------------------------------ DWORD JoinMulticastGroup ( SOCKET Sock, DWORD dwGroup, DWORD IfIndex, IPADDR IpAddr, IPADDR Source ) { struct ip_mreq imOption; DWORD Error = NO_ERROR; DWORD dwRetval; if (Source==0) { imOption.imr_multiaddr.s_addr = dwGroup; imOption.imr_interface.s_addr = IpAddr; dwRetval = setsockopt(Sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (PBYTE)&imOption, sizeof(imOption)); } else { struct ip_mreq_source imr; imr.imr_multiaddr.s_addr = dwGroup; imr.imr_sourceaddr.s_addr = Source; imr.imr_interface.s_addr = IpAddr; dwRetval = setsockopt(Sock, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (PCHAR)&imr, sizeof(imr)); } if (dwRetval == SOCKET_ERROR) { Error = WSAGetLastError(); Trace5(ERR, "ERROR %d JOINING MULTICAST GROUP(%d.%d.%d.%d) " "Source:%d.%d.%d.%d ON INTERFACE (%d) %d.%d.%d.%d", Error, PRINT_IPADDR(dwGroup), PRINT_IPADDR(Source), IfIndex, PRINT_IPADDR(IpAddr)); IgmpAssertOnError(FALSE); Logerr2(JOIN_GROUP_FAILED, "%I%I", dwGroup, IpAddr, Error); } Trace2(MGM, "Joining MCAST: (%d.%d.%d.%d) SOURCE (%d.%d.%d.%d)", PRINT_IPADDR(dwGroup), PRINT_IPADDR(Source)); return Error; } //------------------------------------------------------------------------------ // _LeaveMulticastGroup //------------------------------------------------------------------------------ DWORD LeaveMulticastGroup ( SOCKET Sock, DWORD dwGroup, DWORD IfIndex, IPADDR IpAddr, IPADDR Source ) { struct ip_mreq imOption; DWORD Error = NO_ERROR; DWORD dwRetval; if (Source==0) { imOption.imr_multiaddr.s_addr = dwGroup; imOption.imr_interface.s_addr = IpAddr; dwRetval = setsockopt(Sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (PBYTE)&imOption, sizeof(imOption)); } else { struct ip_mreq_source imr; imr.imr_multiaddr.s_addr = dwGroup; imr.imr_sourceaddr.s_addr = Source; imr.imr_interface.s_addr = IpAddr; dwRetval = setsockopt(Sock, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, (PCHAR)&imr, sizeof(imr)); } if (dwRetval == SOCKET_ERROR) { Error = WSAGetLastError(); Trace5(ERR, "error %d leaving multicast group(%d.%d.%d.%d) " "Source:%d.%d.%d.%d on interface (%d) %d.%d.%d.%d", Error, PRINT_IPADDR(dwGroup), PRINT_IPADDR(Source), IfIndex, PRINT_IPADDR(IpAddr)); IgmpAssertOnError(FALSE); } Trace2(MGM, "Leaving MCAST: (%d.%d.%d.%d) SOURCE (%d.%d.%d.%d)", PRINT_IPADDR(dwGroup), PRINT_IPADDR(Source)); return Error; } //------------------------------------------------------------------------------ // _McastSetTtl // set the ttl value for multicast data. the default ttl for multicast is 1. //------------------------------------------------------------------------------ DWORD McastSetTtl( SOCKET sock, UCHAR ttl ) { INT dwTtl = ttl; DWORD Error=NO_ERROR; Error = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&dwTtl, sizeof(dwTtl)); if (Error != 0) { Error = WSAGetLastError(); Trace1(ERR, "error:%d: unable to set ttl value", Error); IgmpAssertOnError(FALSE); return Error; } return Error; } UCHAR GetMaxRespCode( PIF_TABLE_ENTRY pite, DWORD val ) { if (IS_IF_VER1(pite)) return 0; if (IS_IF_VER2(pite)) return val>255 ? 0 : (UCHAR)val; //version 3 if (val < 128) return (UCHAR)val; { DWORD n,mant, exp; n = val; exp = mant = 0; while (n) { exp++; n = n>>1; } exp=exp-2-3-3; mant = 15; if ( ((mant+16)<<(exp+3)) < val) exp++; mant = (val >> (exp+3)) - 15; IgmpAssert(mant<16 && exp <8); //deldel Trace4(KSL, "\n=======exp: LMQI:%d:%d exp:%d mant:%d\n", val, (mant+16)<<(exp+3), exp, mant); //deldel return (UCHAR)(0x80 + (exp<<4) + mant); } } UCHAR GetQqic ( DWORD val ) { val = val/1000; if ((val) > 31744) return 0; if (val<128) return (UCHAR)val; { DWORD n,mant, exp; n = val; exp = mant = 0; while (n) { exp++; n = n>>1; } exp=exp-2-3-3; mant = 15; if ( ((mant+16)<<(exp+3)) < val) exp++; mant = (val >> (exp+3)) - 15; IgmpAssert(mant<16 && exp <8); //deldel Trace4(KSL, "\n=======exp: QQic:%d:%d exp:%d mant:%d\n", val, (mant+16)<<(exp+3), exp, mant); //deldel return (UCHAR)(0x80 + (exp<<4) + mant); } } //------------------------------------------------------------------------------ // xsum: copied from ipxmit.c //------------------------------------------------------------------------------ USHORT xsum(PVOID Buffer, INT Size) { USHORT UNALIGNED *Buffer1 = (USHORT UNALIGNED *)Buffer; // Buffer expressed as shorts. ULONG csum = 0; while (Size > 1) { csum += *Buffer1++; Size -= sizeof(USHORT); } if (Size) csum += *(UCHAR *)Buffer1; // For odd buffers, add in last byte. csum = (csum >> 16) + (csum & 0xffff); csum += (csum >> 16); return (USHORT)csum; }