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 datapath/forwarding
19 * functionality of the OVS.
32 /* Due to an imported header file */
33 #pragma warning( disable:4505 )
38 #define OVS_DBG_MOD OVS_DBG_DISPATCH
41 extern NDIS_STRING ovsExtGuidUC;
42 extern NDIS_STRING ovsExtFriendlyNameUC;
44 static VOID OvsFinalizeCompletionList(OvsCompletionList *completionList);
45 static VOID OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext,
46 PNET_BUFFER_LIST netBufferLists, ULONG sendCompleteFlags);
47 static NTSTATUS OvsCreateNewNBLsFromMultipleNBs(
48 POVS_SWITCH_CONTEXT switchContext,
49 PNET_BUFFER_LIST *curNbl,
50 PNET_BUFFER_LIST *nextNbl);
53 OvsInitCompletionList(OvsCompletionList *completionList,
54 POVS_SWITCH_CONTEXT switchContext,
55 ULONG sendCompleteFlags)
57 ASSERT(completionList);
58 completionList->dropNbl = NULL;
59 completionList->dropNblNext = &completionList->dropNbl;
60 completionList->switchContext = switchContext;
61 completionList->sendCompleteFlags = sendCompleteFlags;
64 /* Utility function used to complete an NBL. */
66 OvsAddPktCompletionList(OvsCompletionList *completionList,
68 NDIS_SWITCH_PORT_ID sourcePort,
69 PNET_BUFFER_LIST netBufferList,
70 UINT32 netBufferListCount,
71 PNDIS_STRING filterReason)
73 POVS_BUFFER_CONTEXT ctx;
75 /* XXX: We handle one NBL at a time. */
76 ASSERT(netBufferList->Next == NULL);
78 /* Make sure it has a context. */
79 ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(netBufferList);
80 ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC);
82 completionList->switchContext->NdisSwitchHandlers.ReportFilteredNetBufferLists(
83 completionList->switchContext->NdisSwitchContext, &ovsExtGuidUC,
84 &ovsExtFriendlyNameUC, sourcePort,
85 incoming ? NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING : 0,
86 netBufferListCount, netBufferList, filterReason);
88 *completionList->dropNblNext = netBufferList;
89 completionList->dropNblNext = &netBufferList->Next;
90 ASSERT(completionList->dropNbl);
94 OvsReportNBLIngressError(POVS_SWITCH_CONTEXT switchContext,
95 PNET_BUFFER_LIST nblList,
96 PNDIS_STRING filterReason,
99 PNET_BUFFER_LIST nbl = nblList;
101 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
102 fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(nbl);
106 /* This can be optimized by batching NBL's from the same
108 switchContext->NdisSwitchHandlers.ReportFilteredNetBufferLists(
109 switchContext->NdisSwitchContext, &ovsExtGuidUC,
110 &ovsExtFriendlyNameUC, fwdDetail->SourcePortId,
111 NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING,
112 1 /*Nbl count.*/, nbl, filterReason);
114 nbl = NET_BUFFER_LIST_NEXT_NBL(nbl);
118 static __inline ULONG
119 OvsGetSendCompleteFlags(ULONG sendFlags)
121 BOOLEAN dispatch, sameSource;
122 ULONG sendCompleteFlags;
124 dispatch = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(sendFlags);
125 sendCompleteFlags = (dispatch ?
126 NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0);
127 sameSource = NDIS_TEST_SEND_FLAG(sendFlags,
128 NDIS_SEND_FLAGS_SWITCH_SINGLE_SOURCE);
129 sendCompleteFlags |= (sameSource ?
130 NDIS_SEND_COMPLETE_FLAGS_SWITCH_SINGLE_SOURCE : 0);
132 return sendCompleteFlags;
136 OvsSendNBLIngress(POVS_SWITCH_CONTEXT switchContext,
137 PNET_BUFFER_LIST netBufferLists,
140 if (switchContext->dataFlowState == OvsSwitchPaused) {
141 /* If a filter module is in the Paused state, the filter driver must not
142 * originate any send requests for that filter module. If NDIS calls
143 * FilterSendNetBufferLists, the driver must not call
144 * NdisFSendNetBufferLists to pass on the data until the driver is
145 * restarted. The driver should call NdisFSendNetBufferListsComplete
146 * immediately to complete the send operation. It should set the
147 * complete status in each NET_BUFFER_LIST structure to
148 * NDIS_STATUS_PAUSED.
150 * http://msdn.microsoft.com/en-us/library/windows/hardware/
151 * ff549966(v=vs.85).aspx */
152 NDIS_STRING filterReason;
153 ULONG sendCompleteFlags = OvsGetSendCompleteFlags(sendFlags);
155 RtlInitUnicodeString(&filterReason,
156 L"Switch state PAUSED, drop before FSendNBL.");
157 OvsReportNBLIngressError(switchContext, netBufferLists, &filterReason,
159 OvsCompleteNBLIngress(switchContext, netBufferLists,
164 ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
166 NdisFSendNetBufferLists(switchContext->NdisFilterHandle, netBufferLists,
167 NDIS_DEFAULT_PORT_NUMBER, sendFlags);
171 OvsStartNBLIngressError(POVS_SWITCH_CONTEXT switchContext,
172 PNET_BUFFER_LIST nblList,
173 ULONG sendCompleteFlags,
174 PNDIS_STRING filterReason,
178 OvsReportNBLIngressError(switchContext, nblList, filterReason, error);
179 NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle, nblList,
184 OvsAppendNativeForwardedPacket(POVS_SWITCH_CONTEXT switchContext,
185 PNET_BUFFER_LIST curNbl,
186 PNET_BUFFER_LIST *nativeNbls,
190 POVS_BUFFER_CONTEXT ctx = { 0 };
191 NDIS_STRING filterReason;
193 *nativeNbls = curNbl;
194 nativeNbls = &(curNbl->Next);
196 ctx = OvsInitExternalNBLContext(switchContext, curNbl, isRecv);
198 RtlInitUnicodeString(&filterReason,
199 L"Cannot allocate native NBL context.");
201 OvsStartNBLIngressError(switchContext, curNbl, flags, &filterReason,
202 NDIS_STATUS_RESOURCES);
207 OvsStartNBLIngress(POVS_SWITCH_CONTEXT switchContext,
208 PNET_BUFFER_LIST netBufferLists,
211 NDIS_SWITCH_PORT_ID sourcePort = 0;
212 NDIS_SWITCH_NIC_INDEX sourceIndex = 0;
213 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
214 PNET_BUFFER_LIST curNbl = NULL, nextNbl = NULL;
215 ULONG sendCompleteFlags;
217 LOCK_STATE_EX lockState, dpLockState;
219 NDIS_STRING filterReason;
220 LIST_ENTRY missedPackets;
222 OvsCompletionList completionList;
223 #if (NDIS_SUPPORT_NDIS640)
224 PNET_BUFFER_LIST nativeForwardedNbls = NULL;
225 PNET_BUFFER_LIST *nextNativeForwardedNbl = &nativeForwardedNbls;
228 dispatch = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags)?
229 NDIS_RWL_AT_DISPATCH_LEVEL : 0;
230 sendCompleteFlags = OvsGetSendCompleteFlags(SendFlags);
231 SendFlags |= NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP;
233 InitializeListHead(&missedPackets);
234 OvsInitCompletionList(&completionList, switchContext, sendCompleteFlags);
236 for (curNbl = netBufferLists; curNbl != NULL; curNbl = nextNbl) {
237 POVS_VPORT_ENTRY vport;
239 OVS_DATAPATH *datapath = &switchContext->datapath;
240 OVS_PACKET_HDR_INFO layers;
244 POVS_BUFFER_CONTEXT ctx;
246 nextNbl = curNbl->Next;
249 fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl);
250 sourcePort = fwdDetail->SourcePortId;
251 sourceIndex = (NDIS_SWITCH_NIC_INDEX)fwdDetail->SourceNicIndex;
253 #if (NDIS_SUPPORT_NDIS640)
254 if (fwdDetail->NativeForwardingRequired) {
255 /* Add current NBL to those that require native forwarding. */
256 OvsAppendNativeForwardedPacket(
259 nextNativeForwardedNbl,
261 sourcePort == switchContext->virtualExternalPortId);
264 #endif /* NDIS_SUPPORT_NDIS640 */
266 ctx = OvsInitExternalNBLContext(switchContext, curNbl,
267 sourcePort == switchContext->virtualExternalPortId);
269 RtlInitUnicodeString(&filterReason,
270 L"Cannot allocate external NBL context.");
272 OvsStartNBLIngressError(switchContext, curNbl,
273 sendCompleteFlags, &filterReason,
274 NDIS_STATUS_RESOURCES);
278 /* Ethernet Header is a guaranteed safe access. */
279 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
280 if (curNb->Next != NULL) {
281 /* Create a NET_BUFFER_LIST for each NET_BUFFER. */
282 status = OvsCreateNewNBLsFromMultipleNBs(switchContext,
285 if (!NT_SUCCESS(status)) {
286 RtlInitUnicodeString(&filterReason,
287 L"Cannot allocate NBLs with single NB.");
289 OvsStartNBLIngressError(switchContext, curNbl,
290 sendCompleteFlags, &filterReason,
291 NDIS_STATUS_RESOURCES);
298 /* Take the DispatchLock so none of the VPORTs disconnect while
299 * we are setting destination ports.
301 * XXX: acquire/release the dispatch lock for a "batch" of packets
302 * rather than for each packet. */
303 NdisAcquireRWLockRead(switchContext->dispatchLock, &lockState,
306 vport = OvsFindVportByPortIdAndNicIndex(switchContext, sourcePort,
308 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
309 RtlInitUnicodeString(&filterReason,
310 L"OVS-Cannot forward packet from unknown source port");
313 portNo = vport->portNo;
316 vport->stats.rxPackets++;
317 vport->stats.rxBytes += NET_BUFFER_DATA_LENGTH(curNb);
319 status = OvsExtractFlow(curNbl, vport->portNo, &key, &layers, NULL);
320 if (status != NDIS_STATUS_SUCCESS) {
321 RtlInitUnicodeString(&filterReason, L"OVS-Flow extract failed");
325 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
326 OvsAcquireDatapathRead(datapath, &dpLockState, TRUE);
328 flow = OvsLookupFlow(datapath, &key, &hash, FALSE);
330 OvsFlowUsed(flow, curNbl, &layers);
332 /* If successful, OvsActionsExecute() consumes the NBL.
333 * Otherwise, it adds it to the completionList. No need to
334 * check the return value. */
335 OvsActionsExecute(switchContext, &completionList, curNbl,
336 portNo, SendFlags, &key, &hash, &layers,
337 flow->actions, flow->actionsLen);
338 OvsReleaseDatapath(datapath, &dpLockState);
339 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
342 OvsReleaseDatapath(datapath, &dpLockState);
345 status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS,
346 portNo, &key, curNbl,
347 sourcePort == switchContext->virtualExternalPortId,
348 &layers, switchContext, &missedPackets, &num);
349 if (status == NDIS_STATUS_SUCCESS) {
350 /* Complete the packet since it was copied to user
352 RtlInitUnicodeString(&filterReason,
353 L"OVS-Dropped since packet was copied to userspace");
355 RtlInitUnicodeString(&filterReason,
356 L"OVS-Dropped due to failure to queue to userspace");
362 OvsAddPktCompletionList(&completionList, TRUE, sourcePort, curNbl, 0,
364 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
368 #if (NDIS_SUPPORT_NDIS640)
369 if (nativeForwardedNbls) {
370 /* This is NVGRE encapsulated traffic and is forwarded to NDIS
371 * in order to be handled by the HNV module. */
372 OvsSendNBLIngress(switchContext, nativeForwardedNbls, SendFlags);
374 #endif /* NDIS_SUPPORT_NDIS640 */
376 /* Queue the missed packets. */
377 OvsQueuePackets(&missedPackets, num);
378 OvsFinalizeCompletionList(&completionList);
383 * --------------------------------------------------------------------------
384 * Implements filter driver's FilterSendNetBufferLists Function.
385 * --------------------------------------------------------------------------
388 OvsExtSendNBL(NDIS_HANDLE filterModuleContext,
389 PNET_BUFFER_LIST netBufferLists,
390 NDIS_PORT_NUMBER portNumber,
393 UNREFERENCED_PARAMETER(portNumber);
395 /* 'filterModuleContext' is the switch context that gets created in the
397 POVS_SWITCH_CONTEXT switchContext;
398 switchContext = (POVS_SWITCH_CONTEXT) filterModuleContext;
400 if (switchContext->dataFlowState == OvsSwitchPaused) {
401 NDIS_STRING filterReason;
402 ULONG sendCompleteFlags = OvsGetSendCompleteFlags(sendFlags);
404 RtlInitUnicodeString(&filterReason,
405 L"Switch state PAUSED, drop on ingress.");
406 OvsStartNBLIngressError(switchContext, netBufferLists,
407 sendCompleteFlags, &filterReason,
412 ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
414 OvsStartNBLIngress(switchContext, netBufferLists, sendFlags);
418 OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext,
419 PNET_BUFFER_LIST netBufferLists,
420 ULONG sendCompleteFlags)
422 PNET_BUFFER_LIST curNbl = NULL, nextNbl = NULL;
423 OvsCompletionList newList;
425 newList.dropNbl = NULL;
426 newList.dropNblNext = &newList.dropNbl;
428 for (curNbl = netBufferLists; curNbl != NULL; curNbl = nextNbl) {
429 nextNbl = curNbl->Next;
432 curNbl = OvsCompleteNBL(switchContext, curNbl, TRUE);
433 if (curNbl != NULL) {
434 /* NBL originated from the upper layer. */
435 *newList.dropNblNext = curNbl;
436 newList.dropNblNext = &curNbl->Next;
440 /* Complete the NBL's that were sent by the upper layer. */
441 if (newList.dropNbl != NULL) {
442 NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle, newList.dropNbl,
449 * --------------------------------------------------------------------------
450 * Implements filter driver's FilterSendNetBufferListsComplete function.
451 * --------------------------------------------------------------------------
454 OvsExtSendNBLComplete(NDIS_HANDLE filterModuleContext,
455 PNET_BUFFER_LIST netBufferLists,
456 ULONG sendCompleteFlags)
458 OvsCompleteNBLIngress((POVS_SWITCH_CONTEXT)filterModuleContext,
459 netBufferLists, sendCompleteFlags);
464 OvsFinalizeCompletionList(OvsCompletionList *completionList)
466 if (completionList->dropNbl != NULL) {
467 OvsCompleteNBLIngress(completionList->switchContext,
468 completionList->dropNbl,
469 completionList->sendCompleteFlags);
471 completionList->dropNbl = NULL;
472 completionList->dropNblNext = &completionList->dropNbl;
477 * --------------------------------------------------------------------------
478 * Implements filter driver's FilterCancelSendNetBufferLists function.
480 * "If a filter driver specifies a FilterSendNetBufferLists function and it
481 * queues send requests, it must also specify a
482 * FilterCancelSendNetBufferLists function."
484 * http://msdn.microsoft.com/en-us/library/windows/hardware/
485 * ff549966(v=vs.85).aspx
486 * --------------------------------------------------------------------------
489 OvsExtCancelSendNBL(NDIS_HANDLE filterModuleContext,
492 UNREFERENCED_PARAMETER(filterModuleContext);
493 UNREFERENCED_PARAMETER(CancelId);
495 /* All send requests get completed synchronously, so there is no need to
496 * implement this callback. */
500 OvsCreateNewNBLsFromMultipleNBs(POVS_SWITCH_CONTEXT switchContext,
501 PNET_BUFFER_LIST *curNbl,
502 PNET_BUFFER_LIST *nextNbl)
504 NTSTATUS status = STATUS_SUCCESS;
505 PNET_BUFFER_LIST newNbls = NULL;
506 PNET_BUFFER_LIST lastNbl = NULL;
507 PNET_BUFFER_LIST nbl = NULL;
508 POVS_BUFFER_CONTEXT bufContext = NULL;
509 BOOLEAN error = TRUE;
512 /* Create new NBLs from curNbl with multiple net buffers. */
513 newNbls = OvsPartialCopyToMultipleNBLs(switchContext,
514 *curNbl, 0, 0, TRUE);
515 if (NULL == newNbls) {
516 OVS_LOG_ERROR("Failed to allocate NBLs with single NB.");
517 status = NDIS_STATUS_RESOURCES;
524 nbl = NET_BUFFER_LIST_NEXT_NBL(nbl);
526 lastNbl->Next = *nextNbl;
527 *nextNbl = newNbls->Next;
529 (*curNbl)->Next = NULL;
531 OvsCompleteNBL(switchContext, *curNbl, TRUE);