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