/*++ Copyright (c) 1991 Microsoft Corporation Module Name: tcpip\ip\mcastioc.c Abstract: IOCTL handlers for IP Multicasting Author: Amritansh Raghav Revision History: AmritanR Created Notes: --*/ #include "precomp.h" #if IPMCAST #define __FILE_SIG__ IOCT_SIG #include "ipmcast.h" #include "ipmcstxt.h" #include "mcastioc.h" #include "mcastmfe.h" // // The table of IOCTL handlers. // //#pragma data_seg(PAGE) PFN_IOCTL_HNDLR g_rgpfnProcessIoctl[] = { SetMfe, GetMfe, DeleteMfe, SetTtl, GetTtl, ProcessNotification, StartStopDriver, SetIfState, }; //#pragma data_seg() NTSTATUS StartDriver( VOID ); NTSTATUS StopDriver( VOID ); #pragma alloc_text(PAGE, SetMfe) NTSTATUS SetMfe( IN PIRP pIrp, IN ULONG ulInLength, IN ULONG ulOutLength ) /*++ Routine Description: This is the handler for IOCTL_IPMCAST_SET_MFE. We do the normal buffer length checks. We try and find the MFE. If it exists, we ovewrite it with the given MFE, otherwise create a new MFE Locks: None Arguments: pIrp IRP ulInLength The length of the Input Buffer ulOutLength The length of the Output Buffer Return Value: STATUS_SUCCESS STATUS_BUFFER_TOO_SMALL STATUS_INFO_LENGTH_MISMATCH --*/ { PVOID pvIoBuffer; PIPMCAST_MFE pMfe; ULONG i; NTSTATUS nsStatus; TraceEnter(MFE, "SetMfe"); UNREFERENCED_PARAMETER(ulOutLength); i = 0; // // Get the user buffer // pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer; pMfe = (PIPMCAST_MFE)pvIoBuffer; // // Always clean out the information field // pIrp->IoStatus.Information = 0; // // If we have dont even have enough for the basic MFE // there is something bad going on // if(ulInLength < SIZEOF_BASIC_MFE) { Trace(MFE, ERROR, ("SetMfe: In Length %d is less than smallest MFE %d\n", ulInLength, SIZEOF_BASIC_MFE)); TraceLeave(MFE, "SetMfe"); return STATUS_BUFFER_TOO_SMALL; } // // The in length doesnt match with the MFE // if(ulInLength < SIZEOF_MFE(pMfe->ulNumOutIf)) { Trace(MFE, ERROR, ("SetMfe: In length %d is less than required (%d) for %d out i/f\n", ulInLength, SIZEOF_MFE(pMfe->ulNumOutIf), pMfe->ulNumOutIf)); TraceLeave(MFE, "SetMfe"); return STATUS_INFO_LENGTH_MISMATCH; } // // Ok, so we got a good MFE // Trace(MFE, TRACE, ("SetMfe: Group %d.%d.%d.%d Source %d.%d.%d.%d(%d.%d.%d.%d). In If %d Num Out %d\n", PRINT_IPADDR(pMfe->dwGroup), PRINT_IPADDR(pMfe->dwSource), PRINT_IPADDR(pMfe->dwSrcMask), pMfe->dwInIfIndex, pMfe->ulNumOutIf)); #if DBG for(i = 0; i < pMfe->ulNumOutIf; i++) { Trace(MFE, TRACE, ("Out If %d Dial Ctxt %d NextHop %d.%d.%d.%d\n", pMfe->rgioOutInfo[i].dwOutIfIndex, pMfe->rgioOutInfo[i].dwDialContext, PRINT_IPADDR(pMfe->rgioOutInfo[i].dwNextHopAddr))); } #endif // DBG nsStatus = CreateOrUpdateMfe(pMfe); TraceLeave(MFE, "SetMfe"); return nsStatus; } // // MUST BE PAGED IN // #pragma alloc_text(PAGEIPMc, DeleteMfe) NTSTATUS DeleteMfe( IN PIRP pIrp, IN ULONG ulInLength, IN ULONG ulOutLength ) /*++ Routine Description: Handler for IOCTL_IPMCAST_DELETE_MFE. We check the buffer lengths, and if valid call RemoveSource to remove the MFE Locks: Takes the lock for the hash bucket as writer Arguments: pIrp IRP ulInLength The length of the Input Buffer ulOutLength The length of the Output Buffer Return Value: STATUS_SUCCESS STATUS_BUFFER_TOO_SMALL --*/ { PVOID pvIoBuffer; KIRQL kiCurrIrql; ULONG ulIndex; PIPMCAST_DELETE_MFE pDelMfe; TraceEnter(MFE, "DeleteMfe"); UNREFERENCED_PARAMETER(ulOutLength); // // Get the user buffer // pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer; pDelMfe = (PIPMCAST_DELETE_MFE)pvIoBuffer; pIrp->IoStatus.Information = 0; // // Check the length // if(ulInLength < sizeof(IPMCAST_DELETE_MFE)) { Trace(MFE, ERROR, ("DeleteMfe: In Length %d is less required size %d\n", ulInLength, sizeof(IPMCAST_DELETE_MFE))); TraceLeave(MFE, "DeleteMfe"); return STATUS_BUFFER_TOO_SMALL; } Trace(MFE, TRACE, ("DeleteMfe: Group %d.%d.%d.%d Source %d.%d.%d.%d Mask %d.%d.%d.%d\n", PRINT_IPADDR(pDelMfe->dwGroup), PRINT_IPADDR(pDelMfe->dwSource), PRINT_IPADDR(pDelMfe->dwSrcMask))); // // Get exclusive access to the group bucket // ulIndex = GROUP_HASH(pDelMfe->dwGroup); EnterWriter(&g_rgGroupTable[ulIndex].rwlLock, &kiCurrIrql); RemoveSource(pDelMfe->dwGroup, pDelMfe->dwSource, pDelMfe->dwSrcMask, NULL, NULL); ExitWriter(&g_rgGroupTable[ulIndex].rwlLock, kiCurrIrql); TraceLeave(MFE, "DeleteMfe"); return STATUS_SUCCESS; } // // MUST BE PAGED IN // #pragma alloc_text(PAGEIPMc, GetMfe) NTSTATUS GetMfe( IN PIRP pIrp, IN ULONG ulInLength, IN ULONG ulOutLength ) /*++ Routine Description: Handler for IOCTL_IPMCAST_GET_MFE If the buffer is smaller than SIZEOF_BASIC_MFE_STATS, we return with an error If the buffer is larger than SIZEOF_BASIC_MFE_STATS but not large enough to hold the MFE, we fill in the basic MFE (which has the number of OIFs) and return with STATUS_SUCCESS. This allows the caller to determine what size buffer should be passed. If the buffer is large enough to hold all the info, we fill it out and return STATUS_SUCCESS. Locks: Takes the group bucket lock as reader Arguments: pIrp IRP ulInLength The length of the Input Buffer ulOutLength The length of the Output Buffer Return Value: STATUS_SUCCESS STATUS_BUFFER_TOO_SMALL STATUS_NOT_FOUND --*/ { PVOID pvIoBuffer; ULONG i; KIRQL kiCurrIrql; PGROUP pGroup; PSOURCE pSource; POUT_IF pOutIf; ULONG ulIndex; PIPMCAST_MFE_STATS pOutMfe; TraceEnter(MFE, "GetMfe"); UNREFERENCED_PARAMETER(ulInLength); // // Get the user buffer // pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer; pOutMfe = (PIPMCAST_MFE_STATS)pvIoBuffer; pIrp->IoStatus.Information = 0; // // Check the length // if(ulOutLength < SIZEOF_BASIC_MFE_STATS) { Trace(MFE, ERROR, ("GetMfe: Out Length %d is less than smallest MFE %d\n", ulOutLength, SIZEOF_BASIC_MFE_STATS)); TraceLeave(MFE, "GetMfe"); return STATUS_BUFFER_TOO_SMALL; } Trace(MFE, TRACE, ("GetMfe: Group %d.%d.%d.%d Source %d.%d.%d.%d Mask %d.%d.%d.%d\n", PRINT_IPADDR(pOutMfe->dwGroup), PRINT_IPADDR(pOutMfe->dwSource), PRINT_IPADDR(pOutMfe->dwSrcMask))); // // Get shared access to the group bucket // ulIndex = GROUP_HASH(pOutMfe->dwGroup); EnterReader(&g_rgGroupTable[ulIndex].rwlLock, &kiCurrIrql); // // Find the group and the source // pGroup = LookupGroup(pOutMfe->dwGroup); if(pGroup is NULL) { // // We may have deleted it before // Trace(MFE, INFO, ("GetMfe: Group not found")); ExitReader(&g_rgGroupTable[ulIndex].rwlLock, kiCurrIrql); TraceLeave(MFE, "GetMfe"); return STATUS_NOT_FOUND; } pSource = FindSourceGivenGroup(pGroup, pOutMfe->dwSource, pOutMfe->dwSrcMask); if(pSource is NULL) { // // Again, may have been deleted because of inactivity // Trace(MFE, INFO, ("GetMfe: Source not found")); ExitReader(&g_rgGroupTable[ulIndex].rwlLock, kiCurrIrql); TraceLeave(MFE, "GetMfe"); return STATUS_NOT_FOUND; } // // Check the length needed again // if(ulOutLength < SIZEOF_MFE_STATS(pSource->ulNumOutIf)) { // // Not big enough to hold all data. It is, however // large enough to hold the number of out interfaces // Let the user know that, so the next time around // she can supply a buffer with enough space // Trace(MFE, ERROR, ("SetMfe: Out len %d is less than required (%d) for %d out i/f\n", ulOutLength, SIZEOF_MFE_STATS(pSource->ulNumOutIf), pSource->ulNumOutIf)); pOutMfe->ulNumOutIf = pSource->ulNumOutIf; RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock)); DereferenceSource(pSource); ExitReader(&g_rgGroupTable[ulIndex].rwlLock, kiCurrIrql); pIrp->IoStatus.Information = SIZEOF_BASIC_MFE; TraceLeave(MFE, "GetMfe"); // // Just the way NT is. You have to return success for the // I/O subsystem to copy out the data. // return STATUS_SUCCESS; } // // Copy out the information and set the length in // the IRP // pOutMfe->ulNumOutIf = pSource->ulNumOutIf; pOutMfe->dwInIfIndex = pSource->dwInIfIndex; pOutMfe->ulInPkts = pSource->ulInPkts; pOutMfe->ulPktsDifferentIf = pSource->ulPktsDifferentIf; pOutMfe->ulInOctets = pSource->ulInOctets; pOutMfe->ulQueueOverflow = pSource->ulQueueOverflow; pOutMfe->ulUninitMfe = pSource->ulUninitMfe; pOutMfe->ulNegativeMfe = pSource->ulNegativeMfe; pOutMfe->ulInDiscards = pSource->ulInDiscards; pOutMfe->ulInHdrErrors = pSource->ulInHdrErrors; pOutMfe->ulTotalOutPackets = pSource->ulTotalOutPackets; for(pOutIf = pSource->pFirstOutIf, i = 0; pOutIf isnot NULL; pOutIf = pOutIf->pNextOutIf, i++) { pOutMfe->rgiosOutStats[i].dwOutIfIndex = pOutIf->dwIfIndex; pOutMfe->rgiosOutStats[i].dwNextHopAddr = pOutIf->dwNextHopAddr; pOutMfe->rgiosOutStats[i].dwDialContext = pOutIf->dwDialContext; pOutMfe->rgiosOutStats[i].ulTtlTooLow = pOutIf->ulTtlTooLow; pOutMfe->rgiosOutStats[i].ulFragNeeded = pOutIf->ulFragNeeded; pOutMfe->rgiosOutStats[i].ulOutPackets = pOutIf->ulOutPackets; pOutMfe->rgiosOutStats[i].ulOutDiscards = pOutIf->ulOutDiscards; } pIrp->IoStatus.Information = SIZEOF_MFE_STATS(pSource->ulNumOutIf); RtReleaseSpinLockFromDpcLevel(&(pSource->mlLock)); DereferenceSource(pSource); ExitReader(&g_rgGroupTable[ulIndex].rwlLock, kiCurrIrql); return STATUS_SUCCESS; } // // MUST BE PAGED IN // #pragma alloc_text(PAGEIPMc, SetTtl) NTSTATUS SetTtl( IN PIRP pIrp, IN ULONG ulInLength, IN ULONG ulOutLength ) /*++ Routine Description: The handler for IOCTL_IPMCAST_SET_TTL We find the IP Interface referred to by the IOCTL and if found, set the if_mcastttl field. No checks are made on the TTL value, so the caller must ensure that it is between 1 and 255 Locks: None currently, but when IP puts locks around the IFList, we will need to take that lock Arguments: pIrp IRP ulInLength The length of the Input Buffer ulOutLength The length of the Output Buffer Return Value: STATUS_SUCCESS STATUS_BUFFER_TOO_SMALL STATUS_NOT_FOUND --*/ { PVOID pvIoBuffer; Interface *pIpIf; BOOLEAN bFound; PIPMCAST_IF_TTL pTtl; CTELockHandle Handle; UNREFERENCED_PARAMETER(ulOutLength); // // Get the user buffer // pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer; pTtl = (PIPMCAST_IF_TTL)pvIoBuffer; pIrp->IoStatus.Information = 0; // // Check the length // if(ulInLength < sizeof(IPMCAST_IF_TTL)) { Trace(IF, ERROR, ("SetTtl: In Length %d is less required size %d\n", ulInLength, sizeof(IPMCAST_IF_TTL))); return STATUS_BUFFER_TOO_SMALL; } Trace(IF, TRACE, ("SetTtl: Index %d Ttl %d\n", pTtl->dwIfIndex, pTtl->byTtl)); bFound = FALSE; CTEGetLock(&RouteTableLock.Lock, &Handle); for(pIpIf = IFList; pIpIf isnot NULL; pIpIf = pIpIf->if_next) { if(pIpIf->if_index is pTtl->dwIfIndex) { bFound = TRUE; break; } } if(!bFound) { Trace(IF, ERROR, ("SetTtl: If %d not found\n", pTtl->dwIfIndex)); CTEFreeLock(&RouteTableLock.Lock, Handle); return STATUS_NOT_FOUND; } pIpIf->if_mcastttl = pTtl->byTtl; CTEFreeLock(&RouteTableLock.Lock, Handle); return STATUS_SUCCESS; } // // MUST BE PAGED IN // #pragma alloc_text(PAGEIPMc, GetTtl) NTSTATUS GetTtl( IN PIRP pIrp, IN ULONG ulInLength, IN ULONG ulOutLength ) /*++ Routine Description: The handler for IOCTL_IPMCAST_GET_TTL We find the IP Interface referred to by the IOCTL and if found, copy out its if_mcastttl field. No checks are made on the TTL value, so the caller must ensure that it is between 1 and 255 Locks: None currently, but when IP puts locks around the IFList, we will need to take that lock Arguments: pIrp IRP ulInLength The length of the Input Buffer ulOutLength The length of the Output Buffer Return Value: STATUS_SUCCESS STATUS_BUFFER_TOO_SMALL STATUS_NOT_FOUND --*/ { PVOID pvIoBuffer; Interface *pIpIf; BOOLEAN bFound; PIPMCAST_IF_TTL pTtl; CTELockHandle Handle; // // Get the user buffer // pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer; pTtl = (PIPMCAST_IF_TTL)pvIoBuffer; pIrp->IoStatus.Information = 0; // // Check the length of both the in and the out buffers // if(ulInLength < sizeof(IPMCAST_IF_TTL)) { Trace(IF, ERROR, ("GetTtl: In Length %d is less required size %d\n", ulOutLength, sizeof(IPMCAST_IF_TTL))); return STATUS_BUFFER_TOO_SMALL; } if(ulOutLength < sizeof(IPMCAST_IF_TTL)) { Trace(IF, ERROR, ("GetTtl: Out Length %d is less required size %d\n", ulOutLength, sizeof(IPMCAST_IF_TTL))); return STATUS_BUFFER_TOO_SMALL; } Trace(IF, TRACE, ("GetTtl: Index %d\n", pTtl->dwIfIndex)); bFound = FALSE; CTEGetLock(&RouteTableLock.Lock, &Handle); for(pIpIf = IFList; pIpIf isnot NULL; pIpIf = pIpIf->if_next) { if(pIpIf->if_index is pTtl->dwIfIndex) { bFound = TRUE; break; } } if(!bFound) { Trace(IF, ERROR, ("GetTtl: If %d not found\n", pTtl->dwIfIndex)); CTEFreeLock(&RouteTableLock.Lock, Handle); return STATUS_NOT_FOUND; } pIrp->IoStatus.Information = sizeof(IPMCAST_IF_TTL); pTtl->byTtl = pIpIf->if_mcastttl; CTEFreeLock(&RouteTableLock.Lock, Handle); return STATUS_SUCCESS; } // // MUST BE PAGED IN // #pragma alloc_text(PAGEIPMc, SetIfState) NTSTATUS SetIfState( IN PIRP pIrp, IN ULONG ulInLength, IN ULONG ulOutLength ) /*++ Routine Description: The handler for IOCTL_IPMCAST_SET_IF_STATE We find the IP Interface referred to by the IOCTL and if found, set the if_mcaststate field. Locks: None currently, but when IP puts locks around the IFList, we will need to take that lock Arguments: pIrp IRP ulInLength The length of the Input Buffer ulOutLength The length of the Output Buffer Return Value: STATUS_SUCCESS STATUS_BUFFER_TOO_SMALL STATUS_NOT_FOUND --*/ { PVOID pvIoBuffer; Interface *pIpIf; BOOLEAN bFound; PIPMCAST_IF_STATE pState; CTELockHandle Handle; UNREFERENCED_PARAMETER(ulOutLength); // // Get the user buffer // pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer; pState = (PIPMCAST_IF_STATE)pvIoBuffer; pIrp->IoStatus.Information = 0; // // Check the length // if(ulInLength < sizeof(IPMCAST_IF_STATE)) { Trace(IF, ERROR, ("SetState: In Length %d is less required size %d\n", ulInLength, sizeof(IPMCAST_IF_STATE))); return STATUS_BUFFER_TOO_SMALL; } Trace(IF, TRACE, ("SetState: Index %d State %d\n", pState->dwIfIndex, pState->byState)); bFound = FALSE; CTEGetLock(&RouteTableLock.Lock, &Handle); for(pIpIf = IFList; pIpIf isnot NULL; pIpIf = pIpIf->if_next) { if(pIpIf->if_index is pState->dwIfIndex) { bFound = TRUE; break; } } if(!bFound) { Trace(IF, ERROR, ("SetState: If %d not found\n", pState->dwIfIndex)); CTEFreeLock(&RouteTableLock.Lock, Handle); return STATUS_NOT_FOUND; } if(pState->byState) { pIpIf->if_mcastflags |= IPMCAST_IF_ENABLED; } CTEFreeLock(&RouteTableLock.Lock, Handle); return STATUS_SUCCESS; } // // MUST BE PAGED IN // #pragma alloc_text(PAGEIPMc, ProcessNotification) NTSTATUS ProcessNotification( IN PIRP pIrp, IN ULONG ulInLength, IN ULONG ulOutLength ) /*++ Routine Description: The handler for IOCTL_IPMCAST_POST_NOTIFICATION If we have a pending message, we copy it out and complete the IRP synchronously. Otherwise, we put it in the list of pending IRPs. Locks: Since this is a potentially cancellable IRP, it must be operated upon with the CancelSpinLock held Arguments: pIrp IRP ulInLength The length of the Input Buffer ulOutLength The length of the Output Buffer Return Value: STATUS_SUCCESS STATUS_PENDING STATUS_BUFFER_TOO_SMALL --*/ { KIRQL kiIrql; PLIST_ENTRY pleNode; DWORD dwSize = 0; PVOID pvIoBuffer; PNOTIFICATION_MSG pMsg; PIPMCAST_NOTIFICATION pinData; UNREFERENCED_PARAMETER(ulInLength); if(ulOutLength < sizeof(IPMCAST_NOTIFICATION)) { Trace(GLOBAL,ERROR, ("ProcessNotification: Buffer size %d smaller than reqd %d\n", ulOutLength, sizeof(IPMCAST_NOTIFICATION))); return STATUS_BUFFER_TOO_SMALL; } pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer; // // use cancel spin lock to prevent irp being cancelled during this call. // IoAcquireCancelSpinLock(&kiIrql); // // If we have a pending notification then complete it - else // queue the notification IRP // if(!IsListEmpty(&g_lePendingNotification)) { Trace(GLOBAL, TRACE, ("ProcessNotification: Pending notification being completed\n")); pleNode = RemoveHeadList(&g_lePendingNotification); pMsg = CONTAINING_RECORD(pleNode, NOTIFICATION_MSG, leMsgLink); pinData = &(pMsg->inMessage); switch(pinData->dwEvent) { case IPMCAST_RCV_PKT_MSG: { Trace(GLOBAL, TRACE, ("ProcessNotification: Pending notification is RCV_PKT\n")); dwSize = FIELD_OFFSET(IPMCAST_NOTIFICATION, ipmPkt) + SIZEOF_PKT_MSG(&(pinData->ipmPkt)); break; } case IPMCAST_DELETE_MFE_MSG: { Trace(GLOBAL, TRACE, ("ProcessNotification: Pending notification is DELETE_MFE\n")); dwSize = FIELD_OFFSET(IPMCAST_NOTIFICATION, immMfe) + SIZEOF_MFE_MSG(&(pinData->immMfe)); break; } } RtlCopyMemory(pvIoBuffer, pinData, dwSize); IoSetCancelRoutine(pIrp, NULL); // // Free the allocated notification // ExFreeToNPagedLookasideList(&g_llMsgBlocks, pMsg); pIrp->IoStatus.Information = dwSize; IoReleaseCancelSpinLock(kiIrql); return STATUS_SUCCESS ; } else { Trace(GLOBAL, TRACE, ("Notification being queued\n")); // // Mark the irp as pending // IoMarkIrpPending(pIrp); // // Queue up the irp at the end // InsertTailList (&g_lePendingIrpQueue, &(pIrp->Tail.Overlay.ListEntry)); // // Set the cancel routine // IoSetCancelRoutine(pIrp, CancelNotificationIrp); IoReleaseCancelSpinLock(kiIrql); return STATUS_PENDING; } } // // MUST BE PAGED IN // #pragma alloc_text(PAGEIPMc, CancelNotificationIrp) VOID CancelNotificationIrp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Cancellation routine for a pending IRP We remove the IRP from the pending queue, and set its status as STATUS_CANCELLED Locks: IO Subsystem calls this with the CancelSpinLock held. We need to release it in this call. Arguments: Irp The IRP to be cancelled Return Value: None --*/ { UNREFERENCED_PARAMETER(DeviceObject); TraceEnter(GLOBAL, "CancelNotificationIrp"); // // Mark this Irp as cancelled // Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; // // Take off our own list // RemoveEntryList(&Irp->Tail.Overlay.ListEntry); // // Release cancel spin lock which the IO system acquired // IoReleaseCancelSpinLock(Irp->CancelIrql); IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); } // // MUST BE PAGED IN // #pragma alloc_text(PAGEIPMc, CompleteNotificationIrp) VOID CompleteNotificationIrp( IN PNOTIFICATION_MSG pMsg ) /*++ Routine Description: Called to complete an IRP back to User space. If an IRP is pending, we copy out the message to the IRP and complete it Otherwise, we put the message on the pending message queue. The next time a notification IRP is posted to us, we will copy out the message. Locks: Since both the pending message and the pending IRP queue are locked by the CancelSpinLock, we need to acquire that in the function Arguments: pMsg Message to send to user Return Value: None --*/ { KIRQL irql; PLIST_ENTRY pleNode = NULL ; PIRP pIrp ; PVOID pvIoBuffer; DWORD dwSize; PIPMCAST_NOTIFICATION pinData; TraceEnter(GLOBAL,"CompleteNotificationIrp"); pinData = &(pMsg->inMessage); switch(pinData->dwEvent) { case IPMCAST_RCV_PKT_MSG: case IPMCAST_WRONG_IF_MSG: { dwSize = FIELD_OFFSET(IPMCAST_NOTIFICATION, ipmPkt) + SIZEOF_PKT_MSG(&(pinData->ipmPkt)); break; } case IPMCAST_DELETE_MFE_MSG: { dwSize = FIELD_OFFSET(IPMCAST_NOTIFICATION, immMfe) + SIZEOF_MFE_MSG(&(pinData->immMfe)); break; } default: { #pragma warning(push) #pragma warning(disable:4127) // conditional expression is constant RtAssert(FALSE); #pragma warning(pop) dwSize = 0; break; } } // // grab cancel spin lock // IoAcquireCancelSpinLock (&irql); if(!IsListEmpty(&g_lePendingIrpQueue)) { pleNode = RemoveHeadList(&g_lePendingIrpQueue) ; Trace(GLOBAL, TRACE, ("CompleteNotificationIrp: Found a pending Irp\n")); pIrp = CONTAINING_RECORD(pleNode, IRP, Tail.Overlay.ListEntry); pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer; memcpy(pvIoBuffer, pinData, dwSize); Trace(GLOBAL, TRACE, ("CompleteNotificationIrp: Returning Irp with event code of %d\n", ((PIPMCAST_NOTIFICATION)pIrp->AssociatedIrp.SystemBuffer)->dwEvent)); IoSetCancelRoutine(pIrp, NULL) ; pIrp->IoStatus.Information = dwSize; pIrp->IoStatus.Status = STATUS_SUCCESS; // // release lock // IoReleaseCancelSpinLock (irql); IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT); // // Free the allocated notification // ExFreeToNPagedLookasideList(&g_llMsgBlocks, pMsg); } else { Trace(GLOBAL, TRACE, ("CompleteNotificationIrp: Found no pending Irp so queuing the notification\n")); InsertTailList(&g_lePendingNotification, &(pMsg->leMsgLink)); // // release lock // IoReleaseCancelSpinLock (irql); } } // // MUST BE PAGED IN // #pragma alloc_text(PAGEIPMc, ClearPendingIrps) VOID ClearPendingIrps( VOID ) /*++ Routine Description: Called to shutdown time to complete any pending IRPS we may have Locks: Since both the pending message and the pending IRP queue are locked by the CancelSpinLock, we need to acquire that in the function Arguments: None Return Value: None --*/ { KIRQL irql; PLIST_ENTRY pleNode = NULL; PIRP pIrp; TraceEnter(GLOBAL, "ClearPendingIrps"); IoAcquireCancelSpinLock(&irql); while(!IsListEmpty(&g_lePendingIrpQueue)) { pleNode = RemoveHeadList(&g_lePendingIrpQueue); pIrp = CONTAINING_RECORD(pleNode, IRP, Tail.Overlay.ListEntry); IoSetCancelRoutine(pIrp, NULL); pIrp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; pIrp->IoStatus.Information = 0; // // release lock to complete the IRP // IoReleaseCancelSpinLock(irql); IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT); // // Reaquire the lock // IoAcquireCancelSpinLock(&irql); } IoReleaseCancelSpinLock(irql); TraceLeave(GLOBAL, "ClearPendingIrps"); } // // MUST BE PAGED IN // #pragma alloc_text(PAGEIPMc, ClearPendingNotifications) VOID ClearPendingNotifications( VOID ) /*++ Routine Description: Called to shutdown time to complete any pending notification messages we may have Locks: Since both the pending message and the pending IRP queue are locked by the CancelSpinLock, we need to acquire that in the function Arguments: None Return Value: None --*/ { KIRQL irql; PLIST_ENTRY pleNode; PNOTIFICATION_MSG pMsg; TraceEnter(GLOBAL, "ClearPendingNotifications"); IoAcquireCancelSpinLock(&irql); while(!IsListEmpty(&g_lePendingNotification)) { pleNode = RemoveHeadList(&g_lePendingNotification); pMsg = CONTAINING_RECORD(pleNode, NOTIFICATION_MSG, leMsgLink); ExFreeToNPagedLookasideList(&g_llMsgBlocks, pMsg); } IoReleaseCancelSpinLock(irql); TraceLeave(GLOBAL, "ClearPendingNotifications"); } #pragma alloc_text(PAGE, StartStopDriver) NTSTATUS StartStopDriver( IN PIRP pIrp, IN ULONG ulInLength, IN ULONG ulOutLength ) /*++ Routine Description: This is the handler for IOCTL_IPMCAST_START_STOP. We do the normal buffer length checks. Locks: None Arguments: pIrp IRP ulInLength The length of the Input Buffer ulOutLength The length of the Output Buffer Return Value: STATUS_SUCCESS STATUS_BUFFER_TOO_SMALL --*/ { PVOID pvIoBuffer; NTSTATUS nStatus; PDWORD pdwStart; UNREFERENCED_PARAMETER(ulOutLength); TraceEnter(GLOBAL, "StartStopDriver"); // // Get the user buffer // pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer; pdwStart = (PDWORD)pvIoBuffer; // // Always clean out the information field // pIrp->IoStatus.Information = 0; // // If we have dont even have enough for the basic MFE // there is something bad going on // if(ulInLength < sizeof(DWORD)) { Trace(GLOBAL, ERROR, ("StartStopDriver: In Length %d is less than %d\n", ulInLength, sizeof(DWORD))); TraceLeave(GLOBAL, "StartStopDriver"); return STATUS_BUFFER_TOO_SMALL; } if(*pdwStart) { nStatus = StartDriver(); } else { nStatus = StopDriver(); } TraceLeave(GLOBAL, "StartStopDriver"); return nStatus; } #endif //IPMCAST