Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1270 lines
34 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
dispatch.c
Abstract:
This file contains the code for handling I/O request packets.
Author:
Abolade Gbadegesin (t-abolag) 11-July-1997
Revision History:
Abolade Gbadegesin (aboladeg) 19-July-1998
Cleaned up fast-path processing, and corrected input/output buffer logic
while making the mapping-tree global rather than per-interface.
--*/
#include "precomp.h"
#pragma hdrstop
//
// Fast-io-dispatch structure; we only support fast-IO for IOCTLs
//
FAST_IO_DISPATCH NatFastIoDispatch =
{
FIELD_OFFSET(FAST_IO_DISPATCH, FastIoDeviceControl),
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NatFastIoDeviceControl
};
//
// Spinlock to guard file object create / close
//
KSPIN_LOCK NatFileObjectLock;
//
// The process that owns the outstanding file objects
//
HANDLE NatOwnerProcessId;
//
// The count of outstanding user-mode file objects.
//
ULONG NatFileObjectCount;
//
// FORWARD DECLARATIONS
//
NTSTATUS
NatpExecuteIoDeviceControl(
PIRP Irp,
PFILE_OBJECT FileObject,
MODE RequestorMode,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength,
ULONG IoControlCode,
PULONG Size
);
NTSTATUS
NatpSetGlobalInfo(
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength,
PULONG Size
);
BOOLEAN FASTCALL
NatpValidateHeader(
PRTR_INFO_BLOCK_HEADER Header,
ULONG Size
);
NTSTATUS
NatDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is invoked to handle interrupt-request packets
queued to the NAT's device object. A single routine serves
to handle all the varios requests in which we are interested.
Arguments:
DeviceObject - the NAT's device-object
Irp - the interrupt request packet
Return Value:
NTSTATUS - status code.
--*/
{
PVOID Buffer;
PRTR_TOC_ENTRY Entry;
PRTR_INFO_BLOCK_HEADER Header;
ULONG i;
PIO_STACK_LOCATION IrpSp;
KIRQL Irql;
HANDLE ProcessId;
ULONG Size = 0;
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN ShouldComplete = TRUE;
CALLTRACE(("NatDispatch\n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
Buffer = Irp->AssociatedIrp.SystemBuffer;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
switch (IrpSp->MajorFunction) {
case IRP_MJ_CREATE: {
//
// If this is a user-mode request check process
// ownership.
//
if (UserMode == Irp->RequestorMode) {
ProcessId = PsGetCurrentProcessId();
KeAcquireSpinLock(&NatFileObjectLock, &Irql);
if (0 == NatFileObjectCount) {
//
// No process currently owns the NAT -- record
// the new owning process id, and update the
// outstanding file object count.
//
ASSERT(NULL == NatOwnerProcessId);
NatOwnerProcessId = ProcessId;
NatFileObjectCount = 1;
} else if (ProcessId == NatOwnerProcessId) {
//
// The owning process is creating another
// file object.
//
NatFileObjectCount += 1;
} else {
//
// A process that is not our owner is trying
// to create a file object -- fail the request.
//
status = STATUS_ACCESS_DENIED;
}
KeReleaseSpinLock(&NatFileObjectLock, Irql);
}
break;
}
case IRP_MJ_CLEANUP: {
NatDeleteAnyAssociatedInterface(IrpSp->FileObject);
NatCleanupAnyAssociatedRedirect(IrpSp->FileObject);
NatCleanupAnyAssociatedNotification(IrpSp->FileObject);
NatDeleteAnyAssociatedDynamicTicket(IrpSp->FileObject);
break;
}
case IRP_MJ_CLOSE: {
//
// If this is a user-mode request update the outstanding
// file object count and process ownership.
//
if (UserMode == Irp->RequestorMode) {
KeAcquireSpinLock(&NatFileObjectLock, &Irql);
ASSERT(NatFileObjectCount > 0);
ASSERT(PsGetCurrentProcessId() == NatOwnerProcessId);
NatFileObjectCount -= 1;
if (0 == NatFileObjectCount) {
//
// The process has closed its last outstanding
// file object, and thus is no longer our
// owner.
//
NatOwnerProcessId = NULL;
}
KeReleaseSpinLock(&NatFileObjectLock, Irql);
}
break;
}
case IRP_MJ_DEVICE_CONTROL: {
status =
NatpExecuteIoDeviceControl(
Irp,
IrpSp->FileObject,
Irp->RequestorMode,
Buffer,
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
Buffer,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
IrpSp->Parameters.DeviceIoControl.IoControlCode,
&Size
);
break;
}
#if NAT_WMI
case IRP_MJ_SYSTEM_CONTROL: {
status =
NatExecuteSystemControl(
DeviceObject,
Irp,
&ShouldComplete
);
if (ShouldComplete) {
ShouldComplete = FALSE;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
break;
}
#endif
}
if (status != STATUS_PENDING && ShouldComplete) {
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = Size;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return status;
} // NatDispatch
BOOLEAN
NatFastIoDeviceControl(
PFILE_OBJECT FileObject,
BOOLEAN Wait,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength,
ULONG IoControlCode,
PIO_STATUS_BLOCK IoStatus,
PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine is invoked by the I/O system in an attempt to complete
an I/O control request without constructing an IRP.
Arguments:
FileObject - the file associated with the I/O request
Wait - indicates whether a wait is allowed in this context
InputBuffer - input information for the I/O request
InputBufferLength - length of 'InputBuffer'
OutputBuffer - output information for the I/O request
OutputBufferLength - length of 'OutputBuffer'
IoControlCode - I/O request code
IoStatus - receives the status of the I/O request
DeviceObject - device object of the NAT
Return Value:
BOOLEAN - TRUE if completed synchronously, FALSE otherwise
--*/
{
ULONG Size = 0;
NTSTATUS Status;
PVOID LocalInputBuffer;
MODE PreviousMode;
//
// We are in the context of the requesting thread,
// so exceptions may occur, and must be handled.
// To deal with modifications to the user-provided information
// capture the contents of the input buffer in non-paged pool.
//
if (!InputBufferLength) {
LocalInputBuffer = NULL;
} else {
LocalInputBuffer =
ExAllocatePoolWithTag(
NonPagedPool,
InputBufferLength,
NAT_TAG_IOCTL
);
if (!LocalInputBuffer) {
return FALSE;
}
}
PreviousMode = ExGetPreviousMode();
__try {
if (InputBufferLength) {
if (PreviousMode != KernelMode) {
ProbeForRead(InputBuffer, InputBufferLength, sizeof(UCHAR));
}
RtlCopyMemory(LocalInputBuffer, InputBuffer, InputBufferLength);
}
Status =
NatpExecuteIoDeviceControl(
NULL,
FileObject,
PreviousMode,
LocalInputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength,
IoControlCode,
&Size
);
if (Status != STATUS_PENDING && NT_SUCCESS(Status)) {
IoStatus->Information = Size;
IoStatus->Status = Status;
} else {
Status = STATUS_PENDING;
}
} __except(EXCEPTION_EXECUTE_HANDLER) {
if (LocalInputBuffer) { ExFreePool(LocalInputBuffer); }
return FALSE;
}
if (LocalInputBuffer) { ExFreePool(LocalInputBuffer); }
return ((Status == STATUS_PENDING) ? FALSE : TRUE);
} // NatFastIoDeviceControl
NTSTATUS
NatpExecuteIoDeviceControl(
PIRP Irp,
PFILE_OBJECT FileObject,
MODE RequestorMode,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength,
ULONG IoControlCode,
PULONG Size
)
/*++
Routine Description:
This routine is invoked to handle I/O controls, either in the context
of the requesting thread (via FastIoDispatch) or in the context of a
system thread (with a corresponding IRP).
For certain requests, particularly those requiring output information,
we return 'STATUS_PENDING' when invoked in the fast path since we cannot
write into the output buffer at raised IRQL. Instead, we wait to be
reinvoked via the slow path with a non-paged system buffer.
Arguments:
Irp - in the slow-path, the IRP associated with the control;
in the fast-path, NULL
FileObject - the file-object associated with the control
RequestorMode - indicates whether the requestor is in kernel-mode
or user-mode
InputBuffer/InputBufferLength - describe data passed in with the control;
may be user-mode or kernel-mode buffer
OutputBuffer/OutputBufferLength - describe space in which to return
information; may be user-mode or kernel-mode buffer
IoControlCode - indicates control requested
Size - on output, number of bytes stored in 'OutputBuffer'.
Return Value:
NTSTATUS - status code.
--*/
{
PIP_ADAPTER_BINDING_INFO BindingInfo;
PRTR_TOC_ENTRY Entry;
PRTR_INFO_BLOCK_HEADER Header;
ULONG i;
NTSTATUS status = STATUS_SUCCESS;
*Size = 0;
switch (IoControlCode) {
case IOCTL_IP_NAT_REQUEST_NOTIFICATION: {
if (!Irp) { return STATUS_PENDING; }
if (InputBufferLength < sizeof(IP_NAT_REQUEST_NOTIFICATION)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatRequestNotification(
(PIP_NAT_REQUEST_NOTIFICATION)InputBuffer,
Irp,
FileObject
);
break;
}
case IOCTL_IP_NAT_SET_GLOBAL_INFO: {
status =
NatpSetGlobalInfo(
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength,
Size
);
break;
}
case IOCTL_IP_NAT_CREATE_INTERFACE: {
if (InputBufferLength <
sizeof(IP_NAT_CREATE_INTERFACE) +
sizeof(IP_ADAPTER_BINDING_INFO)
) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
BindingInfo =
(PIP_ADAPTER_BINDING_INFO)
((PIP_NAT_CREATE_INTERFACE)InputBuffer)->BindingInfo;
if (BindingInfo->AddressCount >= MAXLONG / sizeof(NAT_ADDRESS) ||
SIZEOF_IP_BINDING(BindingInfo->AddressCount) +
sizeof(IP_NAT_CREATE_INTERFACE) > InputBufferLength) {
status = STATUS_INVALID_PARAMETER;
break;
}
status =
NatCreateInterface(
(PIP_NAT_CREATE_INTERFACE)InputBuffer,
FileObject
);
break;
}
case IOCTL_IP_NAT_DELETE_INTERFACE: {
if (InputBufferLength != sizeof(ULONG)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatDeleteInterface(
*(PULONG)InputBuffer,
FileObject
);
if (status == STATUS_PENDING) {
//
// A return of STATUS_PENDING indicates that the interface
// is now marked for deletion but an active thread holds
// a reference to it; convert this to a STATUS_SUCCESS code
// to avoid bypassing our IRP-completion code in 'NatDispatch'.
//
status = STATUS_SUCCESS;
}
break;
}
case IOCTL_IP_NAT_SET_INTERFACE_INFO: {
if (InputBufferLength <
FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) +
FIELD_OFFSET(RTR_INFO_BLOCK_HEADER, TocEntry)
) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
Header = &((PIP_NAT_INTERFACE_INFO)InputBuffer)->Header;
if (!NatpValidateHeader(
Header,
InputBufferLength -
FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header)
)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatConfigureInterface(
(PIP_NAT_INTERFACE_INFO)InputBuffer,
FileObject
);
break;
}
case IOCTL_IP_NAT_GET_INTERFACE_INFO: {
*Size = OutputBufferLength;
if (InputBufferLength != sizeof(ULONG)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatQueryInformationInterface(
*(PULONG)InputBuffer,
(PIP_NAT_INTERFACE_INFO)OutputBuffer,
Size
);
break;
}
case IOCTL_IP_NAT_GET_INTERFACE_STATISTICS: {
if (InputBufferLength != sizeof(ULONG)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
if (OutputBufferLength < sizeof(IP_NAT_INTERFACE_STATISTICS)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}
*Size = sizeof(IP_NAT_INTERFACE_STATISTICS);
status =
NatQueryStatisticsInterface(
*(PULONG)InputBuffer,
(PIP_NAT_INTERFACE_STATISTICS)OutputBuffer
);
break;
}
case IOCTL_IP_NAT_GET_INTERFACE_MAPPING_TABLE: {
if (!Irp) { return STATUS_PENDING; }
*Size = OutputBufferLength;
if (InputBufferLength <
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS,
EnumerateTable)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
if (OutputBufferLength <
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS,
EnumerateTable)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatQueryInterfaceMappingTable(
(PIP_NAT_ENUMERATE_SESSION_MAPPINGS)InputBuffer,
(PIP_NAT_ENUMERATE_SESSION_MAPPINGS)OutputBuffer,
Size
);
break;
}
case IOCTL_IP_NAT_GET_MAPPING_TABLE: {
if (!Irp) { return STATUS_PENDING; }
*Size = OutputBufferLength;
if (InputBufferLength <
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS,
EnumerateTable)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
if (OutputBufferLength <
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS,
EnumerateTable)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatQueryMappingTable(
(PIP_NAT_ENUMERATE_SESSION_MAPPINGS)InputBuffer,
(PIP_NAT_ENUMERATE_SESSION_MAPPINGS)OutputBuffer,
Size
);
break;
}
case IOCTL_IP_NAT_REGISTER_DIRECTOR: {
*Size = sizeof(IP_NAT_REGISTER_DIRECTOR);
//
// Only kernel-mode drivers can register as directors
//
if (RequestorMode != KernelMode ||
SharedUserData->NtProductType == NtProductWinNt) {
status = STATUS_ACCESS_DENIED;
break;
}
if (InputBufferLength != sizeof(IP_NAT_REGISTER_DIRECTOR)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
if (OutputBufferLength < sizeof(IP_NAT_REGISTER_DIRECTOR)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}
//
// Perform the director-registration
//
status =
NatCreateDirector(
(PIP_NAT_REGISTER_DIRECTOR)InputBuffer
);
break;
}
case IOCTL_IP_NAT_GET_DIRECTOR_TABLE: {
if (!Irp) { return STATUS_PENDING; }
*Size = OutputBufferLength;
if (InputBufferLength <
FIELD_OFFSET(IP_NAT_ENUMERATE_DIRECTORS, EnumerateTable)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
if (OutputBufferLength <
FIELD_OFFSET(IP_NAT_ENUMERATE_DIRECTORS, EnumerateTable)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatQueryDirectorTable(
(PIP_NAT_ENUMERATE_DIRECTORS)InputBuffer,
(PIP_NAT_ENUMERATE_DIRECTORS)OutputBuffer,
Size
);
break;
}
case IOCTL_IP_NAT_REGISTER_EDITOR: {
*Size = sizeof(IP_NAT_REGISTER_EDITOR);
//
// Only kernel-mode drivers can register as editors
//
if (RequestorMode != KernelMode) {
status = STATUS_ACCESS_DENIED;
break;
}
if (InputBufferLength != sizeof(IP_NAT_REGISTER_EDITOR)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
if (OutputBufferLength < sizeof(IP_NAT_REGISTER_EDITOR)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}
//
// Perform the editor-registration
//
status = NatCreateEditor((PIP_NAT_REGISTER_EDITOR)InputBuffer);
break;
}
case IOCTL_IP_NAT_GET_EDITOR_TABLE: {
if (!Irp) { return STATUS_PENDING; }
*Size = OutputBufferLength;
if (InputBufferLength <
FIELD_OFFSET(IP_NAT_ENUMERATE_EDITORS, EnumerateTable)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
if (OutputBufferLength <
FIELD_OFFSET(IP_NAT_ENUMERATE_EDITORS, EnumerateTable)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatQueryEditorTable(
(PIP_NAT_ENUMERATE_EDITORS)InputBuffer,
(PIP_NAT_ENUMERATE_EDITORS)OutputBuffer,
Size
);
break;
}
case IOCTL_IP_NAT_CREATE_REDIRECT: {
if (!Irp) { return STATUS_PENDING; }
#if 0
if (SharedUserData->NtProductType == NtProductWinNt) {
status = STATUS_ACCESS_DENIED;
break;
}
#endif
if (InputBufferLength != sizeof(IP_NAT_CREATE_REDIRECT) ||
OutputBufferLength != sizeof(IP_NAT_REDIRECT_STATISTICS)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatCreateRedirect(
(PIP_NAT_CREATE_REDIRECT)InputBuffer,
Irp,
FileObject
);
break;
}
case IOCTL_IP_NAT_CREATE_REDIRECT_EX: {
if (!Irp) { return STATUS_PENDING; }
#if 0
if (SharedUserData->NtProductType == NtProductWinNt) {
status = STATUS_ACCESS_DENIED;
break;
}
#endif
if (InputBufferLength != sizeof(IP_NAT_CREATE_REDIRECT_EX) ||
OutputBufferLength != sizeof(IP_NAT_REDIRECT_STATISTICS)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatCreateRedirectEx(
(PIP_NAT_CREATE_REDIRECT_EX)InputBuffer,
Irp,
FileObject
);
break;
}
case IOCTL_IP_NAT_CANCEL_REDIRECT: {
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_REDIRECT)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatCancelRedirect(
(PIP_NAT_LOOKUP_REDIRECT)InputBuffer,
FileObject
);
break;
}
case IOCTL_IP_NAT_GET_REDIRECT_STATISTICS: {
if (!Irp) { return STATUS_PENDING; }
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_REDIRECT) ||
OutputBufferLength != sizeof(IP_NAT_REDIRECT_STATISTICS)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatQueryInformationRedirect(
(PIP_NAT_LOOKUP_REDIRECT)InputBuffer,
OutputBuffer,
OutputBufferLength,
NatStatisticsRedirectInformation
);
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
break;
}
case IOCTL_IP_NAT_GET_REDIRECT_DESTINATION_MAPPING: {
if (!Irp) { return STATUS_PENDING; }
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_REDIRECT) ||
OutputBufferLength !=
sizeof(IP_NAT_REDIRECT_DESTINATION_MAPPING)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatQueryInformationRedirect(
(PIP_NAT_LOOKUP_REDIRECT)InputBuffer,
OutputBuffer,
OutputBufferLength,
NatDestinationMappingRedirectInformation
);
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
break;
}
case IOCTL_IP_NAT_GET_REDIRECT_SOURCE_MAPPING: {
if (!Irp) { return STATUS_PENDING; }
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_REDIRECT) ||
OutputBufferLength != sizeof(IP_NAT_REDIRECT_SOURCE_MAPPING)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatQueryInformationRedirect(
(PIP_NAT_LOOKUP_REDIRECT)InputBuffer,
OutputBuffer,
OutputBufferLength,
NatSourceMappingRedirectInformation
);
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
break;
}
case IOCTL_IP_NAT_LOOKUP_SESSION_MAPPING_KEY: {
PIP_NAT_LOOKUP_SESSION_MAPPING LookupMapping;
if (!Irp) { return STATUS_PENDING; }
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_SESSION_MAPPING) ||
OutputBufferLength != sizeof(IP_NAT_SESSION_MAPPING_KEY)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
LookupMapping = (PIP_NAT_LOOKUP_SESSION_MAPPING)InputBuffer;
status =
NatLookupAndQueryInformationMapping(
LookupMapping->Protocol,
LookupMapping->DestinationAddress,
LookupMapping->DestinationPort,
LookupMapping->SourceAddress,
LookupMapping->SourcePort,
OutputBuffer,
OutputBufferLength,
NatKeySessionMappingInformation
);
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
break;
}
case IOCTL_IP_NAT_LOOKUP_SESSION_MAPPING_KEY_EX: {
PIP_NAT_LOOKUP_SESSION_MAPPING LookupMapping;
if (!Irp) { return STATUS_PENDING; }
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_SESSION_MAPPING) ||
OutputBufferLength != sizeof(IP_NAT_SESSION_MAPPING_KEY_EX)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
LookupMapping = (PIP_NAT_LOOKUP_SESSION_MAPPING)InputBuffer;
status =
NatLookupAndQueryInformationMapping(
LookupMapping->Protocol,
LookupMapping->DestinationAddress,
LookupMapping->DestinationPort,
LookupMapping->SourceAddress,
LookupMapping->SourcePort,
OutputBuffer,
OutputBufferLength,
NatKeySessionMappingExInformation
);
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
break;
}
case IOCTL_IP_NAT_LOOKUP_SESSION_MAPPING_STATISTICS: {
PIP_NAT_LOOKUP_SESSION_MAPPING LookupMapping;
if (!Irp) { return STATUS_PENDING; }
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_SESSION_MAPPING) ||
OutputBufferLength != sizeof(IP_NAT_SESSION_MAPPING_STATISTICS)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
LookupMapping = (PIP_NAT_LOOKUP_SESSION_MAPPING)InputBuffer;
status =
NatLookupAndQueryInformationMapping(
LookupMapping->Protocol,
LookupMapping->DestinationAddress,
LookupMapping->DestinationPort,
LookupMapping->SourceAddress,
LookupMapping->SourcePort,
OutputBuffer,
OutputBufferLength,
NatStatisticsSessionMappingInformation
);
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
break;
}
case IOCTL_IP_NAT_CREATE_DYNAMIC_TICKET: {
if (InputBufferLength < sizeof(IP_NAT_CREATE_DYNAMIC_TICKET)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatCreateDynamicTicket(
(PIP_NAT_CREATE_DYNAMIC_TICKET)InputBuffer,
InputBufferLength,
FileObject
);
break;
}
case IOCTL_IP_NAT_DELETE_DYNAMIC_TICKET: {
if (InputBufferLength != sizeof(IP_NAT_DELETE_DYNAMIC_TICKET)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatDeleteDynamicTicket(
(PIP_NAT_DELETE_DYNAMIC_TICKET)InputBuffer,
FileObject
);
break;
}
case IOCTL_IP_NAT_CREATE_TICKET: {
if (InputBufferLength != sizeof(IP_NAT_CREATE_TICKET)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatProcessCreateTicket(
(PIP_NAT_CREATE_TICKET)InputBuffer,
FileObject
);
break;
}
case IOCTL_IP_NAT_DELETE_TICKET: {
if (InputBufferLength != sizeof(IP_NAT_CREATE_TICKET)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatProcessDeleteTicket(
(PIP_NAT_CREATE_TICKET)InputBuffer,
FileObject
);
break;
}
case IOCTL_IP_NAT_LOOKUP_TICKET: {
if (InputBufferLength != sizeof(IP_NAT_CREATE_TICKET)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
if (OutputBufferLength != sizeof(IP_NAT_PORT_MAPPING)) {
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
status =
NatProcessLookupTicket(
(PIP_NAT_CREATE_TICKET)InputBuffer,
(PIP_NAT_PORT_MAPPING)OutputBuffer,
FileObject
);
break;
}
default: {
status = STATUS_INVALID_PARAMETER;
break;
}
}
return status;
} // NatpExecuteIoDeviceControl
NTSTATUS
NatpSetGlobalInfo(
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength,
PULONG Size
)
/*++
Routine Description:
This routine is invoked upon receipt of the NAT's configuration.
Arguments:
InputBuffer/InputBufferLength - describe configuration information
OutputBuffer/OutputBufferLength - unused.
Size - unused
Return Value:
NTSTATUS - status code.
--*/
{
PRTR_TOC_ENTRY Entry;
PRTR_INFO_BLOCK_HEADER Header;
ULONG i;
ULONG Protocol;
if (InputBufferLength <
FIELD_OFFSET(IP_NAT_GLOBAL_INFO, Header) +
FIELD_OFFSET(RTR_INFO_BLOCK_HEADER, TocEntry)
) {
return STATUS_INVALID_BUFFER_SIZE;
}
Header = &((PIP_NAT_GLOBAL_INFO)InputBuffer)->Header;
if (!NatpValidateHeader(
Header,
InputBufferLength - FIELD_OFFSET(IP_NAT_GLOBAL_INFO, Header)
)) {
return STATUS_INVALID_BUFFER_SIZE;
}
for (i = 0; i < Header->TocEntriesCount; i++) {
Entry = &Header->TocEntry[i];
switch (Entry->InfoType) {
case IP_NAT_TIMEOUT_TYPE: {
PIP_NAT_TIMEOUT Timeout = GetInfoFromTocEntry(Header,Entry);
InterlockedExchange(
&TcpTimeoutSeconds,
Timeout->TCPTimeoutSeconds
);
InterlockedExchange(
&UdpTimeoutSeconds,
Timeout->UDPTimeoutSeconds
);
break;
}
case IP_NAT_PROTOCOLS_ALLOWED_TYPE: {
PIP_NAT_PROTOCOLS_ALLOWED ProtocolsAllowed =
GetInfoFromTocEntry(Header,Entry);
//
// The protocols allowed are specified using a 256-bit bitmap;
// an allowed protocol has the bit for its protocol number set.
// For each protocol enabled in the bitmap, we now install the
// default IP-header translation routine, with the exception
// of protocols which are always enabled.
//
#define IS_BIT_SET(b,i) ((b)[(i) / 32] & (1 << ((i) & 31)))
for (Protocol = 0; Protocol < 256; Protocol++) {
if (Protocol == NAT_PROTOCOL_ICMP ||
Protocol == NAT_PROTOCOL_PPTP ||
Protocol == NAT_PROTOCOL_TCP ||
Protocol == NAT_PROTOCOL_UDP
) {
continue;
}
if (IS_BIT_SET(ProtocolsAllowed->Bitmap, Protocol)) {
InterlockedExchangePointer(
(PVOID)TranslateRoutineTable[Protocol],
(PVOID)NatTranslateIp
);
}
else {
InterlockedExchangePointer(
(PVOID)TranslateRoutineTable[Protocol],
NULL
);
}
}
break;
}
}
}
return STATUS_SUCCESS;
}
BOOLEAN FASTCALL
NatpValidateHeader(
PRTR_INFO_BLOCK_HEADER Header,
ULONG Size
)
/*++
Routine Description:
This routine is invoked to ensure that the given header is consistent.
This is the case if
* the header's size is less than or equal to 'Size'
* each entry in the header is contained in 'Header->Size'.
* the data for each entry is contained in 'Header->Size'.
Arguments:
Header - the header to be validated
Size - the size of the buffer in which 'Header' appears
Return Value:
BOOLEAN - TRUE if valid, FALSE otherwise.
--*/
{
ULONG i;
ULONG64 Length;
//
// Check that the base structure is present
//
if (Size < FIELD_OFFSET(RTR_INFO_BLOCK_HEADER, TocEntry) ||
Size < Header->Size) {
return FALSE;
}
//
// Check that the table of contents is present
//
Length = (ULONG64)Header->TocEntriesCount * sizeof(RTR_TOC_ENTRY);
if (Length > MAXLONG) {
return FALSE;
}
Length += FIELD_OFFSET(RTR_INFO_BLOCK_HEADER, TocEntry);
if (Length > Header->Size) {
return FALSE;
}
//
// Check that all the data is present
//
for (i = 0; i < Header->TocEntriesCount; i++) {
Length =
(ULONG64)Header->TocEntry[i].Count * Header->TocEntry[i].InfoSize;
if (Length > MAXLONG) {
return FALSE;
}
if ((Length + Header->TocEntry[i].Offset) > Header->Size) {
return FALSE;
}
}
return TRUE;
} // NatpValidateHeader