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