/*++ Copyright (c) 1995 Microsoft Corporation Module Name: Abstract: Revision History: --*/ #include "allinc.h" PCHAR g_pszMsg[] = { "Packet Received", "MFE Deleted", "Wrong I/f Upcall" }; DWORD QueueAsyncFunction( WORKERFUNCTION pfnFunction, PVOID pvContext, BOOL bAlertable ); DWORD ValidateMfe( IN OUT PIPMCAST_MFE pMfe ); VOID HandleRcvPkt( PVOID pvContext ) /*++ Routine Description: Locks: Arguments: Return Value: NO_ERROR --*/ { PIP_HEADER pHdr; DWORD dwResult, dwOldIf; ULONG ulIndex; PIPMCAST_PKT_MSG pPktInfo; PIPMCAST_NOTIFICATION pMsg; ulIndex = PtrToUlong(pvContext); pMsg = &(g_rginMcastMsg[ulIndex].msg); pPktInfo = &(pMsg->ipmPkt); pHdr = (PIP_HEADER)(pPktInfo->rgbyData); Trace3(MCAST, "HandleRcvPkt: Rcvd pkt from %d.%d.%d.%d to %d.%d.%d.%d on %d", PRINT_IPADDR(pHdr->dwSrc), PRINT_IPADDR(pHdr->dwDest), pPktInfo->dwInIfIndex); dwResult = g_pfnMgmNewPacket(pHdr->dwSrc, pHdr->dwDest, pPktInfo->dwInIfIndex, pPktInfo->dwInNextHopAddress, pPktInfo->cbyDataLen, pPktInfo->rgbyData); if(dwResult isnot NO_ERROR) { Trace1(MCAST, "HandleRcvPkt: MGM returned error %d\n", dwResult); } PostNotificationForMcastEvents(&(g_rginMcastMsg[ulIndex]), g_hMcastEvents[ulIndex]); ExitRouterApi(); } VOID HandleDeleteMfe( PVOID pvContext ) /*++ Routine Description: Locks: Arguments: Return Value: NO_ERROR --*/ { DWORD dwResult; ULONG ulIndex, i; PIPMCAST_MFE_MSG pMfeInfo; PIPMCAST_NOTIFICATION pMsg; ulIndex = PtrToUlong(pvContext); pMsg = &(g_rginMcastMsg[ulIndex].msg); pMfeInfo = &(pMsg->immMfe); Trace1(MCAST, "HandleDeleteMfe: Kernel deleted %d MFEs\n", pMfeInfo->ulNumMfes); for(i = 0; i < pMfeInfo->ulNumMfes; i++) { Trace3(MCAST, "HandleDeleteMfe: Group %d.%d.%d.%d Source %d.%d.%d.%d/%d.%d.%d.%d\n", PRINT_IPADDR(pMsg->immMfe.idmMfe[i].dwGroup), PRINT_IPADDR(pMsg->immMfe.idmMfe[i].dwSource), PRINT_IPADDR(pMsg->immMfe.idmMfe[i].dwSrcMask)); } dwResult = g_pfnMgmMfeDeleted(pMfeInfo->ulNumMfes, pMfeInfo->idmMfe); if(dwResult isnot NO_ERROR) { Trace1(MCAST, "HandleDeleteMfe: MGM returned error %d\n", dwResult); } PostNotificationForMcastEvents(&(g_rginMcastMsg[ulIndex]), g_hMcastEvents[ulIndex]); ExitRouterApi(); } VOID HandleWrongIfUpcall( PVOID pvContext ) /*++ Routine Description: Locks: Arguments: Return Value: NO_ERROR --*/ { PIP_HEADER pHdr; DWORD dwResult, dwOldIf; ULONG ulIndex; PIPMCAST_PKT_MSG pPktInfo; PIPMCAST_NOTIFICATION pMsg; ulIndex = PtrToUlong(pvContext); pMsg = &(g_rginMcastMsg[ulIndex].msg); pPktInfo = &(pMsg->ipmPkt); pHdr = (PIP_HEADER)(pPktInfo->rgbyData); Trace3(MCAST, "HandleWrongIfUpcall: Pkt from %d.%d.%d.%d to %d.%d.%d.%d on %d is wrong", PRINT_IPADDR(pHdr->dwSrc), PRINT_IPADDR(pHdr->dwDest), pPktInfo->dwInIfIndex); dwResult = g_pfnMgmWrongIf(pHdr->dwSrc, pHdr->dwDest, pPktInfo->dwInIfIndex, pPktInfo->dwInNextHopAddress, pPktInfo->cbyDataLen, pPktInfo->rgbyData); if(dwResult isnot NO_ERROR) { Trace1(MCAST, "HandleWrongIfUpcall: MGM returned error %d\n", dwResult); } PostNotificationForMcastEvents(&(g_rginMcastMsg[ulIndex]), g_hMcastEvents[ulIndex]); ExitRouterApi(); } VOID HandleMcastNotification( DWORD dwIndex ) /*++ Routine Description: Locks: Arguments: Return Value: NO_ERROR --*/ { DWORD dwResult; ULONG i; PIPMCAST_NOTIFICATION pMsg; pMsg = &(g_rginMcastMsg[dwIndex].msg); // // read the notification // Trace1(MCAST, "HandleMcastNotification: Notification received for %s\n", g_pszMsg[pMsg->dwEvent]); switch(pMsg->dwEvent) { case IPMCAST_RCV_PKT_MSG: { QueueAsyncFunction(HandleRcvPkt, (PVOID)(ULONG_PTR)dwIndex, FALSE); break; } case IPMCAST_DELETE_MFE_MSG: { QueueAsyncFunction(HandleDeleteMfe, (PVOID)(ULONG_PTR)dwIndex, FALSE); break; } case IPMCAST_WRONG_IF_MSG: { QueueAsyncFunction(HandleWrongIfUpcall, (PVOID)(ULONG_PTR)dwIndex, FALSE); break; } default: { Trace1(MCAST, "HandleMcastNotification: Bad event code %d\n", pMsg->dwEvent); PostNotificationForMcastEvents(&(g_rginMcastMsg[dwIndex]), g_hMcastEvents[dwIndex]); break; } } } VOID PostNotificationForMcastEvents( PMCAST_OVERLAPPED pOverlapped, HANDLE hEvent ) /*++ Routine Description: Locks: Arguments: Return Value: NO_ERROR --*/ { NTSTATUS nsStatus; nsStatus = SendIoctlToMcastDevice(IOCTL_IPMCAST_POST_NOTIFICATION, hEvent, &pOverlapped->ioStatus, &pOverlapped->msg, sizeof(IPMCAST_NOTIFICATION), &pOverlapped->msg, sizeof(IPMCAST_NOTIFICATION)); if((nsStatus isnot STATUS_SUCCESS) and (nsStatus isnot STATUS_PENDING)) { Trace1(ERR, "PostNotificationForMcastEvents: Error %X", nsStatus); } } DWORD SendIoctlToMcastDevice( DWORD dwIoctl, HANDLE hEvent, PIO_STATUS_BLOCK pIoStatus, PVOID pvInBuffer, DWORD dwInBufLen, PVOID pvOutBuffer, DWORD dwOutBufLen ) { NTSTATUS ntStatus; ntStatus = NtDeviceIoControlFile(g_hMcastDevice, hEvent, NULL, NULL, pIoStatus, dwIoctl, pvInBuffer, dwInBufLen, pvOutBuffer, dwOutBufLen); return ntStatus; } DWORD SetMfe( PIPMCAST_MFE pMfe ) /*++ Routine Description: Locks: Arguments: Return Value: NO_ERROR --*/ { DWORD dwResult; IO_STATUS_BLOCK ioStatus; dwResult = ValidateMfe(pMfe); if(dwResult isnot NO_ERROR) { // // Something bad happened while validating the MFE // Trace1(ERR, "SetMfe: Error %d validating MFE", dwResult); return dwResult; } dwResult = SendIoctlToMcastDevice(IOCTL_IPMCAST_SET_MFE, NULL, &ioStatus, pMfe, SIZEOF_MFE(pMfe->ulNumOutIf), NULL, 0); if(dwResult isnot NO_ERROR) { Trace1(MCAST, "SetMfe: NtStatus %x while setting MFE", dwResult); } return dwResult; } DWORD GetMfe( PIPMCAST_MFE_STATS pMfeStats ) { DWORD dwResult; IO_STATUS_BLOCK ioStatus; dwResult = SendIoctlToMcastDevice(IOCTL_IPMCAST_GET_MFE, NULL, &ioStatus, pMfeStats, SIZEOF_MFE_STATS(pMfeStats->ulNumOutIf), pMfeStats, SIZEOF_MFE_STATS(pMfeStats->ulNumOutIf)); if(dwResult isnot NO_ERROR) { Trace1(MCAST, "GetMfe: NtStatus %x while getting MFE", dwResult); } return dwResult; } DWORD DeleteMfe( PIPMCAST_DELETE_MFE pDelMfe ) { DWORD dwResult; IO_STATUS_BLOCK ioStatus; dwResult = SendIoctlToMcastDevice(IOCTL_IPMCAST_DELETE_MFE, NULL, &ioStatus, pDelMfe, sizeof(IPMCAST_DELETE_MFE), NULL, 0); if(dwResult isnot NO_ERROR) { Trace1(MCAST, "DeleteMfe: NtStatus %x while deleting MFE", dwResult); } return dwResult; } DWORD ActivateMcastLimits( PICB picb ) { DWORD dwResult; IO_STATUS_BLOCK ioStatus; IPMCAST_IF_TTL iitTtl; DWORD dwTtl = picb->dwMcastTtl; // Set the TTL threshold iitTtl.dwIfIndex = picb->dwIfIndex; iitTtl.byTtl = LOBYTE(LOWORD(dwTtl)); dwResult = SendIoctlToMcastDevice(IOCTL_IPMCAST_SET_TTL, NULL, &ioStatus, &iitTtl, sizeof(IPMCAST_IF_TTL), NULL, 0); if(dwResult isnot NO_ERROR) { Trace2(ERR, "SetMcastTtl: NtStatus %x from SendIoctl when setting TTL for %S", dwResult, picb->pwszName); return ERROR_CAN_NOT_COMPLETE; } // // Set the rate limit for multicast traffic on an interface. // Currently, the kernel does not support rate limiting. // return NO_ERROR; } DWORD SetMcastLimits( PICB picb, DWORD dwTtl, DWORD dwRateLimit ) { if (dwTtl > 255) { Trace2(ERR, "SetMcastTtl: TTL for %S is %d which is invalid", picb->pwszName, dwTtl); return ERROR_INVALID_DATA; } picb->dwMcastTtl = dwTtl; // // Set the rate limit for multicast traffic on an interface. // Currently, the kernel does not support rate limiting, so // the only valid value is 0 (=none). // if (dwRateLimit != 0) { Trace2(ERR, "SetMcastRateLimit: RateLimit for %S is %d which is invalid", picb->pwszName, dwRateLimit); return ERROR_INVALID_DATA; } picb->dwMcastRateLimit = dwRateLimit; if ( picb->dwOperationalState is IF_OPER_STATUS_OPERATIONAL ) { return ActivateMcastLimits(picb); } return NO_ERROR; } DWORD SetMcastLimitInfo( PICB picb, PRTR_INFO_BLOCK_HEADER pInfoHdr ) /*++ Routine Description: Sets the TTL and rate limit info associated with an interface. Arguments: picb The ICB of the interface Called by: AddInterface() in iprtrmgr.c SetInterfaceInfo() in iprtrmgr.c Locks: BOUNDARY_TABLE for writing --*/ { DWORD dwResult = NO_ERROR, i, j; PRTR_TOC_ENTRY pToc; PMIB_MCAST_LIMIT_ROW pLimit; BOOL bFound; Trace1( MCAST, "ENTERED SetMcastLimitInfo for If %x", picb->dwIfIndex ); pToc = GetPointerToTocEntry(IP_MCAST_LIMIT_INFO, pInfoHdr); if (pToc is NULL) { // No TOC means no change Trace0( MCAST, "LEFT SetMcastLimitInfo" ); return NO_ERROR; } pLimit = (PMIB_MCAST_LIMIT_ROW)GetInfoFromTocEntry(pInfoHdr, pToc); if (pLimit is NULL) { Trace0( MCAST, "LEFT SetMcastLimitInfo" ); return NO_ERROR; } dwResult = SetMcastLimits( picb, pLimit->dwTtl, pLimit->dwRateLimit ); Trace0( MCAST, "LEFT SetMcastLimitInfo" ); return dwResult; } DWORD SetMcastOnIf( PICB picb, BOOL bActivate ) { DWORD dwResult; IO_STATUS_BLOCK ioStatus; IPMCAST_IF_STATE iisState; iisState.dwIfIndex = picb->dwIfIndex; iisState.byState = bActivate?1:0; dwResult = SendIoctlToMcastDevice(IOCTL_IPMCAST_SET_IF_STATE, NULL, &ioStatus, &iisState, sizeof(IPMCAST_IF_STATE), NULL, 0); if(dwResult isnot NO_ERROR) { Trace2(ERR, "SetMcastOnIf: NtStatus %x from SendIoctl for %S", dwResult, picb->pwszName); return ERROR_CAN_NOT_COMPLETE; } return NO_ERROR; } DWORD StartMulticast( VOID ) /*++ Routine Description: Locks: Arguments: Return Value: NO_ERROR --*/ { DWORD i, dwStart; NTSTATUS nStatus; IO_STATUS_BLOCK ioStatus; dwStart = 1; nStatus = SendIoctlToMcastDevice(IOCTL_IPMCAST_START_STOP, NULL, &ioStatus, &dwStart, sizeof(DWORD), NULL, 0); if(nStatus isnot STATUS_SUCCESS) { Trace1(MCAST, "StartMulticast: Error %x starting driver", nStatus); return ERROR_OPEN_FAILED; } for(i = 0; i < NUM_MCAST_IRPS; i++) { PostNotificationForMcastEvents(&(g_rginMcastMsg[i]), g_hMcastEvents[i]); } // Start up mrinfo and mtrace services StartMcMisc(); return NO_ERROR; } DWORD ValidateMfe( IN OUT PIPMCAST_MFE pMfe ) /*++ Routine Description: Locks: Arguments: Return Value: NO_ERROR --*/ { PADAPTER_INFO pBinding; ULONG i; ENTER_READER(BINDING_LIST); // // First find the interface index for incoming i/f // If there are no outgoing interfaces, then this is a NEGATIVE // MFE and the incoming interface index must be 0 (and need not // be mapped) // #if DBG if(pMfe->ulNumOutIf is 0) { IpRtAssert(pMfe->dwInIfIndex is 0); pMfe->dwInIfIndex = 0; } #endif for(i = 0; i < pMfe->ulNumOutIf; i++) { pBinding = GetInterfaceBinding(pMfe->rgioOutInfo[i].dwOutIfIndex); if(!pBinding) { Trace1(ERR, "ValidateMfe: Unable to find binding for outgoing i/f %d", pMfe->rgioOutInfo[i].dwOutIfIndex); EXIT_LOCK(BINDING_LIST); return ERROR_INVALID_INDEX; } if(pBinding->bBound) { // // valid index // pMfe->rgioOutInfo[i].dwOutIfIndex = pBinding->dwIfIndex; } else { // // Demand dial interface // pMfe->rgioOutInfo[i].dwOutIfIndex = INVALID_IF_INDEX; pMfe->rgioOutInfo[i].dwDialContext = pBinding->dwSeqNumber; } } EXIT_LOCK(BINDING_LIST); return NO_ERROR; } DWORD GetInterfaceMcastCounters( IN PICB picb, OUT PIP_MCAST_COUNTER_INFO pOutBuffer ) { DWORD dwAdapterId,dwResult; PPROTO_CB pcbOwner; IO_STATUS_BLOCK ioStatus; ULONG Request = picb->dwIfIndex; HANDLE hEvent; dwResult = NO_ERROR; hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); if(hEvent is NULL) { dwResult = GetLastError(); Trace1(ERR, "GetInterfaceMcastCounters: Error %d creating event", dwResult); return dwResult; } dwResult = NtDeviceIoControlFile(g_hIpDevice, hEvent, NULL, NULL, &ioStatus, IOCTL_IP_GET_MCAST_COUNTERS, &Request, sizeof(Request), pOutBuffer, sizeof(IP_MCAST_COUNTER_INFO)); if(dwResult is STATUS_PENDING) { Trace0(ERR, "GetInterfaceMcastCounters: Pending from ioctl"); dwResult = WaitForSingleObject(hEvent, INFINITE); if(dwResult isnot WAIT_OBJECT_0) // 0 { Trace1(ERR, "GetInterfaceMcastCounters: Error %d from wait", dwResult); dwResult = GetLastError(); } else { dwResult = STATUS_SUCCESS; } } return dwResult; } DWORD GetInterfaceMcastStatistics( IN PICB picb, OUT PMIB_IPMCAST_IF_ENTRY pOutBuffer ) { DWORD dwAdapterId,dwResult; PPROTO_CB pcbOwner; IO_STATUS_BLOCK ioStatus; IP_MCAST_COUNTER_INFO ifStats; dwResult = NO_ERROR; TraceEnter("GetInterfaceMcastStatistics"); pOutBuffer->dwIfIndex = picb->dwIfIndex; pOutBuffer->dwTtl = picb->dwMcastTtl; pOutBuffer->dwRateLimit = 0; // XXX change when we have rate limiting dwResult = GetInterfaceMcastCounters(picb, &ifStats); if (dwResult isnot STATUS_SUCCESS) { return dwResult; } pOutBuffer->ulOutMcastOctets = (ULONG)ifStats.OutMcastOctets; pOutBuffer->ulInMcastOctets = (ULONG)ifStats.InMcastOctets; pOutBuffer->dwProtocol = 2; // "local" (static only) is default dwResult = MulticastOwner(picb, &pcbOwner, NULL); if (dwResult == NO_ERROR && pcbOwner != NULL) { switch(pcbOwner->dwProtocolId) { #ifdef MS_IP_DVMRP case MS_IP_DVMRP: pOutBuffer->dwProtocol = 4; break; #endif #ifdef MS_IP_MOSPF case MS_IP_MOSPF: pOutBuffer->dwProtocol = 5; break; #endif #ifdef MS_IP_CBT case MS_IP_CBT : pOutBuffer->dwProtocol = 7; break; #endif #ifdef MS_IP_PIMSM case MS_IP_PIMSM: pOutBuffer->dwProtocol = 8; break; #endif #ifdef MS_IP_PIMDM case MS_IP_PIMDM: pOutBuffer->dwProtocol = 9; break; #endif case MS_IP_IGMP : pOutBuffer->dwProtocol = 10; break; } } TraceLeave("GetInterfaceMcastStatistics"); return dwResult; } DWORD SetInterfaceMcastStatistics( IN PICB picb, IN PMIB_IPMCAST_IF_ENTRY lpInBuffer ) { DWORD dwResult = NO_ERROR; TraceEnter("SetInterfaceMcastStatistics"); dwResult = SetMcastLimits(picb, lpInBuffer->dwTtl, lpInBuffer->dwRateLimit); if(dwResult isnot NO_ERROR) { Trace2(ERR, "SetInterfaceStatistics: Error %d setting %S", dwResult, picb->pwszName); } TraceLeave("SetInterfaceMcastStatistics"); return dwResult; }