/*++ Copyright (c) 1992-1996 Microsoft Corporation Module Name: registry.c Abstract: This file contains the code to read the registry. Author: Jameel Hyder (jameelh@microsoft.com) July 1996 Environment: Kernel mode Revision History: --*/ #include #define _FILENUM_ FILENUM_REGISTRY NTSTATUS ArpSReadGlobalConfiguration( IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Read the global registry. Arguments: RegistryPath - Pointer to the service section in the registry. Return Value: Error code from registry apis. --*/ { NDIS_STATUS Status; NDIS_HANDLE ConfigHandle; // // Open the per-adapter registry config // NdisOpenProtocolConfiguration(&Status, &ConfigHandle, RegistryPath); if (Status == NDIS_STATUS_SUCCESS) { NDIS_STRING ArpsBufString = NDIS_STRING_CONST("ArpBuffers"); NDIS_STRING FlushString = NDIS_STRING_CONST("FlushTime"); PNDIS_CONFIGURATION_PARAMETER Param; // // Read number of configured buffers // NdisReadConfiguration(&Status, &Param, ConfigHandle, &ArpsBufString, NdisParameterInteger); if ((Status == NDIS_STATUS_SUCCESS) && (Param->ParameterType == NdisParameterInteger)) { ArpSBuffers = Param->ParameterData.IntegerData; } // // Should we save cache in a file ? // NdisReadConfiguration(&Status, &Param, ConfigHandle, &FlushString, NdisParameterInteger); if ((Status == NDIS_STATUS_SUCCESS) && (Param->ParameterType == NdisParameterInteger)) { ArpSFlushTime = (USHORT)(Param->ParameterData.IntegerData * MULTIPLIER); } NdisCloseConfiguration(ConfigHandle); } return NDIS_STATUS_SUCCESS; } NDIS_STATUS ArpSReadAdapterConfigFromRegistry( IN PINTF pIntF, OUT PATMARPS_CONFIG pConfig ) /*++ Routine Description: Read configuration for the specified interface. Arguments: pIntF - Interface pConfig - Place to return information read in. Return Value: Error code from registry apis. --*/ { NDIS_STATUS Status; NDIS_HANDLE ConfigHandle; // // Open the per-adapter registry config // NdisOpenProtocolConfiguration(&Status, &ConfigHandle, &pIntF->ConfigString); if (Status == NDIS_STATUS_SUCCESS) { NDIS_STRING RegdAddrsString = NDIS_STRING_CONST("RegisteredAddresses"); NDIS_STRING SelString = NDIS_STRING_CONST("Selector"); NDIS_STRING McsString = NDIS_STRING_CONST("MulticastAddresses"); PNDIS_CONFIGURATION_PARAMETER Param; PWSTR p; UINT i, Length; // // Read the value, if present for the selector byte to be used for the registered sap // for the std. address (as opposed to added addresses). // pConfig->SelByte = 0; NdisReadConfiguration(&Status, &Param, ConfigHandle, &SelString, NdisParameterInteger); if ((Status == NDIS_STATUS_SUCCESS) && (Param->ParameterType == NdisParameterInteger) && (Param->ParameterData.IntegerData <= 0xFF)) { pConfig->SelByte = (UCHAR)(Param->ParameterData.IntegerData); DBGPRINT(DBG_LEVEL_INFO, ("Selector byte for interface %Z is %d\n", &pIntF->InterfaceName, pConfig->SelByte)); } // // Read registered addresses here. On an interface there can be a set of // atm addresses registered. These need to be added and SAPs registered on // them. // pConfig->NumAllocedRegdAddresses = 0; pConfig->RegAddresses = NULL; NdisReadConfiguration(&Status, &Param, ConfigHandle, &RegdAddrsString, NdisParameterMultiString); if ((Status == NDIS_STATUS_SUCCESS) && (Param->ParameterType == NdisParameterMultiString)) { NDIS_STRING String; // // Param now contains a list of atm addresses. Convert them into the right format and store // it in the intf structure. First determine the number of addresses. // for (p = Param->ParameterData.StringData.Buffer, i = 0; *p != L'\0'; i++) { RtlInitUnicodeString(&String, p); DBGPRINT(DBG_LEVEL_INFO, ("Configured address for interface %Z - %Z\n", &pIntF->InterfaceName, &String)); p = (PWSTR)((PUCHAR)p + String.Length + sizeof(WCHAR)); } if (i) { // // Allocate space for the addresses // pConfig->RegAddresses = (PATM_ADDRESS)ALLOC_NP_MEM(sizeof(ATM_ADDRESS) * i, POOL_TAG_ADDR); if (pConfig->RegAddresses == NULL) { LOG_ERROR(NDIS_STATUS_RESOURCES); } else { DBGPRINT(DBG_LEVEL_INFO, ("%d addresses registered for %Z\n", i, &pIntF->InterfaceName)); ZERO_MEM(pConfig->RegAddresses, sizeof(ATM_ADDRESS) * i); for (i = 0, p = Param->ParameterData.StringData.Buffer; *p != L'\0'; NOTHING) { RtlInitUnicodeString(&String, p); NdisConvertStringToAtmAddress(&Status, &String, &pConfig->RegAddresses[i]); if (Status == NDIS_STATUS_SUCCESS) { i++; pConfig->NumAllocedRegdAddresses ++; } else { DBGPRINT(DBG_LEVEL_ERROR, ("ArpSReadAdapterConfiguration: Failed to convert address %Z\n", &String)); } p = (PWSTR)((PUCHAR)p + String.Length + sizeof(WCHAR)); } } } } pConfig->pMcsList = NULL; NdisReadConfiguration(&Status, &Param, ConfigHandle, &McsString, NdisParameterMultiString); if ((Status == NDIS_STATUS_SUCCESS) && (Param->ParameterType == NdisParameterMultiString)) { NDIS_STRING String; // // Param now contains a list of Multicast IP Address ranges. // Each string is of the form "M.M.M.M-N.N.N.N" // Read them in. // for (p = Param->ParameterData.StringData.Buffer, i = 0; *p != L'\0'; i++) { RtlInitUnicodeString(&String, p); DBGPRINT(DBG_LEVEL_INFO, ("Configured Multicast range for interface %Z - %Z\n", &pIntF->InterfaceName, &String)); p = (PWSTR)((PUCHAR)p + String.Length + sizeof(WCHAR)); } // // Allocate space for the addresses // pConfig->pMcsList = (PMCS_ENTRY)ALLOC_NP_MEM(sizeof(MCS_ENTRY) * i, POOL_TAG_MARS); if (pConfig->pMcsList == (PMCS_ENTRY)NULL) { LOG_ERROR(NDIS_STATUS_RESOURCES); } else { DBGPRINT(DBG_LEVEL_INFO, ("%d Multicast ranges configured on %Z\n", i, &pIntF->InterfaceName)); ZERO_MEM(pConfig->pMcsList, sizeof(MCS_ENTRY) * i); for (i = 0, p = Param->ParameterData.StringData.Buffer; *p != L'\0'; NOTHING) { RtlInitUnicodeString(&String, p); ArpSConvertStringToIpPair(&Status, &String, &pConfig->pMcsList[i]); if (Status == NDIS_STATUS_SUCCESS) { if (i > 0) { pConfig->pMcsList[i-1].Next = &(pConfig->pMcsList[i]); } i++; } else { DBGPRINT(DBG_LEVEL_ERROR, ("ArpSReadAdapterConfiguration: Failed to convert IP Range %Z\n", &String)); } p = (PWSTR)((PUCHAR)p + String.Length + sizeof(WCHAR)); } } } // // Close the configuration handle // NdisCloseConfiguration(ConfigHandle); Status = NDIS_STATUS_SUCCESS; } return Status; } NDIS_STATUS ArpSReadAdapterConfiguration( IN PINTF pIntF ) /*++ Routine Description: Read the registry for parameters for the specified Interface. This could be in response to a reconfiguration event, in which case handle existing values/structures. Arguments: pIntF - Interface to be read in. Return Value: Error code from registry apis. --*/ { NDIS_STATUS Status; ATMARPS_CONFIG AtmArpSConfig; KIRQL OldIrql; ULONG PrevNumAllocedRegdAddresses; PATM_ADDRESS PrevRegAddresses; PMCS_ENTRY PrevMcsList; Status = ArpSReadAdapterConfigFromRegistry(pIntF, &AtmArpSConfig); if (Status == NDIS_STATUS_SUCCESS) { // // Copy them into the interface structure. We could be handling a // parameter reconfig, so any space used to store old information. // ACQUIRE_SPIN_LOCK(&pIntF->Lock, &OldIrql); // // Selector Byte: // pIntF->SelByte = AtmArpSConfig.SelByte; // // List of addresses to be registered with the switch. // Take out the old list first. We'll have to delete those // addresses (deregister them from the switch). // PrevNumAllocedRegdAddresses = pIntF->NumAllocedRegdAddresses; PrevRegAddresses = pIntF->RegAddresses; // // Get the new list in: // pIntF->NumAllocedRegdAddresses = AtmArpSConfig.NumAllocedRegdAddresses; pIntF->RegAddresses = AtmArpSConfig.RegAddresses; pIntF->NumAddressesRegd = 0; // reset count of addresses regd with switch // // Take out the old MCS list and insert the new one. // PrevMcsList = pIntF->pMcsList; pIntF->pMcsList = AtmArpSConfig.pMcsList; RELEASE_SPIN_LOCK(&pIntF->Lock, OldIrql); // // Deregister all previously registered addresses with the switch. // if (PrevNumAllocedRegdAddresses) { ArpSDeleteIntFAddresses(pIntF, PrevNumAllocedRegdAddresses, PrevRegAddresses); // // Register the new list of addresses with the switch. // ArpSQueryAndSetAddresses(pIntF); } // // Free unused memory. // if (PrevMcsList) { FREE_MEM(PrevMcsList); } if (PrevRegAddresses) { FREE_MEM(PrevRegAddresses); } } return Status; } VOID ArpSConvertStringToIpPair( OUT PNDIS_STATUS pStatus, IN PNDIS_STRING pString, IN PMCS_ENTRY pMcsEntry ) /*++ Routine Description: Extract a pair of IP addresses that identify a range of multicast addresses that this MCS serves, from the given string. Arguments: pStatus - Place to return status pString - Points to string containing "-" pMcsEntry - Entry to read into. Return Value: None. *pStatus is set to indicate the status of this call. --*/ { PWSTR pMin, pMax; IPADDR Min, Max; ULONG Length; ULONG i; ARPS_PAGED_CODE(); Length = pString->Length; *pStatus = NDIS_STATUS_FAILURE; do { // // Locate the '-' and replace it with a NULL char. // pMin = pString->Buffer; pMax = pString->Buffer; for (i = 0; i < Length; i++, pMax++) { if (*pMax == L'-') { *pMax++ = L'\0'; break; } } if (i == Length) { break; // Didn't find '-' } if (IPConvertStringToAddress(pMin, &Min) && IPConvertStringToAddress(pMax, &Max)) { DBGPRINT(DBG_LEVEL_INFO, ("MCS pair: ")); ArpSDumpIpAddr(Min, " to "); ArpSDumpIpAddr(Max, "\n"); pMcsEntry->GrpAddrPair.MinAddr = Min; pMcsEntry->GrpAddrPair.MaxAddr = Max; *pStatus = NDIS_STATUS_SUCCESS; } break; } while (FALSE); } #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. Copied from tcpip\ip\ntip.c 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. --*/ { UNICODE_STRING unicodeString; STRING aString; UCHAR dataBuffer[IP_ADDRESS_STRING_LENGTH]; NTSTATUS status; PUCHAR addressPtr, cp, startPointer, endPointer; ULONG digit, multiplier; int i; ARPS_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); } VOID ArpSReadArpCache( IN PINTF pIntF ) /*++ Routine Description: Read the per-adapter Arp Cache. TBD. Arguments: pIntF - Per adapter arp cache. Return Value: None --*/ { HANDLE FileHandle; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatus; NTSTATUS Status; LARGE_INTEGER Offset; ULONG Space, NumEntries; PDISK_HEADER DskHdr; PUCHAR Buffer; PDISK_ENTRY pDskEntry; PARP_ENTRY ArpEntry; Buffer = ALLOC_PG_MEM(DISK_BUFFER_SIZE); if (Buffer == NULL) { LOG_ERROR(NDIS_STATUS_RESOURCES); return; } InitializeObjectAttributes(&ObjectAttributes, &pIntF->FileName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwCreateFile(&FileHandle, SYNCHRONIZE | FILE_READ_DATA, &ObjectAttributes, &IoStatus, NULL, 0, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_SEQUENTIAL_ONLY, NULL, 0); if (Status == STATUS_SUCCESS) { do { // // First read the disk header and validate it // Offset.QuadPart = 0; Status = ZwReadFile(FileHandle, NULL, NULL, NULL, &IoStatus, Buffer, DISK_BUFFER_SIZE, &Offset, NULL); if (Status != STATUS_SUCCESS) { LOG_ERROR(Status); break; } DskHdr = (PDISK_HEADER)Buffer; if ((IoStatus.Information < sizeof(DISK_HEADER)) || (DskHdr->Signature != DISK_HDR_SIGNATURE) || (DskHdr->Version != DISK_HDR_VERSION)) { LOG_ERROR(STATUS_INVALID_LEVEL); break; } NumEntries = DskHdr->NumberOfArpEntries; Space = (ULONG) IoStatus.Information - sizeof(DISK_HEADER); pDskEntry = (PDISK_ENTRY)(Buffer + sizeof(DISK_HEADER)); Offset.QuadPart = sizeof(DISK_HEADER); while (NumEntries > 0) { UINT Consumed; if ((Space < sizeof(DISK_ENTRY)) || (Space < (sizeof(DISK_ENTRY) + pDskEntry->AtmAddr.SubAddrLen))) { Status = ZwReadFile(FileHandle, NULL, NULL, NULL, &IoStatus, Buffer, DISK_BUFFER_SIZE, &Offset, NULL); if (Status != STATUS_SUCCESS) { LOG_ERROR(Status); break; } pDskEntry = (PDISK_ENTRY)Buffer; if ((IoStatus.Information < sizeof(DISK_ENTRY)) || (IoStatus.Information < (sizeof(DISK_ENTRY) + pDskEntry->AtmAddr.SubAddrLen))) { LOG_ERROR(STATUS_INVALID_LEVEL); break; } Space = (ULONG) IoStatus.Information - sizeof(DISK_HEADER); } ArpEntry = ArpSAddArpEntryFromDisk(pIntF, pDskEntry); ASSERT (ArpEntry != NULL); Consumed = (sizeof(DISK_ENTRY) + SIZE_4N(pDskEntry->AtmAddr.SubAddrLen)); (PUCHAR)pDskEntry += Consumed; Offset.QuadPart += Consumed; Space -= Consumed; NumEntries --; } } while (FALSE); ZwClose(FileHandle); } FREE_MEM(Buffer); } BOOLEAN ArpSWriteArpCache( IN PINTF pIntF, IN PTIMER Timer, IN BOOLEAN TimerShuttingDown ) /*++ Routine Description: Write the per-adapter Arp Cache. TBD. Arguments: pIntF - Per adapter arp cache. Timer - FlushTimer TimerShuttingDown - Do not requeue when set. Return Value: TRUE to requeue unless TimerShuttingDown is set --*/ { HANDLE FileHandle; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatus; NTSTATUS Status; LARGE_INTEGER Offset; ULONG Space, i; PDISK_HEADER DskHdr; PUCHAR Buffer; PDISK_ENTRY pDskEntry; PARP_ENTRY ArpEntry; TIME SystemTime, LocalTime; ULONG CurrentTime; Buffer = ALLOC_PG_MEM(DISK_BUFFER_SIZE); if (Buffer == NULL) { LOG_ERROR(NDIS_STATUS_RESOURCES); return (!TimerShuttingDown); } KeQuerySystemTime(&SystemTime); ExSystemTimeToLocalTime(&SystemTime, &LocalTime); // Convert this to number of seconds since 1980 if (!RtlTimeToSecondsSince1980(&LocalTime, &CurrentTime)) { // Could not convert! Bail out. LOG_ERROR(NDIS_STATUS_BUFFER_OVERFLOW); FREE_MEM(Buffer); return (!TimerShuttingDown); } InitializeObjectAttributes(&ObjectAttributes, &pIntF->FileName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwCreateFile(&FileHandle, SYNCHRONIZE | FILE_WRITE_DATA, &ObjectAttributes, &IoStatus, NULL, 0, 0, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_SEQUENTIAL_ONLY, NULL, 0); if (Status == STATUS_SUCCESS) { do { Offset.QuadPart = 0; Space = DISK_BUFFER_SIZE - sizeof(DISK_HEADER); DskHdr = (PDISK_HEADER)Buffer; pDskEntry = (PDISK_ENTRY)(Buffer + sizeof(DISK_HEADER)); DskHdr->Signature = DISK_HDR_SIGNATURE; DskHdr->Version = DISK_HDR_VERSION; DskHdr->NumberOfArpEntries = pIntF->NumCacheEntries; DskHdr->TimeStamp = CurrentTime; for (i =0; i < ARP_TABLE_SIZE; i++) { for (ArpEntry = pIntF->ArpCache[i]; ArpEntry != NULL; ArpEntry = ArpEntry->Next) { UINT Size; Size = sizeof(DISK_ENTRY) + ((ArpEntry->HwAddr.SubAddress != NULL) ? SIZE_4N(ArpEntry->HwAddr.SubAddress->NumberOfDigits) : 0); if (Space < Size) { Status = ZwWriteFile(FileHandle, NULL, NULL, NULL, &IoStatus, Buffer, DISK_BUFFER_SIZE - Space, &Offset, NULL); if (Status != STATUS_SUCCESS) { LOG_ERROR(Status); break; } Space = DISK_BUFFER_SIZE; pDskEntry = (PDISK_ENTRY)Buffer; Offset.QuadPart += (DISK_BUFFER_SIZE - Space); } pDskEntry->IpAddr = ArpEntry->IpAddr; pDskEntry->AtmAddr.AddrType = (UCHAR)ArpEntry->HwAddr.Address.AddressType; pDskEntry->AtmAddr.AddrLen = (UCHAR)ArpEntry->HwAddr.Address.NumberOfDigits; COPY_MEM(pDskEntry->AtmAddr.Address, ArpEntry->HwAddr.Address.Address, pDskEntry->AtmAddr.AddrLen); pDskEntry->AtmAddr.SubAddrLen = 0; if (ArpEntry->HwAddr.SubAddress != NULL) { pDskEntry->AtmAddr.SubAddrLen = (UCHAR)ArpEntry->HwAddr.SubAddress->NumberOfDigits; pDskEntry->AtmAddr.SubAddrType = (UCHAR)ArpEntry->HwAddr.SubAddress->AddressType; COPY_MEM((PUCHAR)pDskEntry + sizeof(DISK_ENTRY), ArpEntry->HwAddr.SubAddress->Address, pDskEntry->AtmAddr.SubAddrLen); } Space -= Size; (PUCHAR)pDskEntry += Size; } if (Status != STATUS_SUCCESS) { break; } } } while (FALSE); if ((Status == STATUS_SUCCESS) && (Space < DISK_BUFFER_SIZE)) { Status = ZwWriteFile(FileHandle, NULL, NULL, NULL, &IoStatus, Buffer, DISK_BUFFER_SIZE - Space, &Offset, NULL); } ZwClose(FileHandle); } FREE_MEM(Buffer); return (!TimerShuttingDown); }