/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "IpHelper.h" #include "Switch.h" #include "Jhash.h" extern POVS_SWITCH_CONTEXT gOvsSwitchContext; #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_IPHELPER #include "Debug.h" /* * IpHelper supports multiple internal adapters. */ KSTART_ROUTINE OvsStartIpHelper; /* Contains the entries of internal adapter objects. */ static LIST_ENTRY ovsInstanceList; /* Passive-level lock used to protect the internal adapter object list. */ static ERESOURCE ovsInstanceListLock; /* * FWD_ENTRY --------> IPFORWARD_ENTRY * | * |--------------------------------------> IPENIGH_ENTRY * * IPFORWARD_ENTRY ------> FWD_ENTRY LIST with same IPFORWARD * * IPNEIGH_ENTRY ------> FWD_ENTRY LIST with same IPNEIGH * */ static PLIST_ENTRY ovsFwdHashTable; // based on DST IP static PLIST_ENTRY ovsRouteHashTable; // based on DST PREFIX static PLIST_ENTRY ovsNeighHashTable; // based on DST IP static LIST_ENTRY ovsSortedIPNeighList; static UINT32 ovsNumFwdEntries; static PNDIS_RW_LOCK_EX ovsTableLock; static NDIS_SPIN_LOCK ovsIpHelperLock; static LIST_ENTRY ovsIpHelperRequestList; static UINT32 ovsNumIpHelperRequests; static HANDLE ipInterfaceNotificationHandle; static HANDLE ipRouteNotificationHandle; static HANDLE unicastIPNotificationHandle; static OVS_IP_HELPER_THREAD_CONTEXT ovsIpHelperThreadContext; static POVS_IPFORWARD_ENTRY OvsLookupIPForwardEntry(PIP_ADDRESS_PREFIX prefix); static VOID OvsRemoveIPForwardEntry(POVS_IPFORWARD_ENTRY ipf); static VOID OvsRemoveAllFwdEntriesWithSrc(UINT32 ipAddr); static VOID OvsRemoveIPNeighEntriesWithInstance(POVS_IPHELPER_INSTANCE instance); static VOID OvsCleanupIpHelperRequestList(VOID); static VOID OvsCleanupFwdTable(VOID); static VOID OvsAddToSortedNeighList(POVS_IPNEIGH_ENTRY ipn); static POVS_IPHELPER_INSTANCE OvsIpHelperAllocateInstance( POVS_IP_HELPER_REQUEST request); static VOID OvsIpHelperDeleteInstance(POVS_IPHELPER_INSTANCE instance); static VOID OvsDumpMessageWithGuid(char* message, GUID guid) { OVS_LOG_INFO(message, guid.Data1, guid.Data2, guid.Data3, *(UINT16 *)guid.Data4, guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); } static VOID OvsDumpIfRow(PMIB_IF_ROW2 ifRow) { OVS_LOG_INFO("InterfaceLuid: NetLuidIndex: %d, type: %d", ifRow->InterfaceLuid.Info.NetLuidIndex, ifRow->InterfaceLuid.Info.IfType); OVS_LOG_INFO("InterfaceIndex: %d", ifRow->InterfaceIndex); OvsDumpMessageWithGuid("Interface GUID: " "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", ifRow->InterfaceGuid); OVS_LOG_INFO("Perm MAC Address: %02x:%02x:%02x:%02x:%02x:%02x", ifRow->PermanentPhysicalAddress[0], ifRow->PermanentPhysicalAddress[1], ifRow->PermanentPhysicalAddress[2], ifRow->PermanentPhysicalAddress[3], ifRow->PermanentPhysicalAddress[4], ifRow->PermanentPhysicalAddress[5]); } static VOID OvsDumpIfTable(PMIB_IF_TABLE2 ifTable) { PMIB_IF_ROW2 ifRow; UINT32 i; OVS_LOG_INFO("======Number of entries: %d========", ifTable->NumEntries); for (i = 0; i < ifTable->NumEntries; i++) { ifRow = &ifTable->Table[i]; OvsDumpIfRow(ifRow); } } NTSTATUS OvsGetIfEntry(GUID *interfaceGuid, PMIB_IF_ROW2 ifEntry) { NTSTATUS status; PMIB_IF_TABLE2 ifTable; UINT32 i; if (interfaceGuid == NULL || ifEntry == NULL) { return STATUS_INVALID_PARAMETER; } status = GetIfTable2Ex(MibIfTableNormal, &ifTable); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fail to get if table, status: %x", status); return status; } status = STATUS_NOT_FOUND; for (i = 0; i < ifTable->NumEntries; i++) { PMIB_IF_ROW2 ifRow; ifRow = &ifTable->Table[i]; if (!memcmp(interfaceGuid, &ifRow->InterfaceGuid, sizeof (GUID))) { RtlCopyMemory(ifEntry, ifRow, sizeof (MIB_IF_ROW2)); status = STATUS_SUCCESS; OvsDumpIfRow(ifEntry); break; } } FreeMibTable(ifTable); return status; } static VOID OvsDumpIPInterfaceEntry(PMIB_IPINTERFACE_ROW ipRow) { OVS_LOG_INFO("InterfaceLuid: NetLuidIndex: %d, type: %d", ipRow->InterfaceLuid.Info.NetLuidIndex, ipRow->InterfaceLuid.Info.IfType); OVS_LOG_INFO("InterfaceIndex: %d", ipRow->InterfaceIndex); OVS_LOG_INFO("MaxReassembleSize: %u", ipRow->MaxReassemblySize); } NTSTATUS OvsGetIPInterfaceEntry(NET_LUID luid, PMIB_IPINTERFACE_ROW ipRow) { NTSTATUS status; if (ipRow == NULL) { return STATUS_INVALID_PARAMETER; } ipRow->Family = AF_INET; ipRow->InterfaceLuid.Value = luid.Value; status = GetIpInterfaceEntry(ipRow); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fail to get internal IP Interface mib row, status: %x", status); return status; } OvsDumpIPInterfaceEntry(ipRow); return status; } static VOID OvsDumpIPEntry(PMIB_UNICASTIPADDRESS_ROW ipRow) { UINT32 ipAddr; OVS_LOG_INFO("InterfaceLuid: NetLuidIndex: %d, type: %d", ipRow->InterfaceLuid.Info.NetLuidIndex, ipRow->InterfaceLuid.Info.IfType); OVS_LOG_INFO("InterfaceIndex: %d", ipRow->InterfaceIndex); ASSERT(ipRow->Address.si_family == AF_INET); ipAddr = ipRow->Address.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Unicast Address: %d.%d.%d.%d\n", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, ipAddr >> 24); } NTSTATUS OvsGetIPEntry(NET_LUID interfaceLuid, PMIB_UNICASTIPADDRESS_ROW ipEntry) { PMIB_UNICASTIPADDRESS_TABLE ipTable; NTSTATUS status; UINT32 i; if (ipEntry == NULL) { return STATUS_INVALID_PARAMETER; } status = GetUnicastIpAddressTable(AF_INET, &ipTable); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fail to get unicast address table, status: %x", status); return status; } status = STATUS_NOT_FOUND; for (i = 0; i < ipTable->NumEntries; i++) { PMIB_UNICASTIPADDRESS_ROW ipRow; ipRow = &ipTable->Table[i]; if (ipRow->InterfaceLuid.Value == interfaceLuid.Value) { RtlCopyMemory(ipEntry, ipRow, sizeof (*ipRow)); OvsDumpIPEntry(ipEntry); status = STATUS_SUCCESS; break; } } FreeMibTable(ipTable); return status; } #ifdef OVS_ENABLE_IPPATH static VOID OvsDumpIPPath(PMIB_IPPATH_ROW ipPath) { UINT32 ipAddr = ipPath->Source.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Source: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); ipAddr = ipPath->Destination.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Destination: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); ipAddr = ipPath->CurrentNextHop.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("NextHop: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); } NTSTATUS OvsGetIPPathEntry(PMIB_IPPATH_ROW ipPath) { NTSTATUS status; UINT32 ipAddr = ipPath->Destination.Ipv4.sin_addr.s_addr; status = GetIpPathEntry(ipPath); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fail to get IP path to %d.%d.%d.%d, status:%x", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status); return status; } OvsDumpIPPath(ipPath); return status; } #endif static VOID OvsDumpRoute(const SOCKADDR_INET *sourceAddress, const SOCKADDR_INET *destinationAddress, PMIB_IPFORWARD_ROW2 route) { UINT32 ipAddr = destinationAddress->Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Destination: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); ipAddr = sourceAddress->Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Source: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); ipAddr = route->NextHop.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("NextHop: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); } NTSTATUS OvsGetRoute(SOCKADDR_INET *destinationAddress, PMIB_IPFORWARD_ROW2 route, SOCKADDR_INET *sourceAddress, POVS_IPHELPER_INSTANCE *instance, POVS_VPORT_ENTRY* vport, UINT32 srcIp) { NTSTATUS status = STATUS_NETWORK_UNREACHABLE; NTSTATUS result = STATUS_SUCCESS; PLIST_ENTRY head, link, next; ULONG minMetric = MAXULONG; if (destinationAddress == NULL || route == NULL) { return STATUS_INVALID_PARAMETER; } ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE); head = &(ovsInstanceList); LIST_FORALL_SAFE(head, link, next) { SOCKADDR_INET crtSrcAddr = { 0 }; MIB_IPFORWARD_ROW2 crtRoute = { 0 }; POVS_IPHELPER_INSTANCE crtInstance = NULL; WCHAR interfaceName[IF_MAX_STRING_SIZE + 1]; crtInstance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, link); ExAcquireResourceExclusiveLite(&crtInstance->lock, TRUE); result = GetBestRoute2(&crtInstance->internalRow.InterfaceLuid, 0, NULL, destinationAddress, 0, &crtRoute, &crtSrcAddr); if (result != STATUS_SUCCESS) { ExReleaseResourceLite(&crtInstance->lock); continue; } if (minMetric > crtRoute.Metric && (!srcIp || srcIp == crtSrcAddr.Ipv4.sin_addr.S_un.S_addr)) { status = STATUS_SUCCESS; size_t len = 0; minMetric = crtRoute.Metric; LOCK_STATE_EX lockState; RtlCopyMemory(sourceAddress, &crtSrcAddr, sizeof(*sourceAddress)); RtlCopyMemory(route, &crtRoute, sizeof(*route)); *instance = crtInstance; status = ConvertInterfaceLuidToAlias(&crtInstance->internalRow.InterfaceLuid, interfaceName, IF_MAX_STRING_SIZE + 1); if (NT_SUCCESS(status)) { status = RtlStringCbLengthW(interfaceName, IF_MAX_STRING_SIZE, &len); } if (gOvsSwitchContext != NULL && NT_SUCCESS(status)) { NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0); *vport = OvsFindVportByHvNameW(gOvsSwitchContext, interfaceName, len); NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); } } ExReleaseResourceLite(&crtInstance->lock); } ExReleaseResourceLite(&ovsInstanceListLock); OvsDumpRoute(sourceAddress, destinationAddress, route); return status; } static VOID OvsDumpIPNeigh(PMIB_IPNET_ROW2 ipNeigh) { UINT32 ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Neigh: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); OVS_LOG_INFO("MAC Address: %02x:%02x:%02x:%02x:%02x:%02x", ipNeigh->PhysicalAddress[0], ipNeigh->PhysicalAddress[1], ipNeigh->PhysicalAddress[2], ipNeigh->PhysicalAddress[3], ipNeigh->PhysicalAddress[4], ipNeigh->PhysicalAddress[5]); } NTSTATUS OvsGetIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh) { NTSTATUS status; ASSERT(ipNeigh); status = GetIpNetEntry2(ipNeigh); if (status != STATUS_SUCCESS) { UINT32 ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Fail to get ARP entry: %d.%d.%d.%d, status: %x", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status); return status; } if (ipNeigh->State == NlnsReachable || ipNeigh->State == NlnsPermanent) { OvsDumpIPNeigh(ipNeigh); return STATUS_SUCCESS; } return STATUS_FWP_TCPIP_NOT_READY; } NTSTATUS OvsResolveIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh) { NTSTATUS status; ASSERT(ipNeigh); status = ResolveIpNetEntry2(ipNeigh, NULL); if (status != STATUS_SUCCESS) { UINT32 ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Fail to resolve ARP entry: %d.%d.%d.%d, status: %x", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status); return status; } if (ipNeigh->State == NlnsReachable || ipNeigh->State == NlnsPermanent) { OvsDumpIPNeigh(ipNeigh); return STATUS_SUCCESS; } return STATUS_FWP_TCPIP_NOT_READY; } NTSTATUS OvsGetOrResolveIPNeigh(PMIB_IF_ROW2 ipRow, UINT32 ipAddr, PMIB_IPNET_ROW2 ipNeigh) { NTSTATUS status; ASSERT(ipNeigh); RtlZeroMemory(ipNeigh, sizeof (*ipNeigh)); ipNeigh->InterfaceLuid.Value = ipRow->InterfaceLuid.Value; ipNeigh->InterfaceIndex = ipRow->InterfaceIndex; ipNeigh->Address.si_family = AF_INET; ipNeigh->Address.Ipv4.sin_addr.s_addr = ipAddr; status = OvsGetIPNeighEntry(ipNeigh); if (status != STATUS_SUCCESS) { RtlZeroMemory(ipNeigh, sizeof (*ipNeigh)); ipNeigh->InterfaceLuid.Value = ipRow->InterfaceLuid.Value; ipNeigh->InterfaceIndex = ipRow->InterfaceIndex; ipNeigh->Address.si_family = AF_INET; ipNeigh->Address.Ipv4.sin_addr.s_addr = ipAddr; status = OvsResolveIPNeighEntry(ipNeigh); } return status; } static __inline BOOLEAN OvsCheckInstanceRow(PMIB_IF_ROW2 instanceRow, PNET_LUID netLuid, NET_IFINDEX ifIndex) { return (instanceRow->InterfaceLuid.Info.NetLuidIndex == netLuid->Info.NetLuidIndex && instanceRow->InterfaceLuid.Info.IfType == netLuid->Info.IfType && instanceRow->InterfaceIndex == ifIndex); } static VOID OvsUpdateIpInterfaceNotification(PMIB_IPINTERFACE_ROW ipRow) { PLIST_ENTRY head, link, next; ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE); head = &(ovsInstanceList); LIST_FORALL_SAFE(head, link, next) { POVS_IPHELPER_INSTANCE instance = NULL; instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, link); ExAcquireResourceExclusiveLite(&instance->lock, TRUE); if (OvsCheckInstanceRow(&instance->internalRow, &ipRow->InterfaceLuid, ipRow->InterfaceIndex)) { /* * Update the IP Interface Row */ RtlCopyMemory(&instance->internalIPRow, ipRow, sizeof(PMIB_IPINTERFACE_ROW)); instance->isIpConfigured = TRUE; OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d is %s", ipRow->InterfaceLuid.Info.NetLuidIndex, ipRow->InterfaceLuid.Info.IfType, "modified"); ExReleaseResourceLite(&instance->lock); break; } ExReleaseResourceLite(&instance->lock); } ExReleaseResourceLite(&ovsInstanceListLock); } static VOID OvsAddIpInterfaceNotification(PMIB_IPINTERFACE_ROW ipRow) { PLIST_ENTRY head, link, next; BOOLEAN found = FALSE; ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE); head = &(ovsInstanceList); LIST_FORALL_SAFE(head, link, next) { POVS_IPHELPER_INSTANCE instance = NULL; instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, link); ExAcquireResourceExclusiveLite(&instance->lock, TRUE); if (OvsCheckInstanceRow(&instance->internalRow, &ipRow->InterfaceLuid, ipRow->InterfaceIndex)) { instance->isIpConfigured = FALSE; ExReleaseResourceLite(&instance->lock); found = TRUE; break; } ExReleaseResourceLite(&instance->lock); } ExReleaseResourceLite(&ovsInstanceListLock); if (found != TRUE) { NTSTATUS status; POVS_IPHELPER_INSTANCE instance = NULL; MIB_UNICASTIPADDRESS_ROW ipEntry; BOOLEAN error = TRUE; LOCK_STATE_EX lockState; instance = (POVS_IPHELPER_INSTANCE)OvsAllocateMemoryWithTag( sizeof(*instance), OVS_IPHELPER_POOL_TAG); if (instance == NULL) { goto error; } RtlZeroMemory(instance, sizeof(*instance)); InitializeListHead(&instance->link); ExInitializeResourceLite(&instance->lock); WCHAR interfaceName[IF_MAX_STRING_SIZE + 1]; status = ConvertInterfaceLuidToAlias(&ipRow->InterfaceLuid, interfaceName, IF_MAX_STRING_SIZE + 1); if (gOvsSwitchContext == NULL || !NT_SUCCESS(status)) { goto error; } NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0); POVS_VPORT_ENTRY vport = OvsFindVportByHvNameW(gOvsSwitchContext, interfaceName, sizeof(WCHAR) * wcslen(interfaceName)); if (vport != NULL) { RtlCopyMemory(&instance->netCfgId, &vport->netCfgInstanceId, sizeof(instance->netCfgId)); instance->portNo = vport->portNo; } NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); RtlZeroMemory(&instance->internalRow, sizeof(MIB_IF_ROW2)); RtlZeroMemory(&instance->internalIPRow, sizeof(MIB_IPINTERFACE_ROW)); status = OvsGetIfEntry(&instance->netCfgId, &instance->internalRow); if (status != STATUS_SUCCESS) { OvsDumpMessageWithGuid("Fail to get IF entry for internal port with GUID" " %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", instance->netCfgId); goto error; } status = OvsGetIPInterfaceEntry(instance->internalRow.InterfaceLuid, &instance->internalIPRow); if (status == STATUS_SUCCESS) { instance->isIpConfigured = TRUE; } else { goto error; } status = OvsGetIPEntry(instance->internalRow.InterfaceLuid, &ipEntry); if (status != STATUS_SUCCESS) { OvsDumpMessageWithGuid("Failed to get IP entry for internal port with GUID" " %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", instance->netCfgId); } ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE); InsertHeadList(&ovsInstanceList, &instance->link); ExReleaseResourceLite(&ovsInstanceListLock); error = FALSE; error: if (error) { OvsIpHelperDeleteInstance(instance); } } } static VOID OvsRemoveIpInterfaceNotification(PMIB_IPINTERFACE_ROW ipRow) { PLIST_ENTRY head, link, next; ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE); head = &(ovsInstanceList); LIST_FORALL_SAFE(head, link, next) { POVS_IPHELPER_INSTANCE instance = NULL; instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, link); ExAcquireResourceExclusiveLite(&instance->lock, TRUE); if (OvsCheckInstanceRow(&instance->internalRow, &ipRow->InterfaceLuid, ipRow->InterfaceIndex)) { instance->isIpConfigured = FALSE; RemoveEntryList(&instance->link); ExReleaseResourceLite(&instance->lock); OvsIpHelperDeleteInstance(instance); OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d is "\ "deleted", ipRow->InterfaceLuid.Info.NetLuidIndex, ipRow->InterfaceLuid.Info.IfType); break; } ExReleaseResourceLite(&instance->lock); } ExReleaseResourceLite(&ovsInstanceListLock); if (IsListEmpty(&ovsInstanceList)) { OvsCleanupIpHelperRequestList(); OvsCleanupFwdTable(); } } static VOID OvsChangeCallbackIpInterface(PVOID context, PMIB_IPINTERFACE_ROW ipRow, MIB_NOTIFICATION_TYPE notificationType) { UNREFERENCED_PARAMETER(context); switch (notificationType) { case MibParameterNotification: OvsUpdateIpInterfaceNotification(ipRow); break; case MibAddInstance: OvsAddIpInterfaceNotification(ipRow); break; case MibDeleteInstance: OvsRemoveIpInterfaceNotification(ipRow); break; case MibInitialNotification: OVS_LOG_INFO("Got Initial notification for IP Interface change."); default: return; } } static VOID OvsChangeCallbackIpRoute(PVOID context, PMIB_IPFORWARD_ROW2 ipRoute, MIB_NOTIFICATION_TYPE notificationType) { UINT32 ipAddr, nextHop; UNREFERENCED_PARAMETER(context); switch (notificationType) { case MibAddInstance: ASSERT(ipRoute); ipAddr = ipRoute->DestinationPrefix.Prefix.Ipv4.sin_addr.s_addr; nextHop = ipRoute->NextHop.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("IPRoute: To %d.%d.%d.%d/%d through %d.%d.%d.%d added", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, ipRoute->DestinationPrefix.PrefixLength, nextHop & 0xff, (nextHop >> 8) & 0xff, (nextHop >> 16) & 0xff, (nextHop >> 24) & 0xff); break; case MibParameterNotification: case MibDeleteInstance: { ASSERT(ipRoute); ipAddr = ipRoute->DestinationPrefix.Prefix.Ipv4.sin_addr.s_addr; nextHop = ipRoute->NextHop.Ipv4.sin_addr.s_addr; POVS_IPFORWARD_ENTRY ipf; LOCK_STATE_EX lockState; NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); ipf = OvsLookupIPForwardEntry(&ipRoute->DestinationPrefix); if (ipf != NULL) { OvsRemoveIPForwardEntry(ipf); } NdisReleaseRWLock(ovsTableLock, &lockState); OVS_LOG_INFO("IPRoute: To %d.%d.%d.%d/%d through %d.%d.%d.%d %s.", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, ipRoute->DestinationPrefix.PrefixLength, nextHop & 0xff, (nextHop >> 8) & 0xff, (nextHop >> 16) & 0xff, (nextHop >> 24) & 0xff, notificationType == MibDeleteInstance ? "deleted" : "modified"); break; } case MibInitialNotification: OVS_LOG_INFO("Get Initial notification for IP Route change."); default: return; } } static VOID OvsChangeCallbackUnicastIpAddress(PVOID context, PMIB_UNICASTIPADDRESS_ROW unicastRow, MIB_NOTIFICATION_TYPE notificationType) { UINT32 ipAddr; UNREFERENCED_PARAMETER(context); switch (notificationType) { case MibParameterNotification: case MibAddInstance: { PLIST_ENTRY head, link, next; ASSERT(unicastRow); ipAddr = unicastRow->Address.Ipv4.sin_addr.s_addr; ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE); head = &(ovsInstanceList); LIST_FORALL_SAFE(head, link, next) { POVS_IPHELPER_INSTANCE instance = NULL; instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, link); ExAcquireResourceExclusiveLite(&instance->lock, TRUE); if (instance->isIpConfigured && OvsCheckInstanceRow(&instance->internalRow, &unicastRow->InterfaceLuid, unicastRow->InterfaceIndex)) { instance->ipAddress = ipAddr; OVS_LOG_INFO("IP Address: %d.%d.%d.%d is %s", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, notificationType == MibAddInstance ? "added": "modified"); ExReleaseResourceLite(&instance->lock); break; } ExReleaseResourceLite(&instance->lock); } ExReleaseResourceLite(&ovsInstanceListLock); break; } case MibDeleteInstance: { PLIST_ENTRY head, link, next; LOCK_STATE_EX lockState; BOOLEAN found = FALSE; ASSERT(unicastRow); ipAddr = unicastRow->Address.Ipv4.sin_addr.s_addr; ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE); head = &(ovsInstanceList); LIST_FORALL_SAFE(head, link, next) { POVS_IPHELPER_INSTANCE instance = NULL; instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, link); ExAcquireResourceExclusiveLite(&instance->lock, TRUE); if (instance->isIpConfigured && OvsCheckInstanceRow(&instance->internalRow, &unicastRow->InterfaceLuid, unicastRow->InterfaceIndex)) { found = TRUE; ExReleaseResourceLite(&instance->lock); break; } ExReleaseResourceLite(&instance->lock); } ExReleaseResourceLite(&ovsInstanceListLock); if (found) { NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); OvsRemoveAllFwdEntriesWithSrc(ipAddr); NdisReleaseRWLock(ovsTableLock, &lockState); OVS_LOG_INFO("IP Address removed: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); } break; } case MibInitialNotification: OVS_LOG_INFO("Get Initial notification for Unicast IP Address change."); default: return; } } static VOID OvsCancelChangeNotification() { if (ipInterfaceNotificationHandle != NULL) { CancelMibChangeNotify2(ipInterfaceNotificationHandle); ipInterfaceNotificationHandle = NULL; } if (ipRouteNotificationHandle != NULL) { CancelMibChangeNotify2(ipRouteNotificationHandle); ipRouteNotificationHandle = NULL; } if (unicastIPNotificationHandle != NULL) { CancelMibChangeNotify2(unicastIPNotificationHandle); unicastIPNotificationHandle = NULL; } } static NTSTATUS OvsRegisterChangeNotification() { NTSTATUS status; UINT dummy = 0; status = NotifyIpInterfaceChange(AF_INET, OvsChangeCallbackIpInterface, NULL, TRUE, &ipInterfaceNotificationHandle); if (status != STATUS_SUCCESS) { OVS_LOG_ERROR("Fail to register Notify IP interface change, status:%x.", status); return status; } /* The CallerContext is dummy and should never be used */ status = NotifyRouteChange2(AF_INET, OvsChangeCallbackIpRoute, &dummy, TRUE, &ipRouteNotificationHandle); if (status != STATUS_SUCCESS) { OVS_LOG_ERROR("Failed to register IP route change, status: %x.", status); goto register_cleanup; } status = NotifyUnicastIpAddressChange(AF_INET, OvsChangeCallbackUnicastIpAddress, NULL, TRUE, &unicastIPNotificationHandle); if (status != STATUS_SUCCESS) { OVS_LOG_ERROR("Failed to register UNICAST IP change, status: %x.", status); } register_cleanup: if (status != STATUS_SUCCESS) { OvsCancelChangeNotification(); } return status; } static POVS_IPNEIGH_ENTRY OvsLookupIPNeighEntry(UINT32 ipAddr) { PLIST_ENTRY link; UINT32 hash = OvsJhashWords(&ipAddr, 1, OVS_HASH_BASIS); LIST_FORALL(&ovsNeighHashTable[hash & OVS_NEIGH_HASH_TABLE_MASK], link) { POVS_IPNEIGH_ENTRY entry; entry = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, link); if (entry->ipAddr == ipAddr) { return entry; } } return NULL; } static UINT32 OvsHashIPPrefix(PIP_ADDRESS_PREFIX prefix) { UINT64 words = (UINT64)prefix->Prefix.Ipv4.sin_addr.s_addr << 32 | (UINT32)prefix->PrefixLength; return OvsJhashWords((UINT32 *)&words, 2, OVS_HASH_BASIS); } static POVS_IPFORWARD_ENTRY OvsLookupIPForwardEntry(PIP_ADDRESS_PREFIX prefix) { PLIST_ENTRY link; UINT32 hash; ASSERT(prefix->Prefix.si_family == AF_INET); hash = RtlUlongByteSwap(prefix->Prefix.Ipv4.sin_addr.s_addr); ASSERT(prefix->PrefixLength >= 32 || (hash & (((UINT32)1 << (32 - prefix->PrefixLength)) - 1)) == 0); hash = OvsHashIPPrefix(prefix); LIST_FORALL(&ovsRouteHashTable[hash & OVS_ROUTE_HASH_TABLE_MASK], link) { POVS_IPFORWARD_ENTRY ipfEntry; ipfEntry = CONTAINING_RECORD(link, OVS_IPFORWARD_ENTRY, link); if (ipfEntry->prefix.PrefixLength == prefix->PrefixLength && ipfEntry->prefix.Prefix.Ipv4.sin_addr.s_addr == prefix->Prefix.Ipv4.sin_addr.s_addr) { return ipfEntry; } } return NULL; } static POVS_FWD_ENTRY OvsLookupIPFwdEntry(UINT32 srcIp, UINT32 dstIp) { PLIST_ENTRY link; UINT32 hash = OvsJhashWords(&dstIp, 1, OVS_HASH_BASIS); LIST_FORALL(&ovsFwdHashTable[hash & OVS_FWD_HASH_TABLE_MASK], link) { POVS_FWD_ENTRY entry; entry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, link); if (entry->info.dstIpAddr == dstIp && (!srcIp || entry->info.srcIpAddr == srcIp)) { return entry; } } return NULL; } NTSTATUS OvsLookupIPFwdInfo(UINT32 srcIp, UINT32 dstIp, POVS_FWD_INFO info) { POVS_FWD_ENTRY entry; LOCK_STATE_EX lockState; NTSTATUS status = STATUS_NOT_FOUND; NdisAcquireRWLockRead(ovsTableLock, &lockState, 0); entry = OvsLookupIPFwdEntry(srcIp, dstIp); if (entry) { RtlCopyMemory(info->value, entry->info.value, sizeof entry->info.value); status = STATUS_SUCCESS; } NdisReleaseRWLock(ovsTableLock, &lockState); return status; } static POVS_IPNEIGH_ENTRY OvsCreateIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh, POVS_IPHELPER_INSTANCE instance) { POVS_IPNEIGH_ENTRY entry; UINT64 timeVal; ASSERT(ipNeigh != NULL); entry = (POVS_IPNEIGH_ENTRY)OvsAllocateMemoryWithTag( sizeof(OVS_IPNEIGH_ENTRY), OVS_IPHELPER_POOL_TAG); if (entry == NULL) { return NULL; } RtlZeroMemory(entry, sizeof (OVS_IPNEIGH_ENTRY)); entry->ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr; KeQuerySystemTime((LARGE_INTEGER *)&timeVal); entry->timeout = timeVal + OVS_IPNEIGH_TIMEOUT; RtlCopyMemory(entry->macAddr, ipNeigh->PhysicalAddress, ETH_ADDR_LEN); InitializeListHead(&entry->fwdList); entry->instance = instance; return entry; } static POVS_IPFORWARD_ENTRY OvsCreateIPForwardEntry(PMIB_IPFORWARD_ROW2 ipRoute) { POVS_IPFORWARD_ENTRY entry; ASSERT(ipRoute); entry = (POVS_IPFORWARD_ENTRY)OvsAllocateMemoryWithTag( sizeof(OVS_IPFORWARD_ENTRY), OVS_IPHELPER_POOL_TAG); if (entry == NULL) { return NULL; } RtlZeroMemory(entry, sizeof (OVS_IPFORWARD_ENTRY)); RtlCopyMemory(&entry->prefix, &ipRoute->DestinationPrefix, sizeof (IP_ADDRESS_PREFIX)); entry->nextHop = ipRoute->NextHop.Ipv4.sin_addr.s_addr; InitializeListHead(&entry->fwdList); return entry; } static POVS_FWD_ENTRY OvsCreateFwdEntry(POVS_FWD_INFO fwdInfo) { POVS_FWD_ENTRY entry; entry = (POVS_FWD_ENTRY)OvsAllocateMemoryWithTag( sizeof(OVS_FWD_ENTRY), OVS_IPHELPER_POOL_TAG); if (entry == NULL) { return NULL; } RtlZeroMemory(entry, sizeof (OVS_FWD_ENTRY)); RtlCopyMemory(&entry->info, fwdInfo, sizeof (OVS_FWD_INFO)); return entry; } static VOID OvsRemoveFwdEntry(POVS_FWD_ENTRY fwdEntry) { POVS_IPFORWARD_ENTRY ipf; POVS_IPNEIGH_ENTRY ipn; ipf = fwdEntry->ipf; ipn = fwdEntry->ipn; RemoveEntryList(&fwdEntry->link); ovsNumFwdEntries--; RemoveEntryList(&fwdEntry->ipfLink); ipf->refCount--; RemoveEntryList(&fwdEntry->ipnLink); ipn->refCount--; if (ipf->refCount == 0) { ASSERT(IsListEmpty(&ipf->fwdList)); RemoveEntryList(&ipf->link); OvsFreeMemoryWithTag(ipf, OVS_IPHELPER_POOL_TAG); } if (ipn->refCount == 0) { ASSERT(IsListEmpty(&ipn->fwdList)); RemoveEntryList(&ipn->link); NdisAcquireSpinLock(&ovsIpHelperLock); RemoveEntryList(&ipn->slink); NdisReleaseSpinLock(&ovsIpHelperLock); OvsFreeMemoryWithTag(ipn, OVS_IPHELPER_POOL_TAG); } OvsFreeMemoryWithTag(fwdEntry, OVS_IPHELPER_POOL_TAG); } static VOID OvsRemoveIPForwardEntry(POVS_IPFORWARD_ENTRY ipf) { PLIST_ENTRY link, next; ipf->refCount++; LIST_FORALL_SAFE(&ipf->fwdList, link, next) { POVS_FWD_ENTRY fwdEntry; fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipfLink); OvsRemoveFwdEntry(fwdEntry); } ASSERT(ipf->refCount == 1); RemoveEntryList(&ipf->link); OvsFreeMemoryWithTag(ipf, OVS_IPHELPER_POOL_TAG); } static VOID OvsRemoveIPNeighEntry(POVS_IPNEIGH_ENTRY ipn) { PLIST_ENTRY link, next; ipn->refCount++; LIST_FORALL_SAFE(&ipn->fwdList, link, next) { POVS_FWD_ENTRY fwdEntry; fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipnLink); OvsRemoveFwdEntry(fwdEntry); } if (ipn->refCount == 1) { RemoveEntryList(&ipn->link); NdisAcquireSpinLock(&ovsIpHelperLock); RemoveEntryList(&ipn->slink); NdisReleaseSpinLock(&ovsIpHelperLock); OvsFreeMemoryWithTag(ipn, OVS_IPHELPER_POOL_TAG); } } static VOID OvsAddToSortedNeighList(POVS_IPNEIGH_ENTRY ipn) { PLIST_ENTRY link; if (!IsListEmpty(&ovsSortedIPNeighList)) { link = ovsSortedIPNeighList.Blink; POVS_IPNEIGH_ENTRY entry; entry = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink); if (entry->timeout > ipn->timeout) { ipn->timeout++; } } InsertTailList(&ovsSortedIPNeighList, &ipn->slink); } static VOID OvsAddIPFwdCache(POVS_FWD_ENTRY fwdEntry, POVS_IPFORWARD_ENTRY ipf, POVS_IPNEIGH_ENTRY ipn) { UINT32 hash; if (ipn->refCount == 0) { NdisAcquireSpinLock(&ovsIpHelperLock); OvsAddToSortedNeighList(ipn); NdisReleaseSpinLock(&ovsIpHelperLock); hash = OvsJhashWords(&ipn->ipAddr, 1, OVS_HASH_BASIS); InsertHeadList(&ovsNeighHashTable[hash & OVS_NEIGH_HASH_TABLE_MASK], &ipn->link); } if (ipf->refCount == 0) { hash = OvsHashIPPrefix(&ipf->prefix); InsertHeadList(&ovsRouteHashTable[hash & OVS_ROUTE_HASH_TABLE_MASK], &ipf->link); } InsertHeadList(&ipf->fwdList, &fwdEntry->ipfLink); ipf->refCount++; fwdEntry->ipf = ipf; InsertHeadList(&ipn->fwdList, &fwdEntry->ipnLink); ipn->refCount++; fwdEntry->ipn = ipn; hash = OvsJhashWords(&fwdEntry->info.dstIpAddr, 1, OVS_HASH_BASIS); InsertHeadList(&ovsFwdHashTable[hash & OVS_FWD_HASH_TABLE_MASK], &fwdEntry->link); ovsNumFwdEntries++; } static VOID OvsRemoveAllFwdEntriesWithSrc(UINT32 ipAddr) { UINT32 i; PLIST_ENTRY link, next; for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) { LIST_FORALL_SAFE(&ovsFwdHashTable[i], link, next) { POVS_FWD_ENTRY fwdEntry; fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, link); if (fwdEntry->info.srcIpAddr == ipAddr) { OvsRemoveFwdEntry(fwdEntry); } } } } static VOID OvsRemoveIPNeighEntriesWithInstance(POVS_IPHELPER_INSTANCE instance) { if (ovsNumFwdEntries) { POVS_IPNEIGH_ENTRY ipn; PLIST_ENTRY link, next; LIST_FORALL_SAFE(&ovsSortedIPNeighList, link, next) { ipn = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink); POVS_IPHELPER_INSTANCE ipnInstance = ipn->instance; if (ipnInstance == instance) { OvsRemoveIPNeighEntry(ipn); } } } } static VOID OvsCleanupFwdTable(VOID) { PLIST_ENTRY link, next; UINT32 i; LOCK_STATE_EX lockState; NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); if (ovsNumFwdEntries) { LIST_FORALL_SAFE(&ovsSortedIPNeighList, link, next) { POVS_IPNEIGH_ENTRY ipn; ipn = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink); OvsRemoveIPNeighEntry(ipn); } } for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) { ASSERT(IsListEmpty(&ovsFwdHashTable[i])); } for (i = 0; i < OVS_ROUTE_HASH_TABLE_SIZE; i++) { ASSERT(IsListEmpty(&ovsRouteHashTable[i])); } NdisReleaseRWLock(ovsTableLock, &lockState); } static VOID OvsCleanupIpHelperRequestList(VOID) { LIST_ENTRY list; PLIST_ENTRY next, link; NdisAcquireSpinLock(&ovsIpHelperLock); InitializeListHead(&list); OvsAppendList(&list, &ovsIpHelperRequestList); ovsNumIpHelperRequests = 0; NdisReleaseSpinLock(&ovsIpHelperLock); LIST_FORALL_SAFE(&list, link, next) { POVS_IP_HELPER_REQUEST request; request = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link); if (request->command == OVS_IP_HELPER_FWD_REQUEST && request->fwdReq.cb) { request->fwdReq.cb(request->fwdReq.nbl, request->fwdReq.inPort, &request->fwdReq.tunnelKey, request->fwdReq.cbData1, request->fwdReq.cbData2, STATUS_DEVICE_NOT_READY, NULL); } OvsFreeMemoryWithTag(request, OVS_IPHELPER_POOL_TAG); } } static VOID OvsWakeupIPHelper(VOID) { KeSetEvent(&ovsIpHelperThreadContext.event, 0, FALSE); } VOID OvsInternalAdapterDown(UINT32 portNo, GUID netCfgInstanceId) { POVS_IP_HELPER_REQUEST request; request = (POVS_IP_HELPER_REQUEST)OvsAllocateMemoryWithTag( sizeof(OVS_IP_HELPER_REQUEST), OVS_IPHELPER_POOL_TAG); if (request == NULL) { OVS_LOG_ERROR("Fail to initialize Internal Adapter"); return; } RtlZeroMemory(request, sizeof (OVS_IP_HELPER_REQUEST)); RtlCopyMemory(&request->instanceReq.netCfgInstanceId, &netCfgInstanceId, sizeof(netCfgInstanceId)); request->command = OVS_IP_HELPER_INTERNAL_ADAPTER_DOWN; request->instanceReq.portNo = portNo; NdisAcquireSpinLock(&ovsIpHelperLock); InsertHeadList(&ovsIpHelperRequestList, &request->link); ovsNumIpHelperRequests++; if (ovsNumIpHelperRequests == 1) { OvsWakeupIPHelper(); } NdisReleaseSpinLock(&ovsIpHelperLock); } VOID OvsInternalAdapterUp(UINT32 portNo, GUID *netCfgInstanceId) { POVS_IP_HELPER_REQUEST request; request = (POVS_IP_HELPER_REQUEST)OvsAllocateMemoryWithTag( sizeof(OVS_IP_HELPER_REQUEST), OVS_IPHELPER_POOL_TAG); if (request == NULL) { OVS_LOG_ERROR("Fail to initialize Internal Adapter"); return; } RtlZeroMemory(request, sizeof (OVS_IP_HELPER_REQUEST)); RtlCopyMemory(&request->instanceReq.netCfgInstanceId, netCfgInstanceId, sizeof(*netCfgInstanceId)); request->command = OVS_IP_HELPER_INTERNAL_ADAPTER_UP; request->instanceReq.portNo = portNo; NdisAcquireSpinLock(&ovsIpHelperLock); InsertHeadList(&ovsIpHelperRequestList, &request->link); ovsNumIpHelperRequests++; if (ovsNumIpHelperRequests == 1) { NdisReleaseSpinLock(&ovsIpHelperLock); OvsWakeupIPHelper(); } else { NdisReleaseSpinLock(&ovsIpHelperLock); } } static POVS_IPHELPER_INSTANCE OvsIpHelperAllocateInstance(POVS_IP_HELPER_REQUEST request) { POVS_IPHELPER_INSTANCE instance = NULL; instance = (POVS_IPHELPER_INSTANCE)OvsAllocateMemoryWithTag( sizeof(*instance), OVS_IPHELPER_POOL_TAG); if (instance) { RtlZeroMemory(instance, sizeof(*instance)); RtlCopyMemory(&instance->netCfgId, &request->instanceReq.netCfgInstanceId, sizeof(instance->netCfgId)); instance->portNo = request->instanceReq.portNo; InitializeListHead(&instance->link); ExInitializeResourceLite(&instance->lock); } return instance; } static VOID OvsIpHelperDeleteInstance(POVS_IPHELPER_INSTANCE instance) { if (instance) { ExDeleteResourceLite(&instance->lock); OvsFreeMemoryWithTag(instance, OVS_IPHELPER_POOL_TAG); } } static VOID OvsIpHelperDeleteAllInstances() { PLIST_ENTRY head, link, next; ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE); head = &ovsInstanceList; if (!IsListEmpty(head)) { LIST_FORALL_SAFE(head, link, next) { POVS_IPHELPER_INSTANCE instance = NULL; instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, link); ExAcquireResourceExclusiveLite(&instance->lock, TRUE); instance->isIpConfigured = FALSE; RemoveEntryList(&instance->link); ExReleaseResourceLite(&instance->lock); OvsIpHelperDeleteInstance(instance); } } ExReleaseResourceLite(&ovsInstanceListLock); } static VOID OvsHandleInternalAdapterUp(POVS_IP_HELPER_REQUEST request) { NTSTATUS status; POVS_IPHELPER_INSTANCE instance = NULL; MIB_UNICASTIPADDRESS_ROW ipEntry; BOOLEAN error = TRUE; do { instance = OvsIpHelperAllocateInstance(request); if (instance == NULL) { break; } RtlZeroMemory(&instance->internalRow, sizeof(MIB_IF_ROW2)); RtlZeroMemory(&instance->internalIPRow, sizeof(MIB_IPINTERFACE_ROW)); status = OvsGetIfEntry(&instance->netCfgId, &instance->internalRow); if (status != STATUS_SUCCESS) { OvsDumpMessageWithGuid("Fail to get IF entry for internal port with GUID" " %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", instance->netCfgId); break; } status = OvsGetIPInterfaceEntry(instance->internalRow.InterfaceLuid, &instance->internalIPRow); if (status == STATUS_SUCCESS) { instance->isIpConfigured = TRUE; } else { break; } status = OvsGetIPEntry(instance->internalRow.InterfaceLuid, &ipEntry); if (status != STATUS_SUCCESS) { OvsDumpMessageWithGuid("Fail to get IP entry for internal port with GUID" " %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", instance->netCfgId); } ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE); InsertHeadList(&ovsInstanceList, &instance->link); ExReleaseResourceLite(&ovsInstanceListLock); error = FALSE; } while (error); OvsFreeMemoryWithTag(request, OVS_IPHELPER_POOL_TAG); if (error) { OvsIpHelperDeleteInstance(instance); } } static NTSTATUS OvsEnqueueIpHelperRequest(POVS_IP_HELPER_REQUEST request) { if (IsListEmpty(&ovsInstanceList)) { OvsFreeMemoryWithTag(request, OVS_IPHELPER_POOL_TAG); return STATUS_NDIS_ADAPTER_NOT_READY; } else { NdisAcquireSpinLock(&ovsIpHelperLock); InsertHeadList(&ovsIpHelperRequestList, &request->link); ovsNumIpHelperRequests++; if (ovsNumIpHelperRequests == 1) { OvsWakeupIPHelper(); } NdisReleaseSpinLock(&ovsIpHelperLock); return STATUS_SUCCESS; } } NTSTATUS OvsFwdIPHelperRequest(PNET_BUFFER_LIST nbl, UINT32 inPort, const OvsIPv4TunnelKey *tunnelKey, OvsIPHelperCallback cb, PVOID cbData1, PVOID cbData2) { POVS_IP_HELPER_REQUEST request; request = (POVS_IP_HELPER_REQUEST)OvsAllocateMemoryWithTag( sizeof(OVS_IP_HELPER_REQUEST), OVS_IPHELPER_POOL_TAG); if (request == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } request->command = OVS_IP_HELPER_FWD_REQUEST; request->fwdReq.nbl = nbl; request->fwdReq.inPort = inPort; RtlCopyMemory(&request->fwdReq.tunnelKey, tunnelKey, sizeof (*tunnelKey)); request->fwdReq.cb = cb; request->fwdReq.cbData1 = cbData1; request->fwdReq.cbData2 = cbData2; return OvsEnqueueIpHelperRequest(request); } static VOID OvsHandleFwdRequest(POVS_IP_HELPER_REQUEST request) { SOCKADDR_INET dst, src; NTSTATUS status; MIB_IPFORWARD_ROW2 ipRoute; MIB_IPNET_ROW2 ipNeigh; OVS_FWD_INFO fwdInfo = { 0 }; UINT32 ipAddr; UINT32 srcAddr; POVS_FWD_ENTRY fwdEntry = NULL; POVS_IPFORWARD_ENTRY ipf = NULL; POVS_IPNEIGH_ENTRY ipn = NULL; LOCK_STATE_EX lockState; BOOLEAN newIPF = FALSE; BOOLEAN newIPN = FALSE; BOOLEAN newFWD = FALSE; POVS_IPHELPER_INSTANCE instance = NULL; status = OvsLookupIPFwdInfo(request->fwdReq.tunnelKey.src, request->fwdReq.tunnelKey.dst, &fwdInfo); if (status == STATUS_SUCCESS) { goto fwd_handle_nbl; } /* find IPRoute */ RtlZeroMemory(&dst, sizeof(dst)); RtlZeroMemory(&src, sizeof(src)); RtlZeroMemory(&ipRoute, sizeof (MIB_IPFORWARD_ROW2)); dst.si_family = AF_INET; dst.Ipv4.sin_addr.s_addr = request->fwdReq.tunnelKey.dst; status = OvsGetRoute(&dst, &ipRoute, &src, &instance, &fwdInfo.vport, request->fwdReq.tunnelKey.src); if (request->fwdReq.tunnelKey.src && request->fwdReq.tunnelKey.src != src.Ipv4.sin_addr.s_addr) { UINT32 tempAddr = dst.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Fail to get route to %d.%d.%d.%d, status: %x", tempAddr & 0xff, (tempAddr >> 8) & 0xff, (tempAddr >> 16) & 0xff, (tempAddr >> 24) & 0xff, status); goto fwd_handle_nbl; } if (status != STATUS_SUCCESS || instance == NULL) { UINT32 tempAddr = dst.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Fail to get route to %d.%d.%d.%d, status: %x", tempAddr & 0xff, (tempAddr >> 8) & 0xff, (tempAddr >> 16) & 0xff, (tempAddr >> 24) & 0xff, status); goto fwd_handle_nbl; } ExAcquireResourceExclusiveLite(&instance->lock, TRUE); srcAddr = src.Ipv4.sin_addr.s_addr; /* find IPNeigh */ ipAddr = ipRoute.NextHop.Ipv4.sin_addr.s_addr; if (ipAddr != 0) { NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); ipn = OvsLookupIPNeighEntry(ipAddr); if (ipn) { goto fwd_request_done; } NdisReleaseRWLock(ovsTableLock, &lockState); } RtlZeroMemory(&ipNeigh, sizeof (ipNeigh)); ipNeigh.InterfaceLuid.Value = instance->internalRow.InterfaceLuid.Value; if (ipAddr == 0) { ipAddr = request->fwdReq.tunnelKey.dst; } status = OvsGetOrResolveIPNeigh(&instance->internalRow, ipAddr, &ipNeigh); if (status != STATUS_SUCCESS) { ExReleaseResourceLite(&instance->lock); goto fwd_handle_nbl; } NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); fwd_request_done: /* * Initialize ipf */ ipf = OvsLookupIPForwardEntry(&ipRoute.DestinationPrefix); if (ipf == NULL) { ipf = OvsCreateIPForwardEntry(&ipRoute); if (ipf == NULL) { NdisReleaseRWLock(ovsTableLock, &lockState); ExReleaseResourceLite(&instance->lock); status = STATUS_INSUFFICIENT_RESOURCES; goto fwd_handle_nbl; } newIPF = TRUE; } else { PLIST_ENTRY link; link = ipf->fwdList.Flink; fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipfLink); if (fwdEntry->info.srcIpAddr != srcAddr) { OvsRemoveFwdEntry(fwdEntry); NdisReleaseRWLock(ovsTableLock, &lockState); ExReleaseResourceLite(&instance->lock); status = STATUS_INSUFFICIENT_RESOURCES; goto fwd_handle_nbl; } srcAddr = fwdEntry->info.srcIpAddr; } /* * initialize ipn */ if (ipn == NULL) { ipn = OvsLookupIPNeighEntry(ipAddr); if (ipn == NULL) { ipn = OvsCreateIPNeighEntry(&ipNeigh, instance); if (ipn == NULL) { NdisReleaseRWLock(ovsTableLock, &lockState); ExReleaseResourceLite(&instance->lock); status = STATUS_INSUFFICIENT_RESOURCES; goto fwd_handle_nbl; } newIPN = TRUE; } } /* * initialize fwdEntry */ fwdInfo.dstIpAddr = request->fwdReq.tunnelKey.dst; fwdInfo.srcIpAddr = srcAddr; RtlCopyMemory(fwdInfo.dstMacAddr, ipn->macAddr, ETH_ADDR_LEN); RtlCopyMemory(fwdInfo.srcMacAddr, instance->internalRow.PhysicalAddress, ETH_ADDR_LEN); fwdInfo.srcPortNo = request->fwdReq.inPort; fwdEntry = OvsCreateFwdEntry(&fwdInfo); if (fwdEntry == NULL) { NdisReleaseRWLock(ovsTableLock, &lockState); ExReleaseResourceLite(&instance->lock); status = STATUS_INSUFFICIENT_RESOURCES; goto fwd_handle_nbl; } newFWD = TRUE; if (status == STATUS_SUCCESS) { /* * Cache the result */ OvsAddIPFwdCache(fwdEntry, ipf, ipn); NdisReleaseRWLock(ovsTableLock, &lockState); ExReleaseResourceLite(&instance->lock); } fwd_handle_nbl: if (status != STATUS_SUCCESS) { if (newFWD) { ASSERT(fwdEntry != NULL); OvsFreeMemoryWithTag(fwdEntry, OVS_IPHELPER_POOL_TAG); } if (newIPF) { ASSERT(ipf && ipf->refCount == 0); OvsFreeMemoryWithTag(ipf, OVS_IPHELPER_POOL_TAG); } if (newIPN) { ASSERT(ipn && ipn->refCount == 0); OvsFreeMemoryWithTag(ipn, OVS_IPHELPER_POOL_TAG); } ipAddr = request->fwdReq.tunnelKey.dst; OVS_LOG_INFO("Fail to handle IP helper request for dst: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); } if (request->fwdReq.cb) { request->fwdReq.cb(request->fwdReq.nbl, request->fwdReq.inPort, &request->fwdReq.tunnelKey, request->fwdReq.cbData1, request->fwdReq.cbData2, status, status == STATUS_SUCCESS ? &fwdInfo : NULL); } OvsFreeMemoryWithTag(request, OVS_IPHELPER_POOL_TAG); } static VOID OvsUpdateIPNeighEntry(UINT32 ipAddr, PMIB_IPNET_ROW2 ipNeigh, NTSTATUS status) { UINT64 timeVal; POVS_IPNEIGH_ENTRY ipn; LOCK_STATE_EX lockState; KeQuerySystemTime((LARGE_INTEGER *)&timeVal); /* * if mac changed, update all relevant fwdEntry */ if (status != STATUS_SUCCESS) { NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); } else { NdisAcquireRWLockRead(ovsTableLock, &lockState, 0); } ipn = OvsLookupIPNeighEntry(ipAddr); if (ipn == NULL) { NdisReleaseRWLock(ovsTableLock, &lockState); return; } if (status != STATUS_SUCCESS) { OvsRemoveIPNeighEntry(ipn); NdisReleaseRWLock(ovsTableLock, &lockState); return; } if (memcmp((const PVOID)ipn->macAddr, (const PVOID)ipNeigh->PhysicalAddress, (size_t)ETH_ADDR_LEN)) { PLIST_ENTRY link; NdisReleaseRWLock(ovsTableLock, &lockState); /* * need update, release and acquire write lock * This is not the common case. */ NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); ipn = OvsLookupIPNeighEntry(ipAddr); if (ipn == NULL) { NdisReleaseRWLock(ovsTableLock, &lockState); return; } LIST_FORALL(&ipn->fwdList, link) { POVS_FWD_ENTRY fwdEntry; fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipnLink); RtlCopyMemory(fwdEntry->info.dstMacAddr, ipNeigh->PhysicalAddress, ETH_ADDR_LEN); } } /* * update timeout and move to the end of * the sorted list */ NdisAcquireSpinLock(&ovsIpHelperLock); RemoveEntryList(&ipn->slink); ipn->timeout = timeVal + OVS_IPNEIGH_TIMEOUT; OvsAddToSortedNeighList(ipn); NdisReleaseSpinLock(&ovsIpHelperLock); NdisReleaseRWLock(ovsTableLock, &lockState); } /* *---------------------------------------------------------------------------- * IP Helper system thread handles the following requests: * 1. Intialize Internal port row when internal port is connected * 2. Handle FWD request * 3. Handle IP Neigh timeout * * IP Interface, unicast address, and IP route change will be handled * by the revelant callbacks. *---------------------------------------------------------------------------- */ VOID OvsStartIpHelper(PVOID data) { POVS_IP_HELPER_THREAD_CONTEXT context = (POVS_IP_HELPER_THREAD_CONTEXT)data; POVS_IP_HELPER_REQUEST req; POVS_IPNEIGH_ENTRY ipn; PLIST_ENTRY link; UINT64 timeVal, timeout; PLARGE_INTEGER threadSleepTimeout; OVS_LOG_INFO("Start the IP Helper Thread, context: %p", context); NdisAcquireSpinLock(&ovsIpHelperLock); while (!context->exit) { threadSleepTimeout = NULL; timeout = 0; while (!IsListEmpty(&ovsIpHelperRequestList)) { if (context->exit) { goto ip_helper_wait; } link = ovsIpHelperRequestList.Flink; RemoveEntryList(link); ovsNumIpHelperRequests--; NdisReleaseSpinLock(&ovsIpHelperLock); req = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link); switch (req->command) { case OVS_IP_HELPER_INTERNAL_ADAPTER_UP: OvsHandleInternalAdapterUp(req); break; case OVS_IP_HELPER_INTERNAL_ADAPTER_DOWN: { PLIST_ENTRY head, current, next; UINT32 portNo = req->instanceReq.portNo; GUID netCfgInstanceId = req->instanceReq.netCfgInstanceId; ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE); head = &ovsInstanceList; LIST_FORALL_SAFE(head, current, next) { POVS_IPHELPER_INSTANCE instance = NULL; LOCK_STATE_EX lockState; instance = CONTAINING_RECORD(current, OVS_IPHELPER_INSTANCE, link); ExAcquireResourceExclusiveLite(&instance->lock, TRUE); if (instance->portNo == portNo && IsEqualGUID(&instance->netCfgId, &netCfgInstanceId)) { NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); OvsRemoveIPNeighEntriesWithInstance(instance); NdisReleaseRWLock(ovsTableLock, &lockState); RemoveEntryList(&instance->link); ExReleaseResourceLite(&instance->lock); OvsIpHelperDeleteInstance(instance); break; } ExReleaseResourceLite(&instance->lock); } if (IsListEmpty(&ovsInstanceList)) { OvsCleanupIpHelperRequestList(); OvsCleanupFwdTable(); } ExReleaseResourceLite(&ovsInstanceListLock); } case OVS_IP_HELPER_FWD_REQUEST: OvsHandleFwdRequest(req); break; default: OvsFreeMemoryWithTag(req, OVS_IPHELPER_POOL_TAG); } NdisAcquireSpinLock(&ovsIpHelperLock); } /* for now, let us hold the lock here, if this cause any issue * we will change to use IpHelper lock only to protect * IPN */ while (!IsListEmpty(&ovsSortedIPNeighList)) { UINT32 ipAddr; if (context->exit) { goto ip_helper_wait; } link = ovsSortedIPNeighList.Flink; ipn = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink); KeQuerySystemTime((LARGE_INTEGER *)&timeVal); if (ipn->timeout > timeVal) { timeout = ipn->timeout; threadSleepTimeout = (PLARGE_INTEGER)&timeout; break; } ipAddr = ipn->ipAddr; MIB_IPNET_ROW2 ipNeigh; NTSTATUS status; POVS_IPHELPER_INSTANCE instance = ipn->instance; NdisReleaseSpinLock(&ovsIpHelperLock); if (instance) { ExAcquireResourceExclusiveLite(&instance->lock, TRUE); status = OvsGetOrResolveIPNeigh(&instance->internalRow, ipAddr, &ipNeigh); OvsUpdateIPNeighEntry(ipAddr, &ipNeigh, status); ExReleaseResourceLite(&instance->lock); } NdisAcquireSpinLock(&ovsIpHelperLock); } if (!IsListEmpty(&ovsIpHelperRequestList)) { continue; } ip_helper_wait: if (context->exit) { break; } KeClearEvent(&context->event); NdisReleaseSpinLock(&ovsIpHelperLock); /* * Wait indefinitely for the thread to be woken up. * Passing NULL as the Timeout value in the below * call to KeWaitForSingleObject achieves this. */ KeWaitForSingleObject(&context->event, Executive, KernelMode, FALSE, threadSleepTimeout); NdisAcquireSpinLock(&ovsIpHelperLock); } NdisReleaseSpinLock(&ovsIpHelperLock); OvsCleanupFwdTable(); OvsCleanupIpHelperRequestList(); OVS_LOG_INFO("Terminating the OVS IP Helper system thread"); PsTerminateSystemThread(STATUS_SUCCESS); } NTSTATUS OvsInitIpHelper(NDIS_HANDLE ndisFilterHandle) { NTSTATUS status = NDIS_STATUS_SUCCESS; HANDLE threadHandle; UINT32 i; status = ExInitializeResourceLite(&ovsInstanceListLock); if (status != NDIS_STATUS_SUCCESS) { return status; } InitializeListHead(&ovsInstanceList); ovsFwdHashTable = (PLIST_ENTRY)OvsAllocateMemoryWithTag( sizeof(LIST_ENTRY) * OVS_FWD_HASH_TABLE_SIZE, OVS_IPHELPER_POOL_TAG); ovsRouteHashTable = (PLIST_ENTRY)OvsAllocateMemoryWithTag( sizeof(LIST_ENTRY) * OVS_ROUTE_HASH_TABLE_SIZE, OVS_IPHELPER_POOL_TAG); ovsNeighHashTable = (PLIST_ENTRY)OvsAllocateMemoryWithTag( sizeof(LIST_ENTRY) * OVS_NEIGH_HASH_TABLE_SIZE, OVS_IPHELPER_POOL_TAG); InitializeListHead(&ovsSortedIPNeighList); ovsTableLock = NdisAllocateRWLock(ndisFilterHandle); NdisAllocateSpinLock(&ovsIpHelperLock); InitializeListHead(&ovsIpHelperRequestList); ovsNumIpHelperRequests = 0; ipInterfaceNotificationHandle = NULL; ipRouteNotificationHandle = NULL; unicastIPNotificationHandle = NULL; if (ovsFwdHashTable == NULL || ovsRouteHashTable == NULL || ovsNeighHashTable == NULL || ovsTableLock == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto init_cleanup; } for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) { InitializeListHead(&ovsFwdHashTable[i]); } for (i = 0; i < OVS_ROUTE_HASH_TABLE_SIZE; i++) { InitializeListHead(&ovsRouteHashTable[i]); } for (i = 0; i < OVS_NEIGH_HASH_TABLE_SIZE; i++) { InitializeListHead(&ovsNeighHashTable[i]); } KeInitializeEvent(&ovsIpHelperThreadContext.event, NotificationEvent, FALSE); status = OvsRegisterChangeNotification(); ovsIpHelperThreadContext.exit = 0; if (status == STATUS_SUCCESS) { status = PsCreateSystemThread(&threadHandle, SYNCHRONIZE, NULL, NULL, NULL, OvsStartIpHelper, &ovsIpHelperThreadContext); if (status != STATUS_SUCCESS) { goto init_cleanup; } ObReferenceObjectByHandle(threadHandle, SYNCHRONIZE, NULL, KernelMode, &ovsIpHelperThreadContext.threadObject, NULL); ZwClose(threadHandle); } init_cleanup: if (status != STATUS_SUCCESS) { OvsCancelChangeNotification(); if (ovsFwdHashTable) { OvsFreeMemoryWithTag(ovsFwdHashTable, OVS_IPHELPER_POOL_TAG); ovsFwdHashTable = NULL; } if (ovsRouteHashTable) { OvsFreeMemoryWithTag(ovsRouteHashTable, OVS_IPHELPER_POOL_TAG); ovsRouteHashTable = NULL; } if (ovsNeighHashTable) { OvsFreeMemoryWithTag(ovsNeighHashTable, OVS_IPHELPER_POOL_TAG); ovsNeighHashTable = NULL; } if (ovsTableLock) { NdisFreeRWLock(ovsTableLock); ovsTableLock = NULL; } ExDeleteResourceLite(&ovsInstanceListLock); NdisFreeSpinLock(&ovsIpHelperLock); } return status; } VOID OvsCleanupIpHelper(VOID) { OvsCancelChangeNotification(); NdisAcquireSpinLock(&ovsIpHelperLock); ovsIpHelperThreadContext.exit = 1; OvsWakeupIPHelper(); NdisReleaseSpinLock(&ovsIpHelperLock); KeWaitForSingleObject(ovsIpHelperThreadContext.threadObject, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(ovsIpHelperThreadContext.threadObject); OvsFreeMemoryWithTag(ovsFwdHashTable, OVS_IPHELPER_POOL_TAG); OvsFreeMemoryWithTag(ovsRouteHashTable, OVS_IPHELPER_POOL_TAG); OvsFreeMemoryWithTag(ovsNeighHashTable, OVS_IPHELPER_POOL_TAG); NdisFreeRWLock(ovsTableLock); NdisFreeSpinLock(&ovsIpHelperLock); OvsIpHelperDeleteAllInstances(); ExDeleteResourceLite(&ovsInstanceListLock); } VOID OvsCancelFwdIpHelperRequest(PNET_BUFFER_LIST nbl) { PLIST_ENTRY link, next; POVS_IP_HELPER_REQUEST req; LIST_ENTRY list; InitializeListHead(&list); NdisAcquireSpinLock(&ovsIpHelperLock); LIST_FORALL_SAFE(&ovsIpHelperRequestList, link, next) { req = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link); if (req->command == OVS_IP_HELPER_FWD_REQUEST && (nbl == NULL || req->fwdReq.nbl == nbl)) { RemoveEntryList(link); InsertHeadList(&list, link); if (nbl != NULL) { break; } } } NdisReleaseSpinLock(&ovsIpHelperLock); LIST_FORALL_SAFE(&list, link, next) { req = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link); if (req->fwdReq.cb) { req->fwdReq.cb(req->fwdReq.nbl, req->fwdReq.inPort, &req->fwdReq.tunnelKey, req->fwdReq.cbData1, req->fwdReq.cbData2, STATUS_DEVICE_NOT_READY, NULL); } OvsFreeMemoryWithTag(req, OVS_IPHELPER_POOL_TAG); } }