/*++ Copyright (c) 1991-2000 Microsoft Corporation Module Name: ntip.c Abstract: NT specific routines for loading and configuring the IP driver. Author: Mike Massa (mikemas) Aug 13, 1993 Revision History: Who When What -------- -------- ---------------------------------------------- mikemas 08-13-93 created Notes: --*/ #include "precomp.h" #include "iproute.h" #include "lookup.h" #include "iprtdef.h" #include "internaldef.h" #include "tcp.h" #include "tcpipbuf.h" #include "mdlpool.h" // // definitions needed by inet_addr. // #define INADDR_NONE 0xffffffff #define INADDR_ANY 0 #define htonl(x) net_long(x) // // Other local constants // #define WORK_BUFFER_SIZE 256 // size of nte context value in string form #define NTE_CONTEXT_SIZE (sizeof(uint)*2+2) // 0xAABBCCDD // // Configuration defaults // #define DEFAULT_IGMP_LEVEL 2 #define DEFAULT_IP_NETS 8 #if MILLEN // On Win9x, this will help boot time and resume time. #define DEFAULT_ARPRETRY_COUNT 1 #else // MILLEN #define DEFAULT_ARPRETRY_COUNT 3 #endif // !MILLEN // // Local types // typedef struct _PerNetConfigInfo { uint UseZeroBroadcast; uint Mtu; uint NumberOfGateways; uint MaxForwardPending; // max routing packets pending } PER_NET_CONFIG_INFO, *PPER_NET_CONFIG_INFO; // // Global variables. // PDRIVER_OBJECT IPDriverObject; PDEVICE_OBJECT IPDeviceObject; HANDLE IPProviderHandle = NULL; #if IPMCAST PDEVICE_OBJECT IpMcastDeviceObject; NTSTATUS InitializeIpMcast( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath, OUT PDEVICE_OBJECT * ppIpMcastDevice ); VOID DeinitializeIpMcast( IN PDEVICE_OBJECT DeviceObject ); #endif // IPMCAST IPConfigInfo *IPConfiguration; uint ArpUseEtherSnap = FALSE; uint ArpAlwaysSourceRoute = FALSE; uint IPAlwaysSourceRoute = TRUE; extern uint DisableIPSourceRouting; uint ArpCacheLife = DEFAULT_ARP_CACHE_LIFE; uint ArpRetryCount = DEFAULT_ARPRETRY_COUNT; uint ArpMinValidCacheLife = DEFAULT_ARP_MIN_VALID_CACHE_LIFE; uint DisableMediaSense = 0; uint DisableMediaSenseEventLog; uint EnableBcastArpReply = TRUE; #if MILLEN // Millennium does not support task offload. uint DisableTaskOffload = TRUE; #else // MILLEN uint DisableTaskOffload = FALSE; #endif // !MILLEN uint DisableUserTOS = TRUE; extern uint MaxRH; extern uint NET_TABLE_SIZE; extern uint DampingInterval; extern uint ConnectDampingInterval; // Used in the conversion of 100ns times to milliseconds. static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758}; // // External variables // extern LIST_ENTRY PendingEchoList; // def needed for initialization extern LIST_ENTRY PendingIPSetNTEAddrList; // def needed for initialization extern LIST_ENTRY PendingIPEventList; // def needed for initialization extern LIST_ENTRY PendingEnableRouterList; // def needed for initialization extern LIST_ENTRY PendingArpSendList; // def needed for initialization extern LIST_ENTRY PendingMediaSenseRequestList; CTEBlockStruc TcpipUnloadBlock; // Structure for blocking at time of unload extern CACHE_LINE_KSPIN_LOCK ArpInterfaceListLock; BOOLEAN fRouteTimerStopping = FALSE; extern CTETimer IPRouteTimer; extern LIST_ENTRY ArpInterfaceList; extern HANDLE IpHeaderPool; DEFINE_LOCK_STRUCTURE(ArpModuleLock) extern void FreeFirewallQ(void); extern VOID TCPUnload(IN PDRIVER_OBJECT DriverObject); extern uint EnableICMPRedirects; extern NDIS_HANDLE NdisPacketPool; extern NDIS_HANDLE TDPacketPool; extern NDIS_HANDLE TDBufferPool; extern TDIEntityID* IPEntityList; extern uint IPEntityCount; extern PWSTR IPBindList; KMUTEX NTEContextMutex; int ARPInit(); // // Macros // //++ // // LARGE_INTEGER // CTEConvertMillisecondsTo100ns( // IN LARGE_INTEGER MsTime // ); // // Routine Description: // // Converts time expressed in hundreds of nanoseconds to milliseconds. // // Arguments: // // MsTime - Time in milliseconds. // // Return Value: // // Time in hundreds of nanoseconds. // //-- #define CTEConvertMillisecondsTo100ns(MsTime) \ RtlExtendedIntegerMultiply(MsTime, 10000) //++ // // LARGE_INTEGER // CTEConvert100nsToMilliseconds( // IN LARGE_INTEGER HnsTime // ); // // Routine Description: // // Converts time expressed in hundreds of nanoseconds to milliseconds. // // Arguments: // // HnsTime - Time in hundreds of nanoseconds. // // Return Value: // // Time in milliseconds. // //-- #define SHIFT10000 13 extern LARGE_INTEGER Magic10000; #define CTEConvert100nsToMilliseconds(HnsTime) \ RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000) // // External function prototypes // extern int IPInit( void ); long IPSetInfo( TDIObjectID * ID, void *Buffer, uint Size ); NTSTATUS IPDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS OpenRegKey( PHANDLE HandlePtr, PWCHAR KeyName ); NTSTATUS GetRegDWORDValue( HANDLE KeyHandle, PWCHAR ValueName, PULONG ValueData ); NTSTATUS GetRegLARGEINTValue( HANDLE KeyHandle, PWCHAR ValueName, PLARGE_INTEGER ValueData ); NTSTATUS SetRegDWORDValue( HANDLE KeyHandle, PWCHAR ValueName, PULONG ValueData ); NTSTATUS GetRegSZValue( HANDLE KeyHandle, PWCHAR ValueName, PUNICODE_STRING ValueData, PULONG ValueType ); NTSTATUS GetRegMultiSZValue( HANDLE KeyHandle, PWCHAR ValueName, PUNICODE_STRING ValueData ); NTSTATUS GetRegMultiSZValueNew( HANDLE KeyHandle, PWCHAR ValueName, PUNICODE_STRING_NEW ValueData ); NTSTATUS InitRegDWORDParameter( HANDLE RegKey, PWCHAR ValueName, ULONG * Value, ULONG DefaultValue ); uint RTReadNext( void *Context, void *Buffer ); uint RTValidateContext( void *Context, uint * Valid ); extern NTSTATUS SetRegMultiSZValue( HANDLE KeyHandle, PWCHAR ValueName, PUNICODE_STRING ValueData ); extern NTSTATUS SetRegMultiSZValueNew( HANDLE KeyHandle, PWCHAR ValueName, PUNICODE_STRING_NEW ValueData ); // // Local funcion prototypes // NTSTATUS IPDriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); NTSTATUS IPProcessConfiguration( VOID ); NTSTATUS IPProcessAdapterSection( WCHAR * DeviceName, WCHAR * AdapterName ); uint GetGeneralIFConfig( IFGeneralConfig * ConfigInfo, NDIS_HANDLE Handle, PNDIS_STRING ConfigName ); int IsLLInterfaceValueNull( NDIS_HANDLE Handle ); NTSTATUS GetLLInterfaceValue( NDIS_HANDLE Handle, PNDIS_STRING valueString ); IFAddrList * GetIFAddrList( UINT * NumAddr, NDIS_HANDLE Handle, UINT * EnableDhcp, BOOLEAN PppIf, PNDIS_STRING ConfigName ); UINT OpenIFConfig( PNDIS_STRING ConfigName, NDIS_HANDLE * Handle ); VOID CloseIFConfig( NDIS_HANDLE Handle ); IPConfigInfo * IPGetConfig( void ); void IPFreeConfig( IPConfigInfo * ConfigInfo ); ulong GetGMTDelta( void ); ulong GetTime( void ); BOOLEAN IPConvertStringToAddress( IN PWCHAR AddressString, OUT PULONG IpAddress ); uint UseEtherSNAP( PNDIS_STRING Name ); void GetAlwaysSourceRoute( uint * pArpAlwaysSourceRoute, uint * pIPAlwaysSourceRoute ); uint GetArpCacheLife( void ); uint GetArpRetryCount( void ); ULONG RouteMatch( IN WCHAR * RouteString, IN IPAddr Address, IN IPMask Mask, OUT IPAddr * DestVal, OUT IPMask * DestMask, OUT IPAddr * GateVal, OUT ULONG * Metric ); VOID SetPersistentRoutesForNTE( IPAddr Address, IPMask Mask, ULONG IFIndex ); BOOLEAN GetTempDHCPAddr( NDIS_HANDLE Handle, IPAddr * Tempdhcpaddr, IPAddr * TempMask, IPAddr * TempGWAddr, PNDIS_STRING ConfigName ); #ifdef ALLOC_PRAGMA #if !MILLEN #pragma alloc_text(INIT, IPDriverEntry) #endif // !MILLEN #pragma alloc_text(INIT, IPProcessConfiguration) #pragma alloc_text(INIT, IPProcessAdapterSection) #pragma alloc_text(INIT, IPGetConfig) #pragma alloc_text(INIT, IPFreeConfig) #pragma alloc_text(INIT, GetGMTDelta) #pragma alloc_text(PAGE, GetGeneralIFConfig) #pragma alloc_text(PAGE, IsLLInterfaceValueNull) #pragma alloc_text(PAGE, GetLLInterfaceValue) #pragma alloc_text(PAGE, GetIFAddrList) #pragma alloc_text(PAGE, UseEtherSNAP) #pragma alloc_text(PAGE, GetAlwaysSourceRoute) #pragma alloc_text(PAGE, GetArpCacheLife) #pragma alloc_text(PAGE, GetArpRetryCount) #if !MILLEN #pragma alloc_text(PAGE, OpenIFConfig) #pragma alloc_text(PAGE, CloseIFConfig) #pragma alloc_text(PAGE, RouteMatch) #pragma alloc_text(PAGE, IPConvertStringToAddress) #endif // !MILLEN #endif // ALLOC_PRAGMA // // Function definitions // NTSTATUS IPDriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Initialization routine for the IP driver. Arguments: DriverObject - Pointer to the IP driver object created by the system. DeviceDescription - The name of IP's node in the registry. Return Value: The final status from the initialization operation. --*/ { NTSTATUS status; UNICODE_STRING deviceName; UNICODE_STRING SymbolicDeviceName; DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("+IPDriverEntry(%x, %x)\n"), DriverObject, RegistryPath)); IPDriverObject = DriverObject; // // Create the device object. IoCreateDevice zeroes the memory // occupied by the object. // RtlInitUnicodeString(&deviceName, DD_IP_DEVICE_NAME); RtlInitUnicodeString(&SymbolicDeviceName, DD_IP_SYMBOLIC_DEVICE_NAME); status = IoCreateDevice( DriverObject, 0, &deviceName, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &IPDeviceObject ); if (!NT_SUCCESS(status)) { CTELogEvent( DriverObject, EVENT_TCPIP_CREATE_DEVICE_FAILED, 1, 1, &deviceName.Buffer, 0, NULL ); DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("IP init failed. Failure %x to create device object %ws\n"), status, DD_IP_DEVICE_NAME)); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-IPDriverEntry [%x]\n"), status)); return (status); } status = IoCreateSymbolicLink(&SymbolicDeviceName, &deviceName); if (!NT_SUCCESS(status)) { CTELogEvent( DriverObject, EVENT_TCPIP_CREATE_DEVICE_FAILED, 1, 1, &deviceName.Buffer, 0, NULL ); DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("IP init failed. Failure %x to create symbolic device name %ws\n"), status, DD_IP_SYMBOLIC_DEVICE_NAME)); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-IPDriverEntry [%x]\n"), status)); return (status); } status = TdiRegisterProvider(&deviceName, &IPProviderHandle); if (!NT_SUCCESS(status)) { IoDeleteDevice(IPDeviceObject); DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("IP init failed. Failure %x to register provider\n"), status)); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-IPDriverEntry [%x]\n"), status)); return (status); } // // Intialize the device object. // IPDeviceObject->Flags |= DO_DIRECT_IO; // // Initialize the list of pending echo request IRPs. // InitializeListHead(&PendingEchoList); InitializeListHead(&PendingArpSendList); // // Initialize the list of pending SetAddr request IRPs. // InitializeListHead(&PendingIPSetNTEAddrList); // // Initialize the list of pending media sense event. // InitializeListHead(&PendingIPEventList); // // Initialize the list of pending enable-router requests. // InitializeListHead(&PendingEnableRouterList); // // Initialize the ARP interface list; used in ArpUnload to walk the // list of ARP IFs so UnBinds can be issued on these. // InitializeListHead(&ArpInterfaceList); // // Init the lock to protect this list // CTEInitLock(&ArpInterfaceListLock.Lock); // // Initialize the list of ARP modules // InitializeListHead(&ArpModuleList); CTEInitLock(&ArpModuleLock); // Initialize media sense request list InitializeListHead(&PendingMediaSenseRequestList); // // Initialize the NTE context-list mutex. // KeInitializeMutex(&NTEContextMutex, 0); // // Finally, read our configuration parameters from the registry. // status = IPProcessConfiguration(); if (status != STATUS_SUCCESS) { if (IPProviderHandle) { TdiDeregisterProvider(IPProviderHandle); } IoDeleteDevice(IPDeviceObject); DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("IPDriverEntry: IPProcessConfiguration failure %x\n"), status)); #if IPMCAST DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-IPDriverEntry [%x]\n"), status)); return status; #endif // IPMCAST } #if IPMCAST // // IP initialized successfully // IpMcastDeviceObject = NULL; status = InitializeIpMcast(DriverObject, RegistryPath, &IpMcastDeviceObject); if (status != STATUS_SUCCESS) { TCPTRACE(("IP initialization failed: Unable to initialize multicast. Status %x", status)); /* CTELogEvent(DriverObject, EVENT_IPMCAST_INIT_FAILED, 1, 1, &deviceName.Buffer, 0, NULL);*/ } // // Mcast init failures is not treated as fatal // status = STATUS_SUCCESS; #endif // IPMCAST DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-IPDriverEntry [%x]\n"), status)); return status; } // // Function definitions // NTSTATUS IPPostDriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Initialization routine for the IP driver. Arguments: DriverObject - Pointer to the IP driver object created by the system. DeviceDescription - The name of IP's node in the registry. Return Value: The final status from the initialization operation. --*/ { DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("+IPPostDriverEntry(%x, %x)\n"), DriverObject, RegistryPath)); if (!ARPInit()) { DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("IPPostDriverEntry: ARPInit failure.\n"))); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-IPPostDriverEntry [FAILURE]\n"))); return IP_INIT_FAILURE; // Couldn't initialize ARP. } DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-IPPostDriverEntry [SUCCESS]\n"))); return IP_SUCCESS; } NTSTATUS IPProcessConfiguration( VOID ) /*++ Routine Description: Reads the IP configuration information from the registry and constructs the configuration structure expected by the IP driver. Arguments: None. Return Value: STATUS_SUCCESS or an error status if an operation fails. --*/ { NTSTATUS status; HANDLE myRegKey = NULL; UNICODE_STRING bindString; WCHAR *aName, *endOfString; WCHAR IPParametersRegistryKey[] = #if MILLEN L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\VxD\\MSTCP"; #else // MILLEN L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters"; WCHAR IPLinkageRegistryKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Linkage"; #endif // !MILLEN uint ArpTRSingleRoute; MM_SYSTEMSIZE systemSize; ULONG ulongValue; const ULONG DefaultMaxNormLookupMem[] = { DEFAULT_MAX_NORM_LOOKUP_MEM_SMALL, DEFAULT_MAX_NORM_LOOKUP_MEM_MEDIUM, DEFAULT_MAX_NORM_LOOKUP_MEM_LARGE }; const ULONG DefaultMaxFastLookupMem[] = { DEFAULT_MAX_FAST_LOOKUP_MEM_SMALL, DEFAULT_MAX_FAST_LOOKUP_MEM_MEDIUM, DEFAULT_MAX_FAST_LOOKUP_MEM_LARGE }; const ULONG DefaultFastLookupLevels[] = { DEFAULT_EXPN_LEVELS_SMALL, DEFAULT_EXPN_LEVELS_MEDIUM, DEFAULT_EXPN_LEVELS_LARGE }; DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("+IPProcessConfiguration()\n"))); bindString.Buffer = NULL; IPConfiguration = CTEAllocMemBoot(sizeof(IPConfigInfo)); if (IPConfiguration == NULL) { CTELogEvent( IPDriverObject, EVENT_TCPIP_NO_RESOURCES_FOR_INIT, 1, 0, NULL, 0, NULL ); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-IPProcessConfiguration [NO_RESOURCES]\n"))); return (STATUS_INSUFFICIENT_RESOURCES); } RtlZeroMemory(IPConfiguration, sizeof(IPConfigInfo)); // // Process the Ip\Parameters section of the registry // status = OpenRegKey(&myRegKey, IPParametersRegistryKey); if (NT_SUCCESS(status)) { DEBUGMSG(DBG_INFO && DBG_INIT, (DTEXT("IPProcessConfiguration: Opened registry path %ws, initializing variables.\n"), IPParametersRegistryKey)); // // Expected configuration values. We use reasonable defaults if they // aren't available for some reason. // status = GetRegDWORDValue( myRegKey, L"IpEnableRouter", &(IPConfiguration->ici_gateway) ); #if MILLEN // // Backwards compatibility. If 'IpEnableRouter' key is not present, then // try to read legacy 'EnableRouting' key. // if (!NT_SUCCESS(status)) { status = GetRegDWORDValue( myRegKey, L"EnableRouting", &(IPConfiguration->ici_gateway) ); } #endif // MILLEN if (!NT_SUCCESS(status)) { TCPTRACE(( "IP: Unable to read IpEnableRouter value from the registry.\n" " Routing will be disabled.\n" )); IPConfiguration->ici_gateway = 0; } (VOID) InitRegDWORDParameter( myRegKey, L"EnableAddrMaskReply", &(IPConfiguration->ici_addrmaskreply), FALSE ); // // Optional (hidden) values // status = InitRegDWORDParameter( myRegKey, L"ForwardBufferMemory", &(IPConfiguration->ici_fwbufsize), DEFAULT_FW_BUFSIZE ); #if MILLEN // // Backwards compatibility. If the 'ForwardBufferMemory' value is not // present, then attempt to read legacy 'RoutingBufSize' value. // if (!NT_SUCCESS(status)) { InitRegDWORDParameter( myRegKey, L"RoutingBufSize", &(IPConfiguration->ici_fwbufsize), DEFAULT_FW_BUFSIZE ); } #endif // MILLEN status = InitRegDWORDParameter( myRegKey, L"MaxForwardBufferMemory", &(IPConfiguration->ici_maxfwbufsize), DEFAULT_MAX_FW_BUFSIZE ); #if MILLEN // // Backwards compatibility. If the 'MaxForwardBufferMemory' value is not // present, then attempt to read legacy 'MaxRoutingBufSize' value. // if (!NT_SUCCESS(status)) { InitRegDWORDParameter( myRegKey, L"MaxRoutingBufSize", &(IPConfiguration->ici_maxfwbufsize), DEFAULT_MAX_FW_BUFSIZE ); } #endif // MILLEN (VOID) InitRegDWORDParameter( myRegKey, L"ForwardBroadcasts", &(IPConfiguration->ici_fwbcast), FALSE ); status = InitRegDWORDParameter( myRegKey, L"NumForwardPackets", &(IPConfiguration->ici_fwpackets), DEFAULT_FW_PACKETS ); #if MILLEN // // Backwards compatibility. If the 'NumForwardPackets' value is not // present, then attempt to read legacy 'RoutingPackets' value. // if (!NT_SUCCESS(status)) { InitRegDWORDParameter( myRegKey, L"RoutingPackets", &(IPConfiguration->ici_fwpackets), DEFAULT_FW_PACKETS ); } #endif // MILLEN status = InitRegDWORDParameter( myRegKey, L"MaxNumForwardPackets", &(IPConfiguration->ici_maxfwpackets), DEFAULT_MAX_FW_PACKETS ); #if MILLEN // // Backwards compatibility. If the 'MaxNumForwardPackets' value is not // present, then attempt to read legacy 'MaxRoutingPackets' value. // if (!NT_SUCCESS(status)) { InitRegDWORDParameter( myRegKey, L"MaxRoutingPackets", &(IPConfiguration->ici_maxfwpackets), DEFAULT_MAX_FW_PACKETS ); } #endif // MILLEN (VOID) InitRegDWORDParameter( myRegKey, L"IGMPLevel", &(IPConfiguration->ici_igmplevel), DEFAULT_IGMP_LEVEL ); status = InitRegDWORDParameter( myRegKey, L"EnableDeadGWDetect", &(IPConfiguration->ici_deadgwdetect), TRUE ); #if MILLEN // // Backwards compatibility. If EnableDeadGWDetect key did not exist, then // check for the DeadGWDetect key. Same default value. // if (!NT_SUCCESS(status)) { InitRegDWORDParameter( myRegKey, L"DeadGWDetect", &(IPConfiguration->ici_deadgwdetect), TRUE ); } #endif // MILLEN (VOID) InitRegDWORDParameter( myRegKey, L"EnablePMTUDiscovery", &(IPConfiguration->ici_pmtudiscovery), TRUE ); (VOID) InitRegDWORDParameter( myRegKey, L"DefaultTTL", &(IPConfiguration->ici_ttl), DEFAULT_TTL ); if (IPConfiguration->ici_ttl == 0) { IPConfiguration->ici_ttl = DEFAULT_TTL; } status = InitRegDWORDParameter( myRegKey, L"DefaultTOSValue", &(IPConfiguration->ici_tos), DEFAULT_TOS ); #if MILLEN // // Backwards compatibility. Read 'DefaultTOS' if 'DefaultTOSValue' is // not present. // if (!NT_SUCCESS(status)) { InitRegDWORDParameter( myRegKey, L"DefaultTOS", &(IPConfiguration->ici_tos), DEFAULT_TOS ); } #endif // MILLEN (VOID) InitRegDWORDParameter( myRegKey, L"DisableUserTOSSetting", &DisableUserTOS, TRUE ); (VOID) InitRegDWORDParameter( myRegKey, L"EnableICMPRedirect", &EnableICMPRedirects, TRUE ); // Get the system size - SMALL, MEDIUM, LARGE systemSize = MmQuerySystemSize(); // Get the route lookup memory usage limits (VOID) InitRegDWORDParameter( myRegKey, L"MaxNormLookupMemory", &(IPConfiguration->ici_maxnormlookupmemory), DefaultMaxNormLookupMem[systemSize] ); if (IPConfiguration->ici_maxnormlookupmemory < MINIMUM_MAX_NORM_LOOKUP_MEM) { IPConfiguration->ici_maxnormlookupmemory = MINIMUM_MAX_NORM_LOOKUP_MEM; } #if MILLEN IPConfiguration->ici_fastroutelookup = FALSE; #else // MILLEN // Are we a gateway ? Is this a medium or large // server ? If so, is fast routing enabled ? if (IPConfiguration->ici_gateway && MmIsThisAnNtAsSystem() && (systemSize > MmSmallSystem)) { (VOID) InitRegDWORDParameter( myRegKey, L"EnableFastRouteLookup", &(IPConfiguration->ici_fastroutelookup), FALSE ); } else { IPConfiguration->ici_fastroutelookup = FALSE; } #endif // !MILLEN // If Fast lookup is enabled, get lookup params if (IPConfiguration->ici_fastroutelookup) { (VOID) InitRegDWORDParameter( myRegKey, L"FastRouteLookupLevels", &(IPConfiguration->ici_fastlookuplevels), DefaultFastLookupLevels[systemSize] ); (VOID) InitRegDWORDParameter( myRegKey, L"MaxFastLookupMemory", &(IPConfiguration->ici_maxfastlookupmemory), DefaultMaxFastLookupMem[systemSize] ); if (IPConfiguration->ici_maxfastlookupmemory < MINIMUM_MAX_FAST_LOOKUP_MEM) { IPConfiguration->ici_maxfastlookupmemory = MINIMUM_MAX_FAST_LOOKUP_MEM; } } (VOID) InitRegDWORDParameter( myRegKey, L"MaxEqualCostRoutes", &ulongValue, DEFAULT_MAX_EQUAL_COST_ROUTES ); MaxEqualCostRoutes = (USHORT) ulongValue; if (MaxEqualCostRoutes > MAXIMUM_MAX_EQUAL_COST_ROUTES) { MaxEqualCostRoutes = DEFAULT_MAX_EQUAL_COST_ROUTES; } #if FFP_SUPPORT (VOID) InitRegDWORDParameter( myRegKey, L"FFPFastForwardingCacheSize", &FFPRegFastForwardingCacheSize, DEFAULT_FFP_FFWDCACHE_SIZE ); (VOID) InitRegDWORDParameter( myRegKey, L"FFPControlFlags", &FFPRegControlFlags, DEFAULT_FFP_CONTROL_FLAGS ); #endif // FFP_SUPPORT (VOID) InitRegDWORDParameter( myRegKey, L"TrFunctionalMcastAddress", &(IPConfiguration->ici_TrFunctionalMcst), TRUE ); status = InitRegDWORDParameter( myRegKey, L"ArpUseEtherSnap", &ArpUseEtherSnap, FALSE ); #if MILLEN // // Backwards compatibility. If the 'ArpUseEtherSnap' key does not exist, // then try to read the 'EtherSNAP' key. // if (!NT_SUCCESS(status)) { InitRegDWORDParameter( myRegKey, L"EtherSNAP", &ArpUseEtherSnap, FALSE ); } #endif // MILLEN (VOID) InitRegDWORDParameter( myRegKey, L"DisableDHCPMediaSense", &DisableMediaSense, 0 ); (VOID) InitRegDWORDParameter( myRegKey, L"DisableMediaSenseEventLog", &DisableMediaSenseEventLog, #if MILLEN // This mediasense event log causes issues // on Windows ME. Since there is no // event log anyways, disable it. TRUE #else // MILLEN FALSE #endif // !MILLEN ); //DisableIPSourceRouting == 2 drop it if SR option // is rcvd, without forwarding. (VOID) InitRegDWORDParameter( myRegKey, L"DisableIPSourceRouting", &DisableIPSourceRouting, 1 ); (VOID) InitRegDWORDParameter( myRegKey, L"MaximumReassemblyHeaders", &MaxRH, 100 ); (VOID) InitRegDWORDParameter( myRegKey, L"NetHashTableSize", &NET_TABLE_SIZE, 8 ); if (NET_TABLE_SIZE < 8) { NET_TABLE_SIZE = 8; } else if (NET_TABLE_SIZE > 0xffff) { NET_TABLE_SIZE = 512; } else { NET_TABLE_SIZE = ComputeLargerOrEqualPowerOfTwo(NET_TABLE_SIZE); } // we check for the return status here because if this parameter was // not defined, then we want the default behavior for both arp // and ip broadcasts. For arp, the behavior is to not source route // and source router alternately. For ip, it is to always source // route. If the parameter is defined and is 0, then for arp the // behavior does not change. For ip however, we do not source route // at all. Ofcourse, when the parameter is set to a non-zero value, // we always source route for both. // status = InitRegDWORDParameter( myRegKey, L"ArpAlwaysSourceRoute", &ArpAlwaysSourceRoute, FALSE ); if (NT_SUCCESS(status)) { IPAlwaysSourceRoute = ArpAlwaysSourceRoute; } (VOID) InitRegDWORDParameter( myRegKey, L"ArpTRSingleRoute", &ArpTRSingleRoute, FALSE ); if (ArpTRSingleRoute) { TrRii = TR_RII_SINGLE; } else { TrRii = TR_RII_ALL; } (VOID) InitRegDWORDParameter( myRegKey, L"ArpCacheLife", &ArpCacheLife, DEFAULT_ARP_CACHE_LIFE ); (VOID) InitRegDWORDParameter( myRegKey, L"ArpCacheMinReferencedLife", &ArpMinValidCacheLife, DEFAULT_ARP_MIN_VALID_CACHE_LIFE ); (VOID) InitRegDWORDParameter( myRegKey, L"ArpRetryCount", &ArpRetryCount, DEFAULT_ARPRETRY_COUNT ); if (((int)ArpRetryCount < 0) || (ArpRetryCount > 3)) { ArpRetryCount = DEFAULT_ARPRETRY_COUNT; } (VOID) InitRegDWORDParameter( myRegKey, L"EnableBcastArpReply", &EnableBcastArpReply, TRUE ); (VOID) InitRegDWORDParameter( myRegKey, L"DisableTaskOffload", &DisableTaskOffload, #if MILLEN TRUE #else // MILLEN FALSE #endif // !MILLEN ); (VOID) InitRegDWORDParameter( myRegKey, L"ConnectDampInterval", &ConnectDampingInterval, 5 ); ConnectDampingInterval = MIN(10, MAX(5, ConnectDampingInterval)); (VOID) InitRegDWORDParameter( myRegKey, L"DisconnectDampInterval", &DampingInterval, 10 ); DampingInterval = MIN(10, MAX(5, DampingInterval)); ZwClose(myRegKey); myRegKey = NULL; } else { // // Use reasonable defaults. // IPConfiguration->ici_fwbcast = 0; IPConfiguration->ici_gateway = 0; IPConfiguration->ici_addrmaskreply = 0; IPConfiguration->ici_fwbufsize = DEFAULT_FW_BUFSIZE; IPConfiguration->ici_fwpackets = DEFAULT_FW_PACKETS; IPConfiguration->ici_maxfwbufsize = DEFAULT_MAX_FW_BUFSIZE; IPConfiguration->ici_maxfwpackets = DEFAULT_MAX_FW_PACKETS; IPConfiguration->ici_igmplevel = DEFAULT_IGMP_LEVEL; IPConfiguration->ici_deadgwdetect = FALSE; IPConfiguration->ici_pmtudiscovery = FALSE; IPConfiguration->ici_ttl = DEFAULT_TTL; IPConfiguration->ici_tos = DEFAULT_TOS; NET_TABLE_SIZE = 8; DEBUGMSG(DBG_WARN && DBG_INIT, (DTEXT("IPProcessConfiguration: Unable to open registry - using defaults.\n"))); } #if !MILLEN // // Retrieve and store the binding list from the Linkage key // status = OpenRegKey(&myRegKey, IPLinkageRegistryKey); if (NT_SUCCESS(status)) { UNICODE_STRING_NEW BindString; BindString.Length = 0; BindString.MaximumLength = WORK_BUFFER_SIZE; BindString.Buffer = CTEAllocMemBoot(WORK_BUFFER_SIZE); if (BindString.Buffer) { status = GetRegMultiSZValueNew(myRegKey, L"Bind", &BindString); if (status == STATUS_SUCCESS) { IPBindList = BindString.Buffer; } else { CTEFreeMem(BindString.Buffer); } } ZwClose(myRegKey); myRegKey = NULL; } #endif status = STATUS_SUCCESS; if (!IPInit()) { CTELogEvent( IPDriverObject, EVENT_TCPIP_IP_INIT_FAILED, 1, 0, NULL, 0, NULL ); DEBUGMSG(DBG_ERROR && DBG_INIT, (DTEXT("IPProcessConfiguration: IPInit failure.\n"))); status = STATUS_UNSUCCESSFUL; } else { status = STATUS_SUCCESS; } if (myRegKey != NULL) { ZwClose(myRegKey); } if (IPConfiguration != NULL) { IPFreeConfig(IPConfiguration); } return (status); } uint GetDefaultGWList( uint * numGW, IPAddr * gwList, uint * gwMetricList, NDIS_HANDLE Handle, PNDIS_STRING ConfigName ) /*++ Routine Description: This routine reads the default gateway list from the registry. Arguments: numberOfGateways - number of gateway entries in the registry. gwList - pointer to the gateway list. gwMetricList - pointer to the metric for each gateway handle - Config handle from OpenIFConfig(). ConfigName - description string for use in logging failures. Return Value: TRUE if we got all the required info, FALSE otherwise. --*/ { UNICODE_STRING valueString; NTSTATUS status; ULONG ulAddGateway, ulTemp; uint numberOfGateways; PAGED_CODE(); // // Process the gateway MultiSZ. The end is signified by a double NULL. // This list currently only applies to the first IP address configured // on this interface. // numberOfGateways = 0; ulAddGateway = TRUE; RtlZeroMemory(gwList, sizeof(IPAddr) * MAX_DEFAULT_GWS); RtlZeroMemory(gwMetricList, sizeof(uint) * MAX_DEFAULT_GWS); valueString.Length = 0; valueString.MaximumLength = WORK_BUFFER_SIZE; valueString.Buffer = CTEAllocMemBoot(WORK_BUFFER_SIZE); if (valueString.Buffer == NULL) { return (FALSE); } ulTemp = 0; status = GetRegDWORDValue(Handle, L"DontAddDefaultGateway", &ulTemp); if (NT_SUCCESS(status)) { if (ulTemp == 1) { ulAddGateway = FALSE; } } if (ulAddGateway) { status = GetRegMultiSZValue( Handle, L"DefaultGateway", &valueString ); if (NT_SUCCESS(status)) { PWCHAR addressString = valueString.Buffer; while (*addressString != UNICODE_NULL) { IPAddr addressValue; BOOLEAN conversionStatus; if (numberOfGateways >= MAX_DEFAULT_GWS) { CTELogEvent( IPDriverObject, EVENT_TCPIP_TOO_MANY_GATEWAYS, 1, 1, &ConfigName->Buffer, 0, NULL ); break; } conversionStatus = IPConvertStringToAddress( addressString, &addressValue ); if (conversionStatus && (addressValue != 0xFFFFFFFF)) { if (addressValue != INADDR_ANY) { gwList[numberOfGateways++] = addressValue; } } else { PWCHAR stringList[2]; stringList[0] = addressString; stringList[1] = ConfigName->Buffer; CTELogEvent( IPDriverObject, EVENT_TCPIP_INVALID_DEFAULT_GATEWAY, 1, 2, stringList, 0, NULL ); TCPTRACE(( "IP: Invalid default gateway address %ws specified for adapter %ws.\n" " Remote networks may not be reachable as a result.\n", addressString, ConfigName->Buffer )); } // // Walk over the entry we just processed. // while (*addressString++ != UNICODE_NULL); } status = GetRegMultiSZValue( Handle, L"DefaultGatewayMetric", &valueString ); if (NT_SUCCESS(status)) { PWCHAR metricBuffer = valueString.Buffer; uint metricIndex = 0; while (*metricBuffer != UNICODE_NULL) { uint metricValue; UNICODE_STRING metricString; if (metricIndex >= numberOfGateways) { break; } RtlInitUnicodeString(&metricString, metricBuffer); status = RtlUnicodeStringToInteger( &metricString, 10, &metricValue ); if (!NT_SUCCESS(status)) { break; } else { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "GetDefaultGWList: read %d\n", metricValue)); if ((LONG) metricValue < 0) { break; } } gwMetricList[metricIndex++] = metricValue; // // Walk over the entry we just processed. // while (*metricBuffer++ != UNICODE_NULL); } } } else { TCPTRACE(( "IP: Unable to read DefaultGateway value for adapter %ws.\n" " Initialization will continue.\n", ConfigName->Buffer )); } } *numGW = numberOfGateways; if (valueString.Buffer) { CTEFreeMem(valueString.Buffer); } return TRUE; } void GetInterfaceMetric( uint * Metric, NDIS_HANDLE Handle ) /*++ Routine Description: A routine to retrieve the metric associated with an interface, if any. Arguments: Metric - receives the metric Handle - Config handle from OpenIFConfig(). Return Value: none. --*/ { NTSTATUS status; status = GetRegDWORDValue( Handle, L"InterfaceMetric", Metric ); if (!NT_SUCCESS(status)) { *Metric = 0; } else { if (*Metric > 9999) { *Metric = 9999; } } } void UpdateTcpParams( NDIS_HANDLE Handle, Interface *IF ) /*++ Routine Description: A routine to update per-interface specific tcp tuning parametsrs. Arguments: Handle - Config handle from OpenIFConfig(). IF - IP Interface which needs to be updated. Return Value: none. --*/ { ULONG ulTemp; NTSTATUS status; status = GetRegDWORDValue( Handle, L"TcpWindowSize", &ulTemp ); if (NT_SUCCESS(status)) { IF->if_TcpWindowSize = ulTemp; } status = GetRegDWORDValue( Handle, L"TcpInitialRTT", &ulTemp ); if (NT_SUCCESS(status)) { IF->if_TcpInitialRTT = ulTemp; } status = GetRegDWORDValue( Handle, L"TcpDelAckTicks", &ulTemp ); if (NT_SUCCESS(status) && (ulTemp <= MAX_DEL_ACK_TICKS)) { IF->if_TcpDelAckTicks = (uchar)ulTemp; } status = GetRegDWORDValue( Handle, L"TcpACKFrequency", &ulTemp ); if (NT_SUCCESS(status)) { IF->if_TcpAckFrequency = (uchar)ulTemp; } } uint GetGeneralIFConfig( IFGeneralConfig * ConfigInfo, NDIS_HANDLE Handle, PNDIS_STRING ConfigName ) /*++ Routine Description: A routine to get the general per-interface config info, such as MTU, type of broadcast, etc. The caller gives us a structure to be filled in and a handle, and we fill in the structure if we can. Arguments: ConfigInfo - Structure to be filled in. Handle - Config handle from OpenIFConfig(). ConfigName - identification string for logging failures. Return Value: TRUE if we got all the required info, FALSE otherwise. --*/ { UNICODE_STRING valueString; NTSTATUS status; UINT numberOfGateways = 0; UCHAR TempBuffer[WORK_BUFFER_SIZE]; ULONG ulAddGateway, ulTemp; PAGED_CODE(); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("+GetGeneralIFConfig(%x, %x)\n"), ConfigInfo, Handle)); if (!GetDefaultGWList( &ConfigInfo->igc_numgws, ConfigInfo->igc_gw, ConfigInfo->igc_gwmetric, Handle, ConfigName)) { DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("GetGeneralIFConfig: GetDefaultGWList failure.\n"))); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-GetGeneralIFConfig [FALSE]\n"))); return FALSE; } // // Are we using zeros broadcasts? // status = GetRegDWORDValue( Handle, L"UseZeroBroadcast", &(ConfigInfo->igc_zerobcast) ); #if MILLEN // // Backwards compatibility. If 'UseZeroBroadcast' value doesn't exist, then // attempt to read legacy value: 'ZeroBroadcast'. // if (!NT_SUCCESS(status)) { status = GetRegDWORDValue( Handle, L"ZeroBroadcast", &(ConfigInfo->igc_zerobcast) ); } #endif // MILLEN if (!NT_SUCCESS(status)) { TCPTRACE(( "IP: Unable to read UseZeroBroadcast value for adapter %ws.\n" " All-nets broadcasts will be addressed to 255.255.255.255.\n", ConfigName->Buffer )); ConfigInfo->igc_zerobcast = FALSE; // default to off } // // Has anyone specified an MTU? // status = GetRegDWORDValue( Handle, L"MTU", &(ConfigInfo->igc_mtu) ); #if MILLEN // // Backwards compatibility. If 'MTU' value doesn't exist, then // attempt to read legacy value: 'MaxMTU'. // if (!NT_SUCCESS(status)) { status = GetRegDWORDValue( Handle, L"MaxMTU", &(ConfigInfo->igc_mtu) ); } #endif // !MILLEN if (!NT_SUCCESS(status)) { ConfigInfo->igc_mtu = 0xFFFFFFF; // The stack will pick one. } // // Have we been configured for more routing packets? // status = GetRegDWORDValue( Handle, L"MaxForwardPending", &(ConfigInfo->igc_maxpending) ); #if MILLEN // // Backwards compatibility. If 'MaxForwardPending' value doesn't exist, then // attempt to read legacy value: 'MaxFWPending'. // if (!NT_SUCCESS(status)) { status = GetRegDWORDValue( Handle, L"MaxFWPending", &(ConfigInfo->igc_maxpending) ); } #endif // !MILLEN if (!NT_SUCCESS(status)) { ConfigInfo->igc_maxpending = DEFAULT_MAX_PENDING; } // // Has Router Discovery been configured? // We accept three values: // 0: disable router-discovery // 1: enable router-discovery // 2: disable router-discovery, and enable it only if the DHCP server // sends the 'Perform Router Discovery' option. In this case, // we wait for the DHCP client service to tell us to start // doing router-discovery. // status = GetRegDWORDValue( Handle, L"PerformRouterDiscovery", &(ConfigInfo->igc_rtrdiscovery) ); if (!NT_SUCCESS(status)) { ConfigInfo->igc_rtrdiscovery = IP_IRDP_DISABLED_USE_DHCP; } else if (ConfigInfo->igc_rtrdiscovery != IP_IRDP_DISABLED && ConfigInfo->igc_rtrdiscovery != IP_IRDP_ENABLED && ConfigInfo->igc_rtrdiscovery != IP_IRDP_DISABLED_USE_DHCP) { ConfigInfo->igc_rtrdiscovery = IP_IRDP_DISABLED_USE_DHCP; } // // Has Router Discovery Address been configured? // status = GetRegDWORDValue( Handle, L"SolicitationAddressBCast", &ulTemp ); if (!NT_SUCCESS(status)) { ConfigInfo->igc_rtrdiscaddr = ALL_ROUTER_MCAST; } else { if (ulTemp == 1) { ConfigInfo->igc_rtrdiscaddr = 0xffffffff; } else { ConfigInfo->igc_rtrdiscaddr = ALL_ROUTER_MCAST; } } ConfigInfo->igc_TcpWindowSize = 0; ConfigInfo->igc_TcpInitialRTT = 0; ConfigInfo->igc_TcpDelAckTicks = DEL_ACK_TICKS; ConfigInfo->igc_TcpAckFrequency = 0; status = GetRegDWORDValue( Handle, L"TcpWindowSize", &ulTemp ); if (NT_SUCCESS(status)) { ConfigInfo->igc_TcpWindowSize = ulTemp; } status = GetRegDWORDValue( Handle, L"TcpInitialRTT", &ulTemp ); if (NT_SUCCESS(status)) { ConfigInfo->igc_TcpInitialRTT = ulTemp; } status = GetRegDWORDValue( Handle, L"TcpDelAckTicks", &ulTemp ); if (NT_SUCCESS(status) && (ulTemp <= MAX_DEL_ACK_TICKS)) { ConfigInfo->igc_TcpDelAckTicks = (uchar)ulTemp; } status = GetRegDWORDValue( Handle, L"TcpACKFrequency", &ulTemp ); if (NT_SUCCESS(status)) { ConfigInfo->igc_TcpAckFrequency = (uchar)ulTemp; } GetInterfaceMetric(&ConfigInfo->igc_metric, Handle); ConfigInfo->igc_iftype = 0; // by default its 0 means both ucast/mcast traffic allowed status = GetRegDWORDValue( Handle, L"TypeofInterface", &ulTemp ); if (NT_SUCCESS(status)) { ConfigInfo->igc_iftype = (uchar)ulTemp; } // Use global value by default. ConfigInfo->igc_disablemediasense = DisableMediaSense ? TRUE : FALSE; #if MILLEN // Only Windows ME supports reading the per-interface setting from the // registry. The global value is used for Win2000+. status = GetRegDWORDValue( Handle, L"DisableDHCPMediaSense", &ulTemp ); if (NT_SUCCESS(status)) { ConfigInfo->igc_disablemediasense = ulTemp ? TRUE : FALSE; } #endif // MILLEN DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-GetGeneralIFConfig [TRUE]\n"))); return TRUE; } NDIS_STATUS GetIPConfigValue( NDIS_HANDLE Handle, PUNICODE_STRING IPConfig ) /*++ Routine Description: Called to get the IPConfig string value Arguments: Handle - Handle to use for reading config. IPConfig - Pointer to Unicode string where IPConfig is stored. Return Value: Status of the operation. --*/ { NTSTATUS status; PAGED_CODE(); IPConfig->MaximumLength = 200; IPConfig->Buffer = CTEAllocMemBoot(IPConfig->MaximumLength); if (IPConfig->Buffer == NULL) { return (STATUS_INSUFFICIENT_RESOURCES); } status = GetRegMultiSZValue( Handle, L"IPConfig", IPConfig ); return status; } int IsLLInterfaceValueNull( NDIS_HANDLE Handle ) /*++ Routine Description: Called to see if the LLInterface value in the registry key for which the handle is provided, is NULL or not. Arguments: Handle - Handle to use for reading config. Return Value: FALSE if value is not null TRUE if it is null --*/ { UNICODE_STRING valueString; ULONG valueType; NTSTATUS status; PAGED_CODE(); valueString.MaximumLength = 200; valueString.Buffer = CTEAllocMemBoot(valueString.MaximumLength); if (valueString.Buffer == NULL) { return (FALSE); } status = GetRegSZValue( Handle, L"LLInterface", &valueString, &valueType ); if (NT_SUCCESS(status) && (*(valueString.Buffer) != UNICODE_NULL)) { CTEFreeMem(valueString.Buffer); return FALSE; } else { CTEFreeMem(valueString.Buffer); return TRUE; } } NTSTATUS GetLLInterfaceValue( NDIS_HANDLE Handle, PNDIS_STRING pValueString ) /*++ Routine Description: Called to read the LLInterface value in the registry key for which the handle is provided. Arguments: Handle - Handle to use for reading config. Return Value: value of key --*/ { NTSTATUS status; ULONG valueType; PAGED_CODE(); status = GetRegSZValue( Handle, L"LLInterface", pValueString, &valueType ); return status; } BOOLEAN GetTempDHCPAddr( NDIS_HANDLE Handle, IPAddr * Tempdhcpaddr, IPAddr * TempMask, IPAddr * TempGWAddr, PNDIS_STRING ConfigName ) /*++ Routine Description: Called to get temp dhcp address if dhcp is enabled Arguments: Handle - Handle to use for reading config. Tempdhcpaddr - temporary addr, mask and gateway TempMask TempGWAddr ConfigName - identifies the interface in logging failures. Return Value: --*/ { NTSTATUS Status; LARGE_INTEGER LeaseTime, systime; IPAddr Addr; UNICODE_STRING valueString; ULONG valueType; BOOLEAN ConversionStatus; Status = GetRegLARGEINTValue( Handle, L"TempLeaseExpirationTime", &LeaseTime ); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "LargeInt status %x\n", Status)); if (Status != STATUS_SUCCESS) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "No Lease time\n")); return FALSE; } valueString.Length = 0; valueString.MaximumLength = WORK_BUFFER_SIZE; valueString.Buffer = (PWCHAR) CTEAllocMemBoot(WORK_BUFFER_SIZE); KeQuerySystemTime(&systime); if (RtlLargeIntegerGreaterThan(systime, LeaseTime)) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "Leastime > systime no tempdhcp\n")); return FALSE; } Status = GetRegSZValue( Handle, L"TempIpAddress", &valueString, &valueType ); if (!NT_SUCCESS(Status) || (*(valueString.Buffer) == UNICODE_NULL)) { return FALSE; } ConversionStatus = IPConvertStringToAddress( (valueString.Buffer), Tempdhcpaddr ); if (!ConversionStatus) { return FALSE; } Status = GetRegSZValue( Handle, L"TempMask", &valueString, &valueType ); if (!NT_SUCCESS(Status) || (*(valueString.Buffer) == UNICODE_NULL)) { return FALSE; } ConversionStatus = IPConvertStringToAddress( (valueString.Buffer), TempMask ); if (!ConversionStatus) { return FALSE; } Status = GetRegMultiSZValue( Handle, L"DhcpDefaultGateway", &valueString ); if (NT_SUCCESS(Status) && (*(valueString.Buffer) != UNICODE_NULL)) { PWCHAR addressString = valueString.Buffer; uint numberOfGateways = 0; while (*addressString != UNICODE_NULL) { IPAddr addressValue; BOOLEAN conversionStatus; if (numberOfGateways >= MAX_DEFAULT_GWS) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "Exceeded mac_default_gws %d\n", numberOfGateways)); break; } conversionStatus = IPConvertStringToAddress( addressString, &addressValue ); if (conversionStatus && (addressValue != 0xFFFFFFFF)) { if (addressValue != INADDR_ANY) { TempGWAddr[numberOfGateways++] = addressValue; } } // // Walk over the entry we just processed. // while (*addressString++ != UNICODE_NULL); } KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "Temp gws - %d\n", numberOfGateways)); } KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "tempdhcp: %x %x %x\n", Tempdhcpaddr, TempMask, TempGWAddr)); return TRUE; } IFAddrList * GetIFAddrList( UINT * NumAddr, NDIS_HANDLE Handle, UINT * EnableDhcp, BOOLEAN PppIf, PNDIS_STRING ConfigName ) /*++ Routine Description: Called to read the list of IF addresses and masks for an interface. We'll get the address pointer first, then walk the list counting to find out how many addresses we have. Then we allocate memory for the list, and walk down the list converting them. After that we'll get the mask list and convert it. Arguments: NumAddr - Where to return number of address we have. Handle - Handle to use for reading config. EnableDhcp - Whether or not dhcp is enabled. ConfigName - identifies the interface in logging failures. Return Value: Pointer to IF address list if we get one, or NULL otherwise. --*/ { UNICODE_STRING_NEW ValueString; NTSTATUS Status; UINT AddressCount = 0; UINT GoodAddresses = 0; PWCHAR CurrentAddress; PWCHAR CurrentMask; PWCHAR AddressString; PWCHAR MaskString; IFAddrList *AddressList; UINT i; BOOLEAN ConversionStatus; IPAddr AddressValue; IPAddr MaskValue; UCHAR TempBuffer[WORK_BUFFER_SIZE]; PAGED_CODE(); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("+GetIFAddrList(%x, %x, %x)\n"), NumAddr, Handle, EnableDhcp)); // First, try to read the EnableDhcp Value. Status = GetRegDWORDValue( Handle, L"EnableDHCP", EnableDhcp ); if (!NT_SUCCESS(Status)) { *EnableDhcp = FALSE; } ValueString.Length = 0; ValueString.MaximumLength = WORK_BUFFER_SIZE; ValueString.Buffer = (PWCHAR) CTEAllocMemBoot(WORK_BUFFER_SIZE); if (ValueString.Buffer == NULL) { CTELogEvent( IPDriverObject, EVENT_TCPIP_NO_ADAPTER_RESOURCES, 2, 1, &ConfigName->Buffer, 0, NULL ); DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("GetIFAddrList: Failure to allocate memory.\n"))); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-GetIFAddrList [%x] Status %x NumAddr %d, EnableDhcp = %s\n"), NULL, Status, *NumAddr, *EnableDhcp ? TEXT("TRUE") : TEXT("FALSE"))); return NULL; } // First, try to read the IpAddress string. Status = GetRegMultiSZValueNew( Handle, L"IpAddress", &ValueString ); if (!NT_SUCCESS(Status)) { CTELogEvent( IPDriverObject, EVENT_TCPIP_NO_ADDRESS_LIST, 1, 1, &ConfigName->Buffer, 0, NULL ); ExFreePool(ValueString.Buffer); DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("GetIFAddrList: unable to read IP address list for adapter %ws.\n"), ConfigName->Buffer)); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-GetIFAddrList [%x] Status %x NumAddr %d, EnableDhcp = %s\n"), NULL, Status, *NumAddr, *EnableDhcp ? TEXT("TRUE") : TEXT("FALSE"))); return NULL; } AddressString = ExAllocatePoolWithTag(NonPagedPool, ValueString.MaximumLength, 'iPCT'); if (AddressString == NULL) { CTELogEvent( IPDriverObject, EVENT_TCPIP_NO_ADAPTER_RESOURCES, 2, 1, &ConfigName->Buffer, 0, NULL ); ExFreePool(ValueString.Buffer); DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("GetIFAddrList: unable to allocate memory for IP address list.\n"))); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-GetIFAddrList [%x] Status %x NumAddr %d, EnableDhcp = %s\n"), NULL, Status, *NumAddr, *EnableDhcp ? TEXT("TRUE") : TEXT("FALSE"))); return NULL; } RtlCopyMemory(AddressString, ValueString.Buffer, ValueString.MaximumLength); Status = GetRegMultiSZValueNew( Handle, L"Subnetmask", &ValueString ); #if MILLEN if (!NT_SUCCESS(Status)) { Status = GetRegMultiSZValueNew( Handle, L"IPMask", &ValueString ); } #endif // MILLEN if (!NT_SUCCESS(Status)) { CTELogEvent( IPDriverObject, EVENT_TCPIP_NO_MASK_LIST, 1, 1, &ConfigName->Buffer, 0, NULL ); TCPTRACE(( "IP: Unable to read the subnet mask list for adapter %ws.\n" " IP will not be operational on this adapter.\n", ConfigName->Buffer )); ExFreePool(AddressString); ExFreePool(ValueString.Buffer); DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("GetIFAddrList: unable to read subnet mask list for adapter %ws.\n"), ConfigName->Buffer)); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-GetIFAddrList [%x] Status %x NumAddr %d, EnableDhcp = %s\n"), NULL, Status, *NumAddr, *EnableDhcp ? TEXT("TRUE") : TEXT("FALSE"))); return NULL; } MaskString = ExAllocatePoolWithTag(NonPagedPool, ValueString.MaximumLength, 'iPCT'); if (MaskString == NULL) { CTELogEvent( IPDriverObject, EVENT_TCPIP_NO_ADAPTER_RESOURCES, 3, 1, &ConfigName->Buffer, 0, NULL ); ExFreePool(AddressString); ExFreePool(ValueString.Buffer); DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("GetIFAddrList: unable to allocate memory for subnet mask list.\n"))); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-GetIFAddrList [%x] Status %x NumAddr %d, EnableDhcp = %s\n"), NULL, Status, *NumAddr, *EnableDhcp ? TEXT("TRUE") : TEXT("FALSE"))); return NULL; } RtlCopyMemory(MaskString, ValueString.Buffer, ValueString.MaximumLength); CurrentAddress = AddressString; CurrentMask = MaskString; while (*CurrentAddress != UNICODE_NULL && *CurrentMask != UNICODE_NULL) { // We have a potential IP address. AddressCount++; // Skip this one. while (*CurrentAddress++ != UNICODE_NULL); while (*CurrentMask++ != UNICODE_NULL); } if (AddressCount == 0) { ExFreePool(AddressString); ExFreePool(MaskString); ExFreePool(ValueString.Buffer); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-GetIFAddrList [%x] Status %x NumAddr %d, EnableDhcp = %s\n"), NULL, Status, *NumAddr, *EnableDhcp ? TEXT("TRUE") : TEXT("FALSE"))); return NULL; } // Allocate memory. AddressList = CTEAllocMemBoot(sizeof(IFAddrList) * AddressCount); if (AddressList == NULL) { CTELogEvent( IPDriverObject, EVENT_TCPIP_NO_ADAPTER_RESOURCES, 2, 1, &ConfigName->Buffer, 0, NULL ); ExFreePool(AddressString); ExFreePool(MaskString); ExFreePool(ValueString.Buffer); DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("GetIFAddrList: unable to allocate memory for IP address list.\n"))); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-GetIFAddrList [%x] Status %x NumAddr %d, EnableDhcp = %s\n"), NULL, Status, *NumAddr, *EnableDhcp ? TEXT("TRUE") : TEXT("FALSE"))); return NULL; } // Walk the list again, converting each address. CurrentAddress = AddressString; CurrentMask = MaskString; for (i = 0; i < AddressCount; i++) { ConversionStatus = IPConvertStringToAddress( CurrentAddress, &AddressValue ); if (!ConversionStatus || (AddressValue == 0xFFFFFFFF)) { PWCHAR stringList[2]; stringList[0] = CurrentAddress; stringList[1] = ConfigName->Buffer; CTELogEvent( IPDriverObject, EVENT_TCPIP_INVALID_ADDRESS, 1, 2, stringList, 0, NULL ); DEBUGMSG(DBG_WARN && DBG_PNP, (DTEXT("IPAddInterface: Invalid IP address %ws specified for \n") TEXT("adapter %ws. Interface may not be init.\n"), CurrentAddress, ConfigName->Buffer)); goto nextone; } // Now do the current mask. ConversionStatus = IPConvertStringToAddress( CurrentMask, &MaskValue ); if (!ConversionStatus) { PWCHAR stringList[3]; stringList[0] = CurrentMask; stringList[1] = CurrentAddress; stringList[2] = ConfigName->Buffer; CTELogEvent( IPDriverObject, EVENT_TCPIP_INVALID_MASK, 1, 3, stringList, 0, NULL ); DEBUGMSG(DBG_WARN && DBG_PNP, (DTEXT("IPAddInterface: Invalid IP mask %ws specified for \n") TEXT("adapter %ws. Interface may not be init.\n"), CurrentMask, ConfigName->Buffer)); } else { AddressList[GoodAddresses].ial_addr = AddressValue; AddressList[GoodAddresses].ial_mask = MaskValue; GoodAddresses++; } nextone: while (*CurrentAddress++ != UNICODE_NULL); while (*CurrentMask++ != UNICODE_NULL); } ExFreePool(AddressString); ExFreePool(MaskString); ExFreePool(ValueString.Buffer); *NumAddr = GoodAddresses; if (GoodAddresses == 0) { ExFreePool(AddressList); AddressList = NULL; } #if MILLEN // // So Millennium may not have the EnableDHCP registry key present, but // we still may want to detect this. So if EnableDHCP is not set, and // there is only one address which is NULL and it is not a PPP interface, // then we set EnableDHCP to true. // if (*EnableDhcp == FALSE && GoodAddresses == 1 && AddressList[0].ial_addr == NULL_IP_ADDR && AddressList[0].ial_mask == NULL_IP_ADDR && PppIf == FALSE ) { *EnableDhcp = TRUE; } #endif // MILLEN DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-GetIFAddrList [%x] Status %x NumAddr %d, EnableDhcp = %s\n"), AddressList, Status, *NumAddr, *EnableDhcp ? TEXT("TRUE") : TEXT("FALSE"))); return AddressList; } #if MILLEN //* OpenIFConfig - Open our per-IF config. info, // // Called when we want to open our per-info config info. We do so if we can, // otherwise we fail the request. // // Input: ConfigName - Name of interface to open. // Handle - Where to return the handle. // // Returns: TRUE if we succeed, FALSE if we don't. // uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE * Handle) { NDIS_STATUS Status; // Status of open attempt. HANDLE myRegKey; UINT RetStatus = FALSE; PWCHAR Config = NULL; DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("+OpenIFConfig(%x, %x)\n"), ConfigName, Handle)); *Handle = NULL; // // We need to ensure that the buffer is NULL terminated since we are passing // in just PWCHAR to OpenRegKey. // Config = ExAllocatePoolWithTag( NonPagedPool, ConfigName->Length + sizeof(WCHAR), 'iPCT'); if (Config == NULL) { goto done; } // Copy the configuration name into new buffer. RtlZeroMemory(Config, ConfigName->Length + sizeof(WCHAR)); RtlCopyMemory(Config, ConfigName->Buffer, ConfigName->Length); Status = OpenRegKey(&myRegKey, Config); if (Status == NDIS_STATUS_SUCCESS) { *Handle = myRegKey; RetStatus = TRUE; } done: if (Config) { ExFreePool(Config); } DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-OpenIFConfig [%s] Handle %x\n"), RetStatus == TRUE ? TEXT("TRUE") : TEXT("FALSE"), *Handle)); return RetStatus; } #else // MILLEN UINT OpenIFConfig( PNDIS_STRING ConfigName, NDIS_HANDLE * Handle ) /*++ Routine Description: Called when we want to open our per-info config info. We do so if we can, otherwise we fail the request. Arguments: ConfigName - Name of interface to open. Handle - Where to return the handle. Return Value: TRUE if we succeed, FALSE if we don't. --*/ { NTSTATUS status; HANDLE myRegKey; UNICODE_STRING valueString; WCHAR ServicesRegistryKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"; UINT RetStatus = FALSE; PAGED_CODE(); valueString.MaximumLength = ConfigName->MaximumLength + ((wcslen(ServicesRegistryKey) + 2) * sizeof(WCHAR)); valueString.Buffer = ExAllocatePoolWithTag( NonPagedPool, valueString.MaximumLength, 'iPCT' ); if (valueString.Buffer == NULL) { CTELogEvent( IPDriverObject, EVENT_TCPIP_NO_ADAPTER_RESOURCES, 4, 1, &ConfigName->Buffer, 0, NULL ); TCPTRACE(("IP: Unable to allocate memory for reg key name\n")); return (FALSE); } RtlZeroMemory(valueString.Buffer, valueString.MaximumLength); valueString.Length = 0; valueString.Buffer[0] = UNICODE_NULL; // // Build the key name for the tcpip parameters section and open key. // Setting Length = 0 and using append is like initializing the string // status = RtlAppendUnicodeToString(&valueString, ServicesRegistryKey); if (!NT_SUCCESS(status)) { CTELogEvent( IPDriverObject, EVENT_TCPIP_ADAPTER_REG_FAILURE, 1, 1, &ConfigName->Buffer, 0, NULL ); TCPTRACE(("IP: Unable to append services name to key string\n")); goto done; } status = RtlAppendUnicodeStringToString(&valueString, ConfigName); if (!NT_SUCCESS(status)) { CTELogEvent( IPDriverObject, EVENT_TCPIP_ADAPTER_REG_FAILURE, 2, 1, &ConfigName->Buffer, 0, NULL ); TCPTRACE(("IP: Unable to append adapter name to key string\n")); goto done; } status = OpenRegKey(&myRegKey, valueString.Buffer); if (!NT_SUCCESS(status)) { CTELogEvent( IPDriverObject, EVENT_TCPIP_ADAPTER_REG_FAILURE, 4, 1, &ConfigName->Buffer, 0, NULL ); TCPTRACE(( "IP: Unable to open adapter registry key %ws\n", valueString.Buffer )); //ASSERT(FALSE); } else { RetStatus = TRUE; *Handle = myRegKey; } done: ExFreePool(valueString.Buffer); return RetStatus; } #endif // !MILLEN VOID CloseIFConfig( NDIS_HANDLE Handle ) /*++ Routine Description: Close a per-interface config handle opened via OpenIFConfig(). Arguments: Handle - Handle to be closed. Return Value: --*/ { PAGED_CODE(); ZwClose(Handle); } IPConfigInfo * IPGetConfig( void ) /*++ Routine Description: Provides IP configuration information for the NT environment. Arguments: None Return Value: A pointer to a structure containing the configuration information. --*/ { return (IPConfiguration); } void IPFreeConfig( IPConfigInfo * ConfigInfo ) /*++ Routine Description: Frees the IP configuration structure allocated by IPGetConfig. Arguments: ConfigInfo - Pointer to the IP configuration information structure to free. Return Value: None. --*/ { int i; if (IPConfiguration != NULL) { CTEFreeMem(IPConfiguration); } IPConfiguration = NULL; return; } ulong GetGMTDelta( void ) /*++ Routine Description: Returns the offset in milliseconds of the time zone of this machine from GMT. Arguments: None. Return Value: Time in milliseconds between this time zone and GMT. --*/ { #if MILLEN return (-1); // Error not supported. #else // MILLEN LARGE_INTEGER localTime, systemTime; // // Get time zone bias in 100ns. // localTime.LowPart = 0; localTime.HighPart = 0; ExLocalTimeToSystemTime(&localTime, &systemTime); if ((localTime.LowPart != 0) || (localTime.HighPart != 0)) { localTime = CTEConvert100nsToMilliseconds(systemTime); } ASSERT(localTime.HighPart == 0); return (localTime.LowPart); #endif // !MILLEN } ulong GetTime( void ) /*++ Routine Description: Returns the time in milliseconds since midnight. Arguments: None. Return Value: Time in milliseconds since midnight. --*/ { LARGE_INTEGER ntTime; TIME_FIELDS breakdownTime; ulong returnValue; KeQuerySystemTime(&ntTime); RtlTimeToTimeFields(&ntTime, &breakdownTime); returnValue = breakdownTime.Hour * 60; returnValue = (returnValue + breakdownTime.Minute) * 60; returnValue = (returnValue + breakdownTime.Second) * 1000; returnValue = returnValue + breakdownTime.Milliseconds; return (returnValue); } ulong GetUnique32BitValue( void ) /*++ Routine Description: Returns a reasonably unique 32-bit number based on the system clock. In NT, we take the current system time, convert it to milliseconds, and return the low 32 bits. Arguments: None. Return Value: A reasonably unique 32-bit value. --*/ { LARGE_INTEGER ntTime, tmpTime; KeQuerySystemTime(&ntTime); tmpTime = CTEConvert100nsToMilliseconds(ntTime); return (tmpTime.LowPart); } uint UseEtherSNAP( PNDIS_STRING Name ) /*++ Routine Description: Determines whether the EtherSNAP protocol should be used on an interface. Arguments: Name - The device name of the interface in question. Return Value: Nonzero if SNAP is to be used on the interface. Zero otherwise. --*/ { UNREFERENCED_PARAMETER(Name); // // We currently set this on a global basis. // return (ArpUseEtherSnap); } void GetAlwaysSourceRoute( uint * pArpAlwaysSourceRoute, uint * pIPAlwaysSourceRoute ) /*++ Routine Description: Determines whether ARP should always turn on source routing in queries. Arguments: None. Return Value: Nonzero if source routing is always to be used. Zero otherwise. --*/ { // // We currently set this on a global basis. // *pArpAlwaysSourceRoute = ArpAlwaysSourceRoute; *pIPAlwaysSourceRoute = IPAlwaysSourceRoute; return; } uint GetArpCacheLife( void ) /*++ Routine Description: Get ArpCacheLife in seconds. Arguments: None. Return Value: Set to default if not found. --*/ { // // We currently set this on a global basis. // return (ArpCacheLife); } uint GetArpRetryCount( void ) /*++ Routine Description: Get ArpRetryCount Arguments: None. Return Value: Set to default if not found. --*/ { // // We currently set this on a global basis. // return (ArpRetryCount); } #define IP_ADDRESS_STRING_LENGTH (16+2) // +2 for double NULL on MULTI_SZ BOOLEAN IPConvertStringToAddress( IN PWCHAR AddressString, OUT PULONG IpAddress ) /*++ Routine Description This function converts an Internet standard 4-octet dotted decimal IP address string into a numeric IP address. Unlike inet_addr(), this routine does not support address strings of less than 4 octets nor does it support octal and hexadecimal octets, and it returns the address in host byte order, rather than network byte order. Arguments AddressString - IP address in dotted decimal notation IpAddress - Pointer to a variable to hold the resulting address Return Value: TRUE if the address string was converted. FALSE otherwise. --*/ { #if !MILLEN NTSTATUS status; PWCHAR endPointer; status = RtlIpv4StringToAddressW(AddressString, TRUE, &endPointer, (struct in_addr *)IpAddress); if (!NT_SUCCESS(status)) { return (FALSE); } *IpAddress = net_long(*IpAddress); return (*endPointer == '\0'); #else // MILLEN UNICODE_STRING unicodeString; STRING aString; UCHAR dataBuffer[IP_ADDRESS_STRING_LENGTH]; NTSTATUS status; PUCHAR addressPtr, cp, startPointer, endPointer; ULONG digit, multiplier; int i; PAGED_CODE(); aString.Length = 0; aString.MaximumLength = IP_ADDRESS_STRING_LENGTH; aString.Buffer = dataBuffer; RtlInitUnicodeString(&unicodeString, AddressString); status = RtlUnicodeStringToAnsiString( &aString, &unicodeString, FALSE ); if (!NT_SUCCESS(status)) { return (FALSE); } *IpAddress = 0; addressPtr = (PUCHAR) IpAddress; startPointer = dataBuffer; endPointer = dataBuffer; i = 3; while (i >= 0) { // // Collect the characters up to a '.' or the end of the string. // while ((*endPointer != '.') && (*endPointer != '\0')) { endPointer++; } if (startPointer == endPointer) { return (FALSE); } // // Convert the number. // for (cp = (endPointer - 1), multiplier = 1, digit = 0; cp >= startPointer; cp--, multiplier *= 10 ) { if ((*cp < '0') || (*cp > '9') || (multiplier > 100)) { return (FALSE); } digit += (multiplier * ((ULONG) (*cp - '0'))); } if (digit > 255) { return (FALSE); } addressPtr[i] = (UCHAR) digit; // // We are finished if we have found and converted 4 octets and have // no other characters left in the string. // if ((i-- == 0) && ((*endPointer == '\0') || (*endPointer == ' ')) ) { return (TRUE); } if (*endPointer == '\0') { return (FALSE); } startPointer = ++endPointer; } return (FALSE); #endif // MILLEN } ULONG RouteMatch( IN WCHAR * RouteString, IN IPAddr Address, IN IPMask Mask, OUT IPAddr * DestVal, OUT IPMask * DestMask, OUT IPAddr * GateVal, OUT ULONG * Metric ) /*++ Routine Description This function checks if a perisitent route should be assigned to a given interface based on the interface address & mask. Arguments RouteString - A NULL-terminated route laid out as Dest,Mask,Gate. Address - The IP address of the interface being processed. Mask - The subnet mask of the interface being processed. DestVal - A pointer to the decoded destination IP address. DestVal - A pointer to the decoded destination subnet mask. DestVal - A pointer to the decoded destination first hop gateway. Metric - A pointer to the decoded route metric. Return Value: The route type, IRE_TYPE_DIRECT or IRE_TYPE_INDIRECT, if the route should be added to the interface, IRE_TYPE_INVALID otherwise. --*/ { #define ROUTE_SEPARATOR L',' WCHAR *labelPtr; WCHAR *indexPtr = RouteString; ULONG i; UNICODE_STRING ustring; NTSTATUS status; BOOLEAN noMetric = FALSE; PAGED_CODE(); // // The route is laid out in the string as "Dest,Mask,Gateway,Metric". // The metric may not be there if this system was upgraded from // NT 3.51. // // Parse the string and convert each label. // for (i = 0; i < 4; i++) { labelPtr = indexPtr; while (1) { if (*indexPtr == UNICODE_NULL) { if ((i < 2) || (indexPtr == labelPtr)) { return (IRE_TYPE_INVALID); } if (i == 2) { // // Old route - no metric. // noMetric = TRUE; } break; } if (*indexPtr == ROUTE_SEPARATOR) { *indexPtr = UNICODE_NULL; break; } indexPtr++; } switch (i) { case 0: if (!IPConvertStringToAddress(labelPtr, DestVal)) { return (IRE_TYPE_INVALID); } break; case 1: if (!IPConvertStringToAddress(labelPtr, DestMask)) { return (IRE_TYPE_INVALID); } break; case 2: if (!IPConvertStringToAddress(labelPtr, GateVal)) { return (IRE_TYPE_INVALID); } break; case 3: RtlInitUnicodeString(&ustring, labelPtr); status = RtlUnicodeStringToInteger( &ustring, 0, Metric ); if (!NT_SUCCESS(status)) { return (IRE_TYPE_INVALID); } break; default: ASSERT(0); return (IRE_TYPE_INVALID); } if (noMetric) { // // Default to 1. // *Metric = 1; break; } indexPtr++; } if (IP_ADDR_EQUAL(*GateVal, Address)) { return (IRE_TYPE_DIRECT); } if (IP_ADDR_EQUAL((*GateVal & Mask), (Address & Mask))) { return (IRE_TYPE_INDIRECT); } return (IRE_TYPE_INVALID); } VOID SetPersistentRoutesForNTE( IPAddr Address, IPMask Mask, ULONG IFIndex ) /*++ Routine Description Adds persistent routes that match an interface. The routes are read from a list in the registry. Arguments Address - The address of the new interface Mask - The subnet mask of the new interface. IFIndex - The index of the new interface. Return Value: None. --*/ { #define ROUTE_DATA_STRING_SIZE (51 * sizeof(WCHAR)) #define BASIC_INFO_SIZE (sizeof(KEY_VALUE_BASIC_INFORMATION) - sizeof(WCHAR) + ROUTE_DATA_STRING_SIZE) #if !MILLEN WCHAR IPRoutesRegistryKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters\\PersistentRoutes"; #else // !MILLEN WCHAR IPRoutesRegistryKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\VxD\\MSTCP\\PersistentRoutes"; #endif // MILLEN UCHAR workbuf[BASIC_INFO_SIZE]; PKEY_VALUE_BASIC_INFORMATION basicInfo = (PKEY_VALUE_BASIC_INFORMATION) workbuf; ULONG resultLength; ULONG type; HANDLE regKey; IPAddr destVal; IPMask destMask; IPAddr gateVal; ULONG metric; TDIObjectID id; ULONG enumIndex = 0; CTELockHandle TableHandle; RouteTableEntry *RTE, *TempRTE; IPRouteEntry routeEntry; NTSTATUS status, setStatus; DEBUGMSG(DBG_TRACE && DBG_ROUTE, (DTEXT("+SetPersistenRoutesForNTE(%x, %x, %x)\n"), Address, Mask, IFIndex)); // // Open the registry key to read list of persistant routes // status = OpenRegKey(®Key, IPRoutesRegistryKey); DEBUGMSG(DBG_WARN && !NT_SUCCESS(status), (DTEXT("SetPersistentRoutesForNTE: failed to open registry key %ls\n"), IPRoutesRegistryKey)); if (NT_SUCCESS(status)) { do { // // Enum each route from the registry list // status = ZwEnumerateValueKey( regKey, enumIndex, KeyValueBasicInformation, basicInfo, BASIC_INFO_SIZE - sizeof(WCHAR), &resultLength ); if (!NT_SUCCESS(status)) { if (status == STATUS_BUFFER_OVERFLOW) { continue; } break; } #if !MILLEN // Millennium seems to return REG_NONE in this case for some reason. // Do we really care, since we are just using the name, and not the // value? if (basicInfo->Type != REG_SZ) { DEBUGMSG(DBG_ERROR, (DTEXT("SetPersistentRoutesForNTE: !NOT REG_SZ!\n"))); continue; } #endif // MILLEN DEBUGMSG(DBG_INFO && DBG_ROUTE, (DTEXT("SetPersistentRoutesForNTE: read key: %ls\n"), basicInfo->Name)); // // Ensure NULL termination // basicInfo->Name[basicInfo->NameLength / sizeof(WCHAR)] = UNICODE_NULL; basicInfo->NameLength += sizeof(WCHAR); type = RouteMatch( basicInfo->Name, Address, Mask, &destVal, &destMask, &gateVal, &metric ); DEBUGMSG(DBG_WARN && type == IRE_TYPE_INVALID, (DTEXT("SetPersistentRoutesForNTE: RouteMatch returned IRE_TYPE_INVALID\n"))); if (type != IRE_TYPE_INVALID) { // // Do we already have a route with dest, mask ? // routeEntry.ire_dest = net_long(destVal); routeEntry.ire_mask = net_long(destMask); CTEGetLock(&RouteTableLock.Lock, &TableHandle); RTE = FindMatchingRTE(routeEntry.ire_dest, routeEntry.ire_mask, 0, 0, &TempRTE, MATCH_NONE); CTEFreeLock(&RouteTableLock.Lock, TableHandle); DEBUGMSG(DBG_WARN && RTE, (DTEXT("SetPersistentRoutesForNTE: route already exists RTE %x\n"), RTE)); if (!RTE) { // // We do not have a route, so add this one // id.toi_entity.tei_entity = CL_NL_ENTITY; id.toi_entity.tei_instance = 0; id.toi_class = INFO_CLASS_PROTOCOL; id.toi_type = INFO_TYPE_PROVIDER; id.toi_id = IP_MIB_RTTABLE_ENTRY_ID; routeEntry.ire_nexthop = net_long(gateVal); routeEntry.ire_type = type; routeEntry.ire_metric1 = metric; routeEntry.ire_index = IFIndex; routeEntry.ire_metric2 = (ULONG) - 1; routeEntry.ire_metric3 = (ULONG) - 1; routeEntry.ire_metric4 = (ULONG) - 1; routeEntry.ire_metric5 = (ULONG) - 1; routeEntry.ire_proto = IRE_PROTO_PERSIST_LOCAL; routeEntry.ire_age = 0; setStatus = IPSetInfo( &id, &routeEntry, sizeof(IPRouteEntry) ); DEBUGMSG(DBG_WARN && setStatus != IP_SUCCESS, (DTEXT("SetPersistentRoutesForNTE: faile to add route [%x, %x, %x, %d], status %d\n"), destVal, destMask, gateVal, metric, setStatus)); } } } while (++enumIndex); ZwClose(regKey); } } extern NetTableEntry *LoopNTE; VOID IPUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This routine cleans up the IP layer. Arguments: DriverObject - Pointer to driver object created by the system. Return Value: None. When the function returns, the driver is unloaded. --*/ { PNDIS_BUFFER Buffer; PSINGLE_LIST_ENTRY BufferLink; PNDIS_PACKET Packet; PacketContext *PC; struct PCCommon *Common; CTELockHandle Handle; NdisResEntry *nrEntry; NdisResEntry *nextEntry; #if IPMCAST if(IpMcastDeviceObject != NULL) { DeinitializeIpMcast(IpMcastDeviceObject); } #endif // IPMCAST // // Free up loopback resources // CTEInitBlockStrucEx(&LoopNTE->nte_timerblock); LoopNTE->nte_if->if_flags |= IF_FLAGS_DELETING; if ((LoopNTE->nte_flags & NTE_TIMER_STARTED) && !CTEStopTimer(&LoopNTE->nte_timer)) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not stop loopback timer - waiting on unload event\n")); (VOID) CTEBlock(&LoopNTE->nte_timerblock); KeClearEvent(&LoopNTE->nte_timerblock.cbs_event); } CTEFreeMem(LoopNTE); // // Shut down all timers // NTE timers are stopped at DelIF time // CTEInitBlockStrucEx(&TcpipUnloadBlock); fRouteTimerStopping = TRUE; if (!CTEStopTimer(&IPRouteTimer)) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not stop route timer - waiting on unload event\n")); #if !MILLEN if (KeReadStateEvent(&(TcpipUnloadBlock.cbs_event))) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Event is signaled...\n")); } #endif // !MILLEN (VOID) CTEBlock(&TcpipUnloadBlock); KeClearEvent(&TcpipUnloadBlock.cbs_event); } // // Free any residual memory - IP buffer/pkt pools // KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Freeing Header buffer pools...\n")); MdpDestroyPool(IpHeaderPool); NdisFreePacketPool(NdisPacketPool); if (TDBufferPool) { NdisFreeBufferPool(TDBufferPool); TDBufferPool = NULL; } if (TDPacketPool) { NdisFreePacketPool(TDPacketPool); TDPacketPool = NULL; } if (IPProviderHandle) { TdiDeregisterProvider(IPProviderHandle); } // // Free the cached entity-list // if (IPEntityList) { CTEFreeMem(IPEntityList); IPEntityList = NULL; IPEntityCount = 0; } // // Free the list of bindings // if (IPBindList) { CTEFreeMem(IPBindList); IPBindList = NULL; } // // Free firewall-hook resources // FreeFirewallQ(); // // Call into TCP so it can shut down // TCPUnload(DriverObject); // // Delete the IP device // IoDeleteDevice(IPDeviceObject); } NTSTATUS IPAddNTEContextList( HANDLE KeyHandle, ushort contextValue, uint isPrimary ) /*++ Routine Description: Writes the interface context of the NTE in the registry. Arguments: KeyHandle - Open handle to the parent key of the value to write. contextvalue - The context value of the NTE isPrimary -whether or not this is a Primary NTE Return Value: STATUS_SUCCESS or an appropriate failure code. --*/ { UNICODE_STRING_NEW contextString; // buffer holding the nte context list NTSTATUS status; // status of this operation PWSTR nextContext; // buffer where next context is stored int i, nextDigit; contextString.Buffer = CTEAllocMemBoot(WORK_BUFFER_SIZE * sizeof(WCHAR)); if (contextString.Buffer == NULL) { CTELogEvent( IPDriverObject, EVENT_TCPIP_NO_RESOURCES_FOR_INIT, 3, 0, NULL, 0, NULL ); status = STATUS_INSUFFICIENT_RESOURCES; return status; } RtlZeroMemory(contextString.Buffer, WORK_BUFFER_SIZE * sizeof(WCHAR)); contextString.Buffer[0] = UNICODE_NULL; contextString.Length = 0; contextString.MaximumLength = WORK_BUFFER_SIZE * sizeof(WCHAR); KeWaitForMutexObject(&NTEContextMutex, Executive, KernelMode, FALSE, NULL); if (!isPrimary) { status = GetRegMultiSZValueNew( KeyHandle, L"NTEContextList", &contextString ); if (NT_SUCCESS(status)) { ASSERT(contextString.Length > 0); if (contextString.MaximumLength >= (contextString.Length + (2 + NTE_CONTEXT_SIZE) * sizeof(WCHAR))) { } else { char *newBuf; newBuf = CTEAllocMemBoot(contextString.Length + (2 + NTE_CONTEXT_SIZE) * sizeof(WCHAR)); if (!newBuf) goto Exit; RtlCopyMemory(newBuf, contextString.Buffer, contextString.Length); RtlZeroMemory(newBuf + contextString.Length, (2 + NTE_CONTEXT_SIZE) * sizeof(WCHAR)); CTEFreeMem(contextString.Buffer); contextString.MaximumLength = contextString.Length + (2 + NTE_CONTEXT_SIZE) * sizeof(WCHAR); contextString.Buffer = (PWCHAR) newBuf; } nextContext = (PWCHAR) ((char *)contextString.Buffer + contextString.Length - 1 * sizeof(WCHAR)); RtlZeroMemory(nextContext, (2 + NTE_CONTEXT_SIZE) * sizeof(WCHAR)); contextString.Length += 1 * sizeof(WCHAR); } else { goto Exit; } } else { // this is the first nte of this if. // add 2 null chars in the length. nextContext = contextString.Buffer; contextString.Length += 2 * sizeof(WCHAR); } for (i = NTE_CONTEXT_SIZE; i >= 2;) { nextDigit = contextValue % 16; if (nextDigit >= 0 && nextDigit <= 9) { nextContext[--i] = L'0' + nextDigit; } else { nextContext[--i] = L'A' + nextDigit - 10; } contextValue /= 16; } // now prepend 0x nextContext[0] = L'0'; nextContext[1] = L'x'; contextString.Length += NTE_CONTEXT_SIZE * sizeof(WCHAR); status = SetRegMultiSZValueNew( KeyHandle, L"NTEContextList", &contextString ); Exit: KeReleaseMutex(&NTEContextMutex, FALSE); if (contextString.Buffer) { CTEFreeMem(contextString.Buffer); } return status; } NTSTATUS IPDelNTEContextList( HANDLE KeyHandle, ushort contextValue ) /*++ Routine Description: Writes the interface context of the NTE in the registry. Arguments: KeyHandle - Open handle to the parent key of the value to write. NTE - The pointer to the NTE Return Value: STATUS_SUCCESS or an appropriate failure code. --*/ { UNICODE_STRING_NEW contextString; // buffer holding the nte context list NTSTATUS status; // status of this operation PWSTR nextContext; // buffer where next context is stored int i, nextDigit; WCHAR thisContext[NTE_CONTEXT_SIZE]; contextString.Buffer = CTEAllocMemBoot(WORK_BUFFER_SIZE * sizeof(WCHAR)); if (contextString.Buffer == NULL) { CTELogEvent( IPDriverObject, EVENT_TCPIP_NO_RESOURCES_FOR_INIT, 3, 0, NULL, 0, NULL ); status = STATUS_INSUFFICIENT_RESOURCES; return status; } RtlZeroMemory(contextString.Buffer, WORK_BUFFER_SIZE * sizeof(WCHAR)); contextString.Buffer[0] = UNICODE_NULL; contextString.Length = 0; contextString.MaximumLength = WORK_BUFFER_SIZE * sizeof(WCHAR); // first read the ntecontext list. KeWaitForMutexObject(&NTEContextMutex, Executive, KernelMode, FALSE, NULL); status = GetRegMultiSZValueNew( KeyHandle, L"NTEContextList", &contextString ); if (NT_SUCCESS(status)) { ASSERT(contextString.Length > 0); // convert this NTE's context into string so that we can do simple mem compare. for (i = NTE_CONTEXT_SIZE; i >= 2;) { nextDigit = contextValue % 16; if (nextDigit >= 0 && nextDigit <= 9) { thisContext[--i] = L'0' + nextDigit; } else { thisContext[--i] = L'A' + nextDigit - 10; } contextValue /= 16; } // now prepend 0x thisContext[0] = L'0'; thisContext[1] = L'x'; // now find thisContext in the contextlist, remove it off the list // and update the contextList in the registry. status = STATUS_UNSUCCESSFUL; for (i = 0; (i + NTE_CONTEXT_SIZE + 1)*sizeof(WCHAR) < contextString.Length && contextString.Buffer[i] != L'\0' && contextString.Buffer[i + NTE_CONTEXT_SIZE] == L'\0'; i += NTE_CONTEXT_SIZE + 1) { nextContext = &contextString.Buffer[i]; if (RtlEqualMemory(nextContext, thisContext, NTE_CONTEXT_SIZE * sizeof(WCHAR))) { PWSTR nextNextContext = nextContext + NTE_CONTEXT_SIZE + 1; RtlMoveMemory(nextContext, nextNextContext, contextString.Length - ((PSTR)nextNextContext - (PSTR)contextString.Buffer)); contextString.Length -= (NTE_CONTEXT_SIZE + 1) * sizeof(WCHAR); status = SetRegMultiSZValueNew(KeyHandle, L"NTEContextList", &contextString); break; } } } KeReleaseMutex(&NTEContextMutex, FALSE); if (contextString.Buffer) { CTEFreeMem(contextString.Buffer); } return status; } static const struct { IP_STATUS ipStatus; NTSTATUS ntStatus; } IPStatusMap[] = { { IP_SUCCESS, STATUS_SUCCESS }, { IP_NO_RESOURCES, STATUS_INSUFFICIENT_RESOURCES }, { IP_DEVICE_DOES_NOT_EXIST, STATUS_DEVICE_DOES_NOT_EXIST }, { IP_DUPLICATE_ADDRESS, STATUS_DUPLICATE_NAME }, { IP_PENDING, STATUS_PENDING }, { IP_DUPLICATE_IPADD, STATUS_DUPLICATE_OBJECTID }, { IP_GENERAL_FAILURE, STATUS_UNSUCCESSFUL } }; NTSTATUS IPStatusToNTStatus( IN IP_STATUS ipStatus ) /*++ Routine Description: This routine converts IP_STATUS to NTSTATUS. Arguments: ipStatus - IP status code. Return Value: correcponding NTSTATUS --*/ { ULONG i; for (i = 0; IPStatusMap[i].ipStatus != IP_GENERAL_FAILURE; i++) { if (IPStatusMap[i].ipStatus == ipStatus) { return IPStatusMap[i].ntStatus; } } return STATUS_UNSUCCESSFUL; }