]> git.proxmox.com Git - mirror_ovs.git/blame - datapath-windows/ovsext/PacketIO.c
OPENFLOW-1.1+.md: Add OpenFlow 1.5 features.
[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"
fa1324c9
SG
23#include "Switch.h"
24#include "Vport.h"
25#include "NetProto.h"
26#include "User.h"
27#include "PacketIO.h"
28#include "Flow.h"
29#include "Event.h"
30#include "User.h"
c803536e
SS
31
32/* Due to an imported header file */
33#pragma warning( disable:4505 )
34
35#ifdef OVS_DBG_MOD
36#undef OVS_DBG_MOD
37#endif
38#define OVS_DBG_MOD OVS_DBG_DISPATCH
fa1324c9 39#include "Debug.h"
c803536e
SS
40
41extern NDIS_STRING ovsExtGuidUC;
42extern NDIS_STRING ovsExtFriendlyNameUC;
43
44static VOID OvsFinalizeCompletionList(OvsCompletionList *completionList);
45static VOID OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext,
46 PNET_BUFFER_LIST netBufferLists, ULONG sendCompleteFlags);
47
48__inline VOID
49OvsInitCompletionList(OvsCompletionList *completionList,
50 POVS_SWITCH_CONTEXT switchContext,
51 ULONG sendCompleteFlags)
52{
53 ASSERT(completionList);
54 completionList->dropNbl = NULL;
55 completionList->dropNblNext = &completionList->dropNbl;
56 completionList->switchContext = switchContext;
57 completionList->sendCompleteFlags = sendCompleteFlags;
58}
59
60/* Utility function used to complete an NBL. */
61__inline VOID
62OvsAddPktCompletionList(OvsCompletionList *completionList,
63 BOOLEAN incoming,
64 NDIS_SWITCH_PORT_ID sourcePort,
65 PNET_BUFFER_LIST netBufferList,
66 UINT32 netBufferListCount,
67 PNDIS_STRING filterReason)
68{
69 POVS_BUFFER_CONTEXT ctx;
70
71 /* XXX: We handle one NBL at a time. */
72 ASSERT(netBufferList->Next == NULL);
73
74 /* Make sure it has a context. */
75 ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(netBufferList);
76 ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC);
77
78 completionList->switchContext->NdisSwitchHandlers.ReportFilteredNetBufferLists(
79 completionList->switchContext->NdisSwitchContext, &ovsExtGuidUC,
80 &ovsExtFriendlyNameUC, sourcePort,
81 incoming ? NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING : 0,
82 netBufferListCount, netBufferList, filterReason);
83
84 *completionList->dropNblNext = netBufferList;
85 completionList->dropNblNext = &netBufferList->Next;
86 ASSERT(completionList->dropNbl);
87}
88
89static __inline VOID
90OvsReportNBLIngressError(POVS_SWITCH_CONTEXT switchContext,
91 PNET_BUFFER_LIST nblList,
92 PNDIS_STRING filterReason,
93 NDIS_STATUS error)
94{
95 PNET_BUFFER_LIST nbl = nblList;
96 while (nbl) {
97 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
98 fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(nbl);
99
100 nbl->Status = error;
101
102 /* This can be optimized by batching NBL's from the same
103 * SourcePortId. */
104 switchContext->NdisSwitchHandlers.ReportFilteredNetBufferLists(
105 switchContext->NdisSwitchContext, &ovsExtGuidUC,
106 &ovsExtFriendlyNameUC, fwdDetail->SourcePortId,
107 NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING,
108 1 /*Nbl count.*/, nbl, filterReason);
109
110 nbl = NET_BUFFER_LIST_NEXT_NBL(nbl);
111 }
112}
113
114static __inline ULONG
115OvsGetSendCompleteFlags(ULONG sendFlags)
116{
117 BOOLEAN dispatch, sameSource;
118 ULONG sendCompleteFlags;
119
120 dispatch = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(sendFlags);
121 sendCompleteFlags = (dispatch ?
122 NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0);
123 sameSource = NDIS_TEST_SEND_FLAG(sendFlags,
124 NDIS_SEND_FLAGS_SWITCH_SINGLE_SOURCE);
125 sendCompleteFlags |= (sameSource ?
126 NDIS_SEND_COMPLETE_FLAGS_SWITCH_SINGLE_SOURCE : 0);
127
128 return sendCompleteFlags;
129}
130
131VOID
132OvsSendNBLIngress(POVS_SWITCH_CONTEXT switchContext,
133 PNET_BUFFER_LIST netBufferLists,
134 ULONG sendFlags)
135{
136 if (switchContext->dataFlowState == OvsSwitchPaused) {
137 /* If a filter module is in the Paused state, the filter driver must not
138 * originate any send requests for that filter module. If NDIS calls
139 * FilterSendNetBufferLists, the driver must not call
140 * NdisFSendNetBufferLists to pass on the data until the driver is
141 * restarted. The driver should call NdisFSendNetBufferListsComplete
142 * immediately to complete the send operation. It should set the
143 * complete status in each NET_BUFFER_LIST structure to
144 * NDIS_STATUS_PAUSED.
145 *
146 * http://msdn.microsoft.com/en-us/library/windows/hardware/
147 * ff549966(v=vs.85).aspx */
148 NDIS_STRING filterReason;
149 ULONG sendCompleteFlags = OvsGetSendCompleteFlags(sendFlags);
150
151 RtlInitUnicodeString(&filterReason,
152 L"Switch state PAUSED, drop before FSendNBL.");
153 OvsReportNBLIngressError(switchContext, netBufferLists, &filterReason,
154 NDIS_STATUS_PAUSED);
155 OvsCompleteNBLIngress(switchContext, netBufferLists,
156 sendCompleteFlags);
157 return;
158 }
159
160 ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
161
162 NdisFSendNetBufferLists(switchContext->NdisFilterHandle, netBufferLists,
163 NDIS_DEFAULT_PORT_NUMBER, sendFlags);
164}
165
166static __inline VOID
167OvsStartNBLIngressError(POVS_SWITCH_CONTEXT switchContext,
168 PNET_BUFFER_LIST nblList,
169 ULONG sendCompleteFlags,
170 PNDIS_STRING filterReason,
171 NDIS_STATUS error)
172{
173 ASSERT(error);
174 OvsReportNBLIngressError(switchContext, nblList, filterReason, error);
175 NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle, nblList,
176 sendCompleteFlags);
177}
178
179static VOID
180OvsStartNBLIngress(POVS_SWITCH_CONTEXT switchContext,
181 PNET_BUFFER_LIST netBufferLists,
182 ULONG SendFlags)
183{
184 NDIS_SWITCH_PORT_ID sourcePort = 0;
185 NDIS_SWITCH_NIC_INDEX sourceIndex = 0;
186 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
187 PNET_BUFFER_LIST curNbl = NULL, nextNbl = NULL;
188 ULONG sendCompleteFlags;
189 UCHAR dispatch;
190 LOCK_STATE_EX lockState, dpLockState;
191 NDIS_STATUS status;
192 NDIS_STRING filterReason;
193 LIST_ENTRY missedPackets;
194 UINT32 num = 0;
195 OvsCompletionList completionList;
196
197 dispatch = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags)?
198 NDIS_RWL_AT_DISPATCH_LEVEL : 0;
199 sendCompleteFlags = OvsGetSendCompleteFlags(SendFlags);
200 SendFlags |= NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP;
201
202 InitializeListHead(&missedPackets);
203 OvsInitCompletionList(&completionList, switchContext, sendCompleteFlags);
204
205 for (curNbl = netBufferLists; curNbl != NULL; curNbl = nextNbl) {
206 POVS_VPORT_ENTRY vport;
207 UINT32 portNo;
208 OVS_DATAPATH *datapath = &switchContext->datapath;
209 OVS_PACKET_HDR_INFO layers;
210 OvsFlowKey key;
211 UINT64 hash;
212 PNET_BUFFER curNb;
213
214 nextNbl = curNbl->Next;
215 curNbl->Next = NULL;
216
217 /* Ethernet Header is a guaranteed safe access. */
218 curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
219 if (curNb->Next != NULL) {
220 /* XXX: This case is not handled yet. */
3bfe44a4
NR
221 RtlInitUnicodeString(&filterReason,
222 L"Dropping NBLs with multiple NBs");
223 OvsStartNBLIngressError(switchContext, curNbl,
224 sendCompleteFlags, &filterReason,
225 NDIS_STATUS_RESOURCES);
226 continue;
c803536e
SS
227 } else {
228 POVS_BUFFER_CONTEXT ctx;
229 OvsFlow *flow;
230
231 fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl);
232 sourcePort = fwdDetail->SourcePortId;
233 sourceIndex = (NDIS_SWITCH_NIC_INDEX)fwdDetail->SourceNicIndex;
234
235 /* Take the DispatchLock so none of the VPORTs disconnect while
236 * we are setting destination ports.
237 *
238 * XXX: acquire/release the dispatch lock for a "batch" of packets
239 * rather than for each packet. */
240 NdisAcquireRWLockRead(switchContext->dispatchLock, &lockState,
241 dispatch);
242
243 ctx = OvsInitExternalNBLContext(switchContext, curNbl,
7434992b 244 sourcePort == switchContext->virtualExternalPortId);
c803536e
SS
245 if (ctx == NULL) {
246 RtlInitUnicodeString(&filterReason,
247 L"Cannot allocate external NBL context.");
248
249 OvsStartNBLIngressError(switchContext, curNbl,
250 sendCompleteFlags, &filterReason,
251 NDIS_STATUS_RESOURCES);
252 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
253 continue;
254 }
255
256 vport = OvsFindVportByPortIdAndNicIndex(switchContext, sourcePort,
257 sourceIndex);
258 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
259 RtlInitUnicodeString(&filterReason,
260 L"OVS-Cannot forward packet from unknown source port");
261 goto dropit;
262 } else {
263 portNo = vport->portNo;
264 }
265
266 vport->stats.rxPackets++;
267 vport->stats.rxBytes += NET_BUFFER_DATA_LENGTH(curNb);
268
269 status = OvsExtractFlow(curNbl, vport->portNo, &key, &layers, NULL);
270 if (status != NDIS_STATUS_SUCCESS) {
271 RtlInitUnicodeString(&filterReason, L"OVS-Flow extract failed");
272 goto dropit;
273 }
274
275 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1ce4551c 276 OvsAcquireDatapathRead(datapath, &dpLockState, TRUE);
c803536e
SS
277
278 flow = OvsLookupFlow(datapath, &key, &hash, FALSE);
279 if (flow) {
280 OvsFlowUsed(flow, curNbl, &layers);
281 datapath->hits++;
282 /* If successful, OvsActionsExecute() consumes the NBL.
283 * Otherwise, it adds it to the completionList. No need to
284 * check the return value. */
285 OvsActionsExecute(switchContext, &completionList, curNbl,
286 portNo, SendFlags, &key, &hash, &layers,
287 flow->actions, flow->actionsLen);
288 OvsReleaseDatapath(datapath, &dpLockState);
289 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
290 continue;
291 } else {
292 OvsReleaseDatapath(datapath, &dpLockState);
293
294 datapath->misses++;
640ebde7 295 status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS,
7434992b
NR
296 portNo, &key, curNbl,
297 sourcePort == switchContext->virtualExternalPortId,
298 &layers, switchContext, &missedPackets, &num);
c803536e
SS
299 if (status == NDIS_STATUS_SUCCESS) {
300 /* Complete the packet since it was copied to user
301 * buffer. */
302 RtlInitUnicodeString(&filterReason,
303 L"OVS-Dropped since packet was copied to userspace");
304 } else {
305 RtlInitUnicodeString(&filterReason,
306 L"OVS-Dropped due to failure to queue to userspace");
307 }
308 goto dropit;
309 }
310
311dropit:
312 OvsAddPktCompletionList(&completionList, TRUE, sourcePort, curNbl, 0,
313 &filterReason);
314 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
315 }
316 }
317
318 /* Queue the missed packets. */
4a3c9b70 319 OvsQueuePackets(&missedPackets, num);
c803536e
SS
320 OvsFinalizeCompletionList(&completionList);
321}
322
323
324/*
325 * --------------------------------------------------------------------------
326 * Implements filter driver's FilterSendNetBufferLists Function.
327 * --------------------------------------------------------------------------
328 */
329VOID
330OvsExtSendNBL(NDIS_HANDLE filterModuleContext,
331 PNET_BUFFER_LIST netBufferLists,
332 NDIS_PORT_NUMBER portNumber,
333 ULONG sendFlags)
334{
335 UNREFERENCED_PARAMETER(portNumber);
336
337 /* 'filterModuleContext' is the switch context that gets created in the
338 * AttachHandler. */
339 POVS_SWITCH_CONTEXT switchContext;
340 switchContext = (POVS_SWITCH_CONTEXT) filterModuleContext;
341
342 if (switchContext->dataFlowState == OvsSwitchPaused) {
343 NDIS_STRING filterReason;
344 ULONG sendCompleteFlags = OvsGetSendCompleteFlags(sendFlags);
345
346 RtlInitUnicodeString(&filterReason,
347 L"Switch state PAUSED, drop on ingress.");
348 OvsStartNBLIngressError(switchContext, netBufferLists,
349 sendCompleteFlags, &filterReason,
350 NDIS_STATUS_PAUSED);
351 return;
352 }
353
354 ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
355
356 OvsStartNBLIngress(switchContext, netBufferLists, sendFlags);
357}
358
359static VOID
360OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext,
361 PNET_BUFFER_LIST netBufferLists,
362 ULONG sendCompleteFlags)
363{
364 PNET_BUFFER_LIST curNbl = NULL, nextNbl = NULL;
365 OvsCompletionList newList;
366
367 newList.dropNbl = NULL;
368 newList.dropNblNext = &newList.dropNbl;
369
370 for (curNbl = netBufferLists; curNbl != NULL; curNbl = nextNbl) {
371 nextNbl = curNbl->Next;
372 curNbl->Next = NULL;
373
374 curNbl = OvsCompleteNBL(switchContext, curNbl, TRUE);
375 if (curNbl != NULL) {
376 /* NBL originated from the upper layer. */
377 *newList.dropNblNext = curNbl;
378 newList.dropNblNext = &curNbl->Next;
379 }
380 }
381
382 /* Complete the NBL's that were sent by the upper layer. */
383 if (newList.dropNbl != NULL) {
384 NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle, newList.dropNbl,
385 sendCompleteFlags);
386 }
387}
388
389
390/*
391 * --------------------------------------------------------------------------
392 * Implements filter driver's FilterSendNetBufferListsComplete function.
393 * --------------------------------------------------------------------------
394 */
395VOID
396OvsExtSendNBLComplete(NDIS_HANDLE filterModuleContext,
397 PNET_BUFFER_LIST netBufferLists,
398 ULONG sendCompleteFlags)
399{
400 OvsCompleteNBLIngress((POVS_SWITCH_CONTEXT)filterModuleContext,
401 netBufferLists, sendCompleteFlags);
402}
403
404
405VOID
406OvsFinalizeCompletionList(OvsCompletionList *completionList)
407{
408 if (completionList->dropNbl != NULL) {
409 OvsCompleteNBLIngress(completionList->switchContext,
410 completionList->dropNbl,
411 completionList->sendCompleteFlags);
412
413 completionList->dropNbl = NULL;
414 completionList->dropNblNext = &completionList->dropNbl;
415 }
416}
417
418/*
419 * --------------------------------------------------------------------------
420 * Implements filter driver's FilterCancelSendNetBufferLists function.
421 *
422 * "If a filter driver specifies a FilterSendNetBufferLists function and it
423 * queues send requests, it must also specify a
424 * FilterCancelSendNetBufferLists function."
425 *
426 * http://msdn.microsoft.com/en-us/library/windows/hardware/
427 * ff549966(v=vs.85).aspx
428 * --------------------------------------------------------------------------
429 */
430VOID
431OvsExtCancelSendNBL(NDIS_HANDLE filterModuleContext,
432 PVOID CancelId)
433{
434 UNREFERENCED_PARAMETER(filterModuleContext);
435 UNREFERENCED_PARAMETER(CancelId);
436
437 /* All send requests get completed synchronously, so there is no need to
438 * implement this callback. */
439}