aboutsummaryrefslogblamecommitdiff
path: root/tap-win32/tapdrvr.c
blob: 12ccc0df4e88600ab2e1bcf76e821cc4c52d741c (plain) (tree)
1
2
3
4
5
6
7
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
  

                                                                 
  
                                                                        
  
                                                                           




                                                                         
























                                                                        

                                     

                                                        
                                

                                                                          

      







                            





                                                          
                                 








                                                          
                         
 
                       

                      
                  




                      
















































































































































































































                                                                                  





                                      







                                                              



                                   

                                            




































































                                                                       














                                                                          
                                      
       


                                                            





                                          










                                                                                                    



                                                         
                                                                                                                  

                                                


                                                                                
                 
                                                           


                                                 


          
                                                                                    
                                                                                            
             




                                                                                                                      
                 






                                                                                                      

                 
         






                                                                       
                                                         


                                     





                                                          
         
                                                          
             





                                                        

             
     
 





                                                          
         
                                                          
             
                                                  
                 

                                                                


                 
     
 
                   





                                                           
         
                                                          
             
                                                  
                 
                                          


                 
     

      






                                                          
         
                                                         
             
                                                                                                                      
                 

                                                                                     


                 
     
 
                                          











                                                                   


                                                                           


















                                                                             
                                                    








                                                       
                                                           




                                         




                                            












































                                                                               
                                            





































































































































































































































































































































































































































































































                                                                                   
                                                










































































































































































































































































































                                                                            

                                        



































                                                                          

                                                     
       
                     

                                                             





                                          

                                                                   
                         

                                                          




                                      



                                                                            







                                                                                                      



















                                                                                                       
                               



































                                                                                                         
                         



















                                                             

                                                   


















                                                                          
                                                  

















































































































































































































                                                                                    


                                                                                                         
                                                                                                   
      





                                                          


                                          

                                        


                                          





























                                                                                        













































                                                                                                              





                                                                          
                                           


                                                                 


                                                                                                  







                                                                                                 
                                          
 
                                                    
























































                                                                                         
                                                    


























































































































































































                                                                                         
                                                                                                   








                                                                                 











                                                                                          





















                                                                                              
                                                                                            









                                                                                  











                                                                                          














































































































































































                                                                                            
                                           




















































































































































































                                                                                                 


                                                     

                                                        
                                                   
 
                                                    
     
                                                                                                   














                                                                               

                                    














                                                                       

                                                               




















                                                                            
                                                           
































































                                                                           
                           
                           

                                 














                                                                            





















































































































                                                                                                      


                                                                        
/*
 *  TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
 *                         device functionality on Windows.
 *
 *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
 *
 *  This source code is Copyright (C) 2002-2008 OpenVPN Technologies, Inc.,
 *  and is released under the GPL version 2 (see below), however due
 *  to the extra costs of supporting Windows Vista, OpenVPN Solutions
 *  LLC reserves the right to change the terms of the TAP-Win32/TAP-Win64
 *  license for versions 9.1 and higher prior to the official release of
 *  OpenVPN 2.1.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2
 *  as published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program (see the file COPYING included with this
 *  distribution); if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

//======================================================
// This driver is designed to work on Win 2000 or higher
// versions of Windows.
//
// It is SMP-safe and handles NDIS 5 power management.
//
// By default we operate as a "tap" virtual ethernet
// 802.3 interface, but we can emulate a "tun"
// interface (point-to-point IPv4) through the
// TAP_IOCTL_CONFIG_POINT_TO_POINT or
// TAP_IOCTL_CONFIG_TUN ioctl.
//======================================================

#include "../../autodefs/defs.h"
#ifndef DDKVER_MAJOR
#error DDKVER_MAJOR must be defined as the major number of the DDK Version
#endif

#define NDIS_MINIPORT_DRIVER
#define BINARY_COMPATIBLE 0
#define NDIS50_MINIPORT 1
#define NDIS_WDM 0
#define NDIS50 1
#define NTSTRSAFE_LIB

// Debug info output
#define ALSO_DBGPRINT           1
#define DEBUGP_AT_DISPATCH      0

//========================================================
// Check for truncated IPv4 packets, log errors if found.
//========================================================
#define PACKET_TRUNCATION_CHECK 0

//========================================================
// EXPERIMENTAL -- Configure TAP device object to be
// accessible from non-administrative accounts, based
// on an advanced properties setting.
//
// Duplicates the functionality of OpenVPN's
// --allow-nonadmin directive.
//========================================================
#define ENABLE_NONADMIN 1

#if DDKVER_MAJOR < 5600
#include <ndis.h>
#include <ntstrsafe.h>
#include <ntddk.h>
#else
#include <ntifs.h>
#include <ndis.h>
#include <ntstrsafe.h>
#endif

#include "lock.h"
#include "constants.h"
#include "common.h"
#include "proto.h"
#include "error.h"
#include "endian.h"
#include "dhcp.h"
#include "types.h"
#include "prototypes.h"

#include "mem.c"
#include "macinfo.c"
#include "error.c"
#include "dhcp.c"
#include "instance.c"

#define IS_UP(ta) \
  ((ta)->m_InterfaceIsRunning && (ta)->m_Extension.m_TapIsRunning)

#define INCREMENT_STAT(s) ++(s)

#define NAME_BUFFER_SIZE 80

//========================================================
//                            Globals
//========================================================

NDIS_HANDLE g_NdisWrapperHandle;

const UINT g_SupportedOIDList[] = {
  OID_GEN_HARDWARE_STATUS,
  OID_GEN_MEDIA_SUPPORTED,
  OID_GEN_MEDIA_IN_USE,
  OID_GEN_MAXIMUM_LOOKAHEAD,
  OID_GEN_MAC_OPTIONS,
  OID_GEN_LINK_SPEED,
  OID_GEN_TRANSMIT_BLOCK_SIZE,
  OID_GEN_RECEIVE_BLOCK_SIZE,
  OID_GEN_VENDOR_DESCRIPTION,
  OID_GEN_DRIVER_VERSION,
  OID_GEN_XMIT_OK,
  OID_GEN_RCV_OK,
  OID_GEN_XMIT_ERROR,
  OID_GEN_RCV_ERROR,
  OID_802_3_PERMANENT_ADDRESS,
  OID_802_3_CURRENT_ADDRESS,
  OID_GEN_RCV_NO_BUFFER,
  OID_802_3_RCV_ERROR_ALIGNMENT,
  OID_802_3_XMIT_ONE_COLLISION,
  OID_802_3_XMIT_MORE_COLLISIONS,
  OID_802_3_MULTICAST_LIST,
  OID_802_3_MAXIMUM_LIST_SIZE,
  OID_GEN_VENDOR_ID,
  OID_GEN_CURRENT_LOOKAHEAD,
  OID_GEN_CURRENT_PACKET_FILTER,
  OID_GEN_PROTOCOL_OPTIONS,
  OID_GEN_MAXIMUM_TOTAL_SIZE,
  OID_GEN_TRANSMIT_BUFFER_SPACE,
  OID_GEN_RECEIVE_BUFFER_SPACE,
  OID_GEN_MAXIMUM_FRAME_SIZE,
  OID_GEN_VENDOR_DRIVER_VERSION,
  OID_GEN_MAXIMUM_SEND_PACKETS,
  OID_GEN_MEDIA_CONNECT_STATUS,
  OID_GEN_SUPPORTED_LIST
};

//============================================================
//                         Driver Entry
//============================================================
#pragma NDIS_INIT_FUNCTION (DriverEntry)

NTSTATUS
DriverEntry (IN PDRIVER_OBJECT p_DriverObject,
	     IN PUNICODE_STRING p_RegistryPath)
{
  NDIS_STATUS l_Status = NDIS_STATUS_FAILURE;
  NDIS_MINIPORT_CHARACTERISTICS *l_Properties = NULL;

  //========================================================
  // Notify NDIS that a new miniport driver is initializing.
  //========================================================

  NdisMInitializeWrapper (&g_NdisWrapperHandle,
			  p_DriverObject,
			  p_RegistryPath, NULL);

  //======================
  // Global initialization
  //======================

#if DBG
  MyDebugInit (10000); // Allocate debugging text space
#endif

  if (!InitInstanceList ())
    {
      DEBUGP (("[TAP] Allocation failed for adapter instance list\n"));
      goto cleanup;
    }

  //=======================================
  // Set and register miniport entry points
  //=======================================

  l_Properties = MemAlloc (sizeof (NDIS_MINIPORT_CHARACTERISTICS), TRUE);

  if (l_Properties == NULL)
    {
      DEBUGP (("[TAP] Allocation failed for miniport entry points\n"));
      goto cleanup;
    }

  l_Properties->MajorNdisVersion = TAP_NDIS_MAJOR_VERSION;
  l_Properties->MinorNdisVersion = TAP_NDIS_MINOR_VERSION;
  l_Properties->InitializeHandler = AdapterCreate;
  l_Properties->HaltHandler = AdapterHalt;
  l_Properties->ResetHandler = AdapterReset;               /* DISPATCH_LEVEL */
  l_Properties->TransferDataHandler = AdapterReceive;      /* DISPATCH_LEVEL */
  l_Properties->SendHandler = AdapterTransmit;             /* DISPATCH_LEVEL */
  l_Properties->QueryInformationHandler = AdapterQuery;    /* DISPATCH_LEVEL */
  l_Properties->SetInformationHandler = AdapterModify;     /* DISPATCH_LEVEL */

  switch (l_Status =
	  NdisMRegisterMiniport (g_NdisWrapperHandle, l_Properties,
				 sizeof (NDIS_MINIPORT_CHARACTERISTICS)))
    {
    case NDIS_STATUS_SUCCESS:
      {
	DEBUGP (("[TAP] version [%d.%d] %s %s registered miniport successfully\n",
		 TAP_DRIVER_MAJOR_VERSION,
		 TAP_DRIVER_MINOR_VERSION,
		 __DATE__,
		 __TIME__));
	DEBUGP (("Registry Path: '%S'\n", p_RegistryPath->Buffer));
	break;
      }

    case NDIS_STATUS_BAD_CHARACTERISTICS:
      {
	DEBUGP (("[TAP] Miniport characteristics were badly defined\n"));
	NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
	break;
      }

    case NDIS_STATUS_BAD_VERSION:
      {
	DEBUGP
	  (("[TAP] NDIS Version is wrong for the given characteristics\n"));
	NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
	break;
      }

    case NDIS_STATUS_RESOURCES:
      {
	DEBUGP (("[TAP] Insufficient resources\n"));
	NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
	break;
      }

    default:
    case NDIS_STATUS_FAILURE:
      {
	DEBUGP (("[TAP] Unknown fatal registration error\n"));
	NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
	break;
      }
    }

 cleanup:
  if (l_Properties)
    MemFree (l_Properties, sizeof (NDIS_MINIPORT_CHARACTERISTICS));

  if (l_Status == NDIS_STATUS_SUCCESS)
    NdisMRegisterUnloadHandler (g_NdisWrapperHandle, TapDriverUnload);
  else
    TapDriverUnload (p_DriverObject);

  return l_Status;
}

//============================================================
//                         Driver Unload
//============================================================
VOID 
TapDriverUnload (IN PDRIVER_OBJECT p_DriverObject)
{
  DEBUGP (("[TAP] version [%d.%d] %s %s unloaded, instances=%d, imbs=%d\n",
	   TAP_DRIVER_MAJOR_VERSION,
	   TAP_DRIVER_MINOR_VERSION,
	   __DATE__,
	   __TIME__,
	   NInstances(),
	   InstanceMaxBucketSize()));

  FreeInstanceList ();

  //==============================
  // Free debugging text space
  //==============================
#if DBG
  MyDebugFree ();
#endif
}

//==========================================================
//                            Adapter Initialization
//==========================================================
NDIS_STATUS AdapterCreate
(OUT PNDIS_STATUS p_ErrorStatus,
 OUT PUINT p_MediaIndex,
 IN PNDIS_MEDIUM p_Media,
 IN UINT p_MediaCount,
 IN NDIS_HANDLE p_AdapterHandle,
 IN NDIS_HANDLE p_ConfigurationHandle)
{
  TapAdapterPointer l_Adapter = NULL;

  NDIS_MEDIUM l_PreferredMedium = NdisMedium802_3; // Ethernet
  BOOLEAN l_MacFromRegistry = FALSE;
  UINT l_Index;
  NDIS_STATUS status;

#if ENABLE_NONADMIN
  BOOLEAN enable_non_admin = FALSE;
#endif

  DEBUGP (("[TAP] AdapterCreate called\n"));

  //====================================
  // Make sure adapter type is supported
  //====================================

  for (l_Index = 0;
       l_Index < p_MediaCount && p_Media[l_Index] != l_PreferredMedium;
       ++l_Index);

  if (l_Index == p_MediaCount)
    {
      DEBUGP (("[TAP] Unsupported adapter type [wanted: %d]\n",
	       l_PreferredMedium));
      return NDIS_STATUS_UNSUPPORTED_MEDIA;
    }

  *p_MediaIndex = l_Index;

  //=========================================
  // Allocate memory for TapAdapter structure
  //=========================================

  l_Adapter = MemAlloc (sizeof (TapAdapter), TRUE);

  if (l_Adapter == NULL)
    {
      DEBUGP (("[TAP] Couldn't allocate adapter memory\n"));
      return NDIS_STATUS_RESOURCES;
    }

  //==========================================
  // Inform the NDIS library about significant
  // features of our virtual NIC.
  //==========================================

  NdisMSetAttributesEx
    (p_AdapterHandle,
     (NDIS_HANDLE) l_Adapter,
     16,
     NDIS_ATTRIBUTE_DESERIALIZE
     | NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT
     | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT
     | NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND,
     NdisInterfaceInternal);

  //=====================================
  // Initialize simple Adapter parameters
  //=====================================

  l_Adapter->m_Lookahead = DEFAULT_PACKET_LOOKAHEAD;
  l_Adapter->m_Medium = l_PreferredMedium;
  l_Adapter->m_DeviceState = '?';
  l_Adapter->m_MiniportAdapterHandle = p_AdapterHandle;

  //==================================
  // Allocate spinlock for controlling
  // access to multicast address list.
  //==================================
  NdisAllocateSpinLock (&l_Adapter->m_MCLock);
  l_Adapter->m_MCLockAllocated = TRUE;

  //====================================================
  // Register a shutdown handler which will be called
  // on system restart/shutdown to halt our virtual NIC.
  //====================================================

  NdisMRegisterAdapterShutdownHandler (p_AdapterHandle, l_Adapter,
				       AdapterHalt);
  l_Adapter->m_RegisteredAdapterShutdownHandler = TRUE;

  //============================================
  // Get parameters from registry which were set
  // in the adapter advanced properties dialog.
  //============================================
  {
    NDIS_STATUS status;
    NDIS_HANDLE configHandle;
    NDIS_CONFIGURATION_PARAMETER *parm;

    // set defaults in case our registry query fails
    l_Adapter->m_MTU = ETHERNET_MTU;
    l_Adapter->m_MediaStateAlwaysConnected = FALSE;
    l_Adapter->m_MediaState = FALSE;

    NdisOpenConfiguration (&status, &configHandle, p_ConfigurationHandle);
    if (status != NDIS_STATUS_SUCCESS)
      {
	DEBUGP (("[TAP] Couldn't open adapter registry\n"));
	AdapterFreeResources (l_Adapter);
	return status;
      }

    //====================================
    // Allocate and construct adapter name
    //====================================
    {
      
      NDIS_STRING mkey = NDIS_STRING_CONST("MiniportName");
      NDIS_STRING vkey = NDIS_STRING_CONST("NdisVersion");
      NDIS_STATUS vstatus;
      NDIS_CONFIGURATION_PARAMETER *vparm;

      NdisReadConfiguration (&vstatus, &vparm, configHandle, &vkey, NdisParameterInteger);
      if (vstatus == NDIS_STATUS_SUCCESS)
	DEBUGP (("[TAP] NdisReadConfiguration NdisVersion=%X\n", vparm->ParameterData.IntegerData));

      NdisReadConfiguration (&status, &parm, configHandle, &mkey, NdisParameterString);
      if (status == NDIS_STATUS_SUCCESS)
	{
	  if (parm->ParameterType == NdisParameterString)
	    {
	      DEBUGP (("[TAP] NdisReadConfiguration (MiniportName=%S)\n", parm->ParameterData.StringData.Buffer));

	      if (RtlUnicodeStringToAnsiString (
						&l_Adapter->m_NameAnsi,
						&parm->ParameterData.StringData,
						TRUE) != STATUS_SUCCESS)
		{
		  DEBUGP (("[TAP] MiniportName failed\n"));
		  status = NDIS_STATUS_RESOURCES;
		}
	    }
	}
      else
	{
	  /* "MiniportName" is available only XP and above.  Not on Windows 2000. */
	  if (vstatus == NDIS_STATUS_SUCCESS && vparm->ParameterData.IntegerData == 0x50000)
	    {
	      /* Fallback for Windows 2000 with NDIS version 5.00.00
		 Don't use this on Vista, 'NDIS_MINIPORT_BLOCK' was changed! */
	      if (RtlUnicodeStringToAnsiString (&l_Adapter->m_NameAnsi,
						&((struct WIN2K_NDIS_MINIPORT_BLOCK *) p_AdapterHandle)->MiniportName,
						TRUE) != STATUS_SUCCESS)
		{
		  DEBUGP (("[TAP] MiniportName (W2K) failed\n"));
		  status = NDIS_STATUS_RESOURCES;
		}
	      else
		{
		  DEBUGP (("[TAP] MiniportName (W2K) succeeded: %s\n", l_Adapter->m_NameAnsi.Buffer));
		  status = NDIS_STATUS_SUCCESS;
		}
	    }
	}
    }

    /* Can't continue without name (see macro 'NAME') */
    if (status != NDIS_STATUS_SUCCESS || !l_Adapter->m_NameAnsi.Buffer)
      {
	NdisCloseConfiguration (configHandle);
	AdapterFreeResources (l_Adapter);
	DEBUGP (("[TAP] failed to get miniport name\n"));
	return NDIS_STATUS_RESOURCES;
      }

    /* Read MTU setting from registry */
    {
      NDIS_STRING key = NDIS_STRING_CONST("MTU");
      NdisReadConfiguration (&status, &parm, configHandle,
			     &key, NdisParameterInteger);
      if (status == NDIS_STATUS_SUCCESS)
	{
	  if (parm->ParameterType == NdisParameterInteger)
	    {
	      int mtu = parm->ParameterData.IntegerData;
	      if (mtu < MINIMUM_MTU)
		mtu = MINIMUM_MTU;
	      if (mtu > MAXIMUM_MTU)
		mtu = MAXIMUM_MTU;
	      l_Adapter->m_MTU = mtu;
	    }
	}
    }

    /* Read Media Status setting from registry */
    {
      NDIS_STRING key = NDIS_STRING_CONST("MediaStatus");
      NdisReadConfiguration (&status, &parm, configHandle,
			     &key, NdisParameterInteger);
      if (status == NDIS_STATUS_SUCCESS)
	{
	  if (parm->ParameterType == NdisParameterInteger)
	    {
	      if (parm->ParameterData.IntegerData)
		{
		  l_Adapter->m_MediaStateAlwaysConnected = TRUE;
		  l_Adapter->m_MediaState = TRUE;
		}
	    }
	}
    }

#if ENABLE_NONADMIN
    /* Read AllowNonAdmin setting from registry */
    {
      NDIS_STRING key = NDIS_STRING_CONST("AllowNonAdmin");
      NdisReadConfiguration (&status, &parm, configHandle,
			     &key, NdisParameterInteger);
      if (status == NDIS_STATUS_SUCCESS)
	{
	  if (parm->ParameterType == NdisParameterInteger)
	    {
	      if (parm->ParameterData.IntegerData)
		{
		  enable_non_admin = TRUE;
		}
	    }
	}
    }
#endif

    /* Read optional MAC setting from registry */
    {
      NDIS_STRING key = NDIS_STRING_CONST("MAC");
      ANSI_STRING mac_string;
      NdisReadConfiguration (&status, &parm, configHandle,
			     &key, NdisParameterString);
      if (status == NDIS_STATUS_SUCCESS)
	{
	  if (parm->ParameterType == NdisParameterString)
	    {
	      if (RtlUnicodeStringToAnsiString (&mac_string, &parm->ParameterData.StringData, TRUE) == STATUS_SUCCESS)
		{
		  l_MacFromRegistry = ParseMAC (l_Adapter->m_MAC, mac_string.Buffer);
		  RtlFreeAnsiString (&mac_string);
		}
	    }
	}
    }

    NdisCloseConfiguration (configHandle);

    DEBUGP (("[%s] MTU=%d\n", NAME (l_Adapter), l_Adapter->m_MTU));
  }

  //==================================
  // Store and update MAC address info
  //==================================

  if (!l_MacFromRegistry)
    GenerateRandomMac (l_Adapter->m_MAC, NAME (l_Adapter));

  DEBUGP (("[%s] Using MAC %x:%x:%x:%x:%x:%x\n",
	   NAME (l_Adapter),
	   l_Adapter->m_MAC[0], l_Adapter->m_MAC[1], l_Adapter->m_MAC[2],
	   l_Adapter->m_MAC[3], l_Adapter->m_MAC[4], l_Adapter->m_MAC[5]));

  //==================
  // Set broadcast MAC
  //==================
  {
    int i;
    for (i = 0; i < sizeof (MACADDR); ++i)
      l_Adapter->m_MAC_Broadcast[i] = 0xFF;
  }

  //====================================
  // Initialize TAP device
  //====================================
  {
    NDIS_STATUS tap_status;
    tap_status = CreateTapDevice (&l_Adapter->m_Extension, NAME (l_Adapter));
    if (tap_status != NDIS_STATUS_SUCCESS)
      {
	AdapterFreeResources (l_Adapter);
	DEBUGP (("[TAP] CreateTapDevice failed\n"));
	return tap_status;
      }
  }

  if (!AddAdapterToInstanceList (l_Adapter))
    {
      NOTE_ERROR ();
      TapDeviceFreeResources (&l_Adapter->m_Extension);
      AdapterFreeResources (l_Adapter);
      DEBUGP (("[TAP] AddAdapterToInstanceList failed\n"));
      return NDIS_STATUS_RESOURCES;
    }

  l_Adapter->m_InterfaceIsRunning = TRUE;

#if ENABLE_NONADMIN
  if (enable_non_admin)
    AllowNonAdmin (&l_Adapter->m_Extension);
#endif

  return NDIS_STATUS_SUCCESS;
}

VOID
AdapterHalt (IN NDIS_HANDLE p_AdapterContext)
{
  BOOLEAN status;

  TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;

  NOTE_ERROR ();

  l_Adapter->m_InterfaceIsRunning = FALSE;

  DEBUGP (("[%s] is being halted\n", NAME (l_Adapter)));
  
  DestroyTapDevice (&l_Adapter->m_Extension);

  // Free resources
  DEBUGP (("[%s] Freeing Resources\n", NAME (l_Adapter)));
  AdapterFreeResources (l_Adapter);

  status = RemoveAdapterFromInstanceList (l_Adapter);
  DEBUGP (("[TAP] RemoveAdapterFromInstanceList returned %d\n", (int) status));

  DEBUGP (("[TAP] version [%d.%d] %s %s AdapterHalt returning\n",
	   TAP_DRIVER_MAJOR_VERSION,
	   TAP_DRIVER_MINOR_VERSION,
	   __DATE__,
	   __TIME__));
}

VOID
AdapterFreeResources (TapAdapterPointer p_Adapter)
{
  MYASSERT (!p_Adapter->m_CalledAdapterFreeResources);
  p_Adapter->m_CalledAdapterFreeResources = TRUE;

  if (p_Adapter->m_NameAnsi.Buffer)
    RtlFreeAnsiString (&p_Adapter->m_NameAnsi);
  
  if (p_Adapter->m_RegisteredAdapterShutdownHandler)
    NdisMDeregisterAdapterShutdownHandler (p_Adapter->m_MiniportAdapterHandle);

  if (p_Adapter->m_MCLockAllocated)
    NdisFreeSpinLock (&p_Adapter->m_MCLock);
}

VOID
DestroyTapDevice (TapExtensionPointer p_Extension)
{
  DEBUGP (("[%s] Destroying tap device\n", p_Extension->m_TapName));

  //======================================
  // Let clients know we are shutting down
  //======================================
  p_Extension->m_TapIsRunning = FALSE;
  p_Extension->m_TapOpens = 0;
  p_Extension->m_Halt = TRUE;

  //=====================================
  // If we are concurrently executing in
  // TapDeviceHook or AdapterTransmit,
  // give those calls time to finish.
  // Note that we must be running at IRQL
  // < DISPATCH_LEVEL in order to call
  // NdisMSleep.
  //=====================================
  NdisMSleep (500000);

  //===========================================================
  // Exhaust IRP and packet queues.  Any pending IRPs will
  // be cancelled, causing user-space to get this error
  // on overlapped reads:
  //   The I/O operation has been aborted because of either a
  //   thread exit or an application request.   (code=995)
  // It's important that user-space close the device handle
  // when this code is returned, so that when we finally
  // do a NdisMDeregisterDevice, the device reference count
  // is 0.  Otherwise the driver will not unload even if the
  // the last adapter has been halted.
  //===========================================================
  FlushQueues (p_Extension);
  NdisMSleep (500000); // give user space time to respond to IRP cancel

  TapDeviceFreeResources (p_Extension);
}

VOID
TapDeviceFreeResources (TapExtensionPointer p_Extension)
{
  MYASSERT (p_Extension);
  MYASSERT (!p_Extension->m_CalledTapDeviceFreeResources);
  p_Extension->m_CalledTapDeviceFreeResources = TRUE;

  if (p_Extension->m_PacketQueue)
    QueueFree (p_Extension->m_PacketQueue);
  if (p_Extension->m_IrpQueue)
    QueueFree (p_Extension->m_IrpQueue);

  if (p_Extension->m_CreatedUnicodeLinkName)
    RtlFreeUnicodeString (&p_Extension->m_UnicodeLinkName);

  //==========================================================
  // According to DDK docs, the device is not actually deleted
  // until its reference count falls to zero.  That means we
  // still need to gracefully fail TapDeviceHook requests
  // after this point, otherwise ugly things would happen if
  // the device was disabled (e.g. in the network connections
  // control panel) while a userspace app still held an open
  // file handle to it.
  //==========================================================
  
  if (p_Extension->m_TapDevice)
    {
      BOOLEAN status;
      status = (NdisMDeregisterDevice (p_Extension->m_TapDeviceHandle)
		== NDIS_STATUS_SUCCESS);
      DEBUGP (("[TAP] Deregistering TAP device, status=%d\n", (int)status));
    }

  if (p_Extension->m_TapName)
    MemFree (p_Extension->m_TapName, NAME_BUFFER_SIZE);
  
  if (p_Extension->m_AllocatedSpinlocks)
    NdisFreeSpinLock (&p_Extension->m_QueueLock);
}

//========================================================================
//                             Tap Device Initialization
//========================================================================

NDIS_STATUS
CreateTapDevice (TapExtensionPointer p_Extension, const char *p_Name)
{
# define SIZEOF_DISPATCH (sizeof(PDRIVER_DISPATCH) * (IRP_MJ_MAXIMUM_FUNCTION + 1))
  PDRIVER_DISPATCH *l_Dispatch = NULL;
  ANSI_STRING l_TapString, l_LinkString;
  UNICODE_STRING l_TapUnicode;
  BOOLEAN l_FreeTapUnicode = FALSE;
  NTSTATUS l_Status, l_Return = NDIS_STATUS_SUCCESS;
  const char *l_UsableName;

  DEBUGP (("[TAP] version [%d.%d] creating tap device: %s\n",
	   TAP_DRIVER_MAJOR_VERSION,
	   TAP_DRIVER_MINOR_VERSION,
	   p_Name));

  NdisZeroMemory (p_Extension, sizeof (TapExtension));

  INIT_MUTEX (&p_Extension->m_OpenCloseMutex);

  l_LinkString.Buffer = NULL;
  l_TapString.Buffer = NULL;

  l_TapString.MaximumLength = l_LinkString.MaximumLength = NAME_BUFFER_SIZE;

  //=======================================
  // Set TAP device entry points
  //=======================================

  if ((l_Dispatch = MemAlloc (SIZEOF_DISPATCH, TRUE)) == NULL)
    {
      DEBUGP (("[%s] couldn't alloc TAP dispatch table\n", p_Name));
      l_Return = NDIS_STATUS_RESOURCES;
      goto cleanup;
    }

  l_Dispatch[IRP_MJ_DEVICE_CONTROL] = TapDeviceHook;
  l_Dispatch[IRP_MJ_READ] = TapDeviceHook;
  l_Dispatch[IRP_MJ_WRITE] = TapDeviceHook;
  l_Dispatch[IRP_MJ_CREATE] = TapDeviceHook;
  l_Dispatch[IRP_MJ_CLOSE] = TapDeviceHook;

  //==================================
  // Find the beginning of the GUID
  //==================================
  l_UsableName = p_Name;
  while (*l_UsableName != '{')
    {
      if (*l_UsableName == '\0')
	{
	  DEBUGP (("[%s] couldn't find leading '{' in name\n", p_Name));
	  l_Return = NDIS_STATUS_RESOURCES;
	  goto cleanup;
	}
      ++l_UsableName;
    }

  //==================================
  // Allocate pool for TAP device name
  //==================================

  if ((p_Extension->m_TapName = l_TapString.Buffer =
       MemAlloc (NAME_BUFFER_SIZE, TRUE)) == NULL)
    {
      DEBUGP (("[%s] couldn't alloc TAP name buffer\n", p_Name));
      l_Return = NDIS_STATUS_RESOURCES;
      goto cleanup;
    }

  //================================================
  // Allocate pool for TAP symbolic link name buffer
  //================================================

  if ((l_LinkString.Buffer =
       MemAlloc (NAME_BUFFER_SIZE, TRUE)) == NULL)
    {
      DEBUGP (("[%s] couldn't alloc TAP symbolic link name buffer\n",
	       p_Name));
      l_Return = NDIS_STATUS_RESOURCES;
      goto cleanup;
    }

  //=======================================================
  // Set TAP device name
  //=======================================================

  l_Status = RtlStringCchPrintfExA
    (l_TapString.Buffer,
     l_TapString.MaximumLength,
     NULL,
     NULL,
     STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS,
     "%s%s%s",
     SYSDEVICEDIR,
     l_UsableName,
     TAPSUFFIX);

  if (l_Status != STATUS_SUCCESS)
    {
      DEBUGP (("[%s] couldn't format TAP device name\n",
	       p_Name));
      l_Return = NDIS_STATUS_RESOURCES;
      goto cleanup;
    }
  l_TapString.Length = (USHORT) strlen (l_TapString.Buffer);

  DEBUGP (("TAP DEV NAME: '%s'\n", l_TapString.Buffer));

  //=======================================================
  // Set TAP link name
  //=======================================================

  l_Status = RtlStringCchPrintfExA
    (l_LinkString.Buffer,
     l_LinkString.MaximumLength,
     NULL,
     NULL,
     STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS,
     "%s%s%s",
     USERDEVICEDIR,
     l_UsableName,
     TAPSUFFIX);

  if (l_Status != STATUS_SUCCESS)
    {
      DEBUGP (("[%s] couldn't format TAP device symbolic link\n",
	       p_Name));
      l_Return = NDIS_STATUS_RESOURCES;
      goto cleanup;
    }
  l_LinkString.Length = (USHORT) strlen (l_LinkString.Buffer);

  DEBUGP (("TAP LINK NAME: '%s'\n", l_LinkString.Buffer));

  //==================================================
  // Convert strings to unicode
  //==================================================
  if (RtlAnsiStringToUnicodeString (&l_TapUnicode, &l_TapString, TRUE) !=
      STATUS_SUCCESS)
    {
      DEBUGP (("[%s] couldn't alloc TAP unicode name buffer\n",
		p_Name));
      l_Return = NDIS_STATUS_RESOURCES;
      goto cleanup;
    }
  l_FreeTapUnicode = TRUE;

  if (RtlAnsiStringToUnicodeString
      (&p_Extension->m_UnicodeLinkName, &l_LinkString, TRUE)
      != STATUS_SUCCESS)
    {
      DEBUGP
	(("[%s] Couldn't allocate unicode string for symbolic link name\n",
	 p_Name));
      l_Return = NDIS_STATUS_RESOURCES;
      goto cleanup;
    }
  p_Extension->m_CreatedUnicodeLinkName = TRUE;

  //==================================================
  // Create new TAP device with symbolic
  // link and associate with adapter.
  //==================================================

  l_Status = NdisMRegisterDevice
    (g_NdisWrapperHandle,
     &l_TapUnicode,
     &p_Extension->m_UnicodeLinkName,
     l_Dispatch,
     &p_Extension->m_TapDevice,
     &p_Extension->m_TapDeviceHandle
     );

  if (l_Status != STATUS_SUCCESS)
    {
      DEBUGP (("[%s] couldn't be created\n", p_Name));
      l_Return = NDIS_STATUS_RESOURCES;
      goto cleanup;
    }

  /* Set TAP device flags */
  p_Extension->m_TapDevice->Flags |= DO_DIRECT_IO;

  //========================================================
  // Initialize Packet and IRP queues.
  //
  // The packet queue is used to buffer data which has been
  // "transmitted" by the virtual NIC, before user space
  // has had a chance to read it.
  //
  // The IRP queue is used to buffer pending I/O requests
  // from userspace, i.e. read requests on the TAP device
  // waiting for the system to "transmit" something through
  // the virtual NIC.
  //
  // Basically, packets in the packet queue are used
  // to satisfy IRP requests in the IRP queue.
  //
  // QueueLock is used to lock the packet queue used
  // for the TAP-Win32 NIC -> User Space packet flow direction.
  //
  // All accesses to packet or IRP queues should be
  // bracketed by the QueueLock spinlock,
  // in order to be SMP-safe.
  //========================================================

  NdisAllocateSpinLock (&p_Extension->m_QueueLock);
  p_Extension->m_AllocatedSpinlocks = TRUE;

  p_Extension->m_PacketQueue = QueueInit (PACKET_QUEUE_SIZE);
  p_Extension->m_IrpQueue = QueueInit (IRP_QUEUE_SIZE);

  if (!p_Extension->m_PacketQueue
      || !p_Extension->m_IrpQueue)
    {
      DEBUGP (("[%s] couldn't alloc TAP queues\n", p_Name));
      l_Return = NDIS_STATUS_RESOURCES;
      goto cleanup;
    }

  //========================
  // Finalize initialization
  //========================

  p_Extension->m_TapIsRunning = TRUE;

  DEBUGP (("[%s] successfully created TAP device [%s]\n", p_Name,
	    p_Extension->m_TapName));

 cleanup:
  if (l_FreeTapUnicode)
    RtlFreeUnicodeString (&l_TapUnicode);
  if (l_LinkString.Buffer)
    MemFree (l_LinkString.Buffer, NAME_BUFFER_SIZE);
  if (l_Dispatch)
    MemFree (l_Dispatch, SIZEOF_DISPATCH);

  if (l_Return != NDIS_STATUS_SUCCESS)
    TapDeviceFreeResources (p_Extension);

  return l_Return;
}
#undef SIZEOF_DISPATCH

//========================================================
//                      Adapter Control
//========================================================
NDIS_STATUS
AdapterReset (OUT PBOOLEAN p_AddressingReset, IN NDIS_HANDLE p_AdapterContext)
{
  TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
  DEBUGP (("[%s] is resetting\n", NAME (l_Adapter)));
  return NDIS_STATUS_SUCCESS;
}

NDIS_STATUS AdapterReceive
  (OUT PNDIS_PACKET p_Packet,
   OUT PUINT p_Transferred,
   IN NDIS_HANDLE p_AdapterContext,
   IN NDIS_HANDLE p_ReceiveContext,
   IN UINT p_Offset,
   IN UINT p_ToTransfer)
{
  return NDIS_STATUS_SUCCESS;
}

//==============================================================
//                  Adapter Option Query/Modification
//==============================================================
NDIS_STATUS AdapterQuery
(IN NDIS_HANDLE p_AdapterContext,
 IN NDIS_OID p_OID,
 IN PVOID p_Buffer,
 IN ULONG p_BufferLength,
 OUT PULONG p_BytesWritten, OUT PULONG p_BytesNeeded)
{
  TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
  TapAdapterQuery l_Query, *l_QueryPtr = &l_Query;
  NDIS_STATUS l_Status = NDIS_STATUS_SUCCESS;
  UINT l_QueryLength = 4;
  BOOLEAN lock_succeeded;

  NdisZeroMemory (&l_Query, sizeof (l_Query));

  switch (p_OID)
    {
      //===================================================================
      //                       Vendor & Driver version Info
      //===================================================================
    case OID_GEN_VENDOR_DESCRIPTION:
      l_QueryPtr = (TapAdapterQueryPointer) PRODUCT_STRING;
      l_QueryLength = strlen (PRODUCT_STRING) + 1;
      break;

    case OID_GEN_VENDOR_ID:
      l_Query.m_Long = 0xffffff;
      break;

    case OID_GEN_DRIVER_VERSION:
      l_Query.m_Short =
	(((USHORT) TAP_NDIS_MAJOR_VERSION) << 8 | (USHORT)
	 TAP_NDIS_MINOR_VERSION);
      l_QueryLength = sizeof (unsigned short);
      break;

    case OID_GEN_VENDOR_DRIVER_VERSION:
      l_Query.m_Long =
	(((USHORT) TAP_DRIVER_MAJOR_VERSION) << 8 | (USHORT)
	 TAP_DRIVER_MINOR_VERSION);
      break;

      //=================================================================
      //                             Statistics
      //=================================================================
    case OID_GEN_RCV_NO_BUFFER:
      l_Query.m_Long = 0;
      break;

    case OID_802_3_RCV_ERROR_ALIGNMENT:
      l_Query.m_Long = 0;
      break;

    case OID_802_3_XMIT_ONE_COLLISION:
      l_Query.m_Long = 0;
      break;

    case OID_802_3_XMIT_MORE_COLLISIONS:
      l_Query.m_Long = 0;
      break;

    case OID_GEN_XMIT_OK:
      l_Query.m_Long = l_Adapter->m_Tx;
      break;

    case OID_GEN_RCV_OK:
      l_Query.m_Long = l_Adapter->m_Rx;
      break;

    case OID_GEN_XMIT_ERROR:
      l_Query.m_Long = l_Adapter->m_TxErr;
      break;

    case OID_GEN_RCV_ERROR:
      l_Query.m_Long = l_Adapter->m_RxErr;
      break;

      //===================================================================
      //                       Device & Protocol Options
      //===================================================================
    case OID_GEN_SUPPORTED_LIST:
      l_QueryPtr = (TapAdapterQueryPointer) g_SupportedOIDList;
      l_QueryLength = sizeof (g_SupportedOIDList);
      break;

    case OID_GEN_MAC_OPTIONS:
      // This MUST be here !!!
      l_Query.m_Long = (NDIS_MAC_OPTION_RECEIVE_SERIALIZED
			| NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA
			| NDIS_MAC_OPTION_NO_LOOPBACK
			| NDIS_MAC_OPTION_TRANSFERS_NOT_PEND);

      break;

    case OID_GEN_CURRENT_PACKET_FILTER:
      l_Query.m_Long =
	(NDIS_PACKET_TYPE_ALL_LOCAL |
	 NDIS_PACKET_TYPE_BROADCAST |
	 NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_ALL_FUNCTIONAL);

      break;

    case OID_GEN_PROTOCOL_OPTIONS:
      l_Query.m_Long = 0;
      break;

      //==================================================================
      //                            Device Info
      //==================================================================
    case OID_GEN_MEDIA_CONNECT_STATUS:
      l_Query.m_Long = l_Adapter->m_MediaState
	? NdisMediaStateConnected : NdisMediaStateDisconnected;
      break;

    case OID_GEN_HARDWARE_STATUS:
      l_Query.m_HardwareStatus = NdisHardwareStatusReady;
      l_QueryLength = sizeof (NDIS_HARDWARE_STATUS);
      break;

    case OID_GEN_MEDIA_SUPPORTED:
    case OID_GEN_MEDIA_IN_USE:
      l_Query.m_Medium = l_Adapter->m_Medium;
      l_QueryLength = sizeof (NDIS_MEDIUM);
      break;

    case OID_GEN_PHYSICAL_MEDIUM:
      l_Query.m_PhysicalMedium = NdisPhysicalMediumUnspecified;
      l_QueryLength = sizeof (NDIS_PHYSICAL_MEDIUM);
      break;
      
    case OID_GEN_LINK_SPEED:
      l_Query.m_Long = 100000; // rate / 100 bps
      break;

    case OID_802_3_PERMANENT_ADDRESS:
    case OID_802_3_CURRENT_ADDRESS:
      COPY_MAC (l_Query.m_MacAddress, l_Adapter->m_MAC);
      l_QueryLength = sizeof (MACADDR);
      break;

      //==================================================================
      //                             Limits
      //==================================================================

    case OID_GEN_MAXIMUM_SEND_PACKETS:
      l_Query.m_Long = 1;
      break;

    case OID_802_3_MAXIMUM_LIST_SIZE:
      l_Query.m_Long = NIC_MAX_MCAST_LIST;
      break;

    case OID_GEN_CURRENT_LOOKAHEAD:
      l_Query.m_Long = l_Adapter->m_Lookahead;
      break;

    case OID_GEN_MAXIMUM_LOOKAHEAD:
    case OID_GEN_MAXIMUM_TOTAL_SIZE:
    case OID_GEN_RECEIVE_BUFFER_SPACE:
    case OID_GEN_RECEIVE_BLOCK_SIZE:
      l_Query.m_Long = DEFAULT_PACKET_LOOKAHEAD;
      break;

    case OID_GEN_MAXIMUM_FRAME_SIZE:
    case OID_GEN_TRANSMIT_BLOCK_SIZE:
    case OID_GEN_TRANSMIT_BUFFER_SPACE:
      l_Query.m_Long = l_Adapter->m_MTU;
      break;

    case OID_PNP_CAPABILITIES:
      do
	{
	  PNDIS_PNP_CAPABILITIES pPNPCapabilities;
	  PNDIS_PM_WAKE_UP_CAPABILITIES pPMstruct;

	  if (p_BufferLength >= sizeof (NDIS_PNP_CAPABILITIES))
	    {
	      pPNPCapabilities = (PNDIS_PNP_CAPABILITIES) (p_Buffer);

	      //
	      // Setting up the buffer to be returned
	      // to the Protocol above the Passthru miniport
	      //
	      pPMstruct = &pPNPCapabilities->WakeUpCapabilities;
	      pPMstruct->MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
	      pPMstruct->MinPatternWakeUp = NdisDeviceStateUnspecified;
	      pPMstruct->MinLinkChangeWakeUp = NdisDeviceStateUnspecified;
	    }
	  l_QueryLength = sizeof (NDIS_PNP_CAPABILITIES);
	}
      while (FALSE);
      break;
    case OID_PNP_QUERY_POWER:
      break;

      // Required OIDs that we don't support

    case OID_GEN_SUPPORTED_GUIDS:
    case OID_GEN_MEDIA_CAPABILITIES:
    case OID_TCP_TASK_OFFLOAD:
    case OID_FFP_SUPPORT:
      l_Status = NDIS_STATUS_INVALID_OID;
      break;

      // Optional stats OIDs

    case OID_GEN_DIRECTED_BYTES_XMIT:
    case OID_GEN_DIRECTED_FRAMES_XMIT:
    case OID_GEN_MULTICAST_BYTES_XMIT:
    case OID_GEN_MULTICAST_FRAMES_XMIT:
    case OID_GEN_BROADCAST_BYTES_XMIT:
    case OID_GEN_BROADCAST_FRAMES_XMIT:
    case OID_GEN_DIRECTED_BYTES_RCV:
    case OID_GEN_DIRECTED_FRAMES_RCV:
    case OID_GEN_MULTICAST_BYTES_RCV:
    case OID_GEN_MULTICAST_FRAMES_RCV:
    case OID_GEN_BROADCAST_BYTES_RCV:
    case OID_GEN_BROADCAST_FRAMES_RCV:
      l_Status = NDIS_STATUS_INVALID_OID;
      break;

      //===================================================================
      //                          Not Handled
      //===================================================================
    default:
      DEBUGP (("[%s] Unhandled OID %lx\n", NAME (l_Adapter), p_OID));
      l_Status = NDIS_STATUS_INVALID_OID;
      break;
    }

  if (l_Status != NDIS_STATUS_SUCCESS)
    ;
  else if (l_QueryLength > p_BufferLength)
    {
      l_Status = NDIS_STATUS_INVALID_LENGTH;
      *p_BytesNeeded = l_QueryLength;
    }
  else
    NdisMoveMemory (p_Buffer, (PVOID) l_QueryPtr,
		    (*p_BytesWritten = l_QueryLength));

  return l_Status;
}

NDIS_STATUS AdapterModify
(IN NDIS_HANDLE p_AdapterContext,
 IN NDIS_OID p_OID,
 IN PVOID p_Buffer,
 IN ULONG p_BufferLength,
 OUT PULONG p_BytesRead,
 OUT PULONG p_BytesNeeded)
{
  TapAdapterQueryPointer l_Query = (TapAdapterQueryPointer) p_Buffer;
  TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
  NDIS_STATUS l_Status = NDIS_STATUS_INVALID_OID;
  ULONG l_Long;

  switch (p_OID)
    {
      //==================================================================
      //                            Device Info
      //==================================================================
    case OID_802_3_MULTICAST_LIST:
      DEBUGP (("[%s] Setting [OID_802_3_MULTICAST_LIST]\n",
	       NAME (l_Adapter)));

      *p_BytesNeeded = sizeof (ETH_ADDR);
      *p_BytesRead = p_BufferLength;

      if (p_BufferLength % sizeof (ETH_ADDR))
	l_Status = NDIS_STATUS_INVALID_LENGTH;
      else if (p_BufferLength > sizeof (MC_LIST))
	{
	  l_Status = NDIS_STATUS_MULTICAST_FULL;
	  *p_BytesNeeded = sizeof (MC_LIST);
	}
      else
	{
	  NdisAcquireSpinLock (&l_Adapter->m_MCLock);

	  NdisZeroMemory(&l_Adapter->m_MCList, sizeof (MC_LIST));
        
	  NdisMoveMemory(&l_Adapter->m_MCList,
			 p_Buffer,
			 p_BufferLength);

	  l_Adapter->m_MCListSize = p_BufferLength / sizeof (ETH_ADDR);
        
	  NdisReleaseSpinLock (&l_Adapter->m_MCLock);

	  l_Status = NDIS_STATUS_SUCCESS;
	}
      break;

    case OID_GEN_CURRENT_PACKET_FILTER:
      l_Status = NDIS_STATUS_INVALID_LENGTH;
      *p_BytesNeeded = 4;

      if (p_BufferLength >= sizeof (ULONG))
	{
	  DEBUGP
	    (("[%s] Setting [OID_GEN_CURRENT_PACKET_FILTER] to [0x%02lx]\n",
	      NAME (l_Adapter), l_Query->m_Long));
	  l_Status = NDIS_STATUS_SUCCESS;
	  *p_BytesRead = sizeof (ULONG);
	}
      break;

    case OID_GEN_CURRENT_LOOKAHEAD:
      if (p_BufferLength < sizeof (ULONG))
	{
	  l_Status = NDIS_STATUS_INVALID_LENGTH;
	  *p_BytesNeeded = 4;
	}
      else if (l_Query->m_Long > DEFAULT_PACKET_LOOKAHEAD
	       || l_Query->m_Long <= 0)
	{
	  l_Status = NDIS_STATUS_INVALID_DATA;
	}
      else
	{
	  DEBUGP (("[%s] Setting [OID_GEN_CURRENT_LOOKAHEAD] to [%d]\n",
		   NAME (l_Adapter), l_Query->m_Long));
	  l_Adapter->m_Lookahead = l_Query->m_Long;
	  l_Status = NDIS_STATUS_SUCCESS;
	  *p_BytesRead = sizeof (ULONG);
	}
      break;

    case OID_GEN_NETWORK_LAYER_ADDRESSES:
      l_Status = NDIS_STATUS_SUCCESS;
      *p_BytesRead = *p_BytesNeeded = 0;
      break;

    case OID_GEN_TRANSPORT_HEADER_OFFSET:
      l_Status = NDIS_STATUS_SUCCESS;
      *p_BytesRead = *p_BytesNeeded = 0;
      break;

    case OID_PNP_SET_POWER:
      do
	{
	  NDIS_DEVICE_POWER_STATE NewDeviceState;

	  NewDeviceState = (*(PNDIS_DEVICE_POWER_STATE) p_Buffer);

	  switch (NewDeviceState)
	    {
	    case NdisDeviceStateD0:
	      l_Adapter->m_DeviceState = '0';
	      break;
	    case NdisDeviceStateD1:
	      l_Adapter->m_DeviceState = '1';
	      break;
	    case NdisDeviceStateD2:
	      l_Adapter->m_DeviceState = '2';
	      break;
	    case NdisDeviceStateD3:
	      l_Adapter->m_DeviceState = '3';
	      break;
	    default:
	      l_Adapter->m_DeviceState = '?';
	      break;
	    }

	  l_Status = NDIS_STATUS_FAILURE;

	  //
	  // Check for invalid length
	  //
	  if (p_BufferLength < sizeof (NDIS_DEVICE_POWER_STATE))
	    {
	      l_Status = NDIS_STATUS_INVALID_LENGTH;
	      break;
	    }

	  if (NewDeviceState > NdisDeviceStateD0)
	    {
	      l_Adapter->m_InterfaceIsRunning = FALSE;
	      DEBUGP (("[%s] Power management device state OFF\n",
		       NAME (l_Adapter)));
	    }
	  else
	    {
	      l_Adapter->m_InterfaceIsRunning = TRUE;
	      DEBUGP (("[%s] Power management device state ON\n",
		       NAME (l_Adapter)));
	    }

	  l_Status = NDIS_STATUS_SUCCESS;
	}
      while (FALSE);

      if (l_Status == NDIS_STATUS_SUCCESS)
	{
	  *p_BytesRead = sizeof (NDIS_DEVICE_POWER_STATE);
	  *p_BytesNeeded = 0;
	}
      else
	{
	  *p_BytesRead = 0;
	  *p_BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE);
	}
      break;

    case OID_PNP_REMOVE_WAKE_UP_PATTERN:
    case OID_PNP_ADD_WAKE_UP_PATTERN:
      l_Status = NDIS_STATUS_SUCCESS;
      *p_BytesRead = *p_BytesNeeded = 0;
      break;

    default:
      DEBUGP (("[%s] Can't set value for OID %lx\n", NAME (l_Adapter),
	       p_OID));
      l_Status = NDIS_STATUS_INVALID_OID;
      *p_BytesRead = *p_BytesNeeded = 0;
      break;
    }

  return l_Status;
}

//====================================================================
//                               Adapter Transmission
//====================================================================
NDIS_STATUS
AdapterTransmit (IN NDIS_HANDLE p_AdapterContext,
		 IN PNDIS_PACKET p_Packet,
		 IN UINT p_Flags)
{
  TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
  ULONG l_Index = 0, l_PacketLength = 0;
  UINT l_BufferLength = 0;
  PIRP l_IRP;
  TapPacketPointer l_PacketBuffer;
  PNDIS_BUFFER l_NDIS_Buffer;
  PUCHAR l_Buffer;
  PVOID result;

  NdisQueryPacket (p_Packet, NULL, NULL, &l_NDIS_Buffer, &l_PacketLength);

  //====================================================
  // Here we abandon the transmission attempt if any of
  // the parameters is wrong or memory allocation fails
  // but we do not indicate failure. The packet is
  // silently dropped.
  //====================================================

  if (l_PacketLength < ETHERNET_HEADER_SIZE || l_PacketLength > 65535)
    goto exit_fail;
  else if (!l_Adapter->m_Extension.m_TapOpens || !l_Adapter->m_MediaState)
    goto exit_success;              // Nothing is bound to the TAP device

  if (NdisAllocateMemoryWithTag (&l_PacketBuffer,
				 TAP_PACKET_SIZE (l_PacketLength),
				 '5PAT') != NDIS_STATUS_SUCCESS)
    goto exit_no_resources;

  if (l_PacketBuffer == NULL)
    goto exit_no_resources;

  l_PacketBuffer->m_SizeFlags = (l_PacketLength & TP_SIZE_MASK);

  //===========================
  // Reassemble packet contents
  //===========================

  __try
  {
    l_Index = 0;
    while (l_NDIS_Buffer && l_Index < l_PacketLength)
      {
	ULONG newlen;
	NdisQueryBuffer (l_NDIS_Buffer, (PVOID *) & l_Buffer,
			 &l_BufferLength);
	newlen = l_Index + l_BufferLength;
	if (newlen > l_PacketLength)
	  {
	    NOTE_ERROR ();
	    goto no_queue; /* overflow */
	  }
	NdisMoveMemory (l_PacketBuffer->m_Data + l_Index, l_Buffer,
			l_BufferLength);
	l_Index = newlen;
	NdisGetNextBuffer (l_NDIS_Buffer, &l_NDIS_Buffer);
      }
    if (l_Index != l_PacketLength)
      {
	NOTE_ERROR ();
	goto no_queue; /* underflow */
      }

    DUMP_PACKET ("AdapterTransmit", l_PacketBuffer->m_Data, l_PacketLength);

    //=====================================================
    // If IPv4 packet, check whether or not packet
    // was truncated.
    //=====================================================
#if PACKET_TRUNCATION_CHECK
    IPv4PacketSizeVerify (l_PacketBuffer->m_Data, l_PacketLength, FALSE, "TX", &l_Adapter->m_TxTrunc);
#endif

    //=====================================================
    // Are we running in DHCP server masquerade mode?
    //
    // If so, catch both DHCP requests and ARP queries
    // to resolve the address of our virtual DHCP server.
    //=====================================================
    if (l_Adapter->m_dhcp_enabled)
      {
	const ETH_HEADER *eth = (ETH_HEADER *) l_PacketBuffer->m_Data;
	const IPHDR *ip = (IPHDR *) (l_PacketBuffer->m_Data + sizeof (ETH_HEADER));
	const UDPHDR *udp = (UDPHDR *) (l_PacketBuffer->m_Data + sizeof (ETH_HEADER) + sizeof (IPHDR));

	// ARP packet?
	if (l_PacketLength == sizeof (ARP_PACKET)
	    && eth->proto == htons (ETH_P_ARP)
	    && l_Adapter->m_dhcp_server_arp)
	  {
	    if (ProcessARP (l_Adapter,
			    (PARP_PACKET) l_PacketBuffer->m_Data,
			    l_Adapter->m_dhcp_addr,
			    l_Adapter->m_dhcp_server_ip,
			    ~0,
			    l_Adapter->m_dhcp_server_mac))
	      goto no_queue;
	  }

	// DHCP packet?
	else if (l_PacketLength >= sizeof (ETH_HEADER) + sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP)
		 && eth->proto == htons (ETH_P_IP)
		 && ip->version_len == 0x45 // IPv4, 20 byte header
		 && ip->protocol == IPPROTO_UDP
		 && udp->dest == htons (BOOTPS_PORT))
	  {
	    const DHCP *dhcp = (DHCP *) (l_PacketBuffer->m_Data
					 + sizeof (ETH_HEADER)
					 + sizeof (IPHDR)
					 + sizeof (UDPHDR));

	    const int optlen = l_PacketLength
	      - sizeof (ETH_HEADER)
	      - sizeof (IPHDR)
	      - sizeof (UDPHDR)
	      - sizeof (DHCP);

	    if (optlen > 0) // we must have at least one DHCP option
	      {
		if (ProcessDHCP (l_Adapter, eth, ip, udp, dhcp, optlen))
		  goto no_queue;
	      }
	    else
	      goto no_queue;
	  }
      }

    //===============================================
    // In Point-To-Point mode, check to see whether
    // packet is ARP or IPv4 (if neither, then drop).
    //===============================================
    if (l_Adapter->m_tun)
      {
	ETH_HEADER *e;

	if (l_PacketLength < ETHERNET_HEADER_SIZE)
	  goto no_queue;

	e = (ETH_HEADER *) l_PacketBuffer->m_Data;

	switch (ntohs (e->proto))
	  {
	  case ETH_P_ARP:

	    // Make sure that packet is the
	    // right size for ARP.
	    if (l_PacketLength != sizeof (ARP_PACKET))
	      goto no_queue;

	    ProcessARP (l_Adapter,
			(PARP_PACKET) l_PacketBuffer->m_Data,
			l_Adapter->m_localIP,
			l_Adapter->m_remoteNetwork,
			l_Adapter->m_remoteNetmask,
			l_Adapter->m_TapToUser.dest);

	  default:
	    goto no_queue;

	  case ETH_P_IP:

	    // Make sure that packet is large
	    // enough to be IPv4.
	    if (l_PacketLength
		< ETHERNET_HEADER_SIZE + IP_HEADER_SIZE)
	      goto no_queue;

	    // Only accept directed packets,
	    // not broadcasts.
	    if (memcmp (e, &l_Adapter->m_TapToUser, ETHERNET_HEADER_SIZE))
	      goto no_queue;

	    // Packet looks like IPv4, queue it.
	    l_PacketBuffer->m_SizeFlags |= TP_TUN;
	  }
      }

    //===============================================
    // Push packet onto queue to wait for read from
    // userspace.
    //===============================================

    NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);

    result = NULL;
    if (IS_UP (l_Adapter))
      result = QueuePush (l_Adapter->m_Extension.m_PacketQueue, l_PacketBuffer);

    NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);

    if ((TapPacketPointer) result != l_PacketBuffer)
      {
	// adapter receive overrun
	INCREMENT_STAT (l_Adapter->m_TxErr);
	goto no_queue;
      }
    else
      {
	INCREMENT_STAT (l_Adapter->m_Tx);
      }

    //============================================================
    // Cycle through IRPs and packets, try to satisfy each pending
    // IRP with a queued packet.
    //============================================================
    while (TRUE)
      {
	l_IRP = NULL;
	l_PacketBuffer = NULL;

	NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);

	if (IS_UP (l_Adapter)
	    && QueueCount (l_Adapter->m_Extension.m_PacketQueue)
	    && QueueCount (l_Adapter->m_Extension.m_IrpQueue))
	  {
	    l_IRP = (PIRP) QueuePop (l_Adapter->m_Extension.m_IrpQueue);
	    l_PacketBuffer = (TapPacketPointer)
	      QueuePop (l_Adapter->m_Extension.m_PacketQueue);
	  }

	NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);

	MYASSERT ((l_IRP != NULL) + (l_PacketBuffer != NULL) != 1);

	if (l_IRP && l_PacketBuffer)
	  {
	    CompleteIRP (l_IRP,
			 l_PacketBuffer, 
			 IO_NETWORK_INCREMENT);
	  }
	else
	  break;
      }
  }
  __except (EXCEPTION_EXECUTE_HANDLER)
    {
    }

  return NDIS_STATUS_SUCCESS;

 no_queue:
  NdisFreeMemory (l_PacketBuffer,
		  TAP_PACKET_SIZE (l_PacketLength),
		  0);
  
 exit_success:
  return NDIS_STATUS_SUCCESS;
    
 exit_fail:
  return NDIS_STATUS_FAILURE;

 exit_no_resources:
  return NDIS_STATUS_RESOURCES;
}

//======================================================================
// Hooks for catching TAP device IRP's.
//======================================================================

NTSTATUS
TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP)
{
  TapAdapterPointer l_Adapter = LookupAdapterInInstanceList (p_DeviceObject);
  PIO_STACK_LOCATION l_IrpSp;
  NTSTATUS l_Status = STATUS_SUCCESS;
  BOOLEAN accessible;

  l_IrpSp = IoGetCurrentIrpStackLocation (p_IRP);

  p_IRP->IoStatus.Status = STATUS_SUCCESS;
  p_IRP->IoStatus.Information = 0;

  if (!l_Adapter || l_Adapter->m_Extension.m_Halt)
    {
      DEBUGP (("TapDeviceHook called when TAP device is halted, MajorFunction=%d\n",
	       (int)l_IrpSp->MajorFunction));

      if (l_IrpSp->MajorFunction == IRP_MJ_CLOSE)
	{
	  IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
	  return STATUS_SUCCESS;
	}
      else
	{
	  p_IRP->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
	  IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
	  return STATUS_NO_SUCH_DEVICE;
	}
    }

  switch (l_IrpSp->MajorFunction)
    {
      //===========================================================
      //                 Ioctl call handlers
      //===========================================================
    case IRP_MJ_DEVICE_CONTROL:
      {
	switch (l_IrpSp->Parameters.DeviceIoControl.IoControlCode)
	  {
	  case TAP_IOCTL_GET_MAC:
	    {
	      if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength
		  >= sizeof (MACADDR))
		{
		  COPY_MAC (p_IRP->AssociatedIrp.SystemBuffer,
			    l_Adapter->m_MAC);
		  p_IRP->IoStatus.Information = sizeof (MACADDR);
		}
	      else
		{
		  NOTE_ERROR ();
		  p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
		}
	      break;
	    }
	  case TAP_IOCTL_GET_VERSION:
	    {
	      const ULONG size = sizeof (ULONG) * 3;
	      if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength
		  >= size)
		{
		  ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0]
		    = TAP_DRIVER_MAJOR_VERSION;
		  ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[1]
		    = TAP_DRIVER_MINOR_VERSION;
		  ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[2]
#if DBG
		    = 1;
#else
		  = 0;
#endif
		  p_IRP->IoStatus.Information = size;
		}
	      else
		{
		  NOTE_ERROR ();
		  p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
		}

	      break;
	    }
	  case TAP_IOCTL_GET_MTU:
	    {
	      const ULONG size = sizeof (ULONG) * 1;
	      if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength
		  >= size)
		{
		  ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0]
		    = l_Adapter->m_MTU;
		  p_IRP->IoStatus.Information = size;
		}
	      else
		{
		  NOTE_ERROR ();
		  p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
		}

	      break;
	    }
	  case TAP_IOCTL_GET_INFO:
	    {
	      char state[16];
	      if (l_Adapter->m_InterfaceIsRunning)
		state[0] = 'A';
	      else
		state[0] = 'a';
	      if (l_Adapter->m_Extension.m_TapIsRunning)
		state[1] = 'T';
	      else
		state[1] = 't';
	      state[2] = l_Adapter->m_DeviceState;
	      if (l_Adapter->m_MediaStateAlwaysConnected)
		state[3] = 'C';
	      else
		state[3] = 'c';
	      state[4] = '\0';

	      p_IRP->IoStatus.Status = l_Status = RtlStringCchPrintfExA (
	        ((LPTSTR) (p_IRP->AssociatedIrp.SystemBuffer)),
		l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
		NULL,
		NULL,
		STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS,
#if PACKET_TRUNCATION_CHECK
		"State=%s Err=[%s/%d] #O=%d Tx=[%d,%d,%d] Rx=[%d,%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d]",
#else
		"State=%s Err=[%s/%d] #O=%d Tx=[%d,%d] Rx=[%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d]",
#endif
		state,
		g_LastErrorFilename,
		g_LastErrorLineNumber,
		(int)l_Adapter->m_Extension.m_NumTapOpens,
		(int)l_Adapter->m_Tx,
		(int)l_Adapter->m_TxErr,
#if PACKET_TRUNCATION_CHECK
		(int)l_Adapter->m_TxTrunc,
#endif
		(int)l_Adapter->m_Rx,
		(int)l_Adapter->m_RxErr,
#if PACKET_TRUNCATION_CHECK
		(int)l_Adapter->m_RxTrunc,
#endif
		(int)l_Adapter->m_Extension.m_IrpQueue->size,
		(int)l_Adapter->m_Extension.m_IrpQueue->max_size,
		(int)IRP_QUEUE_SIZE,
		(int)l_Adapter->m_Extension.m_PacketQueue->size,
		(int)l_Adapter->m_Extension.m_PacketQueue->max_size,
		(int)PACKET_QUEUE_SIZE
		);

	      p_IRP->IoStatus.Information
		= l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength;

	      break;
	    }

#if DBG
	  case TAP_IOCTL_GET_LOG_LINE:
	    {
	      if (GetDebugLine ((LPTSTR)p_IRP->AssociatedIrp.SystemBuffer,
				l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength))
		p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
	      else
		p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;

	      p_IRP->IoStatus.Information
		= l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength;

	      break;
	    }
#endif

	  case TAP_IOCTL_CONFIG_TUN:
	    {
	      if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
		  (sizeof (IPADDR) * 3))
		{
		  MACADDR dest;

		  l_Adapter->m_tun = FALSE;

		  GenerateRelatedMAC (dest, l_Adapter->m_MAC, 1);

		  l_Adapter->m_localIP =       ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0];
		  l_Adapter->m_remoteNetwork = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1];
		  l_Adapter->m_remoteNetmask = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[2];

		  // sanity check on network/netmask
		  if ((l_Adapter->m_remoteNetwork & l_Adapter->m_remoteNetmask) != l_Adapter->m_remoteNetwork)
		    {
		      NOTE_ERROR ();
		      p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
		      break;
		    }

		  COPY_MAC (l_Adapter->m_TapToUser.src, l_Adapter->m_MAC);
		  COPY_MAC (l_Adapter->m_TapToUser.dest, dest);
		  COPY_MAC (l_Adapter->m_UserToTap.src, dest);
		  COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC);

		  l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP);

		  l_Adapter->m_tun = TRUE;

		  CheckIfDhcpAndTunMode (l_Adapter);

		  p_IRP->IoStatus.Information = 1; // Simple boolean value
		}
	      else
		{
		  NOTE_ERROR ();
		  p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
		}
	      
	      break;
	    }

	  case TAP_IOCTL_CONFIG_POINT_TO_POINT: // Obsoleted by TAP_IOCTL_CONFIG_TUN
	    {
	      if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
		  (sizeof (IPADDR) * 2))
		{
		  MACADDR dest;

		  l_Adapter->m_tun = FALSE;

		  GenerateRelatedMAC (dest, l_Adapter->m_MAC, 1);

		  l_Adapter->m_localIP =       ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0];
		  l_Adapter->m_remoteNetwork = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1];
		  l_Adapter->m_remoteNetmask = ~0;

		  COPY_MAC (l_Adapter->m_TapToUser.src, l_Adapter->m_MAC);
		  COPY_MAC (l_Adapter->m_TapToUser.dest, dest);
		  COPY_MAC (l_Adapter->m_UserToTap.src, dest);
		  COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC);

		  l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP);

		  l_Adapter->m_tun = TRUE;

		  CheckIfDhcpAndTunMode (l_Adapter);

		  p_IRP->IoStatus.Information = 1; // Simple boolean value
		}
	      else
		{
		  NOTE_ERROR ();
		  p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
		}
	      
	      break;
	    }

	  case TAP_IOCTL_SET_MEDIA_STATUS:
	    {
	      if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
		  (sizeof (ULONG) * 1))
		{
		  ULONG parm = ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0];
		  SetMediaStatus (l_Adapter, (BOOLEAN) parm);
		  p_IRP->IoStatus.Information = 1;
		}
	      else
		{
		  NOTE_ERROR ();
		  p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
		}
	      break;
	    }

	  case TAP_IOCTL_CONFIG_DHCP_MASQ:
	    {
	      if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
		  (sizeof (IPADDR) * 4))
		{
		  l_Adapter->m_dhcp_enabled = FALSE;
		  l_Adapter->m_dhcp_server_arp = FALSE;
		  l_Adapter->m_dhcp_user_supplied_options_buffer_len = 0;

		  // Adapter IP addr / netmask
		  l_Adapter->m_dhcp_addr =
		    ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0];
		  l_Adapter->m_dhcp_netmask =
		    ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1];

		  // IP addr of DHCP masq server
		  l_Adapter->m_dhcp_server_ip =
		    ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[2];

		  // Lease time in seconds
		  l_Adapter->m_dhcp_lease_time =
		    ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[3];

		  GenerateRelatedMAC (l_Adapter->m_dhcp_server_mac, l_Adapter->m_MAC, 2);

		  l_Adapter->m_dhcp_enabled = TRUE;
		  l_Adapter->m_dhcp_server_arp = TRUE;

		  CheckIfDhcpAndTunMode (l_Adapter);

		  p_IRP->IoStatus.Information = 1; // Simple boolean value
		}
	      else
		{
		  NOTE_ERROR ();
		  p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
		}
	      
	      break;
	    }

	  case TAP_IOCTL_CONFIG_DHCP_SET_OPT:
	    {
	      if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength <=
		  DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE
		  && l_Adapter->m_dhcp_enabled)
		{
		  l_Adapter->m_dhcp_user_supplied_options_buffer_len = 0;

		  NdisMoveMemory (l_Adapter->m_dhcp_user_supplied_options_buffer,
				  p_IRP->AssociatedIrp.SystemBuffer,
				  l_IrpSp->Parameters.DeviceIoControl.InputBufferLength);
		  
		  l_Adapter->m_dhcp_user_supplied_options_buffer_len = 
		    l_IrpSp->Parameters.DeviceIoControl.InputBufferLength;

		  p_IRP->IoStatus.Information = 1; // Simple boolean value
		}
	      else
		{
		  NOTE_ERROR ();
		  p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
		}
	      
	      break;
	    }

	  default:
	    {
	      NOTE_ERROR ();
	      p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
	      break;
	    }
	  }

	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
	break;
      }

      //===========================================================
      // User mode thread issued a read request on the tap device
      // If there are packets waiting to be read, then the request
      // will be satisfied here. If not, then the request will be
      // queued and satisfied by any packet that is not used to
      // satisfy requests ahead of it.
      //===========================================================
    case IRP_MJ_READ:
      {
	TapPacketPointer l_PacketBuffer;
	BOOLEAN pending = FALSE;

	// Save IRP-accessible copy of buffer length
	p_IRP->IoStatus.Information = l_IrpSp->Parameters.Read.Length;

	if (p_IRP->MdlAddress == NULL)
	  {
	    DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_READ\n",
		     NAME (l_Adapter)));
	    NOTE_ERROR ();
	    p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
	    p_IRP->IoStatus.Information = 0;
	    IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
	    break;
	  }
	else if ((p_IRP->AssociatedIrp.SystemBuffer =
		  MmGetSystemAddressForMdlSafe
		  (p_IRP->MdlAddress, NormalPagePriority)) == NULL)
	  {
	    DEBUGP (("[%s] Could not map address in IRP_MJ_READ\n",
		     NAME (l_Adapter)));
	    NOTE_ERROR ();
	    p_IRP->IoStatus.Status = l_Status = STATUS_INSUFFICIENT_RESOURCES;
	    p_IRP->IoStatus.Information = 0;
	    IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
	    break;
	  }
	else if (!l_Adapter->m_InterfaceIsRunning)
	  {
	    DEBUGP (("[%s] Interface is down in IRP_MJ_READ\n",
		     NAME (l_Adapter)));
	    NOTE_ERROR ();
	    p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
	    p_IRP->IoStatus.Information = 0;
	    IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
	    break;
	  }

	//==================================
	// Can we provide immediate service?
	//==================================

	l_PacketBuffer = NULL;

	NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);

	if (IS_UP (l_Adapter)
	    && QueueCount (l_Adapter->m_Extension.m_PacketQueue)
	    && QueueCount (l_Adapter->m_Extension.m_IrpQueue) == 0)
	  {
	    l_PacketBuffer = (TapPacketPointer)
	      QueuePop (l_Adapter->m_Extension.m_PacketQueue);
	  }

	NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);

	if (l_PacketBuffer)
	  {
	    l_Status = CompleteIRP (p_IRP,
				    l_PacketBuffer,
				    IO_NO_INCREMENT);
	    break;
	  }

	//=============================
	// Attempt to pend read request
	//=============================

	NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);

	if (IS_UP (l_Adapter)
	    && QueuePush (l_Adapter->m_Extension.m_IrpQueue, p_IRP) == (PIRP) p_IRP)
	  {
	    IoSetCancelRoutine (p_IRP, CancelIRPCallback);
	    l_Status = STATUS_PENDING;
	    IoMarkIrpPending (p_IRP);
	    pending = TRUE;
	  }

	NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);

	if (pending)
	  break;

	// Can't queue anymore IRP's
	DEBUGP (("[%s] TAP [%s] read IRP overrun\n",
		 NAME (l_Adapter), l_Adapter->m_Extension.m_TapName));
	NOTE_ERROR ();
	p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
	p_IRP->IoStatus.Information = 0;
	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
	break;
      }

      //==============================================================
      // User mode issued a WriteFile request on the TAP file handle.
      // The request will always get satisfied here.  The call may
      // fail if there are too many pending packets (queue full).
      //==============================================================
    case IRP_MJ_WRITE:
      {
	if (p_IRP->MdlAddress == NULL)
	  {
	    DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_WRITE\n",
		     NAME (l_Adapter)));
	    NOTE_ERROR ();
	    p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
	    p_IRP->IoStatus.Information = 0;
	  }
	else if ((p_IRP->AssociatedIrp.SystemBuffer =
		  MmGetSystemAddressForMdlSafe
		  (p_IRP->MdlAddress, NormalPagePriority)) == NULL)
	  {
	    DEBUGP (("[%s] Could not map address in IRP_MJ_WRITE\n",
		     NAME (l_Adapter)));
	    NOTE_ERROR ();
	    p_IRP->IoStatus.Status = l_Status = STATUS_INSUFFICIENT_RESOURCES;
	    p_IRP->IoStatus.Information = 0;
	  }
	else if (!l_Adapter->m_InterfaceIsRunning)
	  {
	    DEBUGP (("[%s] Interface is down in IRP_MJ_WRITE\n",
		     NAME (l_Adapter)));
	    NOTE_ERROR ();
	    p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
	    p_IRP->IoStatus.Information = 0;
	  }
	else if (!l_Adapter->m_tun && ((l_IrpSp->Parameters.Write.Length) >= ETHERNET_HEADER_SIZE))
	  {
	    __try
	      {
		p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length;

		DUMP_PACKET ("IRP_MJ_WRITE ETH",
			     (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
			     l_IrpSp->Parameters.Write.Length);

    //=====================================================
    // If IPv4 packet, check whether or not packet
    // was truncated.
    //=====================================================
#if PACKET_TRUNCATION_CHECK
		IPv4PacketSizeVerify ((unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
				      l_IrpSp->Parameters.Write.Length,
				      FALSE,
				      "RX",
				      &l_Adapter->m_RxTrunc);
#endif

		NdisMEthIndicateReceive
		  (l_Adapter->m_MiniportAdapterHandle,
		   (NDIS_HANDLE) l_Adapter,
		   (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
		   ETHERNET_HEADER_SIZE,
		   (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer + ETHERNET_HEADER_SIZE,
		   l_IrpSp->Parameters.Write.Length - ETHERNET_HEADER_SIZE,
		   l_IrpSp->Parameters.Write.Length - ETHERNET_HEADER_SIZE);
		
		NdisMEthIndicateReceiveComplete (l_Adapter->m_MiniportAdapterHandle);

		p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
	      }
	    __except (EXCEPTION_EXECUTE_HANDLER)
	      {
		DEBUGP (("[%s] NdisMEthIndicateReceive failed in IRP_MJ_WRITE\n",
			 NAME (l_Adapter)));
		NOTE_ERROR ();
		p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
		p_IRP->IoStatus.Information = 0;
	      }
	  }
	else if (l_Adapter->m_tun && ((l_IrpSp->Parameters.Write.Length) >= IP_HEADER_SIZE))
	  {
	    __try
	      {
		p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length;

		DUMP_PACKET2 ("IRP_MJ_WRITE P2P",
			      &l_Adapter->m_UserToTap,
			      (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
			      l_IrpSp->Parameters.Write.Length);

    //=====================================================
    // If IPv4 packet, check whether or not packet
    // was truncated.
    //=====================================================
#if PACKET_TRUNCATION_CHECK
		IPv4PacketSizeVerify ((unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
				      l_IrpSp->Parameters.Write.Length,
				      TRUE,
				      "RX",
				      &l_Adapter->m_RxTrunc);
#endif

		NdisMEthIndicateReceive
		  (l_Adapter->m_MiniportAdapterHandle,
		   (NDIS_HANDLE) l_Adapter,
		   (unsigned char *) &l_Adapter->m_UserToTap,
		   sizeof (l_Adapter->m_UserToTap),
		   (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
		   l_IrpSp->Parameters.Write.Length,
		   l_IrpSp->Parameters.Write.Length);

		NdisMEthIndicateReceiveComplete (l_Adapter->m_MiniportAdapterHandle);

		p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
	      }
	    __except (EXCEPTION_EXECUTE_HANDLER)
	      {
		DEBUGP (("[%s] NdisMEthIndicateReceive failed in IRP_MJ_WRITE (P2P)\n",
			 NAME (l_Adapter)));
		NOTE_ERROR ();
		p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
		p_IRP->IoStatus.Information = 0;
	      }
	  }
	else
	  {
	    DEBUGP (("[%s] Bad buffer size in IRP_MJ_WRITE, len=%d\n",
		     NAME (l_Adapter),
		     l_IrpSp->Parameters.Write.Length));
	    NOTE_ERROR ();
	    p_IRP->IoStatus.Information = 0;	// ETHERNET_HEADER_SIZE;
	    p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
	  }

	if (l_Status == STATUS_SUCCESS)
	  INCREMENT_STAT (l_Adapter->m_Rx);
	else
	  INCREMENT_STAT (l_Adapter->m_RxErr);

	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
	break;
      }

      //--------------------------------------------------------------
      //   User mode thread has called CreateFile() on the tap device
      //--------------------------------------------------------------
    case IRP_MJ_CREATE:
      {
	BOOLEAN succeeded = FALSE;
	BOOLEAN mutex_succeeded;

	DEBUGP
	  (("[%s] [TAP] release [%d.%d] open request (m_TapOpens=%d)\n",
	    NAME (l_Adapter), TAP_DRIVER_MAJOR_VERSION,
	    TAP_DRIVER_MINOR_VERSION, l_Adapter->m_Extension.m_TapOpens));

	ACQUIRE_MUTEX_ADAPTIVE (&l_Adapter->m_Extension.m_OpenCloseMutex, mutex_succeeded);
	if (mutex_succeeded)
	  {
	    if (l_Adapter->m_Extension.m_TapIsRunning && !l_Adapter->m_Extension.m_TapOpens)
	      {
		ResetTapAdapterState (l_Adapter);
		l_Adapter->m_Extension.m_TapOpens = 1;
		succeeded = TRUE;
	      }

	    if (succeeded)
	      {
		INCREMENT_STAT (l_Adapter->m_Extension.m_NumTapOpens);
		p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
		p_IRP->IoStatus.Information = 0;
	      }
	    else
	      {
		DEBUGP (("[%s] TAP is presently unavailable (m_TapOpens=%d)\n",
			 NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens));
		NOTE_ERROR ();
		p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
		p_IRP->IoStatus.Information = 0;
	      }

	    RELEASE_MUTEX (&l_Adapter->m_Extension.m_OpenCloseMutex);
	  }
	else
	  {
	    DEBUGP (("[%s] TAP is presently locked (m_TapOpens=%d)\n",
		     NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens));
	    NOTE_ERROR ();
	    p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
	    p_IRP->IoStatus.Information = 0;
	  }
	
	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
	break;
      }
      
      //-----------------------------------------------------------
      //  User mode thread called CloseHandle() on the tap device
      //-----------------------------------------------------------
    case IRP_MJ_CLOSE:
      {
	BOOLEAN mutex_succeeded;

	DEBUGP (("[%s] [TAP] release [%d.%d] close/cleanup request\n",
		 NAME (l_Adapter), TAP_DRIVER_MAJOR_VERSION,
		 TAP_DRIVER_MINOR_VERSION));

	ACQUIRE_MUTEX_ADAPTIVE (&l_Adapter->m_Extension.m_OpenCloseMutex, mutex_succeeded);
	if (mutex_succeeded)
	  {
	    l_Adapter->m_Extension.m_TapOpens = 0;
	    ResetTapAdapterState (l_Adapter);
	    FlushQueues (&l_Adapter->m_Extension);
	    SetMediaStatus (l_Adapter, FALSE);
	    RELEASE_MUTEX (&l_Adapter->m_Extension.m_OpenCloseMutex);
	  }
	else
	  {
	    DEBUGP (("[%s] TAP is presently locked (m_TapOpens=%d)\n",
		     NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens));
	    NOTE_ERROR ();
	    p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
	    p_IRP->IoStatus.Information = 0;
	  }
	
	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
	break;
      }

      //------------------
      // Strange Request
      //------------------
    default:
      {
	//NOTE_ERROR ();
	p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
	IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
	break;
      }
    }

  return l_Status;
}

//=============================================================
// CompleteIRP is normally called with an adapter -> userspace
// network packet and an IRP (Pending I/O request) from userspace.
//
// The IRP will normally represent a queued overlapped read
// operation from userspace that is in a wait state.
//
// Use the ethernet packet to satisfy the IRP.
//=============================================================

NTSTATUS
CompleteIRP (IN PIRP p_IRP,
	     IN TapPacketPointer p_PacketBuffer,
	     IN CCHAR PriorityBoost)
{
  NTSTATUS l_Status = STATUS_UNSUCCESSFUL;

  int offset;
  int len;

  MYASSERT (p_IRP);
  MYASSERT (p_PacketBuffer);

  IoSetCancelRoutine (p_IRP, NULL);  // Disable cancel routine

  //-------------------------------------------
  // While p_PacketBuffer always contains a
  // full ethernet packet, including the
  // ethernet header, in point-to-point mode,
  // we only want to return the IPv4
  // component.
  //-------------------------------------------

  if (p_PacketBuffer->m_SizeFlags & TP_TUN)
    {
      offset = ETHERNET_HEADER_SIZE;
      len = (int) (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK) - ETHERNET_HEADER_SIZE;
    }
  else
    {
      offset = 0;
      len = (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK);
    }

  if (len < 0 || (int) p_IRP->IoStatus.Information < len)
    {
      p_IRP->IoStatus.Information = 0;
      p_IRP->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
      NOTE_ERROR ();
    }
  else
    {
      p_IRP->IoStatus.Information = len;
      p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;

      __try
	{
	  NdisMoveMemory (p_IRP->AssociatedIrp.SystemBuffer,
			  p_PacketBuffer->m_Data + offset,
			  len);
	}
      __except (EXCEPTION_EXECUTE_HANDLER)
	{
	  NOTE_ERROR ();
	  p_IRP->IoStatus.Status = STATUS_UNSUCCESSFUL;
	  p_IRP->IoStatus.Information = 0;
	}
    }

  __try
    {
      NdisFreeMemory (p_PacketBuffer,
		      TAP_PACKET_SIZE (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK),
		      0);
    }
  __except (EXCEPTION_EXECUTE_HANDLER)
    {
    }
  
  if (l_Status == STATUS_SUCCESS)
    {
      IoCompleteRequest (p_IRP, PriorityBoost);
    }
  else
    IoCompleteRequest (p_IRP, IO_NO_INCREMENT);

  return l_Status;
}

//==============================================
// IRPs get cancelled for a number of reasons.
//
// The TAP device could be closed by userspace
// when there are still pending read operations.
//
// The user could disable the TAP adapter in the
// network connections control panel, while the
// device is still open by a process.
//==============================================
VOID
CancelIRPCallback (IN PDEVICE_OBJECT p_DeviceObject,
		   IN PIRP p_IRP)
{
  TapAdapterPointer l_Adapter = LookupAdapterInInstanceList (p_DeviceObject);
  CancelIRP (l_Adapter ? &l_Adapter->m_Extension : NULL, p_IRP, TRUE);
}

VOID
CancelIRP (TapExtensionPointer p_Extension,
	   IN PIRP p_IRP,
	   BOOLEAN callback)
{
  BOOLEAN exists = FALSE;

  MYASSERT (p_IRP);

  if (p_Extension)
    {
      NdisAcquireSpinLock (&p_Extension->m_QueueLock);
      exists = (QueueExtract (p_Extension->m_IrpQueue, p_IRP) == p_IRP);
      NdisReleaseSpinLock (&p_Extension->m_QueueLock);
    }
  else
    exists = TRUE;

  if (exists)
    {
      IoSetCancelRoutine (p_IRP, NULL);
      p_IRP->IoStatus.Status = STATUS_CANCELLED;
      p_IRP->IoStatus.Information = 0;
    }
     
  if (callback)
    IoReleaseCancelSpinLock (p_IRP->CancelIrql);

  if (exists)
    IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
}

//====================================
// Exhaust packet and IRP queues.
//====================================
VOID
FlushQueues (TapExtensionPointer p_Extension)
{
  PIRP l_IRP;
  TapPacketPointer l_PacketBuffer;
  int n_IRP=0, n_Packet=0;

  MYASSERT (p_Extension);
  MYASSERT (p_Extension->m_TapDevice);

  while (TRUE)
    {
      NdisAcquireSpinLock (&p_Extension->m_QueueLock);
      l_IRP = QueuePop (p_Extension->m_IrpQueue);
      NdisReleaseSpinLock (&p_Extension->m_QueueLock);
      if (l_IRP)
	{
	  ++n_IRP;
	  CancelIRP (NULL, l_IRP, FALSE);
	}
      else
	break;
    }

  while (TRUE)
    {
      NdisAcquireSpinLock (&p_Extension->m_QueueLock);
      l_PacketBuffer = QueuePop (p_Extension->m_PacketQueue);
      NdisReleaseSpinLock (&p_Extension->m_QueueLock);
      if (l_PacketBuffer)
	{
	  ++n_Packet;
	  MemFree (l_PacketBuffer, TAP_PACKET_SIZE (l_PacketBuffer->m_SizeFlags & TP_SIZE_MASK));
	}
      else
	break;
    }

  DEBUGP ((
	   "[%s] [TAP] FlushQueues n_IRP=[%d,%d,%d] n_Packet=[%d,%d,%d]\n",
	   p_Extension->m_TapName,
	   n_IRP,
	   p_Extension->m_IrpQueue->max_size,
	   IRP_QUEUE_SIZE,
	   n_Packet,
	   p_Extension->m_PacketQueue->max_size,
	   PACKET_QUEUE_SIZE
	   ));
}

//===================================================
// Tell Windows whether the TAP device should be
// considered "connected" or "disconnected".
//===================================================
VOID
SetMediaStatus (TapAdapterPointer p_Adapter, BOOLEAN state)
{
  if (p_Adapter->m_MediaState != state && !p_Adapter->m_MediaStateAlwaysConnected)
    {
      if (state)
	NdisMIndicateStatus (p_Adapter->m_MiniportAdapterHandle,
			     NDIS_STATUS_MEDIA_CONNECT, NULL, 0);
      else
	NdisMIndicateStatus (p_Adapter->m_MiniportAdapterHandle,
			     NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0);

      NdisMIndicateStatusComplete (p_Adapter->m_MiniportAdapterHandle);
      p_Adapter->m_MediaState = state;
    }
}


//======================================================
// If DHCP mode is used together with tun
// mode, consider the fact that the P2P remote subnet
// might enclose the DHCP masq server address.
//======================================================
VOID
CheckIfDhcpAndTunMode (TapAdapterPointer p_Adapter)
{
  if (p_Adapter->m_tun && p_Adapter->m_dhcp_enabled)
    {
      if ((p_Adapter->m_dhcp_server_ip & p_Adapter->m_remoteNetmask) == p_Adapter->m_remoteNetwork)
	{
	  COPY_MAC (p_Adapter->m_dhcp_server_mac, p_Adapter->m_TapToUser.dest);
	  p_Adapter->m_dhcp_server_arp = FALSE;
	}
    }
}

//===================================================
// Generate an ARP reply message for specific kinds
// ARP queries.
//===================================================
BOOLEAN
ProcessARP (TapAdapterPointer p_Adapter,
	    const PARP_PACKET src,
	    const IPADDR adapter_ip,
	    const IPADDR ip_network,
	    const IPADDR ip_netmask,
	    const MACADDR mac)
{
  //-----------------------------------------------
  // Is this the kind of packet we are looking for?
  //-----------------------------------------------
  if (src->m_Proto == htons (ETH_P_ARP)
      && MAC_EQUAL (src->m_MAC_Source, p_Adapter->m_MAC)
      && MAC_EQUAL (src->m_ARP_MAC_Source, p_Adapter->m_MAC)
      && MAC_EQUAL (src->m_MAC_Destination, p_Adapter->m_MAC_Broadcast)
      && src->m_ARP_Operation == htons (ARP_REQUEST)
      && src->m_MAC_AddressType == htons (MAC_ADDR_TYPE)
      && src->m_MAC_AddressSize == sizeof (MACADDR)
      && src->m_PROTO_AddressType == htons (ETH_P_IP)
      && src->m_PROTO_AddressSize == sizeof (IPADDR)
      && src->m_ARP_IP_Source == adapter_ip
      && (src->m_ARP_IP_Destination & ip_netmask) == ip_network
      && src->m_ARP_IP_Destination != adapter_ip)
    {
      ARP_PACKET *arp = (ARP_PACKET *) MemAlloc (sizeof (ARP_PACKET), TRUE);
      if (arp)
	{
	  //----------------------------------------------
	  // Initialize ARP reply fields
	  //----------------------------------------------
	  arp->m_Proto = htons (ETH_P_ARP);
	  arp->m_MAC_AddressType = htons (MAC_ADDR_TYPE);
	  arp->m_PROTO_AddressType = htons (ETH_P_IP);
	  arp->m_MAC_AddressSize = sizeof (MACADDR);
	  arp->m_PROTO_AddressSize = sizeof (IPADDR);
	  arp->m_ARP_Operation = htons (ARP_REPLY);

	  //----------------------------------------------
	  // ARP addresses
	  //----------------------------------------------      
	  COPY_MAC (arp->m_MAC_Source, mac);
	  COPY_MAC (arp->m_MAC_Destination, p_Adapter->m_MAC);
	  COPY_MAC (arp->m_ARP_MAC_Source, mac);
	  COPY_MAC (arp->m_ARP_MAC_Destination, p_Adapter->m_MAC);
	  arp->m_ARP_IP_Source = src->m_ARP_IP_Destination;
	  arp->m_ARP_IP_Destination = adapter_ip;

	  DUMP_PACKET ("ProcessARP",
		       (unsigned char *) arp,
		       sizeof (ARP_PACKET));

	  InjectPacket (p_Adapter, (UCHAR *) arp, sizeof (ARP_PACKET));

	  MemFree (arp, sizeof (ARP_PACKET));
	}

      return TRUE;
    }
  else
    return FALSE;
}

//===============================================================
// Used in cases where internally generated packets such as
// ARP or DHCP replies must be returned to the kernel, to be
// seen as an incoming packet "arriving" on the interface.
//===============================================================

VOID
InjectPacket (TapAdapterPointer p_Adapter,
	      UCHAR *packet,
	      const unsigned int len)
{
  MYASSERT (len >= ETHERNET_HEADER_SIZE);

  __try
    {
      //------------------------------------------------------------
      // NdisMEthIndicateReceive and NdisMEthIndicateReceiveComplete
      // could potentially be called reentrantly both here and in
      // TapDeviceHook/IRP_MJ_WRITE.
      //
      // The DDK docs imply that this is okay.
      //------------------------------------------------------------
      NdisMEthIndicateReceive
	(p_Adapter->m_MiniportAdapterHandle,
	 (NDIS_HANDLE) p_Adapter,
	 packet,
	 ETHERNET_HEADER_SIZE,
	 packet + ETHERNET_HEADER_SIZE,
	 len - ETHERNET_HEADER_SIZE,
	 len - ETHERNET_HEADER_SIZE);
      
      NdisMEthIndicateReceiveComplete (p_Adapter->m_MiniportAdapterHandle);
    }
  __except (EXCEPTION_EXECUTE_HANDLER)
    {
      DEBUGP (("[%s] NdisMEthIndicateReceive failed in InjectPacket\n",
	       NAME (p_Adapter)));
      NOTE_ERROR ();
    }
}

//===================================================================
// Go back to default TAP mode from Point-To-Point mode.
// Also reset (i.e. disable) DHCP Masq mode.
//===================================================================
VOID ResetTapAdapterState (TapAdapterPointer p_Adapter)
{
  // Point-To-Point
  p_Adapter->m_tun = FALSE;
  p_Adapter->m_localIP = 0;
  p_Adapter->m_remoteNetwork = 0;
  p_Adapter->m_remoteNetmask = 0;
  NdisZeroMemory (&p_Adapter->m_TapToUser, sizeof (p_Adapter->m_TapToUser));
  NdisZeroMemory (&p_Adapter->m_UserToTap, sizeof (p_Adapter->m_UserToTap));

  // DHCP Masq
  p_Adapter->m_dhcp_enabled = FALSE;
  p_Adapter->m_dhcp_server_arp = FALSE;
  p_Adapter->m_dhcp_user_supplied_options_buffer_len = 0;
  p_Adapter->m_dhcp_addr = 0;
  p_Adapter->m_dhcp_netmask = 0;
  p_Adapter->m_dhcp_server_ip = 0;
  p_Adapter->m_dhcp_lease_time = 0;
  p_Adapter->m_dhcp_received_discover = FALSE;
  p_Adapter->m_dhcp_bad_requests = 0;
  NdisZeroMemory (p_Adapter->m_dhcp_server_mac, sizeof (MACADDR));
}

#if ENABLE_NONADMIN

//===================================================================
// Set TAP device handle to be accessible without admin privileges.
//===================================================================
VOID AllowNonAdmin (TapExtensionPointer p_Extension)
{
  NTSTATUS stat;
  SECURITY_DESCRIPTOR sd;
  OBJECT_ATTRIBUTES oa;
  IO_STATUS_BLOCK isb;
  HANDLE hand = NULL;

  NdisZeroMemory (&sd, sizeof (sd));
  NdisZeroMemory (&oa, sizeof (oa));
  NdisZeroMemory (&isb, sizeof (isb));

  if (!p_Extension->m_CreatedUnicodeLinkName)
    {
      DEBUGP (("[TAP] AllowNonAdmin: UnicodeLinkName is uninitialized\n"));
      NOTE_ERROR ();
      return;
    }

  stat = RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
  if (stat != STATUS_SUCCESS)
    {
      DEBUGP (("[TAP] AllowNonAdmin: RtlCreateSecurityDescriptor failed\n"));
      NOTE_ERROR ();
      return;
    }

  InitializeObjectAttributes (
    &oa,
    &p_Extension->m_UnicodeLinkName,
    OBJ_KERNEL_HANDLE,
    NULL,
    NULL
    );

  stat = ZwOpenFile (
    &hand,
    WRITE_DAC,
    &oa,
    &isb,
    0,
    0
    );
  if (stat != STATUS_SUCCESS)
    {
      DEBUGP (("[TAP] AllowNonAdmin: ZwOpenFile failed, status=0x%08x\n", (unsigned int)stat));
      NOTE_ERROR ();
      return;
    }

  stat = ZwSetSecurityObject (hand, DACL_SECURITY_INFORMATION, &sd);
  if (stat != STATUS_SUCCESS)
    {
      DEBUGP (("[TAP] AllowNonAdmin: ZwSetSecurityObject failed\n"));
      NOTE_ERROR ();
      return;
    }

  stat = ZwClose (hand);
  if (stat != STATUS_SUCCESS)
    {
      DEBUGP (("[TAP] AllowNonAdmin: ZwClose failed\n"));
      NOTE_ERROR ();
      return;
    }

  DEBUGP (("[TAP] AllowNonAdmin: SUCCEEDED\n"));
}

#endif

#if PACKET_TRUNCATION_CHECK

VOID
IPv4PacketSizeVerify (const UCHAR *data, ULONG length, BOOLEAN tun, const char *prefix, LONG *counter)
{
  const IPHDR *ip;
  int len = length;

  if (tun)
    {
      ip = (IPHDR *) data;
    }
  else
    {
      if (length >= sizeof (ETH_HEADER))
	{
	  const ETH_HEADER *eth = (ETH_HEADER *) data;

	  if (eth->proto != htons (ETH_P_IP))
	    return;

	  ip = (IPHDR *) (data + sizeof (ETH_HEADER));
	  len -= sizeof (ETH_HEADER);
	}
      else
	return;
    }

  if (len >= sizeof (IPHDR))
    {
      const int totlen = ntohs (ip->tot_len);

      DEBUGP (("[TAP] IPv4PacketSizeVerify %s len=%d totlen=%d\n", prefix, len, totlen));

      if (len != totlen)
	++(*counter);
    }
}

#endif

//======================================================================
//                                    End of Source
//======================================================================