2 * Copyright (c) 2014, 2016 VMware, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
38 #define OVS_DBG_MOD OVS_DBG_ACTION
40 #define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2
42 typedef struct _OVS_ACTION_STATS {
52 UINT32 failedFlowMiss;
54 UINT32 failedFlowExtract;
59 UINT32 cannotGrowDest;
61 UINT32 failedChecksum;
62 UINT32 deferredActionsQueueFull;
63 UINT32 deferredActionsExecLimit;
64 } OVS_ACTION_STATS, *POVS_ACTION_STATS;
66 OVS_ACTION_STATS ovsActionStats;
69 * There a lot of data that needs to be maintained while executing the pipeline
70 * as dictated by the actions of a flow, across different functions at different
71 * levels. Such data is put together in a 'context' structure. Care should be
72 * exercised while adding new members to the structure - only add ones that get
73 * used across multiple stages in the pipeline/get used in multiple functions.
75 typedef struct OvsForwardingContext {
76 POVS_SWITCH_CONTEXT switchContext;
77 /* The NBL currently used in the pipeline. */
78 PNET_BUFFER_LIST curNbl;
79 /* NDIS forwarding detail for 'curNbl'. */
80 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
81 /* Array of destination ports for 'curNbl'. */
82 PNDIS_SWITCH_FORWARDING_DESTINATION_ARRAY destinationPorts;
83 /* send flags while sending 'curNbl' into NDIS. */
85 /* Total number of output ports, used + unused, in 'curNbl'. */
86 UINT32 destPortsSizeIn;
87 /* Total number of used output ports in 'curNbl'. */
88 UINT32 destPortsSizeOut;
90 * If 'curNbl' is not owned by OVS, they need to be tracked, if they need to
93 OvsCompletionList *completionList;
95 * vport number of 'curNbl' when it is passed from the PIF bridge to the INT
96 * bridge. ie. during tunneling on the Rx side.
102 * - specified in actions during tunneling Tx
103 * - extracted from an NBL during tunneling Rx
105 OvsIPv4TunnelKey tunKey;
109 * To store the output port, when it is a tunneled port. We don't foresee
110 * multiple tunneled ports as outport for any given NBL.
112 POVS_VPORT_ENTRY tunnelTxNic;
116 * Points to the Internal port on the PIF Bridge, if the packet needs to be
119 POVS_VPORT_ENTRY tunnelRxNic;
121 /* header information */
122 OVS_PACKET_HDR_INFO layers;
123 } OvsForwardingContext;
126 * --------------------------------------------------------------------------
127 * OvsInitForwardingCtx --
128 * Function to init/re-init the 'ovsFwdCtx' context as the actions pipeline
132 * NDIS_STATUS_SUCCESS on success
133 * Other NDIS_STATUS upon failure. Upon failure, it is safe to call
134 * OvsCompleteNBLForwardingCtx(), since 'ovsFwdCtx' has been initialized
135 * enough for OvsCompleteNBLForwardingCtx() to do its work.
136 * --------------------------------------------------------------------------
138 static __inline NDIS_STATUS
139 OvsInitForwardingCtx(OvsForwardingContext *ovsFwdCtx,
140 POVS_SWITCH_CONTEXT switchContext,
141 PNET_BUFFER_LIST curNbl,
144 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail,
145 OvsCompletionList *completionList,
146 OVS_PACKET_HDR_INFO *layers,
147 BOOLEAN resetTunnelInfo)
150 ASSERT(switchContext);
155 * Set values for curNbl and switchContext so upon failures, we have enough
156 * information to do cleanup.
158 ovsFwdCtx->curNbl = curNbl;
159 ovsFwdCtx->switchContext = switchContext;
160 ovsFwdCtx->completionList = completionList;
161 ovsFwdCtx->fwdDetail = fwdDetail;
163 if (fwdDetail->NumAvailableDestinations > 0) {
165 * XXX: even though MSDN says GetNetBufferListDestinations() returns
166 * NDIS_STATUS, the header files say otherwise.
168 switchContext->NdisSwitchHandlers.GetNetBufferListDestinations(
169 switchContext->NdisSwitchContext, curNbl,
170 &ovsFwdCtx->destinationPorts);
172 ASSERT(ovsFwdCtx->destinationPorts);
173 /* Ensure that none of the elements are consumed yet. */
174 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
175 fwdDetail->NumAvailableDestinations);
177 ovsFwdCtx->destinationPorts = NULL;
179 ovsFwdCtx->destPortsSizeIn = fwdDetail->NumAvailableDestinations;
180 ovsFwdCtx->destPortsSizeOut = 0;
181 ovsFwdCtx->srcVportNo = srcVportNo;
182 ovsFwdCtx->sendFlags = sendFlags;
184 ovsFwdCtx->layers = *layers;
186 RtlZeroMemory(&ovsFwdCtx->layers, sizeof ovsFwdCtx->layers);
188 if (resetTunnelInfo) {
189 ovsFwdCtx->tunnelTxNic = NULL;
190 ovsFwdCtx->tunnelRxNic = NULL;
191 RtlZeroMemory(&ovsFwdCtx->tunKey, sizeof ovsFwdCtx->tunKey);
194 return NDIS_STATUS_SUCCESS;
198 * --------------------------------------------------------------------------
199 * OvsDetectTunnelRxPkt --
200 * Utility function for an RX packet to detect its tunnel type.
203 * True - if the tunnel type was detected.
204 * False - if not a tunnel packet or tunnel type not supported.
205 * --------------------------------------------------------------------------
207 static __inline BOOLEAN
208 OvsDetectTunnelRxPkt(OvsForwardingContext *ovsFwdCtx,
209 const OvsFlowKey *flowKey)
211 POVS_VPORT_ENTRY tunnelVport = NULL;
213 /* XXX: we should also check for the length of the UDP payload to pick
214 * packets only if they are at least VXLAN header size.
216 if (!flowKey->ipKey.nwFrag) {
217 UINT16 dstPort = htons(flowKey->ipKey.l4.tpDst);
218 switch (flowKey->ipKey.nwProto) {
220 tunnelVport = OvsFindTunnelVportByPortType(ovsFwdCtx->switchContext,
223 ovsActionStats.rxGre++;
227 tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
231 ovsActionStats.rxStt++;
235 tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
237 OVS_VPORT_TYPE_VXLAN);
239 ovsActionStats.rxVxlan++;
245 // We might get tunnel packets even before the tunnel gets initialized.
247 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
248 ovsFwdCtx->tunnelRxNic = tunnelVport;
256 * --------------------------------------------------------------------------
257 * OvsDetectTunnelPkt --
258 * Utility function to detect if a packet is to be subjected to
259 * tunneling (Tx) or de-tunneling (Rx). Various factors such as source
260 * port, destination port, packet contents, and previously setup tunnel
264 * True - If the packet is to be subjected to tunneling.
265 * In case of invalid tunnel context, the tunneling functionality is
266 * a no-op and is completed within this function itself by consuming
267 * all of the tunneling context.
268 * False - If not a tunnel packet or tunnel type not supported. Caller should
269 * process the packet as a non-tunnel packet.
270 * --------------------------------------------------------------------------
272 static __inline BOOLEAN
273 OvsDetectTunnelPkt(OvsForwardingContext *ovsFwdCtx,
274 const POVS_VPORT_ENTRY dstVport,
275 const OvsFlowKey *flowKey)
277 if (OvsIsInternalVportType(dstVport->ovsType)) {
280 * The source of NBL during tunneling Rx could be the external
281 * port or if it is being executed from userspace, the source port is
284 BOOLEAN validSrcPort =
285 (ovsFwdCtx->fwdDetail->SourcePortId ==
286 ovsFwdCtx->switchContext->virtualExternalPortId) ||
287 (ovsFwdCtx->fwdDetail->SourcePortId ==
288 NDIS_SWITCH_DEFAULT_PORT_ID);
290 if (validSrcPort && OvsDetectTunnelRxPkt(ovsFwdCtx, flowKey)) {
291 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
292 ASSERT(ovsFwdCtx->tunnelRxNic != NULL);
295 } else if (OvsIsTunnelVportType(dstVport->ovsType)) {
296 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
297 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
301 * The destination port is a tunnel port. Encapsulation must be
302 * performed only on packets that originate from:
304 * - a bridge-internal port (packets generated from userspace)
307 * If the packet will not be encapsulated, consume the tunnel context
310 if (ovsFwdCtx->srcVportNo != OVS_DPPORT_NUMBER_INVALID) {
312 POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(
313 ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
316 (vport->ovsType != OVS_VPORT_TYPE_NETDEV &&
317 !OvsIsBridgeInternalVport(vport))) {
318 ovsFwdCtx->tunKey.dst = 0;
322 /* Tunnel the packet only if tunnel context is set. */
323 if (ovsFwdCtx->tunKey.dst != 0) {
324 switch(dstVport->ovsType) {
325 case OVS_VPORT_TYPE_GRE:
326 ovsActionStats.txGre++;
328 case OVS_VPORT_TYPE_VXLAN:
329 ovsActionStats.txVxlan++;
331 case OVS_VPORT_TYPE_STT:
332 ovsActionStats.txStt++;
335 ovsFwdCtx->tunnelTxNic = dstVport;
346 * --------------------------------------------------------------------------
348 * Add the specified destination vport into the forwarding context. If the
349 * vport is a VIF/external port, it is added directly to the NBL. If it is
350 * a tunneling port, it is NOT added to the NBL.
353 * NDIS_STATUS_SUCCESS on success
354 * Other NDIS_STATUS upon failure.
355 * --------------------------------------------------------------------------
357 static __inline NDIS_STATUS
358 OvsAddPorts(OvsForwardingContext *ovsFwdCtx,
360 NDIS_SWITCH_PORT_ID dstPortId,
361 BOOLEAN preserveVLAN,
362 BOOLEAN preservePriority)
364 POVS_VPORT_ENTRY vport;
365 PNDIS_SWITCH_PORT_DESTINATION fwdPort;
367 POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext;
370 * We hold the dispatch lock that protects the list of vports, so vports
371 * validated here can be added as destinations safely before we call into
374 * Some of the vports can be tunnelled ports as well in which case
375 * they should be added to a separate list of tunnelled destination ports
376 * instead of the VIF ports. The context for the tunnel is settable
377 * in OvsForwardingContext.
379 vport = OvsFindVportByPortNo(ovsFwdCtx->switchContext, dstPortId);
380 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
382 * There may be some latency between a port disappearing, and userspace
383 * updating the recalculated flows. In the meantime, handle invalid
386 ovsActionStats.noVport++;
387 return NDIS_STATUS_SUCCESS;
389 ASSERT(vport->nicState == NdisSwitchNicStateConnected);
390 vport->stats.txPackets++;
391 vport->stats.txBytes +=
392 NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl));
394 if (OvsIsBridgeInternalVport(vport)) {
395 return NDIS_STATUS_SUCCESS;
398 if (OvsDetectTunnelPkt(ovsFwdCtx, vport, flowKey)) {
399 return NDIS_STATUS_SUCCESS;
402 if (ovsFwdCtx->destPortsSizeOut == ovsFwdCtx->destPortsSizeIn) {
403 if (ovsFwdCtx->destPortsSizeIn == 0) {
404 ASSERT(ovsFwdCtx->destinationPorts == NULL);
405 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0);
407 switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations(
408 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
409 OVS_DEST_PORTS_ARRAY_MIN_SIZE,
410 &ovsFwdCtx->destinationPorts);
411 if (status != NDIS_STATUS_SUCCESS) {
412 ovsActionStats.cannotGrowDest++;
415 ovsFwdCtx->destPortsSizeIn =
416 ovsFwdCtx->fwdDetail->NumAvailableDestinations;
417 ASSERT(ovsFwdCtx->destinationPorts);
419 ASSERT(ovsFwdCtx->destinationPorts != NULL);
422 * A ULONG value that specifies the total number of
423 * NDIS_SWITCH_PORT_DESTINATION elements in the
424 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure.
427 * A ULONG value that specifies the number of
428 * NDIS_SWITCH_PORT_DESTINATION elements in the
429 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure that
430 * specify port destinations.
432 * NumAvailableDestinations:
433 * A value that specifies the number of unused extensible switch
434 * destination ports elements within an NET_BUFFER_LIST structure.
436 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
437 ovsFwdCtx->destPortsSizeIn);
438 ASSERT(ovsFwdCtx->destinationPorts->NumDestinations ==
439 ovsFwdCtx->destPortsSizeOut -
440 ovsFwdCtx->fwdDetail->NumAvailableDestinations);
441 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations > 0);
443 * Before we grow the array of destination ports, the current set
444 * of ports needs to be committed. Only the ports added since the
445 * last commit need to be part of the new update.
447 status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations(
448 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
449 ovsFwdCtx->fwdDetail->NumAvailableDestinations,
450 ovsFwdCtx->destinationPorts);
451 if (status != NDIS_STATUS_SUCCESS) {
452 ovsActionStats.cannotGrowDest++;
455 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
456 ovsFwdCtx->destPortsSizeIn);
457 ASSERT(ovsFwdCtx->destinationPorts->NumDestinations ==
458 ovsFwdCtx->destPortsSizeOut);
459 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0);
461 status = switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations(
462 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
463 ovsFwdCtx->destPortsSizeIn, &ovsFwdCtx->destinationPorts);
464 if (status != NDIS_STATUS_SUCCESS) {
465 ovsActionStats.cannotGrowDest++;
468 ASSERT(ovsFwdCtx->destinationPorts != NULL);
469 ovsFwdCtx->destPortsSizeIn <<= 1;
473 ASSERT(ovsFwdCtx->destPortsSizeOut < ovsFwdCtx->destPortsSizeIn);
475 NDIS_SWITCH_PORT_DESTINATION_AT_ARRAY_INDEX(ovsFwdCtx->destinationPorts,
476 ovsFwdCtx->destPortsSizeOut);
478 fwdPort->PortId = vport->portId;
479 fwdPort->NicIndex = vport->nicIndex;
480 fwdPort->IsExcluded = 0;
481 fwdPort->PreserveVLAN = preserveVLAN;
482 fwdPort->PreservePriority = preservePriority;
483 ovsFwdCtx->destPortsSizeOut += 1;
485 return NDIS_STATUS_SUCCESS;
490 * --------------------------------------------------------------------------
491 * OvsClearTunTxCtx --
492 * Utility function to clear tx tunneling context.
493 * --------------------------------------------------------------------------
496 OvsClearTunTxCtx(OvsForwardingContext *ovsFwdCtx)
498 ovsFwdCtx->tunnelTxNic = NULL;
499 ovsFwdCtx->tunKey.dst = 0;
504 * --------------------------------------------------------------------------
505 * OvsClearTunRxCtx --
506 * Utility function to clear rx tunneling context.
507 * --------------------------------------------------------------------------
510 OvsClearTunRxCtx(OvsForwardingContext *ovsFwdCtx)
512 ovsFwdCtx->tunnelRxNic = NULL;
513 ovsFwdCtx->tunKey.dst = 0;
518 * --------------------------------------------------------------------------
519 * OvsCompleteNBLForwardingCtx --
520 * This utility function is responsible for freeing/completing an NBL - either
521 * by adding it to a completion list or by freeing it.
524 * It also resets the necessary fields in 'ovsFwdCtx'.
525 * --------------------------------------------------------------------------
528 OvsCompleteNBLForwardingCtx(OvsForwardingContext *ovsFwdCtx,
531 NDIS_STRING filterReason;
533 RtlInitUnicodeString(&filterReason, dropReason);
534 if (ovsFwdCtx->completionList) {
535 OvsAddPktCompletionList(ovsFwdCtx->completionList, TRUE,
536 ovsFwdCtx->fwdDetail->SourcePortId, ovsFwdCtx->curNbl, 1,
538 ovsFwdCtx->curNbl = NULL;
540 /* If there is no completionList, we assume this is ovs created NBL */
541 ovsFwdCtx->curNbl = OvsCompleteNBL(ovsFwdCtx->switchContext,
542 ovsFwdCtx->curNbl, TRUE);
543 ASSERT(ovsFwdCtx->curNbl == NULL);
545 /* XXX: these can be made debug only to save cycles. Ideally the pipeline
546 * using these fields should reset the values at the end of the pipeline. */
547 ovsFwdCtx->destPortsSizeOut = 0;
548 ovsFwdCtx->tunnelTxNic = NULL;
549 ovsFwdCtx->tunnelRxNic = NULL;
553 * --------------------------------------------------------------------------
554 * OvsDoFlowLookupOutput --
555 * Function to be used for the second stage of a tunneling workflow, ie.:
556 * - On the encapsulated packet on Tx path, to do a flow extract, flow
557 * lookup and excuting the actions.
558 * - On the decapsulated packet on Rx path, to do a flow extract, flow
559 * lookup and excuting the actions.
561 * XXX: It is assumed that the NBL in 'ovsFwdCtx' is owned by OVS. This is
562 * until the new buffer management framework is adopted.
565 * The NBL in 'ovsFwdCtx' is consumed.
566 * --------------------------------------------------------------------------
568 static __inline NDIS_STATUS
569 OvsDoFlowLookupOutput(OvsForwardingContext *ovsFwdCtx)
571 OvsFlowKey key = { 0 };
572 OvsFlow *flow = NULL;
574 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
575 POVS_VPORT_ENTRY vport =
576 OvsFindVportByPortNo(ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
577 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
578 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
579 L"OVS-Dropped due to internal/tunnel port removal");
580 ovsActionStats.noVport++;
581 return NDIS_STATUS_SUCCESS;
583 ASSERT(vport->nicState == NdisSwitchNicStateConnected);
585 /* Assert that in the Rx direction, key is always setup. */
586 ASSERT(ovsFwdCtx->tunnelRxNic == NULL || ovsFwdCtx->tunKey.dst != 0);
588 OvsExtractFlow(ovsFwdCtx->curNbl, ovsFwdCtx->srcVportNo,
589 &key, &ovsFwdCtx->layers,
590 ovsFwdCtx->tunKey.dst != 0 ? &ovsFwdCtx->tunKey : NULL);
591 if (status != NDIS_STATUS_SUCCESS) {
592 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
593 L"OVS-Flow extract failed");
594 ovsActionStats.failedFlowExtract++;
598 flow = OvsLookupFlow(&ovsFwdCtx->switchContext->datapath, &key, &hash, FALSE);
600 OvsFlowUsed(flow, ovsFwdCtx->curNbl, &ovsFwdCtx->layers);
601 ovsFwdCtx->switchContext->datapath.hits++;
602 status = OvsDoExecuteActions(ovsFwdCtx->switchContext,
603 ovsFwdCtx->completionList,
605 ovsFwdCtx->srcVportNo,
606 ovsFwdCtx->sendFlags,
607 &key, &hash, &ovsFwdCtx->layers,
608 flow->actions, flow->actionsLen);
609 ovsFwdCtx->curNbl = NULL;
611 LIST_ENTRY missedPackets;
613 ovsFwdCtx->switchContext->datapath.misses++;
614 InitializeListHead(&missedPackets);
615 status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS, vport,
616 &key,ovsFwdCtx->curNbl,
617 FALSE, &ovsFwdCtx->layers,
618 ovsFwdCtx->switchContext, &missedPackets, &num);
620 OvsQueuePackets(&missedPackets, num);
622 if (status == NDIS_STATUS_SUCCESS) {
623 /* Complete the packet since it was copied to user buffer. */
624 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
625 L"OVS-Dropped since packet was copied to userspace");
626 ovsActionStats.flowMiss++;
627 status = NDIS_STATUS_SUCCESS;
629 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
630 L"OVS-Dropped due to failure to queue to userspace");
631 status = NDIS_STATUS_FAILURE;
632 ovsActionStats.failedFlowMiss++;
640 * --------------------------------------------------------------------------
642 * The start function for Tx tunneling - encapsulates the packet, and
643 * outputs the packet on the PIF bridge.
646 * The NBL in 'ovsFwdCtx' is consumed.
647 * --------------------------------------------------------------------------
649 static __inline NDIS_STATUS
650 OvsTunnelPortTx(OvsForwardingContext *ovsFwdCtx)
652 NDIS_STATUS status = NDIS_STATUS_FAILURE;
653 PNET_BUFFER_LIST newNbl = NULL;
656 * Setup the source port to be the internal port to as to facilitate the
657 * second OvsLookupFlow.
659 if (ovsFwdCtx->switchContext->internalVport == NULL ||
660 ovsFwdCtx->switchContext->virtualExternalVport == NULL) {
661 OvsClearTunTxCtx(ovsFwdCtx);
662 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
663 L"OVS-Dropped since either internal or external port is absent");
664 return NDIS_STATUS_FAILURE;
666 ovsFwdCtx->srcVportNo =
667 ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->portNo;
669 ovsFwdCtx->fwdDetail->SourcePortId = ovsFwdCtx->switchContext->internalPortId;
670 ovsFwdCtx->fwdDetail->SourceNicIndex =
671 ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->nicIndex;
673 /* Do the encap. Encap function does not consume the NBL. */
674 switch(ovsFwdCtx->tunnelTxNic->ovsType) {
675 case OVS_VPORT_TYPE_GRE:
676 status = OvsEncapGre(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
677 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
678 &ovsFwdCtx->layers, &newNbl);
680 case OVS_VPORT_TYPE_VXLAN:
681 status = OvsEncapVxlan(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
682 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
683 &ovsFwdCtx->layers, &newNbl);
685 case OVS_VPORT_TYPE_STT:
686 status = OvsEncapStt(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
687 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
688 &ovsFwdCtx->layers, &newNbl);
691 ASSERT(! "Tx: Unhandled tunnel type");
694 /* Reset the tunnel context so that it doesn't get used after this point. */
695 OvsClearTunTxCtx(ovsFwdCtx);
697 if (status == NDIS_STATUS_SUCCESS) {
699 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
700 L"Complete after cloning NBL for encapsulation");
701 ovsFwdCtx->curNbl = newNbl;
702 status = OvsDoFlowLookupOutput(ovsFwdCtx);
703 ASSERT(ovsFwdCtx->curNbl == NULL);
706 * XXX: Temporary freeing of the packet until we register a
707 * callback to IP helper.
709 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
710 L"OVS-Dropped due to encap failure");
711 ovsActionStats.failedEncap++;
712 status = NDIS_STATUS_SUCCESS;
719 * --------------------------------------------------------------------------
721 * Decapsulate the incoming NBL based on the tunnel type and goes through
722 * the flow lookup for the inner packet.
724 * Note: IP checksum is validate here, but L4 checksum validation needs
725 * to be done by the corresponding tunnel types.
728 * The NBL in 'ovsFwdCtx' is consumed.
729 * --------------------------------------------------------------------------
731 static __inline NDIS_STATUS
732 OvsTunnelPortRx(OvsForwardingContext *ovsFwdCtx)
734 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
735 PNET_BUFFER_LIST newNbl = NULL;
736 POVS_VPORT_ENTRY tunnelRxVport = ovsFwdCtx->tunnelRxNic;
737 PCWSTR dropReason = L"OVS-dropped due to new decap packet";
739 if (OvsValidateIPChecksum(ovsFwdCtx->curNbl, &ovsFwdCtx->layers)
740 != NDIS_STATUS_SUCCESS) {
741 ovsActionStats.failedChecksum++;
742 OVS_LOG_INFO("Packet dropped due to IP checksum failure.");
747 * Decap port functions should return a new NBL if it was copied, and
748 * this new NBL should be setup as the ovsFwdCtx->curNbl.
751 switch(tunnelRxVport->ovsType) {
752 case OVS_VPORT_TYPE_GRE:
753 status = OvsDecapGre(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
754 &ovsFwdCtx->tunKey, &newNbl);
756 case OVS_VPORT_TYPE_VXLAN:
757 status = OvsDecapVxlan(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
758 &ovsFwdCtx->tunKey, &newNbl);
760 case OVS_VPORT_TYPE_STT:
761 status = OvsDecapStt(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
762 &ovsFwdCtx->tunKey, &newNbl);
763 if (status == NDIS_STATUS_SUCCESS && newNbl == NULL) {
764 /* This was an STT-LSO Fragment */
765 dropReason = L"OVS-STT segment is cached";
769 OVS_LOG_ERROR("Rx: Unhandled tunnel type: %d\n",
770 tunnelRxVport->ovsType);
771 ASSERT(! "Rx: Unhandled tunnel type");
772 status = NDIS_STATUS_NOT_SUPPORTED;
775 if (status != NDIS_STATUS_SUCCESS) {
776 ovsActionStats.failedDecap++;
781 * tunnelRxNic and other fields will be cleared, re-init the context
784 OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason);
787 /* Decapsulated packet is in a new NBL */
788 ovsFwdCtx->tunnelRxNic = tunnelRxVport;
789 OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
790 newNbl, tunnelRxVport->portNo, 0,
791 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
792 ovsFwdCtx->completionList,
793 &ovsFwdCtx->layers, FALSE);
796 * Set the NBL's SourcePortId and SourceNicIndex to default values to
797 * keep NDIS happy when we forward the packet.
799 ovsFwdCtx->fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
800 ovsFwdCtx->fwdDetail->SourceNicIndex = 0;
802 status = OvsDoFlowLookupOutput(ovsFwdCtx);
804 ASSERT(ovsFwdCtx->curNbl == NULL);
805 OvsClearTunRxCtx(ovsFwdCtx);
810 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
811 L"OVS-dropped due to decap failure");
812 OvsClearTunRxCtx(ovsFwdCtx);
818 * --------------------------------------------------------------------------
819 * OvsOutputForwardingCtx --
820 * This function outputs an NBL to NDIS or to a tunneling pipeline based on
821 * the ports added so far into 'ovsFwdCtx'.
824 * This function consumes the NBL - either by forwarding it successfully to
825 * NDIS, or adding it to the completion list in 'ovsFwdCtx', or freeing it.
827 * Also makes sure that the list of destination ports - tunnel or otherwise is
829 * --------------------------------------------------------------------------
831 static __inline NDIS_STATUS
832 OvsOutputForwardingCtx(OvsForwardingContext *ovsFwdCtx)
834 NDIS_STATUS status = STATUS_SUCCESS;
835 POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext;
839 * Handle the case where the some of the destination ports are tunneled
840 * ports - the non-tunneled ports get a unmodified copy of the NBL, and the
841 * tunneling pipeline starts when we output the packet to tunneled port.
843 if (ovsFwdCtx->destPortsSizeOut > 0) {
844 PNET_BUFFER_LIST newNbl = NULL;
846 UINT32 portsToUpdate =
847 ovsFwdCtx->fwdDetail->NumAvailableDestinations -
848 (ovsFwdCtx->destPortsSizeIn - ovsFwdCtx->destPortsSizeOut);
850 ASSERT(ovsFwdCtx->destinationPorts != NULL);
853 * Create a copy of the packet in order to do encap on it later. Also,
854 * don't copy the offload context since the encap'd packet has a
855 * different set of headers. This will change when we implement offloads
856 * before doing encapsulation.
858 if (ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL) {
859 nb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
860 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
861 0, 0, TRUE /*copy NBL info*/);
862 if (newNbl == NULL) {
863 status = NDIS_STATUS_RESOURCES;
864 ovsActionStats.noCopiedNbl++;
865 dropReason = L"Dropped due to failure to create NBL copy.";
870 /* It does not seem like we'll get here unless 'portsToUpdate' > 0. */
871 ASSERT(portsToUpdate > 0);
872 status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations(
873 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
874 portsToUpdate, ovsFwdCtx->destinationPorts);
875 if (status != NDIS_STATUS_SUCCESS) {
876 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
877 ovsActionStats.cannotGrowDest++;
878 dropReason = L"Dropped due to failure to update destinations.";
882 OvsSendNBLIngress(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
883 ovsFwdCtx->sendFlags);
884 /* End this pipeline by resetting the corresponding context. */
885 ovsFwdCtx->destPortsSizeOut = 0;
886 ovsFwdCtx->curNbl = NULL;
888 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
889 newNbl, ovsFwdCtx->srcVportNo, 0,
890 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
891 ovsFwdCtx->completionList,
892 &ovsFwdCtx->layers, FALSE);
893 if (status != NDIS_STATUS_SUCCESS) {
894 dropReason = L"Dropped due to resouces.";
900 if (ovsFwdCtx->tunnelTxNic != NULL) {
901 status = OvsTunnelPortTx(ovsFwdCtx);
902 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
903 ASSERT(ovsFwdCtx->tunKey.dst == 0);
904 } else if (ovsFwdCtx->tunnelRxNic != NULL) {
905 status = OvsTunnelPortRx(ovsFwdCtx);
906 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
907 ASSERT(ovsFwdCtx->tunKey.dst == 0);
909 ASSERT(ovsFwdCtx->curNbl == NULL);
914 if (status != NDIS_STATUS_SUCCESS) {
915 OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason);
923 * --------------------------------------------------------------------------
924 * OvsLookupFlowOutput --
925 * Utility function for external callers to do flow extract, lookup,
926 * actions execute on a given NBL.
928 * Note: If this is being used from a callback function, make sure that the
929 * arguments specified are still valid in the asynchronous context.
932 * This function consumes the NBL.
933 * --------------------------------------------------------------------------
936 OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext,
938 PNET_BUFFER_LIST curNbl)
941 OvsForwardingContext ovsFwdCtx;
942 POVS_VPORT_ENTRY internalVport =
943 (POVS_VPORT_ENTRY)switchContext->internalVport;
945 /* XXX: make sure comp list was not a stack variable previously. */
946 OvsCompletionList *completionList = (OvsCompletionList *)compList;
949 * XXX: can internal port disappear while we are busy doing ARP resolution?
950 * It could, but will we get this callback from IP helper in that case. Need
953 ASSERT(switchContext->internalVport);
954 status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl,
955 internalVport->portNo, 0,
956 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl),
957 completionList, NULL, TRUE);
958 if (status != NDIS_STATUS_SUCCESS) {
959 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
960 L"OVS-Dropped due to resources");
966 * XXX: We need to acquire the dispatch lock and the datapath lock.
969 OvsDoFlowLookupOutput(&ovsFwdCtx);
974 * --------------------------------------------------------------------------
975 * OvsOutputBeforeSetAction --
976 * Function to be called to complete one set of actions on an NBL, before
977 * we start the next one.
978 * --------------------------------------------------------------------------
980 static __inline NDIS_STATUS
981 OvsOutputBeforeSetAction(OvsForwardingContext *ovsFwdCtx)
983 PNET_BUFFER_LIST newNbl;
984 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
987 * Create a copy and work on the copy after this point. The original NBL is
988 * forwarded. One reason to not use the copy for forwarding is that
989 * ports have already been added to the original NBL, and it might be
990 * inefficient/impossible to remove/re-add them to the copy. There's no
991 * notion of removing the ports, the ports need to be marked as
992 * "isExcluded". There's seems no real advantage to retaining the original
993 * and sending out the copy instead.
995 * XXX: We are copying the offload context here. This is to handle actions
997 * outport, pop_vlan(), outport, push_vlan(), outport
999 * copy size needs to include inner ether + IP + TCP, need to revisit
1000 * if we support IP options.
1001 * XXX Head room needs to include the additional encap.
1002 * XXX copySize check is not considering multiple NBs.
1004 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1005 0, 0, TRUE /*copy NBL info*/);
1007 ASSERT(ovsFwdCtx->destPortsSizeOut > 0 ||
1008 ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL);
1010 /* Send the original packet out and save the original source port number */
1011 UINT32 tempVportNo = ovsFwdCtx->srcVportNo;
1012 status = OvsOutputForwardingCtx(ovsFwdCtx);
1013 ASSERT(ovsFwdCtx->curNbl == NULL);
1014 ASSERT(ovsFwdCtx->destPortsSizeOut == 0);
1015 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
1016 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
1018 /* If we didn't make a copy, can't continue. */
1019 if (newNbl == NULL) {
1020 ovsActionStats.noCopiedNbl++;
1021 return NDIS_STATUS_RESOURCES;
1024 /* Finish the remaining actions with the new NBL */
1025 if (status != NDIS_STATUS_SUCCESS) {
1026 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
1028 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
1029 newNbl, tempVportNo, 0,
1030 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1031 ovsFwdCtx->completionList,
1032 &ovsFwdCtx->layers, FALSE);
1040 * --------------------------------------------------------------------------
1041 * OvsPopFieldInPacketBuf --
1042 * Function to pop a specified field of length 'shiftLength' located at
1043 * 'shiftOffset' from the ethernet header. The data on the left of the
1044 * 'shiftOffset' is right shifted.
1046 * Returns a pointer to the new start in 'bufferData'.
1047 * --------------------------------------------------------------------------
1049 static __inline NDIS_STATUS
1050 OvsPopFieldInPacketBuf(OvsForwardingContext *ovsFwdCtx,
1058 UINT32 packetLen, mdlLen;
1059 PNET_BUFFER_LIST newNbl;
1061 PUINT8 tempBuffer[ETH_HEADER_LENGTH];
1063 ASSERT(shiftOffset > ETH_ADDR_LENGTH);
1065 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1066 0, 0, TRUE /* copy NBL info */);
1068 ovsActionStats.noCopiedNbl++;
1069 return NDIS_STATUS_RESOURCES;
1072 /* Complete the original NBL and create a copy to modify. */
1073 OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to copy");
1075 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext, newNbl,
1076 ovsFwdCtx->srcVportNo, 0,
1077 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1078 NULL, &ovsFwdCtx->layers, FALSE);
1079 if (status != NDIS_STATUS_SUCCESS) {
1080 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1081 L"Dropped due to resouces");
1082 return NDIS_STATUS_RESOURCES;
1085 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1086 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1087 ASSERT(curNb->Next == NULL);
1088 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1089 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1091 return NDIS_STATUS_RESOURCES;
1093 mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1094 /* Bail out if L2 + shiftLength is not contiguous in the first buffer. */
1095 if (MIN(packetLen, mdlLen) < sizeof(EthHdr) + shiftLength) {
1097 return NDIS_STATUS_FAILURE;
1099 bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1100 RtlCopyMemory(tempBuffer, bufferStart, shiftOffset);
1101 RtlCopyMemory(bufferStart + shiftLength, tempBuffer, shiftOffset);
1102 NdisAdvanceNetBufferDataStart(curNb, shiftLength, FALSE, NULL);
1105 *bufferData = bufferStart + shiftLength;
1108 return NDIS_STATUS_SUCCESS;
1113 * --------------------------------------------------------------------------
1114 * OvsPopVlanInPktBuf --
1115 * Function to pop a VLAN tag when the tag is in the packet buffer.
1116 * --------------------------------------------------------------------------
1118 static __inline NDIS_STATUS
1119 OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
1122 * Declare a dummy vlanTag structure since we need to compute the size
1123 * of shiftLength. The NDIS one is a unionized structure.
1125 NDIS_PACKET_8021Q_INFO vlanTag = {0};
1126 UINT32 shiftLength = sizeof(vlanTag.TagHeader);
1127 UINT32 shiftOffset = sizeof(DL_EUI48) + sizeof(DL_EUI48);
1129 return OvsPopFieldInPacketBuf(ovsFwdCtx, shiftOffset, shiftLength, NULL);
1134 * --------------------------------------------------------------------------
1135 * OvsActionMplsPop --
1136 * Function to pop the first MPLS label from the current packet.
1137 * --------------------------------------------------------------------------
1139 static __inline NDIS_STATUS
1140 OvsActionMplsPop(OvsForwardingContext *ovsFwdCtx,
1143 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1144 OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
1145 EthHdr *ethHdr = NULL;
1147 status = OvsPopFieldInPacketBuf(ovsFwdCtx, sizeof(*ethHdr),
1148 MPLS_HLEN, (PUINT8*)ðHdr);
1149 if (status == NDIS_STATUS_SUCCESS) {
1150 if (ethHdr && OvsEthertypeIsMpls(ethHdr->Type)) {
1151 ethHdr->Type = ethertype;
1154 layers->l3Offset -= MPLS_HLEN;
1155 layers->l4Offset -= MPLS_HLEN;
1163 * --------------------------------------------------------------------------
1164 * OvsActionMplsPush --
1165 * Function to push the MPLS label into the current packet.
1166 * --------------------------------------------------------------------------
1168 static __inline NDIS_STATUS
1169 OvsActionMplsPush(OvsForwardingContext *ovsFwdCtx,
1170 const struct ovs_action_push_mpls *mpls)
1173 PNET_BUFFER curNb = NULL;
1175 PUINT8 bufferStart = NULL;
1176 OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
1177 EthHdr *ethHdr = NULL;
1178 MPLSHdr *mplsHdr = NULL;
1179 UINT32 mdlLen = 0, curMdlOffset = 0;
1180 PNET_BUFFER_LIST newNbl;
1182 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1183 layers->l3Offset, MPLS_HLEN, TRUE);
1185 ovsActionStats.noCopiedNbl++;
1186 return NDIS_STATUS_RESOURCES;
1188 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1189 L"Complete after partial copy.");
1191 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
1192 newNbl, ovsFwdCtx->srcVportNo, 0,
1193 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1194 NULL, &ovsFwdCtx->layers, FALSE);
1195 if (status != NDIS_STATUS_SUCCESS) {
1196 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1197 L"OVS-Dropped due to resources");
1198 return NDIS_STATUS_RESOURCES;
1201 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1202 ASSERT(curNb->Next == NULL);
1204 status = NdisRetreatNetBufferDataStart(curNb, MPLS_HLEN, 0, NULL);
1205 if (status != NDIS_STATUS_SUCCESS) {
1209 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1210 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1212 ovsActionStats.noResource++;
1213 return NDIS_STATUS_RESOURCES;
1216 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1217 mdlLen -= curMdlOffset;
1218 ASSERT(mdlLen >= MPLS_HLEN);
1220 ethHdr = (EthHdr *)(bufferStart + curMdlOffset);
1221 RtlMoveMemory(ethHdr, (UINT8*)ethHdr + MPLS_HLEN, sizeof(*ethHdr));
1222 ethHdr->Type = mpls->mpls_ethertype;
1224 mplsHdr = (MPLSHdr *)(ethHdr + 1);
1225 mplsHdr->lse = mpls->mpls_lse;
1227 layers->l3Offset += MPLS_HLEN;
1228 layers->l4Offset += MPLS_HLEN;
1230 return NDIS_STATUS_SUCCESS;
1234 * --------------------------------------------------------------------------
1235 * OvsTunnelAttrToIPv4TunnelKey --
1236 * Convert tunnel attribute to OvsIPv4TunnelKey.
1237 * --------------------------------------------------------------------------
1239 static __inline NDIS_STATUS
1240 OvsTunnelAttrToIPv4TunnelKey(PNL_ATTR attr,
1241 OvsIPv4TunnelKey *tunKey)
1246 tunKey->attr[0] = 0;
1247 tunKey->attr[1] = 0;
1248 tunKey->attr[2] = 0;
1249 ASSERT(NlAttrType(attr) == OVS_KEY_ATTR_TUNNEL);
1251 NL_ATTR_FOR_EACH_UNSAFE (a, rem, NlAttrData(attr),
1252 NlAttrGetSize(attr)) {
1253 switch (NlAttrType(a)) {
1254 case OVS_TUNNEL_KEY_ATTR_ID:
1255 tunKey->tunnelId = NlAttrGetBe64(a);
1256 tunKey->flags |= OVS_TNL_F_KEY;
1258 case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
1259 tunKey->src = NlAttrGetBe32(a);
1261 case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
1262 tunKey->dst = NlAttrGetBe32(a);
1264 case OVS_TUNNEL_KEY_ATTR_TOS:
1265 tunKey->tos = NlAttrGetU8(a);
1267 case OVS_TUNNEL_KEY_ATTR_TTL:
1268 tunKey->ttl = NlAttrGetU8(a);
1270 case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
1271 tunKey->flags |= OVS_TNL_F_DONT_FRAGMENT;
1273 case OVS_TUNNEL_KEY_ATTR_CSUM:
1274 tunKey->flags |= OVS_TNL_F_CSUM;
1281 return NDIS_STATUS_SUCCESS;
1285 *----------------------------------------------------------------------------
1286 * OvsUpdateEthHeader --
1287 * Updates the ethernet header in ovsFwdCtx.curNbl inline based on the
1289 *----------------------------------------------------------------------------
1291 static __inline NDIS_STATUS
1292 OvsUpdateEthHeader(OvsForwardingContext *ovsFwdCtx,
1293 const struct ovs_key_ethernet *ethAttr)
1299 UINT32 packetLen, mdlLen;
1301 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1302 ASSERT(curNb->Next == NULL);
1303 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1304 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1305 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1307 ovsActionStats.noResource++;
1308 return NDIS_STATUS_RESOURCES;
1310 mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1312 /* Bail out if the L2 header is not in a contiguous buffer. */
1313 if (MIN(packetLen, mdlLen) < sizeof *ethHdr) {
1315 return NDIS_STATUS_FAILURE;
1317 ethHdr = (EthHdr *)(bufferStart + NET_BUFFER_CURRENT_MDL_OFFSET(curNb));
1319 RtlCopyMemory(ethHdr->Destination, ethAttr->eth_dst,
1320 sizeof ethHdr->Destination);
1321 RtlCopyMemory(ethHdr->Source, ethAttr->eth_src, sizeof ethHdr->Source);
1323 return NDIS_STATUS_SUCCESS;
1327 *----------------------------------------------------------------------------
1328 * OvsUpdateIPv4Header --
1329 * Updates the IPv4 header in ovsFwdCtx.curNbl inline based on the
1331 *----------------------------------------------------------------------------
1333 static __inline NDIS_STATUS
1334 OvsUpdateIPv4Header(OvsForwardingContext *ovsFwdCtx,
1335 const struct ovs_key_ipv4 *ipAttr)
1341 UINT32 mdlLen, hdrSize, packetLen;
1342 OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
1345 TCPHdr *tcpHdr = NULL;
1346 UDPHdr *udpHdr = NULL;
1348 ASSERT(layers->value != 0);
1351 * Peek into the MDL to get a handle to the IP header and if required
1352 * the TCP/UDP header as well. We check if the required headers are in one
1353 * contiguous MDL, and if not, we copy them over to one MDL.
1355 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1356 ASSERT(curNb->Next == NULL);
1357 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1358 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1359 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1361 ovsActionStats.noResource++;
1362 return NDIS_STATUS_RESOURCES;
1364 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1365 mdlLen -= curMdlOffset;
1366 ASSERT((INT)mdlLen >= 0);
1368 if (layers->isTcp || layers->isUdp) {
1369 hdrSize = layers->l4Offset +
1370 layers->isTcp ? sizeof (*tcpHdr) : sizeof (*udpHdr);
1372 hdrSize = layers->l3Offset + sizeof (*ipHdr);
1375 /* Count of number of bytes of valid data there are in the first MDL. */
1376 mdlLen = MIN(packetLen, mdlLen);
1377 if (mdlLen < hdrSize) {
1378 PNET_BUFFER_LIST newNbl;
1379 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1380 hdrSize, 0, TRUE /*copy NBL info*/);
1382 ovsActionStats.noCopiedNbl++;
1383 return NDIS_STATUS_RESOURCES;
1385 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1386 L"Complete after partial copy.");
1388 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
1389 newNbl, ovsFwdCtx->srcVportNo, 0,
1390 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1391 NULL, &ovsFwdCtx->layers, FALSE);
1392 if (status != NDIS_STATUS_SUCCESS) {
1393 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1394 L"OVS-Dropped due to resources");
1395 return NDIS_STATUS_RESOURCES;
1398 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1399 ASSERT(curNb->Next == NULL);
1400 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1401 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1403 ovsActionStats.noResource++;
1404 return NDIS_STATUS_RESOURCES;
1406 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1407 mdlLen -= curMdlOffset;
1408 ASSERT(mdlLen >= hdrSize);
1411 ipHdr = (IPHdr *)(bufferStart + curMdlOffset + layers->l3Offset);
1413 if (layers->isTcp) {
1414 tcpHdr = (TCPHdr *)(bufferStart + curMdlOffset + layers->l4Offset);
1415 } else if (layers->isUdp) {
1416 udpHdr = (UDPHdr *)(bufferStart + curMdlOffset + layers->l4Offset);
1420 * Adjust the IP header inline as dictated by the action, nad also update
1421 * the IP and the TCP checksum for the data modified.
1423 * In the future, this could be optimized to make one call to
1424 * ChecksumUpdate32(). Ignoring this for now, since for the most common
1425 * case, we only update the TTL.
1427 if (ipHdr->saddr != ipAttr->ipv4_src) {
1429 tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->saddr,
1431 } else if (udpHdr && udpHdr->check) {
1432 udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->saddr,
1436 if (ipHdr->check != 0) {
1437 ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->saddr,
1440 ipHdr->saddr = ipAttr->ipv4_src;
1442 if (ipHdr->daddr != ipAttr->ipv4_dst) {
1444 tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->daddr,
1446 } else if (udpHdr && udpHdr->check) {
1447 udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->daddr,
1451 if (ipHdr->check != 0) {
1452 ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->daddr,
1455 ipHdr->daddr = ipAttr->ipv4_dst;
1457 if (ipHdr->protocol != ipAttr->ipv4_proto) {
1458 UINT16 oldProto = (ipHdr->protocol << 16) & 0xff00;
1459 UINT16 newProto = (ipAttr->ipv4_proto << 16) & 0xff00;
1461 tcpHdr->check = ChecksumUpdate16(tcpHdr->check, oldProto, newProto);
1462 } else if (udpHdr && udpHdr->check) {
1463 udpHdr->check = ChecksumUpdate16(udpHdr->check, oldProto, newProto);
1466 if (ipHdr->check != 0) {
1467 ipHdr->check = ChecksumUpdate16(ipHdr->check, oldProto, newProto);
1469 ipHdr->protocol = ipAttr->ipv4_proto;
1471 if (ipHdr->ttl != ipAttr->ipv4_ttl) {
1472 UINT16 oldTtl = (ipHdr->ttl) & 0xff;
1473 UINT16 newTtl = (ipAttr->ipv4_ttl) & 0xff;
1474 if (ipHdr->check != 0) {
1475 ipHdr->check = ChecksumUpdate16(ipHdr->check, oldTtl, newTtl);
1477 ipHdr->ttl = ipAttr->ipv4_ttl;
1480 return NDIS_STATUS_SUCCESS;
1484 * --------------------------------------------------------------------------
1485 * OvsExecuteSetAction --
1486 * Executes a set() action, but storing the actions into 'ovsFwdCtx'
1487 * --------------------------------------------------------------------------
1489 static __inline NDIS_STATUS
1490 OvsExecuteSetAction(OvsForwardingContext *ovsFwdCtx,
1495 enum ovs_key_attr type = NlAttrType(a);
1496 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1499 case OVS_KEY_ATTR_ETHERNET:
1500 status = OvsUpdateEthHeader(ovsFwdCtx,
1501 NlAttrGetUnspec(a, sizeof(struct ovs_key_ethernet)));
1504 case OVS_KEY_ATTR_IPV4:
1505 status = OvsUpdateIPv4Header(ovsFwdCtx,
1506 NlAttrGetUnspec(a, sizeof(struct ovs_key_ipv4)));
1509 case OVS_KEY_ATTR_TUNNEL:
1511 OvsIPv4TunnelKey tunKey;
1512 status = OvsTunnelAttrToIPv4TunnelKey((PNL_ATTR)a, &tunKey);
1513 ASSERT(status == NDIS_STATUS_SUCCESS);
1514 tunKey.flow_hash = (uint16)(hash ? *hash : OvsHashFlow(key));
1515 tunKey.dst_port = key->ipKey.l4.tpDst;
1516 RtlCopyMemory(&ovsFwdCtx->tunKey, &tunKey, sizeof ovsFwdCtx->tunKey);
1521 OVS_LOG_INFO("Unhandled attribute %#x", type);
1528 * --------------------------------------------------------------------------
1529 * OvsExecuteRecirc --
1530 * The function adds a deferred action to allow the current packet, nbl,
1531 * to re-enter datapath packet processing.
1532 * --------------------------------------------------------------------------
1535 OvsExecuteRecirc(OvsForwardingContext *ovsFwdCtx,
1537 const PNL_ATTR actions,
1540 POVS_DEFERRED_ACTION deferredAction = NULL;
1541 PNET_BUFFER_LIST newNbl = NULL;
1543 if (!NlAttrIsLast(actions, rem)) {
1545 * Recirc action is the not the last action of the action list, so we
1546 * need to clone the packet.
1548 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1549 0, 0, TRUE /*copy NBL info*/);
1551 * Skip the recirc action when out of memory, but continue on with the
1552 * rest of the action list.
1554 if (newNbl == NULL) {
1555 ovsActionStats.noCopiedNbl++;
1556 return NDIS_STATUS_SUCCESS;
1558 ovsFwdCtx->curNbl = newNbl;
1561 deferredAction = OvsAddDeferredActions(ovsFwdCtx->curNbl, key, NULL);
1562 if (deferredAction) {
1563 deferredAction->key.recircId = NlAttrGetU32(actions);
1566 ovsActionStats.deferredActionsQueueFull++;
1567 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
1571 return NDIS_STATUS_SUCCESS;
1575 * --------------------------------------------------------------------------
1576 * OvsDoExecuteActions --
1577 * Interpret and execute the specified 'actions' on the specified packet
1578 * 'curNbl'. The expectation is that if the packet needs to be dropped
1579 * (completed) for some reason, it is added to 'completionList' so that the
1580 * caller can complete the packet. If 'completionList' is NULL, the NBL is
1581 * assumed to be generated by OVS and freed up. Otherwise, the function
1582 * consumes the NBL by generating a NDIS send indication for the packet.
1584 * There are one or more of "clone" NBLs that may get generated while
1585 * executing the actions. Upon any failures, the "cloned" NBLs are freed up,
1586 * and the caller does not have to worry about them.
1588 * Success or failure is returned based on whether the specified actions
1589 * were executed successfully on the packet or not.
1590 * --------------------------------------------------------------------------
1593 OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext,
1594 OvsCompletionList *completionList,
1595 PNET_BUFFER_LIST curNbl,
1600 OVS_PACKET_HDR_INFO *layers,
1601 const PNL_ATTR actions,
1607 OvsForwardingContext ovsFwdCtx;
1608 PCWSTR dropReason = L"";
1610 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail =
1611 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl);
1613 /* XXX: ASSERT that the flow table lock is held. */
1614 status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl, portNo,
1615 sendFlags, fwdDetail, completionList,
1617 if (status != NDIS_STATUS_SUCCESS) {
1618 dropReason = L"OVS-initing destination port list failed";
1622 if (actionsLen == 0) {
1623 dropReason = L"OVS-Dropped due to Flow action";
1624 ovsActionStats.zeroActionLen++;
1628 NL_ATTR_FOR_EACH_UNSAFE (a, rem, actions, actionsLen) {
1629 switch(NlAttrType(a)) {
1630 case OVS_ACTION_ATTR_OUTPUT:
1631 dstPortID = NlAttrGetU32(a);
1632 status = OvsAddPorts(&ovsFwdCtx, key, dstPortID,
1634 if (status != NDIS_STATUS_SUCCESS) {
1635 dropReason = L"OVS-adding destination port failed";
1640 case OVS_ACTION_ATTR_PUSH_VLAN:
1642 struct ovs_action_push_vlan *vlan;
1644 PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTag;
1646 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1647 || ovsFwdCtx.tunnelRxNic != NULL) {
1648 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1649 if (status != NDIS_STATUS_SUCCESS) {
1650 dropReason = L"OVS-adding destination failed";
1655 vlanTagValue = NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1656 Ieee8021QNetBufferListInfo);
1657 if (vlanTagValue != NULL) {
1659 * XXX: We don't support double VLAN tag offload. In such cases,
1660 * we need to insert the existing one into the packet buffer,
1661 * and add the new one as offload. This will take care of
1662 * guest tag-in-tag case as well as OVS rules that specify
1667 vlanTag = (PNDIS_NET_BUFFER_LIST_8021Q_INFO)(PVOID *)&vlanTagValue;
1668 vlan = (struct ovs_action_push_vlan *)NlAttrGet((const PNL_ATTR)a);
1669 vlanTag->TagHeader.VlanId = ntohs(vlan->vlan_tci) & 0xfff;
1670 vlanTag->TagHeader.UserPriority = ntohs(vlan->vlan_tci) >> 13;
1672 NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1673 Ieee8021QNetBufferListInfo) = vlanTagValue;
1678 case OVS_ACTION_ATTR_POP_VLAN:
1680 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1681 || ovsFwdCtx.tunnelRxNic != NULL) {
1682 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1683 if (status != NDIS_STATUS_SUCCESS) {
1684 dropReason = L"OVS-adding destination failed";
1689 if (NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1690 Ieee8021QNetBufferListInfo) != 0) {
1691 NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1692 Ieee8021QNetBufferListInfo) = 0;
1695 * The VLAN tag is inserted into the packet buffer. Pop the tag
1696 * by packet buffer modification.
1698 status = OvsPopVlanInPktBuf(&ovsFwdCtx);
1699 if (status != NDIS_STATUS_SUCCESS) {
1700 dropReason = L"OVS-pop vlan action failed";
1707 case OVS_ACTION_ATTR_PUSH_MPLS:
1709 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1710 || ovsFwdCtx.tunnelRxNic != NULL) {
1711 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1712 if (status != NDIS_STATUS_SUCCESS) {
1713 dropReason = L"OVS-adding destination failed";
1718 status = OvsActionMplsPush(&ovsFwdCtx,
1719 (struct ovs_action_push_mpls *)NlAttrGet
1720 ((const PNL_ATTR)a));
1721 if (status != NDIS_STATUS_SUCCESS) {
1722 dropReason = L"OVS-push MPLS action failed";
1725 layers->l3Offset += MPLS_HLEN;
1726 layers->l4Offset += MPLS_HLEN;
1730 case OVS_ACTION_ATTR_POP_MPLS:
1732 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1733 || ovsFwdCtx.tunnelRxNic != NULL) {
1734 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1735 if (status != NDIS_STATUS_SUCCESS) {
1736 dropReason = L"OVS-adding destination failed";
1741 status = OvsActionMplsPop(&ovsFwdCtx, NlAttrGetBe16(a));
1742 if (status != NDIS_STATUS_SUCCESS) {
1743 dropReason = L"OVS-pop MPLS action failed";
1746 layers->l3Offset -= MPLS_HLEN;
1747 layers->l4Offset -= MPLS_HLEN;
1751 case OVS_ACTION_ATTR_RECIRC:
1753 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1754 || ovsFwdCtx.tunnelRxNic != NULL) {
1755 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1756 if (status != NDIS_STATUS_SUCCESS) {
1757 dropReason = L"OVS-adding destination failed";
1762 status = OvsExecuteRecirc(&ovsFwdCtx, key, (const PNL_ATTR)a, rem);
1763 if (status != NDIS_STATUS_SUCCESS) {
1764 dropReason = L"OVS-recirculation action failed";
1768 if (NlAttrIsLast(a, rem)) {
1774 case OVS_ACTION_ATTR_USERSPACE:
1776 PNL_ATTR userdataAttr;
1778 POVS_PACKET_QUEUE_ELEM elem;
1779 BOOLEAN isRecv = FALSE;
1781 POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(switchContext,
1785 if (vport->isExternal ||
1786 OvsIsTunnelVportType(vport->ovsType)) {
1791 queueAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_PID);
1792 userdataAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_USERDATA);
1794 elem = OvsCreateQueueNlPacket((PVOID)userdataAttr,
1795 userdataAttr->nlaLen,
1796 OVS_PACKET_CMD_ACTION,
1797 vport, key, ovsFwdCtx.curNbl,
1798 NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx.curNbl),
1802 LIST_ENTRY missedPackets;
1803 InitializeListHead(&missedPackets);
1804 InsertTailList(&missedPackets, &elem->link);
1805 OvsQueuePackets(&missedPackets, 1);
1806 dropReason = L"OVS-Completed since packet was copied to "
1809 dropReason = L"OVS-Dropped due to failure to queue to "
1815 case OVS_ACTION_ATTR_SET:
1817 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1818 || ovsFwdCtx.tunnelRxNic != NULL) {
1819 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1820 if (status != NDIS_STATUS_SUCCESS) {
1821 dropReason = L"OVS-adding destination failed";
1826 status = OvsExecuteSetAction(&ovsFwdCtx, key, hash,
1827 (const PNL_ATTR)NlAttrGet
1828 ((const PNL_ATTR)a));
1829 if (status != NDIS_STATUS_SUCCESS) {
1830 dropReason = L"OVS-set action failed";
1835 case OVS_ACTION_ATTR_SAMPLE:
1837 status = NDIS_STATUS_NOT_SUPPORTED;
1842 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1843 || ovsFwdCtx.tunnelRxNic != NULL) {
1844 status = OvsOutputForwardingCtx(&ovsFwdCtx);
1845 ASSERT(ovsFwdCtx.curNbl == NULL);
1848 ASSERT(ovsFwdCtx.destPortsSizeOut == 0);
1849 ASSERT(ovsFwdCtx.tunnelRxNic == NULL);
1850 ASSERT(ovsFwdCtx.tunnelTxNic == NULL);
1854 * If curNbl != NULL, it implies the NBL has not been not freed up so far.
1856 if (ovsFwdCtx.curNbl) {
1857 OvsCompleteNBLForwardingCtx(&ovsFwdCtx, dropReason);
1865 * --------------------------------------------------------------------------
1866 * OvsActionsExecute --
1867 * The function interprets and executes the specified 'actions' on the
1868 * specified packet 'curNbl'. See 'OvsDoExecuteActions' description for
1871 * Also executes deferred actions added by recirculation or sample
1873 * --------------------------------------------------------------------------
1876 OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
1877 OvsCompletionList *completionList,
1878 PNET_BUFFER_LIST curNbl,
1883 OVS_PACKET_HDR_INFO *layers,
1884 const PNL_ATTR actions,
1887 NDIS_STATUS status = STATUS_SUCCESS;
1889 status = OvsDoExecuteActions(switchContext, completionList, curNbl,
1890 portNo, sendFlags, key, hash, layers,
1891 actions, actionsLen);
1893 if (status == STATUS_SUCCESS) {
1894 status = OvsProcessDeferredActions(switchContext, completionList,
1895 portNo, sendFlags, layers);
1902 * --------------------------------------------------------------------------
1904 * The function processes the packet 'curNbl' that re-entered datapath
1905 * packet processing after a recirculation action.
1906 * --------------------------------------------------------------------------
1909 OvsDoRecirc(POVS_SWITCH_CONTEXT switchContext,
1910 OvsCompletionList *completionList,
1911 PNET_BUFFER_LIST curNbl,
1914 OVS_PACKET_HDR_INFO *layers)
1916 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1917 OvsFlow *flow = NULL;
1918 OvsForwardingContext ovsFwdCtx = { 0 };
1922 OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl,
1924 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl),
1925 completionList, layers, TRUE);
1927 status = OvsExtractFlow(ovsFwdCtx.curNbl, ovsFwdCtx.srcVportNo, key,
1928 &ovsFwdCtx.layers, NULL);
1929 if (status != NDIS_STATUS_SUCCESS) {
1930 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
1931 L"OVS-Dropped due to extract flow failure");
1932 ovsActionStats.failedFlowMiss++;
1933 return NDIS_STATUS_FAILURE;
1936 flow = OvsLookupFlow(&ovsFwdCtx.switchContext->datapath, key, &hash, FALSE);
1938 UINT32 level = OvsDeferredActionsLevelGet();
1940 if (level > DEFERRED_ACTION_EXEC_LEVEL) {
1941 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
1942 L"OVS-Dropped due to deferred actions execution level limit \
1944 ovsActionStats.deferredActionsExecLimit++;
1945 ovsFwdCtx.curNbl = NULL;
1946 return NDIS_STATUS_FAILURE;
1949 OvsFlowUsed(flow, ovsFwdCtx.curNbl, &ovsFwdCtx.layers);
1950 ovsFwdCtx.switchContext->datapath.hits++;
1952 OvsDeferredActionsLevelInc();
1954 status = OvsDoExecuteActions(ovsFwdCtx.switchContext,
1955 ovsFwdCtx.completionList,
1957 ovsFwdCtx.srcVportNo,
1958 ovsFwdCtx.sendFlags,
1959 key, &hash, &ovsFwdCtx.layers,
1960 flow->actions, flow->actionsLen);
1961 ovsFwdCtx.curNbl = NULL;
1963 OvsDeferredActionsLevelDec();
1965 POVS_VPORT_ENTRY vport = NULL;
1966 LIST_ENTRY missedPackets;
1969 ovsFwdCtx.switchContext->datapath.misses++;
1970 InitializeListHead(&missedPackets);
1971 vport = OvsFindVportByPortNo(switchContext, srcPortNo);
1972 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
1973 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
1974 L"OVS-Dropped due to port removal");
1975 ovsActionStats.noVport++;
1976 return NDIS_STATUS_SUCCESS;
1978 status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS,
1979 vport, key, ovsFwdCtx.curNbl,
1981 switchContext->virtualExternalPortId,
1983 ovsFwdCtx.switchContext,
1984 &missedPackets, &num);
1986 OvsQueuePackets(&missedPackets, num);
1988 if (status == NDIS_STATUS_SUCCESS) {
1989 /* Complete the packet since it was copied to user buffer. */
1990 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
1991 L"OVS-Dropped since packet was copied to userspace");
1992 ovsActionStats.flowMiss++;
1994 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
1995 L"OVS-Dropped due to failure to queue to userspace");
1996 ovsActionStats.failedFlowMiss++;
1997 status = NDIS_STATUS_FAILURE;