text: /*
   * Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
   * Copyright (c) 2005 CACE Technologies, Davis (California)
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   * notice, this list of conditions and the following disclaimer in the
   * documentation and/or other materials provided with the distribution.
   * 3. Neither the name of the Politecnico di Torino, CACE Technologies 
   * nor the names of its contributors may be used to endorse or promote 
   * products derived from this software without specific prior written 
   * permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   */
  
  #include <GlobalConst.h>
  
  #include "stdarg.h"
  #include "ntddk.h"
  #include "ntiologc.h"
  #include "ndis.h"
  
  #include "ntddpack.h"
  
  #include "debug.h"
  #include "packet.h"
  #include "win_bpf.h"
  #include "win_bpf_filter_init.h"
  
  #if DBG
  // Declare the global debug flag for this driver.
  ULONG PacketDebugFlag = PACKET_DEBUG_LOUD;
  
  #endif
  
  PDEVICE_EXTENSION GlobalDeviceExtension;
  
  //
  // Global strings
  //
  NDIS_STRING NPF_Prefix = NDIS_STRING_CONST("SEE_");
  NDIS_STRING devicePrefix = NDIS_STRING_CONST("\\Device\\");
  NDIS_STRING symbolicLinkPrefix = NDIS_STRING_CONST("\\DosDevices\\");
  NDIS_STRING tcpLinkageKeyName = NDIS_STRING_CONST("\\Registry\\Machine\\System"
  								L"\\CurrentControlSet\\Services\\Tcpip\\Linkage");
  NDIS_STRING AdapterListKey = NDIS_STRING_CONST("\\Registry\\Machine\\System"
  								L"\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}");
  NDIS_STRING bindValueName = NDIS_STRING_CONST("Bind");
  
  /// Global variable that points to the names of the bound adapters
  WCHAR* bindP = NULL;
  
  extern struct time_conv G_Start_Time; // from openclos.c
  
  extern NDIS_SPIN_LOCK Opened_Instances_Lock;
  
  ULONG NCpu = 1;
  
  ULONG TimestampMode;
  UINT g_SendPacketFlags = 0;
  
  
  // Crush now
  void Crush(UINT a, UINT b, UINT c, UINT d)
  {
  	KeBugCheckEx(0x3f000000 + a, (ULONG_PTR)a, (ULONG_PTR)b, (ULONG_PTR)c, (ULONG_PTR)d);
  }
  
  //
  //  Packet Driver's entry routine.
  //
  NTSTATUS
  DriverEntry(
      IN PDRIVER_OBJECT DriverObject,
      IN PUNICODE_STRING RegistryPath
      )
  {
  
      NDIS_PROTOCOL_CHARACTERISTICS  ProtocolChar;
      PDEVICE_OBJECT DeviceObject = NULL;
      PDEVICE_EXTENSION DeviceExtension = NULL;
      NTSTATUS Status = STATUS_SUCCESS;
      NTSTATUS ErrorCode = STATUS_SUCCESS;
      NDIS_STRING ProtoName = NDIS_STRING_CONST("PacketDriver");
      ULONG          DevicesCreated=0;
      NDIS_HANDLE    NdisProtocolHandle;
  	WCHAR* bindT;
  	PKEY_VALUE_PARTIAL_INFORMATION tcpBindingsP;
  	UNICODE_STRING macName;
  	ULONG			OsMajorVersion, OsMinorVersion;
  	
  	PsGetVersion(&OsMajorVersion, &OsMinorVersion, NULL, NULL);
  	//
  	// Define the correct flag to skip the loopback packets, according to the OS
  	//
  	if((OsMajorVersion == 5) && (OsMinorVersion == 0))
  	{
  		// Windows 2000 wants both NDIS_FLAGS_DONT_LOOPBACK and NDIS_FLAGS_SKIP_LOOPBACK
  		g_SendPacketFlags = NDIS_FLAGS_DONT_LOOPBACK | NDIS_FLAGS_SKIP_LOOPBACK_W2K;
  	}
  	else
  	{
  		// Windows XP, 2003 and following want only  NDIS_FLAGS_DONT_LOOPBACK
  		g_SendPacketFlags =  NDIS_FLAGS_DONT_LOOPBACK;
  	}
  
  	if (((OsMajorVersion == 6) && (OsMinorVersion >= 1)) || (OsMajorVersion >= 7))
  	{
  		// Use KeQueryActiveProcessors to get the number of CPUs in Windows 7 or later
  		KAFFINITY cpus = KeQueryActiveProcessors();
  		NCpu = 0;
  
  		while (cpus)
  		{
  			if (cpus % 2)
  			{
  				NCpu++;
  			}
  
  			cpus = cpus / 2;
  		}
  	}
  	else
  	{
  		// Use NdisSystemProcessorCount in Windows Vista or earlier
  		NCpu = NdisSystemProcessorCount();
  	}
  
  	
  	ReadTimeStampModeFromRegistry(RegistryPath);
  
  	IF_LOUD(DbgPrint("%ws",RegistryPath->Buffer);)
  
      IF_LOUD(DbgPrint("\n\nPacket: DriverEntry\n");)
  
  	RtlZeroMemory(&ProtocolChar,sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
  
  #ifdef NDIS50
      ProtocolChar.MajorNdisVersion            = 5;
  #else
      ProtocolChar.MajorNdisVersion            = 3;
  #endif
      ProtocolChar.MinorNdisVersion            = 0;
      ProtocolChar.Reserved                    = 0;
      ProtocolChar.OpenAdapterCompleteHandler  = NPF_OpenAdapterComplete;
      ProtocolChar.CloseAdapterCompleteHandler = NPF_CloseAdapterComplete;
      ProtocolChar.SendCompleteHandler         = NPF_SendComplete;
      ProtocolChar.TransferDataCompleteHandler = NPF_TransferDataComplete;
      ProtocolChar.ResetCompleteHandler        = NPF_ResetComplete;
      ProtocolChar.RequestCompleteHandler      = NPF_RequestComplete;
      ProtocolChar.ReceiveHandler              = NPF_tap;
      ProtocolChar.ReceiveCompleteHandler      = NPF_ReceiveComplete;
      ProtocolChar.StatusHandler               = NPF_Status;
      ProtocolChar.StatusCompleteHandler       = NPF_StatusComplete;
  #ifdef NDIS50
      ProtocolChar.BindAdapterHandler          = NPF_BindAdapter;
      ProtocolChar.UnbindAdapterHandler        = NPF_UnbindAdapter;
      ProtocolChar.PnPEventHandler             = NPF_PowerChange;
      ProtocolChar.ReceivePacketHandler        = NULL;
  #endif
      ProtocolChar.Name                        = ProtoName;
  
      NdisRegisterProtocol(
          &Status,
          &NdisProtocolHandle,
          &ProtocolChar,
          sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
  
      if (Status != NDIS_STATUS_SUCCESS) {
  
          IF_LOUD(DbgPrint("NPF: Failed to register protocol with NDIS\n");)
  
          return Status;
  
      }
  	
      NdisAllocateSpinLock(&Opened_Instances_Lock);
  
      // Set up the device driver entry points.
      DriverObject->MajorFunction[IRP_MJ_CREATE] = NPF_Open;
      DriverObject->MajorFunction[IRP_MJ_CLOSE]  = NPF_Close;
      DriverObject->MajorFunction[IRP_MJ_READ]   = NPF_Read;
      DriverObject->MajorFunction[IRP_MJ_WRITE]  = NPF_Write;
      DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = NPF_IoControl;
      DriverObject->DriverUnload = NPF_Unload;
  
   	bindP = getAdaptersList();
  
  	if (bindP == NULL) 
  	{
  		IF_LOUD(DbgPrint("Adapters not found in the registry, try to copy the bindings of TCP-IP.\n");)
  
  		tcpBindingsP = getTcpBindings();
  			
  		if (tcpBindingsP == NULL)
  		{
  			IF_LOUD(DbgPrint("TCP-IP not found, quitting.\n");)
  			goto RegistryError;
  		}
  			
  		bindP = (WCHAR*)tcpBindingsP;
  		bindT = (WCHAR*)(tcpBindingsP->Data);
  			
  	}
  	else 
  	{
  		bindT = bindP;
  	}
  
  	for (; *bindT != UNICODE_NULL; bindT += (macName.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR)) 
  	{
  		RtlInitUnicodeString(&macName, bindT);
  		createDevice(DriverObject, &macName, NdisProtocolHandle);
  	}
  
  	return STATUS_SUCCESS;
  
  RegistryError:
  
      NdisDeregisterProtocol(
          &Status,
          NdisProtocolHandle
          );
  
      Status=STATUS_UNSUCCESSFUL;
  
      return(Status);
  
  }
  
  //-------------------------------------------------------------------
  
  PWCHAR getAdaptersList(void)
  {
  	PKEY_VALUE_PARTIAL_INFORMATION result = NULL;
  	OBJECT_ATTRIBUTES objAttrs;
  	NTSTATUS status;
  	HANDLE keyHandle;
  	UINT BufPos=0;
  	UINT BufLen=4096;
  
  	
  	PWCHAR DeviceNames = (PWCHAR) ExAllocatePoolWithTag(PagedPool, BufLen, '0PWA');
  	
  	if (DeviceNames == NULL) {
  		IF_LOUD(DbgPrint("Unable the allocate the buffer for the list of the network adapters\n");)
  			return NULL;
  	}
  	
  	InitializeObjectAttributes(&objAttrs, &AdapterListKey,
  		OBJ_CASE_INSENSITIVE, NULL, NULL);
  	status = ZwOpenKey(&keyHandle, KEY_READ, &objAttrs);
  	if (!NT_SUCCESS(status)) {
  		IF_LOUD(DbgPrint("\n\nStatus of %x opening %ws\n", status, tcpLinkageKeyName.Buffer);)
  	}
  	else { //OK
  		
  		ULONG resultLength;
  		CHAR AdapInfo[1024];
  		UINT i=0;
  		KEY_VALUE_PARTIAL_INFORMATION valueInfo;
  		
  		IF_LOUD(DbgPrint("getAdaptersList: scanning the list of the adapters in the registry, DeviceNames=%x\n",DeviceNames);)
  			
  			// Scan the list of the devices
  			while((status=ZwEnumerateKey(keyHandle,i,KeyBasicInformation,AdapInfo,sizeof(AdapInfo),&resultLength))==STATUS_SUCCESS)
  			{
  				WCHAR ExportKeyName [512];
  				PWCHAR ExportKeyPrefix = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\";
  				UINT ExportKeyPrefixSize = sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}");
  				PWCHAR LinkageKeyPrefix = L"\\Linkage";
  				UINT LinkageKeyPrefixSize = sizeof(L"\\Linkage");
  				NDIS_STRING FinalExportKey = NDIS_STRING_CONST("Export");
  				PKEY_BASIC_INFORMATION tInfo= (PKEY_BASIC_INFORMATION)AdapInfo;
  				UNICODE_STRING AdapterKeyName;
  				HANDLE ExportKeyHandle;
  				
  				RtlCopyMemory(ExportKeyName,
  					ExportKeyPrefix,
  					ExportKeyPrefixSize);
  				
  				RtlCopyMemory((PCHAR)ExportKeyName+ExportKeyPrefixSize,
  					tInfo->Name,
  					tInfo->NameLength+2);
  				
  				RtlCopyMemory((PCHAR)ExportKeyName+ExportKeyPrefixSize+tInfo->NameLength,
  					LinkageKeyPrefix,
  					LinkageKeyPrefixSize);
  				
  				IF_LOUD(DbgPrint("Key name=%ws\n", ExportKeyName);)
  										
  				RtlInitUnicodeString(&AdapterKeyName, ExportKeyName);
  				
  				InitializeObjectAttributes(&objAttrs, &AdapterKeyName,
  					OBJ_CASE_INSENSITIVE, NULL, NULL);
  				
  				status=ZwOpenKey(&ExportKeyHandle,KEY_READ,&objAttrs);
  				
  				if (!NT_SUCCESS(status)) {
  					IF_LOUD(DbgPrint("OpenKey Failed, %d!\n",status);)
  					i++;
  					continue;
  				}
  				
  				status = ZwQueryValueKey(ExportKeyHandle, &FinalExportKey,
  					KeyValuePartialInformation, &valueInfo,
  					sizeof(valueInfo), &resultLength);
  				
  				if (!NT_SUCCESS(status) && (status != STATUS_BUFFER_OVERFLOW)) {
  					IF_LOUD(DbgPrint("\n\nStatus of %x querying key value for size\n", status);)
  				}
  				else {                      // We know how big it needs to be.
  					ULONG valueInfoLength = valueInfo.DataLength + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]);
  					PKEY_VALUE_PARTIAL_INFORMATION valueInfoP =	(PKEY_VALUE_PARTIAL_INFORMATION) ExAllocatePoolWithTag(PagedPool, valueInfoLength, '1PWA');
  					if (valueInfoP != NULL) {
  						status = ZwQueryValueKey(ExportKeyHandle, &FinalExportKey,
  							KeyValuePartialInformation,
  							valueInfoP,
  							valueInfoLength, &resultLength);
  						if (!NT_SUCCESS(status)) {
  							IF_LOUD(DbgPrint("Status of %x querying key value\n", status);)
  						}
  						else{
  							IF_LOUD(DbgPrint("Device %d = %ws\n", i, valueInfoP->Data);)
  								if( BufPos + valueInfoP->DataLength > BufLen ) {
  									// double the buffer size
  									PWCHAR DeviceNames2 = (PWCHAR) ExAllocatePoolWithTag(PagedPool, BufLen
  										<< 1, '0PWA');
  									if( DeviceNames2 ) {
  										RtlCopyMemory((PCHAR)DeviceNames2, (PCHAR)DeviceNames, BufLen);
  										BufLen <<= 1;
  										ExFreePool(DeviceNames);
  										DeviceNames = DeviceNames2;
  									}
  								} 
  								if( BufPos + valueInfoP->DataLength < BufLen ) {
  									RtlCopyMemory((PCHAR)DeviceNames+BufPos,
  										valueInfoP->Data,
  										valueInfoP->DataLength);
  									BufPos+=valueInfoP->DataLength-2;
  								}
  						}
  						
  						ExFreePool(valueInfoP);
  					}
  					else {
  						IF_LOUD(DbgPrint("Error Allocating the buffer for the device name\n");)
  					}
  					
  				}
  				
  				// terminate the buffer
  				DeviceNames[BufPos/2]=0;
  				DeviceNames[BufPos/2+1]=0;
  				
  				ZwClose (ExportKeyHandle);
  				i++;
  				
  			}
  			
  			ZwClose (keyHandle);
  			
  	}
  	if(BufPos==0){
  		ExFreePool(DeviceNames);
  		return NULL;
  	}
  	return DeviceNames;
  }
  
  //-------------------------------------------------------------------
  
  PKEY_VALUE_PARTIAL_INFORMATION getTcpBindings(void)
  {
    PKEY_VALUE_PARTIAL_INFORMATION result = NULL;
    OBJECT_ATTRIBUTES objAttrs;
    NTSTATUS status;
    HANDLE keyHandle;
  
    InitializeObjectAttributes(&objAttrs, &tcpLinkageKeyName,
                               OBJ_CASE_INSENSITIVE, NULL, NULL);
    status = ZwOpenKey(&keyHandle, KEY_READ, &objAttrs);
    if (!NT_SUCCESS(status)) {
      IF_LOUD(DbgPrint("\n\nStatus of %x opening %ws\n", status, tcpLinkageKeyName.Buffer);)
    }
    else {
      ULONG resultLength;
      KEY_VALUE_PARTIAL_INFORMATION valueInfo;
  
      IF_LOUD(DbgPrint("\n\nOpened %ws\n", tcpLinkageKeyName.Buffer);)
  
      status = ZwQueryValueKey(keyHandle, &bindValueName,
                               KeyValuePartialInformation, &valueInfo,
                               sizeof(valueInfo), &resultLength);
      if (!NT_SUCCESS(status) && (status != STATUS_BUFFER_OVERFLOW)) {
        IF_LOUD(DbgPrint("\n\nStatus of %x querying key value for size\n", status);)
      }
      else {                      // We know how big it needs to be.
        ULONG valueInfoLength = valueInfo.DataLength + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]);
        PKEY_VALUE_PARTIAL_INFORMATION valueInfoP =
          (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePoolWithTag(PagedPool, valueInfoLength, '2PWA');
        
  	  if (valueInfoP != NULL) {
          status = ZwQueryValueKey(keyHandle, &bindValueName,
                                   KeyValuePartialInformation,
                                   valueInfoP,
                                   valueInfoLength, &resultLength);
        
  		if (!NT_SUCCESS(status)) {
            IF_LOUD(DbgPrint("\n\nStatus of %x querying key value\n", status);)
          }
          else if (valueInfoLength != resultLength) {
            IF_LOUD(DbgPrint("\n\nQuerying key value result len = %u "
                       "but previous len = %u\n",
                       resultLength, valueInfoLength);)
          }
          else if (valueInfoP->Type != REG_MULTI_SZ) {
            IF_LOUD(DbgPrint("\n\nTcpip bind value not REG_MULTI_SZ but %u\n",
                       valueInfoP->Type);)
          }
          else {                  // It's OK
  #if DBG
            ULONG i;
            WCHAR* dataP = (WCHAR*)(&valueInfoP->Data[0]);
            IF_LOUD(DbgPrint("\n\nBind value:\n");)
            for (i = 0; *dataP != UNICODE_NULL; i++) {
              UNICODE_STRING macName;
              RtlInitUnicodeString(&macName, dataP);
              IF_LOUD(DbgPrint("\n\nMac %u = %ws\n", i, macName.Buffer);)
              dataP +=
                (macName.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR);
            }
  #endif // DBG
            result = valueInfoP;
          }
        }
      }
      ZwClose(keyHandle);
    }
    return result;
  }
  
  //-------------------------------------------------------------------
  
  BOOLEAN createDevice(IN OUT PDRIVER_OBJECT adriverObjectP,
  					 IN PUNICODE_STRING amacNameP, NDIS_HANDLE aProtoHandle)
  {
  	NTSTATUS status;
  	PDEVICE_OBJECT devObjP;
  	UNICODE_STRING deviceName;
  	UNICODE_STRING deviceSymLink;
  
  	IF_LOUD(DbgPrint("\n\ncreateDevice for MAC %ws\n", amacNameP->Buffer););
  	if (RtlCompareMemory(amacNameP->Buffer, devicePrefix.Buffer,
  		devicePrefix.Length) < devicePrefix.Length) 
  	{
  		return FALSE;
  	}
  
  	deviceName.Length = 0;
  	deviceName.MaximumLength = (USHORT)(amacNameP->Length + NPF_Prefix.Length + sizeof(UNICODE_NULL));
  	deviceName.Buffer = ExAllocatePoolWithTag(PagedPool, deviceName.MaximumLength, '3PWA');
  
  	if (deviceName.Buffer == NULL)
  		return FALSE;
  
  	deviceSymLink.Length = 0;
  	deviceSymLink.MaximumLength =(USHORT)(amacNameP->Length-devicePrefix.Length 
  		+ symbolicLinkPrefix.Length 
  		+ NPF_Prefix.Length 
  		+ sizeof(UNICODE_NULL));
  
  	deviceSymLink.Buffer = ExAllocatePoolWithTag(NonPagedPool, deviceSymLink.MaximumLength, '3PWA');
  
  	if (deviceSymLink.Buffer  == NULL)
  	{
  		ExFreePool(deviceName.Buffer);
  		return FALSE;
  	}
  
  	RtlAppendUnicodeStringToString(&deviceName, &devicePrefix);
  	RtlAppendUnicodeStringToString(&deviceName, &NPF_Prefix);
  	RtlAppendUnicodeToString(&deviceName, amacNameP->Buffer +
  		devicePrefix.Length / sizeof(WCHAR));
  
  	RtlAppendUnicodeStringToString(&deviceSymLink, &symbolicLinkPrefix);
  	RtlAppendUnicodeStringToString(&deviceSymLink, &NPF_Prefix);
  	RtlAppendUnicodeToString(&deviceSymLink, amacNameP->Buffer +
  		devicePrefix.Length / sizeof(WCHAR));
  
  	IF_LOUD(DbgPrint("Creating device name: %ws\n", deviceName.Buffer);)
  
  		status = IoCreateDevice(adriverObjectP, 
  		sizeof(DEVICE_EXTENSION),
  		&deviceName, 
  		FILE_DEVICE_TRANSPORT, 
  		0, 
  		FALSE,
  		&devObjP);
  
  	if (NT_SUCCESS(status)) 
  	{
  		PDEVICE_EXTENSION devExtP = (PDEVICE_EXTENSION)devObjP->DeviceExtension;
  		
  		IF_LOUD(DbgPrint("Device created successfully\n"););
  
  		devObjP->Flags |= DO_DIRECT_IO;
  		RtlInitUnicodeString(&devExtP->AdapterName,amacNameP->Buffer);   
  		devExtP->NdisProtocolHandle=aProtoHandle;
  
  		IF_LOUD(DbgPrint("Trying to create SymLink %ws\n",deviceSymLink.Buffer););
  
  		if (IoCreateSymbolicLink(&deviceSymLink,&deviceName) != STATUS_SUCCESS)
  		{
  			IF_LOUD(DbgPrint("\n\nError creating SymLink %ws\nn", deviceSymLink.Buffer););
  
  			ExFreePool(deviceName.Buffer);
  			ExFreePool(deviceSymLink.Buffer);
  
  			devExtP->ExportString = NULL;
  
  			return FALSE;
  		}
  
  		IF_LOUD(DbgPrint("SymLink %ws successfully created.\n\n", deviceSymLink.Buffer););
  
  		devExtP->ExportString = deviceSymLink.Buffer;
  
  		ExFreePool(deviceName.Buffer);
  
  		return TRUE;
  	}
  
  	else 
  	{
  		IF_LOUD(DbgPrint("\n\nIoCreateDevice status = %x\n", status););
  
  		ExFreePool(deviceName.Buffer);
  		ExFreePool(deviceSymLink.Buffer);
  		
  		return FALSE;
  	}
  }
  //-------------------------------------------------------------------
  
  VOID NPF_Unload(IN PDRIVER_OBJECT DriverObject)
  {
  	PDEVICE_OBJECT     DeviceObject;
  	PDEVICE_OBJECT     OldDeviceObject;
  	PDEVICE_EXTENSION  DeviceExtension;
  
  	NDIS_HANDLE        NdisProtocolHandle = NULL;
  	NDIS_STATUS        Status;
  
  	NDIS_STRING		   SymLink;
  
  	IF_LOUD(DbgPrint("NPF: Unload\n"););
  
  	DeviceObject    = DriverObject->DeviceObject;
  
  	while (DeviceObject != NULL) {
  		OldDeviceObject = DeviceObject;
  
  		DeviceObject = DeviceObject->NextDevice;
  
  		DeviceExtension = OldDeviceObject->DeviceExtension;
  
  		NdisProtocolHandle=DeviceExtension->NdisProtocolHandle;
  
  		IF_LOUD(DbgPrint("Deleting Adapter %ws, Protocol Handle=%x, Device Obj=%x (%x)\n",
  			DeviceExtension->AdapterName.Buffer,
  			NdisProtocolHandle,
  			DeviceObject,
  			OldDeviceObject););
  
  		if (DeviceExtension->ExportString)
  		{
  			RtlInitUnicodeString(&SymLink , DeviceExtension->ExportString);
  
  			IF_LOUD(DbgPrint("Deleting SymLink at %p\n", SymLink.Buffer););
  
  			IoDeleteSymbolicLink(&SymLink);
  			ExFreePool(DeviceExtension->ExportString);
  		}
  
  		IoDeleteDevice(OldDeviceObject);
  	}
  
  	NdisDeregisterProtocol(
  		&Status,
  		NdisProtocolHandle
  		);
  
  	// Free the adapters names
  	ExFreePool( bindP );
  }
  
  #define SET_FAILURE_BUFFER_SMALL() do{\
  	Information = 0; \
  	Status = STATUS_BUFFER_TOO_SMALL; \
  } while(FALSE)
  
  #define SET_RESULT_SUCCESS(__a__) do{\
  	Information = __a__;	\
  	Status = STATUS_SUCCESS;	\
  } while(FALSE)
  
  #define SET_FAILURE_INVALID_REQUEST() do{\
  	Information = 0; \
  	Status = STATUS_INVALID_DEVICE_REQUEST; \
  } while(FALSE)
  
  #define SET_FAILURE_UNSUCCESSFUL()  do{\
  	Information = 0; \
  	Status = STATUS_UNSUCCESSFUL; \
  } while(FALSE)
  
  #define SET_FAILURE_NOMEM()  do{\
  	Information = 0; \
  	Status = STATUS_INSUFFICIENT_RESOURCES; \
  } while(FALSE)
  
  
  //-------------------------------------------------------------------
  
  NTSTATUS NPF_IoControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
  {
      POPEN_INSTANCE      Open;
      PIO_STACK_LOCATION  IrpSp;
      PLIST_ENTRY         RequestListEntry;
      PINTERNAL_REQUEST   pRequest;
      ULONG               FunctionCode;
      NDIS_STATUS	        Status;
  	UINT				i;
  	PUCHAR				tpointer;
  	ULONG				dim,timeout;
  	PUCHAR				prog;
  	PPACKET_OID_DATA    OidData;
  	ULONG				mode;
  //	PWSTR				DumpNameBuff;
  	PUCHAR				TmpBPFProgram;
  	INT					WriteRes;
  	BOOLEAN				SyncWrite = FALSE;
  //	struct bpf_insn		*initprogram;
  	ULONG				insns;
  	ULONG				cnt;
  	BOOLEAN				IsExtendedFilter=FALSE;
  
  	BOOLEAN				Flag;
  	PUINT				pStats;
  	ULONG				Information = 0;
  
  	BOOLEAN check_ok;
  
      IF_LOUD(DbgPrint("NPF: IoControl\n");)
  		
  	IrpSp = IoGetCurrentIrpStackLocation(Irp);
      FunctionCode=IrpSp->Parameters.DeviceIoControl.IoControlCode;
      Open=IrpSp->FileObject->FsContext;
  
      Irp->IoStatus.Status = STATUS_SUCCESS;
  
  	IF_LOUD(DbgPrint("NPF: Function code is %08lx  buff size=%08lx  %08lx\n",FunctionCode,IrpSp->Parameters.DeviceIoControl.InputBufferLength,IrpSp->Parameters.DeviceIoControl.OutputBufferLength);)
  
  	switch (FunctionCode){
  		
  	case BIOCGSTATS: //function to get the capture stats
  		
  		if(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < 4*sizeof(UINT)){			
  			EXIT_FAILURE(0);
  		}
  
  		check_ok = TRUE;
  		__try
  		{
  			ProbeForWrite(Irp->UserBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, 1);
  		}
  		__except (EXCEPTION_EXECUTE_HANDLER)
  		{
  			check_ok = FALSE;
  		}
  
  		if (check_ok == FALSE)
  		{
  			EXIT_FAILURE(0);
  		}
  		else
  		{
  			pStats = (PUINT)(Irp->UserBuffer);
  
  			pStats[3] = 0;
  			pStats[0] = 0;
  			pStats[1] = 0;
  			pStats[2] = 0;		// Not yet supported
  
  			for(i = 0 ; i < NCpu ; i++)
  			{
  
  				pStats[3] += Open->CpuData[i].Accepted;
  				pStats[0] += Open->CpuData[i].Received;
  				pStats[1] += Open->CpuData[i].Dropped;
  				pStats[2] += 0;		// Not yet supported
  			}
  			EXIT_SUCCESS(4*sizeof(UINT));
  		}
  		
  		break;
  		
  	case BIOCGEVNAME: //function to get the name of the event associated with the current instance
  
  		if(IrpSp->Parameters.DeviceIoControl.OutputBufferLength<26){			
  			EXIT_FAILURE(0);
  		}
  
  		check_ok = TRUE;
  		__try
  		{
  			ProbeForWrite(Irp->UserBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, 1);
  		}
  		__except (EXCEPTION_EXECUTE_HANDLER)
  		{
  			check_ok = FALSE;
  		}
  
  		if (check_ok == FALSE)
  		{
  			EXIT_FAILURE(0);
  		}
  		else
  		{
  			RtlCopyMemory(Irp->UserBuffer,(Open->ReadEventName.Buffer)+18,26);
  
  			EXIT_SUCCESS(26);
  		}
  
  		break;
  
  	case BIOCSENDPACKETSSYNC:
  
  		SyncWrite = TRUE;
  
  	case BIOCSENDPACKETSNOSYNC:
  		
  		NdisAcquireSpinLock(&Open->WriteLock);
  		if(Open->WriteInProgress)
  		{
  			// Another write operation is currently in progress
  			EXIT_FAILURE(0);
  		}
  		else
  		{
  			Open->WriteInProgress = TRUE;
  		}
  		NdisReleaseSpinLock(&Open->WriteLock);
  		
  		WriteRes = NPF_BufferedWrite(Irp,
  			(PUCHAR)Irp->AssociatedIrp.SystemBuffer,
  			IrpSp->Parameters.DeviceIoControl.InputBufferLength,
  			SyncWrite);
  
  		NdisAcquireSpinLock(&Open->WriteLock);
  		Open->WriteInProgress = FALSE;
  		NdisReleaseSpinLock(&Open->WriteLock);
  
  		if( WriteRes != -1)
  		{
  			EXIT_SUCCESS(WriteRes);
  		}
  		
  		EXIT_FAILURE(WriteRes);
  
  		break;
  
  	case BIOCSETF:  
  
  		Open->SkipProcessing = 1;
  
  		do
  		{
  			Flag = FALSE;
  			for(i = 0; i < NCpu ; i++)
  				if (Open->CpuData[i].Processing == 1)
  					Flag = TRUE;
  		}
  		while(Flag);  //BUSY FORM WAITING...
  
  
  		// Free the previous buffer if it was present
  		if(Open->bpfprogram != NULL){
  			TmpBPFProgram = Open->bpfprogram;
  			Open->bpfprogram = NULL;
  			ExFreePool(TmpBPFProgram);
  		}
  		
  //
  // Jitted filters are supported on x86 (32bit) only
  // 
  #ifdef __NPF_x86__
  		if (Open->Filter != NULL)
  		{
  			JIT_BPF_Filter *OldFilter=Open->Filter;
  			Open->Filter=NULL;
  			BPF_Destroy_JIT_Filter(OldFilter);
  		}
  #endif // __NPF_x86__
  
  		// Get the pointer to the new program
  		prog=(PUCHAR)Irp->AssociatedIrp.SystemBuffer;
  		
  		if(prog==NULL)
  		{
  			Open->SkipProcessing = 0;
  			EXIT_FAILURE(0);
  		}
  		
  		insns = (IrpSp->Parameters.DeviceIoControl.InputBufferLength)/sizeof(struct bpf_insn);
  		
  		//count the number of operative instructions
  		for (cnt=0;(cnt<insns) &&(((struct bpf_insn*)prog)[cnt].code!=BPF_SEPARATION); cnt++);
  		
  		IF_LOUD(DbgPrint("Operative instructions=%u\n",cnt);)
  
  #ifdef __NPF_x86__
  		if ( cnt != insns && insns != cnt+1 && ((struct bpf_insn*)prog)[cnt].code == BPF_SEPARATION ) 
  		{
  			IF_LOUD(DbgPrint("Initialization instructions=%u\n",insns-cnt-1);)
  	
  			IsExtendedFilter=TRUE;
  
  			initprogram=&((struct bpf_insn*)prog)[cnt+1];
  			
  			if(bpf_filter_init(initprogram,&(Open->mem_ex),&(Open->tme), &G_Start_Time)!=INIT_OK)
  			{
  			
  				IF_LOUD(DbgPrint("Error initializing NPF machine (bpf_filter_init)\n");)
  				
  				Open->SkipProcessing = 0;
  				EXIT_FAILURE(0);
  			}
  		}
  #else  //x86-64 and IA64
  		if ( cnt != insns)
  		{
  			IF_LOUD(DbgPrint("Error installing the BPF filter. The filter contains TME extensions,"
  				" not supported on 64bit platforms.\n");)
  
  			Open->SkipProcessing = 0;
  			EXIT_FAILURE(0);
  		}
  
  
  #endif
  
  		//the NPF processor has been initialized, we have to validate the operative instructions
  		insns = cnt;
  		
  		//NOTE: the validation code checks for TME instructions, and fails if a TME instruction is
  		//encountered on 64 bit machines
  		if(bpf_validate((struct bpf_insn*)prog,cnt,Open->mem_ex.size)==0)
  		{
  			IF_LOUD(DbgPrint("Error validating program");)
  			//FIXME: the machine has been initialized(?), but the operative code is wrong. 
  			//we have to reset the machine!
  			//something like: reallocate the mem_ex, and reset the tme_core
  			Open->SkipProcessing = 0;
  			EXIT_FAILURE(0);
  		}
  		
  		// Allocate the memory to contain the new filter program
  		// We could need the original BPF binary if we are forced to use bpf_filter_with_2_buffers()
  		TmpBPFProgram = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, cnt*sizeof(struct bpf_insn), '4PWA');
  		if (TmpBPFProgram == NULL)
  		{
  			IF_LOUD(DbgPrint("Error - No memory for filter");)
  			// no memory
  			Open->SkipProcessing = 0;
  			EXIT_FAILURE(0);
  		}
  		
  		//copy the program in the new buffer
  		RtlCopyMemory(TmpBPFProgram,prog,cnt*sizeof(struct bpf_insn));
  		Open->bpfprogram=TmpBPFProgram;
  		
  		//
  		// At the moment the JIT compiler works on x86 (32 bit) only
  		//
  #ifdef __NPF_x86__
  		// Create the new JIT filter function
  		if(!IsExtendedFilter)
  			if((Open->Filter=BPF_jitter((struct bpf_insn*)Open->bpfprogram,cnt)) == NULL)
  			{
  				IF_LOUD(DbgPrint("Error jittering filter");)
  				Open->SkipProcessing = 0;
  				EXIT_FAILURE(0);
  			}
  #endif
  
  		//return
  		for (i = 0 ; i < NCpu ; i++)
  		{
  			Open->CpuData[i].C=0;
  			Open->CpuData[i].P=0;
  			Open->CpuData[i].Free = Open->Size;
  			Open->CpuData[i].Accepted=0;
  			Open->CpuData[i].Dropped=0;
  			Open->CpuData[i].Received = 0;
  		}
  
  		Open->ReaderSN=0;
  		Open->WriterSN=0;
  
  		Open->SkipProcessing = 0;
  		EXIT_SUCCESS(IrpSp->Parameters.DeviceIoControl.InputBufferLength);
  		
  		break;		
  		
  	case BIOCSMODE:  //set the capture mode
  		
  		if(IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
  		{			
  			EXIT_FAILURE(0);
  		}
  
  		mode=*((PULONG)Irp->AssociatedIrp.SystemBuffer);
  		
  ///////kernel dump does not work at the moment//////////////////////////////////////////
  		if (mode & MODE_DUMP)
  		{			
  			EXIT_FAILURE(0);
  		}
  ///////kernel dump does not work at the moment//////////////////////////////////////////
  
  		if(mode == MODE_CAPT)
  		{
  			Open->mode = MODE_CAPT;
  			
  			EXIT_SUCCESS(0);
  		}
   		else if (mode == MODE_MON)
  		{
  //
  // The MONITOR_MODE (aka TME extensions) is not supported on 
  // 64 bit architectures
  //
  #ifdef __NPF_x86__
  			Open->mode = MODE_MON;
  			EXIT_SUCCESS(0);
  #else // _NPF_x86__
  			EXIT_FAILURE(0);
  #endif // __NPF_x86__
  		
  		}	
  		else{
  			if(mode & MODE_STAT){
  				Open->mode = MODE_STAT;
  				NdisAcquireSpinLock(&Open->CountersLock);
  				Open->Nbytes.QuadPart = 0;
  				Open->Npackets.QuadPart = 0;
  				NdisReleaseSpinLock(&Open->CountersLock);
  				
  				if(Open->TimeOut.QuadPart==0)Open->TimeOut.QuadPart = -10000000;
  				
  			}
  			
  			if(mode & MODE_DUMP){
  				
  				Open->mode |= MODE_DUMP;
  //				Open->MinToCopy=(Open->BufSize<2000000)?Open->BufSize/2:1000000;
  				
  			}	
  			EXIT_SUCCESS(0);
  		}
  		
  		EXIT_FAILURE(0);
  		
  		break;
  
  	case BIOCSETDUMPFILENAME:
  
  ///////kernel dump does not work at the moment//////////////////////////////////////////
  		EXIT_FAILURE(0);
  ///////kernel dump does not work at the moment//////////////////////////////////////////
  
  //
  // Remove the following #if 0 to enable the kernel dump again
  //
  #if 0
  		if(Open->mode & MODE_DUMP)
  		{
  			
  			// Close current dump file
  			if(Open->DumpFileHandle != NULL)
  			{
  				NPF_CloseDumpFile(Open);
  				Open->DumpFileHandle = NULL;
  			}
  			
  			if(IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0){
  				EXIT_FAILURE(0);
  			}
  			
  			// Allocate the buffer that will contain the string
  			DumpNameBuff=ExAllocatePoolWithTag(NonPagedPool, IrpSp->Parameters.DeviceIoControl.InputBufferLength, '5PWA');
  			if(DumpNameBuff==NULL || Open->DumpFileName.Buffer!=NULL){
  				IF_LOUD(DbgPrint("NPF: unable to allocate the dump filename: not enough memory or name already set\n");)
  					EXIT_FAILURE(0);
  			}
  			
  			// Copy the buffer
  			RtlCopyBytes((PVOID)DumpNameBuff, 
  				Irp->AssociatedIrp.SystemBuffer, 
  				IrpSp->Parameters.DeviceIoControl.InputBufferLength);
  			
  			// Force a \0 at the end of the filename to avoid that malformed strings cause RtlInitUnicodeString to crash the system 
  			((PSHORT)DumpNameBuff)[IrpSp->Parameters.DeviceIoControl.InputBufferLength/2-1]=0;
  			
  			// Create the unicode string
  			RtlInitUnicodeString(&Open->DumpFileName, DumpNameBuff);
  			
  			IF_LOUD(DbgPrint("NPF: dump file name set to %ws, len=%d\n",
  				Open->DumpFileName.Buffer,
  				IrpSp->Parameters.DeviceIoControl.InputBufferLength);)
  				
  			// Try to create the file
  			if ( NT_SUCCESS( NPF_OpenDumpFile(Open,&Open->DumpFileName,FALSE)) &&
  				NT_SUCCESS( NPF_StartDump(Open)))
  			{
  				EXIT_SUCCESS(0);
  			}
  		}
  		
  		EXIT_FAILURE(0);
  		
  		break;
  #endif // #if 0				
  	case BIOCSETDUMPLIMITS:
  
  ///////kernel dump does not work at the moment//////////////////////////////////////////
  		EXIT_FAILURE(0);
  ///////kernel dump does not work at the moment//////////////////////////////////////////
  
  //
  // Remove the following #if 0 to enable the kernel dump again
  //
  #if 0
  		if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < 2*sizeof(ULONG))
  		{
  			EXIT_FAILURE(0);
  		}
  
  		Open->MaxDumpBytes = *(PULONG)Irp->AssociatedIrp.SystemBuffer;
  		Open->MaxDumpPacks = *((PULONG)Irp->AssociatedIrp.SystemBuffer + 1);
  
  		IF_LOUD(DbgPrint("NPF: Set dump limits to %u bytes, %u packs\n", Open->MaxDumpBytes, Open->MaxDumpPacks);)
  
  		EXIT_SUCCESS(0);
  
  		break;
  
  #endif // #if 0
  
  	case BIOCISDUMPENDED:
  
  ///////kernel dump does not work at the moment//////////////////////////////////////////
  		EXIT_FAILURE(0);
  ///////kernel dump does not work at the moment//////////////////////////////////////////
  
  //
  // Remove the following #if 0 to enable the kernel dump again
  //
  #if 0
  		if(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(UINT))
  		{			
  			EXIT_FAILURE(0);
  		}
  
  		*((UINT*)Irp->UserBuffer) = (Open->DumpLimitReached)?1:0;
  
  		EXIT_SUCCESS(4);
  
  		break;
  
  #endif // #if 0
  
  	case BIOCSETBUFFERSIZE:
  		
  
  		if(IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
  		{			
  			EXIT_FAILURE(0);
  		}
  
  		// Get the number of bytes to allocate
  		dim = *((PULONG)Irp->AssociatedIrp.SystemBuffer);
  
  		Open->SkipProcessing = 1;
  
  		do
  		{
  			Flag = FALSE;
  			for(i=0;i<NCpu;i++)
  				if (Open->CpuData[i].Processing == 1)
  					Flag = TRUE;
  		}
  		while(Flag);  //BUSY FORM WAITING...
  
  		if (dim / NCpu < sizeof(struct PacketHeader))
  			dim = 0;
  		else
  		{
  			tpointer = ExAllocatePoolWithTag(NonPagedPool, dim, '6PWA');
  			if (tpointer==NULL)
  			{
  				// no memory
  				Open->SkipProcessing = 0;
  				EXIT_FAILURE(0);
  			}
  		}
  
  		if (Open->CpuData[0].Buffer != NULL)
  			ExFreePool(Open->CpuData[0].Buffer);
  
  		for (i = 0 ; i < NCpu ; i++)
  		{
  			if (dim > 0) 
  				Open->CpuData[i].Buffer=(PUCHAR)tpointer + (dim/NCpu)*i;
  			else
  				Open->CpuData[i].Buffer = NULL;
  			Open->CpuData[i].Free = dim/NCpu;
  			Open->CpuData[i].P = 0;
  			Open->CpuData[i].C = 0;
  			Open->CpuData[i].Accepted = 0;
  			Open->CpuData[i].Dropped = 0;
  			Open->CpuData[i].Received = 0;
  		}
  
  		Open->ReaderSN=0;
  		Open->WriterSN=0;
  
  		Open->Size = dim/NCpu;
      
  		Open->SkipProcessing = 0;
  		EXIT_SUCCESS(dim);
  		
  		break;
  		
  	case BIOCSRTIMEOUT: //set the timeout on the read calls
  		
  
  		if(IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
  		{			
  			EXIT_FAILURE(0);
  		}
  
  		timeout = *((PULONG)Irp->AssociatedIrp.SystemBuffer);
  		if(timeout == (ULONG)-1)
  			Open->TimeOut.QuadPart=(LONGLONG)IMMEDIATE;
  		else
  		{
  			Open->TimeOut.QuadPart = (LONGLONG)timeout;
  			Open->TimeOut.QuadPart *= 10000;
  			Open->TimeOut.QuadPart = -Open->TimeOut.QuadPart;
  		}
  
  		IF_LOUD(DbgPrint("NPF: read timeout set to %d:%d\n",Open->TimeOut.HighPart,Open->TimeOut.LowPart);)
  		EXIT_SUCCESS(timeout);
  		
  		break;
  		
  	case BIOCSWRITEREP: //set the writes repetition number
  		
  	if(IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
  	{			
  		EXIT_FAILURE(0);
  	}
  
  		Open->Nwrites = *((PULONG)Irp->AssociatedIrp.SystemBuffer);
  		
  		EXIT_SUCCESS(Open->Nwrites);
  		
  		break;
  
  	case BIOCSMINTOCOPY: //set the minimum buffer's size to copy to the application
  
  		if(IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
  		{			
  			EXIT_FAILURE(0);
  		}
  
  		Open->MinToCopy = (*((PULONG)Irp->AssociatedIrp.SystemBuffer))/NCpu;  //An hack to make the NCPU-buffers behave like a larger one
  		
  		EXIT_SUCCESS(Open->MinToCopy);
  		
  		break;
  		
  	case IOCTL_PROTOCOL_RESET:
  		
          IF_LOUD(DbgPrint("NPF: IoControl - Reset request\n");)
  
  		IoMarkIrpPending(Irp);
  		Irp->IoStatus.Status = STATUS_SUCCESS;
  
  		ExInterlockedInsertTailList(&Open->ResetIrpList,&Irp->Tail.Overlay.ListEntry,&Open->RequestSpinLock);
          NdisReset(&Status,Open->AdapterHandle);
          if (Status != NDIS_STATUS_PENDING)
          {
              IF_LOUD(DbgPrint("NPF: IoControl - ResetComplete being called\n");)
  				NPF_ResetComplete(Open,Status);
          }
  		
  		break;
  		
  		
  	case BIOCSETOID:
  	case BIOCQUERYOID:
  		
  		// Extract a request from the list of free ones
  		RequestListEntry=ExInterlockedRemoveHeadList(&Open->RequestList,&Open->RequestSpinLock);
  		if (RequestListEntry == NULL)
  		{
  			EXIT_FAILURE(0);
  		}
  
  		pRequest=CONTAINING_RECORD(RequestListEntry,INTERNAL_REQUEST,ListElement);
  		pRequest->Irp = Irp;
  		pRequest->Internal = FALSE;
  
          
  		//
          //  See if it is an Ndis request
          //
          OidData=Irp->AssociatedIrp.SystemBuffer;
  		
          if (((FunctionCode == BIOCSETOID) || (FunctionCode == BIOCQUERYOID))
              &&
              (IrpSp->Parameters.DeviceIoControl.InputBufferLength == IrpSp->Parameters.DeviceIoControl.OutputBufferLength)
              &&
              (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(PACKET_OID_DATA))
              &&
              (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(PACKET_OID_DATA)-1+OidData->Length)) {
  			
              IF_LOUD(DbgPrint("NPF: IoControl: Request: Oid=%08lx, Length=%08lx\n",OidData->Oid,OidData->Length);)
  				
  				//
  				//  The buffer is valid
  				//
  				if (FunctionCode == BIOCSETOID){
  					
  					pRequest->Request.RequestType=NdisRequestSetInformation;
  					pRequest->Request.DATA.SET_INFORMATION.Oid=OidData->Oid;
  					
  					pRequest->Request.DATA.SET_INFORMATION.InformationBuffer=OidData->Data;
  					pRequest->Request.DATA.SET_INFORMATION.InformationBufferLength=OidData->Length;
  					
  					
  				} 
  				else{
  								
  					pRequest->Request.RequestType=NdisRequestQueryInformation;
  					pRequest->Request.DATA.QUERY_INFORMATION.Oid=OidData->Oid;
  					
  					pRequest->Request.DATA.QUERY_INFORMATION.InformationBuffer=OidData->Data;
  					pRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength=OidData->Length;
  					
  				}
  
  				NdisResetEvent(&Open->IOEvent);
  				//
  				//  submit the request
  				//
  				NdisRequest(
  					&Status,
  					Open->AdapterHandle,
  					&pRequest->Request
  					);
  				
          } else {
              //
              //  buffer too small
              //
              Status=NDIS_STATUS_FAILURE;
              pRequest->Request.DATA.SET_INFORMATION.BytesRead=0;
              pRequest->Request.DATA.QUERY_INFORMATION.BytesWritten=0;
  			
          }
  		
          if (Status != NDIS_STATUS_PENDING) {
              IF_LOUD(DbgPrint("NPF: Calling RequestCompleteHandler\n");)
  				
  			NPF_RequestComplete(Open, &pRequest->Request, Status);
              return Status;
  			
          }
  
  		NdisWaitEvent(&Open->IOEvent, 5000);
  
  		return(Open->IOStatus);
  		
  		break;
  
  	case BIOCISETLOBBEH:
  
  		if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(INT))
  		{
  			SET_FAILURE_BUFFER_SMALL();
  			break;
  		}
  
  #ifdef __NPF_NT4__
  
  		// NT4 doesn't support loopback inhibition / activation
  		SET_FAILURE_INVALID_REQUEST();
  		break;
  
  #else //not __NPF_NT4__
  		//
  		// win2000/xp/2003/vista
  		//
  		if(*(PINT)Irp->AssociatedIrp.SystemBuffer == 1)
  		{
  			Open->SkipSentPackets = TRUE;
  
  			//
  			// Reset the capture buffers, since they could contain loopbacked packets
  			//
  
  //			NPF_ResetBufferContents(Open);
  
  			SET_RESULT_SUCCESS(0);
  			break;
  
  		}
  		else
  			if(*(PINT)Irp->AssociatedIrp.SystemBuffer == 2)
  			{
  				Open->SkipSentPackets = FALSE;
  
  				SET_RESULT_SUCCESS(0);
  				break;
  			}
  			else
  			{
  				// Unknown operation
  				SET_FAILURE_INVALID_REQUEST();
  				break;
  			}
  
  #endif // !__NPF_NT4__
  			break;
  
  		
  		
  	default:
  		
  		EXIT_FAILURE(0);
  	}
  	
  	if (FunctionCode == BIOCISETLOBBEH)
  	{
  		Irp->IoStatus.Information = Information;
  		Irp->IoStatus.Status = Status;
  		IoCompleteRequest(Irp, IO_NO_INCREMENT);
  	}
  
  	return Status;
  }
  
  //-------------------------------------------------------------------
  
  VOID
  NPF_RequestComplete(
      IN NDIS_HANDLE   ProtocolBindingContext,
      IN PNDIS_REQUEST NdisRequest,
      IN NDIS_STATUS   Status
      )
  
  {
      POPEN_INSTANCE      Open;
      PIO_STACK_LOCATION  IrpSp;
      PIRP                Irp;
      PINTERNAL_REQUEST   pRequest;
      UINT                FunctionCode;
  //	KIRQL				OldIrq;
  
      PPACKET_OID_DATA    OidData;
  
      IF_LOUD(DbgPrint("NPF: RequestComplete\n");)
  
      Open= (POPEN_INSTANCE)ProtocolBindingContext;
  
      pRequest=CONTAINING_RECORD(NdisRequest,INTERNAL_REQUEST,Request);
      Irp=pRequest->Irp;
  
  	if(pRequest->Internal == TRUE){
  
  		// Put the request in the list of the free ones
  		ExInterlockedInsertTailList(&Open->RequestList, &pRequest->ListElement, &Open->RequestSpinLock);
  
  	    if(Status != NDIS_STATUS_SUCCESS)
  			Open->MaxFrameSize = 1600;	// Assume Ethernet
  
  		// We always return success, because the adapter has been already opened
  		Irp->IoStatus.Status = NDIS_STATUS_SUCCESS;
  		Irp->IoStatus.Information = 0;
  		IoCompleteRequest(Irp, IO_NO_INCREMENT);
  		return;
  	}
  
      IrpSp = IoGetCurrentIrpStackLocation(Irp);
  
      FunctionCode=IrpSp->Parameters.DeviceIoControl.IoControlCode;
  
      OidData=Irp->AssociatedIrp.SystemBuffer;
  
      if (FunctionCode == BIOCSETOID) {
  
          OidData->Length=pRequest->Request.DATA.SET_INFORMATION.BytesRead;
  
      } else {
  
          if (FunctionCode == BIOCQUERYOID) {
  
              OidData->Length=pRequest->Request.DATA.QUERY_INFORMATION.BytesWritten;
  
  		    IF_LOUD(DbgPrint("RequestComplete: BytesWritten=%d\n",pRequest->Request.DATA.QUERY_INFORMATION.BytesWritten);)
          }
  
      }
  
      Irp->IoStatus.Information=IrpSp->Parameters.DeviceIoControl.InputBufferLength;
  
      IF_LOUD(DbgPrint("RequestComplete: BytesReturned=%d\n",IrpSp->Parameters.DeviceIoControl.InputBufferLength);)
  
      ExInterlockedInsertTailList(
          &Open->RequestList,
          &pRequest->ListElement,
          &Open->RequestSpinLock);
  
      Irp->IoStatus.Status = Status;
  
  	Open->IOStatus = Status;
  
  	IoCompleteRequest(Irp, IO_NO_INCREMENT);
  
  	// Unlock the caller
  	NdisSetEvent(&Open->IOEvent);
  
      return;
  
  
  }
  
  //-------------------------------------------------------------------
  
  VOID
  NPF_Status(
      IN NDIS_HANDLE   ProtocolBindingContext,
      IN NDIS_STATUS   Status,
      IN PVOID         StatusBuffer,
      IN UINT          StatusBufferSize
      )
  
  {
  
      IF_LOUD(DbgPrint("NPF: Status Indication\n");)
  
      return;
  
  }
  
  //-------------------------------------------------------------------
  
  VOID
  NPF_StatusComplete(
      IN NDIS_HANDLE  ProtocolBindingContext
      )
  
  {
  
      IF_LOUD(DbgPrint("NPF: StatusIndicationComplete\n");)
  
      return;
  
  }
  
  //-------------------------------------------------------------------
  
  NTSTATUS
  NPF_ReadRegistry(
      IN  PWSTR              *MacDriverName,
      IN  PWSTR              *PacketDriverName,
      IN  PUNICODE_STRING     RegistryPath
      )
  
  {
      NTSTATUS   Status;
  
      RTL_QUERY_REGISTRY_TABLE ParamTable[4];
  
      PWSTR      Bind       = L"Bind";
      PWSTR      Export     = L"Export";
      PWSTR      Parameters = L"Parameters";
      PWSTR      Linkage    = L"Linkage";
  
      PWCHAR     Path;
  
  
  
      Path=ExAllocatePoolWithTag(PagedPool, RegistryPath->Length+sizeof(WCHAR), '7PWA');
  
      if (Path == NULL) {
          return STATUS_INSUFFICIENT_RESOURCES;
      }
  
      RtlZeroMemory(
          Path,
          RegistryPath->Length+sizeof(WCHAR)
          );
  
      RtlCopyMemory(
          Path,
          RegistryPath->Buffer,
          RegistryPath->Length
          );
  
      IF_LOUD(DbgPrint("NPF: Reg path is %ws\n",RegistryPath->Buffer);)
  
      RtlZeroMemory(
          ParamTable,
          sizeof(ParamTable)
          );
  
  
  
      //
      //  change to the linkage key
      //
  
      ParamTable[0].QueryRoutine = NULL;
      ParamTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY;
      ParamTable[0].Name = Linkage;
  
  
      //
      //  Get the name of the mac driver we should bind to
      //
  
      ParamTable[1].QueryRoutine = NPF_QueryRegistryRoutine;
      ParamTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED |
                            RTL_QUERY_REGISTRY_NOEXPAND;
  
      ParamTable[1].Name = Bind;
      ParamTable[1].EntryContext = (PVOID)MacDriverName;
      ParamTable[1].DefaultType = REG_MULTI_SZ;
  
      //
      //  Get the name that we should use for the driver object
      //
  
      ParamTable[2].QueryRoutine = NPF_QueryRegistryRoutine;
      ParamTable[2].Flags = RTL_QUERY_REGISTRY_REQUIRED |
                            RTL_QUERY_REGISTRY_NOEXPAND;
  
      ParamTable[2].Name = Export;
      ParamTable[2].EntryContext = (PVOID)PacketDriverName;
      ParamTable[2].DefaultType = REG_MULTI_SZ;
  
  
      Status=RtlQueryRegistryValues(
                 RTL_REGISTRY_ABSOLUTE,
                 Path,
                 ParamTable,
                 NULL,
                 NULL
                 );
  
  
      ExFreePool(Path);
  
      return Status;
  }
  
  //-------------------------------------------------------------------
  
  NTSTATUS
  NPF_QueryRegistryRoutine(
      IN PWSTR     ValueName,
      IN ULONG     ValueType,
      IN PVOID     ValueData,
      IN ULONG     ValueLength,
      IN PVOID     Context,
      IN PVOID     EntryContext
      )
  
  {
  
      PUCHAR       Buffer;
  
      IF_LOUD(DbgPrint("Perf: QueryRegistryRoutine\n");)
  
      if (ValueType != REG_MULTI_SZ) {
  
          return STATUS_OBJECT_NAME_NOT_FOUND;
  
      }
  
      Buffer=ExAllocatePoolWithTag(NonPagedPool, ValueLength, '8PWA');
  
      if (Buffer==NULL) {
  
          return STATUS_INSUFFICIENT_RESOURCES;
  
      }
  
      RtlCopyMemory(
          Buffer,
          ValueData,
          ValueLength
          );
  
      *((PUCHAR *)EntryContext)=Buffer;
  
      return STATUS_SUCCESS;
  
  }
  