]> git.proxmox.com Git - ovs.git/blame - datapath-windows/ovsext/Actions.c
datapath-windows: Avoid using uninitialized gOvsExtDriverHandle
[ovs.git] / datapath-windows / ovsext / Actions.c
CommitLineData
c803536e 1/*
7b383a56 2 * Copyright (c) 2014, 2016 VMware, Inc.
c803536e
SS
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#include "precomp.h"
18
ee25964a 19#include "Actions.h"
792d377d 20#include "Conntrack.h"
7b383a56 21#include "Debug.h"
fa1324c9 22#include "Event.h"
fa1324c9 23#include "Flow.h"
85571a3d 24#include "Gre.h"
245eedef 25#include "Jhash.h"
5874d571 26#include "Mpls.h"
85571a3d 27#include "NetProto.h"
7b383a56 28#include "Offload.h"
fa1324c9 29#include "PacketIO.h"
ee25964a 30#include "Recirc.h"
85571a3d
AS
31#include "Stt.h"
32#include "Switch.h"
33#include "User.h"
34#include "Vport.h"
35#include "Vxlan.h"
c803536e 36
c803536e
SS
37#ifdef OVS_DBG_MOD
38#undef OVS_DBG_MOD
39#endif
40#define OVS_DBG_MOD OVS_DBG_ACTION
c803536e 41
ee25964a
SV
42#define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2
43
c803536e 44typedef struct _OVS_ACTION_STATS {
85571a3d
AS
45 UINT64 rxGre;
46 UINT64 txGre;
c803536e
SS
47 UINT64 rxVxlan;
48 UINT64 txVxlan;
022c2040
EE
49 UINT64 rxStt;
50 UINT64 txStt;
c803536e
SS
51 UINT64 flowMiss;
52 UINT64 flowUserspace;
53 UINT64 txTcp;
54 UINT32 failedFlowMiss;
55 UINT32 noVport;
56 UINT32 failedFlowExtract;
57 UINT32 noResource;
58 UINT32 noCopiedNbl;
59 UINT32 failedEncap;
60 UINT32 failedDecap;
61 UINT32 cannotGrowDest;
62 UINT32 zeroActionLen;
63 UINT32 failedChecksum;
ee25964a
SV
64 UINT32 deferredActionsQueueFull;
65 UINT32 deferredActionsExecLimit;
c803536e
SS
66} OVS_ACTION_STATS, *POVS_ACTION_STATS;
67
68OVS_ACTION_STATS ovsActionStats;
69
70/*
71 * There a lot of data that needs to be maintained while executing the pipeline
72 * as dictated by the actions of a flow, across different functions at different
73 * levels. Such data is put together in a 'context' structure. Care should be
74 * exercised while adding new members to the structure - only add ones that get
75 * used across multiple stages in the pipeline/get used in multiple functions.
76 */
c803536e
SS
77typedef struct OvsForwardingContext {
78 POVS_SWITCH_CONTEXT switchContext;
79 /* The NBL currently used in the pipeline. */
80 PNET_BUFFER_LIST curNbl;
81 /* NDIS forwarding detail for 'curNbl'. */
82 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
83 /* Array of destination ports for 'curNbl'. */
84 PNDIS_SWITCH_FORWARDING_DESTINATION_ARRAY destinationPorts;
85 /* send flags while sending 'curNbl' into NDIS. */
86 ULONG sendFlags;
87 /* Total number of output ports, used + unused, in 'curNbl'. */
88 UINT32 destPortsSizeIn;
89 /* Total number of used output ports in 'curNbl'. */
90 UINT32 destPortsSizeOut;
91 /*
92 * If 'curNbl' is not owned by OVS, they need to be tracked, if they need to
93 * be freed/completed.
94 */
95 OvsCompletionList *completionList;
96 /*
97 * vport number of 'curNbl' when it is passed from the PIF bridge to the INT
98 * bridge. ie. during tunneling on the Rx side.
99 */
100 UINT32 srcVportNo;
101
102 /*
103 * Tunnel key:
104 * - specified in actions during tunneling Tx
105 * - extracted from an NBL during tunneling Rx
106 */
107 OvsIPv4TunnelKey tunKey;
108
ee25964a 109 /*
c803536e
SS
110 * Tunneling - Tx:
111 * To store the output port, when it is a tunneled port. We don't foresee
112 * multiple tunneled ports as outport for any given NBL.
113 */
114 POVS_VPORT_ENTRY tunnelTxNic;
115
116 /*
117 * Tunneling - Rx:
118 * Points to the Internal port on the PIF Bridge, if the packet needs to be
119 * de-tunneled.
120 */
121 POVS_VPORT_ENTRY tunnelRxNic;
122
123 /* header information */
124 OVS_PACKET_HDR_INFO layers;
125} OvsForwardingContext;
126
c803536e
SS
127/*
128 * --------------------------------------------------------------------------
129 * OvsInitForwardingCtx --
130 * Function to init/re-init the 'ovsFwdCtx' context as the actions pipeline
131 * is being executed.
132 *
133 * Result:
134 * NDIS_STATUS_SUCCESS on success
135 * Other NDIS_STATUS upon failure. Upon failure, it is safe to call
136 * OvsCompleteNBLForwardingCtx(), since 'ovsFwdCtx' has been initialized
137 * enough for OvsCompleteNBLForwardingCtx() to do its work.
138 * --------------------------------------------------------------------------
139 */
140static __inline NDIS_STATUS
141OvsInitForwardingCtx(OvsForwardingContext *ovsFwdCtx,
142 POVS_SWITCH_CONTEXT switchContext,
143 PNET_BUFFER_LIST curNbl,
144 UINT32 srcVportNo,
145 ULONG sendFlags,
146 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail,
147 OvsCompletionList *completionList,
148 OVS_PACKET_HDR_INFO *layers,
149 BOOLEAN resetTunnelInfo)
150{
151 ASSERT(ovsFwdCtx);
152 ASSERT(switchContext);
153 ASSERT(curNbl);
154 ASSERT(fwdDetail);
155
156 /*
157 * Set values for curNbl and switchContext so upon failures, we have enough
158 * information to do cleanup.
159 */
160 ovsFwdCtx->curNbl = curNbl;
161 ovsFwdCtx->switchContext = switchContext;
162 ovsFwdCtx->completionList = completionList;
163 ovsFwdCtx->fwdDetail = fwdDetail;
164
165 if (fwdDetail->NumAvailableDestinations > 0) {
166 /*
167 * XXX: even though MSDN says GetNetBufferListDestinations() returns
168 * NDIS_STATUS, the header files say otherwise.
169 */
170 switchContext->NdisSwitchHandlers.GetNetBufferListDestinations(
171 switchContext->NdisSwitchContext, curNbl,
172 &ovsFwdCtx->destinationPorts);
173
174 ASSERT(ovsFwdCtx->destinationPorts);
175 /* Ensure that none of the elements are consumed yet. */
176 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
177 fwdDetail->NumAvailableDestinations);
178 } else {
179 ovsFwdCtx->destinationPorts = NULL;
180 }
181 ovsFwdCtx->destPortsSizeIn = fwdDetail->NumAvailableDestinations;
182 ovsFwdCtx->destPortsSizeOut = 0;
183 ovsFwdCtx->srcVportNo = srcVportNo;
184 ovsFwdCtx->sendFlags = sendFlags;
185 if (layers) {
186 ovsFwdCtx->layers = *layers;
187 } else {
188 RtlZeroMemory(&ovsFwdCtx->layers, sizeof ovsFwdCtx->layers);
189 }
190 if (resetTunnelInfo) {
191 ovsFwdCtx->tunnelTxNic = NULL;
192 ovsFwdCtx->tunnelRxNic = NULL;
193 RtlZeroMemory(&ovsFwdCtx->tunKey, sizeof ovsFwdCtx->tunKey);
194 }
195
196 return NDIS_STATUS_SUCCESS;
197}
198
199/*
200 * --------------------------------------------------------------------------
201 * OvsDetectTunnelRxPkt --
202 * Utility function for an RX packet to detect its tunnel type.
203 *
204 * Result:
205 * True - if the tunnel type was detected.
206 * False - if not a tunnel packet or tunnel type not supported.
207 * --------------------------------------------------------------------------
208 */
209static __inline BOOLEAN
210OvsDetectTunnelRxPkt(OvsForwardingContext *ovsFwdCtx,
211 const OvsFlowKey *flowKey)
212{
213 POVS_VPORT_ENTRY tunnelVport = NULL;
214
215 /* XXX: we should also check for the length of the UDP payload to pick
216 * packets only if they are at least VXLAN header size.
217 */
85571a3d 218 if (!flowKey->ipKey.nwFrag) {
022c2040 219 UINT16 dstPort = htons(flowKey->ipKey.l4.tpDst);
85571a3d
AS
220 switch (flowKey->ipKey.nwProto) {
221 case IPPROTO_GRE:
222 tunnelVport = OvsFindTunnelVportByPortType(ovsFwdCtx->switchContext,
223 OVS_VPORT_TYPE_GRE);
224 if (tunnelVport) {
225 ovsActionStats.rxGre++;
226 }
227 break;
228 case IPPROTO_TCP:
229 tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
230 dstPort,
231 OVS_VPORT_TYPE_STT);
232 if (tunnelVport) {
233 ovsActionStats.rxStt++;
234 }
235 break;
236 case IPPROTO_UDP:
237 tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
238 dstPort,
239 OVS_VPORT_TYPE_VXLAN);
240 if (tunnelVport) {
241 ovsActionStats.rxVxlan++;
242 }
243 break;
022c2040 244 }
c803536e
SS
245 }
246
247 // We might get tunnel packets even before the tunnel gets initialized.
248 if (tunnelVport) {
249 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
250 ovsFwdCtx->tunnelRxNic = tunnelVport;
251 return TRUE;
252 }
253
254 return FALSE;
255}
256
257/*
258 * --------------------------------------------------------------------------
259 * OvsDetectTunnelPkt --
b2d9d3e8
NR
260 * Utility function to detect if a packet is to be subjected to
261 * tunneling (Tx) or de-tunneling (Rx). Various factors such as source
262 * port, destination port, packet contents, and previously setup tunnel
263 * context are used.
c803536e
SS
264 *
265 * Result:
b2d9d3e8
NR
266 * True - If the packet is to be subjected to tunneling.
267 * In case of invalid tunnel context, the tunneling functionality is
268 * a no-op and is completed within this function itself by consuming
269 * all of the tunneling context.
270 * False - If not a tunnel packet or tunnel type not supported. Caller should
271 * process the packet as a non-tunnel packet.
c803536e
SS
272 * --------------------------------------------------------------------------
273 */
274static __inline BOOLEAN
275OvsDetectTunnelPkt(OvsForwardingContext *ovsFwdCtx,
276 const POVS_VPORT_ENTRY dstVport,
277 const OvsFlowKey *flowKey)
278{
c803536e 279 if (OvsIsInternalVportType(dstVport->ovsType)) {
b2d9d3e8
NR
280 /*
281 * Rx:
282 * The source of NBL during tunneling Rx could be the external
283 * port or if it is being executed from userspace, the source port is
284 * default port.
285 */
7434992b
NR
286 BOOLEAN validSrcPort =
287 (ovsFwdCtx->fwdDetail->SourcePortId ==
288 ovsFwdCtx->switchContext->virtualExternalPortId) ||
289 (ovsFwdCtx->fwdDetail->SourcePortId ==
290 NDIS_SWITCH_DEFAULT_PORT_ID);
c803536e
SS
291
292 if (validSrcPort && OvsDetectTunnelRxPkt(ovsFwdCtx, flowKey)) {
293 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
294 ASSERT(ovsFwdCtx->tunnelRxNic != NULL);
295 return TRUE;
296 }
297 } else if (OvsIsTunnelVportType(dstVport->ovsType)) {
298 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
299 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
b2d9d3e8
NR
300
301 /*
302 * Tx:
303 * The destination port is a tunnel port. Encapsulation must be
30bc8153
NR
304 * performed only on packets that originate from:
305 * - a VIF port
306 * - a bridge-internal port (packets generated from userspace)
307 * - no port.
b2d9d3e8
NR
308 *
309 * If the packet will not be encapsulated, consume the tunnel context
310 * by clearing it.
311 */
12e888ba 312 if (ovsFwdCtx->srcVportNo != OVS_DPPORT_NUMBER_INVALID) {
429d4556
AS
313
314 POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(
315 ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
316
30bc8153
NR
317 if (!vport ||
318 (vport->ovsType != OVS_VPORT_TYPE_NETDEV &&
319 !OvsIsBridgeInternalVport(vport))) {
429d4556
AS
320 ovsFwdCtx->tunKey.dst = 0;
321 }
b2d9d3e8
NR
322 }
323
324 /* Tunnel the packet only if tunnel context is set. */
325 if (ovsFwdCtx->tunKey.dst != 0) {
022c2040 326 switch(dstVport->ovsType) {
85571a3d
AS
327 case OVS_VPORT_TYPE_GRE:
328 ovsActionStats.txGre++;
329 break;
022c2040
EE
330 case OVS_VPORT_TYPE_VXLAN:
331 ovsActionStats.txVxlan++;
332 break;
333 case OVS_VPORT_TYPE_STT:
334 ovsActionStats.txStt++;
335 break;
336 }
b2d9d3e8
NR
337 ovsFwdCtx->tunnelTxNic = dstVport;
338 }
339
c803536e
SS
340 return TRUE;
341 }
342
343 return FALSE;
344}
345
346
347/*
348 * --------------------------------------------------------------------------
349 * OvsAddPorts --
350 * Add the specified destination vport into the forwarding context. If the
351 * vport is a VIF/external port, it is added directly to the NBL. If it is
352 * a tunneling port, it is NOT added to the NBL.
353 *
354 * Result:
355 * NDIS_STATUS_SUCCESS on success
356 * Other NDIS_STATUS upon failure.
357 * --------------------------------------------------------------------------
358 */
359static __inline NDIS_STATUS
360OvsAddPorts(OvsForwardingContext *ovsFwdCtx,
361 OvsFlowKey *flowKey,
362 NDIS_SWITCH_PORT_ID dstPortId,
363 BOOLEAN preserveVLAN,
364 BOOLEAN preservePriority)
365{
366 POVS_VPORT_ENTRY vport;
367 PNDIS_SWITCH_PORT_DESTINATION fwdPort;
368 NDIS_STATUS status;
369 POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext;
370
371 /*
372 * We hold the dispatch lock that protects the list of vports, so vports
373 * validated here can be added as destinations safely before we call into
374 * NDIS.
375 *
376 * Some of the vports can be tunnelled ports as well in which case
377 * they should be added to a separate list of tunnelled destination ports
378 * instead of the VIF ports. The context for the tunnel is settable
379 * in OvsForwardingContext.
380 */
381 vport = OvsFindVportByPortNo(ovsFwdCtx->switchContext, dstPortId);
382 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
383 /*
384 * There may be some latency between a port disappearing, and userspace
385 * updating the recalculated flows. In the meantime, handle invalid
386 * ports gracefully.
387 */
388 ovsActionStats.noVport++;
389 return NDIS_STATUS_SUCCESS;
390 }
391 ASSERT(vport->nicState == NdisSwitchNicStateConnected);
392 vport->stats.txPackets++;
393 vport->stats.txBytes +=
394 NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl));
395
7434992b
NR
396 if (OvsIsBridgeInternalVport(vport)) {
397 return NDIS_STATUS_SUCCESS;
398 }
399
c803536e 400 if (OvsDetectTunnelPkt(ovsFwdCtx, vport, flowKey)) {
c803536e
SS
401 return NDIS_STATUS_SUCCESS;
402 }
403
404 if (ovsFwdCtx->destPortsSizeOut == ovsFwdCtx->destPortsSizeIn) {
405 if (ovsFwdCtx->destPortsSizeIn == 0) {
406 ASSERT(ovsFwdCtx->destinationPorts == NULL);
407 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0);
408 status =
409 switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations(
410 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
411 OVS_DEST_PORTS_ARRAY_MIN_SIZE,
412 &ovsFwdCtx->destinationPorts);
413 if (status != NDIS_STATUS_SUCCESS) {
414 ovsActionStats.cannotGrowDest++;
415 return status;
416 }
417 ovsFwdCtx->destPortsSizeIn =
418 ovsFwdCtx->fwdDetail->NumAvailableDestinations;
419 ASSERT(ovsFwdCtx->destinationPorts);
420 } else {
421 ASSERT(ovsFwdCtx->destinationPorts != NULL);
422 /*
423 * NumElements:
424 * A ULONG value that specifies the total number of
425 * NDIS_SWITCH_PORT_DESTINATION elements in the
426 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure.
427 *
428 * NumDestinations:
429 * A ULONG value that specifies the number of
430 * NDIS_SWITCH_PORT_DESTINATION elements in the
431 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure that
432 * specify port destinations.
433 *
434 * NumAvailableDestinations:
435 * A value that specifies the number of unused extensible switch
436 * destination ports elements within an NET_BUFFER_LIST structure.
437 */
438 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
439 ovsFwdCtx->destPortsSizeIn);
440 ASSERT(ovsFwdCtx->destinationPorts->NumDestinations ==
441 ovsFwdCtx->destPortsSizeOut -
442 ovsFwdCtx->fwdDetail->NumAvailableDestinations);
443 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations > 0);
444 /*
445 * Before we grow the array of destination ports, the current set
446 * of ports needs to be committed. Only the ports added since the
447 * last commit need to be part of the new update.
448 */
449 status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations(
450 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
451 ovsFwdCtx->fwdDetail->NumAvailableDestinations,
452 ovsFwdCtx->destinationPorts);
453 if (status != NDIS_STATUS_SUCCESS) {
454 ovsActionStats.cannotGrowDest++;
455 return status;
456 }
457 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
458 ovsFwdCtx->destPortsSizeIn);
459 ASSERT(ovsFwdCtx->destinationPorts->NumDestinations ==
460 ovsFwdCtx->destPortsSizeOut);
461 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0);
462
463 status = switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations(
464 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
465 ovsFwdCtx->destPortsSizeIn, &ovsFwdCtx->destinationPorts);
466 if (status != NDIS_STATUS_SUCCESS) {
467 ovsActionStats.cannotGrowDest++;
468 return status;
469 }
470 ASSERT(ovsFwdCtx->destinationPorts != NULL);
471 ovsFwdCtx->destPortsSizeIn <<= 1;
472 }
473 }
474
475 ASSERT(ovsFwdCtx->destPortsSizeOut < ovsFwdCtx->destPortsSizeIn);
476 fwdPort =
477 NDIS_SWITCH_PORT_DESTINATION_AT_ARRAY_INDEX(ovsFwdCtx->destinationPorts,
478 ovsFwdCtx->destPortsSizeOut);
479
480 fwdPort->PortId = vport->portId;
481 fwdPort->NicIndex = vport->nicIndex;
482 fwdPort->IsExcluded = 0;
483 fwdPort->PreserveVLAN = preserveVLAN;
484 fwdPort->PreservePriority = preservePriority;
485 ovsFwdCtx->destPortsSizeOut += 1;
486
487 return NDIS_STATUS_SUCCESS;
488}
489
490
491/*
492 * --------------------------------------------------------------------------
493 * OvsClearTunTxCtx --
494 * Utility function to clear tx tunneling context.
495 * --------------------------------------------------------------------------
496 */
497static __inline VOID
498OvsClearTunTxCtx(OvsForwardingContext *ovsFwdCtx)
499{
500 ovsFwdCtx->tunnelTxNic = NULL;
501 ovsFwdCtx->tunKey.dst = 0;
502}
503
504
505/*
506 * --------------------------------------------------------------------------
507 * OvsClearTunRxCtx --
508 * Utility function to clear rx tunneling context.
509 * --------------------------------------------------------------------------
510 */
511static __inline VOID
512OvsClearTunRxCtx(OvsForwardingContext *ovsFwdCtx)
513{
514 ovsFwdCtx->tunnelRxNic = NULL;
515 ovsFwdCtx->tunKey.dst = 0;
516}
517
518
519/*
520 * --------------------------------------------------------------------------
521 * OvsCompleteNBLForwardingCtx --
522 * This utility function is responsible for freeing/completing an NBL - either
523 * by adding it to a completion list or by freeing it.
524 *
525 * Side effects:
526 * It also resets the necessary fields in 'ovsFwdCtx'.
527 * --------------------------------------------------------------------------
528 */
529static __inline VOID
530OvsCompleteNBLForwardingCtx(OvsForwardingContext *ovsFwdCtx,
531 PCWSTR dropReason)
532{
533 NDIS_STRING filterReason;
534
535 RtlInitUnicodeString(&filterReason, dropReason);
536 if (ovsFwdCtx->completionList) {
537 OvsAddPktCompletionList(ovsFwdCtx->completionList, TRUE,
538 ovsFwdCtx->fwdDetail->SourcePortId, ovsFwdCtx->curNbl, 1,
539 &filterReason);
540 ovsFwdCtx->curNbl = NULL;
541 } else {
542 /* If there is no completionList, we assume this is ovs created NBL */
543 ovsFwdCtx->curNbl = OvsCompleteNBL(ovsFwdCtx->switchContext,
544 ovsFwdCtx->curNbl, TRUE);
545 ASSERT(ovsFwdCtx->curNbl == NULL);
546 }
547 /* XXX: these can be made debug only to save cycles. Ideally the pipeline
548 * using these fields should reset the values at the end of the pipeline. */
549 ovsFwdCtx->destPortsSizeOut = 0;
550 ovsFwdCtx->tunnelTxNic = NULL;
551 ovsFwdCtx->tunnelRxNic = NULL;
552}
553
554/*
555 * --------------------------------------------------------------------------
556 * OvsDoFlowLookupOutput --
557 * Function to be used for the second stage of a tunneling workflow, ie.:
558 * - On the encapsulated packet on Tx path, to do a flow extract, flow
559 * lookup and excuting the actions.
560 * - On the decapsulated packet on Rx path, to do a flow extract, flow
561 * lookup and excuting the actions.
562 *
563 * XXX: It is assumed that the NBL in 'ovsFwdCtx' is owned by OVS. This is
564 * until the new buffer management framework is adopted.
565 *
566 * Side effects:
567 * The NBL in 'ovsFwdCtx' is consumed.
568 * --------------------------------------------------------------------------
569 */
570static __inline NDIS_STATUS
571OvsDoFlowLookupOutput(OvsForwardingContext *ovsFwdCtx)
572{
ee25964a
SV
573 OvsFlowKey key = { 0 };
574 OvsFlow *flow = NULL;
575 UINT64 hash = 0;
576 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
c803536e
SS
577 POVS_VPORT_ENTRY vport =
578 OvsFindVportByPortNo(ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
579 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
c803536e
SS
580 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
581 L"OVS-Dropped due to internal/tunnel port removal");
582 ovsActionStats.noVport++;
583 return NDIS_STATUS_SUCCESS;
584 }
585 ASSERT(vport->nicState == NdisSwitchNicStateConnected);
586
587 /* Assert that in the Rx direction, key is always setup. */
588 ASSERT(ovsFwdCtx->tunnelRxNic == NULL || ovsFwdCtx->tunKey.dst != 0);
4c470e88
SV
589 status =
590 OvsExtractFlow(ovsFwdCtx->curNbl, ovsFwdCtx->srcVportNo,
591 &key, &ovsFwdCtx->layers,
592 ovsFwdCtx->tunKey.dst != 0 ? &ovsFwdCtx->tunKey : NULL);
c803536e
SS
593 if (status != NDIS_STATUS_SUCCESS) {
594 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
595 L"OVS-Flow extract failed");
596 ovsActionStats.failedFlowExtract++;
597 return status;
598 }
599
600 flow = OvsLookupFlow(&ovsFwdCtx->switchContext->datapath, &key, &hash, FALSE);
601 if (flow) {
602 OvsFlowUsed(flow, ovsFwdCtx->curNbl, &ovsFwdCtx->layers);
603 ovsFwdCtx->switchContext->datapath.hits++;
ee25964a
SV
604 status = OvsDoExecuteActions(ovsFwdCtx->switchContext,
605 ovsFwdCtx->completionList,
606 ovsFwdCtx->curNbl,
607 ovsFwdCtx->srcVportNo,
608 ovsFwdCtx->sendFlags,
609 &key, &hash, &ovsFwdCtx->layers,
610 flow->actions, flow->actionsLen);
c803536e
SS
611 ovsFwdCtx->curNbl = NULL;
612 } else {
613 LIST_ENTRY missedPackets;
614 UINT32 num = 0;
615 ovsFwdCtx->switchContext->datapath.misses++;
616 InitializeListHead(&missedPackets);
4c470e88 617 status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS, vport,
640ebde7 618 &key,ovsFwdCtx->curNbl,
a422ea1d 619 FALSE, &ovsFwdCtx->layers,
640ebde7 620 ovsFwdCtx->switchContext, &missedPackets, &num);
c803536e 621 if (num) {
4a3c9b70 622 OvsQueuePackets(&missedPackets, num);
c803536e
SS
623 }
624 if (status == NDIS_STATUS_SUCCESS) {
625 /* Complete the packet since it was copied to user buffer. */
626 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
627 L"OVS-Dropped since packet was copied to userspace");
628 ovsActionStats.flowMiss++;
629 status = NDIS_STATUS_SUCCESS;
630 } else {
631 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
632 L"OVS-Dropped due to failure to queue to userspace");
633 status = NDIS_STATUS_FAILURE;
634 ovsActionStats.failedFlowMiss++;
635 }
636 }
637
638 return status;
639}
640
641/*
642 * --------------------------------------------------------------------------
643 * OvsTunnelPortTx --
644 * The start function for Tx tunneling - encapsulates the packet, and
645 * outputs the packet on the PIF bridge.
646 *
647 * Side effects:
648 * The NBL in 'ovsFwdCtx' is consumed.
649 * --------------------------------------------------------------------------
650 */
651static __inline NDIS_STATUS
652OvsTunnelPortTx(OvsForwardingContext *ovsFwdCtx)
653{
654 NDIS_STATUS status = NDIS_STATUS_FAILURE;
655 PNET_BUFFER_LIST newNbl = NULL;
656
657 /*
658 * Setup the source port to be the internal port to as to facilitate the
659 * second OvsLookupFlow.
660 */
022c2040
EE
661 if (ovsFwdCtx->switchContext->internalVport == NULL ||
662 ovsFwdCtx->switchContext->virtualExternalVport == NULL) {
ad0d70d2
EE
663 OvsClearTunTxCtx(ovsFwdCtx);
664 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
022c2040 665 L"OVS-Dropped since either internal or external port is absent");
ad0d70d2
EE
666 return NDIS_STATUS_FAILURE;
667 }
c803536e
SS
668 ovsFwdCtx->srcVportNo =
669 ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->portNo;
670
671 ovsFwdCtx->fwdDetail->SourcePortId = ovsFwdCtx->switchContext->internalPortId;
672 ovsFwdCtx->fwdDetail->SourceNicIndex =
673 ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->nicIndex;
674
675 /* Do the encap. Encap function does not consume the NBL. */
676 switch(ovsFwdCtx->tunnelTxNic->ovsType) {
85571a3d
AS
677 case OVS_VPORT_TYPE_GRE:
678 status = OvsEncapGre(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
679 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
680 &ovsFwdCtx->layers, &newNbl);
681 break;
e00afcf6 682 case OVS_VPORT_TYPE_VXLAN:
0b623ad5
NR
683 status = OvsEncapVxlan(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
684 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
c803536e
SS
685 &ovsFwdCtx->layers, &newNbl);
686 break;
022c2040
EE
687 case OVS_VPORT_TYPE_STT:
688 status = OvsEncapStt(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
0b623ad5 689 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
022c2040
EE
690 &ovsFwdCtx->layers, &newNbl);
691 break;
c803536e
SS
692 default:
693 ASSERT(! "Tx: Unhandled tunnel type");
694 }
695
696 /* Reset the tunnel context so that it doesn't get used after this point. */
697 OvsClearTunTxCtx(ovsFwdCtx);
698
699 if (status == NDIS_STATUS_SUCCESS) {
700 ASSERT(newNbl);
701 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
702 L"Complete after cloning NBL for encapsulation");
703 ovsFwdCtx->curNbl = newNbl;
704 status = OvsDoFlowLookupOutput(ovsFwdCtx);
705 ASSERT(ovsFwdCtx->curNbl == NULL);
706 } else {
707 /*
708 * XXX: Temporary freeing of the packet until we register a
709 * callback to IP helper.
710 */
711 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
712 L"OVS-Dropped due to encap failure");
713 ovsActionStats.failedEncap++;
714 status = NDIS_STATUS_SUCCESS;
715 }
716
717 return status;
718}
719
720/*
721 * --------------------------------------------------------------------------
722 * OvsTunnelPortRx --
723 * Decapsulate the incoming NBL based on the tunnel type and goes through
724 * the flow lookup for the inner packet.
725 *
726 * Note: IP checksum is validate here, but L4 checksum validation needs
727 * to be done by the corresponding tunnel types.
728 *
729 * Side effects:
730 * The NBL in 'ovsFwdCtx' is consumed.
731 * --------------------------------------------------------------------------
732 */
733static __inline NDIS_STATUS
734OvsTunnelPortRx(OvsForwardingContext *ovsFwdCtx)
735{
736 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
737 PNET_BUFFER_LIST newNbl = NULL;
738 POVS_VPORT_ENTRY tunnelRxVport = ovsFwdCtx->tunnelRxNic;
a422ea1d 739 PCWSTR dropReason = L"OVS-dropped due to new decap packet";
c803536e
SS
740
741 if (OvsValidateIPChecksum(ovsFwdCtx->curNbl, &ovsFwdCtx->layers)
742 != NDIS_STATUS_SUCCESS) {
743 ovsActionStats.failedChecksum++;
744 OVS_LOG_INFO("Packet dropped due to IP checksum failure.");
745 goto dropNbl;
746 }
747
022c2040
EE
748 /*
749 * Decap port functions should return a new NBL if it was copied, and
750 * this new NBL should be setup as the ovsFwdCtx->curNbl.
751 */
752
c803536e 753 switch(tunnelRxVport->ovsType) {
85571a3d
AS
754 case OVS_VPORT_TYPE_GRE:
755 status = OvsDecapGre(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
756 &ovsFwdCtx->tunKey, &newNbl);
757 break;
e00afcf6 758 case OVS_VPORT_TYPE_VXLAN:
022c2040
EE
759 status = OvsDecapVxlan(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
760 &ovsFwdCtx->tunKey, &newNbl);
761 break;
762 case OVS_VPORT_TYPE_STT:
763 status = OvsDecapStt(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
764 &ovsFwdCtx->tunKey, &newNbl);
a422ea1d
SV
765 if (status == NDIS_STATUS_SUCCESS && newNbl == NULL) {
766 /* This was an STT-LSO Fragment */
767 dropReason = L"OVS-STT segment is cached";
768 }
c803536e
SS
769 break;
770 default:
771 OVS_LOG_ERROR("Rx: Unhandled tunnel type: %d\n",
772 tunnelRxVport->ovsType);
773 ASSERT(! "Rx: Unhandled tunnel type");
774 status = NDIS_STATUS_NOT_SUPPORTED;
775 }
776
777 if (status != NDIS_STATUS_SUCCESS) {
778 ovsActionStats.failedDecap++;
779 goto dropNbl;
780 }
781
782 /*
783 * tunnelRxNic and other fields will be cleared, re-init the context
784 * before usage.
785 */
a422ea1d 786 OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason);
c803536e 787
a422ea1d
SV
788 if (newNbl) {
789 /* Decapsulated packet is in a new NBL */
790 ovsFwdCtx->tunnelRxNic = tunnelRxVport;
791 OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
792 newNbl, tunnelRxVport->portNo, 0,
793 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
794 ovsFwdCtx->completionList,
795 &ovsFwdCtx->layers, FALSE);
c803536e 796
a422ea1d
SV
797 /*
798 * Set the NBL's SourcePortId and SourceNicIndex to default values to
799 * keep NDIS happy when we forward the packet.
800 */
801 ovsFwdCtx->fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
802 ovsFwdCtx->fwdDetail->SourceNicIndex = 0;
c803536e 803
a422ea1d
SV
804 status = OvsDoFlowLookupOutput(ovsFwdCtx);
805 }
c803536e
SS
806 ASSERT(ovsFwdCtx->curNbl == NULL);
807 OvsClearTunRxCtx(ovsFwdCtx);
808
809 return status;
810
811dropNbl:
812 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
813 L"OVS-dropped due to decap failure");
814 OvsClearTunRxCtx(ovsFwdCtx);
815 return status;
816}
817
818
819/*
820 * --------------------------------------------------------------------------
821 * OvsOutputForwardingCtx --
822 * This function outputs an NBL to NDIS or to a tunneling pipeline based on
823 * the ports added so far into 'ovsFwdCtx'.
824 *
825 * Side effects:
826 * This function consumes the NBL - either by forwarding it successfully to
827 * NDIS, or adding it to the completion list in 'ovsFwdCtx', or freeing it.
828 *
829 * Also makes sure that the list of destination ports - tunnel or otherwise is
830 * drained.
831 * --------------------------------------------------------------------------
832 */
833static __inline NDIS_STATUS
834OvsOutputForwardingCtx(OvsForwardingContext *ovsFwdCtx)
835{
836 NDIS_STATUS status = STATUS_SUCCESS;
837 POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext;
eda457f1 838 PCWSTR dropReason;
c803536e
SS
839
840 /*
841 * Handle the case where the some of the destination ports are tunneled
842 * ports - the non-tunneled ports get a unmodified copy of the NBL, and the
843 * tunneling pipeline starts when we output the packet to tunneled port.
844 */
845 if (ovsFwdCtx->destPortsSizeOut > 0) {
846 PNET_BUFFER_LIST newNbl = NULL;
847 PNET_BUFFER nb;
848 UINT32 portsToUpdate =
849 ovsFwdCtx->fwdDetail->NumAvailableDestinations -
850 (ovsFwdCtx->destPortsSizeIn - ovsFwdCtx->destPortsSizeOut);
851
852 ASSERT(ovsFwdCtx->destinationPorts != NULL);
853
854 /*
855 * Create a copy of the packet in order to do encap on it later. Also,
856 * don't copy the offload context since the encap'd packet has a
857 * different set of headers. This will change when we implement offloads
858 * before doing encapsulation.
859 */
860 if (ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL) {
861 nb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
862 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
863 0, 0, TRUE /*copy NBL info*/);
864 if (newNbl == NULL) {
865 status = NDIS_STATUS_RESOURCES;
866 ovsActionStats.noCopiedNbl++;
eda457f1 867 dropReason = L"Dropped due to failure to create NBL copy.";
c803536e
SS
868 goto dropit;
869 }
870 }
871
872 /* It does not seem like we'll get here unless 'portsToUpdate' > 0. */
873 ASSERT(portsToUpdate > 0);
874 status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations(
875 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
876 portsToUpdate, ovsFwdCtx->destinationPorts);
877 if (status != NDIS_STATUS_SUCCESS) {
878 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
879 ovsActionStats.cannotGrowDest++;
eda457f1 880 dropReason = L"Dropped due to failure to update destinations.";
c803536e
SS
881 goto dropit;
882 }
883
884 OvsSendNBLIngress(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
885 ovsFwdCtx->sendFlags);
886 /* End this pipeline by resetting the corresponding context. */
887 ovsFwdCtx->destPortsSizeOut = 0;
888 ovsFwdCtx->curNbl = NULL;
889 if (newNbl) {
890 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
891 newNbl, ovsFwdCtx->srcVportNo, 0,
892 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
893 ovsFwdCtx->completionList,
894 &ovsFwdCtx->layers, FALSE);
895 if (status != NDIS_STATUS_SUCCESS) {
eda457f1 896 dropReason = L"Dropped due to resouces.";
c803536e
SS
897 goto dropit;
898 }
899 }
900 }
901
902 if (ovsFwdCtx->tunnelTxNic != NULL) {
903 status = OvsTunnelPortTx(ovsFwdCtx);
904 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
905 ASSERT(ovsFwdCtx->tunKey.dst == 0);
906 } else if (ovsFwdCtx->tunnelRxNic != NULL) {
907 status = OvsTunnelPortRx(ovsFwdCtx);
908 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
909 ASSERT(ovsFwdCtx->tunKey.dst == 0);
910 }
911 ASSERT(ovsFwdCtx->curNbl == NULL);
912
913 return status;
914
915dropit:
916 if (status != NDIS_STATUS_SUCCESS) {
eda457f1 917 OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason);
c803536e
SS
918 }
919
920 return status;
921}
922
923
924/*
925 * --------------------------------------------------------------------------
926 * OvsLookupFlowOutput --
927 * Utility function for external callers to do flow extract, lookup,
928 * actions execute on a given NBL.
929 *
930 * Note: If this is being used from a callback function, make sure that the
931 * arguments specified are still valid in the asynchronous context.
932 *
933 * Side effects:
934 * This function consumes the NBL.
935 * --------------------------------------------------------------------------
936 */
937VOID
938OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext,
939 VOID *compList,
940 PNET_BUFFER_LIST curNbl)
941{
942 NDIS_STATUS status;
943 OvsForwardingContext ovsFwdCtx;
944 POVS_VPORT_ENTRY internalVport =
945 (POVS_VPORT_ENTRY)switchContext->internalVport;
946
947 /* XXX: make sure comp list was not a stack variable previously. */
948 OvsCompletionList *completionList = (OvsCompletionList *)compList;
949
950 /*
951 * XXX: can internal port disappear while we are busy doing ARP resolution?
952 * It could, but will we get this callback from IP helper in that case. Need
953 * to check.
954 */
955 ASSERT(switchContext->internalVport);
956 status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl,
957 internalVport->portNo, 0,
958 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl),
959 completionList, NULL, TRUE);
960 if (status != NDIS_STATUS_SUCCESS) {
961 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
962 L"OVS-Dropped due to resources");
963 return;
964 }
965
966 ASSERT(FALSE);
967 /*
968 * XXX: We need to acquire the dispatch lock and the datapath lock.
969 */
970
971 OvsDoFlowLookupOutput(&ovsFwdCtx);
972}
973
974
975/*
976 * --------------------------------------------------------------------------
977 * OvsOutputBeforeSetAction --
978 * Function to be called to complete one set of actions on an NBL, before
979 * we start the next one.
980 * --------------------------------------------------------------------------
981 */
982static __inline NDIS_STATUS
983OvsOutputBeforeSetAction(OvsForwardingContext *ovsFwdCtx)
984{
985 PNET_BUFFER_LIST newNbl;
986 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
c803536e
SS
987
988 /*
989 * Create a copy and work on the copy after this point. The original NBL is
990 * forwarded. One reason to not use the copy for forwarding is that
991 * ports have already been added to the original NBL, and it might be
992 * inefficient/impossible to remove/re-add them to the copy. There's no
993 * notion of removing the ports, the ports need to be marked as
994 * "isExcluded". There's seems no real advantage to retaining the original
995 * and sending out the copy instead.
996 *
997 * XXX: We are copying the offload context here. This is to handle actions
998 * such as:
999 * outport, pop_vlan(), outport, push_vlan(), outport
1000 *
1001 * copy size needs to include inner ether + IP + TCP, need to revisit
1002 * if we support IP options.
1003 * XXX Head room needs to include the additional encap.
1004 * XXX copySize check is not considering multiple NBs.
1005 */
c803536e
SS
1006 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1007 0, 0, TRUE /*copy NBL info*/);
1008
1009 ASSERT(ovsFwdCtx->destPortsSizeOut > 0 ||
1010 ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL);
1011
ba472491
AS
1012 /* Send the original packet out and save the original source port number */
1013 UINT32 tempVportNo = ovsFwdCtx->srcVportNo;
c803536e
SS
1014 status = OvsOutputForwardingCtx(ovsFwdCtx);
1015 ASSERT(ovsFwdCtx->curNbl == NULL);
1016 ASSERT(ovsFwdCtx->destPortsSizeOut == 0);
1017 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
1018 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
1019
1020 /* If we didn't make a copy, can't continue. */
1021 if (newNbl == NULL) {
1022 ovsActionStats.noCopiedNbl++;
1023 return NDIS_STATUS_RESOURCES;
1024 }
1025
1026 /* Finish the remaining actions with the new NBL */
1027 if (status != NDIS_STATUS_SUCCESS) {
1028 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
1029 } else {
1030 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
ba472491 1031 newNbl, tempVportNo, 0,
c803536e
SS
1032 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1033 ovsFwdCtx->completionList,
1034 &ovsFwdCtx->layers, FALSE);
1035 }
1036
1037 return status;
1038}
1039
1040
1041/*
1042 * --------------------------------------------------------------------------
5874d571
SV
1043 * OvsPopFieldInPacketBuf --
1044 * Function to pop a specified field of length 'shiftLength' located at
1045 * 'shiftOffset' from the ethernet header. The data on the left of the
1046 * 'shiftOffset' is right shifted.
1047 *
1048 * Returns a pointer to the new start in 'bufferData'.
c803536e
SS
1049 * --------------------------------------------------------------------------
1050 */
1051static __inline NDIS_STATUS
5874d571
SV
1052OvsPopFieldInPacketBuf(OvsForwardingContext *ovsFwdCtx,
1053 UINT32 shiftOffset,
1054 UINT32 shiftLength,
1055 PUINT8 *bufferData)
c803536e
SS
1056{
1057 PNET_BUFFER curNb;
1058 PMDL curMdl;
1059 PUINT8 bufferStart;
c803536e
SS
1060 UINT32 packetLen, mdlLen;
1061 PNET_BUFFER_LIST newNbl;
1062 NDIS_STATUS status;
5874d571 1063 PUINT8 tempBuffer[ETH_HEADER_LENGTH];
c803536e 1064
5874d571 1065 ASSERT(shiftOffset > ETH_ADDR_LENGTH);
c803536e
SS
1066
1067 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1068 0, 0, TRUE /* copy NBL info */);
1069 if (!newNbl) {
1070 ovsActionStats.noCopiedNbl++;
1071 return NDIS_STATUS_RESOURCES;
1072 }
1073
1074 /* Complete the original NBL and create a copy to modify. */
1075 OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to copy");
1076
5874d571
SV
1077 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext, newNbl,
1078 ovsFwdCtx->srcVportNo, 0,
c803536e
SS
1079 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1080 NULL, &ovsFwdCtx->layers, FALSE);
1081 if (status != NDIS_STATUS_SUCCESS) {
1082 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1083 L"Dropped due to resouces");
1084 return NDIS_STATUS_RESOURCES;
1085 }
1086
1087 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1088 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1089 ASSERT(curNb->Next == NULL);
1090 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1091 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1092 if (!bufferStart) {
1093 return NDIS_STATUS_RESOURCES;
1094 }
1095 mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
5874d571
SV
1096 /* Bail out if L2 + shiftLength is not contiguous in the first buffer. */
1097 if (MIN(packetLen, mdlLen) < sizeof(EthHdr) + shiftLength) {
c803536e
SS
1098 ASSERT(FALSE);
1099 return NDIS_STATUS_FAILURE;
1100 }
1101 bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
5874d571
SV
1102 RtlCopyMemory(tempBuffer, bufferStart, shiftOffset);
1103 RtlCopyMemory(bufferStart + shiftLength, tempBuffer, shiftOffset);
c803536e
SS
1104 NdisAdvanceNetBufferDataStart(curNb, shiftLength, FALSE, NULL);
1105
5874d571
SV
1106 if (bufferData) {
1107 *bufferData = bufferStart + shiftLength;
1108 }
1109
1110 return NDIS_STATUS_SUCCESS;
1111}
1112
1113
1114/*
1115 * --------------------------------------------------------------------------
1116 * OvsPopVlanInPktBuf --
1117 * Function to pop a VLAN tag when the tag is in the packet buffer.
1118 * --------------------------------------------------------------------------
1119 */
1120static __inline NDIS_STATUS
1121OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
1122{
1123 /*
1124 * Declare a dummy vlanTag structure since we need to compute the size
1125 * of shiftLength. The NDIS one is a unionized structure.
1126 */
1127 NDIS_PACKET_8021Q_INFO vlanTag = {0};
1128 UINT32 shiftLength = sizeof(vlanTag.TagHeader);
1129 UINT32 shiftOffset = sizeof(DL_EUI48) + sizeof(DL_EUI48);
1130
1131 return OvsPopFieldInPacketBuf(ovsFwdCtx, shiftOffset, shiftLength, NULL);
1132}
1133
1134
1135/*
1136 * --------------------------------------------------------------------------
1137 * OvsActionMplsPop --
1138 * Function to pop the first MPLS label from the current packet.
1139 * --------------------------------------------------------------------------
1140 */
1141static __inline NDIS_STATUS
1142OvsActionMplsPop(OvsForwardingContext *ovsFwdCtx,
1143 ovs_be16 ethertype)
1144{
1145 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1146 OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
1147 EthHdr *ethHdr = NULL;
1148
1149 status = OvsPopFieldInPacketBuf(ovsFwdCtx, sizeof(*ethHdr),
1150 MPLS_HLEN, (PUINT8*)&ethHdr);
1151 if (status == NDIS_STATUS_SUCCESS) {
1152 if (ethHdr && OvsEthertypeIsMpls(ethHdr->Type)) {
1153 ethHdr->Type = ethertype;
1154 }
1155
1156 layers->l3Offset -= MPLS_HLEN;
1157 layers->l4Offset -= MPLS_HLEN;
1158 }
1159
1160 return status;
1161}
1162
1163
1164/*
1165 * --------------------------------------------------------------------------
1166 * OvsActionMplsPush --
1167 * Function to push the MPLS label into the current packet.
1168 * --------------------------------------------------------------------------
1169 */
1170static __inline NDIS_STATUS
1171OvsActionMplsPush(OvsForwardingContext *ovsFwdCtx,
1172 const struct ovs_action_push_mpls *mpls)
1173{
1174 NDIS_STATUS status;
1175 PNET_BUFFER curNb = NULL;
1176 PMDL curMdl = NULL;
1177 PUINT8 bufferStart = NULL;
1178 OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
1179 EthHdr *ethHdr = NULL;
1180 MPLSHdr *mplsHdr = NULL;
1181 UINT32 mdlLen = 0, curMdlOffset = 0;
1182 PNET_BUFFER_LIST newNbl;
1183
1184 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1185 layers->l3Offset, MPLS_HLEN, TRUE);
1186 if (!newNbl) {
1187 ovsActionStats.noCopiedNbl++;
1188 return NDIS_STATUS_RESOURCES;
1189 }
1190 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1191 L"Complete after partial copy.");
1192
1193 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
1194 newNbl, ovsFwdCtx->srcVportNo, 0,
1195 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1196 NULL, &ovsFwdCtx->layers, FALSE);
1197 if (status != NDIS_STATUS_SUCCESS) {
1198 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1199 L"OVS-Dropped due to resources");
1200 return NDIS_STATUS_RESOURCES;
1201 }
1202
1203 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1204 ASSERT(curNb->Next == NULL);
1205
1206 status = NdisRetreatNetBufferDataStart(curNb, MPLS_HLEN, 0, NULL);
1207 if (status != NDIS_STATUS_SUCCESS) {
1208 return status;
1209 }
1210
1211 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1212 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1213 if (!curMdl) {
1214 ovsActionStats.noResource++;
1215 return NDIS_STATUS_RESOURCES;
1216 }
1217
1218 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1219 mdlLen -= curMdlOffset;
1220 ASSERT(mdlLen >= MPLS_HLEN);
1221
1222 ethHdr = (EthHdr *)(bufferStart + curMdlOffset);
1223 RtlMoveMemory(ethHdr, (UINT8*)ethHdr + MPLS_HLEN, sizeof(*ethHdr));
1224 ethHdr->Type = mpls->mpls_ethertype;
1225
1226 mplsHdr = (MPLSHdr *)(ethHdr + 1);
1227 mplsHdr->lse = mpls->mpls_lse;
1228
1229 layers->l3Offset += MPLS_HLEN;
1230 layers->l4Offset += MPLS_HLEN;
1231
c803536e
SS
1232 return NDIS_STATUS_SUCCESS;
1233}
1234
1235/*
1236 * --------------------------------------------------------------------------
1237 * OvsTunnelAttrToIPv4TunnelKey --
1238 * Convert tunnel attribute to OvsIPv4TunnelKey.
1239 * --------------------------------------------------------------------------
1240 */
1241static __inline NDIS_STATUS
d838e577 1242OvsTunnelAttrToIPv4TunnelKey(PNL_ATTR attr,
c803536e
SS
1243 OvsIPv4TunnelKey *tunKey)
1244{
d838e577 1245 PNL_ATTR a;
c803536e
SS
1246 INT rem;
1247
1248 tunKey->attr[0] = 0;
1249 tunKey->attr[1] = 0;
1250 tunKey->attr[2] = 0;
d838e577 1251 ASSERT(NlAttrType(attr) == OVS_KEY_ATTR_TUNNEL);
c803536e 1252
d838e577
AS
1253 NL_ATTR_FOR_EACH_UNSAFE (a, rem, NlAttrData(attr),
1254 NlAttrGetSize(attr)) {
1255 switch (NlAttrType(a)) {
c803536e 1256 case OVS_TUNNEL_KEY_ATTR_ID:
d838e577 1257 tunKey->tunnelId = NlAttrGetBe64(a);
c803536e
SS
1258 tunKey->flags |= OVS_TNL_F_KEY;
1259 break;
1260 case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
d838e577 1261 tunKey->src = NlAttrGetBe32(a);
c803536e
SS
1262 break;
1263 case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
d838e577 1264 tunKey->dst = NlAttrGetBe32(a);
c803536e
SS
1265 break;
1266 case OVS_TUNNEL_KEY_ATTR_TOS:
d838e577 1267 tunKey->tos = NlAttrGetU8(a);
c803536e
SS
1268 break;
1269 case OVS_TUNNEL_KEY_ATTR_TTL:
d838e577 1270 tunKey->ttl = NlAttrGetU8(a);
c803536e
SS
1271 break;
1272 case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
1273 tunKey->flags |= OVS_TNL_F_DONT_FRAGMENT;
1274 break;
1275 case OVS_TUNNEL_KEY_ATTR_CSUM:
1276 tunKey->flags |= OVS_TNL_F_CSUM;
1277 break;
1278 default:
1279 ASSERT(0);
1280 }
1281 }
1282
1283 return NDIS_STATUS_SUCCESS;
1284}
1285
1286/*
1287 *----------------------------------------------------------------------------
1288 * OvsUpdateEthHeader --
1289 * Updates the ethernet header in ovsFwdCtx.curNbl inline based on the
1290 * specified key.
1291 *----------------------------------------------------------------------------
1292 */
1293static __inline NDIS_STATUS
1294OvsUpdateEthHeader(OvsForwardingContext *ovsFwdCtx,
1295 const struct ovs_key_ethernet *ethAttr)
1296{
1297 PNET_BUFFER curNb;
1298 PMDL curMdl;
1299 PUINT8 bufferStart;
1300 EthHdr *ethHdr;
1301 UINT32 packetLen, mdlLen;
1302
1303 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1304 ASSERT(curNb->Next == NULL);
1305 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1306 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1307 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1308 if (!bufferStart) {
1309 ovsActionStats.noResource++;
1310 return NDIS_STATUS_RESOURCES;
1311 }
1312 mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1313 ASSERT(mdlLen > 0);
1314 /* Bail out if the L2 header is not in a contiguous buffer. */
1315 if (MIN(packetLen, mdlLen) < sizeof *ethHdr) {
1316 ASSERT(FALSE);
1317 return NDIS_STATUS_FAILURE;
1318 }
1319 ethHdr = (EthHdr *)(bufferStart + NET_BUFFER_CURRENT_MDL_OFFSET(curNb));
1320
1321 RtlCopyMemory(ethHdr->Destination, ethAttr->eth_dst,
1322 sizeof ethHdr->Destination);
1323 RtlCopyMemory(ethHdr->Source, ethAttr->eth_src, sizeof ethHdr->Source);
1324
1325 return NDIS_STATUS_SUCCESS;
1326}
1327
1328/*
1329 *----------------------------------------------------------------------------
1330 * OvsUpdateIPv4Header --
1331 * Updates the IPv4 header in ovsFwdCtx.curNbl inline based on the
1332 * specified key.
1333 *----------------------------------------------------------------------------
1334 */
1335static __inline NDIS_STATUS
1336OvsUpdateIPv4Header(OvsForwardingContext *ovsFwdCtx,
1337 const struct ovs_key_ipv4 *ipAttr)
1338{
1339 PNET_BUFFER curNb;
1340 PMDL curMdl;
1341 ULONG curMdlOffset;
1342 PUINT8 bufferStart;
1343 UINT32 mdlLen, hdrSize, packetLen;
1344 OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
1345 NDIS_STATUS status;
1346 IPHdr *ipHdr;
1347 TCPHdr *tcpHdr = NULL;
1348 UDPHdr *udpHdr = NULL;
1349
1350 ASSERT(layers->value != 0);
1351
1352 /*
1353 * Peek into the MDL to get a handle to the IP header and if required
1354 * the TCP/UDP header as well. We check if the required headers are in one
1355 * contiguous MDL, and if not, we copy them over to one MDL.
1356 */
1357 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1358 ASSERT(curNb->Next == NULL);
1359 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1360 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1361 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1362 if (!bufferStart) {
1363 ovsActionStats.noResource++;
1364 return NDIS_STATUS_RESOURCES;
1365 }
1366 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1367 mdlLen -= curMdlOffset;
1368 ASSERT((INT)mdlLen >= 0);
1369
1370 if (layers->isTcp || layers->isUdp) {
1371 hdrSize = layers->l4Offset +
1372 layers->isTcp ? sizeof (*tcpHdr) : sizeof (*udpHdr);
1373 } else {
1374 hdrSize = layers->l3Offset + sizeof (*ipHdr);
1375 }
1376
1377 /* Count of number of bytes of valid data there are in the first MDL. */
1378 mdlLen = MIN(packetLen, mdlLen);
1379 if (mdlLen < hdrSize) {
1380 PNET_BUFFER_LIST newNbl;
1381 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1382 hdrSize, 0, TRUE /*copy NBL info*/);
1383 if (!newNbl) {
1384 ovsActionStats.noCopiedNbl++;
1385 return NDIS_STATUS_RESOURCES;
1386 }
1387 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1388 L"Complete after partial copy.");
1389
1390 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
1391 newNbl, ovsFwdCtx->srcVportNo, 0,
1392 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1393 NULL, &ovsFwdCtx->layers, FALSE);
1394 if (status != NDIS_STATUS_SUCCESS) {
1395 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1396 L"OVS-Dropped due to resources");
1397 return NDIS_STATUS_RESOURCES;
1398 }
1399
1400 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1401 ASSERT(curNb->Next == NULL);
1402 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1403 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1404 if (!curMdl) {
1405 ovsActionStats.noResource++;
1406 return NDIS_STATUS_RESOURCES;
1407 }
1408 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1409 mdlLen -= curMdlOffset;
1410 ASSERT(mdlLen >= hdrSize);
1411 }
1412
1413 ipHdr = (IPHdr *)(bufferStart + curMdlOffset + layers->l3Offset);
1414
1415 if (layers->isTcp) {
1416 tcpHdr = (TCPHdr *)(bufferStart + curMdlOffset + layers->l4Offset);
1417 } else if (layers->isUdp) {
1418 udpHdr = (UDPHdr *)(bufferStart + curMdlOffset + layers->l4Offset);
1419 }
1420
1421 /*
1422 * Adjust the IP header inline as dictated by the action, nad also update
1423 * the IP and the TCP checksum for the data modified.
1424 *
1425 * In the future, this could be optimized to make one call to
1426 * ChecksumUpdate32(). Ignoring this for now, since for the most common
1427 * case, we only update the TTL.
1428 */
1429 if (ipHdr->saddr != ipAttr->ipv4_src) {
1430 if (tcpHdr) {
1431 tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->saddr,
1432 ipAttr->ipv4_src);
1433 } else if (udpHdr && udpHdr->check) {
1434 udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->saddr,
1435 ipAttr->ipv4_src);
1436 }
1437
1438 if (ipHdr->check != 0) {
1439 ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->saddr,
1440 ipAttr->ipv4_src);
1441 }
1442 ipHdr->saddr = ipAttr->ipv4_src;
1443 }
1444 if (ipHdr->daddr != ipAttr->ipv4_dst) {
1445 if (tcpHdr) {
1446 tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->daddr,
1447 ipAttr->ipv4_dst);
1448 } else if (udpHdr && udpHdr->check) {
1449 udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->daddr,
1450 ipAttr->ipv4_dst);
1451 }
1452
1453 if (ipHdr->check != 0) {
1454 ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->daddr,
1455 ipAttr->ipv4_dst);
1456 }
1457 ipHdr->daddr = ipAttr->ipv4_dst;
1458 }
1459 if (ipHdr->protocol != ipAttr->ipv4_proto) {
1460 UINT16 oldProto = (ipHdr->protocol << 16) & 0xff00;
1461 UINT16 newProto = (ipAttr->ipv4_proto << 16) & 0xff00;
1462 if (tcpHdr) {
1463 tcpHdr->check = ChecksumUpdate16(tcpHdr->check, oldProto, newProto);
1464 } else if (udpHdr && udpHdr->check) {
1465 udpHdr->check = ChecksumUpdate16(udpHdr->check, oldProto, newProto);
1466 }
1467
1468 if (ipHdr->check != 0) {
1469 ipHdr->check = ChecksumUpdate16(ipHdr->check, oldProto, newProto);
1470 }
1471 ipHdr->protocol = ipAttr->ipv4_proto;
1472 }
1473 if (ipHdr->ttl != ipAttr->ipv4_ttl) {
1474 UINT16 oldTtl = (ipHdr->ttl) & 0xff;
1475 UINT16 newTtl = (ipAttr->ipv4_ttl) & 0xff;
1476 if (ipHdr->check != 0) {
1477 ipHdr->check = ChecksumUpdate16(ipHdr->check, oldTtl, newTtl);
1478 }
1479 ipHdr->ttl = ipAttr->ipv4_ttl;
1480 }
1481
1482 return NDIS_STATUS_SUCCESS;
1483}
1484
1485/*
1486 * --------------------------------------------------------------------------
1487 * OvsExecuteSetAction --
1488 * Executes a set() action, but storing the actions into 'ovsFwdCtx'
1489 * --------------------------------------------------------------------------
1490 */
1491static __inline NDIS_STATUS
1492OvsExecuteSetAction(OvsForwardingContext *ovsFwdCtx,
1493 OvsFlowKey *key,
1494 UINT64 *hash,
d838e577 1495 const PNL_ATTR a)
c803536e 1496{
d838e577 1497 enum ovs_key_attr type = NlAttrType(a);
c803536e
SS
1498 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1499
1500 switch (type) {
1501 case OVS_KEY_ATTR_ETHERNET:
1502 status = OvsUpdateEthHeader(ovsFwdCtx,
d838e577 1503 NlAttrGetUnspec(a, sizeof(struct ovs_key_ethernet)));
c803536e
SS
1504 break;
1505
1506 case OVS_KEY_ATTR_IPV4:
1507 status = OvsUpdateIPv4Header(ovsFwdCtx,
d838e577 1508 NlAttrGetUnspec(a, sizeof(struct ovs_key_ipv4)));
c803536e
SS
1509 break;
1510
1511 case OVS_KEY_ATTR_TUNNEL:
1512 {
1513 OvsIPv4TunnelKey tunKey;
022c2040 1514 status = OvsTunnelAttrToIPv4TunnelKey((PNL_ATTR)a, &tunKey);
c803536e
SS
1515 ASSERT(status == NDIS_STATUS_SUCCESS);
1516 tunKey.flow_hash = (uint16)(hash ? *hash : OvsHashFlow(key));
ffde5f8f 1517 tunKey.dst_port = key->ipKey.l4.tpDst;
c803536e 1518 RtlCopyMemory(&ovsFwdCtx->tunKey, &tunKey, sizeof ovsFwdCtx->tunKey);
c803536e
SS
1519 break;
1520 }
4ac06450 1521
c803536e 1522 default:
4ac06450 1523 OVS_LOG_INFO("Unhandled attribute %#x", type);
3819692e 1524 break;
c803536e
SS
1525 }
1526 return status;
1527}
1528
1529/*
1530 * --------------------------------------------------------------------------
ee25964a
SV
1531 * OvsExecuteRecirc --
1532 * The function adds a deferred action to allow the current packet, nbl,
1533 * to re-enter datapath packet processing.
1534 * --------------------------------------------------------------------------
1535 */
1536NDIS_STATUS
1537OvsExecuteRecirc(OvsForwardingContext *ovsFwdCtx,
1538 OvsFlowKey *key,
1539 const PNL_ATTR actions,
1540 int rem)
1541{
1542 POVS_DEFERRED_ACTION deferredAction = NULL;
1543 PNET_BUFFER_LIST newNbl = NULL;
1544
1545 if (!NlAttrIsLast(actions, rem)) {
1546 /*
1547 * Recirc action is the not the last action of the action list, so we
1548 * need to clone the packet.
1549 */
1550 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1551 0, 0, TRUE /*copy NBL info*/);
1552 /*
1553 * Skip the recirc action when out of memory, but continue on with the
1554 * rest of the action list.
1555 */
1556 if (newNbl == NULL) {
1557 ovsActionStats.noCopiedNbl++;
1558 return NDIS_STATUS_SUCCESS;
1559 }
1560 ovsFwdCtx->curNbl = newNbl;
1561 }
1562
1563 deferredAction = OvsAddDeferredActions(ovsFwdCtx->curNbl, key, NULL);
1564 if (deferredAction) {
1565 deferredAction->key.recircId = NlAttrGetU32(actions);
1566 } else {
1567 if (newNbl) {
1568 ovsActionStats.deferredActionsQueueFull++;
1569 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
1570 }
1571 }
1572
1573 return NDIS_STATUS_SUCCESS;
1574}
1575
245eedef
SV
1576/*
1577 * --------------------------------------------------------------------------
1578 * OvsExecuteHash --
1579 * The function updates datapath hash read from userspace.
1580 * --------------------------------------------------------------------------
1581 */
1582VOID
1583OvsExecuteHash(OvsFlowKey *key,
1584 const PNL_ATTR attr)
1585{
1586 struct ovs_action_hash *hash_act = NlAttrData(attr);
1587 UINT32 hash = 0;
1588
1589 hash = (UINT32)OvsHashFlow(key);
1590 hash = OvsJhashWords(&hash, 1, hash_act->hash_basis);
1591 if (!hash)
1592 hash = 1;
1593
1594 key->dpHash = hash;
1595}
1596
ee25964a
SV
1597/*
1598 * --------------------------------------------------------------------------
1599 * OvsDoExecuteActions --
1600 * Interpret and execute the specified 'actions' on the specified packet
c803536e
SS
1601 * 'curNbl'. The expectation is that if the packet needs to be dropped
1602 * (completed) for some reason, it is added to 'completionList' so that the
1603 * caller can complete the packet. If 'completionList' is NULL, the NBL is
1604 * assumed to be generated by OVS and freed up. Otherwise, the function
1605 * consumes the NBL by generating a NDIS send indication for the packet.
1606 *
1607 * There are one or more of "clone" NBLs that may get generated while
1608 * executing the actions. Upon any failures, the "cloned" NBLs are freed up,
1609 * and the caller does not have to worry about them.
1610 *
1611 * Success or failure is returned based on whether the specified actions
1612 * were executed successfully on the packet or not.
1613 * --------------------------------------------------------------------------
1614 */
1615NDIS_STATUS
ee25964a
SV
1616OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext,
1617 OvsCompletionList *completionList,
1618 PNET_BUFFER_LIST curNbl,
1619 UINT32 portNo,
1620 ULONG sendFlags,
1621 OvsFlowKey *key,
1622 UINT64 *hash,
1623 OVS_PACKET_HDR_INFO *layers,
1624 const PNL_ATTR actions,
1625 INT actionsLen)
c803536e 1626{
d838e577 1627 PNL_ATTR a;
c803536e
SS
1628 INT rem;
1629 UINT32 dstPortID;
1630 OvsForwardingContext ovsFwdCtx;
1631 PCWSTR dropReason = L"";
1632 NDIS_STATUS status;
1633 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail =
1634 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl);
1635
1636 /* XXX: ASSERT that the flow table lock is held. */
1637 status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl, portNo,
1638 sendFlags, fwdDetail, completionList,
1639 layers, TRUE);
1640 if (status != NDIS_STATUS_SUCCESS) {
1641 dropReason = L"OVS-initing destination port list failed";
1642 goto dropit;
1643 }
1644
1645 if (actionsLen == 0) {
1646 dropReason = L"OVS-Dropped due to Flow action";
1647 ovsActionStats.zeroActionLen++;
1648 goto dropit;
1649 }
1650
1651 NL_ATTR_FOR_EACH_UNSAFE (a, rem, actions, actionsLen) {
d838e577 1652 switch(NlAttrType(a)) {
c803536e 1653 case OVS_ACTION_ATTR_OUTPUT:
d838e577 1654 dstPortID = NlAttrGetU32(a);
c803536e
SS
1655 status = OvsAddPorts(&ovsFwdCtx, key, dstPortID,
1656 TRUE, TRUE);
1657 if (status != NDIS_STATUS_SUCCESS) {
1658 dropReason = L"OVS-adding destination port failed";
1659 goto dropit;
1660 }
1661 break;
1662
1663 case OVS_ACTION_ATTR_PUSH_VLAN:
1664 {
1665 struct ovs_action_push_vlan *vlan;
1666 PVOID vlanTagValue;
1667 PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTag;
1668
1669 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1670 || ovsFwdCtx.tunnelRxNic != NULL) {
1671 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1672 if (status != NDIS_STATUS_SUCCESS) {
1673 dropReason = L"OVS-adding destination failed";
1674 goto dropit;
1675 }
1676 }
1677
1678 vlanTagValue = NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1679 Ieee8021QNetBufferListInfo);
1680 if (vlanTagValue != NULL) {
1681 /*
1682 * XXX: We don't support double VLAN tag offload. In such cases,
1683 * we need to insert the existing one into the packet buffer,
1684 * and add the new one as offload. This will take care of
1685 * guest tag-in-tag case as well as OVS rules that specify
1686 * tag-in-tag.
1687 */
1688 } else {
1689 vlanTagValue = 0;
1690 vlanTag = (PNDIS_NET_BUFFER_LIST_8021Q_INFO)(PVOID *)&vlanTagValue;
d838e577 1691 vlan = (struct ovs_action_push_vlan *)NlAttrGet((const PNL_ATTR)a);
c803536e
SS
1692 vlanTag->TagHeader.VlanId = ntohs(vlan->vlan_tci) & 0xfff;
1693 vlanTag->TagHeader.UserPriority = ntohs(vlan->vlan_tci) >> 13;
1694
1695 NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1696 Ieee8021QNetBufferListInfo) = vlanTagValue;
1697 }
1698 break;
1699 }
1700
1701 case OVS_ACTION_ATTR_POP_VLAN:
1702 {
1703 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1704 || ovsFwdCtx.tunnelRxNic != NULL) {
1705 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1706 if (status != NDIS_STATUS_SUCCESS) {
1707 dropReason = L"OVS-adding destination failed";
1708 goto dropit;
1709 }
1710 }
1711
1712 if (NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1713 Ieee8021QNetBufferListInfo) != 0) {
1714 NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1715 Ieee8021QNetBufferListInfo) = 0;
1716 } else {
1717 /*
1718 * The VLAN tag is inserted into the packet buffer. Pop the tag
1719 * by packet buffer modification.
1720 */
1721 status = OvsPopVlanInPktBuf(&ovsFwdCtx);
1722 if (status != NDIS_STATUS_SUCCESS) {
1723 dropReason = L"OVS-pop vlan action failed";
1724 goto dropit;
1725 }
1726 }
1727 break;
1728 }
1729
5874d571
SV
1730 case OVS_ACTION_ATTR_PUSH_MPLS:
1731 {
1732 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1733 || ovsFwdCtx.tunnelRxNic != NULL) {
1734 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1735 if (status != NDIS_STATUS_SUCCESS) {
1736 dropReason = L"OVS-adding destination failed";
1737 goto dropit;
1738 }
1739 }
1740
1741 status = OvsActionMplsPush(&ovsFwdCtx,
1742 (struct ovs_action_push_mpls *)NlAttrGet
1743 ((const PNL_ATTR)a));
1744 if (status != NDIS_STATUS_SUCCESS) {
1745 dropReason = L"OVS-push MPLS action failed";
1746 goto dropit;
1747 }
1748 layers->l3Offset += MPLS_HLEN;
1749 layers->l4Offset += MPLS_HLEN;
1750 break;
1751 }
1752
1753 case OVS_ACTION_ATTR_POP_MPLS:
1754 {
1755 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1756 || ovsFwdCtx.tunnelRxNic != NULL) {
1757 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1758 if (status != NDIS_STATUS_SUCCESS) {
1759 dropReason = L"OVS-adding destination failed";
1760 goto dropit;
1761 }
1762 }
1763
1764 status = OvsActionMplsPop(&ovsFwdCtx, NlAttrGetBe16(a));
1765 if (status != NDIS_STATUS_SUCCESS) {
1766 dropReason = L"OVS-pop MPLS action failed";
1767 goto dropit;
1768 }
1769 layers->l3Offset -= MPLS_HLEN;
1770 layers->l4Offset -= MPLS_HLEN;
1771 break;
1772 }
1773
245eedef
SV
1774 case OVS_ACTION_ATTR_HASH:
1775 {
1776 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1777 || ovsFwdCtx.tunnelRxNic != NULL) {
1778 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1779 if (status != NDIS_STATUS_SUCCESS) {
1780 dropReason = L"OVS-adding destination failed";
1781 goto dropit;
1782 }
1783 }
1784
1785 OvsExecuteHash(key, (const PNL_ATTR)a);
1786
1787 break;
1788 }
1789
792d377d
SV
1790 case OVS_ACTION_ATTR_CT:
1791 {
1792 if (ovsFwdCtx.destPortsSizeOut > 0
1793 || ovsFwdCtx.tunnelTxNic != NULL
1794 || ovsFwdCtx.tunnelRxNic != NULL) {
1795 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1796 if (status != NDIS_STATUS_SUCCESS) {
1797 dropReason = L"OVS-adding destination failed";
1798 goto dropit;
1799 }
1800 }
1801
1802 status = OvsExecuteConntrackAction(ovsFwdCtx.curNbl, layers,
1803 key, (const PNL_ATTR)a);
1804 if (status != NDIS_STATUS_SUCCESS) {
1805 OVS_LOG_ERROR("CT Action failed");
1806 dropReason = L"OVS-conntrack action failed";
1807 goto dropit;
1808 }
1809 break;
1810 }
1811
ee25964a
SV
1812 case OVS_ACTION_ATTR_RECIRC:
1813 {
1814 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1815 || ovsFwdCtx.tunnelRxNic != NULL) {
1816 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1817 if (status != NDIS_STATUS_SUCCESS) {
1818 dropReason = L"OVS-adding destination failed";
1819 goto dropit;
1820 }
1821 }
1822
1823 status = OvsExecuteRecirc(&ovsFwdCtx, key, (const PNL_ATTR)a, rem);
1824 if (status != NDIS_STATUS_SUCCESS) {
1825 dropReason = L"OVS-recirculation action failed";
1826 goto dropit;
1827 }
1828
1829 if (NlAttrIsLast(a, rem)) {
1830 goto exit;
1831 }
1832 break;
1833 }
1834
c803536e
SS
1835 case OVS_ACTION_ATTR_USERSPACE:
1836 {
d838e577
AS
1837 PNL_ATTR userdataAttr;
1838 PNL_ATTR queueAttr;
c803536e 1839 POVS_PACKET_QUEUE_ELEM elem;
429d4556
AS
1840 BOOLEAN isRecv = FALSE;
1841
1842 POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(switchContext,
1843 portNo);
1844
1845 if (vport) {
1846 if (vport->isExternal ||
1847 OvsIsTunnelVportType(vport->ovsType)) {
1848 isRecv = TRUE;
1849 }
1850 }
c803536e 1851
d838e577
AS
1852 queueAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_PID);
1853 userdataAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_USERDATA);
c803536e 1854
640ebde7
EE
1855 elem = OvsCreateQueueNlPacket((PVOID)userdataAttr,
1856 userdataAttr->nlaLen,
1857 OVS_PACKET_CMD_ACTION,
4c470e88 1858 vport, key, ovsFwdCtx.curNbl,
640ebde7
EE
1859 NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx.curNbl),
1860 isRecv,
1861 layers);
c803536e
SS
1862 if (elem) {
1863 LIST_ENTRY missedPackets;
1864 InitializeListHead(&missedPackets);
1865 InsertTailList(&missedPackets, &elem->link);
4a3c9b70 1866 OvsQueuePackets(&missedPackets, 1);
c803536e
SS
1867 dropReason = L"OVS-Completed since packet was copied to "
1868 L"userspace";
1869 } else {
1870 dropReason = L"OVS-Dropped due to failure to queue to "
1871 L"userspace";
1872 goto dropit;
1873 }
1874 break;
1875 }
1876 case OVS_ACTION_ATTR_SET:
1877 {
1878 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1879 || ovsFwdCtx.tunnelRxNic != NULL) {
1880 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1881 if (status != NDIS_STATUS_SUCCESS) {
1882 dropReason = L"OVS-adding destination failed";
1883 goto dropit;
1884 }
1885 }
1886
1887 status = OvsExecuteSetAction(&ovsFwdCtx, key, hash,
d838e577
AS
1888 (const PNL_ATTR)NlAttrGet
1889 ((const PNL_ATTR)a));
c803536e
SS
1890 if (status != NDIS_STATUS_SUCCESS) {
1891 dropReason = L"OVS-set action failed";
1892 goto dropit;
1893 }
1894 break;
1895 }
1896 case OVS_ACTION_ATTR_SAMPLE:
c803536e 1897 default:
7c5d9f17 1898 status = NDIS_STATUS_NOT_SUPPORTED;
c803536e
SS
1899 break;
1900 }
1901 }
1902
1903 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1904 || ovsFwdCtx.tunnelRxNic != NULL) {
1905 status = OvsOutputForwardingCtx(&ovsFwdCtx);
1906 ASSERT(ovsFwdCtx.curNbl == NULL);
1907 }
1908
1909 ASSERT(ovsFwdCtx.destPortsSizeOut == 0);
1910 ASSERT(ovsFwdCtx.tunnelRxNic == NULL);
1911 ASSERT(ovsFwdCtx.tunnelTxNic == NULL);
1912
1913dropit:
1914 /*
1915 * If curNbl != NULL, it implies the NBL has not been not freed up so far.
1916 */
1917 if (ovsFwdCtx.curNbl) {
1918 OvsCompleteNBLForwardingCtx(&ovsFwdCtx, dropReason);
1919 }
1920
ee25964a
SV
1921exit:
1922 return status;
1923}
1924
1925/*
1926 * --------------------------------------------------------------------------
1927 * OvsActionsExecute --
1928 * The function interprets and executes the specified 'actions' on the
1929 * specified packet 'curNbl'. See 'OvsDoExecuteActions' description for
1930 * more details.
1931 *
1932 * Also executes deferred actions added by recirculation or sample
1933 * actions.
1934 * --------------------------------------------------------------------------
1935 */
1936NDIS_STATUS
1937OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
1938 OvsCompletionList *completionList,
1939 PNET_BUFFER_LIST curNbl,
1940 UINT32 portNo,
1941 ULONG sendFlags,
1942 OvsFlowKey *key,
1943 UINT64 *hash,
1944 OVS_PACKET_HDR_INFO *layers,
1945 const PNL_ATTR actions,
1946 INT actionsLen)
1947{
1948 NDIS_STATUS status = STATUS_SUCCESS;
1949
1950 status = OvsDoExecuteActions(switchContext, completionList, curNbl,
1951 portNo, sendFlags, key, hash, layers,
1952 actions, actionsLen);
1953
1954 if (status == STATUS_SUCCESS) {
1955 status = OvsProcessDeferredActions(switchContext, completionList,
1956 portNo, sendFlags, layers);
1957 }
1958
1959 return status;
1960}
1961
1962/*
1963 * --------------------------------------------------------------------------
1964 * OvsDoRecirc --
1965 * The function processes the packet 'curNbl' that re-entered datapath
1966 * packet processing after a recirculation action.
1967 * --------------------------------------------------------------------------
1968 */
1969NDIS_STATUS
1970OvsDoRecirc(POVS_SWITCH_CONTEXT switchContext,
1971 OvsCompletionList *completionList,
1972 PNET_BUFFER_LIST curNbl,
1973 OvsFlowKey *key,
1974 UINT32 srcPortNo,
1975 OVS_PACKET_HDR_INFO *layers)
1976{
1977 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1978 OvsFlow *flow = NULL;
1979 OvsForwardingContext ovsFwdCtx = { 0 };
1980 UINT64 hash = 0;
1981 ASSERT(layers);
1982
1983 OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl,
1984 srcPortNo, 0,
1985 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl),
1986 completionList, layers, TRUE);
1987
1988 status = OvsExtractFlow(ovsFwdCtx.curNbl, ovsFwdCtx.srcVportNo, key,
1989 &ovsFwdCtx.layers, NULL);
1990 if (status != NDIS_STATUS_SUCCESS) {
1991 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
1992 L"OVS-Dropped due to extract flow failure");
1993 ovsActionStats.failedFlowMiss++;
1994 return NDIS_STATUS_FAILURE;
1995 }
1996
1997 flow = OvsLookupFlow(&ovsFwdCtx.switchContext->datapath, key, &hash, FALSE);
1998 if (flow) {
1999 UINT32 level = OvsDeferredActionsLevelGet();
2000
2001 if (level > DEFERRED_ACTION_EXEC_LEVEL) {
2002 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
2003 L"OVS-Dropped due to deferred actions execution level limit \
2004 reached");
2005 ovsActionStats.deferredActionsExecLimit++;
2006 ovsFwdCtx.curNbl = NULL;
2007 return NDIS_STATUS_FAILURE;
2008 }
2009
2010 OvsFlowUsed(flow, ovsFwdCtx.curNbl, &ovsFwdCtx.layers);
2011 ovsFwdCtx.switchContext->datapath.hits++;
2012
2013 OvsDeferredActionsLevelInc();
2014
2015 status = OvsDoExecuteActions(ovsFwdCtx.switchContext,
2016 ovsFwdCtx.completionList,
2017 ovsFwdCtx.curNbl,
2018 ovsFwdCtx.srcVportNo,
2019 ovsFwdCtx.sendFlags,
2020 key, &hash, &ovsFwdCtx.layers,
2021 flow->actions, flow->actionsLen);
2022 ovsFwdCtx.curNbl = NULL;
2023
2024 OvsDeferredActionsLevelDec();
2025 } else {
2026 POVS_VPORT_ENTRY vport = NULL;
2027 LIST_ENTRY missedPackets;
2028 UINT32 num = 0;
2029
2030 ovsFwdCtx.switchContext->datapath.misses++;
2031 InitializeListHead(&missedPackets);
2032 vport = OvsFindVportByPortNo(switchContext, srcPortNo);
2033 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
2034 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
2035 L"OVS-Dropped due to port removal");
2036 ovsActionStats.noVport++;
2037 return NDIS_STATUS_SUCCESS;
2038 }
2039 status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS,
2040 vport, key, ovsFwdCtx.curNbl,
e97d67f4 2041 vport->portId ==
ee25964a
SV
2042 switchContext->virtualExternalPortId,
2043 &ovsFwdCtx.layers,
2044 ovsFwdCtx.switchContext,
2045 &missedPackets, &num);
2046 if (num) {
2047 OvsQueuePackets(&missedPackets, num);
2048 }
2049 if (status == NDIS_STATUS_SUCCESS) {
2050 /* Complete the packet since it was copied to user buffer. */
2051 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
2052 L"OVS-Dropped since packet was copied to userspace");
2053 ovsActionStats.flowMiss++;
2054 } else {
2055 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
2056 L"OVS-Dropped due to failure to queue to userspace");
2057 ovsActionStats.failedFlowMiss++;
2058 status = NDIS_STATUS_FAILURE;
2059 }
2060 }
2061
c803536e
SS
2062 return status;
2063}