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.
20 #include "Conntrack.h"
40 #define OVS_DBG_MOD OVS_DBG_ACTION
42 #define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2
44 typedef struct _OVS_ACTION_STATS
{
54 UINT32 failedFlowMiss
;
56 UINT32 failedFlowExtract
;
61 UINT32 cannotGrowDest
;
63 UINT32 failedChecksum
;
64 UINT32 deferredActionsQueueFull
;
65 UINT32 deferredActionsExecLimit
;
66 } OVS_ACTION_STATS
, *POVS_ACTION_STATS
;
68 OVS_ACTION_STATS ovsActionStats
;
71 * There a lot of data that needs to be maintained while executing the pipeline
72 * as dictated by the actions of a flow, across different functions at different
73 * levels. Such data is put together in a 'context' structure. Care should be
74 * exercised while adding new members to the structure - only add ones that get
75 * used across multiple stages in the pipeline/get used in multiple functions.
77 typedef struct OvsForwardingContext
{
78 POVS_SWITCH_CONTEXT switchContext
;
79 /* The NBL currently used in the pipeline. */
80 PNET_BUFFER_LIST curNbl
;
81 /* NDIS forwarding detail for 'curNbl'. */
82 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail
;
83 /* Array of destination ports for 'curNbl'. */
84 PNDIS_SWITCH_FORWARDING_DESTINATION_ARRAY destinationPorts
;
85 /* send flags while sending 'curNbl' into NDIS. */
87 /* Total number of output ports, used + unused, in 'curNbl'. */
88 UINT32 destPortsSizeIn
;
89 /* Total number of used output ports in 'curNbl'. */
90 UINT32 destPortsSizeOut
;
92 * If 'curNbl' is not owned by OVS, they need to be tracked, if they need to
95 OvsCompletionList
*completionList
;
97 * vport number of 'curNbl' when it is passed from the PIF bridge to the INT
98 * bridge. ie. during tunneling on the Rx side.
104 * - specified in actions during tunneling Tx
105 * - extracted from an NBL during tunneling Rx
107 OvsIPv4TunnelKey tunKey
;
111 * To store the output port, when it is a tunneled port. We don't foresee
112 * multiple tunneled ports as outport for any given NBL.
114 POVS_VPORT_ENTRY tunnelTxNic
;
118 * Points to the Internal port on the PIF Bridge, if the packet needs to be
121 POVS_VPORT_ENTRY tunnelRxNic
;
123 /* header information */
124 OVS_PACKET_HDR_INFO layers
;
125 } OvsForwardingContext
;
128 * --------------------------------------------------------------------------
129 * OvsInitForwardingCtx --
130 * Function to init/re-init the 'ovsFwdCtx' context as the actions pipeline
134 * NDIS_STATUS_SUCCESS on success
135 * Other NDIS_STATUS upon failure. Upon failure, it is safe to call
136 * OvsCompleteNBLForwardingCtx(), since 'ovsFwdCtx' has been initialized
137 * enough for OvsCompleteNBLForwardingCtx() to do its work.
138 * --------------------------------------------------------------------------
140 static __inline NDIS_STATUS
141 OvsInitForwardingCtx(OvsForwardingContext
*ovsFwdCtx
,
142 POVS_SWITCH_CONTEXT switchContext
,
143 PNET_BUFFER_LIST curNbl
,
146 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail
,
147 OvsCompletionList
*completionList
,
148 OVS_PACKET_HDR_INFO
*layers
,
149 BOOLEAN resetTunnelInfo
)
152 ASSERT(switchContext
);
157 * Set values for curNbl and switchContext so upon failures, we have enough
158 * information to do cleanup.
160 ovsFwdCtx
->curNbl
= curNbl
;
161 ovsFwdCtx
->switchContext
= switchContext
;
162 ovsFwdCtx
->completionList
= completionList
;
163 ovsFwdCtx
->fwdDetail
= fwdDetail
;
165 if (fwdDetail
->NumAvailableDestinations
> 0) {
167 * XXX: even though MSDN says GetNetBufferListDestinations() returns
168 * NDIS_STATUS, the header files say otherwise.
170 switchContext
->NdisSwitchHandlers
.GetNetBufferListDestinations(
171 switchContext
->NdisSwitchContext
, curNbl
,
172 &ovsFwdCtx
->destinationPorts
);
174 ASSERT(ovsFwdCtx
->destinationPorts
);
175 /* Ensure that none of the elements are consumed yet. */
176 ASSERT(ovsFwdCtx
->destinationPorts
->NumElements
==
177 fwdDetail
->NumAvailableDestinations
);
179 ovsFwdCtx
->destinationPorts
= NULL
;
181 ovsFwdCtx
->destPortsSizeIn
= fwdDetail
->NumAvailableDestinations
;
182 ovsFwdCtx
->destPortsSizeOut
= 0;
183 ovsFwdCtx
->srcVportNo
= srcVportNo
;
184 ovsFwdCtx
->sendFlags
= sendFlags
;
186 ovsFwdCtx
->layers
= *layers
;
188 RtlZeroMemory(&ovsFwdCtx
->layers
, sizeof ovsFwdCtx
->layers
);
190 if (resetTunnelInfo
) {
191 ovsFwdCtx
->tunnelTxNic
= NULL
;
192 ovsFwdCtx
->tunnelRxNic
= NULL
;
193 RtlZeroMemory(&ovsFwdCtx
->tunKey
, sizeof ovsFwdCtx
->tunKey
);
196 return NDIS_STATUS_SUCCESS
;
200 * --------------------------------------------------------------------------
201 * OvsDetectTunnelRxPkt --
202 * Utility function for an RX packet to detect its tunnel type.
205 * True - if the tunnel type was detected.
206 * False - if not a tunnel packet or tunnel type not supported.
207 * --------------------------------------------------------------------------
209 static __inline BOOLEAN
210 OvsDetectTunnelRxPkt(OvsForwardingContext
*ovsFwdCtx
,
211 const OvsFlowKey
*flowKey
)
213 POVS_VPORT_ENTRY tunnelVport
= NULL
;
215 /* XXX: we should also check for the length of the UDP payload to pick
216 * packets only if they are at least VXLAN header size.
218 if (!flowKey
->ipKey
.nwFrag
) {
219 UINT16 dstPort
= htons(flowKey
->ipKey
.l4
.tpDst
);
220 switch (flowKey
->ipKey
.nwProto
) {
222 tunnelVport
= OvsFindTunnelVportByPortType(ovsFwdCtx
->switchContext
,
225 ovsActionStats
.rxGre
++;
229 tunnelVport
= OvsFindTunnelVportByDstPort(ovsFwdCtx
->switchContext
,
233 ovsActionStats
.rxStt
++;
237 tunnelVport
= OvsFindTunnelVportByDstPort(ovsFwdCtx
->switchContext
,
239 OVS_VPORT_TYPE_VXLAN
);
241 ovsActionStats
.rxVxlan
++;
247 // We might get tunnel packets even before the tunnel gets initialized.
249 ASSERT(ovsFwdCtx
->tunnelRxNic
== NULL
);
250 ovsFwdCtx
->tunnelRxNic
= tunnelVport
;
258 * --------------------------------------------------------------------------
259 * OvsDetectTunnelPkt --
260 * Utility function to detect if a packet is to be subjected to
261 * tunneling (Tx) or de-tunneling (Rx). Various factors such as source
262 * port, destination port, packet contents, and previously setup tunnel
266 * True - If the packet is to be subjected to tunneling.
267 * In case of invalid tunnel context, the tunneling functionality is
268 * a no-op and is completed within this function itself by consuming
269 * all of the tunneling context.
270 * False - If not a tunnel packet or tunnel type not supported. Caller should
271 * process the packet as a non-tunnel packet.
272 * --------------------------------------------------------------------------
274 static __inline BOOLEAN
275 OvsDetectTunnelPkt(OvsForwardingContext
*ovsFwdCtx
,
276 const POVS_VPORT_ENTRY dstVport
,
277 const OvsFlowKey
*flowKey
)
279 if (OvsIsInternalVportType(dstVport
->ovsType
)) {
282 * The source of NBL during tunneling Rx could be the external
283 * port or if it is being executed from userspace, the source port is
286 BOOLEAN validSrcPort
=
287 (ovsFwdCtx
->fwdDetail
->SourcePortId
==
288 ovsFwdCtx
->switchContext
->virtualExternalPortId
) ||
289 (ovsFwdCtx
->fwdDetail
->SourcePortId
==
290 NDIS_SWITCH_DEFAULT_PORT_ID
);
292 if (validSrcPort
&& OvsDetectTunnelRxPkt(ovsFwdCtx
, flowKey
)) {
293 ASSERT(ovsFwdCtx
->tunnelTxNic
== NULL
);
294 ASSERT(ovsFwdCtx
->tunnelRxNic
!= NULL
);
297 } else if (OvsIsTunnelVportType(dstVport
->ovsType
)) {
298 ASSERT(ovsFwdCtx
->tunnelTxNic
== NULL
);
299 ASSERT(ovsFwdCtx
->tunnelRxNic
== NULL
);
303 * The destination port is a tunnel port. Encapsulation must be
304 * performed only on packets that originate from:
306 * - a bridge-internal port (packets generated from userspace)
309 * If the packet will not be encapsulated, consume the tunnel context
312 if (ovsFwdCtx
->srcVportNo
!= OVS_DPPORT_NUMBER_INVALID
) {
314 POVS_VPORT_ENTRY vport
= OvsFindVportByPortNo(
315 ovsFwdCtx
->switchContext
, ovsFwdCtx
->srcVportNo
);
318 (vport
->ovsType
!= OVS_VPORT_TYPE_NETDEV
&&
319 !OvsIsBridgeInternalVport(vport
))) {
320 ovsFwdCtx
->tunKey
.dst
= 0;
324 /* Tunnel the packet only if tunnel context is set. */
325 if (ovsFwdCtx
->tunKey
.dst
!= 0) {
326 switch(dstVport
->ovsType
) {
327 case OVS_VPORT_TYPE_GRE
:
328 ovsActionStats
.txGre
++;
330 case OVS_VPORT_TYPE_VXLAN
:
331 ovsActionStats
.txVxlan
++;
333 case OVS_VPORT_TYPE_STT
:
334 ovsActionStats
.txStt
++;
337 ovsFwdCtx
->tunnelTxNic
= dstVport
;
348 * --------------------------------------------------------------------------
350 * Add the specified destination vport into the forwarding context. If the
351 * vport is a VIF/external port, it is added directly to the NBL. If it is
352 * a tunneling port, it is NOT added to the NBL.
355 * NDIS_STATUS_SUCCESS on success
356 * Other NDIS_STATUS upon failure.
357 * --------------------------------------------------------------------------
359 static __inline NDIS_STATUS
360 OvsAddPorts(OvsForwardingContext
*ovsFwdCtx
,
362 NDIS_SWITCH_PORT_ID dstPortId
,
363 BOOLEAN preserveVLAN
,
364 BOOLEAN preservePriority
)
366 POVS_VPORT_ENTRY vport
;
367 PNDIS_SWITCH_PORT_DESTINATION fwdPort
;
369 POVS_SWITCH_CONTEXT switchContext
= ovsFwdCtx
->switchContext
;
372 * We hold the dispatch lock that protects the list of vports, so vports
373 * validated here can be added as destinations safely before we call into
376 * Some of the vports can be tunnelled ports as well in which case
377 * they should be added to a separate list of tunnelled destination ports
378 * instead of the VIF ports. The context for the tunnel is settable
379 * in OvsForwardingContext.
381 vport
= OvsFindVportByPortNo(ovsFwdCtx
->switchContext
, dstPortId
);
382 if (vport
== NULL
|| vport
->ovsState
!= OVS_STATE_CONNECTED
) {
384 * There may be some latency between a port disappearing, and userspace
385 * updating the recalculated flows. In the meantime, handle invalid
388 ovsActionStats
.noVport
++;
389 return NDIS_STATUS_SUCCESS
;
391 ASSERT(vport
->nicState
== NdisSwitchNicStateConnected
);
392 vport
->stats
.txPackets
++;
393 vport
->stats
.txBytes
+=
394 NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
));
396 if (OvsIsBridgeInternalVport(vport
)) {
397 return NDIS_STATUS_SUCCESS
;
400 if (OvsDetectTunnelPkt(ovsFwdCtx
, vport
, flowKey
)) {
401 return NDIS_STATUS_SUCCESS
;
404 if (ovsFwdCtx
->destPortsSizeOut
== ovsFwdCtx
->destPortsSizeIn
) {
405 if (ovsFwdCtx
->destPortsSizeIn
== 0) {
406 ASSERT(ovsFwdCtx
->destinationPorts
== NULL
);
407 ASSERT(ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
== 0);
409 switchContext
->NdisSwitchHandlers
.GrowNetBufferListDestinations(
410 switchContext
->NdisSwitchContext
, ovsFwdCtx
->curNbl
,
411 OVS_DEST_PORTS_ARRAY_MIN_SIZE
,
412 &ovsFwdCtx
->destinationPorts
);
413 if (status
!= NDIS_STATUS_SUCCESS
) {
414 ovsActionStats
.cannotGrowDest
++;
417 ovsFwdCtx
->destPortsSizeIn
=
418 ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
;
419 ASSERT(ovsFwdCtx
->destinationPorts
);
421 ASSERT(ovsFwdCtx
->destinationPorts
!= NULL
);
424 * A ULONG value that specifies the total number of
425 * NDIS_SWITCH_PORT_DESTINATION elements in the
426 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure.
429 * A ULONG value that specifies the number of
430 * NDIS_SWITCH_PORT_DESTINATION elements in the
431 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure that
432 * specify port destinations.
434 * NumAvailableDestinations:
435 * A value that specifies the number of unused extensible switch
436 * destination ports elements within an NET_BUFFER_LIST structure.
438 ASSERT(ovsFwdCtx
->destinationPorts
->NumElements
==
439 ovsFwdCtx
->destPortsSizeIn
);
440 ASSERT(ovsFwdCtx
->destinationPorts
->NumDestinations
==
441 ovsFwdCtx
->destPortsSizeOut
-
442 ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
);
443 ASSERT(ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
> 0);
445 * Before we grow the array of destination ports, the current set
446 * of ports needs to be committed. Only the ports added since the
447 * last commit need to be part of the new update.
449 status
= switchContext
->NdisSwitchHandlers
.UpdateNetBufferListDestinations(
450 switchContext
->NdisSwitchContext
, ovsFwdCtx
->curNbl
,
451 ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
,
452 ovsFwdCtx
->destinationPorts
);
453 if (status
!= NDIS_STATUS_SUCCESS
) {
454 ovsActionStats
.cannotGrowDest
++;
457 ASSERT(ovsFwdCtx
->destinationPorts
->NumElements
==
458 ovsFwdCtx
->destPortsSizeIn
);
459 ASSERT(ovsFwdCtx
->destinationPorts
->NumDestinations
==
460 ovsFwdCtx
->destPortsSizeOut
);
461 ASSERT(ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
== 0);
463 status
= switchContext
->NdisSwitchHandlers
.GrowNetBufferListDestinations(
464 switchContext
->NdisSwitchContext
, ovsFwdCtx
->curNbl
,
465 ovsFwdCtx
->destPortsSizeIn
, &ovsFwdCtx
->destinationPorts
);
466 if (status
!= NDIS_STATUS_SUCCESS
) {
467 ovsActionStats
.cannotGrowDest
++;
470 ASSERT(ovsFwdCtx
->destinationPorts
!= NULL
);
471 ovsFwdCtx
->destPortsSizeIn
<<= 1;
475 ASSERT(ovsFwdCtx
->destPortsSizeOut
< ovsFwdCtx
->destPortsSizeIn
);
477 NDIS_SWITCH_PORT_DESTINATION_AT_ARRAY_INDEX(ovsFwdCtx
->destinationPorts
,
478 ovsFwdCtx
->destPortsSizeOut
);
480 fwdPort
->PortId
= vport
->portId
;
481 fwdPort
->NicIndex
= vport
->nicIndex
;
482 fwdPort
->IsExcluded
= 0;
483 fwdPort
->PreserveVLAN
= preserveVLAN
;
484 fwdPort
->PreservePriority
= preservePriority
;
485 ovsFwdCtx
->destPortsSizeOut
+= 1;
487 return NDIS_STATUS_SUCCESS
;
492 * --------------------------------------------------------------------------
493 * OvsClearTunTxCtx --
494 * Utility function to clear tx tunneling context.
495 * --------------------------------------------------------------------------
498 OvsClearTunTxCtx(OvsForwardingContext
*ovsFwdCtx
)
500 ovsFwdCtx
->tunnelTxNic
= NULL
;
501 ovsFwdCtx
->tunKey
.dst
= 0;
506 * --------------------------------------------------------------------------
507 * OvsClearTunRxCtx --
508 * Utility function to clear rx tunneling context.
509 * --------------------------------------------------------------------------
512 OvsClearTunRxCtx(OvsForwardingContext
*ovsFwdCtx
)
514 ovsFwdCtx
->tunnelRxNic
= NULL
;
515 ovsFwdCtx
->tunKey
.dst
= 0;
520 * --------------------------------------------------------------------------
521 * OvsCompleteNBLForwardingCtx --
522 * This utility function is responsible for freeing/completing an NBL - either
523 * by adding it to a completion list or by freeing it.
526 * It also resets the necessary fields in 'ovsFwdCtx'.
527 * --------------------------------------------------------------------------
530 OvsCompleteNBLForwardingCtx(OvsForwardingContext
*ovsFwdCtx
,
533 NDIS_STRING filterReason
;
535 RtlInitUnicodeString(&filterReason
, dropReason
);
536 if (ovsFwdCtx
->completionList
) {
537 OvsAddPktCompletionList(ovsFwdCtx
->completionList
, TRUE
,
538 ovsFwdCtx
->fwdDetail
->SourcePortId
, ovsFwdCtx
->curNbl
, 1,
540 ovsFwdCtx
->curNbl
= NULL
;
542 /* If there is no completionList, we assume this is ovs created NBL */
543 ovsFwdCtx
->curNbl
= OvsCompleteNBL(ovsFwdCtx
->switchContext
,
544 ovsFwdCtx
->curNbl
, TRUE
);
545 ASSERT(ovsFwdCtx
->curNbl
== NULL
);
547 /* XXX: these can be made debug only to save cycles. Ideally the pipeline
548 * using these fields should reset the values at the end of the pipeline. */
549 ovsFwdCtx
->destPortsSizeOut
= 0;
550 ovsFwdCtx
->tunnelTxNic
= NULL
;
551 ovsFwdCtx
->tunnelRxNic
= NULL
;
555 * --------------------------------------------------------------------------
556 * OvsDoFlowLookupOutput --
557 * Function to be used for the second stage of a tunneling workflow, ie.:
558 * - On the encapsulated packet on Tx path, to do a flow extract, flow
559 * lookup and excuting the actions.
560 * - On the decapsulated packet on Rx path, to do a flow extract, flow
561 * lookup and excuting the actions.
563 * XXX: It is assumed that the NBL in 'ovsFwdCtx' is owned by OVS. This is
564 * until the new buffer management framework is adopted.
567 * The NBL in 'ovsFwdCtx' is consumed.
568 * --------------------------------------------------------------------------
570 static __inline NDIS_STATUS
571 OvsDoFlowLookupOutput(OvsForwardingContext
*ovsFwdCtx
)
573 OvsFlowKey key
= { 0 };
574 OvsFlow
*flow
= NULL
;
576 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
577 POVS_VPORT_ENTRY vport
=
578 OvsFindVportByPortNo(ovsFwdCtx
->switchContext
, ovsFwdCtx
->srcVportNo
);
579 if (vport
== NULL
|| vport
->ovsState
!= OVS_STATE_CONNECTED
) {
580 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
581 L
"OVS-Dropped due to internal/tunnel port removal");
582 ovsActionStats
.noVport
++;
583 return NDIS_STATUS_SUCCESS
;
585 ASSERT(vport
->nicState
== NdisSwitchNicStateConnected
);
587 /* Assert that in the Rx direction, key is always setup. */
588 ASSERT(ovsFwdCtx
->tunnelRxNic
== NULL
|| ovsFwdCtx
->tunKey
.dst
!= 0);
590 OvsExtractFlow(ovsFwdCtx
->curNbl
, ovsFwdCtx
->srcVportNo
,
591 &key
, &ovsFwdCtx
->layers
,
592 ovsFwdCtx
->tunKey
.dst
!= 0 ? &ovsFwdCtx
->tunKey
: NULL
);
593 if (status
!= NDIS_STATUS_SUCCESS
) {
594 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
595 L
"OVS-Flow extract failed");
596 ovsActionStats
.failedFlowExtract
++;
600 flow
= OvsLookupFlow(&ovsFwdCtx
->switchContext
->datapath
, &key
, &hash
, FALSE
);
602 OvsFlowUsed(flow
, ovsFwdCtx
->curNbl
, &ovsFwdCtx
->layers
);
603 ovsFwdCtx
->switchContext
->datapath
.hits
++;
604 status
= OvsDoExecuteActions(ovsFwdCtx
->switchContext
,
605 ovsFwdCtx
->completionList
,
607 ovsFwdCtx
->srcVportNo
,
608 ovsFwdCtx
->sendFlags
,
609 &key
, &hash
, &ovsFwdCtx
->layers
,
610 flow
->actions
, flow
->actionsLen
);
611 ovsFwdCtx
->curNbl
= NULL
;
613 LIST_ENTRY missedPackets
;
615 ovsFwdCtx
->switchContext
->datapath
.misses
++;
616 InitializeListHead(&missedPackets
);
617 status
= OvsCreateAndAddPackets(NULL
, 0, OVS_PACKET_CMD_MISS
, vport
,
618 &key
,ovsFwdCtx
->curNbl
,
619 FALSE
, &ovsFwdCtx
->layers
,
620 ovsFwdCtx
->switchContext
, &missedPackets
, &num
);
622 OvsQueuePackets(&missedPackets
, num
);
624 if (status
== NDIS_STATUS_SUCCESS
) {
625 /* Complete the packet since it was copied to user buffer. */
626 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
627 L
"OVS-Dropped since packet was copied to userspace");
628 ovsActionStats
.flowMiss
++;
629 status
= NDIS_STATUS_SUCCESS
;
631 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
632 L
"OVS-Dropped due to failure to queue to userspace");
633 status
= NDIS_STATUS_FAILURE
;
634 ovsActionStats
.failedFlowMiss
++;
642 * --------------------------------------------------------------------------
644 * The start function for Tx tunneling - encapsulates the packet, and
645 * outputs the packet on the PIF bridge.
648 * The NBL in 'ovsFwdCtx' is consumed.
649 * --------------------------------------------------------------------------
651 static __inline NDIS_STATUS
652 OvsTunnelPortTx(OvsForwardingContext
*ovsFwdCtx
)
654 NDIS_STATUS status
= NDIS_STATUS_FAILURE
;
655 PNET_BUFFER_LIST newNbl
= NULL
;
658 * Setup the source port to be the internal port to as to facilitate the
659 * second OvsLookupFlow.
661 if (ovsFwdCtx
->switchContext
->internalVport
== NULL
||
662 ovsFwdCtx
->switchContext
->virtualExternalVport
== NULL
) {
663 OvsClearTunTxCtx(ovsFwdCtx
);
664 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
665 L
"OVS-Dropped since either internal or external port is absent");
666 return NDIS_STATUS_FAILURE
;
668 ovsFwdCtx
->srcVportNo
=
669 ((POVS_VPORT_ENTRY
)ovsFwdCtx
->switchContext
->internalVport
)->portNo
;
671 ovsFwdCtx
->fwdDetail
->SourcePortId
= ovsFwdCtx
->switchContext
->internalPortId
;
672 ovsFwdCtx
->fwdDetail
->SourceNicIndex
=
673 ((POVS_VPORT_ENTRY
)ovsFwdCtx
->switchContext
->internalVport
)->nicIndex
;
675 /* Do the encap. Encap function does not consume the NBL. */
676 switch(ovsFwdCtx
->tunnelTxNic
->ovsType
) {
677 case OVS_VPORT_TYPE_GRE
:
678 status
= OvsEncapGre(ovsFwdCtx
->tunnelTxNic
, ovsFwdCtx
->curNbl
,
679 &ovsFwdCtx
->tunKey
, ovsFwdCtx
->switchContext
,
680 &ovsFwdCtx
->layers
, &newNbl
);
682 case OVS_VPORT_TYPE_VXLAN
:
683 status
= OvsEncapVxlan(ovsFwdCtx
->tunnelTxNic
, ovsFwdCtx
->curNbl
,
684 &ovsFwdCtx
->tunKey
, ovsFwdCtx
->switchContext
,
685 &ovsFwdCtx
->layers
, &newNbl
);
687 case OVS_VPORT_TYPE_STT
:
688 status
= OvsEncapStt(ovsFwdCtx
->tunnelTxNic
, ovsFwdCtx
->curNbl
,
689 &ovsFwdCtx
->tunKey
, ovsFwdCtx
->switchContext
,
690 &ovsFwdCtx
->layers
, &newNbl
);
693 ASSERT(! "Tx: Unhandled tunnel type");
696 /* Reset the tunnel context so that it doesn't get used after this point. */
697 OvsClearTunTxCtx(ovsFwdCtx
);
699 if (status
== NDIS_STATUS_SUCCESS
) {
701 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
702 L
"Complete after cloning NBL for encapsulation");
703 ovsFwdCtx
->curNbl
= newNbl
;
704 status
= OvsDoFlowLookupOutput(ovsFwdCtx
);
705 ASSERT(ovsFwdCtx
->curNbl
== NULL
);
708 * XXX: Temporary freeing of the packet until we register a
709 * callback to IP helper.
711 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
712 L
"OVS-Dropped due to encap failure");
713 ovsActionStats
.failedEncap
++;
714 status
= NDIS_STATUS_SUCCESS
;
721 * --------------------------------------------------------------------------
723 * Decapsulate the incoming NBL based on the tunnel type and goes through
724 * the flow lookup for the inner packet.
726 * Note: IP checksum is validate here, but L4 checksum validation needs
727 * to be done by the corresponding tunnel types.
730 * The NBL in 'ovsFwdCtx' is consumed.
731 * --------------------------------------------------------------------------
733 static __inline NDIS_STATUS
734 OvsTunnelPortRx(OvsForwardingContext
*ovsFwdCtx
)
736 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
737 PNET_BUFFER_LIST newNbl
= NULL
;
738 POVS_VPORT_ENTRY tunnelRxVport
= ovsFwdCtx
->tunnelRxNic
;
739 PCWSTR dropReason
= L
"OVS-dropped due to new decap packet";
741 if (OvsValidateIPChecksum(ovsFwdCtx
->curNbl
, &ovsFwdCtx
->layers
)
742 != NDIS_STATUS_SUCCESS
) {
743 ovsActionStats
.failedChecksum
++;
744 OVS_LOG_INFO("Packet dropped due to IP checksum failure.");
749 * Decap port functions should return a new NBL if it was copied, and
750 * this new NBL should be setup as the ovsFwdCtx->curNbl.
753 switch(tunnelRxVport
->ovsType
) {
754 case OVS_VPORT_TYPE_GRE
:
755 status
= OvsDecapGre(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
756 &ovsFwdCtx
->tunKey
, &newNbl
);
758 case OVS_VPORT_TYPE_VXLAN
:
759 status
= OvsDecapVxlan(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
760 &ovsFwdCtx
->tunKey
, &newNbl
);
762 case OVS_VPORT_TYPE_STT
:
763 status
= OvsDecapStt(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
764 &ovsFwdCtx
->tunKey
, &newNbl
);
765 if (status
== NDIS_STATUS_SUCCESS
&& newNbl
== NULL
) {
766 /* This was an STT-LSO Fragment */
767 dropReason
= L
"OVS-STT segment is cached";
771 OVS_LOG_ERROR("Rx: Unhandled tunnel type: %d\n",
772 tunnelRxVport
->ovsType
);
773 ASSERT(! "Rx: Unhandled tunnel type");
774 status
= NDIS_STATUS_NOT_SUPPORTED
;
777 if (status
!= NDIS_STATUS_SUCCESS
) {
778 ovsActionStats
.failedDecap
++;
783 * tunnelRxNic and other fields will be cleared, re-init the context
786 OvsCompleteNBLForwardingCtx(ovsFwdCtx
, dropReason
);
789 /* Decapsulated packet is in a new NBL */
790 ovsFwdCtx
->tunnelRxNic
= tunnelRxVport
;
791 OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
,
792 newNbl
, tunnelRxVport
->portNo
, 0,
793 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
794 ovsFwdCtx
->completionList
,
795 &ovsFwdCtx
->layers
, FALSE
);
798 * Set the NBL's SourcePortId and SourceNicIndex to default values to
799 * keep NDIS happy when we forward the packet.
801 ovsFwdCtx
->fwdDetail
->SourcePortId
= NDIS_SWITCH_DEFAULT_PORT_ID
;
802 ovsFwdCtx
->fwdDetail
->SourceNicIndex
= 0;
804 status
= OvsDoFlowLookupOutput(ovsFwdCtx
);
806 ASSERT(ovsFwdCtx
->curNbl
== NULL
);
807 OvsClearTunRxCtx(ovsFwdCtx
);
812 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
813 L
"OVS-dropped due to decap failure");
814 OvsClearTunRxCtx(ovsFwdCtx
);
820 * --------------------------------------------------------------------------
821 * OvsOutputForwardingCtx --
822 * This function outputs an NBL to NDIS or to a tunneling pipeline based on
823 * the ports added so far into 'ovsFwdCtx'.
826 * This function consumes the NBL - either by forwarding it successfully to
827 * NDIS, or adding it to the completion list in 'ovsFwdCtx', or freeing it.
829 * Also makes sure that the list of destination ports - tunnel or otherwise is
831 * --------------------------------------------------------------------------
833 static __inline NDIS_STATUS
834 OvsOutputForwardingCtx(OvsForwardingContext
*ovsFwdCtx
)
836 NDIS_STATUS status
= STATUS_SUCCESS
;
837 POVS_SWITCH_CONTEXT switchContext
= ovsFwdCtx
->switchContext
;
841 * Handle the case where the some of the destination ports are tunneled
842 * ports - the non-tunneled ports get a unmodified copy of the NBL, and the
843 * tunneling pipeline starts when we output the packet to tunneled port.
845 if (ovsFwdCtx
->destPortsSizeOut
> 0) {
846 PNET_BUFFER_LIST newNbl
= NULL
;
848 UINT32 portsToUpdate
=
849 ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
-
850 (ovsFwdCtx
->destPortsSizeIn
- ovsFwdCtx
->destPortsSizeOut
);
852 ASSERT(ovsFwdCtx
->destinationPorts
!= NULL
);
855 * Create a copy of the packet in order to do encap on it later. Also,
856 * don't copy the offload context since the encap'd packet has a
857 * different set of headers. This will change when we implement offloads
858 * before doing encapsulation.
860 if (ovsFwdCtx
->tunnelTxNic
!= NULL
|| ovsFwdCtx
->tunnelRxNic
!= NULL
) {
861 nb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
862 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
863 0, 0, TRUE
/*copy NBL info*/);
864 if (newNbl
== NULL
) {
865 status
= NDIS_STATUS_RESOURCES
;
866 ovsActionStats
.noCopiedNbl
++;
867 dropReason
= L
"Dropped due to failure to create NBL copy.";
872 /* It does not seem like we'll get here unless 'portsToUpdate' > 0. */
873 ASSERT(portsToUpdate
> 0);
874 status
= switchContext
->NdisSwitchHandlers
.UpdateNetBufferListDestinations(
875 switchContext
->NdisSwitchContext
, ovsFwdCtx
->curNbl
,
876 portsToUpdate
, ovsFwdCtx
->destinationPorts
);
877 if (status
!= NDIS_STATUS_SUCCESS
) {
878 OvsCompleteNBL(ovsFwdCtx
->switchContext
, newNbl
, TRUE
);
879 ovsActionStats
.cannotGrowDest
++;
880 dropReason
= L
"Dropped due to failure to update destinations.";
884 OvsSendNBLIngress(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
885 ovsFwdCtx
->sendFlags
);
886 /* End this pipeline by resetting the corresponding context. */
887 ovsFwdCtx
->destPortsSizeOut
= 0;
888 ovsFwdCtx
->curNbl
= NULL
;
890 status
= OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
,
891 newNbl
, ovsFwdCtx
->srcVportNo
, 0,
892 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
893 ovsFwdCtx
->completionList
,
894 &ovsFwdCtx
->layers
, FALSE
);
895 if (status
!= NDIS_STATUS_SUCCESS
) {
896 dropReason
= L
"Dropped due to resouces.";
902 if (ovsFwdCtx
->tunnelTxNic
!= NULL
) {
903 status
= OvsTunnelPortTx(ovsFwdCtx
);
904 ASSERT(ovsFwdCtx
->tunnelTxNic
== NULL
);
905 ASSERT(ovsFwdCtx
->tunKey
.dst
== 0);
906 } else if (ovsFwdCtx
->tunnelRxNic
!= NULL
) {
907 status
= OvsTunnelPortRx(ovsFwdCtx
);
908 ASSERT(ovsFwdCtx
->tunnelRxNic
== NULL
);
909 ASSERT(ovsFwdCtx
->tunKey
.dst
== 0);
911 ASSERT(ovsFwdCtx
->curNbl
== NULL
);
916 if (status
!= NDIS_STATUS_SUCCESS
) {
917 OvsCompleteNBLForwardingCtx(ovsFwdCtx
, dropReason
);
925 * --------------------------------------------------------------------------
926 * OvsLookupFlowOutput --
927 * Utility function for external callers to do flow extract, lookup,
928 * actions execute on a given NBL.
930 * Note: If this is being used from a callback function, make sure that the
931 * arguments specified are still valid in the asynchronous context.
934 * This function consumes the NBL.
935 * --------------------------------------------------------------------------
938 OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext
,
940 PNET_BUFFER_LIST curNbl
)
943 OvsForwardingContext ovsFwdCtx
;
944 POVS_VPORT_ENTRY internalVport
=
945 (POVS_VPORT_ENTRY
)switchContext
->internalVport
;
947 /* XXX: make sure comp list was not a stack variable previously. */
948 OvsCompletionList
*completionList
= (OvsCompletionList
*)compList
;
951 * XXX: can internal port disappear while we are busy doing ARP resolution?
952 * It could, but will we get this callback from IP helper in that case. Need
955 ASSERT(switchContext
->internalVport
);
956 status
= OvsInitForwardingCtx(&ovsFwdCtx
, switchContext
, curNbl
,
957 internalVport
->portNo
, 0,
958 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl
),
959 completionList
, NULL
, TRUE
);
960 if (status
!= NDIS_STATUS_SUCCESS
) {
961 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
,
962 L
"OVS-Dropped due to resources");
968 * XXX: We need to acquire the dispatch lock and the datapath lock.
971 OvsDoFlowLookupOutput(&ovsFwdCtx
);
976 * --------------------------------------------------------------------------
977 * OvsOutputBeforeSetAction --
978 * Function to be called to complete one set of actions on an NBL, before
979 * we start the next one.
980 * --------------------------------------------------------------------------
982 static __inline NDIS_STATUS
983 OvsOutputBeforeSetAction(OvsForwardingContext
*ovsFwdCtx
)
985 PNET_BUFFER_LIST newNbl
;
989 * Create a copy and work on the copy after this point. The original NBL is
990 * forwarded. One reason to not use the copy for forwarding is that
991 * ports have already been added to the original NBL, and it might be
992 * inefficient/impossible to remove/re-add them to the copy. There's no
993 * notion of removing the ports, the ports need to be marked as
994 * "isExcluded". There's seems no real advantage to retaining the original
995 * and sending out the copy instead.
997 * XXX: We are copying the offload context here. This is to handle actions
999 * outport, pop_vlan(), outport, push_vlan(), outport
1001 * copy size needs to include inner ether + IP + TCP, need to revisit
1002 * if we support IP options.
1003 * XXX Head room needs to include the additional encap.
1004 * XXX copySize check is not considering multiple NBs.
1006 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1007 0, 0, TRUE
/*copy NBL info*/);
1009 ASSERT(ovsFwdCtx
->destPortsSizeOut
> 0 ||
1010 ovsFwdCtx
->tunnelTxNic
!= NULL
|| ovsFwdCtx
->tunnelRxNic
!= NULL
);
1012 /* Send the original packet out and save the original source port number */
1013 UINT32 tempVportNo
= ovsFwdCtx
->srcVportNo
;
1014 status
= OvsOutputForwardingCtx(ovsFwdCtx
);
1015 ASSERT(ovsFwdCtx
->curNbl
== NULL
);
1016 ASSERT(ovsFwdCtx
->destPortsSizeOut
== 0);
1017 ASSERT(ovsFwdCtx
->tunnelRxNic
== NULL
);
1018 ASSERT(ovsFwdCtx
->tunnelTxNic
== NULL
);
1020 /* If we didn't make a copy, can't continue. */
1021 if (newNbl
== NULL
) {
1022 ovsActionStats
.noCopiedNbl
++;
1023 return NDIS_STATUS_RESOURCES
;
1026 /* Finish the remaining actions with the new NBL */
1027 if (status
!= NDIS_STATUS_SUCCESS
) {
1028 OvsCompleteNBL(ovsFwdCtx
->switchContext
, newNbl
, TRUE
);
1030 status
= OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
,
1031 newNbl
, tempVportNo
, 0,
1032 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
1033 ovsFwdCtx
->completionList
,
1034 &ovsFwdCtx
->layers
, FALSE
);
1042 * --------------------------------------------------------------------------
1043 * OvsPopFieldInPacketBuf --
1044 * Function to pop a specified field of length 'shiftLength' located at
1045 * 'shiftOffset' from the ethernet header. The data on the left of the
1046 * 'shiftOffset' is right shifted.
1048 * Returns a pointer to the new start in 'bufferData'.
1049 * --------------------------------------------------------------------------
1051 static __inline NDIS_STATUS
1052 OvsPopFieldInPacketBuf(OvsForwardingContext
*ovsFwdCtx
,
1060 UINT32 packetLen
, mdlLen
;
1061 PNET_BUFFER_LIST newNbl
;
1063 PUINT8 tempBuffer
[ETH_HEADER_LENGTH
];
1065 ASSERT(shiftOffset
> ETH_ADDR_LENGTH
);
1067 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1068 0, 0, TRUE
/* copy NBL info */);
1070 ovsActionStats
.noCopiedNbl
++;
1071 return NDIS_STATUS_RESOURCES
;
1074 /* Complete the original NBL and create a copy to modify. */
1075 OvsCompleteNBLForwardingCtx(ovsFwdCtx
, L
"OVS-Dropped due to copy");
1077 status
= OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
, newNbl
,
1078 ovsFwdCtx
->srcVportNo
, 0,
1079 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
1080 NULL
, &ovsFwdCtx
->layers
, FALSE
);
1081 if (status
!= NDIS_STATUS_SUCCESS
) {
1082 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
1083 L
"Dropped due to resouces");
1084 return NDIS_STATUS_RESOURCES
;
1087 curNb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
1088 packetLen
= NET_BUFFER_DATA_LENGTH(curNb
);
1089 ASSERT(curNb
->Next
== NULL
);
1090 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
1091 NdisQueryMdl(curMdl
, &bufferStart
, &mdlLen
, LowPagePriority
);
1093 return NDIS_STATUS_RESOURCES
;
1095 mdlLen
-= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1096 /* Bail out if L2 + shiftLength is not contiguous in the first buffer. */
1097 if (MIN(packetLen
, mdlLen
) < sizeof(EthHdr
) + shiftLength
) {
1099 return NDIS_STATUS_FAILURE
;
1101 bufferStart
+= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1102 RtlCopyMemory(tempBuffer
, bufferStart
, shiftOffset
);
1103 RtlCopyMemory(bufferStart
+ shiftLength
, tempBuffer
, shiftOffset
);
1104 NdisAdvanceNetBufferDataStart(curNb
, shiftLength
, FALSE
, NULL
);
1107 *bufferData
= bufferStart
+ shiftLength
;
1110 return NDIS_STATUS_SUCCESS
;
1115 * --------------------------------------------------------------------------
1116 * OvsPopVlanInPktBuf --
1117 * Function to pop a VLAN tag when the tag is in the packet buffer.
1118 * --------------------------------------------------------------------------
1120 static __inline NDIS_STATUS
1121 OvsPopVlanInPktBuf(OvsForwardingContext
*ovsFwdCtx
)
1124 * Declare a dummy vlanTag structure since we need to compute the size
1125 * of shiftLength. The NDIS one is a unionized structure.
1127 NDIS_PACKET_8021Q_INFO vlanTag
= {0};
1128 UINT32 shiftLength
= sizeof(vlanTag
.TagHeader
);
1129 UINT32 shiftOffset
= sizeof(DL_EUI48
) + sizeof(DL_EUI48
);
1131 return OvsPopFieldInPacketBuf(ovsFwdCtx
, shiftOffset
, shiftLength
, NULL
);
1136 * --------------------------------------------------------------------------
1137 * OvsActionMplsPop --
1138 * Function to pop the first MPLS label from the current packet.
1139 * --------------------------------------------------------------------------
1141 static __inline NDIS_STATUS
1142 OvsActionMplsPop(OvsForwardingContext
*ovsFwdCtx
,
1146 OVS_PACKET_HDR_INFO
*layers
= &ovsFwdCtx
->layers
;
1147 EthHdr
*ethHdr
= NULL
;
1149 status
= OvsPopFieldInPacketBuf(ovsFwdCtx
, sizeof(*ethHdr
),
1150 MPLS_HLEN
, (PUINT8
*)ðHdr
);
1151 if (status
== NDIS_STATUS_SUCCESS
) {
1152 if (ethHdr
&& OvsEthertypeIsMpls(ethHdr
->Type
)) {
1153 ethHdr
->Type
= ethertype
;
1156 layers
->l3Offset
-= MPLS_HLEN
;
1157 layers
->l4Offset
-= MPLS_HLEN
;
1165 * --------------------------------------------------------------------------
1166 * OvsActionMplsPush --
1167 * Function to push the MPLS label into the current packet.
1168 * --------------------------------------------------------------------------
1170 static __inline NDIS_STATUS
1171 OvsActionMplsPush(OvsForwardingContext
*ovsFwdCtx
,
1172 const struct ovs_action_push_mpls
*mpls
)
1175 PNET_BUFFER curNb
= NULL
;
1177 PUINT8 bufferStart
= NULL
;
1178 OVS_PACKET_HDR_INFO
*layers
= &ovsFwdCtx
->layers
;
1179 EthHdr
*ethHdr
= NULL
;
1180 MPLSHdr
*mplsHdr
= NULL
;
1181 UINT32 mdlLen
= 0, curMdlOffset
= 0;
1182 PNET_BUFFER_LIST newNbl
;
1184 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1185 layers
->l3Offset
, MPLS_HLEN
, TRUE
);
1187 ovsActionStats
.noCopiedNbl
++;
1188 return NDIS_STATUS_RESOURCES
;
1190 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
1191 L
"Complete after partial copy.");
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
;
1203 curNb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
1204 ASSERT(curNb
->Next
== NULL
);
1206 status
= NdisRetreatNetBufferDataStart(curNb
, MPLS_HLEN
, 0, NULL
);
1207 if (status
!= NDIS_STATUS_SUCCESS
) {
1211 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
1212 NdisQueryMdl(curMdl
, &bufferStart
, &mdlLen
, LowPagePriority
);
1214 ovsActionStats
.noResource
++;
1215 return NDIS_STATUS_RESOURCES
;
1218 curMdlOffset
= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1219 mdlLen
-= curMdlOffset
;
1220 ASSERT(mdlLen
>= MPLS_HLEN
);
1222 ethHdr
= (EthHdr
*)(bufferStart
+ curMdlOffset
);
1223 RtlMoveMemory(ethHdr
, (UINT8
*)ethHdr
+ MPLS_HLEN
, sizeof(*ethHdr
));
1224 ethHdr
->Type
= mpls
->mpls_ethertype
;
1226 mplsHdr
= (MPLSHdr
*)(ethHdr
+ 1);
1227 mplsHdr
->lse
= mpls
->mpls_lse
;
1229 layers
->l3Offset
+= MPLS_HLEN
;
1230 layers
->l4Offset
+= MPLS_HLEN
;
1232 return NDIS_STATUS_SUCCESS
;
1236 * --------------------------------------------------------------------------
1237 * OvsTunnelAttrToIPv4TunnelKey --
1238 * Convert tunnel attribute to OvsIPv4TunnelKey.
1239 * --------------------------------------------------------------------------
1241 static __inline NDIS_STATUS
1242 OvsTunnelAttrToIPv4TunnelKey(PNL_ATTR attr
,
1243 OvsIPv4TunnelKey
*tunKey
)
1248 tunKey
->attr
[0] = 0;
1249 tunKey
->attr
[1] = 0;
1250 tunKey
->attr
[2] = 0;
1251 ASSERT(NlAttrType(attr
) == OVS_KEY_ATTR_TUNNEL
);
1253 NL_ATTR_FOR_EACH_UNSAFE (a
, rem
, NlAttrData(attr
),
1254 NlAttrGetSize(attr
)) {
1255 switch (NlAttrType(a
)) {
1256 case OVS_TUNNEL_KEY_ATTR_ID
:
1257 tunKey
->tunnelId
= NlAttrGetBe64(a
);
1258 tunKey
->flags
|= OVS_TNL_F_KEY
;
1260 case OVS_TUNNEL_KEY_ATTR_IPV4_SRC
:
1261 tunKey
->src
= NlAttrGetBe32(a
);
1263 case OVS_TUNNEL_KEY_ATTR_IPV4_DST
:
1264 tunKey
->dst
= NlAttrGetBe32(a
);
1266 case OVS_TUNNEL_KEY_ATTR_TOS
:
1267 tunKey
->tos
= NlAttrGetU8(a
);
1269 case OVS_TUNNEL_KEY_ATTR_TTL
:
1270 tunKey
->ttl
= NlAttrGetU8(a
);
1272 case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT
:
1273 tunKey
->flags
|= OVS_TNL_F_DONT_FRAGMENT
;
1275 case OVS_TUNNEL_KEY_ATTR_CSUM
:
1276 tunKey
->flags
|= OVS_TNL_F_CSUM
;
1283 return NDIS_STATUS_SUCCESS
;
1287 *----------------------------------------------------------------------------
1288 * OvsUpdateEthHeader --
1289 * Updates the ethernet header in ovsFwdCtx.curNbl inline based on the
1291 *----------------------------------------------------------------------------
1293 static __inline NDIS_STATUS
1294 OvsUpdateEthHeader(OvsForwardingContext
*ovsFwdCtx
,
1295 const struct ovs_key_ethernet
*ethAttr
)
1301 UINT32 packetLen
, mdlLen
;
1303 curNb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
1304 ASSERT(curNb
->Next
== NULL
);
1305 packetLen
= NET_BUFFER_DATA_LENGTH(curNb
);
1306 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
1307 NdisQueryMdl(curMdl
, &bufferStart
, &mdlLen
, LowPagePriority
);
1309 ovsActionStats
.noResource
++;
1310 return NDIS_STATUS_RESOURCES
;
1312 mdlLen
-= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1314 /* Bail out if the L2 header is not in a contiguous buffer. */
1315 if (MIN(packetLen
, mdlLen
) < sizeof *ethHdr
) {
1317 return NDIS_STATUS_FAILURE
;
1319 ethHdr
= (EthHdr
*)(bufferStart
+ NET_BUFFER_CURRENT_MDL_OFFSET(curNb
));
1321 RtlCopyMemory(ethHdr
->Destination
, ethAttr
->eth_dst
,
1322 sizeof ethHdr
->Destination
);
1323 RtlCopyMemory(ethHdr
->Source
, ethAttr
->eth_src
, sizeof ethHdr
->Source
);
1325 return NDIS_STATUS_SUCCESS
;
1329 *----------------------------------------------------------------------------
1330 * OvsUpdateIPv4Header --
1331 * Updates the IPv4 header in ovsFwdCtx.curNbl inline based on the
1333 *----------------------------------------------------------------------------
1335 static __inline NDIS_STATUS
1336 OvsUpdateIPv4Header(OvsForwardingContext
*ovsFwdCtx
,
1337 const struct ovs_key_ipv4
*ipAttr
)
1343 UINT32 mdlLen
, hdrSize
, packetLen
;
1344 OVS_PACKET_HDR_INFO
*layers
= &ovsFwdCtx
->layers
;
1347 TCPHdr
*tcpHdr
= NULL
;
1348 UDPHdr
*udpHdr
= NULL
;
1350 ASSERT(layers
->value
!= 0);
1353 * Peek into the MDL to get a handle to the IP header and if required
1354 * the TCP/UDP header as well. We check if the required headers are in one
1355 * contiguous MDL, and if not, we copy them over to one MDL.
1357 curNb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
1358 ASSERT(curNb
->Next
== NULL
);
1359 packetLen
= NET_BUFFER_DATA_LENGTH(curNb
);
1360 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
1361 NdisQueryMdl(curMdl
, &bufferStart
, &mdlLen
, LowPagePriority
);
1363 ovsActionStats
.noResource
++;
1364 return NDIS_STATUS_RESOURCES
;
1366 curMdlOffset
= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1367 mdlLen
-= curMdlOffset
;
1368 ASSERT((INT
)mdlLen
>= 0);
1370 if (layers
->isTcp
|| layers
->isUdp
) {
1371 hdrSize
= layers
->l4Offset
+
1372 layers
->isTcp
? sizeof (*tcpHdr
) : sizeof (*udpHdr
);
1374 hdrSize
= layers
->l3Offset
+ sizeof (*ipHdr
);
1377 /* Count of number of bytes of valid data there are in the first MDL. */
1378 mdlLen
= MIN(packetLen
, mdlLen
);
1379 if (mdlLen
< hdrSize
) {
1380 PNET_BUFFER_LIST newNbl
;
1381 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1382 hdrSize
, 0, TRUE
/*copy NBL info*/);
1384 ovsActionStats
.noCopiedNbl
++;
1385 return NDIS_STATUS_RESOURCES
;
1387 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
1388 L
"Complete after partial copy.");
1390 status
= OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
,
1391 newNbl
, ovsFwdCtx
->srcVportNo
, 0,
1392 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
1393 NULL
, &ovsFwdCtx
->layers
, FALSE
);
1394 if (status
!= NDIS_STATUS_SUCCESS
) {
1395 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
1396 L
"OVS-Dropped due to resources");
1397 return NDIS_STATUS_RESOURCES
;
1400 curNb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
1401 ASSERT(curNb
->Next
== NULL
);
1402 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
1403 NdisQueryMdl(curMdl
, &bufferStart
, &mdlLen
, LowPagePriority
);
1405 ovsActionStats
.noResource
++;
1406 return NDIS_STATUS_RESOURCES
;
1408 curMdlOffset
= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1409 mdlLen
-= curMdlOffset
;
1410 ASSERT(mdlLen
>= hdrSize
);
1413 ipHdr
= (IPHdr
*)(bufferStart
+ curMdlOffset
+ layers
->l3Offset
);
1415 if (layers
->isTcp
) {
1416 tcpHdr
= (TCPHdr
*)(bufferStart
+ curMdlOffset
+ layers
->l4Offset
);
1417 } else if (layers
->isUdp
) {
1418 udpHdr
= (UDPHdr
*)(bufferStart
+ curMdlOffset
+ layers
->l4Offset
);
1422 * Adjust the IP header inline as dictated by the action, nad also update
1423 * the IP and the TCP checksum for the data modified.
1425 * In the future, this could be optimized to make one call to
1426 * ChecksumUpdate32(). Ignoring this for now, since for the most common
1427 * case, we only update the TTL.
1429 if (ipHdr
->saddr
!= ipAttr
->ipv4_src
) {
1431 tcpHdr
->check
= ChecksumUpdate32(tcpHdr
->check
, ipHdr
->saddr
,
1433 } else if (udpHdr
&& udpHdr
->check
) {
1434 udpHdr
->check
= ChecksumUpdate32(udpHdr
->check
, ipHdr
->saddr
,
1438 if (ipHdr
->check
!= 0) {
1439 ipHdr
->check
= ChecksumUpdate32(ipHdr
->check
, ipHdr
->saddr
,
1442 ipHdr
->saddr
= ipAttr
->ipv4_src
;
1444 if (ipHdr
->daddr
!= ipAttr
->ipv4_dst
) {
1446 tcpHdr
->check
= ChecksumUpdate32(tcpHdr
->check
, ipHdr
->daddr
,
1448 } else if (udpHdr
&& udpHdr
->check
) {
1449 udpHdr
->check
= ChecksumUpdate32(udpHdr
->check
, ipHdr
->daddr
,
1453 if (ipHdr
->check
!= 0) {
1454 ipHdr
->check
= ChecksumUpdate32(ipHdr
->check
, ipHdr
->daddr
,
1457 ipHdr
->daddr
= ipAttr
->ipv4_dst
;
1459 if (ipHdr
->protocol
!= ipAttr
->ipv4_proto
) {
1460 UINT16 oldProto
= (ipHdr
->protocol
<< 16) & 0xff00;
1461 UINT16 newProto
= (ipAttr
->ipv4_proto
<< 16) & 0xff00;
1463 tcpHdr
->check
= ChecksumUpdate16(tcpHdr
->check
, oldProto
, newProto
);
1464 } else if (udpHdr
&& udpHdr
->check
) {
1465 udpHdr
->check
= ChecksumUpdate16(udpHdr
->check
, oldProto
, newProto
);
1468 if (ipHdr
->check
!= 0) {
1469 ipHdr
->check
= ChecksumUpdate16(ipHdr
->check
, oldProto
, newProto
);
1471 ipHdr
->protocol
= ipAttr
->ipv4_proto
;
1473 if (ipHdr
->ttl
!= ipAttr
->ipv4_ttl
) {
1474 UINT16 oldTtl
= (ipHdr
->ttl
) & 0xff;
1475 UINT16 newTtl
= (ipAttr
->ipv4_ttl
) & 0xff;
1476 if (ipHdr
->check
!= 0) {
1477 ipHdr
->check
= ChecksumUpdate16(ipHdr
->check
, oldTtl
, newTtl
);
1479 ipHdr
->ttl
= ipAttr
->ipv4_ttl
;
1482 return NDIS_STATUS_SUCCESS
;
1486 * --------------------------------------------------------------------------
1487 * OvsExecuteSetAction --
1488 * Executes a set() action, but storing the actions into 'ovsFwdCtx'
1489 * --------------------------------------------------------------------------
1491 static __inline NDIS_STATUS
1492 OvsExecuteSetAction(OvsForwardingContext
*ovsFwdCtx
,
1497 enum ovs_key_attr type
= NlAttrType(a
);
1498 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
1501 case OVS_KEY_ATTR_ETHERNET
:
1502 status
= OvsUpdateEthHeader(ovsFwdCtx
,
1503 NlAttrGetUnspec(a
, sizeof(struct ovs_key_ethernet
)));
1506 case OVS_KEY_ATTR_IPV4
:
1507 status
= OvsUpdateIPv4Header(ovsFwdCtx
,
1508 NlAttrGetUnspec(a
, sizeof(struct ovs_key_ipv4
)));
1511 case OVS_KEY_ATTR_TUNNEL
:
1513 OvsIPv4TunnelKey tunKey
;
1514 status
= OvsTunnelAttrToIPv4TunnelKey((PNL_ATTR
)a
, &tunKey
);
1515 ASSERT(status
== NDIS_STATUS_SUCCESS
);
1516 tunKey
.flow_hash
= (uint16
)(hash
? *hash
: OvsHashFlow(key
));
1517 tunKey
.dst_port
= key
->ipKey
.l4
.tpDst
;
1518 RtlCopyMemory(&ovsFwdCtx
->tunKey
, &tunKey
, sizeof ovsFwdCtx
->tunKey
);
1523 OVS_LOG_INFO("Unhandled attribute %#x", type
);
1530 * --------------------------------------------------------------------------
1531 * OvsExecuteRecirc --
1532 * The function adds a deferred action to allow the current packet, nbl,
1533 * to re-enter datapath packet processing.
1534 * --------------------------------------------------------------------------
1537 OvsExecuteRecirc(OvsForwardingContext
*ovsFwdCtx
,
1539 const PNL_ATTR actions
,
1542 POVS_DEFERRED_ACTION deferredAction
= NULL
;
1543 PNET_BUFFER_LIST newNbl
= NULL
;
1545 if (!NlAttrIsLast(actions
, rem
)) {
1547 * Recirc action is the not the last action of the action list, so we
1548 * need to clone the packet.
1550 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1551 0, 0, TRUE
/*copy NBL info*/);
1553 * Skip the recirc action when out of memory, but continue on with the
1554 * rest of the action list.
1556 if (newNbl
== NULL
) {
1557 ovsActionStats
.noCopiedNbl
++;
1558 return NDIS_STATUS_SUCCESS
;
1563 deferredAction
= OvsAddDeferredActions(newNbl
, key
, NULL
);
1565 deferredAction
= OvsAddDeferredActions(ovsFwdCtx
->curNbl
, key
, NULL
);
1568 if (deferredAction
) {
1569 deferredAction
->key
.recircId
= NlAttrGetU32(actions
);
1572 ovsActionStats
.deferredActionsQueueFull
++;
1573 OvsCompleteNBL(ovsFwdCtx
->switchContext
, newNbl
, TRUE
);
1577 return NDIS_STATUS_SUCCESS
;
1581 * --------------------------------------------------------------------------
1583 * The function updates datapath hash read from userspace.
1584 * --------------------------------------------------------------------------
1587 OvsExecuteHash(OvsFlowKey
*key
,
1588 const PNL_ATTR attr
)
1590 struct ovs_action_hash
*hash_act
= NlAttrData(attr
);
1593 hash
= (UINT32
)OvsHashFlow(key
);
1594 hash
= OvsJhashWords(&hash
, 1, hash_act
->hash_basis
);
1602 * --------------------------------------------------------------------------
1603 * OvsOutputUserspaceAction --
1604 * This function sends the packet to userspace according to nested
1605 * %OVS_USERSPACE_ATTR_* attributes.
1606 * --------------------------------------------------------------------------
1608 static __inline NDIS_STATUS
1609 OvsOutputUserspaceAction(OvsForwardingContext
*ovsFwdCtx
,
1611 const PNL_ATTR attr
)
1613 NTSTATUS status
= NDIS_STATUS_SUCCESS
;
1614 PNL_ATTR userdataAttr
;
1616 POVS_PACKET_QUEUE_ELEM elem
;
1617 POVS_PACKET_HDR_INFO layers
= &ovsFwdCtx
->layers
;
1618 BOOLEAN isRecv
= FALSE
;
1620 POVS_VPORT_ENTRY vport
= OvsFindVportByPortNo(ovsFwdCtx
->switchContext
,
1621 ovsFwdCtx
->srcVportNo
);
1624 if (vport
->isExternal
||
1625 OvsIsTunnelVportType(vport
->ovsType
)) {
1630 queueAttr
= NlAttrFindNested(attr
, OVS_USERSPACE_ATTR_PID
);
1631 userdataAttr
= NlAttrFindNested(attr
, OVS_USERSPACE_ATTR_USERDATA
);
1633 elem
= OvsCreateQueueNlPacket(NlAttrData(userdataAttr
),
1634 NlAttrGetSize(userdataAttr
),
1635 OVS_PACKET_CMD_ACTION
,
1636 vport
, key
, ovsFwdCtx
->curNbl
,
1637 NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
),
1641 LIST_ENTRY missedPackets
;
1642 InitializeListHead(&missedPackets
);
1643 InsertTailList(&missedPackets
, &elem
->link
);
1644 OvsQueuePackets(&missedPackets
, 1);
1646 status
= NDIS_STATUS_FAILURE
;
1653 * --------------------------------------------------------------------------
1654 * OvsExecuteSampleAction --
1655 * Executes actions based on probability, as specified in the nested
1656 * %OVS_SAMPLE_ATTR_* attributes.
1657 * --------------------------------------------------------------------------
1659 static __inline NDIS_STATUS
1660 OvsExecuteSampleAction(OvsForwardingContext
*ovsFwdCtx
,
1662 const PNL_ATTR attr
)
1664 PNET_BUFFER_LIST newNbl
= NULL
;
1665 PNL_ATTR actionsList
= NULL
;
1670 NL_ATTR_FOR_EACH_UNSAFE(a
, rem
, NlAttrData(attr
), NlAttrGetSize(attr
)) {
1671 switch (NlAttrType(a
)) {
1672 case OVS_SAMPLE_ATTR_PROBABILITY
:
1674 UINT32 probability
= NlAttrGetU32(a
);
1676 if (!probability
|| Rand() > probability
) {
1681 case OVS_SAMPLE_ATTR_ACTIONS
:
1688 rem
= NlAttrGetSize(actionsList
);
1689 a
= (PNL_ATTR
)NlAttrData(actionsList
);
1693 /* Actions list is empty, do nothing */
1694 return STATUS_SUCCESS
;
1698 * The only known usage of sample action is having a single user-space
1699 * action. Treat this usage as a special case.
1701 if (NlAttrType(a
) == OVS_ACTION_ATTR_USERSPACE
&&
1702 NlAttrIsLast(a
, rem
)) {
1703 return OvsOutputUserspaceAction(ovsFwdCtx
, key
, a
);
1706 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1707 0, 0, TRUE
/*copy NBL info*/);
1708 if (newNbl
== NULL
) {
1710 * Skip the sample action when out of memory, but continue on with the
1711 * rest of the action list.
1713 ovsActionStats
.noCopiedNbl
++;
1714 return STATUS_SUCCESS
;
1717 if (!OvsAddDeferredActions(newNbl
, key
, a
)) {
1719 "Deferred actions limit reached, dropping sample action.");
1720 OvsCompleteNBL(ovsFwdCtx
->switchContext
, newNbl
, TRUE
);
1723 return STATUS_SUCCESS
;
1727 * --------------------------------------------------------------------------
1728 * OvsDoExecuteActions --
1729 * Interpret and execute the specified 'actions' on the specified packet
1730 * 'curNbl'. The expectation is that if the packet needs to be dropped
1731 * (completed) for some reason, it is added to 'completionList' so that the
1732 * caller can complete the packet. If 'completionList' is NULL, the NBL is
1733 * assumed to be generated by OVS and freed up. Otherwise, the function
1734 * consumes the NBL by generating a NDIS send indication for the packet.
1736 * There are one or more of "clone" NBLs that may get generated while
1737 * executing the actions. Upon any failures, the "cloned" NBLs are freed up,
1738 * and the caller does not have to worry about them.
1740 * Success or failure is returned based on whether the specified actions
1741 * were executed successfully on the packet or not.
1742 * --------------------------------------------------------------------------
1745 OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext
,
1746 OvsCompletionList
*completionList
,
1747 PNET_BUFFER_LIST curNbl
,
1752 OVS_PACKET_HDR_INFO
*layers
,
1753 const PNL_ATTR actions
,
1759 OvsForwardingContext ovsFwdCtx
;
1760 PCWSTR dropReason
= L
"";
1762 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail
=
1763 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl
);
1765 /* XXX: ASSERT that the flow table lock is held. */
1766 status
= OvsInitForwardingCtx(&ovsFwdCtx
, switchContext
, curNbl
, portNo
,
1767 sendFlags
, fwdDetail
, completionList
,
1769 if (status
!= NDIS_STATUS_SUCCESS
) {
1770 dropReason
= L
"OVS-initing destination port list failed";
1774 if (actionsLen
== 0) {
1775 dropReason
= L
"OVS-Dropped due to Flow action";
1776 ovsActionStats
.zeroActionLen
++;
1780 NL_ATTR_FOR_EACH_UNSAFE (a
, rem
, actions
, actionsLen
) {
1781 switch(NlAttrType(a
)) {
1782 case OVS_ACTION_ATTR_OUTPUT
:
1783 dstPortID
= NlAttrGetU32(a
);
1784 status
= OvsAddPorts(&ovsFwdCtx
, key
, dstPortID
,
1786 if (status
!= NDIS_STATUS_SUCCESS
) {
1787 dropReason
= L
"OVS-adding destination port failed";
1792 case OVS_ACTION_ATTR_PUSH_VLAN
:
1794 struct ovs_action_push_vlan
*vlan
;
1796 PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTag
;
1798 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
1799 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1800 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1801 if (status
!= NDIS_STATUS_SUCCESS
) {
1802 dropReason
= L
"OVS-adding destination failed";
1807 vlanTagValue
= NET_BUFFER_LIST_INFO(ovsFwdCtx
.curNbl
,
1808 Ieee8021QNetBufferListInfo
);
1809 if (vlanTagValue
!= NULL
) {
1811 * XXX: We don't support double VLAN tag offload. In such cases,
1812 * we need to insert the existing one into the packet buffer,
1813 * and add the new one as offload. This will take care of
1814 * guest tag-in-tag case as well as OVS rules that specify
1819 vlanTag
= (PNDIS_NET_BUFFER_LIST_8021Q_INFO
)(PVOID
*)&vlanTagValue
;
1820 vlan
= (struct ovs_action_push_vlan
*)NlAttrGet((const PNL_ATTR
)a
);
1821 vlanTag
->TagHeader
.VlanId
= ntohs(vlan
->vlan_tci
) & 0xfff;
1822 vlanTag
->TagHeader
.UserPriority
= ntohs(vlan
->vlan_tci
) >> 13;
1824 NET_BUFFER_LIST_INFO(ovsFwdCtx
.curNbl
,
1825 Ieee8021QNetBufferListInfo
) = vlanTagValue
;
1830 case OVS_ACTION_ATTR_POP_VLAN
:
1832 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
1833 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1834 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1835 if (status
!= NDIS_STATUS_SUCCESS
) {
1836 dropReason
= L
"OVS-adding destination failed";
1841 if (NET_BUFFER_LIST_INFO(ovsFwdCtx
.curNbl
,
1842 Ieee8021QNetBufferListInfo
) != 0) {
1843 NET_BUFFER_LIST_INFO(ovsFwdCtx
.curNbl
,
1844 Ieee8021QNetBufferListInfo
) = 0;
1847 * The VLAN tag is inserted into the packet buffer. Pop the tag
1848 * by packet buffer modification.
1850 status
= OvsPopVlanInPktBuf(&ovsFwdCtx
);
1851 if (status
!= NDIS_STATUS_SUCCESS
) {
1852 dropReason
= L
"OVS-pop vlan action failed";
1859 case OVS_ACTION_ATTR_PUSH_MPLS
:
1861 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
1862 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1863 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1864 if (status
!= NDIS_STATUS_SUCCESS
) {
1865 dropReason
= L
"OVS-adding destination failed";
1870 status
= OvsActionMplsPush(&ovsFwdCtx
,
1871 (struct ovs_action_push_mpls
*)NlAttrGet
1872 ((const PNL_ATTR
)a
));
1873 if (status
!= NDIS_STATUS_SUCCESS
) {
1874 dropReason
= L
"OVS-push MPLS action failed";
1877 layers
->l3Offset
+= MPLS_HLEN
;
1878 layers
->l4Offset
+= MPLS_HLEN
;
1882 case OVS_ACTION_ATTR_POP_MPLS
:
1884 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
1885 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1886 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1887 if (status
!= NDIS_STATUS_SUCCESS
) {
1888 dropReason
= L
"OVS-adding destination failed";
1893 status
= OvsActionMplsPop(&ovsFwdCtx
, NlAttrGetBe16(a
));
1894 if (status
!= NDIS_STATUS_SUCCESS
) {
1895 dropReason
= L
"OVS-pop MPLS action failed";
1898 layers
->l3Offset
-= MPLS_HLEN
;
1899 layers
->l4Offset
-= MPLS_HLEN
;
1903 case OVS_ACTION_ATTR_HASH
:
1905 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
1906 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1907 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1908 if (status
!= NDIS_STATUS_SUCCESS
) {
1909 dropReason
= L
"OVS-adding destination failed";
1914 OvsExecuteHash(key
, (const PNL_ATTR
)a
);
1919 case OVS_ACTION_ATTR_CT
:
1921 if (ovsFwdCtx
.destPortsSizeOut
> 0
1922 || ovsFwdCtx
.tunnelTxNic
!= NULL
1923 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1924 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1925 if (status
!= NDIS_STATUS_SUCCESS
) {
1926 dropReason
= L
"OVS-adding destination failed";
1931 status
= OvsExecuteConntrackAction(ovsFwdCtx
.curNbl
, layers
,
1932 key
, (const PNL_ATTR
)a
);
1933 if (status
!= NDIS_STATUS_SUCCESS
) {
1934 OVS_LOG_ERROR("CT Action failed");
1935 dropReason
= L
"OVS-conntrack action failed";
1941 case OVS_ACTION_ATTR_RECIRC
:
1943 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
1944 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1945 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1946 if (status
!= NDIS_STATUS_SUCCESS
) {
1947 dropReason
= L
"OVS-adding destination failed";
1952 status
= OvsExecuteRecirc(&ovsFwdCtx
, key
, (const PNL_ATTR
)a
, rem
);
1953 if (status
!= NDIS_STATUS_SUCCESS
) {
1954 dropReason
= L
"OVS-recirculation action failed";
1958 if (NlAttrIsLast(a
, rem
)) {
1964 case OVS_ACTION_ATTR_USERSPACE
:
1966 status
= OvsOutputUserspaceAction(&ovsFwdCtx
, key
,
1968 if (status
!= NDIS_STATUS_SUCCESS
) {
1969 dropReason
= L
"OVS-Dropped due to failure to queue to "
1973 dropReason
= L
"OVS-Completed since packet was copied to "
1977 case OVS_ACTION_ATTR_SET
:
1979 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
1980 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1981 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1982 if (status
!= NDIS_STATUS_SUCCESS
) {
1983 dropReason
= L
"OVS-adding destination failed";
1988 status
= OvsExecuteSetAction(&ovsFwdCtx
, key
, hash
,
1989 (const PNL_ATTR
)NlAttrGet
1990 ((const PNL_ATTR
)a
));
1991 if (status
!= NDIS_STATUS_SUCCESS
) {
1992 dropReason
= L
"OVS-set action failed";
1997 case OVS_ACTION_ATTR_SAMPLE
:
1999 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
2000 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
2001 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
2002 if (status
!= NDIS_STATUS_SUCCESS
) {
2003 dropReason
= L
"OVS-adding destination failed";
2008 status
= OvsExecuteSampleAction(&ovsFwdCtx
, key
,
2010 if (status
!= NDIS_STATUS_SUCCESS
) {
2011 dropReason
= L
"OVS-sample action failed";
2017 status
= NDIS_STATUS_NOT_SUPPORTED
;
2022 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
2023 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
2024 status
= OvsOutputForwardingCtx(&ovsFwdCtx
);
2025 ASSERT(ovsFwdCtx
.curNbl
== NULL
);
2028 ASSERT(ovsFwdCtx
.destPortsSizeOut
== 0);
2029 ASSERT(ovsFwdCtx
.tunnelRxNic
== NULL
);
2030 ASSERT(ovsFwdCtx
.tunnelTxNic
== NULL
);
2034 * If curNbl != NULL, it implies the NBL has not been not freed up so far.
2036 if (ovsFwdCtx
.curNbl
) {
2037 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
, dropReason
);
2045 * --------------------------------------------------------------------------
2046 * OvsActionsExecute --
2047 * The function interprets and executes the specified 'actions' on the
2048 * specified packet 'curNbl'. See 'OvsDoExecuteActions' description for
2051 * Also executes deferred actions added by recirculation or sample
2053 * --------------------------------------------------------------------------
2056 OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext
,
2057 OvsCompletionList
*completionList
,
2058 PNET_BUFFER_LIST curNbl
,
2063 OVS_PACKET_HDR_INFO
*layers
,
2064 const PNL_ATTR actions
,
2069 status
= OvsDoExecuteActions(switchContext
, completionList
, curNbl
,
2070 portNo
, sendFlags
, key
, hash
, layers
,
2071 actions
, actionsLen
);
2073 if (status
== STATUS_SUCCESS
) {
2074 status
= OvsProcessDeferredActions(switchContext
, completionList
,
2075 portNo
, sendFlags
, layers
);
2082 * --------------------------------------------------------------------------
2084 * The function processes the packet 'curNbl' that re-entered datapath
2085 * packet processing after a recirculation action.
2086 * --------------------------------------------------------------------------
2089 OvsDoRecirc(POVS_SWITCH_CONTEXT switchContext
,
2090 OvsCompletionList
*completionList
,
2091 PNET_BUFFER_LIST curNbl
,
2094 OVS_PACKET_HDR_INFO
*layers
)
2098 OvsForwardingContext ovsFwdCtx
= { 0 };
2102 OvsInitForwardingCtx(&ovsFwdCtx
, switchContext
, curNbl
,
2104 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl
),
2105 completionList
, layers
, TRUE
);
2107 flow
= OvsLookupFlow(&ovsFwdCtx
.switchContext
->datapath
, key
, &hash
, FALSE
);
2109 UINT32 level
= OvsDeferredActionsLevelGet();
2111 if (level
> DEFERRED_ACTION_EXEC_LEVEL
) {
2112 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
,
2113 L
"OVS-Dropped due to deferred actions execution level limit \
2115 ovsActionStats
.deferredActionsExecLimit
++;
2116 ovsFwdCtx
.curNbl
= NULL
;
2117 return NDIS_STATUS_FAILURE
;
2120 OvsFlowUsed(flow
, ovsFwdCtx
.curNbl
, &ovsFwdCtx
.layers
);
2121 ovsFwdCtx
.switchContext
->datapath
.hits
++;
2123 OvsDeferredActionsLevelInc();
2125 status
= OvsDoExecuteActions(ovsFwdCtx
.switchContext
,
2126 ovsFwdCtx
.completionList
,
2128 ovsFwdCtx
.srcVportNo
,
2129 ovsFwdCtx
.sendFlags
,
2130 key
, &hash
, &ovsFwdCtx
.layers
,
2131 flow
->actions
, flow
->actionsLen
);
2132 ovsFwdCtx
.curNbl
= NULL
;
2134 OvsDeferredActionsLevelDec();
2136 POVS_VPORT_ENTRY vport
= NULL
;
2137 LIST_ENTRY missedPackets
;
2140 ovsFwdCtx
.switchContext
->datapath
.misses
++;
2141 InitializeListHead(&missedPackets
);
2142 vport
= OvsFindVportByPortNo(switchContext
, srcPortNo
);
2143 if (vport
== NULL
|| vport
->ovsState
!= OVS_STATE_CONNECTED
) {
2144 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
,
2145 L
"OVS-Dropped due to port removal");
2146 ovsActionStats
.noVport
++;
2147 return NDIS_STATUS_SUCCESS
;
2149 status
= OvsCreateAndAddPackets(NULL
, 0, OVS_PACKET_CMD_MISS
,
2150 vport
, key
, ovsFwdCtx
.curNbl
,
2152 switchContext
->virtualExternalPortId
,
2154 ovsFwdCtx
.switchContext
,
2155 &missedPackets
, &num
);
2157 OvsQueuePackets(&missedPackets
, num
);
2159 if (status
== NDIS_STATUS_SUCCESS
) {
2160 /* Complete the packet since it was copied to user buffer. */
2161 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
,
2162 L
"OVS-Dropped since packet was copied to userspace");
2163 ovsActionStats
.flowMiss
++;
2165 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
,
2166 L
"OVS-Dropped due to failure to queue to userspace");
2167 ovsActionStats
.failedFlowMiss
++;
2168 status
= NDIS_STATUS_FAILURE
;