[ovs-dev] [PATCH v8 1/3] datapath-windows: Support for custom VXLAN tunnel port
Ben Pfaff
blp at nicira.com
Wed May 27 16:37:47 UTC 2015
Unfortunately I'm still seeing a patch failure against current master:
Applying: datapath-windows: Support for custom VXLAN tunnel port
/home/blp/nicira/ovs/.git/rebase-apply/patch:919: trailing whitespace.
/home/blp/nicira/ovs/.git/rebase-apply/patch:1959: trailing whitespace.
NTSTATUS OvsRemoveAndDeleteVport(PVOID usrParamsCtx,
/home/blp/nicira/ovs/.git/rebase-apply/patch:2018: trailing whitespace.
*
error: patch failed: datapath-windows/ovsext/Datapath.c:925
error: datapath-windows/ovsext/Datapath.c: patch does not apply
Patch failed at 0001 datapath-windows: Support for custom VXLAN tunnel port
The copy of the patch that failed is found in:
/home/blp/nicira/ovs/.git/rebase-apply/patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
On Wed, May 27, 2015 at 04:31:01PM +0000, Sorin Vinturis wrote:
> The kernel datapath supports only port 4789 for VXLAN tunnel creation.
> Added support in order to allow for the VXLAN tunnel port to be
> configurable to any port number set by the userspace.
>
> The patch also checks to see if an existing WFP filter, for the
> necessary UDP tunnel port, is already created before adding a new one.
> This is a double check, because currently the userspace also verifies
> this, but it is necessary to avoid future issues.
>
> Custom VXLAN tunnel port requires the addition of a new WFP filter
> with the new UDP tunnel port. The creation of a new WFP filter is
> triggered in OvsInitVxlanTunnel function and the removal of the WFP
> filter in OvsCleanupVxlanTunnel function.
> But the latter functions are running at IRQL = DISPATCH_LEVEL, due
> to the NDIS RW lock acquisition, and all WFP calls must be running at
> IRQL = PASSIVE_LEVEL. This is why I have created a system thread which
> records all filter addition/removal requests into a list for later
> processing by the system thread. The ThreadStart routine processes all
> received requests at IRQL = PASSIVE_LEVEL, which is the required IRQL
> for the necessary WFP calls for adding/removal of the WFP filters.
>
> The WFP filter for the default VXLAN port 4789 is not added anymore at
> filter attach. All WFP filters for the tunnel ports are added when the
> tunnel ports are initialized and are removed at cleanup. WFP operation
> status is then reported to userspace.
>
> It is necessary that OvsTunnelFilterUninitialize function is called
> after OvsClearAllSwitchVports in order to allow for the added WFP
> filters to be removed. OvsTunnelFilterUninitialize function closes the
> global engine handle used by most of the WFP calls, including filter
> removal.
>
> Signed-off-by: Sorin Vinturis <svinturis at cloudbasesolutions.com>
> Reported-by: Alin Gabriel Serdean <aserdean at cloudbasesolutions.com>
> Reported-at: https://github.com/openvswitch/ovs-issues/issues/66
> Acked-by: Nithin Raju <nithin at vmware.com>
> ---
> v8: Added acked.
> ---
> datapath-windows/ovsext/Datapath.c | 8 +-
> datapath-windows/ovsext/Netlink/Netlink.c | 2 +
> datapath-windows/ovsext/Netlink/NetlinkError.h | 19 +-
> datapath-windows/ovsext/Switch.c | 2 +-
> datapath-windows/ovsext/Tunnel.h | 6 -
> datapath-windows/ovsext/TunnelFilter.c | 881 ++++++++++++++++++++-----
> datapath-windows/ovsext/TunnelIntf.h | 15 +
> datapath-windows/ovsext/Vport.c | 445 ++++++++++---
> datapath-windows/ovsext/Vport.h | 12 +-
> datapath-windows/ovsext/Vxlan.c | 102 ++-
> datapath-windows/ovsext/Vxlan.h | 13 +-
> 11 files changed, 1226 insertions(+), 279 deletions(-)
>
> diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c
> index 7646f0a..d482825 100644
> --- a/datapath-windows/ovsext/Datapath.c
> +++ b/datapath-windows/ovsext/Datapath.c
> @@ -670,7 +670,6 @@ OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
> return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
> }
>
> -
> /*
> * --------------------------------------------------------------------------
> * IOCTL function handler for the device.
> @@ -925,9 +924,12 @@ exit:
> KeMemoryBarrier();
> instance->inUse = 0;
>
> - /* Should not complete a pending IRP unless proceesing is completed */
> + /* Should not complete a pending IRP unless proceesing is completed. */
> if (status == STATUS_PENDING) {
> - return status;
> + /* STATUS_PENDING is returned by the NL handler when the request is
> + * to be processed later, so we mark the IRP as pending and complete
> + * it in another thread when the request is processed. */
> + IoMarkIrpPending(irp);
> return status;
> }
> return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
> }
> diff --git a/datapath-windows/ovsext/Netlink/Netlink.c b/datapath-windows/ovsext/Netlink/Netlink.c
> index 589e3a1..a62d760 100644
> --- a/datapath-windows/ovsext/Netlink/Netlink.c
> +++ b/datapath-windows/ovsext/Netlink/Netlink.c
> @@ -112,6 +112,8 @@ NlBuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgError, UINT errorCode)
> {
> NL_BUFFER nlBuffer;
>
> + ASSERT(errorCode != NL_ERROR_PENDING);
> +
> NlBufInit(&nlBuffer, (PCHAR)msgError, sizeof *msgError);
> NlFillNlHdr(&nlBuffer, NLMSG_ERROR, 0,
> msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid);
> diff --git a/datapath-windows/ovsext/Netlink/NetlinkError.h b/datapath-windows/ovsext/Netlink/NetlinkError.h
> index 53c935f..eefa89e 100644
> --- a/datapath-windows/ovsext/Netlink/NetlinkError.h
> +++ b/datapath-windows/ovsext/Netlink/NetlinkError.h
> @@ -195,14 +195,16 @@ typedef enum _NL_ERROR_
> NL_ERROR_TIMEDOUT = ((ULONG)-138),
> /* The given text file is busy */
> NL_ERROR_TXTBSY = ((ULONG)-139),
> - /*the operation would block */
> + /* The operation would block */
> NL_ERROR_WOULDBLOCK = ((ULONG)-140),
> + /* The operation is not finished */
> + NL_ERROR_PENDING = ((ULONG)-141),
> } NL_ERROR;
>
> static __inline
> NlMapStatusToNlErr(NTSTATUS status)
> {
> - NL_ERROR ret = NL_ERROR_INVAL;
> + NL_ERROR ret;
>
> switch (status)
> {
> @@ -215,7 +217,20 @@ NlMapStatusToNlErr(NTSTATUS status)
> case STATUS_SUCCESS:
> ret = NL_ERROR_SUCCESS;
> break;
> + case STATUS_PENDING:
> + ret = NL_ERROR_PENDING;
> + break;
> + case STATUS_CANCELLED:
> + ret = NL_ERROR_CANCELED;
> + break;
> + case STATUS_INVALID_PARAMETER:
> + ret = NL_ERROR_INVAL;
> + break;
> + case STATUS_OBJECT_NAME_EXISTS:
> + ret = NL_ERROR_EXIST;
> + break;
> default:
> + ret = NL_ERROR_OTHER;
> break;
> }
>
> diff --git a/datapath-windows/ovsext/Switch.c b/datapath-windows/ovsext/Switch.c
> index 032153d..416bcc0 100644
> --- a/datapath-windows/ovsext/Switch.c
> +++ b/datapath-windows/ovsext/Switch.c
> @@ -263,8 +263,8 @@ OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext)
> if (switchContext)
> {
> dpNo = switchContext->dpNo;
> - OvsUninitTunnelFilter(gOvsExtDriverObject);
> OvsClearAllSwitchVports(switchContext);
> + OvsUninitTunnelFilter(gOvsExtDriverObject);
> OvsUninitSwitchContext(switchContext);
> }
> OVS_LOG_TRACE("Exit: deleted switch %p dpNo: %d", switchContext, dpNo);
> diff --git a/datapath-windows/ovsext/Tunnel.h b/datapath-windows/ovsext/Tunnel.h
> index 2978bb3..2c45e35 100644
> --- a/datapath-windows/ovsext/Tunnel.h
> +++ b/datapath-windows/ovsext/Tunnel.h
> @@ -32,12 +32,6 @@ typedef struct OVS_TUNNEL_PENDED_PACKET_
> FWPS_CLASSIFY_OUT *classifyOut;
> } OVS_TUNNEL_PENDED_PACKET;
>
> -/* Shared global data. */
> -
> -extern UINT16 configNewDestPort;
> -
> -extern UINT32 gCalloutIdV4;
> -
> //
> // Shared function prototypes
> //
> diff --git a/datapath-windows/ovsext/TunnelFilter.c b/datapath-windows/ovsext/TunnelFilter.c
> index c2186eb..00b3c0e 100644
> --- a/datapath-windows/ovsext/TunnelFilter.c
> +++ b/datapath-windows/ovsext/TunnelFilter.c
> @@ -48,27 +48,24 @@
> /* 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,
> @@ -106,41 +103,108 @@ DEFINE_GUID(
> );
>
> /*
> - * 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);
> }
>
> @@ -148,23 +212,23 @@ OvsTunnelEngineOpen(HANDLE *handle)
> }
>
> 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;
> }
> @@ -180,7 +244,7 @@ OvsTunnelAddSystemProvider(HANDLE handle)
> */
> provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
>
> - status = FwpmProviderAdd(handle,
> + status = FwpmProviderAdd(engineSession,
> &provider,
> NULL);
> if (!NT_SUCCESS(status)) {
> @@ -191,7 +255,7 @@ OvsTunnelAddSystemProvider(HANDLE handle)
> }
> }
>
> - status = FwpmTransactionCommit(handle);
> + status = FwpmTransactionCommit(engineSession);
> if (!NT_SUCCESS(status)) {
> break;
> }
> @@ -200,30 +264,30 @@ OvsTunnelAddSystemProvider(HANDLE handle)
> } 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;
> }
> @@ -232,29 +296,30 @@ OvsTunnelRemoveSystemProvider(HANDLE handle)
> } 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;
> @@ -284,64 +349,18 @@ OvsTunnelAddFilter(PWSTR filterName,
>
> filter.numFilterConditions = conditionIndex;
>
> - status = FwpmFilterAdd(gEngineHandle,
> + status = FwpmFilterAdd(engineSession,
> &filter,
> NULL,
> - NULL);
> -
> - 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;
> - }
> + filterID);
>
> - 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
> @@ -368,10 +387,7 @@ OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
> 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;
> }
> @@ -384,24 +400,11 @@ OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
> 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)){
> @@ -416,24 +419,16 @@ Exit:
>
> /*
> * --------------------------------------------------------------------------
> - * 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)) {
> @@ -476,22 +471,17 @@ Exit:
> 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
> @@ -499,16 +489,22 @@ OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
> {
> 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");
> @@ -522,21 +518,31 @@ OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
> &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;
> @@ -546,16 +552,16 @@ VOID NTAPI
> 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);
> }
> }
>
> @@ -599,16 +605,16 @@ VOID
> 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();
> }
> @@ -617,13 +623,13 @@ OvsRegisterSystemProvider(PVOID deviceObject)
>
> 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();
> }
> @@ -711,3 +717,566 @@ VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject)
> 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);
> +}
> diff --git a/datapath-windows/ovsext/TunnelIntf.h b/datapath-windows/ovsext/TunnelIntf.h
> index 82a5145..eb8c1d5 100644
> --- a/datapath-windows/ovsext/TunnelIntf.h
> +++ b/datapath-windows/ovsext/TunnelIntf.h
> @@ -17,6 +17,10 @@
> #ifndef __TUNNEL_INTF_H_
> #define __TUNNEL_INTF_H_ 1
>
> +typedef VOID(*PFNTunnelVportPendingOp)(PVOID context,
> + NTSTATUS status,
> + UINT32 *replyLen);
> +
> /* Tunnel callout driver load/unload functions */
> NTSTATUS OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject);
>
> @@ -26,4 +30,15 @@ VOID OvsRegisterSystemProvider(PVOID deviceObject);
>
> VOID OvsUnregisterSystemProvider();
>
> +NTSTATUS OvsTunelFilterCreate(PIRP irp,
> + UINT16 filterPort,
> + UINT64 *filterID,
> + PFNTunnelVportPendingOp callback,
> + PVOID context);
> +
> +NTSTATUS OvsTunelFilterDelete(PIRP irp,
> + UINT64 filterID,
> + PFNTunnelVportPendingOp callback,
> + PVOID context);
> +
> #endif /* __TUNNEL_INTF_H_ */
> diff --git a/datapath-windows/ovsext/Vport.c b/datapath-windows/ovsext/Vport.c
> index 1423ace..66f9189 100644
> --- a/datapath-windows/ovsext/Vport.c
> +++ b/datapath-windows/ovsext/Vport.c
> @@ -47,6 +47,20 @@
>
> #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,
> @@ -69,6 +83,18 @@ static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
> 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.
> @@ -246,7 +272,7 @@ HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
> * 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.");
> }
> @@ -534,13 +560,14 @@ HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
> 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? */
> @@ -850,11 +877,14 @@ OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport,
> * --------------------------------------------------------------------------
> */
> 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;
> @@ -865,8 +895,26 @@ OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
> 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);
> }
> @@ -1012,7 +1060,6 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
>
> switch(vport->ovsType) {
> case OVS_VPORT_TYPE_VXLAN:
> - ASSERT(switchContext->vxlanVport == NULL);
> switchContext->vxlanVport = vport;
> switchContext->numNonHvVports++;
> break;
> @@ -1043,6 +1090,64 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
> 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);
> + }
> +}
>
> /*
> * --------------------------------------------------------------------------
> @@ -1055,19 +1160,17 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
> * 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) {
> @@ -1075,10 +1178,7 @@ OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
> 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--;
> @@ -1096,9 +1196,38 @@ OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
> }
> 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;
> @@ -1108,55 +1237,15 @@ OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
> 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
> @@ -1294,7 +1383,7 @@ OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
> 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);
> }
> }
> /*
> @@ -1302,9 +1391,8 @@ OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
> * '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++) {
> @@ -1317,7 +1405,7 @@ OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
> 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);
> }
> }
>
> @@ -1895,7 +1983,7 @@ Cleanup:
>
> /*
> * --------------------------------------------------------------------------
> - * 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.
> @@ -2020,8 +2108,6 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> } 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) {
> @@ -2031,11 +2117,23 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> 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) {
> @@ -2047,6 +2145,8 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> * corresponding hyper-v switch part.
> */
> vport->isPresentOnHv = TRUE;
> + } else {
> + goto Cleanup;
> }
> }
>
> @@ -2106,14 +2206,14 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> 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);
> @@ -2123,7 +2223,7 @@ Cleanup:
> *replyLen = msgError->nlMsg.nlmsgLen;
> }
>
> - return STATUS_SUCCESS;
> + return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
> }
>
>
> @@ -2297,18 +2397,25 @@ OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
> 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;
>
> @@ -2316,5 +2423,173 @@ Cleanup:
> *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;
> + }
> }
> diff --git a/datapath-windows/ovsext/Vport.h b/datapath-windows/ovsext/Vport.h
> index 348fbfd..4cfda30 100644
> --- a/datapath-windows/ovsext/Vport.h
> +++ b/datapath-windows/ovsext/Vport.h
> @@ -207,15 +207,15 @@ OvsIsBridgeInternalVport(POVS_VPORT_ENTRY vport)
> return vport->isBridgeInternal == TRUE;
> }
>
> -VOID OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
> - POVS_VPORT_ENTRY vport,
> - BOOLEAN hvDelete, BOOLEAN ovsDelete,
> - BOOLEAN *vportDeallocated);
> +NTSTATUS OvsRemoveAndDeleteVport(PVOID usrParamsCtx,
> + POVS_SWITCH_CONTEXT switchContext,
> + POVS_VPORT_ENTRY vport,
> + BOOLEAN hvDelete, BOOLEAN ovsDelete);
>
> NDIS_STATUS InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
> POVS_VPORT_ENTRY vport);
> -NTSTATUS OvsInitTunnelVport(POVS_VPORT_ENTRY vport, OVS_VPORT_TYPE ovsType,
> - UINT16 dstport);
> +NTSTATUS OvsInitTunnelVport(PVOID usrParamsCtx, POVS_VPORT_ENTRY vport,
> + OVS_VPORT_TYPE ovsType, UINT16 dstport);
> NTSTATUS OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport);
>
> POVS_VPORT_ENTRY OvsAllocateVport(VOID);
> diff --git a/datapath-windows/ovsext/Vxlan.c b/datapath-windows/ovsext/Vxlan.c
> index 8c57185..bf16e35 100644
> --- a/datapath-windows/ovsext/Vxlan.c
> +++ b/datapath-windows/ovsext/Vxlan.c
> @@ -50,14 +50,57 @@
> extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
>
> /*
> + *----------------------------------------------------------------------------
> + * This function verifies if the VXLAN tunnel already exists, in order to
> + * avoid sending a duplicate request to the WFP base filtering engine.
> + *----------------------------------------------------------------------------
> + */
> +static BOOLEAN
> +OvsIsTunnelFilterCreated(POVS_SWITCH_CONTEXT switchContext,
> + UINT16 udpPortDest)
> +{
> + for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
> + PLIST_ENTRY head, link, next;
> +
> + head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
> + LIST_FORALL_SAFE(head, link, next) {
> + POVS_VPORT_ENTRY vport = NULL;
> + POVS_VXLAN_VPORT vxlanPort = NULL;
> + vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
> + vxlanPort = (POVS_VXLAN_VPORT)vport->priv;
> + if (vxlanPort) {
> + if ((udpPortDest == vxlanPort->dstPort)) {
> + /* The VXLAN tunnel was already created. */
> + return TRUE;
> + }
> + }
> + }
> + }
> +
> + return FALSE;
> +}
> +
> +/*
> + *----------------------------------------------------------------------------
> + * This function allocates and initializes the OVS_VXLAN_VPORT. The function
> + * also creates a WFP tunnel filter for the necessary destination port. The
> + * tunnel filter create request is passed to the tunnel filter threads that
> + * will complete the request at a later time when IRQL is lowered to
> + * PASSIVE_LEVEL.
> + *
> * udpDestPort: the vxlan is set as payload to a udp frame. If the destination
> * port of an udp frame is udpDestPort, we understand it to be vxlan.
> + *----------------------------------------------------------------------------
> */
> NTSTATUS
> -OvsInitVxlanTunnel(POVS_VPORT_ENTRY vport,
> - UINT16 udpDestPort)
> +OvsInitVxlanTunnel(PIRP irp,
> + POVS_VPORT_ENTRY vport,
> + UINT16 udpDestPort,
> + PFNTunnelVportPendingOp callback,
> + PVOID tunnelContext)
> {
> - POVS_VXLAN_VPORT vxlanPort;
> + NTSTATUS status = STATUS_SUCCESS;
> + POVS_VXLAN_VPORT vxlanPort = NULL;
>
> vxlanPort = OvsAllocateMemoryWithTag(sizeof (*vxlanPort),
> OVS_VXLAN_POOL_TAG);
> @@ -67,28 +110,56 @@ OvsInitVxlanTunnel(POVS_VPORT_ENTRY vport,
>
> RtlZeroMemory(vxlanPort, sizeof(*vxlanPort));
> vxlanPort->dstPort = udpDestPort;
> - /*
> - * since we are installing the WFP filter before the port is created
> - * We need to check if it is the same number
> - * XXX should be removed later
> - */
> - ASSERT(vxlanPort->dstPort == VXLAN_UDP_PORT);
> vport->priv = (PVOID)vxlanPort;
>
> - return STATUS_SUCCESS;
> -}
> + if (!OvsIsTunnelFilterCreated(gOvsSwitchContext, udpDestPort)) {
> + status = OvsTunelFilterCreate(irp,
> + udpDestPort,
> + &vxlanPort->filterID,
> + callback,
> + tunnelContext);
> + } else {
> + status = STATUS_OBJECT_NAME_EXISTS;
> + }
>
> + return status;
> +}
>
> -VOID
> -OvsCleanupVxlanTunnel(POVS_VPORT_ENTRY vport)
> +/*
> + *----------------------------------------------------------------------------
> + * This function releases the OVS_VXLAN_VPORT. The function also deletes the
> + * WFP tunnel filter previously created. The tunnel filter delete request is
> + * passed to the tunnel filter threads that will complete the request at a
> + * later time when IRQL is lowered to PASSIVE_LEVEL.
> + *----------------------------------------------------------------------------
> + */
> +NTSTATUS
> +OvsCleanupVxlanTunnel(PIRP irp,
> + POVS_VPORT_ENTRY vport,
> + PFNTunnelVportPendingOp callback,
> + PVOID tunnelContext)
> {
> + NTSTATUS status = STATUS_SUCCESS;
> + POVS_VXLAN_VPORT vxlanPort = NULL;
> +
> if (vport->ovsType != OVS_VPORT_TYPE_VXLAN ||
> vport->priv == NULL) {
> - return;
> + return STATUS_SUCCESS;
> + }
> +
> + vxlanPort = (POVS_VXLAN_VPORT)vport->priv;
> +
> + if (vxlanPort->filterID != 0) {
> + status = OvsTunelFilterDelete(irp,
> + vxlanPort->filterID,
> + callback,
> + tunnelContext);
> }
>
> OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
> vport->priv = NULL;
> +
> + return status;
> }
>
>
> @@ -475,9 +546,6 @@ OvsSlowPathDecapVxlan(const PNET_BUFFER_LIST packet,
> break;
> }
>
> - /* XXX Should be tested against the dynamic port # in the VXLAN vport */
> - ASSERT(udp->dest == RtlUshortByteSwap(VXLAN_UDP_PORT));
> -
> VxlanHeader = (VXLANHdr *)OvsGetPacketBytes(packet,
> sizeof(*VxlanHeader),
> layers.l7Offset,
> diff --git a/datapath-windows/ovsext/Vxlan.h b/datapath-windows/ovsext/Vxlan.h
> index d84796d..248a5dc 100644
> --- a/datapath-windows/ovsext/Vxlan.h
> +++ b/datapath-windows/ovsext/Vxlan.h
> @@ -24,6 +24,7 @@ typedef struct _OVS_VXLAN_VPORT {
> UINT64 outPkts;
> UINT64 slowInPkts;
> UINT64 slowOutPkts;
> + UINT64 filterID;
> /*
> * To be filled
> */
> @@ -47,10 +48,16 @@ typedef struct VXLANHdr {
> UINT32 reserved2:8;
> } VXLANHdr;
>
> -NTSTATUS OvsInitVxlanTunnel(POVS_VPORT_ENTRY vport,
> - UINT16 udpDestPort);
> +NTSTATUS OvsInitVxlanTunnel(PIRP irp,
> + POVS_VPORT_ENTRY vport,
> + UINT16 udpDestPort,
> + PFNTunnelVportPendingOp callback,
> + PVOID tunnelContext);
>
> -VOID OvsCleanupVxlanTunnel(POVS_VPORT_ENTRY vport);
> +NTSTATUS OvsCleanupVxlanTunnel(PIRP irp,
> + POVS_VPORT_ENTRY vport,
> + PFNTunnelVportPendingOp callback,
> + PVOID tunnelContext);
>
> NDIS_STATUS OvsSlowPathDecapVxlan(const PNET_BUFFER_LIST packet,
> OvsIPv4TunnelKey *tunnelKey);
> --
> 1.9.0.msysgit.0
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
More information about the dev
mailing list