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