|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
tcpip\ip\mcastini.c
Abstract:
Initialization for IP Multicasting
Author:
Amritansh Raghav
Revision History:
AmritanR Created
Notes:
--*/
#include "precomp.h"
#if IPMCAST
#define __FILE_SIG__ INI_SIG
#include "ipmcast.h"
#include "ipmcstxt.h"
#include "mcastioc.h"
#include "mcastmfe.h"
//
// Storage for extern declarations
//
//#pragma data_seg("PAGE")
LIST_ENTRY g_lePendingNotification; LIST_ENTRY g_lePendingIrpQueue; GROUP_ENTRY g_rgGroupTable[GROUP_TABLE_SIZE];
NPAGED_LOOKASIDE_LIST g_llGroupBlocks; NPAGED_LOOKASIDE_LIST g_llSourceBlocks; NPAGED_LOOKASIDE_LIST g_llOifBlocks; NPAGED_LOOKASIDE_LIST g_llMsgBlocks;
PVOID g_pvCodeSectionHandle, g_pvDataSectionHandle;
KTIMER g_ktTimer; KDPC g_kdTimerDpc; DWORD g_ulNextHashIndex;
DWORD g_dwMcastState; DWORD g_dwNumThreads; LONG g_lNumOpens; KEVENT g_keStateEvent; FAST_MUTEX g_StartStopMutex; RT_LOCK g_rlStateLock;
//#pragma data_seg()
//
// Forward declarations of functions
//
BOOLEAN SetupExternalName( PUNICODE_STRING pusNtName, BOOLEAN bCreate );
NTSTATUS InitializeMcastData( VOID );
NTSTATUS InitializeIpMcast( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath, OUT PDEVICE_OBJECT * ppIpMcastDevice );
NTSTATUS IpMcastDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
NTSTATUS StartDriver( VOID );
NTSTATUS StopDriver( VOID );
VOID McastTimerRoutine( PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2 );
NTSTATUS OpenRegKeyEx( OUT PHANDLE phHandle, IN PUNICODE_STRING pusKeyName );
NTSTATUS GetRegDWORDValue( HANDLE KeyHandle, PWCHAR ValueName, PULONG ValueData );
BOOLEAN EnterDriverCode( IN DWORD dwIoCode );
VOID ExitDriverCode( IN DWORD dwIoCode );
//////////////////////////////////////////////////////////////////////////////
// //
// Routines //
// //
//////////////////////////////////////////////////////////////////////////////
//
// The code is only called on initialization
//
#pragma alloc_text(INIT, InitializeIpMcast)
NTSTATUS InitializeIpMcast( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath, OUT PDEVICE_OBJECT * ppIpMcastDevice )
/*++
Routine Description:
Reads the registry value for multicast forwarding. If enabled, creates the IPMcast device object. Does other MCast specific initialization
Locks:
Arguments:
pIpMcastDevice Pointer to created device
Return Value:
STATUS_SUCCESS or an error status
--*/
{ UNICODE_STRING usDeviceName, usParamString, usTempString; NTSTATUS nStatus; HANDLE hRegKey; DWORD dwVal; USHORT usRegLen; PWCHAR pwcBuffer;
dwVal = 0; RtInitializeDebug();
usRegLen = (USHORT) (RegistryPath->Length + (uint) (sizeof(WCHAR) * (wcslen(L"\\Parameters") + 2)));
//
// use a random tag
//
pwcBuffer = ExAllocatePoolWithTag(NonPagedPool, usRegLen, MSG_TAG);
if(pwcBuffer is NULL) {
return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(pwcBuffer, usRegLen);
usParamString.MaximumLength = usRegLen; usParamString.Buffer = pwcBuffer;
RtlCopyUnicodeString(&usParamString, RegistryPath);
RtlInitUnicodeString(&usTempString, L"\\Parameters");
RtlAppendUnicodeStringToString(&usParamString, &usTempString);
nStatus = OpenRegKeyEx(&hRegKey, &usParamString);
ExFreePool(pwcBuffer);
if(nStatus is STATUS_SUCCESS) { #if RT_TRACE_DEBUG
nStatus = GetRegDWORDValue(hRegKey, L"DebugLevel", &dwVal);
if(nStatus is STATUS_SUCCESS) { g_byDebugLevel = (BYTE) dwVal; } nStatus = GetRegDWORDValue(hRegKey, L"DebugComp", &dwVal);
if(nStatus is STATUS_SUCCESS) { g_fDebugComp = dwVal; } #endif
#if DBG
nStatus = GetRegDWORDValue(hRegKey, L"DebugBreak", &dwVal);
if((nStatus is STATUS_SUCCESS) and (dwVal is 1)) { DbgBreakPoint(); } #endif
ZwClose(hRegKey); }
TraceEnter(GLOBAL, "InitializeIpMcast");
//
// Read the value for multicast forwarding
//
//
// The g_dwMcastStart controls whether any forwarding actually happens
// It gets set to 1 one an NtCreateFile is done on the Multicast Device.
// It gets reset when the handle is closed
//
g_dwMcastState = MCAST_STOPPED; g_dwNumThreads = 0; g_lNumOpens = 0;
//
// Handles to code and data sections
//
g_pvCodeSectionHandle = NULL; g_pvDataSectionHandle = NULL;
//
// Used for the timer routine. Tells the DPC which index to start in in the
// group hash table
//
g_ulNextHashIndex = 0;
//
// Create the device
//
RtlInitUnicodeString(&usDeviceName, DD_IPMCAST_DEVICE_NAME);
nStatus = IoCreateDevice(DriverObject, 0, &usDeviceName, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, ppIpMcastDevice);
if(!NT_SUCCESS(nStatus)) { Trace(GLOBAL, ERROR, ("InitializeIpMcast: IP initialization failed: Unable to create device object %ws, status %lx.\n", DD_IPMCAST_DEVICE_NAME, nStatus));
TraceLeave(GLOBAL, "InitializeIpMcast");
return nStatus; }
//
// Create a symbolic link in Dos Space
//
if(!SetupExternalName(&usDeviceName, TRUE)) { Trace(GLOBAL, ERROR, ("InitializeIpMcast: Win32 device name could not be created\n"));
IoDeleteDevice(*ppIpMcastDevice);
TraceLeave(GLOBAL, "InitializeIpMcast");
return STATUS_UNSUCCESSFUL; }
RtInitializeSpinLock(&g_rlStateLock);
KeInitializeEvent(&(g_keStateEvent), SynchronizationEvent, FALSE);
ExInitializeFastMutex(&g_StartStopMutex);
return STATUS_SUCCESS; }
VOID DeinitializeIpMcast( IN PDEVICE_OBJECT DeviceObject ) { StopDriver();
IoDeleteDevice(DeviceObject); }
#pragma alloc_text(PAGE, SetupExternalName)
BOOLEAN SetupExternalName( PUNICODE_STRING pusNtName, BOOLEAN bCreate ) { UNICODE_STRING usSymbolicLinkName; WCHAR rgwcBuffer[100];
PAGED_CODE();
//
// Form the full symbolic link name we wish to create.
//
usSymbolicLinkName.Buffer = rgwcBuffer;
RtlInitUnicodeString(&usSymbolicLinkName, WIN32_IPMCAST_SYMBOLIC_LINK);
if(bCreate) { if(!NT_SUCCESS(IoCreateSymbolicLink(&usSymbolicLinkName, pusNtName))) { return FALSE; }
} else { IoDeleteSymbolicLink(&usSymbolicLinkName); }
return TRUE; }
#pragma alloc_text(PAGE, InitializeMcastData)
NTSTATUS InitializeMcastData( VOID ) { LARGE_INTEGER liDueTime; ULONG ulCnt;
if(g_pvCodeSectionHandle) { MmLockPagableSectionByHandle(g_pvCodeSectionHandle); }else { g_pvCodeSectionHandle = MmLockPagableCodeSection(McastTimerRoutine);
if(g_pvCodeSectionHandle is NULL) { #pragma warning(push)
#pragma warning(disable:4127) // conditional expression is constant
RtAssert(FALSE); #pragma warning(pop)
} }
for(ulCnt = 0; ulCnt < GROUP_TABLE_SIZE; ulCnt++) { InitializeListHead(&(g_rgGroupTable[ulCnt].leHashHead)); InitRwLock(&(g_rgGroupTable[ulCnt].rwlLock));
#if DBG
g_rgGroupTable[ulCnt].ulGroupCount = 0; g_rgGroupTable[ulCnt].ulCacheHits = 0; g_rgGroupTable[ulCnt].ulCacheMisses = 0; #endif
g_rgGroupTable[ulCnt].pGroup = NULL; }
InitializeListHead(&g_lePendingNotification); InitializeListHead(&g_lePendingIrpQueue);
ExInitializeNPagedLookasideList(&g_llGroupBlocks, NULL, NULL, 0, sizeof(GROUP), GROUP_TAG, GROUP_LOOKASIDE_DEPTH);
ExInitializeNPagedLookasideList(&g_llSourceBlocks, NULL, NULL, 0, sizeof(SOURCE), SOURCE_TAG, SOURCE_LOOKASIDE_DEPTH);
ExInitializeNPagedLookasideList(&g_llOifBlocks, NULL, NULL, 0, sizeof(OUT_IF), OIF_TAG, OIF_LOOKASIDE_DEPTH);
ExInitializeNPagedLookasideList(&g_llMsgBlocks, NULL, NULL, 0, sizeof(NOTIFICATION_MSG), MSG_TAG, MSG_LOOKASIDE_DEPTH);
KeInitializeDpc(&g_kdTimerDpc, McastTimerRoutine, NULL);
KeInitializeTimer(&g_ktTimer);
liDueTime = RtlEnlargedUnsignedMultiply(TIMER_IN_MILLISECS, SYS_UNITS_IN_ONE_MILLISEC);
liDueTime = RtlLargeIntegerNegate(liDueTime);
KeSetTimerEx(&g_ktTimer, liDueTime, 0, &g_kdTimerDpc);
return STATUS_SUCCESS; }
#pragma alloc_text(PAGE, IpMcastDispatch)
NTSTATUS IpMcastDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
/*++
Routine Description:
The functions which handles the IRPs sent to the driver The IOCTLS are further dispatched using a function table
THIS CODE IS PAGEABLE so it CAN NOT ACQUIRE ANY LOCKS
Locks:
Must be at passive
Arguments:
Return Value:
NO_ERROR
--*/
{ PIO_STACK_LOCATION irpStack; ULONG ulInputBuffLen; ULONG ulOutputBuffLen; ULONG ioControlCode; NTSTATUS ntStatus; BOOLEAN bEnter;
UNREFERENCED_PARAMETER(DeviceObject);
PAGED_CODE();
TraceEnter(GLOBAL, "IpMcastDispatch");
Irp->IoStatus.Information = 0;
//
// Get a pointer to the current location in the Irp. This is where
// the function codes and parameters are located.
//
irpStack = IoGetCurrentIrpStackLocation(Irp);
//
// Get the pointer to the input/output buffer and it's length
//
ulInputBuffLen = irpStack->Parameters.DeviceIoControl.InputBufferLength; ulOutputBuffLen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
switch (irpStack->MajorFunction) { case IRP_MJ_CREATE: { Trace(GLOBAL, TRACE, ("IpMcastDispatch: IRP_MJ_CREATE\n"));
//
// Make sure that the user is not attempting to sneak around the
// security checks. Make sure that FileObject->RelatedFileObject is
// NULL and that the FileName length is zero!
//
if((irpStack->FileObject->RelatedFileObject isnot NULL) or (irpStack->FileObject->FileName.Length isnot 0)) { ntStatus = STATUS_ACCESS_DENIED;
break; }
InterlockedIncrement(&g_lNumOpens);
ntStatus = STATUS_SUCCESS;
break; }
case IRP_MJ_CLOSE: { Trace(GLOBAL, TRACE, ("IpMcastDispatch: IRP_MJ_CLOSE\n"));
ntStatus = STATUS_SUCCESS;
break; }
case IRP_MJ_CLEANUP: { Trace(GLOBAL, TRACE, ("IpMcastDispatch: IRP_MJ_CLEANUP\n"));
if((InterlockedDecrement(&g_lNumOpens) is 0) and (g_dwMcastState isnot MCAST_STOPPED)) { StopDriver(); } ntStatus = STATUS_SUCCESS;
break; }
case IRP_MJ_DEVICE_CONTROL: { ULONG ulControl;
//
// The assumption is that IOCTL_IPMCAST_START_STOP will be
// serialized wrt to 2 calls, i.e we wont get a stop when a start
// is in progress and we will not get a start when a stop has been
// issued. No assumption is made about IOCTL_IPMCAST_START_STOP's
// serialization with other IRPS.
// So when we are in this code we assume that we wont get
// a close beneath us
//
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; ulControl = IoGetFunctionCodeFromCtlCode(ioControlCode);
bEnter = EnterDriverCode(ioControlCode);
if(!bEnter) { // keep devioctl testers happy
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "IpMcastDispatch: Driver is not started\n"));
ntStatus = STATUS_NO_SUCH_DEVICE;
break; }
switch (ioControlCode) { case IOCTL_IPMCAST_SET_MFE: { ntStatus = SetMfe(Irp, ulInputBuffLen, ulOutputBuffLen);
break; }
case IOCTL_IPMCAST_GET_MFE: { ntStatus = GetMfe(Irp, ulInputBuffLen, ulOutputBuffLen);
break; }
case IOCTL_IPMCAST_DELETE_MFE: { ntStatus = DeleteMfe(Irp, ulInputBuffLen, ulOutputBuffLen);
break; }
case IOCTL_IPMCAST_SET_TTL: { ntStatus = SetTtl(Irp, ulInputBuffLen, ulOutputBuffLen);
break; }
case IOCTL_IPMCAST_GET_TTL: { ntStatus = GetTtl(Irp, ulInputBuffLen, ulOutputBuffLen);
break; }
case IOCTL_IPMCAST_POST_NOTIFICATION: { ntStatus = ProcessNotification(Irp, ulInputBuffLen, ulOutputBuffLen);
break; }
case IOCTL_IPMCAST_START_STOP: { ntStatus = StartStopDriver(Irp, ulInputBuffLen, ulOutputBuffLen);
break; }
case IOCTL_IPMCAST_SET_IF_STATE: { ntStatus = SetIfState(Irp, ulInputBuffLen, ulOutputBuffLen);
break; }
default: { // Keep devioctl testers happy
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "IpMcastDispatch: unknown IRP_MJ_DEVICE_CONTROL - 0x%X which evaluates to a code of %d\n", ioControlCode, ulControl));
ntStatus = STATUS_INVALID_PARAMETER;
break; } }
ExitDriverCode(ioControlCode);
break; }
default: { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "IpMcastDispatch: unknown IRP_MJ_XX - %x\n", irpStack->MajorFunction));
ntStatus = STATUS_INVALID_PARAMETER;
break; } }
//
// Fill in status into IRP
//
//
// This bit commented out because we cant touch the irp since it
// may have been completed
//
// Trace(GLOBAL, INFO,
// ("IpMcastDispatch: Returning status %x info %d\n",
// ntStatus, Irp->IoStatus.Information));
if(ntStatus isnot STATUS_PENDING) { Irp->IoStatus.Status = ntStatus;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); }
TraceLeave(GLOBAL, "IpMcastDispatch");
return ntStatus; }
NTSTATUS StartDriver( VOID ) { KIRQL irql;
TraceEnter(GLOBAL, "StartDriver");
RtAcquireSpinLock(&g_rlStateLock, &irql);
if(g_dwMcastState is MCAST_STARTED) { RtReleaseSpinLock(&g_rlStateLock, irql);
return STATUS_SUCCESS; }
RtReleaseSpinLock(&g_rlStateLock, irql);
InitializeMcastData();
g_dwMcastState = MCAST_STARTED;
TraceLeave(GLOBAL, "StartDriver");
return STATUS_SUCCESS; }
//
// MUST BE PAGED IN
//
#pragma alloc_text(PAGEIPMc, StopDriver)
NTSTATUS StopDriver( VOID ) { DWORD i; KIRQL irql; PLIST_ENTRY pleGrpNode, pleSrcNode; PGROUP pGroup; PSOURCE pSource; BOOLEAN bWait; NTSTATUS nStatus;
TraceEnter(GLOBAL, "StopDriver");
//
// Set the state to STOPPING
//
RtAcquireSpinLock(&g_rlStateLock, &irql);
if(g_dwMcastState isnot MCAST_STARTED) { Trace(GLOBAL, ERROR, ("StopDriver: Called when state is %d\n", g_dwMcastState));
// RtAssert(FALSE);
RtReleaseSpinLock(&g_rlStateLock, irql);
TraceLeave(GLOBAL, "StopDriver");
return STATUS_SUCCESS; } else { g_dwMcastState = MCAST_STOPPED; }
RtReleaseSpinLock(&g_rlStateLock, irql);
//
// First of all, kill the timer
//
i = 0;
while(KeCancelTimer(&g_ktTimer) is FALSE) { LARGE_INTEGER liTimeOut;
//
// Hmm, timer was not in the system queue.
// Set the wait to 2, 4, 6... secs
//
liTimeOut.QuadPart = (LONGLONG) ((i + 1) * 2 * 1000 * 1000 * 10 * -1);
KeDelayExecutionThread(UserMode, FALSE, &liTimeOut);
i++; }
//
// Delete all the (S,G) entries
//
for(i = 0; i < GROUP_TABLE_SIZE; i++) { //
// Lock out the bucket
//
EnterWriter(&g_rgGroupTable[i].rwlLock, &irql);
pleGrpNode = g_rgGroupTable[i].leHashHead.Flink;
while(pleGrpNode isnot & (g_rgGroupTable[i].leHashHead)) { pGroup = CONTAINING_RECORD(pleGrpNode, GROUP, leHashLink);
pleGrpNode = pleGrpNode->Flink;
pleSrcNode = pGroup->leSrcHead.Flink;
while(pleSrcNode isnot & pGroup->leSrcHead) { pSource = CONTAINING_RECORD(pleSrcNode, SOURCE, leGroupLink);
pleSrcNode = pleSrcNode->Flink;
//
// Ref and lock the source, since we need to pass it that
// way to RemoveSource
//
ReferenceSource(pSource);
RtAcquireSpinLockAtDpcLevel(&(pSource->mlLock));
RemoveSource(pGroup->dwGroup, pSource->dwSource, pSource->dwSrcMask, pGroup, pSource); } }
ExitWriter(&g_rgGroupTable[i].rwlLock, irql); }
//
// Complete any pendying IRP
//
ClearPendingIrps();
//
// Free any pending messages
//
ClearPendingNotifications();
//
// Wait for everyone to leave the code
//
RtAcquireSpinLock(&g_rlStateLock, &irql);
//
// Need to wait if the number of threads isnot 0
//
bWait = (BOOLEAN) (g_dwNumThreads isnot 0);
RtReleaseSpinLock(&g_rlStateLock, irql);
if(bWait) { nStatus = KeWaitForSingleObject(&g_keStateEvent, Executive, KernelMode, FALSE, NULL);
RtAssert(nStatus is STATUS_SUCCESS); }
//
// Clear out the last of the data structures
//
ExDeleteNPagedLookasideList(&g_llGroupBlocks);
ExDeleteNPagedLookasideList(&g_llSourceBlocks);
ExDeleteNPagedLookasideList(&g_llOifBlocks);
ExDeleteNPagedLookasideList(&g_llMsgBlocks);
//
// Page out the code and data
//
MmUnlockPagableImageSection(g_pvCodeSectionHandle);
g_pvCodeSectionHandle = NULL;
//MmUnlockPagableImageSection(g_pvDataSectionHandle);
g_pvDataSectionHandle = NULL;
TraceLeave(GLOBAL, "StopDriver");
return STATUS_SUCCESS; }
BOOLEAN EnterDriverCode( DWORD dwIoCode ) { KIRQL irql; BOOLEAN bEnter;
RtAcquireSpinLock(&g_rlStateLock, &irql);
if((g_dwMcastState is MCAST_STARTED) or (dwIoCode is IOCTL_IPMCAST_START_STOP)) { g_dwNumThreads++;
bEnter = TRUE;
} else {
bEnter = FALSE; }
RtReleaseSpinLock(&g_rlStateLock, irql);
if(dwIoCode is IOCTL_IPMCAST_START_STOP) { ExAcquireFastMutex(&g_StartStopMutex); }
return bEnter; }
VOID ExitDriverCode( DWORD dwIoCode ) { KIRQL irql;
RtAcquireSpinLock(&g_rlStateLock, &irql);
g_dwNumThreads--;
if((g_dwMcastState is MCAST_STOPPED) and (g_dwNumThreads is 0)) {
KeSetEvent(&g_keStateEvent, 0, FALSE); }
RtReleaseSpinLock(&g_rlStateLock, irql);
if(dwIoCode is IOCTL_IPMCAST_START_STOP) { ExReleaseFastMutex(&g_StartStopMutex); }
}
#pragma alloc_text(PAGE, OpenRegKeyEx)
NTSTATUS OpenRegKeyEx( OUT PHANDLE phHandle, IN PUNICODE_STRING pusKeyName ) { NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes;
PAGED_CODE();
RtlZeroMemory(&ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
InitializeObjectAttributes(&ObjectAttributes, pusKeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
Status = ZwOpenKey(phHandle, KEY_READ, &ObjectAttributes);
return Status; }
#endif // IPMCAST
|