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