2 * Copyright (c) 2014 VMware, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * This file contains the implementation of the management functionality of the
29 #include "TunnelIntf.h"
35 #define OVS_DBG_MOD OVS_DBG_SWITCH
38 POVS_SWITCH_CONTEXT gOvsSwitchContext;
40 UINT64 ovsTimeIncrementPerTick;
42 extern PNDIS_SPIN_LOCK gOvsCtrlLock;
43 extern NDIS_HANDLE gOvsExtDriverHandle;
44 extern NDIS_HANDLE gOvsExtDriverObject;
46 static NDIS_STATUS OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
47 POVS_SWITCH_CONTEXT *switchContextOut);
48 static NDIS_STATUS OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext);
49 static VOID OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext);
50 static VOID OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext);
51 static NDIS_STATUS OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext);
55 * --------------------------------------------------------------------------
56 * Implements filter driver's FilterAttach function.
58 * This function allocates the switch context, and initializes its necessary
60 * --------------------------------------------------------------------------
63 OvsExtAttach(NDIS_HANDLE ndisFilterHandle,
64 NDIS_HANDLE filterDriverContext,
65 PNDIS_FILTER_ATTACH_PARAMETERS attachParameters)
67 NDIS_STATUS status = NDIS_STATUS_FAILURE;
68 NDIS_FILTER_ATTRIBUTES ovsExtAttributes;
69 POVS_SWITCH_CONTEXT switchContext = NULL;
71 UNREFERENCED_PARAMETER(filterDriverContext);
73 OVS_LOG_TRACE("Enter: ndisFilterHandle %p", ndisFilterHandle);
75 ASSERT(filterDriverContext == (NDIS_HANDLE)gOvsExtDriverObject);
76 if (attachParameters->MiniportMediaType != NdisMedium802_3) {
77 status = NDIS_STATUS_INVALID_PARAMETER;
81 if (gOvsExtDriverHandle == NULL) {
82 OVS_LOG_TRACE("Exit: OVSEXT driver is not loaded.");
87 NdisAcquireSpinLock(gOvsCtrlLock);
88 if (gOvsSwitchContext) {
89 NdisReleaseSpinLock(gOvsCtrlLock);
90 OVS_LOG_TRACE("Exit: Failed to create OVS Switch, only one datapath is"
91 "supported, %p.", gOvsSwitchContext);
95 NdisReleaseSpinLock(gOvsCtrlLock);
96 /* Just fail the request. */
97 OVS_LOG_TRACE("Exit: Failed to create OVS Switch, since another attach"
98 "instance is in attach process.");
102 NdisReleaseSpinLock(gOvsCtrlLock);
104 status = OvsInitIpHelper(ndisFilterHandle);
105 if (status != STATUS_SUCCESS) {
106 OVS_LOG_ERROR("Exit: Failed to initialize IP helper.");
110 status = OvsCreateSwitch(ndisFilterHandle, &switchContext);
111 if (status != NDIS_STATUS_SUCCESS) {
112 OvsCleanupIpHelper();
115 ASSERT(switchContext);
118 * Register the switch context with NDIS so NDIS can pass it back to the
119 * Filterxxx callback functions as the 'FilterModuleContext' parameter.
121 RtlZeroMemory(&ovsExtAttributes, sizeof(NDIS_FILTER_ATTRIBUTES));
122 ovsExtAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1;
123 ovsExtAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES);
124 ovsExtAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES;
125 ovsExtAttributes.Flags = 0;
127 NDIS_DECLARE_FILTER_MODULE_CONTEXT(OVS_SWITCH_CONTEXT);
128 status = NdisFSetAttributes(ndisFilterHandle, switchContext, &ovsExtAttributes);
129 if (status != NDIS_STATUS_SUCCESS) {
130 OVS_LOG_ERROR("Failed to set attributes.");
131 OvsCleanupIpHelper();
135 /* Setup the state machine. */
136 switchContext->controlFlowState = OvsSwitchAttached;
137 switchContext->dataFlowState = OvsSwitchPaused;
139 gOvsSwitchContext = switchContext;
143 gOvsInAttach = FALSE;
144 if (status != NDIS_STATUS_SUCCESS) {
145 if (switchContext != NULL) {
146 OvsDeleteSwitch(switchContext);
149 OVS_LOG_TRACE("Exit: status %x", status);
156 * --------------------------------------------------------------------------
157 * This function allocated the switch context, and initializes its necessary
159 * --------------------------------------------------------------------------
162 OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
163 POVS_SWITCH_CONTEXT *switchContextOut)
166 POVS_SWITCH_CONTEXT switchContext;
167 NDIS_SWITCH_CONTEXT hostSwitchContext;
168 NDIS_SWITCH_OPTIONAL_HANDLERS hostSwitchHandler;
170 OVS_LOG_TRACE("Enter: Create switch object");
173 (POVS_SWITCH_CONTEXT) OvsAllocateMemory(sizeof(OVS_SWITCH_CONTEXT));
174 if (switchContext == NULL) {
175 status = NDIS_STATUS_RESOURCES;
176 goto create_switch_done;
178 RtlZeroMemory(switchContext, sizeof(OVS_SWITCH_CONTEXT));
180 /* Initialize the switch. */
181 hostSwitchHandler.Header.Type = NDIS_OBJECT_TYPE_SWITCH_OPTIONAL_HANDLERS;
182 hostSwitchHandler.Header.Size = NDIS_SIZEOF_SWITCH_OPTIONAL_HANDLERS_REVISION_1;
183 hostSwitchHandler.Header.Revision = NDIS_SWITCH_OPTIONAL_HANDLERS_REVISION_1;
185 status = NdisFGetOptionalSwitchHandlers(ndisFilterHandle,
188 if (status != NDIS_STATUS_SUCCESS) {
189 OVS_LOG_ERROR("OvsExtAttach: Extension is running in "
190 "non-switch environment.");
191 OvsFreeMemory(switchContext);
192 goto create_switch_done;
195 switchContext->NdisFilterHandle = ndisFilterHandle;
196 switchContext->NdisSwitchContext = hostSwitchContext;
197 RtlCopyMemory(&switchContext->NdisSwitchHandlers, &hostSwitchHandler,
198 sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS));
200 status = OvsInitSwitchContext(switchContext);
201 if (status != NDIS_STATUS_SUCCESS) {
202 OvsFreeMemory(switchContext);
203 goto create_switch_done;
206 status = OvsTunnelFilterInitialize(gOvsExtDriverObject);
207 if (status != NDIS_STATUS_SUCCESS) {
208 OvsUninitSwitchContext(switchContext);
209 OvsFreeMemory(switchContext);
210 goto create_switch_done;
212 *switchContextOut = switchContext;
215 OVS_LOG_TRACE("Exit: switchContext: %p status: %#lx",
216 switchContext, status);
222 * --------------------------------------------------------------------------
223 * Implements filter driver's FilterDetach function.
224 * --------------------------------------------------------------------------
226 _Use_decl_annotations_
228 OvsExtDetach(NDIS_HANDLE filterModuleContext)
230 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
232 OVS_LOG_TRACE("Enter: filterModuleContext %p", filterModuleContext);
234 ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
235 switchContext->controlFlowState = OvsSwitchDetached;
237 while(switchContext->pendingOidCount > 0) {
240 OvsDeleteSwitch(switchContext);
241 OvsCleanupIpHelper();
242 gOvsSwitchContext = NULL;
243 /* This completes the cleanup, and a new attach can be handled now. */
245 OVS_LOG_TRACE("Exit: OvsDetach Successfully");
250 * --------------------------------------------------------------------------
251 * This function deletes the switch by freeing all memory previously allocated.
252 * XXX need synchronization with other path.
253 * --------------------------------------------------------------------------
256 OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext)
258 UINT32 dpNo = (UINT32) -1;
260 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
264 dpNo = switchContext->dpNo;
265 OvsTunnelFilterUninitialize(gOvsExtDriverObject);
266 OvsClearAllSwitchVports(switchContext);
267 OvsUninitSwitchContext(switchContext);
268 OvsFreeMemory(switchContext);
270 OVS_LOG_TRACE("Exit: deleted switch %p dpNo: %d", switchContext, dpNo);
275 * --------------------------------------------------------------------------
276 * Implements filter driver's FilterRestart function.
277 * --------------------------------------------------------------------------
279 _Use_decl_annotations_
281 OvsExtRestart(NDIS_HANDLE filterModuleContext,
282 PNDIS_FILTER_RESTART_PARAMETERS filterRestartParameters)
284 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
285 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
286 BOOLEAN switchActive;
288 UNREFERENCED_PARAMETER(filterRestartParameters);
290 OVS_LOG_TRACE("Enter: filterModuleContext %p",
291 filterModuleContext);
293 /* Activate the switch if this is the first restart. */
294 if (!switchContext->isActivated && !switchContext->isActivateFailed) {
295 status = OvsQuerySwitchActivationComplete(switchContext,
297 if (status != NDIS_STATUS_SUCCESS) {
298 switchContext->isActivateFailed = TRUE;
299 status = NDIS_STATUS_RESOURCES;
304 status = OvsActivateSwitch(switchContext);
306 if (status != NDIS_STATUS_SUCCESS) {
307 OVS_LOG_WARN("Failed to activate switch, dpNo:%d",
308 switchContext->dpNo);
309 status = NDIS_STATUS_RESOURCES;
315 ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
316 switchContext->dataFlowState = OvsSwitchRunning;
319 OVS_LOG_TRACE("Exit: Restart switch:%p, dpNo: %d, status: %#x",
320 switchContext, switchContext->dpNo, status);
326 * --------------------------------------------------------------------------
327 * Implements filter driver's FilterPause function
328 * --------------------------------------------------------------------------
331 OvsExtPause(NDIS_HANDLE filterModuleContext,
332 PNDIS_FILTER_PAUSE_PARAMETERS pauseParameters)
334 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
336 UNREFERENCED_PARAMETER(pauseParameters);
337 OVS_LOG_TRACE("Enter: filterModuleContext %p",
338 filterModuleContext);
340 ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
341 switchContext->dataFlowState = OvsSwitchPaused;
343 while(switchContext->pendingOidCount > 0) {
347 OVS_LOG_TRACE("Exit: OvsDetach Successfully");
348 return NDIS_STATUS_SUCCESS;
352 OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
357 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
359 switchContext->dispatchLock =
360 NdisAllocateRWLock(switchContext->NdisFilterHandle);
362 switchContext->portNoHashArray = (PLIST_ENTRY)
363 OvsAllocateMemory(sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE);
364 switchContext->ovsPortNameHashArray = (PLIST_ENTRY)
365 OvsAllocateMemory(sizeof (LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE);
366 switchContext->portIdHashArray= (PLIST_ENTRY)
367 OvsAllocateMemory(sizeof (LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE);
368 switchContext->pidHashArray = (PLIST_ENTRY)
369 OvsAllocateMemory(sizeof(LIST_ENTRY) * OVS_MAX_PID_ARRAY_SIZE);
370 status = OvsAllocateFlowTable(&switchContext->datapath, switchContext);
372 if (status == NDIS_STATUS_SUCCESS) {
373 status = OvsInitBufferPool(switchContext);
375 if (status != NDIS_STATUS_SUCCESS ||
376 switchContext->dispatchLock == NULL ||
377 switchContext->portNoHashArray == NULL ||
378 switchContext->ovsPortNameHashArray == NULL ||
379 switchContext->portIdHashArray== NULL ||
380 switchContext->pidHashArray == NULL) {
381 if (switchContext->dispatchLock) {
382 NdisFreeRWLock(switchContext->dispatchLock);
384 if (switchContext->portNoHashArray) {
385 OvsFreeMemory(switchContext->portNoHashArray);
387 if (switchContext->ovsPortNameHashArray) {
388 OvsFreeMemory(switchContext->ovsPortNameHashArray);
390 if (switchContext->portIdHashArray) {
391 OvsFreeMemory(switchContext->portIdHashArray);
394 if (switchContext->pidHashArray) {
395 OvsFreeMemory(switchContext->pidHashArray);
398 OvsDeleteFlowTable(&switchContext->datapath);
399 OvsCleanupBufferPool(switchContext);
401 OVS_LOG_TRACE("Exit: Failed to init switchContext");
402 return NDIS_STATUS_RESOURCES;
405 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
406 InitializeListHead(&switchContext->ovsPortNameHashArray[i]);
408 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
409 InitializeListHead(&switchContext->portIdHashArray[i]);
411 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
412 InitializeListHead(&switchContext->portNoHashArray[i]);
415 for (i = 0; i < OVS_MAX_PID_ARRAY_SIZE; i++) {
416 InitializeListHead(&switchContext->pidHashArray[i]);
419 NdisAllocateSpinLock(&(switchContext->pidHashLock));
420 switchContext->isActivated = FALSE;
421 switchContext->isActivateFailed = FALSE;
422 switchContext->dpNo = OVS_DP_NUMBER;
423 ovsTimeIncrementPerTick = KeQueryTimeIncrement() / 10000;
424 OVS_LOG_TRACE("Exit: Succesfully initialized switchContext: %p",
426 return NDIS_STATUS_SUCCESS;
430 OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
432 OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext);
434 /* We need to do cleanup for tunnel port here. */
435 ASSERT(switchContext->numVports == 0);
437 NdisFreeRWLock(switchContext->dispatchLock);
438 switchContext->dispatchLock = NULL;
439 NdisFreeSpinLock(&(switchContext->pidHashLock));
440 OvsFreeMemory(switchContext->ovsPortNameHashArray);
441 switchContext->ovsPortNameHashArray = NULL;
442 OvsFreeMemory(switchContext->portIdHashArray);
443 switchContext->portIdHashArray = NULL;
444 OvsFreeMemory(switchContext->portNoHashArray);
445 switchContext->portNoHashArray = NULL;
446 OvsFreeMemory(switchContext->pidHashArray);
447 switchContext->pidHashArray = NULL;
448 OvsDeleteFlowTable(&switchContext->datapath);
449 OvsCleanupBufferPool(switchContext);
450 OVS_LOG_TRACE("Exit: Delete switchContext: %p", switchContext);
454 * --------------------------------------------------------------------------
455 * This function activates the switch by initializing it with all the runtime
456 * state. First it queries all of the MAC addresses set as custom switch policy
457 * to allow sends from, and adds tme to the property list. Then it queries the
458 * NIC list and verifies it can support all of the NICs currently connected to
459 * the switch, and adds the NICs to the NIC list.
460 * --------------------------------------------------------------------------
463 OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext)
467 ASSERT(!switchContext->isActivated);
469 OVS_LOG_TRACE("Enter: activate switch %p, dpNo: %ld",
470 switchContext, switchContext->dpNo);
472 status = OvsAddConfiguredSwitchPorts(switchContext);
474 if (status != NDIS_STATUS_SUCCESS) {
475 OVS_LOG_WARN("Failed to add configured switch ports");
479 status = OvsInitConfiguredSwitchNics(switchContext);
481 if (status != NDIS_STATUS_SUCCESS) {
482 OVS_LOG_WARN("Failed to add configured vports");
483 OvsClearAllSwitchVports(switchContext);
486 switchContext->isActivated = TRUE;
487 OvsPostEvent(OVS_DEFAULT_PORT_NO, OVS_DEFAULT_EVENT_STATUS);
490 OVS_LOG_TRACE("Exit: activate switch:%p, isActivated: %s, status = %lx",
492 (switchContext->isActivated ? "TRUE" : "FALSE"), status);
497 OvsGetExternalVport()
499 return gOvsSwitchContext->externalVport;
504 * --------------------------------------------------------------------------
505 * Implements filter driver's FilterNetPnPEvent function.
506 * --------------------------------------------------------------------------
509 OvsExtNetPnPEvent(NDIS_HANDLE filterModuleContext,
510 PNET_PNP_EVENT_NOTIFICATION netPnPEvent)
512 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
513 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
514 BOOLEAN switchActive;
516 OVS_LOG_TRACE("Enter: filterModuleContext: %p, NetEvent: %d",
517 filterModuleContext, (netPnPEvent->NetPnPEvent).NetEvent);
519 * The only interesting event is the NetEventSwitchActivate. It provides
520 * an asynchronous notification of the switch completing activation.
522 if (netPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) {
523 status = OvsQuerySwitchActivationComplete(switchContext, &switchActive);
524 if (status != NDIS_STATUS_SUCCESS) {
525 switchContext->isActivateFailed = TRUE;
527 ASSERT(switchContext->isActivated == FALSE);
528 ASSERT(switchActive == TRUE);
529 if (switchContext->isActivated == FALSE && switchActive == TRUE) {
530 status = OvsActivateSwitch(switchContext);
531 OVS_LOG_TRACE("OvsExtNetPnPEvent: activated switch: %p "
532 "status: %s", switchContext,
533 status ? "TRUE" : "FALSE");
538 if (status == NDIS_STATUS_SUCCESS) {
539 status = NdisFNetPnPEvent(switchContext->NdisFilterHandle,
542 OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent");