/*++ Copyright (c) 2000, Microsoft Corporation Module Name: ipfw.c Abstract: This module implements a driver which demonstrates the use of the TCP/IP driver's support for firewall hooks. It interacts with a user-mode control-program to support registration of multiple firewall routines. Author: Abolade Gbadegesin (aboladeg) 7-March-2000 Revision History: --*/ #include #include #include "ipfw.h" // // Structure: IPFW_ROUTINE // // Used to manage the table of routines registered with TCP/IP. // typedef struct _IPFW_ROUTINE { IPPacketFirewallPtr Routine; UINT Priority; ULONG Flags; ULONG PacketCount; } IPFW_ROUTINE, *PIPFW_ROUTINE; #define IPFW_ROUTINE_FLAG_REGISTERED 0x00000001 extern IPFW_ROUTINE IpfwRoutineTable[]; #define DEFINE_IPFW_ROUTINE(_Index) \ FORWARD_ACTION IpfwRoutine##_Index( \ VOID** Data, \ UINT ReceiveIndex, \ UINT* SendIndex, \ PUCHAR DestinationType, \ PVOID Context, \ UINT ContextLength, \ IPRcvBuf** OutputData \ ) { \ InterlockedIncrement(&IpfwRoutineTable[_Index].PacketCount); \ return FORWARD; \ } #define INCLUDE_IPFW_ROUTINE(_Index) \ { IpfwRoutine##_Index, 0, 0 }, DEFINE_IPFW_ROUTINE(0) DEFINE_IPFW_ROUTINE(1) DEFINE_IPFW_ROUTINE(2) DEFINE_IPFW_ROUTINE(3) DEFINE_IPFW_ROUTINE(4) DEFINE_IPFW_ROUTINE(5) DEFINE_IPFW_ROUTINE(6) DEFINE_IPFW_ROUTINE(7) DEFINE_IPFW_ROUTINE(8) DEFINE_IPFW_ROUTINE(9) IPFW_ROUTINE IpfwRoutineTable[IPFW_ROUTINE_COUNT] = { INCLUDE_IPFW_ROUTINE(0) INCLUDE_IPFW_ROUTINE(1) INCLUDE_IPFW_ROUTINE(2) INCLUDE_IPFW_ROUTINE(3) INCLUDE_IPFW_ROUTINE(4) INCLUDE_IPFW_ROUTINE(5) INCLUDE_IPFW_ROUTINE(6) INCLUDE_IPFW_ROUTINE(7) INCLUDE_IPFW_ROUTINE(8) INCLUDE_IPFW_ROUTINE(9) }; KSPIN_LOCK IpfwRoutineLock; PDEVICE_OBJECT IpfwDeviceObject = NULL; // // FORWARD DECLARATIONS // NTSTATUS IpfwClose( PDEVICE_OBJECT DeviceObject, PIRP Irp ); NTSTATUS IpfwCreate( PDEVICE_OBJECT DeviceObject, PIRP Irp ); VOID IpfwUnload( IN PDRIVER_OBJECT DriverObject ); NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine implements the standard driver-entry for an NT driver. It is responsible for registering with the TCP/IP driver. Arguments: DriverObject - object to be initialized with NT driver entrypoints RegistryPath - contains path to this driver's registry key Return Value: NTSTATUS - indicates success/failure. --*/ { UNICODE_STRING DeviceName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE ServiceKey; NTSTATUS status; KdPrint(("DriverEntry\n")); KeInitializeSpinLock(&IpfwRoutineLock); // // Create a device-object on which to communicate with the control program. // RtlInitUnicodeString(&DeviceName, DD_IPFW_DEVICE_NAME); status = IoCreateDevice( DriverObject, 0, &DeviceName, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &IpfwDeviceObject ); if (!NT_SUCCESS(status)) { KdPrint(("DriverEntry: IoCreateDevice=%08x\n", status)); return status; } // // Create dispatch points for create/open, cleanup, unload. // DriverObject->MajorFunction[IRP_MJ_CREATE] = IpfwCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = IpfwClose; DriverObject->DriverUnload = IpfwUnload; return STATUS_SUCCESS; } // DriverEntry NTSTATUS IpfwClose( PDEVICE_OBJECT DeviceObject, PIRP Irp ) { UNICODE_STRING DeviceString; KEVENT Event; PFILE_OBJECT FileObject; ULONG i; IO_STATUS_BLOCK IoStatus; PDEVICE_OBJECT IpDeviceObject; KIRQL Irql; PIRP RegisterIrp; IP_SET_FIREWALL_HOOK_INFO SetHookInfo; NTSTATUS status; KdPrint(("IpfwClose\n")); i = PtrToUlong(IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext); #if DBG KeAcquireSpinLock(&IpfwRoutineLock, &Irql); ASSERT(IpfwRoutineTable[i].Flags & IPFW_ROUTINE_FLAG_REGISTERED); KeReleaseSpinLock(&IpfwRoutineLock, Irql); #endif // // Revoke the registration made on behalf of the client whose file-object // is being closed. // Obtain a pointer to the IP device-object, // construct a registration IRP, and attempt to register the routine // selected above. // RtlInitUnicodeString(&DeviceString, DD_IP_DEVICE_NAME); status = IoGetDeviceObjectPointer( &DeviceString, SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, &FileObject, &IpDeviceObject ); if (NT_SUCCESS(status)) { ObReferenceObject(IpDeviceObject); SetHookInfo.FirewallPtr = IpfwRoutineTable[i].Routine; SetHookInfo.Priority = 0; // Unused SetHookInfo.Add = FALSE; KeInitializeEvent(&Event, SynchronizationEvent, FALSE); RegisterIrp = IoBuildDeviceIoControlRequest( IOCTL_IP_SET_FIREWALL_HOOK, IpDeviceObject, (PVOID)&SetHookInfo, sizeof(SetHookInfo), NULL, 0, FALSE, &Event, &IoStatus ); if (!RegisterIrp) { status = STATUS_UNSUCCESSFUL; } else { status = IoCallDriver(IpDeviceObject, RegisterIrp); if (status == STATUS_PENDING) { KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); status = IoStatus.Status; } ASSERT(NT_SUCCESS(status)); } ObDereferenceObject((PVOID)FileObject); ObDereferenceObject(IpDeviceObject); } // // Release the entry in the table of routines. // KeAcquireSpinLock(&IpfwRoutineLock, &Irql); IpfwRoutineTable[i].Flags &= ~IPFW_ROUTINE_FLAG_REGISTERED; KeReleaseSpinLock(&IpfwRoutineLock, Irql); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } // IpfwClose NTSTATUS IpfwCreate( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++ Routine Description: This routine is invoked by the I/O manager to inform us that a handle has been opened on our device-object. --*/ { PIPFW_CREATE_PACKET CreatePacket; UNICODE_STRING DeviceString; PFILE_FULL_EA_INFORMATION EaBuffer; KEVENT Event; PFILE_OBJECT FileObject; ULONG i; IO_STATUS_BLOCK IoStatus; PDEVICE_OBJECT IpDeviceObject; KIRQL Irql; UINT Priority; PIRP RegisterIrp; IP_SET_FIREWALL_HOOK_INFO SetHookInfo; NTSTATUS status; KdPrint(("IpfwCreate\n")); // // Extract the parameters supplied by the caller. // if ((EaBuffer = Irp->AssociatedIrp.SystemBuffer) && EaBuffer->EaValueLength >= sizeof(IPFW_CREATE_PACKET)) { CreatePacket = (PIPFW_CREATE_PACKET) (EaBuffer->EaName + EaBuffer->EaNameLength + 1); Priority = CreatePacket->Priority; } else { Priority = 0; } // // Look for a free entry in the function-table // KeAcquireSpinLock(&IpfwRoutineLock, &Irql); for (i = 0; i < IPFW_ROUTINE_COUNT; i++) { if (!(IpfwRoutineTable[i].Flags & IPFW_ROUTINE_FLAG_REGISTERED)) { IpfwRoutineTable[i].Flags |= IPFW_ROUTINE_FLAG_REGISTERED; break; } } KeReleaseSpinLock(&IpfwRoutineLock, Irql); if (i >= IPFW_ROUTINE_COUNT) { status = STATUS_UNSUCCESSFUL; } else { // // Obtain a pointer to the IP device-object, // construct a registration IRP, and attempt to register the routine // selected above. // RtlInitUnicodeString(&DeviceString, DD_IP_DEVICE_NAME); status = IoGetDeviceObjectPointer( &DeviceString, SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, &FileObject, &IpDeviceObject ); if (NT_SUCCESS(status)) { ObReferenceObject(IpDeviceObject); SetHookInfo.FirewallPtr = IpfwRoutineTable[i].Routine; SetHookInfo.Priority = Priority ? Priority : i + 1; SetHookInfo.Add = TRUE; KeInitializeEvent(&Event, SynchronizationEvent, FALSE); RegisterIrp = IoBuildDeviceIoControlRequest( IOCTL_IP_SET_FIREWALL_HOOK, IpDeviceObject, (PVOID)&SetHookInfo, sizeof(SetHookInfo), NULL, 0, FALSE, &Event, &IoStatus ); if (!RegisterIrp) { status = STATUS_UNSUCCESSFUL; } else { status = IoCallDriver(IpDeviceObject, RegisterIrp); if (status == STATUS_PENDING) { KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); status = IoStatus.Status; } } ObDereferenceObject((PVOID)FileObject); ObDereferenceObject(IpDeviceObject); } // // If the routine was successfully registered, remember its index // in the client's file-object. Otherwise, if the routine could not be // registered for any reason, release it. // if (NT_SUCCESS(status)) { IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext = UlongToPtr(i); } else { KeAcquireSpinLock(&IpfwRoutineLock, &Irql); IpfwRoutineTable[i].Flags &= ~IPFW_ROUTINE_FLAG_REGISTERED; KeReleaseSpinLock(&IpfwRoutineLock, Irql); } } IoStatus.Status = status; IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } // IpfwCreate VOID IpfwUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This routine is invoked by the I/O manager to unload this driver. Arguments: DriverObject - the object for this driver Return Value: none. --*/ { KdPrint(("IpfwUnload\n")); if (IpfwDeviceObject) { IoDeleteDevice(IpfwDeviceObject); } }