/* Infinite timeout */
#define INFINITE 0xFFFFFFFF
-/*
- * The provider name should always match the provider string from the install
- * file.
- */
+/* The provider name should always match the provider string from the install
+ * file. */
#define OVS_TUNNEL_PROVIDER_NAME L"Open vSwitch"
-/*
- * The provider description should always contain the OVS service description
- * string from the install file.
- */
+/* The provider description should always contain the OVS service description
+ * string from the install file. */
#define OVS_TUNNEL_PROVIDER_DESC L"Open vSwitch Extension tunnel provider"
/* The session name isn't required but it's useful for diagnostics. */
#define OVS_TUNNEL_SESSION_NAME L"OVS tunnel session"
-/* Configurable parameters (addresses and ports are in host order) */
-UINT16 configNewDestPort = VXLAN_UDP_PORT;
+/* Maximum number of tunnel threads to be created. */
+#define OVS_TUNFLT_MAX_THREADS 8
/*
* Callout and sublayer GUIDs
*/
+
// b16b0a6e-2b2a-41a3-8b39-bd3ffc855ff8
DEFINE_GUID(
OVS_TUNNEL_CALLOUT_V4,
);
/*
- * Callout driver global variables
+ * Callout driver type definitions
*/
-PDEVICE_OBJECT gDeviceObject;
+typedef enum _OVS_TUNFLT_OPERATION {
+ OVS_TUN_FILTER_CREATE = 0,
+ OVS_TUN_FILTER_DELETE
+} OVS_TUNFLT_OPERATION;
+
+typedef struct _OVS_TUNFLT_REQUEST {
+ LIST_ENTRY entry;
+ /* Tunnel filter destination port. */
+ UINT16 port;
+ /* XXX: We also need to specify the tunnel L4 protocol, because there are
+ * different protocols that can use the same destination port.*/
+ union {
+ /* Tunnel filter identification used for filter deletion. */
+ UINT64 delID;
+ /* Pointer used to return filter ID to the caller on filter creation. */
+ PUINT64 addID;
+ }filterID;
+ /* Requested operation to be performed. */
+ OVS_TUNFLT_OPERATION operation;
+ /* Current I/O request to be completed when requested
+ * operation is finished. */
+ PIRP irp;
+ /* Callback function called before completing the IRP. */
+ PFNTunnelVportPendingOp callback;
+ /* Context passed to the callback function. */
+ PVOID context;
+} OVS_TUNFLT_REQUEST, *POVS_TUNFLT_REQUEST;
+
+typedef struct _OVS_TUNFLT_REQUEST_LIST {
+ /* SpinLock for syncronizing access to the requests list. */
+ NDIS_SPIN_LOCK spinlock;
+ /* Head of the requests list. */
+ LIST_ENTRY head;
+ /* Number of requests in the list. This variable is used by
+ * InterlockedCompareExchange function and needs to be aligned
+ * at 32-bit boundaries. */
+ UINT32 numEntries;
+} OVS_TUNFLT_REQUEST_LIST, *POVS_TUNFLT_REQUEST_LIST;
+
+typedef struct _OVS_TUNFLT_THREAD_CONTEXT {
+ /* Thread identification. */
+ UINT threadID;
+ /* Thread's engine session handle. */
+ HANDLE engineSession;
+ /* Reference of the thread object. */
+ PVOID threadObject;
+ /* Requests queue list. */
+ OVS_TUNFLT_REQUEST_LIST listRequests;
+ /* Event signaling that there are requests to process. */
+ KEVENT requestEvent;
+ /* Event for stopping thread execution. */
+ KEVENT stopEvent;
+} OVS_TUNFLT_THREAD_CONTEXT, *POVS_TUNFLT_THREAD_CONTEXT;
+
+KSTART_ROUTINE OvsTunnelFilterThreadProc;
+
+static NTSTATUS OvsTunnelFilterStartThreads();
+static NTSTATUS OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
+static VOID OvsTunnelFilterStopThreads();
+static VOID OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
+ BOOLEAN signalEvent);
+static NTSTATUS OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
+static VOID OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
-HANDLE gEngineHandle = NULL;
-HANDLE gTunnelProviderBfeHandle = NULL;
-HANDLE gTunnelInitBfeHandle = NULL;
-UINT32 gCalloutIdV4;
+/*
+ * Callout driver global variables
+ */
+static PDEVICE_OBJECT gDeviceObject = NULL;
+static HANDLE gEngineHandle = NULL;
+static HANDLE gTunnelProviderBfeHandle = NULL;
+static HANDLE gTunnelInitBfeHandle = NULL;
+static HANDLE gBfeSubscriptionHandle = NULL;
+static UINT32 gCalloutIdV4 = 0;
+static OVS_TUNFLT_THREAD_CONTEXT gTunnelThreadCtx[OVS_TUNFLT_MAX_THREADS] = { 0 };
-/* Callout driver implementation */
+/*
+ * Callout driver implementation.
+ */
NTSTATUS
-OvsTunnelEngineOpen(HANDLE *handle)
+OvsTunnelEngineOpen(HANDLE *engineSession)
{
NTSTATUS status = STATUS_SUCCESS;
FWPM_SESSION session = { 0 };
- /* The session name isn't required but may be useful for diagnostics. */
- session.displayData.name = OVS_TUNNEL_SESSION_NAME;
/*
* Set an infinite wait timeout, so we don't have to handle FWP_E_TIMEOUT
* errors while waiting to acquire the transaction lock.
*/
session.txnWaitTimeoutInMSec = INFINITE;
- session.flags = FWPM_SESSION_FLAG_DYNAMIC;
/* The authentication service should always be RPC_C_AUTHN_DEFAULT. */
status = FwpmEngineOpen(NULL,
RPC_C_AUTHN_DEFAULT,
NULL,
&session,
- handle);
+ engineSession);
if (!NT_SUCCESS(status)) {
- OVS_LOG_ERROR("Fail to open filtering engine session, status: %x.",
+ OVS_LOG_ERROR("Failed to open filtering engine session, status: %x.",
status);
}
}
VOID
-OvsTunnelEngineClose(HANDLE *handle)
+OvsTunnelEngineClose(HANDLE *engineSession)
{
- if (*handle) {
- FwpmEngineClose(*handle);
- *handle = NULL;
+ if (*engineSession) {
+ FwpmEngineClose(*engineSession);
+ *engineSession = NULL;
}
}
VOID
-OvsTunnelAddSystemProvider(HANDLE handle)
+OvsTunnelAddSystemProvider(HANDLE engineSession)
{
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN inTransaction = FALSE;
FWPM_PROVIDER provider = { 0 };
do {
- status = FwpmTransactionBegin(handle, 0);
+ status = FwpmTransactionBegin(engineSession, 0);
if (!NT_SUCCESS(status)) {
break;
}
*/
provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
- status = FwpmProviderAdd(handle,
+ status = FwpmProviderAdd(engineSession,
&provider,
NULL);
if (!NT_SUCCESS(status)) {
}
}
- status = FwpmTransactionCommit(handle);
+ status = FwpmTransactionCommit(engineSession);
if (!NT_SUCCESS(status)) {
break;
}
} while (inTransaction);
if (inTransaction){
- FwpmTransactionAbort(handle);
+ FwpmTransactionAbort(engineSession);
}
}
VOID
-OvsTunnelRemoveSystemProvider(HANDLE handle)
+OvsTunnelRemoveSystemProvider(HANDLE engineSession)
{
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN inTransaction = FALSE;
do {
- status = FwpmTransactionBegin(handle, 0);
+ status = FwpmTransactionBegin(engineSession, 0);
if (!NT_SUCCESS(status)) {
break;
}
inTransaction = TRUE;
- status = FwpmProviderDeleteByKey(handle,
+ status = FwpmProviderDeleteByKey(engineSession,
&OVS_TUNNEL_PROVIDER_KEY);
if (!NT_SUCCESS(status)) {
break;
}
- status = FwpmTransactionCommit(handle);
+ status = FwpmTransactionCommit(engineSession);
if (!NT_SUCCESS(status)) {
break;
}
} while (inTransaction);
if (inTransaction){
- FwpmTransactionAbort(handle);
+ FwpmTransactionAbort(engineSession);
}
}
NTSTATUS
-OvsTunnelAddFilter(PWSTR filterName,
+OvsTunnelAddFilter(HANDLE engineSession,
+ PWSTR filterName,
const PWSTR filterDesc,
USHORT remotePort,
FWP_DIRECTION direction,
UINT64 context,
const GUID *filterKey,
const GUID *layerKey,
- const GUID *calloutKey)
+ const GUID *calloutKey,
+ UINT64 *filterID)
{
NTSTATUS status = STATUS_SUCCESS;
FWPM_FILTER filter = {0};
FWPM_FILTER_CONDITION filterConditions[3] = {0};
UINT conditionIndex;
- UNREFERENCED_PARAMETER(remotePort);
- UNREFERENCED_PARAMETER(direction);
-
- filter.filterKey = *filterKey;
+ if (filterKey) {
+ filter.filterKey = *filterKey;
+ }
filter.layerKey = *layerKey;
filter.displayData.name = (wchar_t*)filterName;
filter.displayData.description = (wchar_t*)filterDesc;
filter.numFilterConditions = conditionIndex;
- status = FwpmFilterAdd(gEngineHandle,
+ status = FwpmFilterAdd(engineSession,
&filter,
NULL,
- NULL);
+ filterID);
return status;
}
-NTSTATUS
-OvsTunnelRemoveFilter(const GUID *filterKey,
- const GUID *sublayerKey)
-{
- NTSTATUS status = STATUS_SUCCESS;
- BOOLEAN inTransaction = FALSE;
-
- do {
- status = FwpmTransactionBegin(gEngineHandle, 0);
- if (!NT_SUCCESS(status)) {
- break;
- }
-
- inTransaction = TRUE;
-
- /*
- * We have to delete the filter first since it references the
- * sublayer. If we tried to delete the sublayer first, it would fail
- * with FWP_ERR_IN_USE.
- */
- status = FwpmFilterDeleteByKey(gEngineHandle,
- filterKey);
- if (!NT_SUCCESS(status)) {
- break;
- }
-
- status = FwpmSubLayerDeleteByKey(gEngineHandle,
- sublayerKey);
- if (!NT_SUCCESS(status)) {
- break;
- }
-
- status = FwpmTransactionCommit(gEngineHandle);
- if (!NT_SUCCESS(status)){
- break;
- }
-
- inTransaction = FALSE;
- } while (inTransaction);
-
- if (inTransaction) {
- FwpmTransactionAbort(gEngineHandle);
- }
- return status;
-}
-
/*
* --------------------------------------------------------------------------
- * This function registers callouts and filters that intercept UDP traffic at
- * WFP FWPM_LAYER_DATAGRAM_DATA_V4
+ * This function registers callouts for intercepting UDP traffic at WFP
+ * FWPM_LAYER_DATAGRAM_DATA_V4 layer.
* --------------------------------------------------------------------------
*/
NTSTATUS
sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
#endif
- status = FwpsCalloutRegister(deviceObject,
- &sCallout,
- calloutId);
-
+ status = FwpsCalloutRegister(deviceObject, &sCallout, calloutId);
if (!NT_SUCCESS(status)) {
goto Exit;
}
mCallout.displayData = displayData;
mCallout.applicableLayer = *layerKey;
- status = FwpmCalloutAdd(gEngineHandle,
- &mCallout,
- NULL,
- NULL);
-
+ status = FwpmCalloutAdd(gEngineHandle, &mCallout, NULL, NULL);
if (!NT_SUCCESS(status)) {
goto Exit;
}
- status = OvsTunnelAddFilter(L"Datagram-Data OVS Filter (Inbound)",
- L"address/port for UDP",
- configNewDestPort,
- FWP_DIRECTION_INBOUND,
- 0,
- &OVS_TUNNEL_FILTER_KEY,
- layerKey,
- calloutKey);
-
Exit:
if (!NT_SUCCESS(status)){
/*
* --------------------------------------------------------------------------
- * This function registers dynamic callouts and filters that intercept UDP
- * Callouts and filters will be removed during De-Initialize.
+ * This function registers non-dynamic callouts for intercepting UDP traffic.
+ * Callouts will be removed during un-initializing phase.
* --------------------------------------------------------------------------
*/
NTSTATUS
OvsTunnelRegisterCallouts(VOID *deviceObject)
{
- NTSTATUS status = STATUS_SUCCESS;
- FWPM_SUBLAYER OvsTunnelSubLayer;
-
- BOOLEAN engineOpened = FALSE;
- BOOLEAN inTransaction = FALSE;
-
- status = OvsTunnelEngineOpen(&gEngineHandle);
- if (!NT_SUCCESS(status)) {
- goto Exit;
- }
- engineOpened = TRUE;
+ NTSTATUS status = STATUS_SUCCESS;
+ BOOLEAN inTransaction = FALSE;
+ FWPM_SUBLAYER OvsTunnelSubLayer;
status = FwpmTransactionBegin(gEngineHandle, 0);
if (!NT_SUCCESS(status)) {
if (inTransaction) {
FwpmTransactionAbort(gEngineHandle);
}
- if (engineOpened) {
- OvsTunnelEngineClose(&gEngineHandle);
- }
}
return status;
}
VOID
-OvsTunnelUnregisterCallouts(VOID)
+OvsTunnelUnregisterCallouts()
{
- OvsTunnelRemoveFilter(&OVS_TUNNEL_FILTER_KEY,
- &OVS_TUNNEL_SUBLAYER);
FwpsCalloutUnregisterById(gCalloutIdV4);
+ FwpmSubLayerDeleteByKey(gEngineHandle, &OVS_TUNNEL_SUBLAYER);
FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
- OvsTunnelEngineClose(&gEngineHandle);
}
VOID
{
UNREFERENCED_PARAMETER(driverObject);
+ OvsTunnelFilterStopThreads();
+
OvsTunnelUnregisterCallouts();
- IoDeleteDevice(gDeviceObject);
+ OvsTunnelEngineClose(&gEngineHandle);
+
+ if (gDeviceObject) {
+ IoDeleteDevice(gDeviceObject);
+ }
}
NTSTATUS
OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
{
- NTSTATUS status = STATUS_SUCCESS;
- UNICODE_STRING deviceName;
+ NTSTATUS status = STATUS_SUCCESS;
+ UNICODE_STRING deviceName;
RtlInitUnicodeString(&deviceName,
L"\\Device\\OvsTunnelFilter");
FALSE,
&gDeviceObject);
+ if (!NT_SUCCESS(status)){
+ OVS_LOG_ERROR("Failed to create tunnel filter device, status: %x.",
+ status);
+ goto Exit;
+ }
+
+ status = OvsTunnelFilterStartThreads();
+ if (!NT_SUCCESS(status)){
+ goto Exit;
+ }
+
+ status = OvsTunnelEngineOpen(&gEngineHandle);
if (!NT_SUCCESS(status)){
goto Exit;
}
status = OvsTunnelRegisterCallouts(gDeviceObject);
+ if (!NT_SUCCESS(status)) {
+ OVS_LOG_ERROR("Failed to register callout, status: %x.",
+ status);
+ }
Exit:
if (!NT_SUCCESS(status)){
- if (gEngineHandle != NULL) {
- OvsTunnelUnregisterCallouts();
- }
-
- if (gDeviceObject) {
- IoDeleteDevice(gDeviceObject);
- }
+ OvsTunnelFilterUninitialize(driverObject);
}
return status;
OvsTunnelProviderBfeCallback(PVOID context,
FWPM_SERVICE_STATE bfeState)
{
- HANDLE handle = NULL;
+ HANDLE engineSession = NULL;
DBG_UNREFERENCED_PARAMETER(context);
if (FWPM_SERVICE_RUNNING == bfeState) {
- OvsTunnelEngineOpen(&handle);
- if (handle) {
- OvsTunnelAddSystemProvider(handle);
+ OvsTunnelEngineOpen(&engineSession);
+ if (engineSession) {
+ OvsTunnelAddSystemProvider(engineSession);
}
- OvsTunnelEngineClose(&handle);
+ OvsTunnelEngineClose(&engineSession);
}
}
OvsRegisterSystemProvider(PVOID deviceObject)
{
NTSTATUS status = STATUS_SUCCESS;
- HANDLE handle = NULL;
+ HANDLE engineSession = NULL;
status = OvsSubscribeTunnelProviderBfeStateChanges(deviceObject);
if (NT_SUCCESS(status)) {
if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
- OvsTunnelEngineOpen(&handle);
- if (handle) {
- OvsTunnelAddSystemProvider(handle);
+ OvsTunnelEngineOpen(&engineSession);
+ if (engineSession) {
+ OvsTunnelAddSystemProvider(engineSession);
}
- OvsTunnelEngineClose(&handle);
+ OvsTunnelEngineClose(&engineSession);
OvsUnsubscribeTunnelProviderBfeStateChanges();
}
VOID OvsUnregisterSystemProvider()
{
- HANDLE handle = NULL;
+ HANDLE engineSession = NULL;
- OvsTunnelEngineOpen(&handle);
- if (handle) {
- OvsTunnelRemoveSystemProvider(handle);
+ OvsTunnelEngineOpen(&engineSession);
+ if (engineSession) {
+ OvsTunnelRemoveSystemProvider(engineSession);
}
- OvsTunnelEngineClose(&handle);
+ OvsTunnelEngineClose(&engineSession);
OvsUnsubscribeTunnelProviderBfeStateChanges();
}
OvsTunnelFilterUninitialize(driverObject);
OvsUnsubscribeTunnelInitBfeStateChanges();
}
+
+NTSTATUS
+OvsTunnelAddFilterEx(HANDLE engineSession,
+ UINT32 filterPort,
+ UINT64 *filterID)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ status = OvsTunnelAddFilter(engineSession,
+ L"Datagram-Data OVS Filter (Inbound)",
+ L"address/port for UDP",
+ (USHORT)filterPort,
+ FWP_DIRECTION_INBOUND,
+ 0,
+ NULL,
+ &FWPM_LAYER_DATAGRAM_DATA_V4,
+ &OVS_TUNNEL_CALLOUT_V4,
+ filterID);
+ if (!NT_SUCCESS(status)) {
+ OVS_LOG_ERROR("Failed to add tunnel filter for port: %d, status: %x.",
+ filterPort, status);
+ } else {
+ OVS_LOG_INFO("Filter added, filter port: %d, filter ID: %d.",
+ filterPort, *filterID);
+ }
+
+ return status;
+}
+
+NTSTATUS
+OvsTunnelRemoveFilterEx(HANDLE engineSession,
+ UINT64 filterID)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ BOOLEAN error = TRUE;
+
+ do {
+ if (filterID == 0) {
+ OVS_LOG_INFO("No tunnel filter to remove.");
+ break;
+ }
+
+ status = FwpmFilterDeleteById(engineSession, filterID);
+ if (!NT_SUCCESS(status)) {
+ OVS_LOG_ERROR("Failed to remove tunnel with filter ID: %d,\
+ status: %x.", filterID, status);
+ break;
+ }
+ OVS_LOG_INFO("Filter removed, filter ID: %d.",
+ filterID);
+
+ error = FALSE;
+ } while (error);
+
+ return status;
+}
+
+NTSTATUS
+OvsTunnelFilterExecuteAction(HANDLE engineSession,
+ POVS_TUNFLT_REQUEST request)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ switch (request->operation)
+ {
+ case OVS_TUN_FILTER_CREATE:
+ status = OvsTunnelAddFilterEx(engineSession,
+ request->port,
+ request->filterID.addID);
+ break;
+ case OVS_TUN_FILTER_DELETE:
+ status = OvsTunnelRemoveFilterEx(engineSession,
+ request->filterID.delID);
+ break;
+ default:
+ status = STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ return status;
+}
+
+VOID
+OvsTunnelFilterRequestPopList(POVS_TUNFLT_REQUEST_LIST listRequests,
+ PLIST_ENTRY head,
+ UINT32 *count)
+{
+ NdisAcquireSpinLock(&listRequests->spinlock);
+
+ if (!IsListEmpty(&listRequests->head)) {
+ PLIST_ENTRY PrevEntry;
+ PLIST_ENTRY NextEntry;
+
+ NextEntry = listRequests->head.Flink;
+ PrevEntry = listRequests->head.Blink;
+
+ head->Flink = NextEntry;
+ NextEntry->Blink = head;
+
+ head->Blink = PrevEntry;
+ PrevEntry->Flink = head;
+
+ *count = listRequests->numEntries;
+
+ InitializeListHead(&listRequests->head);
+ listRequests->numEntries = 0;
+ }
+
+ NdisReleaseSpinLock(&listRequests->spinlock);
+}
+
+VOID
+OvsTunnelFilterRequestPush(POVS_TUNFLT_REQUEST_LIST listRequests,
+ POVS_TUNFLT_REQUEST request)
+{
+ NdisAcquireSpinLock(&listRequests->spinlock);
+
+ InsertTailList(&listRequests->head, &(request->entry));
+ listRequests->numEntries++;
+
+ NdisReleaseSpinLock(&listRequests->spinlock);
+}
+
+VOID
+OvsTunnelFilterThreadPush(POVS_TUNFLT_REQUEST request)
+{
+ UINT32 threadIndex;
+
+ threadIndex = request->port % OVS_TUNFLT_MAX_THREADS;
+
+ OvsTunnelFilterRequestPush(
+ &gTunnelThreadCtx[threadIndex].listRequests,
+ request);
+
+ KeSetEvent(&gTunnelThreadCtx[threadIndex].requestEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+}
+
+VOID
+OvsTunnelFilterCompleteRequest(PIRP irp,
+ PFNTunnelVportPendingOp callback,
+ PVOID context,
+ NTSTATUS status)
+{
+ UINT32 replyLen = 0;
+
+ if (callback) {
+ callback(context, status, &replyLen);
+ /* Release the context passed to the callback function. */
+ OvsFreeMemory(context);
+ }
+
+ if (irp) {
+ OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
+ }
+}
+
+VOID
+OvsTunnelFilterRequestListProcess(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
+{
+ POVS_TUNFLT_REQUEST request = NULL;
+ PLIST_ENTRY link = NULL;
+ PLIST_ENTRY next = NULL;
+ LIST_ENTRY head;
+ NTSTATUS status = STATUS_SUCCESS;
+ UINT32 count = 0;
+ BOOLEAN inTransaction = FALSE;
+ BOOLEAN error = TRUE;
+
+ do
+ {
+ if (!InterlockedCompareExchange(
+ (LONG volatile *)&threadCtx->listRequests.numEntries, 0, 0)) {
+ OVS_LOG_INFO("Nothing to do... request list is empty.");
+ break;
+ }
+
+ status = FwpmTransactionBegin(threadCtx->engineSession, 0);
+ if (!NT_SUCCESS(status)) {
+ OVS_LOG_ERROR("Failed to start transaction, status: %x.",
+ status);
+ break;
+ }
+ inTransaction = TRUE;
+
+ InitializeListHead(&head);
+ OvsTunnelFilterRequestPopList(&threadCtx->listRequests, &head, &count);
+
+ LIST_FORALL_SAFE(&head, link, next) {
+ request = CONTAINING_RECORD(link, OVS_TUNFLT_REQUEST, entry);
+
+ status = OvsTunnelFilterExecuteAction(threadCtx->engineSession,
+ request);
+ if (!NT_SUCCESS(status)) {
+ RemoveEntryList(&request->entry);
+ count--;
+
+ /* Complete the IRP with the failure status. */
+ OvsTunnelFilterCompleteRequest(request->irp,
+ request->callback,
+ request->context,
+ status);
+ OvsFreeMemory(request);
+ request = NULL;
+ } else {
+ error = FALSE;
+ }
+ }
+
+ if (error) {
+ /* No successful requests were made, so there is no point to commit
+ * the transaction. */
+ break;
+ }
+
+ status = FwpmTransactionCommit(threadCtx->engineSession);
+ if (!NT_SUCCESS(status)){
+ OVS_LOG_ERROR("Failed to commit transaction, status: %x.",
+ status);
+ break;
+ }
+
+ inTransaction = FALSE;
+ } while (inTransaction);
+
+ if (inTransaction) {
+ FwpmTransactionAbort(threadCtx->engineSession);
+ OVS_LOG_ERROR("Failed to execute request, status: %x.\
+ Transaction aborted.", status);
+ }
+
+ /* Complete the requests successfully executed with the transaction commit
+ * status. */
+ while (count) {
+ request = (POVS_TUNFLT_REQUEST)RemoveHeadList(&head);
+ count--;
+
+ OvsTunnelFilterCompleteRequest(request->irp,
+ request->callback,
+ request->context,
+ status);
+ OvsFreeMemory(request);
+ request = NULL;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * System thread routine that handles tunnel filter create/delete requests.
+ *----------------------------------------------------------------------------
+ */
+_Use_decl_annotations_
+VOID
+OvsTunnelFilterThreadProc(PVOID context)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ POVS_TUNFLT_THREAD_CONTEXT threadCtx = (POVS_TUNFLT_THREAD_CONTEXT)context;
+ PKEVENT eventArray[2] = { 0 };
+ ULONG count = 0;
+ BOOLEAN exit = FALSE;
+ BOOLEAN error = TRUE;
+
+ OVS_LOG_INFO("Starting OVS Tunnel system thread %d.",
+ threadCtx->threadID);
+
+ eventArray[0] = &threadCtx->stopEvent;
+ eventArray[1] = &threadCtx->requestEvent;
+ count = ARRAY_SIZE(eventArray);
+
+ do {
+ status = OvsTunnelFilterThreadInit(threadCtx);
+ if (!NT_SUCCESS(status)) {
+ OVS_LOG_ERROR("Failed to initialize tunnel filter thread %d.",
+ threadCtx->threadID);
+ break;
+ }
+
+ do {
+ status = KeWaitForMultipleObjects(count,
+ (PVOID)eventArray,
+ WaitAny,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL,
+ NULL);
+ switch (status) {
+ case STATUS_WAIT_1:
+ /* Start processing requests. */
+ OvsTunnelFilterRequestListProcess(threadCtx);
+ break;
+ default:
+ /* Finish processing the received requests and exit. */
+ OvsTunnelFilterRequestListProcess(threadCtx);
+ exit = TRUE;
+ break;
+ }
+ } while (!exit);
+
+ OvsTunnelFilterThreadUninit(threadCtx);
+
+ error = FALSE;
+ } while (error);
+
+ OVS_LOG_INFO("Terminating OVS Tunnel system thread %d.",
+ threadCtx->threadID);
+
+ PsTerminateSystemThread(STATUS_SUCCESS);
+};
+
+static NTSTATUS
+OvsTunnelFilterStartThreads()
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
+ gTunnelThreadCtx[index].threadID = index;
+
+ status = OvsTunnelFilterThreadStart(&gTunnelThreadCtx[index]);
+ if (!NT_SUCCESS(status)) {
+ OVS_LOG_ERROR("Failed to start tunnel filter thread %d.", index);
+ break;
+ }
+ }
+
+ return status;
+}
+
+static NTSTATUS
+OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ HANDLE threadHandle = NULL;
+ BOOLEAN error = TRUE;
+
+ do {
+ status = PsCreateSystemThread(&threadHandle,
+ SYNCHRONIZE,
+ NULL,
+ NULL,
+ NULL,
+ OvsTunnelFilterThreadProc,
+ threadCtx);
+ if (!NT_SUCCESS(status)) {
+ OVS_LOG_ERROR("Failed to create tunnel thread, status: %x.",
+ status);
+ break;
+ }
+
+ ObReferenceObjectByHandle(threadHandle,
+ SYNCHRONIZE,
+ NULL,
+ KernelMode,
+ &threadCtx->threadObject,
+ NULL);
+ ZwClose(threadHandle);
+ threadHandle = NULL;
+
+ error = FALSE;
+ } while (error);
+
+ return status;
+}
+
+static VOID
+OvsTunnelFilterStopThreads()
+{
+ /* Signal all threads to stop and ignore all subsequent requests. */
+ for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
+ OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], TRUE);
+ }
+
+ /* Wait for all threads to finish processing the requests. */
+ for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
+ OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], FALSE);
+ }
+}
+
+static VOID
+OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
+ BOOLEAN signalEvent)
+{
+ if (signalEvent) {
+ /* Signal stop thread event. */
+ OVS_LOG_INFO("Received stop event for OVS Tunnel system thread %d.",
+ threadCtx->threadID);
+ KeSetEvent(&threadCtx->stopEvent, IO_NO_INCREMENT, FALSE);
+ } else {
+ /* Wait for the tunnel thread to finish. */
+ KeWaitForSingleObject(threadCtx->threadObject,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ ObDereferenceObject(threadCtx->threadObject);
+ }
+}
+
+static NTSTATUS
+OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ BOOLEAN error = TRUE;
+
+ do {
+ /* Create thread's engine session object. */
+ status = OvsTunnelEngineOpen(&threadCtx->engineSession);
+ if (!NT_SUCCESS(status)) {
+ break;
+ }
+
+ NdisAllocateSpinLock(&threadCtx->listRequests.spinlock);
+
+ InitializeListHead(&threadCtx->listRequests.head);
+
+ KeInitializeEvent(&threadCtx->stopEvent,
+ NotificationEvent,
+ FALSE);
+
+ KeInitializeEvent(&threadCtx->requestEvent,
+ SynchronizationEvent,
+ FALSE);
+
+ error = FALSE;
+ } while (error);
+
+ return status;
+}
+
+static VOID
+OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
+{
+ if (threadCtx->engineSession) {
+ /* Close thread's FWPM session. */
+ OvsTunnelEngineClose(&threadCtx->engineSession);
+
+ NdisFreeSpinLock(&threadCtx->listRequests.spinlock);
+ }
+}
+
+NTSTATUS
+OvsTunnelFilterQueueRequest(PIRP irp,
+ UINT16 remotePort,
+ UINT64 *filterID,
+ OVS_TUNFLT_OPERATION operation,
+ PFNTunnelVportPendingOp callback,
+ PVOID tunnelContext)
+{
+ POVS_TUNFLT_REQUEST request = NULL;
+ NTSTATUS status = STATUS_PENDING;
+ BOOLEAN error = TRUE;
+ UINT64 timeout = 0;
+
+ do {
+ /* Verify if the stop event was signaled. */
+ if (STATUS_SUCCESS == KeWaitForSingleObject(
+ &gTunnelThreadCtx[0].stopEvent,
+ Executive,
+ KernelMode,
+ FALSE,
+ (LARGE_INTEGER *)&timeout)) {
+ /* The stop event is signaled. Completed the IRP with
+ * STATUS_CANCELLED. */
+ status = STATUS_CANCELLED;
+ break;
+ }
+
+ if (NULL == filterID) {
+ OVS_LOG_ERROR("Invalid request.");
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ request = (POVS_TUNFLT_REQUEST) OvsAllocateMemory(sizeof(*request));
+ if (NULL == request) {
+ OVS_LOG_ERROR("Failed to allocate list item.");
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ request->port = remotePort;
+ request->operation = operation;
+ switch (operation) {
+ case OVS_TUN_FILTER_CREATE:
+ request->filterID.addID = filterID;
+ break;
+ case OVS_TUN_FILTER_DELETE:
+ request->filterID.delID = *filterID;
+ break;
+ }
+ request->irp = irp;
+ request->callback = callback;
+ request->context = tunnelContext;
+
+ OvsTunnelFilterThreadPush(request);
+
+ error = FALSE;
+ } while (error);
+
+ if (error) {
+ OvsTunnelFilterCompleteRequest(irp, callback, tunnelContext, status);
+ if (request) {
+ OvsFreeMemory(request);
+ request = NULL;
+ }
+ }
+
+ return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function adds a new WFP filter for the received port and returns the
+ * ID of the created WFP filter.
+ *
+ * Note:
+ * All necessary calls to the WFP filtering engine must be running at IRQL =
+ * PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
+ * we register an OVS_TUN_FILTER_CREATE request that will be processed by
+ * the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsTunelFilterCreate(PIRP irp,
+ UINT16 filterPort,
+ UINT64 *filterID,
+ PFNTunnelVportPendingOp callback,
+ PVOID tunnelContext)
+{
+ return OvsTunnelFilterQueueRequest(irp,
+ filterPort,
+ filterID,
+ OVS_TUN_FILTER_CREATE,
+ callback,
+ tunnelContext);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function removes a WFP filter using the received filter ID.
+ *
+ * Note:
+ * All necessary calls to the WFP filtering engine must be running at IRQL =
+ * PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
+ * we register an OVS_TUN_FILTER_DELETE request that will be processed by
+ * the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsTunelFilterDelete(PIRP irp,
+ UINT64 filterID,
+ PFNTunnelVportPendingOp callback,
+ PVOID tunnelContext)
+{
+ return OvsTunnelFilterQueueRequest(irp,
+ 0,
+ &filterID,
+ OVS_TUN_FILTER_DELETE,
+ callback,
+ tunnelContext);
+}
#define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC 100
+/* Context structure used to pass back and forth information to the tunnel
+ * filter threads. */
+typedef struct _OVS_TUNFLT_INIT_CONTEXT {
+ POVS_SWITCH_CONTEXT switchContext;
+ UINT32 outputLength;
+ PVOID outputBuffer;
+ PVOID inputBuffer;
+ POVS_VPORT_ENTRY vport;
+ BOOLEAN hvSwitchPort;
+ BOOLEAN hvDelete;
+ BOOLEAN ovsDelete;
+} OVS_TUNFLT_INIT_CONTEXT, *POVS_TUNFLT_INIT_CONTEXT;
+
+
extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
static NDIS_STATUS InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
POVS_VPORT_ENTRY vport,
BOOLEAN newPort);
+static VOID OvsCleanupVportCommon(POVS_SWITCH_CONTEXT switchContext,
+ POVS_VPORT_ENTRY vport,
+ BOOLEAN hvSwitchPort,
+ BOOLEAN hvDelete,
+ BOOLEAN ovsDelete);
+static VOID OvsTunnelVportPendingInit(PVOID context,
+ NTSTATUS status,
+ UINT32 *replyLen);
+static VOID OvsTunnelVportPendingUninit(PVOID context,
+ NTSTATUS status,
+ UINT32 *replyLen);
+
/*
* Functions implemented in relaton to NDIS port manipulation.
* delete will delete the vport.
*/
if (vport) {
- OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
+ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
} else {
OVS_LOG_WARN("Vport not present.");
}
goto done;
}
+ vport->nicState = NdisSwitchNicStateUnknown;
+ vport->ovsState = OVS_STATE_PORT_CREATED;
+
portNo = vport->portNo;
if (vport->portType == NdisSwitchPortTypeExternal &&
vport->nicIndex != 0) {
- OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
+ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
}
- vport->nicState = NdisSwitchNicStateUnknown;
- vport->ovsState = OVS_STATE_PORT_CREATED;
NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
/* XXX if portNo != INVALID or always? */
* --------------------------------------------------------------------------
*/
NTSTATUS
-OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
+OvsInitTunnelVport(PVOID userContext,
+ POVS_VPORT_ENTRY vport,
OVS_VPORT_TYPE ovsType,
UINT16 dstPort)
{
NTSTATUS status = STATUS_SUCCESS;
+ POVS_USER_PARAMS_CONTEXT usrParamsCtx =
+ (POVS_USER_PARAMS_CONTEXT)userContext;
vport->isBridgeInternal = FALSE;
vport->ovsType = ovsType;
case OVS_VPORT_TYPE_GRE64:
break;
case OVS_VPORT_TYPE_VXLAN:
- status = OvsInitVxlanTunnel(vport, dstPort);
+ {
+ POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
+
+ tunnelContext = OvsAllocateMemory(sizeof(*tunnelContext));
+ if (tunnelContext == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+ tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
+ tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
+ tunnelContext->outputLength = usrParamsCtx->outputLength;
+ tunnelContext->vport = vport;
+
+ status = OvsInitVxlanTunnel(usrParamsCtx->irp,
+ vport,
+ dstPort,
+ OvsTunnelVportPendingInit,
+ (PVOID)tunnelContext);
break;
+ }
default:
ASSERT(0);
}
switch(vport->ovsType) {
case OVS_VPORT_TYPE_VXLAN:
- ASSERT(switchContext->vxlanVport == NULL);
switchContext->vxlanVport = vport;
switchContext->numNonHvVports++;
break;
return STATUS_SUCCESS;
}
+static VOID
+OvsCleanupVportCommon(POVS_SWITCH_CONTEXT switchContext,
+ POVS_VPORT_ENTRY vport,
+ BOOLEAN hvSwitchPort,
+ BOOLEAN hvDelete,
+ BOOLEAN ovsDelete)
+{
+ BOOLEAN deletedOnOvs = FALSE;
+ BOOLEAN deletedOnHv = FALSE;
+
+ /*
+ * 'hvDelete' == TRUE indicates that the port should be removed from the
+ * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
+ * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
+ *
+ * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
+ */
+ if (vport->isPresentOnHv == TRUE) {
+ deletedOnHv = TRUE;
+ }
+ if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
+ deletedOnOvs = TRUE;
+ }
+
+ if (hvDelete && !deletedOnHv) {
+ vport->isPresentOnHv = TRUE;
+
+ /* Remove the port from the relevant lists. */
+ RemoveEntryList(&vport->portIdLink);
+ InitializeListHead(&vport->portIdLink);
+ deletedOnHv = TRUE;
+ }
+ if (ovsDelete && !deletedOnOvs) {
+ vport->portNo = OVS_DPPORT_NUMBER_INVALID;
+ vport->ovsName[0] = '\0';
+
+ /* Remove the port from the relevant lists. */
+ RemoveEntryList(&vport->ovsNameLink);
+ InitializeListHead(&vport->ovsNameLink);
+ RemoveEntryList(&vport->portNoLink);
+ InitializeListHead(&vport->portNoLink);
+ deletedOnOvs = TRUE;
+ }
+
+ /*
+ * Deallocate the port if it has been deleted on the Hyper-V switch as well
+ * as OVS userspace.
+ */
+ if (deletedOnHv && deletedOnOvs) {
+ if (hvSwitchPort) {
+ switchContext->numHvVports--;
+ }
+ else {
+ switchContext->numNonHvVports--;
+ }
+ OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
+ }
+}
/*
* --------------------------------------------------------------------------
* port being removed from OVS userspace.
* --------------------------------------------------------------------------
*/
-VOID
-OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
+NTSTATUS
+OvsRemoveAndDeleteVport(PVOID usrParamsContext,
+ POVS_SWITCH_CONTEXT switchContext,
POVS_VPORT_ENTRY vport,
BOOLEAN hvDelete,
- BOOLEAN ovsDelete,
- BOOLEAN *vportDeallocated)
+ BOOLEAN ovsDelete)
{
+ NTSTATUS status = STATUS_SUCCESS;
+ POVS_USER_PARAMS_CONTEXT usrParamsCtx =
+ (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
BOOLEAN hvSwitchPort = FALSE;
- BOOLEAN deletedOnOvs = FALSE, deletedOnHv = FALSE;
-
- if (vportDeallocated) {
- *vportDeallocated = FALSE;
- }
if (vport->isExternal) {
if (vport->nicIndex == 0) {
switchContext->virtualExternalPortId = 0;
switchContext->virtualExternalVport = NULL;
OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
- if (vportDeallocated) {
- *vportDeallocated = TRUE;
- }
- return;
+ return STATUS_SUCCESS;
} else {
ASSERT(switchContext->numPhysicalNics);
switchContext->numPhysicalNics--;
}
break;
case OVS_VPORT_TYPE_VXLAN:
- OvsCleanupVxlanTunnel(vport);
+ {
+ POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
+ PIRP irp = NULL;
+
+ tunnelContext = OvsAllocateMemory(sizeof(*tunnelContext));
+ if (tunnelContext == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+ RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
+
+ tunnelContext->switchContext = switchContext;
+ tunnelContext->hvSwitchPort = hvSwitchPort;
+ tunnelContext->hvDelete = hvDelete;
+ tunnelContext->ovsDelete = ovsDelete;
+ tunnelContext->vport = vport;
+
+ if (usrParamsCtx) {
+ tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
+ tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
+ tunnelContext->outputLength = usrParamsCtx->outputLength;
+ irp = usrParamsCtx->irp;
+ }
+
+ status = OvsCleanupVxlanTunnel(irp,
+ vport,
+ OvsTunnelVportPendingUninit,
+ tunnelContext);
+
switchContext->vxlanVport = NULL;
break;
+ }
case OVS_VPORT_TYPE_GRE:
case OVS_VPORT_TYPE_GRE64:
break;
break;
}
- /*
- * 'hvDelete' == TRUE indicates that the port should be removed from the
- * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
- * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
- *
- * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
- */
- if (vport->isPresentOnHv == TRUE) {
- deletedOnHv = TRUE;
- }
- if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
- deletedOnOvs = TRUE;
- }
-
- if (hvDelete && !deletedOnHv) {
- vport->isPresentOnHv = TRUE;
-
- /* Remove the port from the relevant lists. */
- RemoveEntryList(&vport->portIdLink);
- InitializeListHead(&vport->portIdLink);
- deletedOnHv = TRUE;
+ if (STATUS_SUCCESS == status) {
+ OvsCleanupVportCommon(switchContext,
+ vport,
+ hvSwitchPort,
+ hvDelete,
+ ovsDelete);
}
- if (ovsDelete && !deletedOnOvs) {
- vport->portNo = OVS_DPPORT_NUMBER_INVALID;
- vport->ovsName[0] = '\0';
- /* Remove the port from the relevant lists. */
- RemoveEntryList(&vport->ovsNameLink);
- InitializeListHead(&vport->ovsNameLink);
- RemoveEntryList(&vport->portNoLink);
- InitializeListHead(&vport->portNoLink);
- deletedOnOvs = TRUE;
- }
-
- /*
- * Deallocate the port if it has been deleted on the Hyper-V switch as well
- * as OVS userspace.
- */
- if (deletedOnHv && deletedOnOvs) {
- if (hvSwitchPort) {
- switchContext->numHvVports--;
- } else {
- switchContext->numNonHvVports--;
- }
- OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
- if (vportDeallocated) {
- *vportDeallocated = TRUE;
- }
- }
+ return status;
}
NDIS_STATUS
LIST_FORALL_SAFE(head, link, next) {
POVS_VPORT_ENTRY vport;
vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
- OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
+ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
}
}
/*
* 'portIdHashArray'.
*/
if (switchContext->virtualExternalVport) {
- OvsRemoveAndDeleteVport(switchContext,
- (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE,
- NULL);
+ OvsRemoveAndDeleteVport(NULL, switchContext,
+ (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
}
for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
(vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
vport->isBridgeInternal) || vport->isPresentOnHv == TRUE);
- OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
+ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
}
}
/*
* --------------------------------------------------------------------------
- * Command Handler for 'OVS_VPORT_CMD_NEW'.
+ * Command Handler for 'OVS_VPORT_CMD_GET'.
*
* The function handles the initial call to setup the dump state, as well as
* subsequent calls to continue dumping data.
} else {
ASSERT(OvsIsTunnelVportType(portType) ||
(portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
- ASSERT(OvsGetTunnelVport(gOvsSwitchContext, portType) == NULL ||
- !OvsIsTunnelVportType(portType));
vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
if (vport == NULL) {
vportAllocated = TRUE;
if (OvsIsTunnelVportType(portType)) {
- status = OvsInitTunnelVport(vport, portType, VXLAN_UDP_PORT);
+ UINT16 udpPortDest = VXLAN_UDP_PORT;
+ PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
+ OVS_TUNNEL_ATTR_DST_PORT);
+ if (attr) {
+ udpPortDest = NlAttrGetU16(attr);
+ }
+
+ status = OvsInitTunnelVport(usrParamsCtx,
+ vport,
+ portType,
+ udpPortDest);
+
nlError = NlMapStatusToNlErr(status);
} else {
OvsInitBridgeInternalVport(vport);
}
+
vportInitialized = TRUE;
if (nlError == NL_ERROR_SUCCESS) {
* corresponding hyper-v switch part.
*/
vport->isPresentOnHv = TRUE;
+ } else {
+ goto Cleanup;
}
}
Cleanup:
NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
- if (nlError != NL_ERROR_SUCCESS) {
+ if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
usrParamsCtx->outputBuffer;
if (vport && vportAllocated == TRUE) {
if (vportInitialized == TRUE) {
if (OvsIsTunnelVportType(portType)) {
- OvsCleanupVxlanTunnel(vport);
+ OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
}
}
OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
*replyLen = msgError->nlMsg.nlmsgLen;
}
- return STATUS_SUCCESS;
+ return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
}
usrParamsCtx->outputLength,
gOvsSwitchContext->dpNo);
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+
/*
* Mark the port as deleted from OVS userspace. If the port does not exist
* on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
*/
- OvsRemoveAndDeleteVport(gOvsSwitchContext, vport, FALSE, TRUE, NULL);
-
- *replyLen = msgOut->nlMsg.nlmsgLen;
+ status = OvsRemoveAndDeleteVport(usrParamsCtx,
+ gOvsSwitchContext,
+ vport,
+ FALSE,
+ TRUE);
+ if (status) {
+ nlError = NlMapStatusToNlErr(status);
+ }
Cleanup:
NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
- if (nlError != NL_ERROR_SUCCESS) {
+ if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
usrParamsCtx->outputBuffer;
*replyLen = msgError->nlMsg.nlmsgLen;
}
- return STATUS_SUCCESS;
+ return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
+}
+
+static VOID
+OvsTunnelVportPendingUninit(PVOID context,
+ NTSTATUS status,
+ UINT32 *replyLen)
+{
+ POVS_TUNFLT_INIT_CONTEXT tunnelContext =
+ (POVS_TUNFLT_INIT_CONTEXT) context;
+ POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
+ POVS_VPORT_ENTRY vport = tunnelContext->vport;
+ POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
+ POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
+ NL_ERROR nlError = NlMapStatusToNlErr(status);
+ LOCK_STATE_EX lockState;
+
+ NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
+
+ if (msgIn && msgOut) {
+ /* Check the received status to reply to the caller. */
+ if (STATUS_SUCCESS == status) {
+ OvsCreateMsgFromVport(vport,
+ msgIn,
+ msgOut,
+ tunnelContext->outputLength,
+ switchContext->dpNo);
+
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+ } else {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
+
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ }
+ }
+
+ OvsCleanupVportCommon(switchContext,
+ vport,
+ tunnelContext->hvSwitchPort,
+ tunnelContext->hvDelete,
+ tunnelContext->ovsDelete);
+
+ NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
+}
+
+static VOID
+OvsTunnelVportPendingInit(PVOID context,
+ NTSTATUS status,
+ UINT32 *replyLen)
+{
+ POVS_TUNFLT_INIT_CONTEXT tunnelContext =
+ (POVS_TUNFLT_INIT_CONTEXT) context;
+ POVS_VPORT_ENTRY vport = tunnelContext->vport;
+ POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
+ POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
+ PCHAR portName;
+ ULONG portNameLen = 0;
+ UINT32 portType = 0;
+ NL_ERROR nlError = NL_ERROR_SUCCESS;
+ BOOLEAN error = TRUE;
+
+ do {
+ if (!NT_SUCCESS(status)) {
+ nlError = NlMapStatusToNlErr(status);
+ break;
+ }
+
+ static const NL_POLICY ovsVportPolicy[] = {
+ [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
+ [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
+ [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
+ .optional = FALSE },
+ [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
+ .optional = FALSE },
+ [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
+ };
+
+ PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
+
+ /* input buffer has been validated while validating write dev op. */
+ ASSERT(msgIn != NULL);
+
+ /* Output buffer has been validated while validating transact dev op. */
+ ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
+
+ if (!NlAttrParse((PNL_MSG_HDR)msgIn,
+ NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
+ NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
+ ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
+ nlError = NL_ERROR_INVAL;
+ break;
+ }
+
+ portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
+ portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
+ portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
+
+ if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
+ nlError = NL_ERROR_EXIST;
+ break;
+ }
+
+ vport->ovsState = OVS_STATE_CONNECTED;
+ vport->nicState = NdisSwitchNicStateConnected;
+
+ /*
+ * Allow the vport to be deleted, because there is no
+ * corresponding hyper-v switch part.
+ */
+ vport->isPresentOnHv = TRUE;
+
+ if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
+ /*
+ * XXX: when we implement the limit for OVS port number to be
+ * MAXUINT16, we'll need to check the port number received from the
+ * userspace.
+ */
+ vport->portNo =
+ NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
+ } else {
+ vport->portNo =
+ OvsComputeVportNo(gOvsSwitchContext);
+ if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
+ nlError = NL_ERROR_NOMEM;
+ break;
+ }
+ }
+
+ /* The ovs port name must be uninitialized. */
+ ASSERT(vport->ovsName[0] == '\0');
+ ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
+
+ RtlCopyMemory(vport->ovsName, portName, portNameLen);
+ /* if we don't have options, then vport->portOptions will be NULL */
+ vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
+
+ /*
+ * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
+ * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
+ * it means we have an array of pids, instead of a single pid.
+ * ATM we assume we have one pid only.
+ */
+ vport->upcallPid =
+ NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
+
+ status = InitOvsVportCommon(gOvsSwitchContext, vport);
+ ASSERT(status == STATUS_SUCCESS);
+
+ OvsCreateMsgFromVport(vport,
+ msgIn,
+ msgOut,
+ tunnelContext->outputLength,
+ gOvsSwitchContext->dpNo);
+
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+
+ error = FALSE;
+ } while (error);
+
+ if (error) {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
+
+ OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
+ OvsFreeMemory(vport);
+
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ }
}