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