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