diff options
author | Samuel Ghinet <sghinet@cloudbasesolutions.com> | 2014-08-29 04:06:48 +0000 |
---|---|---|
committer | Ben Pfaff <blp@nicira.com> | 2014-08-29 07:55:05 -0700 |
commit | fa1324c92810c6b1e33b7e87caaaf2e6c4041040 (patch) | |
tree | 8e06f5d991d755215bb6839a997bc58721b2d754 /datapath-windows/ovsext/IpHelper.c | |
parent | fd972eb87a888242fb1a8ec2394fa7b3030fbd7d (diff) | |
download | openvswitch-fa1324c92810c6b1e33b7e87caaaf2e6c4041040.tar.gz |
datapath-windows: Rename files.
This patch includes the file renaming and accommodations needed for the file
renaming to build the forwarding extension for Hyper-V.
This patch is also a follow-up for the thread:
http://openvswitch.org/pipermail/dev/2014-August/044005.html
Signed-off-by: Samuel Ghinet <sghinet@cloudbasesolutions.com>
Co-authored-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
Diffstat (limited to 'datapath-windows/ovsext/IpHelper.c')
-rw-r--r-- | datapath-windows/ovsext/IpHelper.c | 1689 |
1 files changed, 1689 insertions, 0 deletions
diff --git a/datapath-windows/ovsext/IpHelper.c b/datapath-windows/ovsext/IpHelper.c new file mode 100644 index 000000000..fd663dc3a --- /dev/null +++ b/datapath-windows/ovsext/IpHelper.c @@ -0,0 +1,1689 @@ +/* + * 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" + +#ifdef OVS_DBG_MOD +#undef OVS_DBG_MOD +#endif +#define OVS_DBG_MOD OVS_DBG_IPHELPER +#include "Debug.h" + +/* + * Fow now, we assume only one internal adapter + */ + +KSTART_ROUTINE OvsStartIpHelper; + + +/* + * Only when the internal IP is configured and virtual + * internal port is connected, the IP helper request can be + * queued. + */ +static BOOLEAN ovsInternalIPConfigured; +static UINT32 ovsInternalPortNo; +static GUID ovsInternalNetCfgId; +static MIB_IF_ROW2 ovsInternalRow; +static MIB_IPINTERFACE_ROW ovsInternalIPRow; + +/* we only keep one internal IP for reference, it will not be used for + * determining SRC IP of Tunnel + */ +static UINT32 ovsInternalIP; + + +/* + * 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 OvsCleanupIpHelperRequestList(VOID); +static VOID OvsCleanupFwdTable(VOID); +static VOID OvsAddToSortedNeighList(POVS_IPNEIGH_ENTRY ipn); + +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); + + OVS_LOG_INFO("Interface GUID: %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + ifRow->InterfaceGuid.Data1, + ifRow->InterfaceGuid.Data2, + ifRow->InterfaceGuid.Data3, + *(UINT16 *)ifRow->InterfaceGuid.Data4, + ifRow->InterfaceGuid.Data4[2], + ifRow->InterfaceGuid.Data4[3], + ifRow->InterfaceGuid.Data4[4], + ifRow->InterfaceGuid.Data4[5], + ifRow->InterfaceGuid.Data4[6], + ifRow->InterfaceGuid.Data4[7]); + 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 || 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(NET_LUID interfaceLuid, + const SOCKADDR_INET *destinationAddress, + PMIB_IPFORWARD_ROW2 route, + SOCKADDR_INET *sourceAddress) +{ + NTSTATUS status; + + if (destinationAddress == NULL || route == NULL) { + return STATUS_INVALID_PARAMETER; + } + + status = GetBestRoute2(&interfaceLuid, 0, + NULL, destinationAddress, + 0, route, sourceAddress); + + if (status != STATUS_SUCCESS) { + UINT32 ipAddr = destinationAddress->Ipv4.sin_addr.s_addr; + OVS_LOG_INFO("Fail to get route to %d.%d.%d.%d, status: %x", + ipAddr & 0xff, (ipAddr >> 8) & 0xff, + (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status); + return status; + } + + 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(UINT32 ipAddr, + PMIB_IPNET_ROW2 ipNeigh) +{ + NTSTATUS status; + + ASSERT(ipNeigh); + + RtlZeroMemory(ipNeigh, sizeof (*ipNeigh)); + ipNeigh->InterfaceLuid.Value = ovsInternalRow.InterfaceLuid.Value; + ipNeigh->InterfaceIndex = ovsInternalRow.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 = ovsInternalRow.InterfaceLuid.Value; + ipNeigh->InterfaceIndex = ovsInternalRow.InterfaceIndex; + ipNeigh->Address.si_family = AF_INET; + ipNeigh->Address.Ipv4.sin_addr.s_addr = ipAddr; + status = OvsResolveIPNeighEntry(ipNeigh); + } + return status; +} + + +static VOID +OvsChangeCallbackIpInterface(PVOID context, + PMIB_IPINTERFACE_ROW ipRow, + MIB_NOTIFICATION_TYPE notificationType) +{ + UNREFERENCED_PARAMETER(context); + switch (notificationType) { + case MibParameterNotification: + case MibAddInstance: + if (ipRow->InterfaceLuid.Info.NetLuidIndex == + ovsInternalRow.InterfaceLuid.Info.NetLuidIndex && + ipRow->InterfaceLuid.Info.IfType == + ovsInternalRow.InterfaceLuid.Info.IfType && + ipRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) { + /* + * Update the IP Interface Row + */ + NdisAcquireSpinLock(&ovsIpHelperLock); + RtlCopyMemory(&ovsInternalIPRow, ipRow, + sizeof (PMIB_IPINTERFACE_ROW)); + ovsInternalIPConfigured = TRUE; + NdisReleaseSpinLock(&ovsIpHelperLock); + } + OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d is %s", + ipRow->InterfaceLuid.Info.NetLuidIndex, + ipRow->InterfaceLuid.Info.IfType, + notificationType == MibAddInstance ? "added" : "modified"); + break; + case MibDeleteInstance: + OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d, deleted", + ipRow->InterfaceLuid.Info.NetLuidIndex, + ipRow->InterfaceLuid.Info.IfType); + if (ipRow->InterfaceLuid.Info.NetLuidIndex == + ovsInternalRow.InterfaceLuid.Info.NetLuidIndex && + ipRow->InterfaceLuid.Info.IfType == + ovsInternalRow.InterfaceLuid.Info.IfType && + ipRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) { + + NdisAcquireSpinLock(&ovsIpHelperLock); + ovsInternalIPConfigured = FALSE; + NdisReleaseSpinLock(&ovsIpHelperLock); + + OvsCleanupIpHelperRequestList(); + + OvsCleanupFwdTable(); + } + + break; + case MibInitialNotification: + OVS_LOG_INFO("Get 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; + + 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"); + + if (ipRoute->InterfaceLuid.Info.NetLuidIndex == + ovsInternalRow.InterfaceLuid.Info.NetLuidIndex && + ipRoute->InterfaceLuid.Info.IfType == + ovsInternalRow.InterfaceLuid.Info.IfType && + ipRoute->InterfaceIndex == ovsInternalRow.InterfaceIndex) { + + POVS_IPFORWARD_ENTRY ipf; + LOCK_STATE_EX lockState; + + NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); + ipf = OvsLookupIPForwardEntry(&ipRoute->DestinationPrefix); + if (ipf != NULL) { + OvsRemoveIPForwardEntry(ipf); + } + NdisReleaseRWLock(ovsTableLock, &lockState); + } + 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: + ASSERT(unicastRow); + ipAddr = unicastRow->Address.Ipv4.sin_addr.s_addr; + if (unicastRow->InterfaceLuid.Info.NetLuidIndex == + ovsInternalRow.InterfaceLuid.Info.NetLuidIndex && + unicastRow->InterfaceLuid.Info.IfType == + ovsInternalRow.InterfaceLuid.Info.IfType && + unicastRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) { + ovsInternalIP = 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"); + break; + + case MibDeleteInstance: + ASSERT(unicastRow); + ipAddr = unicastRow->Address.Ipv4.sin_addr.s_addr; + OVS_LOG_INFO("IP Address removed: %d.%d.%d.%d", + ipAddr & 0xff, (ipAddr >> 8) & 0xff, + (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); + if (unicastRow->InterfaceLuid.Info.NetLuidIndex == + ovsInternalRow.InterfaceLuid.Info.NetLuidIndex && + unicastRow->InterfaceLuid.Info.IfType == + ovsInternalRow.InterfaceLuid.Info.IfType && + unicastRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) { + + LOCK_STATE_EX lockState; + NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); + OvsRemoveAllFwdEntriesWithSrc(ipAddr); + NdisReleaseRWLock(ovsTableLock, &lockState); + + } + 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; + + + 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; + } + + status = NotifyRouteChange2(AF_INET, OvsChangeCallbackIpRoute, NULL, + TRUE, &ipRouteNotificationHandle); + if (status != STATUS_SUCCESS) { + OVS_LOG_ERROR("Fail to regiter ip route change, status: %x.", + status); + goto register_cleanup; + } + status = NotifyUnicastIpAddressChange(AF_INET, + OvsChangeCallbackUnicastIpAddress, + NULL, TRUE, + &unicastIPNotificationHandle); + if (status != STATUS_SUCCESS) { + OVS_LOG_ERROR("Fail to regiter 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; + POVS_IPNEIGH_ENTRY entry; + UINT32 hash = OvsJhashWords(&ipAddr, 1, OVS_HASH_BASIS); + + LIST_FORALL(&ovsNeighHashTable[hash & OVS_NEIGH_HASH_TABLE_MASK], link) { + 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; + POVS_IPFORWARD_ENTRY ipfEntry; + 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) { + 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 dstIp) +{ + PLIST_ENTRY link; + POVS_FWD_ENTRY entry; + UINT32 hash = OvsJhashWords(&dstIp, 1, OVS_HASH_BASIS); + + LIST_FORALL(&ovsFwdHashTable[hash & OVS_FWD_HASH_TABLE_MASK], link) { + entry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, link); + if (entry->info.dstIpAddr == dstIp) { + return entry; + } + } + return NULL; +} + + +NTSTATUS +OvsLookupIPFwdInfo(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(dstIp); + if (entry) { + info->value[0] = entry->info.value[0]; + info->value[1] = entry->info.value[1]; + info->value[2] = entry->info.value[2]; + status = STATUS_SUCCESS; + } + NdisReleaseRWLock(ovsTableLock, &lockState); + return status; +} + + +static POVS_IPNEIGH_ENTRY +OvsCreateIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh) +{ + + POVS_IPNEIGH_ENTRY entry; + UINT64 timeVal; + + ASSERT(ipNeigh != NULL); + entry = (POVS_IPNEIGH_ENTRY)OvsAllocateMemory(sizeof (OVS_IPNEIGH_ENTRY)); + 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, + MAC_ADDRESS_LEN); + InitializeListHead(&entry->fwdList); + + return entry; +} + + +static POVS_IPFORWARD_ENTRY +OvsCreateIPForwardEntry(PMIB_IPFORWARD_ROW2 ipRoute) +{ + + POVS_IPFORWARD_ENTRY entry; + + ASSERT(ipRoute); + + entry = + (POVS_IPFORWARD_ENTRY)OvsAllocateMemory(sizeof (OVS_IPFORWARD_ENTRY)); + 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)OvsAllocateMemory(sizeof (OVS_FWD_ENTRY)); + 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); + OvsFreeMemory(ipf); + } + + if (ipn->refCount == 0) { + ASSERT(IsListEmpty(&ipn->fwdList)); + RemoveEntryList(&ipn->link); + NdisAcquireSpinLock(&ovsIpHelperLock); + RemoveEntryList(&ipn->slink); + NdisReleaseSpinLock(&ovsIpHelperLock); + OvsFreeMemory(ipn); + } + + OvsFreeMemory(fwdEntry); +} + + +static VOID +OvsRemoveIPForwardEntry(POVS_IPFORWARD_ENTRY ipf) +{ + POVS_FWD_ENTRY fwdEntry; + PLIST_ENTRY link, next; + + ipf->refCount++; + + LIST_FORALL_SAFE(&ipf->fwdList, link, next) { + fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipfLink); + OvsRemoveFwdEntry(fwdEntry); + } + ASSERT(ipf->refCount == 1); + + RemoveEntryList(&ipf->link); + OvsFreeMemory(ipf); +} + + +static VOID +OvsRemoveIPNeighEntry(POVS_IPNEIGH_ENTRY ipn) +{ + PLIST_ENTRY link, next; + POVS_FWD_ENTRY fwdEntry; + + ipn->refCount++; + + LIST_FORALL_SAFE(&ipn->fwdList, link, next) { + fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipnLink); + OvsRemoveFwdEntry(fwdEntry); + } + + if (ipn->refCount == 1) { + RemoveEntryList(&ipn->link); + NdisAcquireSpinLock(&ovsIpHelperLock); + RemoveEntryList(&ipn->slink); + NdisReleaseSpinLock(&ovsIpHelperLock); + OvsFreeMemory(ipn); + } +} + + +static VOID +OvsAddToSortedNeighList(POVS_IPNEIGH_ENTRY ipn) +{ + PLIST_ENTRY link; + POVS_IPNEIGH_ENTRY entry; + + if (!IsListEmpty(&ovsSortedIPNeighList)) { + link = ovsSortedIPNeighList.Blink; + 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; + POVS_FWD_ENTRY fwdEntry; + PLIST_ENTRY link, next; + + for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) { + LIST_FORALL_SAFE(&ovsFwdHashTable[i], link, next) { + fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, link); + if (fwdEntry->info.srcIpAddr == ipAddr) { + OvsRemoveFwdEntry(fwdEntry); + } + } + } +} + + +static VOID +OvsCleanupFwdTable(VOID) +{ + PLIST_ENTRY link, next; + POVS_IPNEIGH_ENTRY ipn; + UINT32 i; + LOCK_STATE_EX lockState; + + NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); + if (ovsNumFwdEntries) { + LIST_FORALL_SAFE(&ovsSortedIPNeighList, link, next) { + 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; + POVS_IP_HELPER_REQUEST request; + + NdisAcquireSpinLock(&ovsIpHelperLock); + if (ovsNumIpHelperRequests == 0) { + NdisReleaseSpinLock(&ovsIpHelperLock); + return; + } + + InitializeListHead(&list); + OvsAppendList(&list, &ovsIpHelperRequestList); + ovsNumIpHelperRequests = 0; + NdisReleaseSpinLock(&ovsIpHelperLock); + + LIST_FORALL_SAFE(&list, link, next) { + 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); + } + OvsFreeMemory(request); + } +} + + + +static VOID +OvsWakeupIPHelper(VOID) +{ + KeSetEvent(&ovsIpHelperThreadContext.event, 0, FALSE); +} + +VOID +OvsInternalAdapterDown(VOID) +{ + NdisAcquireSpinLock(&ovsIpHelperLock); + ovsInternalPortNo = OVS_DEFAULT_PORT_NO; + ovsInternalIPConfigured = FALSE; + NdisReleaseSpinLock(&ovsIpHelperLock); + + OvsCleanupIpHelperRequestList(); + + OvsCleanupFwdTable(); +} + + +VOID +OvsInternalAdapterUp(UINT32 portNo, + GUID *netCfgInstanceId) +{ + POVS_IP_HELPER_REQUEST request; + + RtlCopyMemory(&ovsInternalNetCfgId, netCfgInstanceId, sizeof (GUID)); + RtlZeroMemory(&ovsInternalRow, sizeof (MIB_IF_ROW2)); + + request = + (POVS_IP_HELPER_REQUEST)OvsAllocateMemory(sizeof (OVS_IP_HELPER_REQUEST)); + if (request == NULL) { + OVS_LOG_ERROR("Fail to initialize Internal Adapter"); + return; + } + RtlZeroMemory(request, sizeof (OVS_IP_HELPER_REQUEST)); + request->command = OVS_IP_HELPER_INTERNAL_ADAPTER_UP; + + NdisAcquireSpinLock(&ovsIpHelperLock); + ovsInternalPortNo = portNo; + InsertHeadList(&ovsIpHelperRequestList, &request->link); + ovsNumIpHelperRequests++; + if (ovsNumIpHelperRequests == 1) { + OvsWakeupIPHelper(); + } + NdisReleaseSpinLock(&ovsIpHelperLock); +} + + +static VOID +OvsHandleInternalAdapterUp(POVS_IP_HELPER_REQUEST request) +{ + NTSTATUS status; + MIB_UNICASTIPADDRESS_ROW ipEntry; + GUID *netCfgInstanceId = &ovsInternalNetCfgId; + + OvsFreeMemory(request); + + status = OvsGetIfEntry(&ovsInternalNetCfgId, &ovsInternalRow); + + if (status != STATUS_SUCCESS) { + OVS_LOG_ERROR("Fali to get IF entry for internal port with GUID" + " %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + netCfgInstanceId->Data1, + netCfgInstanceId->Data2, + netCfgInstanceId->Data3, + *(UINT16 *)netCfgInstanceId->Data4, + netCfgInstanceId->Data4[2], + netCfgInstanceId->Data4[3], + netCfgInstanceId->Data4[4], + netCfgInstanceId->Data4[5], + netCfgInstanceId->Data4[6], + netCfgInstanceId->Data4[7]); + return; + } + + status = OvsGetIPInterfaceEntry(ovsInternalRow.InterfaceLuid, + &ovsInternalIPRow); + + if (status == STATUS_SUCCESS) { + NdisAcquireSpinLock(&ovsIpHelperLock); + ovsInternalIPConfigured = TRUE; + NdisReleaseSpinLock(&ovsIpHelperLock); + } else { + return; + } + + status = OvsGetIPEntry(ovsInternalRow.InterfaceLuid, &ipEntry); + if (status != STATUS_SUCCESS) { + OVS_LOG_INFO("Fali to get IP entry for internal port with GUID" + " %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + netCfgInstanceId->Data1, + netCfgInstanceId->Data2, + netCfgInstanceId->Data3, + *(UINT16 *)netCfgInstanceId->Data4, + netCfgInstanceId->Data4[2], + netCfgInstanceId->Data4[3], + netCfgInstanceId->Data4[4], + netCfgInstanceId->Data4[5], + netCfgInstanceId->Data4[6], + netCfgInstanceId->Data4[7]); + } +} + + +static NTSTATUS +OvsEnqueueIpHelperRequest(POVS_IP_HELPER_REQUEST request) +{ + + NdisAcquireSpinLock(&ovsIpHelperLock); + + if (ovsInternalPortNo == OVS_DEFAULT_PORT_NO || + ovsInternalIPConfigured == FALSE) { + NdisReleaseSpinLock(&ovsIpHelperLock); + OvsFreeMemory(request); + return STATUS_NDIS_ADAPTER_NOT_READY; + } else { + 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)OvsAllocateMemory(sizeof (OVS_IP_HELPER_REQUEST)); + + 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 = STATUS_SUCCESS; + MIB_IPFORWARD_ROW2 ipRoute; + MIB_IPNET_ROW2 ipNeigh; + OVS_FWD_INFO fwdInfo; + 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; + + status = OvsLookupIPFwdInfo(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(ovsInternalRow.InterfaceLuid, &dst, &ipRoute, &src); + if (status != STATUS_SUCCESS) { + goto fwd_handle_nbl; + } + 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 = ovsInternalRow.InterfaceLuid.Value; + if (ipAddr == 0) { + ipAddr = request->fwdReq.tunnelKey.dst; + } + status = OvsGetOrResolveIPNeigh(ipAddr, &ipNeigh); + if (status != STATUS_SUCCESS) { + 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); + 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); + srcAddr = fwdEntry->info.srcIpAddr; + } + + /* + * initialize ipn + */ + if (ipn == NULL) { + ipn = OvsLookupIPNeighEntry(ipAddr); + if (ipn == NULL) { + ipn = OvsCreateIPNeighEntry(&ipNeigh); + if (ipn == NULL) { + NdisReleaseRWLock(ovsTableLock, &lockState); + 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, MAC_ADDRESS_LEN); + RtlCopyMemory(fwdInfo.srcMacAddr, ovsInternalRow.PhysicalAddress, + MAC_ADDRESS_LEN); + fwdInfo.srcPortNo = request->fwdReq.inPort; + + fwdEntry = OvsCreateFwdEntry(&fwdInfo); + if (fwdEntry == NULL) { + NdisReleaseRWLock(ovsTableLock, &lockState); + status = STATUS_INSUFFICIENT_RESOURCES; + goto fwd_handle_nbl; + } + newFWD = TRUE; + /* + * Cache the result + */ + OvsAddIPFwdCache(fwdEntry, ipf, ipn); + NdisReleaseRWLock(ovsTableLock, &lockState); + +fwd_handle_nbl: + + if (status != STATUS_SUCCESS) { + if (newFWD) { + ASSERT(fwdEntry != NULL); + OvsFreeMemory(fwdEntry); + } + if (newIPF) { + ASSERT(ipf && ipf->refCount == 0); + OvsFreeMemory(ipf); + } + if (newIPN) { + ASSERT(ipn && ipn->refCount == 0); + OvsFreeMemory(ipn); + } + 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); + } + OvsFreeMemory(request); +} + + +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)MAC_ADDRESS_LEN)) { + PLIST_ENTRY link; + POVS_FWD_ENTRY fwdEntry; + 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) { + fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipnLink); + RtlCopyMemory(fwdEntry->info.dstMacAddr, + ipNeigh->PhysicalAddress, MAC_ADDRESS_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); +} + + +static VOID +OvsHandleIPNeighTimeout(UINT32 ipAddr) +{ + MIB_IPNET_ROW2 ipNeigh; + NTSTATUS status; + + status = OvsGetOrResolveIPNeigh(ipAddr, &ipNeigh); + + OvsUpdateIPNeighEntry(ipAddr, &ipNeigh, status); +} + + +/* + *---------------------------------------------------------------------------- + * IP Helper system threash handle following request + * 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 callback. + *---------------------------------------------------------------------------- + */ +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; + + OVS_LOG_INFO("Start the IP Helper Thread, context: %p", context); + + NdisAcquireSpinLock(&ovsIpHelperLock); + while (!context->exit) { + + timeout = 0; + while (!IsListEmpty(&ovsIpHelperRequestList)) { + if (context->exit) { + goto ip_helper_wait; + } + link = ovsIpHelperRequestList.Flink; + RemoveEntryList(link); + 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_FWD_REQUEST: + OvsHandleFwdRequest(req); + break; + default: + OvsFreeMemory(req); + } + 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; + break; + } + ipAddr = ipn->ipAddr; + + NdisReleaseSpinLock(&ovsIpHelperLock); + + OvsHandleIPNeighTimeout(ipAddr); + + NdisAcquireSpinLock(&ovsIpHelperLock); + } + if (!IsListEmpty(&ovsIpHelperRequestList)) { + continue; + } + +ip_helper_wait: + if (context->exit) { + break; + } + + KeClearEvent(&context->event); + NdisReleaseSpinLock(&ovsIpHelperLock); + + KeWaitForSingleObject(&context->event, Executive, KernelMode, + FALSE, (LARGE_INTEGER *)&timeout); + 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; + HANDLE threadHandle; + UINT32 i; + + ovsFwdHashTable = (PLIST_ENTRY)OvsAllocateMemory(sizeof(LIST_ENTRY) * + OVS_FWD_HASH_TABLE_SIZE); + + ovsRouteHashTable = (PLIST_ENTRY)OvsAllocateMemory(sizeof(LIST_ENTRY) * + OVS_ROUTE_HASH_TABLE_SIZE); + + ovsNeighHashTable = (PLIST_ENTRY)OvsAllocateMemory(sizeof(LIST_ENTRY) * + OVS_NEIGH_HASH_TABLE_SIZE); + + RtlZeroMemory(&ovsInternalRow, sizeof(MIB_IF_ROW2)); + RtlZeroMemory(&ovsInternalIPRow, sizeof (MIB_IPINTERFACE_ROW)); + ovsInternalIP = 0; + + ovsInternalPortNo = OVS_DEFAULT_PORT_NO; + + 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) { + OvsFreeMemory(ovsFwdHashTable); + ovsFwdHashTable = NULL; + } + if (ovsRouteHashTable) { + OvsFreeMemory(ovsRouteHashTable); + ovsRouteHashTable = NULL; + } + if (ovsNeighHashTable) { + OvsFreeMemory(ovsNeighHashTable); + ovsNeighHashTable = NULL; + } + if (ovsTableLock) { + NdisFreeRWLock(ovsTableLock); + ovsTableLock = NULL; + } + NdisFreeSpinLock(&ovsIpHelperLock); + } + return STATUS_SUCCESS; +} + + +VOID +OvsCleanupIpHelper(VOID) +{ + OvsCancelChangeNotification(); + + NdisAcquireSpinLock(&ovsIpHelperLock); + ovsIpHelperThreadContext.exit = 1; + OvsWakeupIPHelper(); + NdisReleaseSpinLock(&ovsIpHelperLock); + + KeWaitForSingleObject(ovsIpHelperThreadContext.threadObject, Executive, + KernelMode, FALSE, NULL); + ObDereferenceObject(ovsIpHelperThreadContext.threadObject); + + OvsFreeMemory(ovsFwdHashTable); + OvsFreeMemory(ovsRouteHashTable); + OvsFreeMemory(ovsNeighHashTable); + + NdisFreeRWLock(ovsTableLock); + NdisFreeSpinLock(&ovsIpHelperLock); +} + +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); + } + OvsFreeMemory(req); + } +} |