/*++ Copyright (c) 1994 Microsoft Corporation Module Name: ioctl.c Abstract: This file contains functions to indicate to the other system services that the IP address and other TCP/IP parameters have changed. Author: Madan Appiah (madana) 30-Nov-1993 Environment: User Mode - Win32 Revision History: --*/ #include "precomp.h" #include "dhcpglobal.h" #include #include #include #include // for DhcpReRegisterDynDns ? #define NT // to include data structures for NT build. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_DEST 0 #define DEFAULT_DEST_MASK 0 #define DEFAULT_METRIC 1 // // Following two functions (APIs) should be remove when MIKEMAS provides // entry point DLL for these API. // // Also all TDI related include files that are checked-in in this dir. // should be delfile'd when MIKEMAS checkin those files in private\inc. // NTSTATUS TCPQueryInformationEx( IN HANDLE TCPHandle, IN TDIObjectID FAR *ID, OUT void FAR *Buffer, IN OUT DWORD FAR *BufferSize, IN OUT BYTE FAR *Context ) /*++ Routine Description: This routine provides the interface to the TDI QueryInformationEx facility of the TCP/IP stack on NT. Someday, this facility will be part of TDI. Arguments: TCPHandle - Open handle to the TCP driver ID - The TDI Object ID to query Buffer - Data buffer to contain the query results BufferSize - Pointer to the size of the results buffer. Filled in with the amount of results data on return. Context - Context value for the query. Should be zeroed for a new query. It will be filled with context information for linked enumeration queries. Return Value: An NTSTATUS value. --*/ { TCP_REQUEST_QUERY_INFORMATION_EX queryBuffer; DWORD queryBufferSize; NTSTATUS status; IO_STATUS_BLOCK ioStatusBlock; if (TCPHandle == NULL) { return(TDI_INVALID_PARAMETER); } queryBufferSize = sizeof(TCP_REQUEST_QUERY_INFORMATION_EX); RtlCopyMemory( &(queryBuffer.ID), ID, sizeof(TDIObjectID) ); RtlCopyMemory( &(queryBuffer.Context), Context, CONTEXT_SIZE ); status = NtDeviceIoControlFile( TCPHandle, // Driver handle NULL, // Event NULL, // APC Routine NULL, // APC context &ioStatusBlock, // Status block IOCTL_TCP_QUERY_INFORMATION_EX, // Control code &queryBuffer, // Input buffer queryBufferSize, // Input buffer size Buffer, // Output buffer *BufferSize // Output buffer size ); if (status == STATUS_PENDING) { status = NtWaitForSingleObject( TCPHandle, TRUE, NULL ); } if (status == STATUS_SUCCESS) { // // Copy the return context to the caller's context buffer // RtlCopyMemory( Context, &(queryBuffer.Context), CONTEXT_SIZE ); *BufferSize = (ULONG)ioStatusBlock.Information; status = ioStatusBlock.Status; } else { if ( status != TDI_BUFFER_OVERFLOW) { DhcpPrint((DEBUG_ERRORS, "TCPQueryInformationEx returned failure %lx\n", status )); } *BufferSize = 0; } return(status); } NTSTATUS TCPSetInformationEx( IN HANDLE TCPHandle, IN TDIObjectID FAR *ID, IN void FAR *Buffer, IN DWORD FAR BufferSize ) /*++ Routine Description: This routine provides the interface to the TDI SetInformationEx facility of the TCP/IP stack on NT. Someday, this facility will be part of TDI. Arguments: TCPHandle - Open handle to the TCP driver ID - The TDI Object ID to set Buffer - Data buffer containing the information to be set BufferSize - The size of the set data buffer. Return Value: An NTSTATUS value. --*/ { PTCP_REQUEST_SET_INFORMATION_EX setBuffer; NTSTATUS status; IO_STATUS_BLOCK ioStatusBlock; DWORD setBufferSize; if (TCPHandle == NULL) { return(TDI_INVALID_PARAMETER); } setBufferSize = FIELD_OFFSET(TCP_REQUEST_SET_INFORMATION_EX, Buffer) + BufferSize; setBuffer = LocalAlloc(LMEM_FIXED, setBufferSize); if (setBuffer == NULL) { return(TDI_NO_RESOURCES); } setBuffer->BufferSize = BufferSize; RtlCopyMemory( &(setBuffer->ID), ID, sizeof(TDIObjectID) ); RtlCopyMemory( &(setBuffer->Buffer[0]), Buffer, BufferSize ); status = NtDeviceIoControlFile( TCPHandle, // Driver handle NULL, // Event NULL, // APC Routine NULL, // APC context &ioStatusBlock, // Status block IOCTL_TCP_SET_INFORMATION_EX, // Control code setBuffer, // Input buffer setBufferSize, // Input buffer size NULL, // Output buffer 0 // Output buffer size ); if (status == STATUS_PENDING) { status = NtWaitForSingleObject( TCPHandle, TRUE, NULL ); if ( STATUS_SUCCESS == status ) status = ioStatusBlock.Status; } else if ( status == STATUS_SUCCESS ) { status = ioStatusBlock.Status; } LocalFree(setBuffer); return(status); } DWORD OpenDriver( HANDLE *Handle, LPWSTR DriverName ) /*++ Routine Description: This function opens a specified IO drivers. Arguments: Handle - pointer to location where the opened drivers handle is returned. DriverName - name of the driver to be opened. Return Value: Windows Error Code. --*/ { OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; UNICODE_STRING nameString; NTSTATUS status; *Handle = NULL; // // Open a Handle to the IP driver. // RtlInitUnicodeString(&nameString, DriverName); InitializeObjectAttributes( &objectAttributes, &nameString, OBJ_CASE_INSENSITIVE, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); status = NtCreateFile( Handle, SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, &objectAttributes, &ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, 0, NULL, 0 ); return( RtlNtStatusToDosError( status ) ); } DWORD IPSetIPAddress( DWORD IpInterfaceContext, DHCP_IP_ADDRESS Address, DHCP_IP_ADDRESS SubnetMask ) /*++ Routine Description: This rountine sets the IP Address and subnet mask of the IP stack. Arguments: IpInterfaceContext - Context value of the Ip Table Entry. Address - New IP Address. SubnetMask - New subnet mask. Return Value: Windows Error Code. --*/ { HANDLE IPHandle; IP_SET_ADDRESS_REQUEST requestBuffer; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; DWORD Error; DhcpPrint((DEBUG_TRACE, "IPSetIPAddress: settting %s address on interface context %lx\n", inet_ntoa(*(struct in_addr *)&Address), IpInterfaceContext )); Error = OpenDriver(&IPHandle, DD_IP_DEVICE_NAME); if (Error != ERROR_SUCCESS) { return( Error ); } // // Initialize the input buffer. // requestBuffer.Context = (USHORT)IpInterfaceContext; requestBuffer.Address = Address; requestBuffer.SubnetMask = SubnetMask; status = NtDeviceIoControlFile( IPHandle, // Driver handle NULL, // Event NULL, // APC Routine NULL, // APC context &ioStatusBlock, // Status block IOCTL_IP_SET_ADDRESS, // Control code &requestBuffer, // Input buffer sizeof(IP_SET_ADDRESS_REQUEST), // Input buffer size NULL, // Output buffer 0 // Output buffer size ); if ( status == STATUS_UNSUCCESSFUL ) { // whoa? syscall failed? should not really happen DhcpPrint( (DEBUG_ERRORS, "IOCTL_IP_SET_ADDRESS returned immediate STATUS_UNSUCCESSFUL for %s\n", inet_ntoa(*(struct in_addr *)&Address))); } else if ( STATUS_PENDING == status ) { // ip is trying to do things.. status = NtWaitForSingleObject( IPHandle, TRUE, NULL ); status = ioStatusBlock.Status; } else if ( STATUS_SUCCESS == status ) { // DeviceIoControl worked, but how does ip feel? status = ioStatusBlock.Status; } if ( STATUS_SUCCESS != status ) { DhcpPrint((DEBUG_ERRORS, "IOCTL_IP_SET_ADDRESS returned STATUS_UNSUCCESSFUL<0x%lx> for %s\n", status, inet_ntoa(*(struct in_addr *)&Address))); } NtClose( IPHandle ); if( 0 == Address && STATUS_DUPLICATE_NAME == status ) { // I think this is what happens when you try to set zero if it is already zero!!! DhcpPrint((DEBUG_ERRORS, "Trying to set zero address: ADDRESS_CONFLICT??? Ignored\n")); status = STATUS_SUCCESS; } if( IP_MEDIA_DISCONNECT == status ) { // // You get this if the media is disconnected... We just ignore this for now. // DhcpPrint((DEBUG_ERRORS, "Trying to set address while media disconnected..\n")); status = STATUS_SUCCESS; } return( RtlNtStatusToDosError( status ) ); } DWORD IPDelIPAddress( DWORD IpInterfaceContext ) /*++ Routine Description: This rountine deletes a static IP address for the supplied IpInterfaceContext Arguments: IpInterfaceContext - Context value of the Ip Table Entry. Return Value: Windows Error Code. --*/ { HANDLE IPHandle; IP_DELETE_NTE_REQUEST requestBuffer; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; DWORD Error; DhcpPrint((DEBUG_MISC, "IPDelIPAddress: deleting address with ipcontext %x \n", IpInterfaceContext)); Error = OpenDriver(&IPHandle, DD_IP_DEVICE_NAME); if (Error != ERROR_SUCCESS) { return( Error ); } requestBuffer.Context = (USHORT)IpInterfaceContext; status = NtDeviceIoControlFile( IPHandle, // Driver handle NULL, // Event NULL, // APC Routine NULL, // APC context &ioStatusBlock, // Status block IOCTL_IP_DELETE_NTE, // Control code &requestBuffer, // Input buffer sizeof(requestBuffer), // Input buffer size NULL, // Output buffer 0 // Output buffer size ); if ( status == STATUS_UNSUCCESSFUL ) { DhcpPrint( (DEBUG_ERRORS, "IOCTL_IP_DELETE_NTE returned immediate STATUS_UNSUCCESSFUL for context %lx\n", IpInterfaceContext)); } else if ( STATUS_PENDING == status ) { status = NtWaitForSingleObject( IPHandle, TRUE, NULL ); status = ioStatusBlock.Status; if ( STATUS_UNSUCCESSFUL == status ){ DhcpPrint( (DEBUG_ERRORS, "IOCTL_IP_DELETE_NTE returned STATUS_UNSUCCESSFUL for context %lx\n", IpInterfaceContext)); } } else if ( STATUS_SUCCESS == status ) { status = ioStatusBlock.Status; } NtClose( IPHandle ); return( RtlNtStatusToDosError( status ) ); } DWORD IPDelNonPrimaryAddresses( LPWSTR AdapterName ) /*++ Routine Description: This rountine deletes all the static ip addresses but the primary one. Arguments: AdapterName - The adaptername that identifies the ip interface Return Value: Windows Error Code. --*/ { DWORD Error; LPWSTR RegKey = NULL; HKEY KeyHandle = NULL; LPWSTR nteContextList = NULL; PCHAR oemNextContext = NULL; LPWSTR nextContext; DWORD i; // // Open device parameter. // RegKey = DhcpAllocateMemory( (wcslen(DHCP_SERVICES_KEY) + wcslen(REGISTRY_CONNECT_STRING) + wcslen(AdapterName) + wcslen(DHCP_ADAPTER_PARAMETERS_KEY) + 1) * sizeof(WCHAR) ); // termination char. if( RegKey == NULL ) { Error = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } wcscpy( RegKey, DHCP_SERVICES_KEY ); wcscat( RegKey, DHCP_ADAPTER_PARAMETERS_KEY ); wcscat( RegKey, REGISTRY_CONNECT_STRING ); wcscat( RegKey, AdapterName ); // // open this key. // Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, RegKey, 0, // Reserved field DHCP_CLIENT_KEY_ACCESS, &KeyHandle ); if( Error != ERROR_SUCCESS ) { goto Cleanup; } Error = GetRegistryString( KeyHandle, DHCP_NTE_CONTEXT_LIST, &nteContextList, NULL ); if ( ERROR_SUCCESS != Error ) { DhcpPrint( (DEBUG_ERRORS, "GetIpInterfaceContext: Could not read nteContextList %lx\n", Error)); goto Cleanup; } // if the adapter is disabled, nteContextList contains nothing // more than a L'\0'. No address to be deleted in this case. if (*nteContextList != L'\0') { nextContext = nteContextList; // delete all the addresses but the first one. for( nextContext += (wcslen(nextContext) + 1), i = 1; *nextContext != L'\0'; i++, nextContext += (wcslen(nextContext) + 1) ) { ULONG ival; oemNextContext = DhcpUnicodeToOem(nextContext, NULL); if ( NULL == oemNextContext ) { Error = ERROR_BAD_FORMAT; } else { ival = strtoul(oemNextContext, NULL, 0); if ( ival == ULONG_MAX || ival == 0) { Error = ERROR_BAD_FORMAT; } else { // delete this address Error = IPDelIPAddress( ival ); } } } } Cleanup: if( RegKey != NULL ) { DhcpFreeMemory( RegKey ); } if( KeyHandle != NULL ) { RegCloseKey( KeyHandle ); } if ( nteContextList != NULL ) { DhcpFreeMemory( nteContextList ); } if ( oemNextContext != NULL ) { DhcpFreeMemory( oemNextContext ); } return( Error ); } DWORD IPGetWOLCapability( IN ULONG IfIndex, OUT PULONG pRetVal ) { HANDLE IPHandle; ULONG RetVal; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; DWORD Error; DhcpPrint(( DEBUG_MISC, "IPGetWOLCapability(0x%lx) called\n", IfIndex )); Error = OpenDriver(&IPHandle, DD_IP_DEVICE_NAME); if (Error != ERROR_SUCCESS) { return( Error ); } status = NtDeviceIoControlFile( IPHandle, // Driver handle NULL, // Event NULL, // APC Routine NULL, // APC context &ioStatusBlock, // Status block IOCTL_IP_GET_WOL_CAPABILITY, // Control code &IfIndex, // Input buffer sizeof(IfIndex), // Input buffer size pRetVal, // Output buffer sizeof(*pRetVal) // Output buffer size ); if ( status == STATUS_UNSUCCESSFUL ) { DhcpPrint(( DEBUG_ERRORS, "IOCTL_IP_GET_WOL_CAPABILITY(0x%lx): STATUS_UNSUCCESSFUL\n", IfIndex )); } else if ( STATUS_PENDING == status ) { status = NtWaitForSingleObject( IPHandle, TRUE, NULL ); status = ioStatusBlock.Status; if ( STATUS_UNSUCCESSFUL == status ) DhcpPrint(( DEBUG_ERRORS, "IOCTL_IP_GET_WOL_CAPABILITY(0x%lx): failed\n", IfIndex )); } else if( STATUS_SUCCESS == status ) { status = ioStatusBlock.Status; } NtClose( IPHandle ); return( RtlNtStatusToDosError( status ) ); } DWORD IPAddIPAddress( LPWSTR AdapterName, DHCP_IP_ADDRESS Address, DHCP_IP_ADDRESS SubnetMask ) /*++ Routine Description: This rountine adds an static ipaddress to the IP interface for the given adaptername. Arguments: AdapterName - The adaptername that identifies the ip interface Address - IPaddress to be added SubnetMask - SubnetMask Return Value: Windows Error Code. --*/ { HANDLE IPHandle; PIP_ADD_NTE_REQUEST requestBuffer; IP_ADD_NTE_RESPONSE responseBuffer; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; DWORD Error; DWORD requestBufferSize; DhcpPrint((DEBUG_MISC, "IPAddIPAddress: adding an address on adapter %ws\n", AdapterName)); Error = OpenDriver(&IPHandle, DD_IP_DEVICE_NAME); if (Error != ERROR_SUCCESS) { return( Error ); } // // The adapter name that we pass to TCPIP should be of form // \device\TCPIP_ // // // Initialize the input buffer. // requestBufferSize = FIELD_OFFSET(IP_ADD_NTE_REQUEST, InterfaceNameBuffer) + (wcslen(DHCP_TCPIP_DEVICE_STRING) // \Device + wcslen(AdapterName)) * sizeof(WCHAR); requestBuffer = DhcpAllocateMemory( requestBufferSize + sizeof(WCHAR)); if (requestBuffer == NULL) { NtClose(IPHandle); return ERROR_NOT_ENOUGH_MEMORY; } wcscpy((PWCHAR)requestBuffer->InterfaceNameBuffer, DHCP_TCPIP_DEVICE_STRING); wcscat((PWCHAR)requestBuffer->InterfaceNameBuffer, AdapterName); RtlInitUnicodeString(&requestBuffer->InterfaceName, (PWCHAR)requestBuffer->InterfaceNameBuffer); RtlUpcaseUnicodeString( &requestBuffer->InterfaceName, &requestBuffer->InterfaceName, FALSE ); requestBuffer->InterfaceContext = INVALID_INTERFACE_CONTEXT; requestBuffer->Address = Address; requestBuffer->SubnetMask = SubnetMask; status = NtDeviceIoControlFile( IPHandle, // Driver handle NULL, // Event NULL, // APC Routine NULL, // APC context &ioStatusBlock, // Status block IOCTL_IP_ADD_NTE, // Control code requestBuffer, // Input buffer requestBufferSize, // Input buffer size &responseBuffer, // Output buffer sizeof(responseBuffer) // Output buffer size ); if ( status == STATUS_UNSUCCESSFUL ) { DhcpPrint( (DEBUG_ERRORS, "IOCTL_IP_ADD_NTE returned immediate STATUS_UNSUCCESSFUL for %s\n", inet_ntoa(*(struct in_addr *)&Address))); } else if ( STATUS_PENDING == status ) { status = NtWaitForSingleObject( IPHandle, TRUE, NULL ); status = ioStatusBlock.Status; } else if (STATUS_SUCCESS == status ) { status = ioStatusBlock.Status; } DhcpPrint( (DEBUG_ERRORS, "IOCTL_IP_ADD_NTE returned 0x%lx for %s\n", status, inet_ntoa(*(struct in_addr *)&Address))); NtClose( IPHandle ); DhcpFreeMemory(requestBuffer); return( RtlNtStatusToDosError( status ) ); } DWORD IPSetInterface( DWORD IpInterfaceContext ) /*++ Routine Description: This rountine sets the IP interface for sending DHCP broadcasts. Arguments: IpInterfaceContext - Context value of the Ip Table Entry. Return Value: Windows Error Code. --*/ { HANDLE IPHandle; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; DWORD Error; Error = OpenDriver(&IPHandle, DD_IP_DEVICE_NAME); if (Error != ERROR_SUCCESS) { return( Error ); } status = NtDeviceIoControlFile( IPHandle, // Driver handle NULL, // Event NULL, // APC Routine NULL, // APC context &ioStatusBlock, // Status block IOCTL_IP_SET_DHCP_INTERFACE, // Control code &IpInterfaceContext, // Input buffer sizeof(IpInterfaceContext), // Input buffer size NULL, // Output buffer 0 // Output buffer size ); if (status == STATUS_PENDING) { status = NtWaitForSingleObject( IPHandle, TRUE, NULL ); if ( STATUS_SUCCESS == status ) status = ioStatusBlock.Status; } else if ( STATUS_SUCCESS == status ) { status = ioStatusBlock.Status; } NtClose(IPHandle); return( RtlNtStatusToDosError( status ) ); } DWORD IPResetInterface( DWORD dwIpInterfaceContext ) /*++ Routine Description: This rountine resets the IP interface to restore normal IP interface behaviour. Arguments: VOID Return Value: Windows Error Code. --*/ { DWORD Error; LOCK_INTERFACE(); Error = IPSetInterface(dwIpInterfaceContext); if( ERROR_SUCCESS == Error ) { Error = IPSetInterface( 0xFFFFFFFF ); } UNLOCK_INTERFACE(); return Error; } DWORD IPResetIPAddress( DWORD dwInterfaceContext, DHCP_IP_ADDRESS SubnetMask ) /*++ Routine Description: This rountine resets the IP Address of the IP to ZERO. Arguments: IpInterfaceContext - Context value of the Ip Table Entry. SubnetMask - default subnet mask. Return Value: Windows Error Code. --*/ { DWORD dwResult = IPSetIPAddress( dwInterfaceContext, 0, SubnetMask); if ( ERROR_SUCCESS != dwResult ) DhcpPrint( ( DEBUG_ERRORS, "IPResetIPAddress failed: %x\n", dwResult )); return dwResult; } // // Doing our own NdisHandlePnPRequest per Alid's suggestion // #include #include #define UNICODE_STRING_SIZE(x) \ ((((x) == NULL) ? 0 : (x)->Length) + sizeof(WCHAR)) VOID DhcpNdispUnicodeStringToVar( IN PVOID Base, IN PUNICODE_STRING String, IN OUT PNDIS_VAR_DATA_DESC NdisVar ) /*++ Routine Description: This function copies the contents of a UNICODE_STRING to an NDIS_VAR_DATA structure. NdisVar->Offset is treated as an input parameter and represents the offset into Base that the string characters should be copied to. Arguments: Base - Specifies the base address of the IOCTL buffer. String - Supplies a pointer to the UNICODE_STRING that should be copied. NdisVar - Supplies a pointer to the target NDIS_VAR_DATA_DESC. Its Offset field is taken as input, and its Length and MaximumLength fields are treated as output. Return Value: None. --*/ { PWCHAR destination; // // NdisVar->Offset is assumed to be filled in and is treated // as an input parameter. // destination = (PWCHAR)(((PCHAR)Base) + NdisVar->Offset); // // Copy over the UNICODE_STRING, if any, and set NdisVar->Length // if ((String != NULL) && (String->Length > 0)) { NdisVar->Length = String->Length; memcpy(destination, String->Buffer, NdisVar->Length ); } else { NdisVar->Length = 0; } // // Null-terminate, fill in MaxiumLength and we're done. // *(destination + NdisVar->Length / sizeof(WCHAR)) = L'\0'; NdisVar->MaximumLength = NdisVar->Length + sizeof(WCHAR); } UINT DhcpNdisHandlePnPEvent( IN UINT Layer, IN UINT Operation, IN PUNICODE_STRING LowerComponent OPTIONAL, IN PUNICODE_STRING UpperComponent OPTIONAL, IN PUNICODE_STRING BindList OPTIONAL, IN PVOID ReConfigBuffer OPTIONAL, IN UINT ReConfigBufferSize OPTIONAL ) { PNDIS_PNP_OPERATION Op; NDIS_PNP_OPERATION tempOp; HANDLE hDevice; BOOL fResult = FALSE; UINT cb, Size; DWORD Error; ULONG padding; do { // // Validate Layer & Operation // if (((Layer != NDIS) && (Layer != TDI)) || ((Operation != BIND) && (Operation != UNBIND) && (Operation != RECONFIGURE) && (Operation != UNLOAD) && (Operation != REMOVE_DEVICE) && (Operation != ADD_IGNORE_BINDING) && (Operation != DEL_IGNORE_BINDING) && (Operation != BIND_LIST))) { Error = ERROR_INVALID_PARAMETER; break; } // // Allocate and initialize memory for the block to be passed down. The buffer // will look like this: // // // +=================================+ // | NDIS_PNP_OPERATION | // | ReConfigBufferOff | ----+ // +--- | LowerComponent.Offset | | // | | UpperComponent.Offset | --+ | // +-|--- | BindList.Offset | | | // | +--> +---------------------------------+ | | // | | LowerComponentStringBuffer | | | // | +---------------------------------+ <-+ | // | | UpperComponentStringBuffer | | // +----> +---------------------------------+ | // | BindListStringBuffer | | // +---------------------------------+ | // | Padding to ensure ULONG_PTR | | // | alignment of ReConfigBuffer | | // +---------------------------------+ <---+ // | ReConfigBuffer | // +=================================+ // // tempOp is a temporary structure into which we will store offsets as // they are calculated. This temporary structure will be moved to // the head of the real buffer once its size is known and it is // allocated. // Size = sizeof(NDIS_PNP_OPERATION); tempOp.LowerComponent.Offset = Size; Size += UNICODE_STRING_SIZE(LowerComponent); tempOp.UpperComponent.Offset = Size; Size += UNICODE_STRING_SIZE(UpperComponent); tempOp.BindList.Offset = Size; Size += UNICODE_STRING_SIZE(BindList); padding = (sizeof(ULONG_PTR) - (Size & (sizeof(ULONG_PTR) - 1))) & (sizeof(ULONG_PTR) - 1); Size += padding; tempOp.ReConfigBufferOff = Size; Size += ReConfigBufferSize + 1; Op = (PNDIS_PNP_OPERATION)LocalAlloc(LPTR, Size); if (Op == NULL) { Error = ERROR_NOT_ENOUGH_MEMORY; break; } // // We have a buffer of the necessary size. Copy in the partially- // filled in tempOp, then fill in the remaining fields and copy the // data into the buffer. // *Op = tempOp; Op->Layer = Layer; Op->Operation = Operation; // // Copy over the three unicode strings // DhcpNdispUnicodeStringToVar( Op, LowerComponent, &Op->LowerComponent ); DhcpNdispUnicodeStringToVar( Op, UpperComponent, &Op->UpperComponent ); DhcpNdispUnicodeStringToVar( Op, BindList, &Op->BindList ); // // Finally, copy over the ReConfigBuffer // Op->ReConfigBufferSize = ReConfigBufferSize; if (ReConfigBufferSize > 0) { memcpy((PUCHAR)Op + Op->ReConfigBufferOff, ReConfigBuffer, ReConfigBufferSize); } *((PUCHAR)Op + Op->ReConfigBufferOff + ReConfigBufferSize) = 0; hDevice = CreateFile(L"\\\\.\\NDIS", 0, 0, // sharing mode - not significant NULL, // security attributes OPEN_EXISTING, 0, // file attributes and flags NULL); // handle to template file if (hDevice != INVALID_HANDLE_VALUE) { fResult = DeviceIoControl(hDevice, IOCTL_NDIS_DO_PNP_OPERATION, Op, // input buffer Size, // input buffer size NULL, // output buffer 0, // output buffer size &cb, // bytes returned NULL); // OVERLAPPED structure Error = GetLastError(); CloseHandle(hDevice); } else { Error = GetLastError(); } LocalFree(Op); } while (FALSE); SetLastError(Error); return(fResult); } ULONG TcpIpNotifyRouterDiscoveryOption( IN LPCWSTR AdapterName, IN BOOL fOptionPresent, IN DWORD OptionValue ) { ULONG Error; ULONG RetVal; WCHAR TcpipAdapter[300+sizeof(DHCP_ADAPTERS_DEVICE_STRING)]; UNICODE_STRING UpperLayer, LowerLayer, BindString; IP_PNP_RECONFIG_REQUEST Request; Error = NO_ERROR; if (wcslen(AdapterName) > 150) { ASSERT(0); Error = ERROR_INVALID_DATA; return Error; } RtlZeroMemory(&Request, sizeof(Request)); Request.version = IP_PNP_RECONFIG_VERSION; Request.Flags |= IP_PNP_FLAG_DHCP_PERFORM_ROUTER_DISCOVERY; if( fOptionPresent ) { Request.DhcpPerformRouterDiscovery = (BOOLEAN)OptionValue; } wcscpy(TcpipAdapter, DHCP_ADAPTERS_DEVICE_STRING); wcscat(TcpipAdapter, AdapterName); RtlInitUnicodeString(&BindString, NULL); // no bind string RtlInitUnicodeString(&UpperLayer, TEXT("Tcpip")); RtlInitUnicodeString(&LowerLayer, TcpipAdapter); RetVal = DhcpNdisHandlePnPEvent( NDIS, // uiLayer RECONFIGURE, // Operation &LowerLayer, &UpperLayer, &BindString, &Request, sizeof(Request) ); if( 0 == RetVal) Error = GetLastError(); if( ERROR_SUCCESS != Error) { DhcpPrint((DEBUG_ERRORS, "TcpipNotifyRegChanges:0x%ld\n", Error)); } return Error; } DWORD // win32 status NetBTNotifyRegChanges( // Notify NetBT of some parameter changes IN LPWSTR AdapterName // the adapter that needs this change notification ) { DWORD Error; DWORD RetVal; WCHAR NetBTBindAdapter[300+sizeof( DHCP_TCPIP_DEVICE_STRING )]; UNICODE_STRING UpperLayer; UNICODE_STRING LowerLayer; UNICODE_STRING BindString; if (wcslen(AdapterName) > 150) { return ERROR_INVALID_DATA; } Error = ERROR_SUCCESS; wcscpy(NetBTBindAdapter, DHCP_TCPIP_DEVICE_STRING); wcscat(NetBTBindAdapter, AdapterName); // \\Device\\Tcpip_{AdapterGuid} is what NetBT expects. RtlInitUnicodeString(&BindString, NULL); // no bind string RtlInitUnicodeString(&UpperLayer, TEXT("NetBT")); RtlInitUnicodeString(&LowerLayer, NetBTBindAdapter); RetVal = DhcpNdisHandlePnPEvent( TDI, // uiLayer RECONFIGURE, // Operation &LowerLayer, &UpperLayer, &BindString, NULL, 0 ); if( 0 != RetVal) Error = GetLastError(); if( ERROR_SUCCESS != Error) { DhcpPrint((DEBUG_ERRORS, "NetBTNotifyRegChanges:0x%ld\n", Error)); } return Error; } NTSTATUS FindHardwareAddr( HANDLE TCPHandle, TDIEntityID *EList, DWORD cEntities, IPAddrEntry *pIAE, LPBYTE HardwareAddressType, LPBYTE *HardwareAddress, LPDWORD HardwareAddressLength, DWORD *pIpInterfaceInstance, #ifdef BOOTPERF BOOL *pfInterfaceDown, #endif BOOTPERF BOOL *pfFound ) /*++ Routine Description: This function browses the TDI entries list and finds out the hardware address for the specified address entry. Arguments: TCPHandle - handle TCP driver. EList - list of TDI entries. cEntities - number of entries in the above list. pIAE - IP entry for which we need HW address. HardwareAddressType - hardware address type. HardwareAddress - pointer to location where the HW address buffer pointer is returned. HardwareAddressLength - length of the HW address returned. pIpInterfaceInstance - pointer to interface instance for the matching entry pfFound - pointer to BOOL location which is set to TRUE if we found the HW address otherwise set to FALSE. Return Value: Windows Error Code. --*/ { DWORD i; BYTE Context[CONTEXT_SIZE]; TDIObjectID ID; NTSTATUS Status; DWORD Size; *pfFound = FALSE; ID.toi_entity.tei_entity = IF_MIB; ID.toi_type = INFO_TYPE_PROVIDER; for ( i = 0; i < cEntities; i++ ) { DhcpPrint((DEBUG_TCP_INFO, "FindHardwareAddress: entity %lx, type %lx, instance %lx\n", i, EList[i].tei_entity, EList[i].tei_instance)); if (EList[i].tei_entity == IF_ENTITY) { IFEntry IFE; DWORD IFType; // // Check and make sure the interface supports MIB-2 // ID.toi_entity.tei_entity = EList[i].tei_entity; ID.toi_entity.tei_instance = EList[i].tei_instance; ID.toi_class = INFO_CLASS_GENERIC; ID.toi_id = ENTITY_TYPE_ID; Size = sizeof( IFType ); IFType = 0; RtlZeroMemory(Context, CONTEXT_SIZE); DhcpPrint((DEBUG_TCP_INFO, "FindHardwareAddress: querying IF_ENTITY %lx\n",i)); Status = TCPQueryInformationEx( TCPHandle, &ID, &IFType, &Size, Context); if (Status != TDI_SUCCESS) { continue; } if ( IFType != IF_MIB ) { DhcpPrint((DEBUG_TCP_INFO, "FindHardwareAddress: entity %lx does not support MIB\n",i)); continue; } // // We've found an interface, get its index and see if it // matches the IP Address entry // ID.toi_class = INFO_CLASS_PROTOCOL; ID.toi_id = IF_MIB_STATS_ID; Size = sizeof(IFEntry); RtlZeroMemory(Context, CONTEXT_SIZE); RtlZeroMemory(&IFE, Size); Status = TCPQueryInformationEx( TCPHandle, &ID, &IFE, &Size, Context); if ( Status != TDI_SUCCESS && Status != TDI_BUFFER_OVERFLOW ) { goto Cleanup; } DhcpPrint(( DEBUG_TCP_INFO, "FindHardwareAddress: IFEntry %lx has if_index %lx.\n", &IFE, IFE.if_index )); if ( IFE.if_index == pIAE->iae_index ) { LPBYTE Address; DhcpPrint(( DEBUG_TCP_INFO, "FindHardwareAddress: IFEntry %lx has our if_index %lx\n", &IFE, pIAE->iae_index )); // // Allocate Memory. // Address = DhcpAllocateMemory( IFE.if_physaddrlen ); if( Address == NULL ) { Status = STATUS_NO_MEMORY; goto Cleanup; } RtlCopyMemory( Address, IFE.if_physaddr, IFE.if_physaddrlen ); switch( IFE.if_type ) { case IF_TYPE_ETHERNET_CSMACD: *HardwareAddressType = HARDWARE_TYPE_10MB_EITHERNET; break; case IF_TYPE_ISO88025_TOKENRING: case IF_TYPE_FDDI: *HardwareAddressType = HARDWARE_TYPE_IEEE_802; break; case IF_TYPE_OTHER: *HardwareAddressType = HARDWARE_ARCNET; break; case IF_TYPE_PPP: *HardwareAddressType = HARDWARE_PPP; break; case IF_TYPE_IEEE1394: *HardwareAddressType = HARDWARE_1394; break; default: DhcpPrint(( DEBUG_ERRORS, "Invalid HW Type, %ld.\n", IFE.if_type )); *HardwareAddressType = HARDWARE_ARCNET; break; } *HardwareAddress = Address; *HardwareAddressLength = IFE.if_physaddrlen; *pIpInterfaceInstance = ID.toi_entity.tei_instance; DhcpPrint( (DEBUG_MISC, "tei_instance = %d\n", *pIpInterfaceInstance )); *pfFound = TRUE; #ifdef BOOTPERF if( pfInterfaceDown ) { *pfInterfaceDown = (IFE.if_adminstatus != IF_STATUS_UP); } #endif BOOTPERF Status = TDI_SUCCESS; goto Cleanup; } } } // // we couldn't find a corresponding entry. But it may be available // in another tanel. // Status = STATUS_SUCCESS; Cleanup: if (Status != TDI_SUCCESS) { DhcpPrint(( DEBUG_ERRORS, "FindHardwareAddr failed, %lx.\n", Status )); } return TDI_SUCCESS; } #ifdef BOOTPERF DWORD DhcpQueryHWInfoEx( DWORD IpInterfaceContext, DWORD *pIpInterfaceInstance, DWORD *pOldIpAddress OPTIONAL, DWORD *pOldMask OPTIONAL, BOOL *pfInterfaceDown OPTIONAL, LPBYTE HardwareAddressType, LPBYTE *HardwareAddress, LPDWORD HardwareAddressLength ) /*++ Routine Description: This function queries and browses through the TDI list to find out the specified IpTable entry and then determines the HW address that corresponds to this entry. Arguments: IpInterfaceContext - Context value of the Ip Table Entry. pIpInterfaceInstance - pointer to the interface instance ID that corresponds to matching IpTable entry pOldIpAddress - the old IP address that used to exist. pOldMask - the old IP mask for this entry. pfInterfaceDown -- location of BOOL that tells if the interface is DOWN or UP HardwareAddressType - hardware address type. HardwareAddress - pointer to location where the HW address buffer pointer is returned. HardwareAddressLength - length of the HW address returned. Return Value: Windows Error Code. --*/ { DWORD Error; NTSTATUS Status; DWORD i, j; BYTE Context[CONTEXT_SIZE]; TDIEntityID *EList = NULL; TDIObjectID ID; DWORD Size; DWORD NumReturned; BOOL fFound; IPAddrEntry * pIAE = NULL; IPAddrEntry *pIAEMatch = NULL; HANDLE TCPHandle = NULL; DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: querying for interface context %lx\n", IpInterfaceContext)); Error = OpenDriver(&TCPHandle, DD_TCP_DEVICE_NAME); if (Error != ERROR_SUCCESS) { return( Error ); } // // The first thing to do is get the list of available entities, and make // sure that there are some interface entities present. // ID.toi_entity.tei_entity = GENERIC_ENTITY; ID.toi_entity.tei_instance = 0; ID.toi_class = INFO_CLASS_GENERIC; ID.toi_type = INFO_TYPE_PROVIDER; ID.toi_id = ENTITY_LIST_ID; Size = sizeof(TDIEntityID) * MAX_TDI_ENTITIES; EList = (TDIEntityID*)DhcpAllocateMemory(Size); if (EList == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlZeroMemory(EList, Size); RtlZeroMemory(Context, CONTEXT_SIZE); Status = TCPQueryInformationEx(TCPHandle, &ID, EList, &Size, Context); if (Status != TDI_SUCCESS) { goto Cleanup; } NumReturned = Size/sizeof(TDIEntityID); DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: No of total entities %lx\n", NumReturned)); for (i = 0; i < NumReturned; i++) { DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: entity %lx, type %lx, instance %lx\n", i, EList[i].tei_entity, EList[i].tei_instance)); if ( EList[i].tei_entity == CL_NL_ENTITY ) { IPSNMPInfo IPStats; DWORD NLType; // // Does this entity support IP? // ID.toi_entity.tei_entity = EList[i].tei_entity; ID.toi_entity.tei_instance = EList[i].tei_instance; ID.toi_class = INFO_CLASS_GENERIC; ID.toi_type = INFO_TYPE_PROVIDER; ID.toi_id = ENTITY_TYPE_ID; Size = sizeof( NLType ); NLType = 0; RtlZeroMemory(Context, CONTEXT_SIZE); DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: querying CL_NL_ENTITY %lx\n",i)); Status = TCPQueryInformationEx(TCPHandle, &ID, &NLType, &Size, Context); if (Status != TDI_SUCCESS) { goto Cleanup; } if ( NLType != CL_NL_IP ) { DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: entity %lx does not support IP\n",i)); continue; } // // We've got an IP driver so get it's address table // ID.toi_class = INFO_CLASS_PROTOCOL; ID.toi_id = IP_MIB_STATS_ID; Size = sizeof(IPStats); RtlZeroMemory( &IPStats, Size); RtlZeroMemory(Context, CONTEXT_SIZE); Status = TCPQueryInformationEx( TCPHandle, &ID, &IPStats, &Size, Context); if (Status != TDI_SUCCESS) { goto Cleanup; } DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: entity %lx, numaddr %lx\n",i, IPStats.ipsi_numaddr)); if ( IPStats.ipsi_numaddr == 0 ) { continue; } Size = sizeof(IPAddrEntry) * IPStats.ipsi_numaddr; while (1) { DWORD OldSize; pIAE = DhcpAllocateMemory(Size); if ( pIAE == NULL ) { Status = STATUS_NO_MEMORY; goto Cleanup; } ID.toi_id = IP_MIB_ADDRTABLE_ENTRY_ID; RtlZeroMemory(Context, CONTEXT_SIZE); OldSize = Size; Status = TCPQueryInformationEx(TCPHandle, &ID, pIAE, &Size, Context); if (Status == TDI_BUFFER_OVERFLOW) { Size = OldSize * 2; DhcpFreeMemory(pIAE); pIAE = NULL; continue; } if (Status != TDI_SUCCESS) { goto Cleanup; } if (Status == TDI_SUCCESS) { IPStats.ipsi_numaddr = Size/sizeof(IPAddrEntry); DhcpAssert((Size % sizeof(IPAddrEntry)) == 0); break; } } // // We have the IP address table for this IP driver. // Find the hardware address corresponds to the given // IpInterfaceContext. // // Loop through the IP table entries and findout the // matching entry. // pIAEMatch = NULL; for( j = 0; j < IPStats.ipsi_numaddr ; j++) { DhcpPrint(( DEBUG_TCP_INFO, "QueryHWInfo: IPAddrEntry %lx has iae_index %lx iae_context %lx\n", &pIAE[j], pIAE[j].iae_index, pIAE[j].iae_context )); if( pIAE[j].iae_context == IpInterfaceContext ) { DhcpPrint(( DEBUG_TCP_INFO, "QueryHWInfo: IPAddrEntry %lx has our interface context %lx\n", &pIAE[j], IpInterfaceContext )); pIAEMatch = &pIAE[j]; break; } } if( pIAEMatch == NULL ) { // // freeup the loop memory. // DhcpFreeMemory( pIAE ); pIAE = NULL; continue; } // // NOTE : There may be more than one IpTable in the TDI // list. We need additional information to select the // IpTable we want. For now, we assume only one table // is supported, so pick the first and only table from the // list. // // If the old ip address is requested, return it. // if( pOldIpAddress ) *pOldIpAddress = pIAE->iae_addr; if( pOldMask ) *pOldMask = pIAE->iae_mask; Status = FindHardwareAddr( TCPHandle, EList, NumReturned, pIAEMatch, HardwareAddressType, HardwareAddress, HardwareAddressLength, pIpInterfaceInstance, pfInterfaceDown, &fFound ); if (Status != TDI_SUCCESS) { goto Cleanup; } if ( fFound ) { Status = TDI_SUCCESS; goto Cleanup; } // // freeup the loop memory. // DhcpFreeMemory( pIAE ); pIAE = NULL; } // if IP } // entity traversal Status = STATUS_UNSUCCESSFUL; Cleanup: if( pIAE != NULL ) { DhcpFreeMemory( pIAE ); } if( TCPHandle != NULL ) { NtClose( TCPHandle ); } if (Status != TDI_SUCCESS) { DhcpPrint(( DEBUG_ERRORS, "QueryHWInfo failed, %lx.\n", Status )); } if (NULL != EList) { DhcpFreeMemory(EList); } return( RtlNtStatusToDosError( Status ) ); } DWORD DhcpQueryHWInfo( DWORD IpInterfaceContext, DWORD *pIpInterfaceInstance, LPBYTE HardwareAddressType, LPBYTE *HardwareAddress, LPDWORD HardwareAddressLength ) /*++ Routine Description: See DhcpQueryHWInfo --*/ { return DhcpQueryHWInfoEx( IpInterfaceContext, pIpInterfaceInstance, NULL, NULL, NULL, HardwareAddressType, HardwareAddress, HardwareAddressLength ); } #else BOOTPERF DWORD DhcpQueryHWInfo( DWORD IpInterfaceContext, DWORD *pIpInterfaceInstance, LPBYTE HardwareAddressType, LPBYTE *HardwareAddress, LPDWORD HardwareAddressLength ) /*++ Routine Description: This function queries and browses through the TDI list to find out the specified IpTable entry and then determines the HW address that corresponds to this entry. Arguments: IpInterfaceContext - Context value of the Ip Table Entry. pIpInterfaceInstance - pointer to the interface instance ID that corresponds to matching IpTable entry HardwareAddressType - hardware address type. HardwareAddress - pointer to location where the HW address buffer pointer is returned. HardwareAddressLength - length of the HW address returned. Return Value: Windows Error Code. --*/ { DWORD Error; NTSTATUS Status; DWORD i, j; BYTE Context[CONTEXT_SIZE]; TDIEntityID *EList = NULL; TDIObjectID ID; DWORD Size; DWORD NumReturned; BOOL fFound; IPAddrEntry * pIAE = NULL; IPAddrEntry *pIAEMatch = NULL; HANDLE TCPHandle = NULL; DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: querying for interface context %lx\n", IpInterfaceContext)); Error = OpenDriver(&TCPHandle, DD_TCP_DEVICE_NAME); if (Error != ERROR_SUCCESS) { return( Error ); } // // The first thing to do is get the list of available entities, and make // sure that there are some interface entities present. // ID.toi_entity.tei_entity = GENERIC_ENTITY; ID.toi_entity.tei_instance = 0; ID.toi_class = INFO_CLASS_GENERIC; ID.toi_type = INFO_TYPE_PROVIDER; ID.toi_id = ENTITY_LIST_ID; Size = sizeof(TDIEntityID) * MAX_TDI_ENTITIES; EList = (TDIEntityID*)DhcpAllocateMemory(Size); if (EList == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlZeroMemory(EList, Size); RtlZeroMemory(Context, CONTEXT_SIZE); Status = TCPQueryInformationEx(TCPHandle, &ID, EList, &Size, Context); if (Status != TDI_SUCCESS) { goto Cleanup; } NumReturned = Size/sizeof(TDIEntityID); DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: No of total entities %lx\n", NumReturned)); for (i = 0; i < NumReturned; i++) { DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: entity %lx, type %lx, instance %lx\n", i, EList[i].tei_entity, EList[i].tei_instance)); if ( EList[i].tei_entity == CL_NL_ENTITY ) { IPSNMPInfo IPStats; DWORD NLType; // // Does this entity support IP? // ID.toi_entity.tei_entity = EList[i].tei_entity; ID.toi_entity.tei_instance = EList[i].tei_instance; ID.toi_class = INFO_CLASS_GENERIC; ID.toi_type = INFO_TYPE_PROVIDER; ID.toi_id = ENTITY_TYPE_ID; Size = sizeof( NLType ); NLType = 0; RtlZeroMemory(Context, CONTEXT_SIZE); DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: querying CL_NL_ENTITY %lx\n",i)); Status = TCPQueryInformationEx(TCPHandle, &ID, &NLType, &Size, Context); if (Status != TDI_SUCCESS) { goto Cleanup; } if ( NLType != CL_NL_IP ) { DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: entity %lx does not support IP\n",i)); continue; } // // We've got an IP driver so get it's address table // ID.toi_class = INFO_CLASS_PROTOCOL; ID.toi_id = IP_MIB_STATS_ID; Size = sizeof(IPStats); RtlZeroMemory( &IPStats, Size); RtlZeroMemory(Context, CONTEXT_SIZE); Status = TCPQueryInformationEx( TCPHandle, &ID, &IPStats, &Size, Context); if (Status != TDI_SUCCESS) { goto Cleanup; } DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: entity %lx, numaddr %lx\n",i, IPStats.ipsi_numaddr)); if ( IPStats.ipsi_numaddr == 0 ) { continue; } Size = sizeof(IPAddrEntry) * IPStats.ipsi_numaddr; while (1) { DWORD OldSize; pIAE = DhcpAllocateMemory(Size); if ( pIAE == NULL ) { Status = STATUS_NO_MEMORY; goto Cleanup; } ID.toi_id = IP_MIB_ADDRTABLE_ENTRY_ID; RtlZeroMemory(Context, CONTEXT_SIZE); OldSize = Size; Status = TCPQueryInformationEx(TCPHandle, &ID, pIAE, &Size, Context); if (Status == TDI_BUFFER_OVERFLOW) { Size = OldSize * 2; DhcpFreeMemory(pIAE); pIAE = NULL; continue; } if (Status != TDI_SUCCESS) { goto Cleanup; } if (Status == TDI_SUCCESS) { IPStats.ipsi_numaddr = Size/sizeof(IPAddrEntry); DhcpAssert((Size % sizeof(IPAddrEntry)) == 0); break; } } // // We have the IP address table for this IP driver. // Find the hardware address corresponds to the given // IpInterfaceContext. // // Loop through the IP table entries and findout the // matching entry. // pIAEMatch = NULL; for( j = 0; j < IPStats.ipsi_numaddr ; j++) { DhcpPrint(( DEBUG_TCP_INFO, "QueryHWInfo: IPAddrEntry %lx has iae_index %lx iae_context %lx\n", &pIAE[j], pIAE[j].iae_index, pIAE[j].iae_context )); if( pIAE[j].iae_context == IpInterfaceContext ) { DhcpPrint(( DEBUG_TCP_INFO, "QueryHWInfo: IPAddrEntry %lx has our interface context %lx\n", &pIAE[j], IpInterfaceContext )); pIAEMatch = &pIAE[j]; break; } } if( pIAEMatch == NULL ) { // // freeup the loop memory. // DhcpFreeMemory( pIAE ); pIAE = NULL; continue; } // // NOTE : There may be more than one IpTable in the TDI // list. We need additional information to select the // IpTable we want. For now, we assume only one table // is supported, so pick the first and only table from the // list. Status = FindHardwareAddr( TCPHandle, EList, NumReturned, pIAEMatch, HardwareAddressType, HardwareAddress, HardwareAddressLength, pIpInterfaceInstance, &fFound ); if (Status != TDI_SUCCESS) { goto Cleanup; } if ( fFound ) { Status = TDI_SUCCESS; goto Cleanup; } // // freeup the loop memory. // DhcpFreeMemory( pIAE ); pIAE = NULL; } // if IP } // entity traversal Status = STATUS_UNSUCCESSFUL; Cleanup: if( pIAE != NULL ) { DhcpFreeMemory( pIAE ); } if( TCPHandle != NULL ) { NtClose( TCPHandle ); } if (Status != TDI_SUCCESS) { DhcpPrint(( DEBUG_ERRORS, "QueryHWInfo failed, %lx.\n", Status )); } if (NULL != EList) { DhcpFreeMemory(EList); } return( RtlNtStatusToDosError( Status ) ); } #endif BOOTPERF #if DBG #define print(X) DhcpPrint((DEBUG_TRACE, "%20s\t", inet_ntoa(*(struct in_addr *)&X))) #define printx(X) DhcpPrint((DEBUG_TRACE, "%05x\t", X)) DWORD PrintDefaultGateways( VOID ) { DWORD Error; NTSTATUS Status; HANDLE TCPHandle = NULL; BYTE Context[CONTEXT_SIZE]; TDIObjectID ID; DWORD Size; IPSNMPInfo IPStats; IPAddrEntry *AddrTable = NULL; DWORD NumReturned; DWORD Type; DWORD i; DWORD MatchIndex; IPRouteEntry RouteEntry; IPRouteEntry *RtTable; DHCP_IP_ADDRESS NetworkOrderGatewayAddress; Error = OpenDriver(&TCPHandle, DD_TCP_DEVICE_NAME); if (Error != ERROR_SUCCESS) { return( Error ); } // // Get the NetAddr info, to find an interface index for the gateway. // 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_STATS_ID; Size = sizeof(IPStats); RtlZeroMemory(&IPStats, Size); RtlZeroMemory(Context, CONTEXT_SIZE); Status = TCPQueryInformationEx( TCPHandle, &ID, &IPStats, &Size, Context); if (Status != TDI_SUCCESS) { goto Cleanup; } // hack: RouteTable in IP is about 32 in size... and IP seems tob // be writing the whole bunch always! if(IPStats.ipsi_numroutes <= 32) IPStats.ipsi_numroutes = 32; Size = IPStats.ipsi_numroutes * sizeof(IPRouteEntry); RtTable = DhcpAllocateMemory(Size); if (RtTable == NULL) { Status = STATUS_NO_MEMORY; goto Cleanup; } ID.toi_id = IP_MIB_RTTABLE_ENTRY_ID; RtlZeroMemory(Context, CONTEXT_SIZE); Status = TCPQueryInformationEx( TCPHandle, &ID, RtTable, &Size, Context); if (Status != TDI_SUCCESS) { goto Cleanup; } NumReturned = Size/sizeof(IPAddrEntry); DhcpPrint((DEBUG_TRACE, "IP returned %ld routes\n", NumReturned)); // The following is almost always true... IP returns the whole array.. valid or not! // DhcpAssert( NumReturned == IPStats.ipsi_numroutes ); if( NumReturned > IPStats.ipsi_numroutes) NumReturned = IPStats.ipsi_numroutes; // // We've got the address table. Loop through it. If we find an exact // match for the gateway, then we're adding or deleting a direct route // and we're done. Otherwise try to find a match on the subnet mask, // and remember the first one we find. // DhcpPrint((DEBUG_TRACE,"Dest mask nexthop index metric1 type proto\n")); for (i = 0, MatchIndex = 0xffff; i < NumReturned; i++) { print(RtTable[i].ire_dest); print(RtTable[i].ire_mask); print(RtTable[i].ire_nexthop); printx(RtTable[i].ire_index); printx(RtTable[i].ire_metric1); printx(RtTable[i].ire_type); printx(RtTable[i].ire_proto); DhcpPrint((DEBUG_TRACE, "\n")); } DhcpPrint((DEBUG_TRACE, "--------------------------------------------------------\n")); Status = TDI_SUCCESS; Cleanup: if( AddrTable != NULL ) { DhcpFreeMemory( AddrTable ); } if( TCPHandle != NULL ) { NtClose( TCPHandle ); } if( (Status != TDI_SUCCESS) && (Status != STATUS_UNSUCCESSFUL) ) { // HACK. DhcpPrint(( DEBUG_ERRORS, "SetDefaultGateway failed, %lx.\n", Status )); } return( RtlNtStatusToDosError( Status ) ); } #endif DWORD SetDefaultGateway( DWORD Command, DHCP_IP_ADDRESS GatewayAddress, DWORD Metric ) /*++ Routine Description: This function adds/deletes a default gateway entry from the router table. Arguments: Command : Either DEFAULT_GATEWAY_ADD/DEFAULT_GATEWAY_DELETE. GatewayAddress : Address of the default gateway. Return Value: Windows Error Code. --*/ { DWORD Error; NTSTATUS Status; HANDLE TCPHandle = NULL; BYTE Context[CONTEXT_SIZE]; TDIObjectID ID; DWORD Size; IPSNMPInfo IPStats; IPAddrEntry *AddrTable = NULL; DWORD NumReturned; DWORD Type; DWORD i; DWORD MatchIndex; IPRouteEntry RouteEntry; DHCP_IP_ADDRESS NetworkOrderGatewayAddress; NetworkOrderGatewayAddress = htonl( GatewayAddress ); Error = OpenDriver(&TCPHandle, DD_TCP_DEVICE_NAME); if (Error != ERROR_SUCCESS) { return( Error ); } // // Get the NetAddr info, to find an interface index for the gateway. // 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_STATS_ID; Size = sizeof(IPStats); RtlZeroMemory(&IPStats, Size); RtlZeroMemory(Context, CONTEXT_SIZE); Status = TCPQueryInformationEx( TCPHandle, &ID, &IPStats, &Size, Context ); if (Status != TDI_SUCCESS) { goto Cleanup; } Size = IPStats.ipsi_numaddr * sizeof(IPAddrEntry); AddrTable = DhcpAllocateMemory(Size); if (AddrTable == NULL) { Status = STATUS_NO_MEMORY; goto Cleanup; } ID.toi_id = IP_MIB_ADDRTABLE_ENTRY_ID; RtlZeroMemory(Context, CONTEXT_SIZE); Status = TCPQueryInformationEx( TCPHandle, &ID, AddrTable, &Size, Context ); if (Status != TDI_SUCCESS) { goto Cleanup; } NumReturned = Size/sizeof(IPAddrEntry); DhcpAssert( NumReturned == IPStats.ipsi_numaddr ); // // We've got the address table. Loop through it. If we find an exact // match for the gateway, then we're adding or deleting a direct route // and we're done. Otherwise try to find a match on the subnet mask, // and remember the first one we find. // Type = IRE_TYPE_INDIRECT; for (i = 0, MatchIndex = 0xffff; i < NumReturned; i++) { if( AddrTable[i].iae_addr == NetworkOrderGatewayAddress ) { // // Found an exact match. // MatchIndex = i; Type = IRE_TYPE_DIRECT; break; } // // The next hop is on the same subnet as this address. If // we haven't already found a match, remember this one. // if ( (MatchIndex == 0xffff) && (AddrTable[i].iae_addr != 0) && (AddrTable[i].iae_mask != 0) && ((AddrTable[i].iae_addr & AddrTable[i].iae_mask) == (NetworkOrderGatewayAddress & AddrTable[i].iae_mask)) ) { MatchIndex = i; } } // // We've looked at all of the entries. See if we found a match. // if (MatchIndex == 0xffff) { // // Didn't find a match. // Status = STATUS_UNSUCCESSFUL; goto Cleanup; } // // We've found a match. Fill in the route entry, and call the // Set API. // RouteEntry.ire_dest = DEFAULT_DEST; RouteEntry.ire_index = AddrTable[MatchIndex].iae_index; RouteEntry.ire_metric1 = Metric; RouteEntry.ire_metric2 = (DWORD)(-1); RouteEntry.ire_metric3 = (DWORD)(-1); RouteEntry.ire_metric4 = (DWORD)(-1); RouteEntry.ire_nexthop = NetworkOrderGatewayAddress; RouteEntry.ire_type = (Command == DEFAULT_GATEWAY_DELETE ? IRE_TYPE_INVALID : Type); RouteEntry.ire_proto = IRE_PROTO_NETMGMT; RouteEntry.ire_age = 0; RouteEntry.ire_mask = DEFAULT_DEST_MASK; RouteEntry.ire_metric5 = (DWORD)(-1); RouteEntry.ire_context = 0; Size = sizeof(RouteEntry); ID.toi_id = IP_MIB_RTTABLE_ENTRY_ID; Status = TCPSetInformationEx( TCPHandle, &ID, &RouteEntry, Size ); if ( Status != TDI_SUCCESS && Status != TDI_BUFFER_OVERFLOW ) { goto Cleanup; } Status = TDI_SUCCESS; Cleanup: if( AddrTable != NULL ) { DhcpFreeMemory( AddrTable ); } if( TCPHandle != NULL ) { NtClose( TCPHandle ); } if( (Status != TDI_SUCCESS) && (Status != STATUS_UNSUCCESSFUL) ) { // HACK. DhcpPrint(( DEBUG_ERRORS, "SetDefaultGateway failed, %lx.\n", Status )); } return( RtlNtStatusToDosError( Status ) ); } DWORD GetIpInterfaceContext( LPWSTR AdapterName, DWORD IpIndex, LPDWORD IpInterfaceContext ) /*++ Routine Description: This function returns the IpInterfaceContext for the specified IpAddress and devicename. Arguments: AdapterName - name of the device. IpIndex - index of the IpAddress for this device. IpInterfaceContext - pointer to a location where the interface context is returned. Return Value: Windows Error Code. --*/ { DWORD Error; LPWSTR RegKey = NULL; HKEY KeyHandle = NULL; LPWSTR nteContextList = NULL; PCHAR oemNextContext = NULL; LPWSTR nextContext; DWORD i; *IpInterfaceContext = INVALID_INTERFACE_CONTEXT; // // Open device parameter. // RegKey = DhcpAllocateMemory( (wcslen(DHCP_SERVICES_KEY) + wcslen(REGISTRY_CONNECT_STRING) + wcslen(AdapterName) + wcslen(DHCP_ADAPTER_PARAMETERS_KEY) + 1) * sizeof(WCHAR) ); // termination char. if( RegKey == NULL ) { Error = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } wcscpy( RegKey, DHCP_SERVICES_KEY ); wcscat( RegKey, DHCP_ADAPTER_PARAMETERS_KEY ); wcscat( RegKey, REGISTRY_CONNECT_STRING ); wcscat( RegKey, AdapterName ); // // open this key. // Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, RegKey, 0, // Reserved field DHCP_CLIENT_KEY_ACCESS, &KeyHandle ); if( Error != ERROR_SUCCESS ) { goto Cleanup; } Error = GetRegistryString( KeyHandle, DHCP_NTE_CONTEXT_LIST, &nteContextList, NULL ); if( nteContextList == NULL ) { Error = ERROR_BAD_FORMAT; DhcpPrint((DEBUG_ERRORS, "NteContextList empty\n")); goto Cleanup; } if ( ERROR_SUCCESS != Error ) { DhcpPrint( (DEBUG_ERRORS, "GetIpInterfaceContext: Could not read nteContextList %lx\n", Error)); goto Cleanup; } for( nextContext = nteContextList, i = 0; *nextContext != L'\0' && i < IpIndex; i++, nextContext += (wcslen(nextContext) + 1) ); if ( *nextContext != L'\0' && i == IpIndex ) { ULONG ival; oemNextContext = DhcpUnicodeToOem(nextContext, NULL); if ( NULL == oemNextContext ) { Error = ERROR_BAD_FORMAT; } else { ival = strtoul(oemNextContext, NULL, 0); if ( ival == ULONG_MAX || ival == 0) { Error = ERROR_BAD_FORMAT; } else { *IpInterfaceContext = ival; } } } Cleanup: if( RegKey != NULL ) { DhcpFreeMemory( RegKey ); } if( KeyHandle != NULL ) { RegCloseKey( KeyHandle ); } if ( nteContextList != NULL ) { DhcpFreeMemory( nteContextList ); } if ( oemNextContext != NULL ) { DhcpFreeMemory( oemNextContext ); } return( Error ); } HANDLE APIENTRY DhcpOpenGlobalEvent( void ) /*++ Routine Description: This functions creates global event that signals the the ipaddress changes to other waiting processes. The security dacl is set to NULL that makes anyone to open and read/set this event. Arguments: None. Return Value: Handle value of the global event. If the handle is NULL, GetLastError() function will return Windows error code. --*/ { DWORD Error = NO_ERROR, Status, Length; BOOL BoolError; HANDLE EventHandle = NULL; SECURITY_ATTRIBUTES SecurityAttributes; SID_IDENTIFIER_AUTHORITY Authority = SECURITY_WORLD_SID_AUTHORITY; PACL Acl = NULL; PSID WorldSID = NULL; PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; // // If event can be opened, chose that, don't attempt create // EventHandle = OpenEvent( EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, DHCP_NEW_IPADDRESS_EVENT_NAME ); if( NULL != EventHandle ) return EventHandle; // // Set DACL also.. first create basic SIDs // BoolError = AllocateAndInitializeSid( &Authority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &WorldSID ); if( BoolError == FALSE ) { return NULL; } Length = ( (ULONG)sizeof(ACL) + (ULONG)sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid( WorldSID ) + 16 ); Acl = DhcpAllocateMemory( Length ); if( NULL == Acl ) { Error = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } BoolError = InitializeAcl( Acl, Length, ACL_REVISION2 ); if( FALSE == BoolError ) { Error = GetLastError(); goto Cleanup; } BoolError = AddAccessAllowedAce( Acl, ACL_REVISION2, EVENT_MODIFY_STATE | SYNCHRONIZE, WorldSID ); if( FALSE == BoolError ) { Error = GetLastError(); goto Cleanup; } SecurityDescriptor = DhcpAllocateMemory( SECURITY_DESCRIPTOR_MIN_LENGTH ); if( NULL == SecurityDescriptor ) { Error = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } BoolError = InitializeSecurityDescriptor( SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); if( BoolError == FALSE ) { Error = GetLastError(); goto Cleanup; } BoolError = SetSecurityDescriptorDacl( SecurityDescriptor, TRUE, Acl, FALSE ); if( BoolError == FALSE ) { Error = GetLastError(); goto Cleanup; } SecurityAttributes.nLength = sizeof( SecurityAttributes ); SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor; SecurityAttributes.bInheritHandle = FALSE; EventHandle = CreateEvent( &SecurityAttributes, // everyone all access security. TRUE, // MANUAL reset. FALSE, // initial state is signaled. DHCP_NEW_IPADDRESS_EVENT_NAME ); if( NULL == EventHandle ) { Error = GetLastError(); } else { Error = NO_ERROR; } Cleanup: if( SecurityDescriptor ) { DhcpFreeMemory( SecurityDescriptor ); } if( Acl ) { DhcpFreeMemory( Acl ); } if( WorldSID ) { FreeSid( WorldSID ); } if( NO_ERROR != Error ) { SetLastError( Error ); } return( EventHandle ); } BOOL NdisWanAdapter( // Is this an NdisWan adapter? IN PDHCP_CONTEXT DhcpContext ) { return DhcpContext->HardwareAddressType == HARDWARE_PPP; } DWORD INLINE // win32 status DhcpEnableDynamicConfigEx( // convert from static to dhcp and start DHCP client if reqd IN LPWSTR AdapterName ) { DWORD Error; // ask the dhcp client to takeup this adapter also Error = DhcpEnableDynamicConfig(AdapterName); // now there are a couple possibilities: // - the above call succeeded // - DHCP service is not started or just got terminated // - DHCP service failed to process the request with some error // in the first case just go on straight to exit and return success // in the second case we attempt to start the DHCP service if it is not already started // in the last case we just bail out with the specific error if( Error == ERROR_FILE_NOT_FOUND || Error == ERROR_BROKEN_PIPE ) { SC_HANDLE SCHandle; SC_HANDLE ServiceHandle; SERVICE_STATUS svcStatus; // attempt now to start the DHCP service. // first thing to do is to open SCM SCHandle = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_QUERY_LOCK_STATUS ); if( SCHandle == NULL ) return GetLastError(); // shouldn't happen normally // attempt to open the DHCP service ServiceHandle = OpenService( SCHandle, SERVICE_DHCP, SERVICE_QUERY_STATUS | SERVICE_START ); if (ServiceHandle != NULL) { // check the status of the service if (!QueryServiceStatus(ServiceHandle, &svcStatus) || svcStatus.dwCurrentState != SERVICE_RUNNING) { // is it worthy to attempt to start the service if QueryServiceStatus failed? Error = StartService(ServiceHandle, 0, NULL) ? ERROR_SUCCESS : GetLastError(); } CloseServiceHandle(ServiceHandle); } else Error = GetLastError(); CloseServiceHandle(SCHandle); } return Error; } DWORD // win32 status DhcpNotifyConfigChangeNotifications( // notify whoever needed of param changes VOID ) { HANDLE NotifyEvent; DWORD Error; BOOL BoolError; NotifyEvent = DhcpOpenGlobalEvent(); if( NULL == NotifyEvent ) { Error = GetLastError(); DhcpPrint((DEBUG_ERRORS, "DhcpOpenGlobalEvent:0x%lx\n", Error)); return Error; } BoolError = PulseEvent(NotifyEvent); if( BoolError ) Error = ERROR_SUCCESS; else Error = GetLastError(); CloseHandle(NotifyEvent); if( ERROR_SUCCESS != Error ) { DhcpPrint((DEBUG_ERRORS, "PulseEvent(NotifyEvent): 0x%lx\n", Error)); } return Error; } DWORD // win32 status APIENTRY DhcpNotifyConfigChangeEx( // handle address changes, param changes etc. IN LPWSTR ServerName, // name of server where this will be executed IN LPWSTR AdapterName, // which adapter is going to be reconfigured? IN BOOL IsNewIpAddress,// is address new/ or address is same? IN DWORD IpIndex, // index of addr for this adapter -- 0 ==> first interface... IN DWORD IpAddress, // the ip address that is being set IN DWORD SubnetMask, // corresponding subnet mask IN SERVICE_ENABLE DhcpServiceEnabled, IN ULONG Flags ) { DWORD Error; DWORD IpInterfaceContext; DWORD DefaultSubnetMask; DhcpPrint(( DEBUG_MISC, "DhcpNotifyConfigChange: Adapter %ws, IsNewIp %s, IpAddr %lx, IpIndex %x, ServiceFlag %d\n", AdapterName, IsNewIpAddress ? "TRUE" : "FALSE", IpAddress, IpIndex, DhcpServiceEnabled )); // param checks if( NULL == AdapterName ) return ERROR_INVALID_PARAMETER; if( DhcpEnable == DhcpServiceEnabled ) { // converting from static to dhcp enabled address if( FALSE != IsNewIpAddress ) return ERROR_INVALID_PARAMETER; if( 0 != IpIndex ) return ERROR_INVALID_PARAMETER; if( IpAddress || SubnetMask ) return ERROR_INVALID_PARAMETER; } else if( DhcpDisable == DhcpServiceEnabled){// converting from dhcp to static address if( TRUE != IsNewIpAddress ) return ERROR_INVALID_PARAMETER; if( 0 != IpIndex ) return ERROR_INVALID_PARAMETER; if( 0 == IpAddress || 0 == SubnetMask ) return ERROR_INVALID_PARAMETER; } else { if( IgnoreFlag != DhcpServiceEnabled ) return ERROR_INVALID_PARAMETER; // if( TRUE != IsNewIpAddress ) return ERROR_INVALID_PARAMETER; if( 0xFFFF == IpIndex ) { if( 0 == SubnetMask || 0 == IpAddress ) return ERROR_INVALID_PARAMETER; } } if( IgnoreFlag == DhcpServiceEnabled && FALSE == IsNewIpAddress ) { ULONG LocalError; // just some parameters changed -- currently, this could only be DNS domain name or server list change // or may be static gateway list change or static route change Error = DhcpStaticRefreshParams(AdapterName); if( ERROR_SUCCESS != Error ) { DhcpPrint((DEBUG_ERRORS, "DhcpNotifyConfigChange:DhcpStaticRefreshParams:0x%lx\n", Error)); } LocalError = NetBTNotifyRegChanges(AdapterName); if( ERROR_SUCCESS != LocalError ) { DhcpPrint(( DEBUG_ERRORS, "NetbtNotify(%ws): 0x%lx\n", AdapterName, LocalError )); } return Error; } if( DhcpEnable == DhcpServiceEnabled ) { // convert from static to dhcp Error = IPDelNonPrimaryAddresses( // remove all but the first static address AdapterName ); if( ERROR_SUCCESS != Error ) return Error; Error = DhcpEnableDynamicConfigEx( // convert this to dhcp, maybe starting dhcp in the process AdapterName ); return Error; // notifications already done by service when we dhcp enable it.. } else if( DhcpDisable == DhcpServiceEnabled ) { Error = DhcpDisableDynamicConfig( AdapterName ); if( Error != ERROR_SUCCESS ) return Error; } // NetBt device name stuff removed, see any version pre- Oct 10, 1997 DhcpAssert(TRUE == IsNewIpAddress); // ip address changed in some way DhcpAssert(DhcpEnable != DhcpServiceEnabled); // static->dhcp already handled before DefaultSubnetMask = DhcpDefaultSubnetMask(0); if( INVALID_INTERFACE_CONTEXT == IpIndex ) { // adding a new ip address DhcpAssert( IpAddress && SubnetMask); // cannot be zero, these Error = IPAddIPAddress( // add the reqd ip address AdapterName, IpAddress, SubnetMask ); if( ERROR_SUCCESS != Error ) return Error; } else { // either delete or modify -- first find ipinterfacecontext Error = GetIpInterfaceContext( // get the interface context value for this AdapterName, IpIndex, &IpInterfaceContext ); if( ERROR_SUCCESS != Error ) { DhcpPrint((DEBUG_ERRORS, "GetIpInterfaceContext: 0x%lx\n", Error)); return Error; } if( IpInterfaceContext == INVALID_INTERFACE_CONTEXT) { DhcpPrint((DEBUG_ERRORS, "GetIpInterfaceContext: returned ifctxt=INVALID_INTERFACE_CONTEXT\n")); return ERROR_INVALID_DRIVE; } if ( IpAddress != 0 ) { // if address is non-zero, we are changing address if (Flags & NOTIFY_FLG_RESET_IPADDR) { Error = IPResetIPAddress( // first reset the interface to zero address IpInterfaceContext, DefaultSubnetMask ); if( ERROR_SUCCESS != Error ) return Error; } Error = IPSetIPAddress( // then set the required address IpInterfaceContext, IpAddress, SubnetMask ); if( ERROR_SUCCESS != Error ) return Error; Error = SetOverRideDefaultGateway( AdapterName ); } else { // we are deleting addresses // we need to treat the 0th index separately from others. // IPDelIPAddress actually destroys the NTE from IP. But // we never blow away 0th index NTE. Just reset the ipaddr on it. if ( IpIndex == 0 ) { Error = IPResetIPAddress( // just set this address to zero, dont blow interface away IpInterfaceContext,DefaultSubnetMask ); } else { // in this case, blow this interface altogether Error = IPDelIPAddress( IpInterfaceContext ); } if( ERROR_SUCCESS != Error ) return Error; } } Error = DhcpNotifyConfigChangeNotifications();// notify clients, pulse the global event if( ERROR_SUCCESS != Error ) return Error; // refresh the parameters for static addresses Error = DhcpStaticRefreshParamsInternal( AdapterName, (Flags & NOTIFY_FLG_DO_DNS) ? TRUE : FALSE ); if( ERROR_SUCCESS != Error ) { // ignore this error anyways DhcpPrint((DEBUG_ERRORS, "DhcpStaticRefreshParams(%ws):0x%lx\n", AdapterName,Error)); } return ERROR_SUCCESS; } //================================================================================ // This function (API) notifies the TCP/IP configuration changes to // appropriate services. These changes will be in effect as soon as // possible. // // If the IP Address is modified, the services are reset to ZERO IP // address (to cleanup the current IP address) and then set to new // address. // // IpIndex - if the specified device is configured with multiple IP // addresses, specify index of address that is modified (0 - first // IpAddress, 1 - second IpAddres, so on) Pass 0xFFFF if adding an // additional address. The order of IP address is determined by the // order in the registry MULTI_SZ value "IPAddress" for the static // addresses. For dhcp enabled ip address, only ipindex 0 is valid. // // Everytime when an address is added, removed or modified, the // order in the registry may change. It is caller's responsibility // to check the current order, and hence the index, before calling // this api. // // DhcpServiceEnabled - // IgnoreFlag - indicates Ignore this flag. IgnoreFlag // DhcpEnable - indicates DHCP is enabled for this adapter. // DhcpDisable - indicates DHCP is diabled for this adapter. // //Invarient: // // (1) DHCP enabled IPAddr and Static addr can exists only mutually exclusively. // (2) An interface cannot have more than 1 dhcp enabled ip address. However it // can have many static addresses. // //Usage: // // Case 1: Changing from dhcp enabled ipaddress to static address(es) // - Firstly, change the first dhcp enabled ipaddress to static address. // arguments {SN, AN, TRUE, 0, I1, S1, DhcpDisable} // - Seconfly, add the remaining static address(es) // arguments (SN, AN, TRUE, 0xFFFF, I2, S2, DhcpIgnore) // arguments (SN, AN, TRUE, 0xFFFF, I3, S3, IgnoreFlag) and so on. // // Case 2: Changing from static address(es) to dhcp enabled ipaddress // - Change the first static address to dhcp enabled. The api will delete // the remaining static address(es). // arguments (SN, AN, FALSE, 0, 0, 0, DhcpEnable) // // Case 3: Adding, removing or changing static addresses. // - Adding: // arguments (SN, AN, TRUE, 0xFFFF, I, S, DhcpIgnore) // - Removing, say address # 2 i.e ipindex = 1 // arguments (SN, AN, TRUE, 1, 0, 0, DhcpIgnore) // - Changing, say address # 2 i.e ipindex = 1 // arguments (SN, AN, TRUE, 1, I, S, DhcpIgnore) // //================================================================================ DWORD // win32 status APIENTRY DhcpNotifyConfigChange( // handle address changes, param changes etc. IN LPWSTR ServerName, // name of server where this will be executed IN LPWSTR AdapterName, // which adapter is going to be reconfigured? IN BOOL IsNewIpAddress,// is address new/ or address is same? IN DWORD IpIndex, // index of addr for this adapter -- 0 ==> first interface... IN DWORD IpAddress, // the ip address that is being set IN DWORD SubnetMask, // corresponding subnet mask IN SERVICE_ENABLE DhcpServiceEnabled ) { return DhcpNotifyConfigChangeEx( ServerName, AdapterName, IsNewIpAddress, IpIndex, IpAddress, SubnetMask, DhcpServiceEnabled, NOTIFY_FLG_DO_DNS | NOTIFY_FLG_RESET_IPADDR ); } DWORD BringUpInterface( PVOID pvLocalInformation ) { LOCAL_CONTEXT_INFO *pContext; TCP_REQUEST_SET_INFORMATION_EX *pTcpRequest; TDIObjectID *pObjectID; IFEntry *pIFEntry; int cbTcpRequest; HANDLE hDriver = NULL; DWORD dwResult; NTSTATUS NtStatus; IO_STATUS_BLOCK IoStatusBlock; DhcpPrint( ( DEBUG_MISC, "Entering BringUpInterface\n" )); dwResult = OpenDriver( &hDriver, DD_TCP_DEVICE_NAME ); if ( ERROR_SUCCESS != dwResult ) { DhcpPrint( ( DEBUG_ERRORS, "BringUpInterface: Unable to open TCP driver.\n" ) ); return dwResult; } pContext = (LOCAL_CONTEXT_INFO *) pvLocalInformation; // // compute the input buffer size and allocate // cbTcpRequest = sizeof( TCP_REQUEST_SET_INFORMATION_EX ) + sizeof( IFEntry ) -1; // // initialize the request // pTcpRequest = DhcpAllocateMemory( cbTcpRequest ); if ( !pTcpRequest ) { NtClose( hDriver ); DhcpPrint( ( DEBUG_ERRORS, "BringUpInterface: Insufficient memory\n" )); return ERROR_NOT_ENOUGH_MEMORY; } pTcpRequest->BufferSize = cbTcpRequest - sizeof(TCP_REQUEST_SET_INFORMATION_EX); pObjectID = &pTcpRequest->ID; pIFEntry = (IFEntry *) &pTcpRequest->Buffer[0]; pObjectID->toi_entity.tei_entity = IF_ENTITY; pObjectID->toi_entity.tei_instance = pContext->IpInterfaceInstance; pObjectID->toi_class = INFO_CLASS_PROTOCOL; pObjectID->toi_type = INFO_TYPE_PROVIDER; pObjectID->toi_id = IF_MIB_STATS_ID; pIFEntry->if_adminstatus = IF_STATUS_UP; NtStatus = NtDeviceIoControlFile( hDriver, NULL, NULL, NULL, &IoStatusBlock, IOCTL_TCP_SET_INFORMATION_EX, pTcpRequest, cbTcpRequest, NULL, 0 ); if ( STATUS_PENDING == NtStatus ) { if ( STATUS_SUCCESS == NtWaitForSingleObject( hDriver, TRUE, NULL ) ) NtStatus = IoStatusBlock.Status; #ifdef DBG if ( STATUS_SUCCESS != NtStatus ) DhcpPrint( ( DEBUG_ERRORS, "BringUpInterface: failed to bring up adapter\n" )); #endif } else if ( STATUS_SUCCESS == NtStatus ) { NtStatus = IoStatusBlock.Status; } // // Clean up // if ( hDriver ) NtClose( hDriver ); if ( pTcpRequest ) DhcpFreeMemory( pTcpRequest ); DhcpPrint( ( DEBUG_MISC, "Leaving BringUpInterface\n" ) ); return RtlNtStatusToDosError( NtStatus ); } #if defined(_PNP_POWER_) DWORD IPGetIPEventRequest( HANDLE handle, HANDLE event, UINT seqNo, PIP_GET_IP_EVENT_RESPONSE responseBuffer, DWORD responseBufferSize, PIO_STATUS_BLOCK ioStatusBlock ) /*++ Routine Description: This rountine sends the ioctl to get media sense notification from IP. Arguments: handle - handle to tcpip driver. event - the event we need to do wait on. seqNo - seqNo of the last event received. responseBuffer - pointer to the buffer where event info will be stored. ioStatusBlock - status of the operation, if not pending. Return Value: NT Error Code. --*/ { NTSTATUS status; DWORD Error; IP_GET_IP_EVENT_REQUEST requestBuffer; requestBuffer.SequenceNo = seqNo; RtlZeroMemory( responseBuffer, sizeof(IP_GET_IP_EVENT_RESPONSE)); responseBuffer->ContextStart = 0xFFFF; status = NtDeviceIoControlFile( handle, // Driver handle event, // Event NULL, // APC Routine NULL, // APC context ioStatusBlock, // Status block IOCTL_IP_GET_IP_EVENT, // Control code &requestBuffer, // Input buffer sizeof(IP_GET_IP_EVENT_REQUEST), // Input buffer size responseBuffer, // Output buffer responseBufferSize // Output buffer size ); if ( status == STATUS_SUCCESS ) { status = ioStatusBlock->Status; } return status; } DWORD IPCancelIPEventRequest( HANDLE handle, PIO_STATUS_BLOCK ioStatusBlock ) /*++ Routine Description: This rountine cancels the ioctl that was sent to get media sense notification from IP. Arguments: handle - handle to the ip driver. Return Value: NT Error Code. --*/ { NTSTATUS status; DWORD Error; status = NtCancelIoFile( handle, // Driver handle ioStatusBlock); // Status block DhcpPrint( (DEBUG_TRACE,"IPCancelIPEventRequest: status %lx\n",status)); DhcpAssert( status == STATUS_SUCCESS ); return RtlNtStatusToDosError( status ); } #endif _PNP_POWER_ #define IPSTRING(x) (inet_ntoa(*(struct in_addr*)&(x))) DWORD // return interface index or -1 DhcpIpGetIfIndex( // get the IF index for this adapter IN PDHCP_CONTEXT DhcpContext // context of adapter to get IfIndex for ) { return ((PLOCAL_CONTEXT_INFO)DhcpContext->LocalInformation)->IfIndex; } DWORD QueryIfIndex( IN ULONG IpInterfaceContext, IN ULONG IpInterfaceInstance ) { DWORD Error; DWORD Index; DWORD Size; DWORD NumReturned; DWORD i; BYTE Context[CONTEXT_SIZE]; HANDLE TcpHandle; NTSTATUS Status; TDIObjectID ID; IFEntry IFE; Error = OpenDriver(&TcpHandle, DD_TCP_DEVICE_NAME); if( ERROR_SUCCESS != Error ) { DhcpPrint((DEBUG_ERRORS, "DhcpIpGetIfIndex:OpenDriver(DD_TCP):0x%lx\n", Error)); return (DWORD)-1; } ID.toi_entity.tei_entity = IF_ENTITY; ID.toi_entity.tei_instance = IpInterfaceInstance; ID.toi_class = INFO_CLASS_PROTOCOL; ID.toi_type = INFO_TYPE_PROVIDER; ID.toi_id = IF_MIB_STATS_ID; Size = sizeof(IFE); RtlZeroMemory(&IFE, sizeof(IFE)); RtlZeroMemory(Context, CONTEXT_SIZE); Index = -1; Status = TCPQueryInformationEx( TcpHandle, &ID, &IFE, &Size, Context ); if( TDI_SUCCESS != Status && TDI_BUFFER_OVERFLOW != Status ) { goto Cleanup; } Index = IFE.if_index; DhcpPrint((DEBUG_STACK, "IfIndex(0x%lx,0x%lx):0x%lx\n", IpInterfaceContext, IpInterfaceInstance, Index )); Cleanup: if( TcpHandle ) NtClose(TcpHandle); if( TDI_SUCCESS != Status ) { DhcpPrint((DEBUG_ERRORS, "DhcpIpGetIfIndex:TCPQueryInformationEx:%ld\n", Status)); } DhcpPrint((DEBUG_TCP_INFO, "DhcpIpGetIfIndex:0x%lx\n", Index)); return Index; } DWORD // win32 status DhcpSetRoute( // set a route with the stack IN DWORD Dest, // network order destination IN DWORD DestMask, // network order destination mask IN DWORD IfIndex, // interface index to route IN DWORD NextHop, // next hop n/w order address IN DWORD Metric, // metric IN BOOL IsLocal, // is this a local address? (IRE_DIRECT) IN BOOL IsDelete // is this route being deleted? ) { DWORD Error; NTSTATUS Status; HANDLE TcpHandle; IPRouteEntry RTE; TDIObjectID ID; if( 0xFFFFFFFF == IfIndex ) { // invalid If Index return ERROR_INVALID_PARAMETER; } Error = OpenDriver(&TcpHandle, DD_TCP_DEVICE_NAME); if( ERROR_SUCCESS != Error ) { // should not really fail DhcpPrint((DEBUG_ERRORS, "OpenDriver(TCP_DEVICE):%ld\n", Error)); return Error; } memset(&RTE, 0, sizeof(RTE)); memset(&ID, 0, sizeof(ID)); RTE.ire_dest = Dest; RTE.ire_index = IfIndex; RTE.ire_metric1 = Metric; RTE.ire_metric2 = (DWORD)(-1); RTE.ire_metric3 = (DWORD)(-1); RTE.ire_metric4 = (DWORD)(-1); RTE.ire_metric5 = (DWORD)(-1); RTE.ire_nexthop = NextHop; RTE.ire_type = (IsDelete?IRE_TYPE_INVALID:(IsLocal?IRE_TYPE_DIRECT:IRE_TYPE_INDIRECT)); RTE.ire_proto = IRE_PROTO_NETMGMT; RTE.ire_age = 0; RTE.ire_mask = DestMask; RTE.ire_context = 0; ID.toi_id = IP_MIB_RTTABLE_ENTRY_ID; 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; DhcpPrint((DEBUG_TCP_INFO, "DhcpSetRoute:n/w Dest: %s\n", IPSTRING(Dest))); DhcpPrint((DEBUG_TCP_INFO, "DhcpSetRoute:n/w IfIndex:0x%lx\n", IfIndex)); DhcpPrint((DEBUG_TCP_INFO, "DhcpSetRoute:n/w NextHop:%s\n", IPSTRING(NextHop))); DhcpPrint((DEBUG_TCP_INFO, "DhcpSetRoute:n/w Type:0x%lx\n", RTE.ire_type)); DhcpPrint((DEBUG_TCP_INFO, "DhcpSetRoute:n/w DestMask:%s\n", IPSTRING(DestMask))); Status = TCPSetInformationEx( TcpHandle, &ID, &RTE, sizeof(RTE) ); if( TDI_BUFFER_OVERFLOW == Status ) Status = TDI_SUCCESS; NtClose(TcpHandle); if( TDI_SUCCESS != Status ) { DhcpPrint((DEBUG_ERRORS, "DhcpSetRoute: 0x%lx\n", Status)); } return RtlNtStatusToDosError(Status); } DWORD GetAdapterFlag( HANDLE TCPHandle, DHCP_IP_ADDRESS ipaddr ) { BYTE Buffer[256]; DWORD AdapterFlag; NTSTATUS Status; DWORD Size; TDIObjectID ID; BYTE Context[CONTEXT_SIZE]; /* * Read in adapter flag, which could be * 1. Point to Point * 2. Point to MultiPoint * 3. Unidirectional * 4. Non of the above */ DhcpAssert(CONTEXT_SIZE >= sizeof(ipaddr)); RtlCopyMemory(Context, &ipaddr, CONTEXT_SIZE); 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_INTFC_INFO_ID; Size = sizeof(Buffer); Status = TCPQueryInformationEx(TCPHandle, &ID, Buffer, &Size, Context); if (Status != TDI_SUCCESS) { AdapterFlag = 0; DhcpPrint(( DEBUG_TCP_INFO, "QueryInterfaceType: IpAddress=%s Status=%lx\n", inet_ntoa(*(struct in_addr*)&ipaddr), Status)); } else { AdapterFlag = ((IPInterfaceInfo*)Buffer)->iii_flags; DhcpPrint(( DEBUG_TCP_INFO, "QueryInterfaceType: IpAddress=%s AdapterFlag=%lx\n", inet_ntoa(*(struct in_addr*)&ipaddr), AdapterFlag)); } return AdapterFlag; } BOOL IsUnidirectionalAdapter( DWORD IpInterfaceContext ) /*++ Routine Description: This function queries and browses through the TDI list to find out the specified IpTable entry and then determines if it is a unidirectional adapter. It almost identical to DhcpQueryHWInfo Arguments: IpInterfaceContext - Context value of the Ip Table Entry. Return Value: Windows Error Code. --*/ { DWORD Error; NTSTATUS Status; DWORD i, j; BYTE Context[CONTEXT_SIZE]; TDIEntityID *EList = NULL; TDIObjectID ID; DWORD Size; DWORD NumReturned; BOOL fFound; IPAddrEntry * pIAE = NULL; IPAddrEntry *pIAEMatch = NULL; HANDLE TCPHandle = NULL; DWORD AdapterFlag = 0; BYTE HardwareAddressType = 0; LPBYTE HardwareAddress = NULL; DWORD HardwareAddressLength = 0; DWORD pIpInterfaceInstance = 0; DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: querying for interface context %lx\n", IpInterfaceContext)); Error = OpenDriver(&TCPHandle, DD_TCP_DEVICE_NAME); if (Error != ERROR_SUCCESS) { return( Error ); } // // The first thing to do is get the list of available entities, and make // sure that there are some interface entities present. // ID.toi_entity.tei_entity = GENERIC_ENTITY; ID.toi_entity.tei_instance = 0; ID.toi_class = INFO_CLASS_GENERIC; ID.toi_type = INFO_TYPE_PROVIDER; ID.toi_id = ENTITY_LIST_ID; Size = sizeof(TDIEntityID) * MAX_TDI_ENTITIES; EList = (TDIEntityID*)DhcpAllocateMemory(Size); if (EList == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlZeroMemory(EList, Size); RtlZeroMemory(Context, CONTEXT_SIZE); Status = TCPQueryInformationEx(TCPHandle, &ID, EList, &Size, Context); if (Status != TDI_SUCCESS) { goto Cleanup; } NumReturned = Size/sizeof(TDIEntityID); DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: No of total entities %lx\n", NumReturned)); for (i = 0; i < NumReturned; i++) { DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: entity %lx, type %lx, instance %lx\n", i, EList[i].tei_entity, EList[i].tei_instance)); if ( EList[i].tei_entity == CL_NL_ENTITY ) { IPSNMPInfo IPStats; DWORD NLType; // // Does this entity support IP? // ID.toi_entity.tei_entity = EList[i].tei_entity; ID.toi_entity.tei_instance = EList[i].tei_instance; ID.toi_class = INFO_CLASS_GENERIC; ID.toi_type = INFO_TYPE_PROVIDER; ID.toi_id = ENTITY_TYPE_ID; Size = sizeof( NLType ); NLType = 0; RtlZeroMemory(Context, CONTEXT_SIZE); DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: querying CL_NL_ENTITY %lx\n",i)); Status = TCPQueryInformationEx(TCPHandle, &ID, &NLType, &Size, Context); if (Status != TDI_SUCCESS) { goto Cleanup; } if ( NLType != CL_NL_IP ) { DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: entity %lx does not support IP\n",i)); continue; } // // We've got an IP driver so get it's address table // ID.toi_class = INFO_CLASS_PROTOCOL; ID.toi_id = IP_MIB_STATS_ID; Size = sizeof(IPStats); RtlZeroMemory( &IPStats, Size); RtlZeroMemory(Context, CONTEXT_SIZE); Status = TCPQueryInformationEx( TCPHandle, &ID, &IPStats, &Size, Context); if (Status != TDI_SUCCESS) { goto Cleanup; } DhcpPrint((DEBUG_TCP_INFO, "DhcpQueryHWInfo: entity %lx, numaddr %lx\n",i, IPStats.ipsi_numaddr)); if ( IPStats.ipsi_numaddr == 0 ) { continue; } Size = sizeof(IPAddrEntry) * IPStats.ipsi_numaddr; while (1) { DWORD OldSize; pIAE = DhcpAllocateMemory(Size); if ( pIAE == NULL ) { Status = STATUS_NO_MEMORY; goto Cleanup; } ID.toi_id = IP_MIB_ADDRTABLE_ENTRY_ID; RtlZeroMemory(Context, CONTEXT_SIZE); OldSize = Size; Status = TCPQueryInformationEx(TCPHandle, &ID, pIAE, &Size, Context); if (Status == TDI_BUFFER_OVERFLOW) { Size = OldSize * 2; DhcpFreeMemory(pIAE); pIAE = NULL; continue; } if (Status != TDI_SUCCESS) { goto Cleanup; } if (Status == TDI_SUCCESS) { IPStats.ipsi_numaddr = Size/sizeof(IPAddrEntry); DhcpAssert((Size % sizeof(IPAddrEntry)) == 0); break; } } // // We have the IP address table for this IP driver. // Find the hardware address corresponds to the given // IpInterfaceContext. // // Loop through the IP table entries and findout the // matching entry. // pIAEMatch = NULL; for( j = 0; j < IPStats.ipsi_numaddr ; j++) { DhcpPrint(( DEBUG_TCP_INFO, "QueryHWInfo: IPAddrEntry %lx has iae_index %lx iae_context %lx\n", &pIAE[j], pIAE[j].iae_index, pIAE[j].iae_context )); if( pIAE[j].iae_context == IpInterfaceContext ) { DhcpPrint(( DEBUG_TCP_INFO, "QueryHWInfo: IPAddrEntry %lx has our interface context %lx\n", &pIAE[j], IpInterfaceContext )); pIAEMatch = &pIAE[j]; break; } } if( pIAEMatch == NULL ) { // // freeup the loop memory. // DhcpFreeMemory( pIAE ); pIAE = NULL; continue; } // // NOTE : There may be more than one IpTable in the TDI // list. We need additional information to select the // IpTable we want. For now, we assume only one table // is supported, so pick the first and only table from the // list. Status = FindHardwareAddr( TCPHandle, EList, NumReturned, pIAEMatch, &HardwareAddressType, &HardwareAddress, &HardwareAddressLength, &pIpInterfaceInstance, &fFound ); if (Status != TDI_SUCCESS) { goto Cleanup; } if ( fFound ) { Status = TDI_SUCCESS; AdapterFlag = GetAdapterFlag(TCPHandle, pIAEMatch->iae_addr); goto Cleanup; } // // freeup the loop memory. // DhcpFreeMemory( pIAE ); pIAE = NULL; } // if IP } // entity traversal Status = STATUS_UNSUCCESSFUL; Cleanup: if( pIAE != NULL ) { DhcpFreeMemory( pIAE ); } if( TCPHandle != NULL ) { NtClose( TCPHandle ); } if (Status != TDI_SUCCESS) { DhcpPrint(( DEBUG_ERRORS, "QueryHWInfo failed, %lx.\n", Status )); } if( HardwareAddress ) DhcpFreeMemory(HardwareAddress); if (NULL != EList) { DhcpFreeMemory(EList); } return (AdapterFlag & IP_INTFC_FLAG_UNIDIRECTIONAL); }