windows-nt/Source/XPSP1/NT/net/rras/ip/rtrmgr/mcastif.c
2020-09-26 16:20:57 +08:00

957 lines
20 KiB
C

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