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.
34 /* Due to an imported header file */
35 #pragma warning( disable:4505 )
40 #define OVS_DBG_MOD OVS_DBG_DISPATCH
43 extern NDIS_STRING ovsExtGuidUC
;
44 extern NDIS_STRING ovsExtFriendlyNameUC
;
46 static VOID
OvsFinalizeCompletionList(OvsCompletionList
*completionList
);
47 static VOID
OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext
,
48 PNET_BUFFER_LIST netBufferLists
, ULONG sendCompleteFlags
);
49 static NTSTATUS
OvsCreateNewNBLsFromMultipleNBs(
50 POVS_SWITCH_CONTEXT switchContext
,
51 PNET_BUFFER_LIST
*curNbl
,
52 PNET_BUFFER_LIST
*nextNbl
);
55 OvsInitCompletionList(OvsCompletionList
*completionList
,
56 POVS_SWITCH_CONTEXT switchContext
,
57 ULONG sendCompleteFlags
)
59 ASSERT(completionList
);
60 completionList
->dropNbl
= NULL
;
61 completionList
->dropNblNext
= &completionList
->dropNbl
;
62 completionList
->switchContext
= switchContext
;
63 completionList
->sendCompleteFlags
= sendCompleteFlags
;
66 /* Utility function used to complete an NBL. */
68 OvsAddPktCompletionList(OvsCompletionList
*completionList
,
70 NDIS_SWITCH_PORT_ID sourcePort
,
71 PNET_BUFFER_LIST netBufferList
,
72 UINT32 netBufferListCount
,
73 PNDIS_STRING filterReason
)
75 POVS_BUFFER_CONTEXT ctx
;
77 /* XXX: We handle one NBL at a time. */
78 ASSERT(netBufferList
->Next
== NULL
);
80 /* Make sure it has a context. */
81 ctx
= (POVS_BUFFER_CONTEXT
)NET_BUFFER_LIST_CONTEXT_DATA_START(netBufferList
);
82 ASSERT(ctx
&& ctx
->magic
== OVS_CTX_MAGIC
);
84 completionList
->switchContext
->NdisSwitchHandlers
.ReportFilteredNetBufferLists(
85 completionList
->switchContext
->NdisSwitchContext
, &ovsExtGuidUC
,
86 &ovsExtFriendlyNameUC
, sourcePort
,
87 incoming
? NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING
: 0,
88 netBufferListCount
, netBufferList
, filterReason
);
90 *completionList
->dropNblNext
= netBufferList
;
91 completionList
->dropNblNext
= &netBufferList
->Next
;
92 ASSERT(completionList
->dropNbl
);
96 OvsReportNBLIngressError(POVS_SWITCH_CONTEXT switchContext
,
97 PNET_BUFFER_LIST nblList
,
98 PNDIS_STRING filterReason
,
101 PNET_BUFFER_LIST nbl
= nblList
;
103 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail
;
104 fwdDetail
= NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(nbl
);
108 /* This can be optimized by batching NBL's from the same
110 switchContext
->NdisSwitchHandlers
.ReportFilteredNetBufferLists(
111 switchContext
->NdisSwitchContext
, &ovsExtGuidUC
,
112 &ovsExtFriendlyNameUC
, fwdDetail
->SourcePortId
,
113 NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING
,
114 1 /*Nbl count.*/, nbl
, filterReason
);
116 nbl
= NET_BUFFER_LIST_NEXT_NBL(nbl
);
120 static __inline ULONG
121 OvsGetSendCompleteFlags(ULONG sendFlags
)
123 BOOLEAN dispatch
, sameSource
;
124 ULONG sendCompleteFlags
;
126 dispatch
= NDIS_TEST_SEND_AT_DISPATCH_LEVEL(sendFlags
);
127 sendCompleteFlags
= (dispatch
?
128 NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL
: 0);
129 sameSource
= NDIS_TEST_SEND_FLAG(sendFlags
,
130 NDIS_SEND_FLAGS_SWITCH_SINGLE_SOURCE
);
131 sendCompleteFlags
|= (sameSource
?
132 NDIS_SEND_COMPLETE_FLAGS_SWITCH_SINGLE_SOURCE
: 0);
134 return sendCompleteFlags
;
138 OvsSendNBLIngress(POVS_SWITCH_CONTEXT switchContext
,
139 PNET_BUFFER_LIST netBufferLists
,
142 if (switchContext
->dataFlowState
== OvsSwitchPaused
) {
143 /* If a filter module is in the Paused state, the filter driver must not
144 * originate any send requests for that filter module. If NDIS calls
145 * FilterSendNetBufferLists, the driver must not call
146 * NdisFSendNetBufferLists to pass on the data until the driver is
147 * restarted. The driver should call NdisFSendNetBufferListsComplete
148 * immediately to complete the send operation. It should set the
149 * complete status in each NET_BUFFER_LIST structure to
150 * NDIS_STATUS_PAUSED.
152 * http://msdn.microsoft.com/en-us/library/windows/hardware/
153 * ff549966(v=vs.85).aspx */
154 NDIS_STRING filterReason
;
155 ULONG sendCompleteFlags
= OvsGetSendCompleteFlags(sendFlags
);
157 RtlInitUnicodeString(&filterReason
,
158 L
"Switch state PAUSED, drop before FSendNBL.");
159 OvsReportNBLIngressError(switchContext
, netBufferLists
, &filterReason
,
161 OvsCompleteNBLIngress(switchContext
, netBufferLists
,
166 ASSERT(switchContext
->dataFlowState
== OvsSwitchRunning
);
168 NdisFSendNetBufferLists(switchContext
->NdisFilterHandle
, netBufferLists
,
169 NDIS_DEFAULT_PORT_NUMBER
, sendFlags
);
173 OvsStartNBLIngressError(POVS_SWITCH_CONTEXT switchContext
,
174 PNET_BUFFER_LIST nblList
,
175 ULONG sendCompleteFlags
,
176 PNDIS_STRING filterReason
,
180 OvsReportNBLIngressError(switchContext
, nblList
, filterReason
, error
);
181 NdisFSendNetBufferListsComplete(switchContext
->NdisFilterHandle
, nblList
,
186 OvsAppendNativeForwardedPacket(POVS_SWITCH_CONTEXT switchContext
,
187 PNET_BUFFER_LIST curNbl
,
188 PNET_BUFFER_LIST
*nativeNbls
,
192 POVS_BUFFER_CONTEXT ctx
= { 0 };
193 NDIS_STRING filterReason
;
195 *nativeNbls
= curNbl
;
197 ctx
= OvsInitExternalNBLContext(switchContext
, curNbl
, isRecv
);
199 RtlInitUnicodeString(&filterReason
,
200 L
"Cannot allocate native NBL context.");
202 OvsStartNBLIngressError(switchContext
, curNbl
, flags
, &filterReason
,
203 NDIS_STATUS_RESOURCES
);
208 OvsStartNBLIngress(POVS_SWITCH_CONTEXT switchContext
,
209 PNET_BUFFER_LIST netBufferLists
,
212 NDIS_SWITCH_PORT_ID sourcePort
= 0;
213 NDIS_SWITCH_NIC_INDEX sourceIndex
= 0;
214 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail
;
215 PNET_BUFFER_LIST curNbl
= NULL
, nextNbl
= NULL
;
216 ULONG sendCompleteFlags
;
218 LOCK_STATE_EX lockState
, dpLockState
;
220 NDIS_STRING filterReason
;
221 LIST_ENTRY missedPackets
;
223 OvsCompletionList completionList
;
224 #if (NDIS_SUPPORT_NDIS640)
225 PNET_BUFFER_LIST nativeForwardedNbls
= NULL
;
226 PNET_BUFFER_LIST
*nextNativeForwardedNbl
= &nativeForwardedNbls
;
229 dispatch
= NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags
)?
230 NDIS_RWL_AT_DISPATCH_LEVEL
: 0;
231 sendCompleteFlags
= OvsGetSendCompleteFlags(SendFlags
);
232 SendFlags
|= NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP
;
234 InitializeListHead(&missedPackets
);
235 OvsInitCompletionList(&completionList
, switchContext
, sendCompleteFlags
);
237 for (curNbl
= netBufferLists
; curNbl
!= NULL
; curNbl
= nextNbl
) {
238 POVS_VPORT_ENTRY vport
= NULL
;
240 OVS_DATAPATH
*datapath
= &switchContext
->datapath
;
241 OVS_PACKET_HDR_INFO layers
= { 0 };
242 OvsFlowKey key
= { 0 };
244 PNET_BUFFER curNb
= NULL
;
245 POVS_BUFFER_CONTEXT ctx
= NULL
;
247 nextNbl
= curNbl
->Next
;
250 fwdDetail
= NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl
);
251 sourcePort
= fwdDetail
->SourcePortId
;
252 sourceIndex
= (NDIS_SWITCH_NIC_INDEX
)fwdDetail
->SourceNicIndex
;
254 #if (NDIS_SUPPORT_NDIS640)
255 if (fwdDetail
->NativeForwardingRequired
) {
256 /* Add current NBL to those that require native forwarding. */
257 OvsAppendNativeForwardedPacket(
260 nextNativeForwardedNbl
,
262 sourcePort
== switchContext
->virtualExternalPortId
);
265 #endif /* NDIS_SUPPORT_NDIS640 */
267 ctx
= OvsInitExternalNBLContext(switchContext
, curNbl
,
268 sourcePort
== switchContext
->virtualExternalPortId
);
270 RtlInitUnicodeString(&filterReason
,
271 L
"Cannot allocate external NBL context.");
273 OvsStartNBLIngressError(switchContext
, curNbl
,
274 sendCompleteFlags
, &filterReason
,
275 NDIS_STATUS_RESOURCES
);
279 /* Ethernet Header is a guaranteed safe access. */
280 curNb
= NET_BUFFER_LIST_FIRST_NB(curNbl
);
281 if (curNb
->Next
!= NULL
) {
282 /* Create a NET_BUFFER_LIST for each NET_BUFFER. */
283 status
= OvsCreateNewNBLsFromMultipleNBs(switchContext
,
286 if (!NT_SUCCESS(status
)) {
287 RtlInitUnicodeString(&filterReason
,
288 L
"Cannot allocate NBLs with single NB.");
290 OvsStartNBLIngressError(switchContext
, curNbl
,
291 sendCompleteFlags
, &filterReason
,
292 NDIS_STATUS_RESOURCES
);
299 /* Take the DispatchLock so none of the VPORTs disconnect while
300 * we are setting destination ports.
302 * XXX: acquire/release the dispatch lock for a "batch" of packets
303 * rather than for each packet. */
304 NdisAcquireRWLockRead(switchContext
->dispatchLock
, &lockState
,
307 vport
= OvsFindVportByPortIdAndNicIndex(switchContext
, sourcePort
,
309 if (vport
== NULL
|| vport
->ovsState
!= OVS_STATE_CONNECTED
) {
310 RtlInitUnicodeString(&filterReason
,
311 L
"OVS-Cannot forward packet from unknown source port");
314 portNo
= vport
->portNo
;
317 vport
->stats
.rxPackets
++;
318 vport
->stats
.rxBytes
+= NET_BUFFER_DATA_LENGTH(curNb
);
320 status
= OvsExtractFlow(curNbl
, vport
->portNo
, &key
, &layers
, NULL
);
321 if (status
!= NDIS_STATUS_SUCCESS
) {
322 RtlInitUnicodeString(&filterReason
, L
"OVS-Flow extract failed");
326 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
327 OvsAcquireDatapathRead(datapath
, &dpLockState
, TRUE
);
329 flow
= OvsLookupFlow(datapath
, &key
, &hash
, FALSE
);
331 OvsFlowUsed(flow
, curNbl
, &layers
);
333 /* If successful, OvsActionsExecute() consumes the NBL.
334 * Otherwise, it adds it to the completionList. No need to
335 * check the return value. */
336 OvsActionsExecute(switchContext
, &completionList
, curNbl
,
337 portNo
, SendFlags
, &key
, &hash
, &layers
,
338 flow
->actions
, flow
->actionsLen
);
339 OvsReleaseDatapath(datapath
, &dpLockState
);
340 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
343 OvsReleaseDatapath(datapath
, &dpLockState
);
346 status
= OvsCreateAndAddPackets(NULL
, 0, OVS_PACKET_CMD_MISS
,
348 sourcePort
== switchContext
->virtualExternalPortId
,
349 &layers
, switchContext
, &missedPackets
, &num
);
350 if (status
== NDIS_STATUS_SUCCESS
) {
351 /* Complete the packet since it was copied to user
353 RtlInitUnicodeString(&filterReason
,
354 L
"OVS-Dropped since packet was copied to userspace");
356 RtlInitUnicodeString(&filterReason
,
357 L
"OVS-Dropped due to failure to queue to userspace");
363 OvsAddPktCompletionList(&completionList
, TRUE
, sourcePort
, curNbl
, 0,
365 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
369 #if (NDIS_SUPPORT_NDIS640)
370 if (nativeForwardedNbls
) {
371 /* This is NVGRE encapsulated traffic and is forwarded to NDIS
372 * in order to be handled by the HNV module. */
373 OvsSendNBLIngress(switchContext
, nativeForwardedNbls
, SendFlags
);
375 #endif /* NDIS_SUPPORT_NDIS640 */
377 /* Queue the missed packets. */
378 OvsQueuePackets(&missedPackets
, num
);
379 OvsFinalizeCompletionList(&completionList
);
384 * --------------------------------------------------------------------------
385 * Implements filter driver's FilterSendNetBufferLists Function.
386 * --------------------------------------------------------------------------
389 OvsExtSendNBL(NDIS_HANDLE filterModuleContext
,
390 PNET_BUFFER_LIST netBufferLists
,
391 NDIS_PORT_NUMBER portNumber
,
394 UNREFERENCED_PARAMETER(portNumber
);
396 /* 'filterModuleContext' is the switch context that gets created in the
398 POVS_SWITCH_CONTEXT switchContext
;
399 switchContext
= (POVS_SWITCH_CONTEXT
) filterModuleContext
;
401 if (switchContext
->dataFlowState
== OvsSwitchPaused
) {
402 NDIS_STRING filterReason
;
403 ULONG sendCompleteFlags
= OvsGetSendCompleteFlags(sendFlags
);
405 RtlInitUnicodeString(&filterReason
,
406 L
"Switch state PAUSED, drop on ingress.");
407 OvsStartNBLIngressError(switchContext
, netBufferLists
,
408 sendCompleteFlags
, &filterReason
,
413 ASSERT(switchContext
->dataFlowState
== OvsSwitchRunning
);
415 OvsStartNBLIngress(switchContext
, netBufferLists
, sendFlags
);
419 OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext
,
420 PNET_BUFFER_LIST netBufferLists
,
421 ULONG sendCompleteFlags
)
423 PNET_BUFFER_LIST curNbl
= NULL
, nextNbl
= NULL
;
424 OvsCompletionList newList
;
426 newList
.dropNbl
= NULL
;
427 newList
.dropNblNext
= &newList
.dropNbl
;
429 for (curNbl
= netBufferLists
; curNbl
!= NULL
; curNbl
= nextNbl
) {
430 nextNbl
= curNbl
->Next
;
433 curNbl
= OvsCompleteNBL(switchContext
, curNbl
, TRUE
);
434 if (curNbl
!= NULL
) {
435 /* NBL originated from the upper layer. */
436 *newList
.dropNblNext
= curNbl
;
437 newList
.dropNblNext
= &curNbl
->Next
;
441 /* Complete the NBL's that were sent by the upper layer. */
442 if (newList
.dropNbl
!= NULL
) {
443 NdisFSendNetBufferListsComplete(switchContext
->NdisFilterHandle
, newList
.dropNbl
,
450 * --------------------------------------------------------------------------
451 * Implements filter driver's FilterSendNetBufferListsComplete function.
452 * --------------------------------------------------------------------------
455 OvsExtSendNBLComplete(NDIS_HANDLE filterModuleContext
,
456 PNET_BUFFER_LIST netBufferLists
,
457 ULONG sendCompleteFlags
)
459 OvsCompleteNBLIngress((POVS_SWITCH_CONTEXT
)filterModuleContext
,
460 netBufferLists
, sendCompleteFlags
);
465 OvsFinalizeCompletionList(OvsCompletionList
*completionList
)
467 if (completionList
->dropNbl
!= NULL
) {
468 OvsCompleteNBLIngress(completionList
->switchContext
,
469 completionList
->dropNbl
,
470 completionList
->sendCompleteFlags
);
472 completionList
->dropNbl
= NULL
;
473 completionList
->dropNblNext
= &completionList
->dropNbl
;
478 * --------------------------------------------------------------------------
479 * Implements filter driver's FilterCancelSendNetBufferLists function.
481 * "If a filter driver specifies a FilterSendNetBufferLists function and it
482 * queues send requests, it must also specify a
483 * FilterCancelSendNetBufferLists function."
485 * http://msdn.microsoft.com/en-us/library/windows/hardware/
486 * ff549966(v=vs.85).aspx
487 * --------------------------------------------------------------------------
490 OvsExtCancelSendNBL(NDIS_HANDLE filterModuleContext
,
493 UNREFERENCED_PARAMETER(filterModuleContext
);
494 UNREFERENCED_PARAMETER(CancelId
);
496 /* All send requests get completed synchronously, so there is no need to
497 * implement this callback. */
501 OvsCreateNewNBLsFromMultipleNBs(POVS_SWITCH_CONTEXT switchContext
,
502 PNET_BUFFER_LIST
*curNbl
,
503 PNET_BUFFER_LIST
*nextNbl
)
505 NTSTATUS status
= STATUS_SUCCESS
;
506 PNET_BUFFER_LIST newNbls
= NULL
;
507 PNET_BUFFER_LIST lastNbl
= NULL
;
508 PNET_BUFFER_LIST nbl
= 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 OvsCompleteNBL(switchContext
, *curNbl
, TRUE
);
532 (*curNbl
)->Next
= NULL
;