]> git.proxmox.com Git - mirror_ovs.git/blame - datapath-windows/ovsext/PacketIO.c
datapath-windows: Fix conntrack event handler
[mirror_ovs.git] / datapath-windows / ovsext / PacketIO.c
CommitLineData
c803536e
SS
1/*
2 * Copyright (c) 2014 VMware, Inc.
3 *
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:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17/*
18 * This file contains the implementation of the datapath/forwarding
19 * functionality of the OVS.
20 */
21
22#include "precomp.h"
ee25964a
SV
23
24#include "Actions.h"
fa1324c9
SG
25#include "Switch.h"
26#include "Vport.h"
27#include "NetProto.h"
28#include "User.h"
29#include "PacketIO.h"
30#include "Flow.h"
31#include "Event.h"
32#include "User.h"
c803536e
SS
33
34/* Due to an imported header file */
35#pragma warning( disable:4505 )
36
37#ifdef OVS_DBG_MOD
38#undef OVS_DBG_MOD
39#endif
40#define OVS_DBG_MOD OVS_DBG_DISPATCH
fa1324c9 41#include "Debug.h"
c803536e
SS
42
43extern NDIS_STRING ovsExtGuidUC;
44extern NDIS_STRING ovsExtFriendlyNameUC;
45
46static VOID OvsFinalizeCompletionList(OvsCompletionList *completionList);
47static VOID OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext,
48 PNET_BUFFER_LIST netBufferLists, ULONG sendCompleteFlags);
58b11928
SV
49static NTSTATUS OvsCreateNewNBLsFromMultipleNBs(
50 POVS_SWITCH_CONTEXT switchContext,
51 PNET_BUFFER_LIST *curNbl,
52 PNET_BUFFER_LIST *nextNbl);
c803536e
SS
53
54__inline VOID
55OvsInitCompletionList(OvsCompletionList *completionList,
56 POVS_SWITCH_CONTEXT switchContext,
57 ULONG sendCompleteFlags)
58{
59 ASSERT(completionList);
60 completionList->dropNbl = NULL;
61 completionList->dropNblNext = &completionList->dropNbl;
62 completionList->switchContext = switchContext;
63 completionList->sendCompleteFlags = sendCompleteFlags;
64}
65
66/* Utility function used to complete an NBL. */
67__inline VOID
68OvsAddPktCompletionList(OvsCompletionList *completionList,
69 BOOLEAN incoming,
70 NDIS_SWITCH_PORT_ID sourcePort,
71 PNET_BUFFER_LIST netBufferList,
72 UINT32 netBufferListCount,
73 PNDIS_STRING filterReason)
74{
75 POVS_BUFFER_CONTEXT ctx;
76
77 /* XXX: We handle one NBL at a time. */
78 ASSERT(netBufferList->Next == NULL);
79
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);
83
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);
89
90 *completionList->dropNblNext = netBufferList;
91 completionList->dropNblNext = &netBufferList->Next;
92 ASSERT(completionList->dropNbl);
93}
94
95static __inline VOID
96OvsReportNBLIngressError(POVS_SWITCH_CONTEXT switchContext,
97 PNET_BUFFER_LIST nblList,
98 PNDIS_STRING filterReason,
99 NDIS_STATUS error)
100{
101 PNET_BUFFER_LIST nbl = nblList;
102 while (nbl) {
103 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
104 fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(nbl);
105
106 nbl->Status = error;
107
108 /* This can be optimized by batching NBL's from the same
109 * SourcePortId. */
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);
115
116 nbl = NET_BUFFER_LIST_NEXT_NBL(nbl);
117 }
118}
119
120static __inline ULONG
121OvsGetSendCompleteFlags(ULONG sendFlags)
122{
123 BOOLEAN dispatch, sameSource;
124 ULONG sendCompleteFlags;
125
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);
133
134 return sendCompleteFlags;
135}
136
137VOID
138OvsSendNBLIngress(POVS_SWITCH_CONTEXT switchContext,
139 PNET_BUFFER_LIST netBufferLists,
140 ULONG sendFlags)
141{
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.
151 *
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);
156
157 RtlInitUnicodeString(&filterReason,
158 L"Switch state PAUSED, drop before FSendNBL.");
159 OvsReportNBLIngressError(switchContext, netBufferLists, &filterReason,
160 NDIS_STATUS_PAUSED);
161 OvsCompleteNBLIngress(switchContext, netBufferLists,
162 sendCompleteFlags);
163 return;
164 }
165
166 ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
167
168 NdisFSendNetBufferLists(switchContext->NdisFilterHandle, netBufferLists,
169 NDIS_DEFAULT_PORT_NUMBER, sendFlags);
170}
171
172static __inline VOID
173OvsStartNBLIngressError(POVS_SWITCH_CONTEXT switchContext,
174 PNET_BUFFER_LIST nblList,
175 ULONG sendCompleteFlags,
176 PNDIS_STRING filterReason,
177 NDIS_STATUS error)
178{
179 ASSERT(error);
180 OvsReportNBLIngressError(switchContext, nblList, filterReason, error);
181 NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle, nblList,
182 sendCompleteFlags);
183}
184
d0000b68
SV
185static VOID
186OvsAppendNativeForwardedPacket(POVS_SWITCH_CONTEXT switchContext,
187 PNET_BUFFER_LIST curNbl,
188 PNET_BUFFER_LIST *nativeNbls,
189 ULONG flags,
190 BOOLEAN isRecv)
191{
192 POVS_BUFFER_CONTEXT ctx = { 0 };
193 NDIS_STRING filterReason;
194
195 *nativeNbls = curNbl;
196 nativeNbls = &(curNbl->Next);
197
198 ctx = OvsInitExternalNBLContext(switchContext, curNbl, isRecv);
199 if (ctx == NULL) {
200 RtlInitUnicodeString(&filterReason,
201 L"Cannot allocate native NBL context.");
202
203 OvsStartNBLIngressError(switchContext, curNbl, flags, &filterReason,
204 NDIS_STATUS_RESOURCES);
205 }
206}
207
c803536e
SS
208static VOID
209OvsStartNBLIngress(POVS_SWITCH_CONTEXT switchContext,
210 PNET_BUFFER_LIST netBufferLists,
211 ULONG SendFlags)
212{
213 NDIS_SWITCH_PORT_ID sourcePort = 0;
214 NDIS_SWITCH_NIC_INDEX sourceIndex = 0;
215 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
216 PNET_BUFFER_LIST curNbl = NULL, nextNbl = NULL;
217 ULONG sendCompleteFlags;
218 UCHAR dispatch;
219 LOCK_STATE_EX lockState, dpLockState;
220 NDIS_STATUS status;
221 NDIS_STRING filterReason;
222 LIST_ENTRY missedPackets;
223 UINT32 num = 0;
224 OvsCompletionList completionList;
8217bc65 225#if (NDIS_SUPPORT_NDIS640)
d6b7cf62 226 PNET_BUFFER_LIST nativeForwardedNbls = NULL;
d6b7cf62 227 PNET_BUFFER_LIST *nextNativeForwardedNbl = &nativeForwardedNbls;
8217bc65 228#endif
c803536e
SS
229
230 dispatch = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags)?
231 NDIS_RWL_AT_DISPATCH_LEVEL : 0;
232 sendCompleteFlags = OvsGetSendCompleteFlags(SendFlags);
233 SendFlags |= NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP;
234
235 InitializeListHead(&missedPackets);
236 OvsInitCompletionList(&completionList, switchContext, sendCompleteFlags);
237
238 for (curNbl = netBufferLists; curNbl != NULL; curNbl = nextNbl) {
ee25964a
SV
239 POVS_VPORT_ENTRY vport = NULL;
240 UINT32 portNo = 0;
c803536e 241 OVS_DATAPATH *datapath = &switchContext->datapath;
ee25964a
SV
242 OVS_PACKET_HDR_INFO layers = { 0 };
243 OvsFlowKey key = { 0 };
244 UINT64 hash = 0;
245 PNET_BUFFER curNb = NULL;
246 POVS_BUFFER_CONTEXT ctx = NULL;
c803536e
SS
247
248 nextNbl = curNbl->Next;
249 curNbl->Next = NULL;
250
d0000b68
SV
251 fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl);
252 sourcePort = fwdDetail->SourcePortId;
253 sourceIndex = (NDIS_SWITCH_NIC_INDEX)fwdDetail->SourceNicIndex;
254
255#if (NDIS_SUPPORT_NDIS640)
256 if (fwdDetail->NativeForwardingRequired) {
257 /* Add current NBL to those that require native forwarding. */
258 OvsAppendNativeForwardedPacket(
259 switchContext,
260 curNbl,
261 nextNativeForwardedNbl,
262 sendCompleteFlags,
263 sourcePort == switchContext->virtualExternalPortId);
264 continue;
265 }
8217bc65 266#endif /* NDIS_SUPPORT_NDIS640 */
d0000b68 267
58b11928
SV
268 ctx = OvsInitExternalNBLContext(switchContext, curNbl,
269 sourcePort == switchContext->virtualExternalPortId);
270 if (ctx == NULL) {
3bfe44a4 271 RtlInitUnicodeString(&filterReason,
58b11928
SV
272 L"Cannot allocate external NBL context.");
273
3bfe44a4
NR
274 OvsStartNBLIngressError(switchContext, curNbl,
275 sendCompleteFlags, &filterReason,
276 NDIS_STATUS_RESOURCES);
277 continue;
58b11928
SV
278 }
279
280 /* Ethernet Header is a guaranteed safe access. */
281 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
282 if (curNb->Next != NULL) {
283 /* Create a NET_BUFFER_LIST for each NET_BUFFER. */
284 status = OvsCreateNewNBLsFromMultipleNBs(switchContext,
285 &curNbl,
286 &nextNbl);
287 if (!NT_SUCCESS(status)) {
288 RtlInitUnicodeString(&filterReason,
289 L"Cannot allocate NBLs with single NB.");
290
291 OvsStartNBLIngressError(switchContext, curNbl,
292 sendCompleteFlags, &filterReason,
293 NDIS_STATUS_RESOURCES);
294 continue;
295 }
296 }
297 {
c803536e
SS
298 OvsFlow *flow;
299
c803536e
SS
300 /* Take the DispatchLock so none of the VPORTs disconnect while
301 * we are setting destination ports.
302 *
303 * XXX: acquire/release the dispatch lock for a "batch" of packets
304 * rather than for each packet. */
305 NdisAcquireRWLockRead(switchContext->dispatchLock, &lockState,
306 dispatch);
307
c803536e
SS
308 vport = OvsFindVportByPortIdAndNicIndex(switchContext, sourcePort,
309 sourceIndex);
310 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
311 RtlInitUnicodeString(&filterReason,
312 L"OVS-Cannot forward packet from unknown source port");
313 goto dropit;
314 } else {
315 portNo = vport->portNo;
316 }
317
318 vport->stats.rxPackets++;
319 vport->stats.rxBytes += NET_BUFFER_DATA_LENGTH(curNb);
320
321 status = OvsExtractFlow(curNbl, vport->portNo, &key, &layers, NULL);
322 if (status != NDIS_STATUS_SUCCESS) {
323 RtlInitUnicodeString(&filterReason, L"OVS-Flow extract failed");
324 goto dropit;
325 }
326
327 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1ce4551c 328 OvsAcquireDatapathRead(datapath, &dpLockState, TRUE);
c803536e
SS
329
330 flow = OvsLookupFlow(datapath, &key, &hash, FALSE);
331 if (flow) {
332 OvsFlowUsed(flow, curNbl, &layers);
333 datapath->hits++;
334 /* If successful, OvsActionsExecute() consumes the NBL.
335 * Otherwise, it adds it to the completionList. No need to
336 * check the return value. */
337 OvsActionsExecute(switchContext, &completionList, curNbl,
4c470e88
SV
338 portNo, SendFlags, &key, &hash, &layers,
339 flow->actions, flow->actionsLen);
c803536e
SS
340 OvsReleaseDatapath(datapath, &dpLockState);
341 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
342 continue;
343 } else {
344 OvsReleaseDatapath(datapath, &dpLockState);
345
346 datapath->misses++;
640ebde7 347 status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS,
4c470e88 348 vport, &key, curNbl,
7434992b
NR
349 sourcePort == switchContext->virtualExternalPortId,
350 &layers, switchContext, &missedPackets, &num);
c803536e
SS
351 if (status == NDIS_STATUS_SUCCESS) {
352 /* Complete the packet since it was copied to user
353 * buffer. */
354 RtlInitUnicodeString(&filterReason,
355 L"OVS-Dropped since packet was copied to userspace");
356 } else {
357 RtlInitUnicodeString(&filterReason,
358 L"OVS-Dropped due to failure to queue to userspace");
359 }
360 goto dropit;
361 }
362
363dropit:
364 OvsAddPktCompletionList(&completionList, TRUE, sourcePort, curNbl, 0,
365 &filterReason);
366 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
367 }
368 }
369
8217bc65 370#if (NDIS_SUPPORT_NDIS640)
d6b7cf62
SV
371 if (nativeForwardedNbls) {
372 /* This is NVGRE encapsulated traffic and is forwarded to NDIS
373 * in order to be handled by the HNV module. */
374 OvsSendNBLIngress(switchContext, nativeForwardedNbls, SendFlags);
375 }
8217bc65 376#endif /* NDIS_SUPPORT_NDIS640 */
d6b7cf62 377
c803536e 378 /* Queue the missed packets. */
4a3c9b70 379 OvsQueuePackets(&missedPackets, num);
c803536e
SS
380 OvsFinalizeCompletionList(&completionList);
381}
382
383
384/*
385 * --------------------------------------------------------------------------
386 * Implements filter driver's FilterSendNetBufferLists Function.
387 * --------------------------------------------------------------------------
388 */
389VOID
390OvsExtSendNBL(NDIS_HANDLE filterModuleContext,
391 PNET_BUFFER_LIST netBufferLists,
392 NDIS_PORT_NUMBER portNumber,
393 ULONG sendFlags)
394{
395 UNREFERENCED_PARAMETER(portNumber);
396
397 /* 'filterModuleContext' is the switch context that gets created in the
398 * AttachHandler. */
399 POVS_SWITCH_CONTEXT switchContext;
400 switchContext = (POVS_SWITCH_CONTEXT) filterModuleContext;
401
402 if (switchContext->dataFlowState == OvsSwitchPaused) {
403 NDIS_STRING filterReason;
404 ULONG sendCompleteFlags = OvsGetSendCompleteFlags(sendFlags);
405
406 RtlInitUnicodeString(&filterReason,
407 L"Switch state PAUSED, drop on ingress.");
408 OvsStartNBLIngressError(switchContext, netBufferLists,
409 sendCompleteFlags, &filterReason,
410 NDIS_STATUS_PAUSED);
411 return;
412 }
413
414 ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
415
416 OvsStartNBLIngress(switchContext, netBufferLists, sendFlags);
417}
418
419static VOID
420OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext,
421 PNET_BUFFER_LIST netBufferLists,
422 ULONG sendCompleteFlags)
423{
424 PNET_BUFFER_LIST curNbl = NULL, nextNbl = NULL;
425 OvsCompletionList newList;
426
427 newList.dropNbl = NULL;
428 newList.dropNblNext = &newList.dropNbl;
429
430 for (curNbl = netBufferLists; curNbl != NULL; curNbl = nextNbl) {
431 nextNbl = curNbl->Next;
432 curNbl->Next = NULL;
433
434 curNbl = OvsCompleteNBL(switchContext, curNbl, TRUE);
435 if (curNbl != NULL) {
436 /* NBL originated from the upper layer. */
437 *newList.dropNblNext = curNbl;
438 newList.dropNblNext = &curNbl->Next;
439 }
440 }
441
442 /* Complete the NBL's that were sent by the upper layer. */
443 if (newList.dropNbl != NULL) {
444 NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle, newList.dropNbl,
445 sendCompleteFlags);
446 }
447}
448
449
450/*
451 * --------------------------------------------------------------------------
452 * Implements filter driver's FilterSendNetBufferListsComplete function.
453 * --------------------------------------------------------------------------
454 */
455VOID
456OvsExtSendNBLComplete(NDIS_HANDLE filterModuleContext,
457 PNET_BUFFER_LIST netBufferLists,
458 ULONG sendCompleteFlags)
459{
460 OvsCompleteNBLIngress((POVS_SWITCH_CONTEXT)filterModuleContext,
461 netBufferLists, sendCompleteFlags);
462}
463
464
465VOID
466OvsFinalizeCompletionList(OvsCompletionList *completionList)
467{
468 if (completionList->dropNbl != NULL) {
469 OvsCompleteNBLIngress(completionList->switchContext,
470 completionList->dropNbl,
471 completionList->sendCompleteFlags);
472
473 completionList->dropNbl = NULL;
474 completionList->dropNblNext = &completionList->dropNbl;
475 }
476}
477
478/*
479 * --------------------------------------------------------------------------
480 * Implements filter driver's FilterCancelSendNetBufferLists function.
481 *
482 * "If a filter driver specifies a FilterSendNetBufferLists function and it
483 * queues send requests, it must also specify a
484 * FilterCancelSendNetBufferLists function."
485 *
486 * http://msdn.microsoft.com/en-us/library/windows/hardware/
487 * ff549966(v=vs.85).aspx
488 * --------------------------------------------------------------------------
489 */
490VOID
491OvsExtCancelSendNBL(NDIS_HANDLE filterModuleContext,
492 PVOID CancelId)
493{
494 UNREFERENCED_PARAMETER(filterModuleContext);
495 UNREFERENCED_PARAMETER(CancelId);
496
497 /* All send requests get completed synchronously, so there is no need to
498 * implement this callback. */
499}
58b11928
SV
500
501static NTSTATUS
502OvsCreateNewNBLsFromMultipleNBs(POVS_SWITCH_CONTEXT switchContext,
503 PNET_BUFFER_LIST *curNbl,
504 PNET_BUFFER_LIST *nextNbl)
505{
506 NTSTATUS status = STATUS_SUCCESS;
507 PNET_BUFFER_LIST newNbls = NULL;
508 PNET_BUFFER_LIST lastNbl = NULL;
509 PNET_BUFFER_LIST nbl = NULL;
58b11928
SV
510 BOOLEAN error = TRUE;
511
512 do {
513 /* Create new NBLs from curNbl with multiple net buffers. */
514 newNbls = OvsPartialCopyToMultipleNBLs(switchContext,
515 *curNbl, 0, 0, TRUE);
516 if (NULL == newNbls) {
517 OVS_LOG_ERROR("Failed to allocate NBLs with single NB.");
518 status = NDIS_STATUS_RESOURCES;
519 break;
520 }
521
522 nbl = newNbls;
523 while (nbl) {
524 lastNbl = nbl;
525 nbl = NET_BUFFER_LIST_NEXT_NBL(nbl);
526 }
527 lastNbl->Next = *nextNbl;
528 *nextNbl = newNbls->Next;
58b11928
SV
529
530 OvsCompleteNBL(switchContext, *curNbl, TRUE);
531
21f21788
SV
532 *curNbl = newNbls;
533 (*curNbl)->Next = NULL;
534
58b11928
SV
535 error = FALSE;
536 } while (error);
537
538 return status;
539}