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"
41 #define OVS_DBG_MOD OVS_DBG_ACTION
43 #define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2
45 typedef struct _OVS_ACTION_STATS
{
57 UINT32 failedFlowMiss
;
59 UINT32 failedFlowExtract
;
64 UINT32 cannotGrowDest
;
66 UINT32 failedChecksum
;
67 UINT32 deferredActionsQueueFull
;
68 UINT32 deferredActionsExecLimit
;
69 } OVS_ACTION_STATS
, *POVS_ACTION_STATS
;
71 OVS_ACTION_STATS ovsActionStats
;
74 * There a lot of data that needs to be maintained while executing the pipeline
75 * as dictated by the actions of a flow, across different functions at different
76 * levels. Such data is put together in a 'context' structure. Care should be
77 * exercised while adding new members to the structure - only add ones that get
78 * used across multiple stages in the pipeline/get used in multiple functions.
80 typedef struct OvsForwardingContext
{
81 POVS_SWITCH_CONTEXT switchContext
;
82 /* The NBL currently used in the pipeline. */
83 PNET_BUFFER_LIST curNbl
;
84 /* NDIS forwarding detail for 'curNbl'. */
85 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail
;
86 /* Array of destination ports for 'curNbl'. */
87 PNDIS_SWITCH_FORWARDING_DESTINATION_ARRAY destinationPorts
;
88 /* send flags while sending 'curNbl' into NDIS. */
90 /* Total number of output ports, used + unused, in 'curNbl'. */
91 UINT32 destPortsSizeIn
;
92 /* Total number of used output ports in 'curNbl'. */
93 UINT32 destPortsSizeOut
;
95 * If 'curNbl' is not owned by OVS, they need to be tracked, if they need to
98 OvsCompletionList
*completionList
;
100 * vport number of 'curNbl' when it is passed from the PIF bridge to the INT
101 * bridge. ie. during tunneling on the Rx side.
107 * - specified in actions during tunneling Tx
108 * - extracted from an NBL during tunneling Rx
110 OvsIPv4TunnelKey tunKey
;
114 * To store the output port, when it is a tunneled port. We don't foresee
115 * multiple tunneled ports as outport for any given NBL.
117 POVS_VPORT_ENTRY tunnelTxNic
;
121 * Points to the Internal port on the PIF Bridge, if the packet needs to be
124 POVS_VPORT_ENTRY tunnelRxNic
;
126 /* header information */
127 OVS_PACKET_HDR_INFO layers
;
128 } OvsForwardingContext
;
131 * --------------------------------------------------------------------------
132 * OvsInitForwardingCtx --
133 * Function to init/re-init the 'ovsFwdCtx' context as the actions pipeline
137 * NDIS_STATUS_SUCCESS on success
138 * Other NDIS_STATUS upon failure. Upon failure, it is safe to call
139 * OvsCompleteNBLForwardingCtx(), since 'ovsFwdCtx' has been initialized
140 * enough for OvsCompleteNBLForwardingCtx() to do its work.
141 * --------------------------------------------------------------------------
143 static __inline NDIS_STATUS
144 OvsInitForwardingCtx(OvsForwardingContext
*ovsFwdCtx
,
145 POVS_SWITCH_CONTEXT switchContext
,
146 PNET_BUFFER_LIST curNbl
,
149 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail
,
150 OvsCompletionList
*completionList
,
151 OVS_PACKET_HDR_INFO
*layers
,
152 BOOLEAN resetTunnelInfo
)
155 ASSERT(switchContext
);
160 * Set values for curNbl and switchContext so upon failures, we have enough
161 * information to do cleanup.
163 ovsFwdCtx
->curNbl
= curNbl
;
164 ovsFwdCtx
->switchContext
= switchContext
;
165 ovsFwdCtx
->completionList
= completionList
;
166 ovsFwdCtx
->fwdDetail
= fwdDetail
;
168 if (fwdDetail
->NumAvailableDestinations
> 0) {
170 * XXX: even though MSDN says GetNetBufferListDestinations() returns
171 * NDIS_STATUS, the header files say otherwise.
173 switchContext
->NdisSwitchHandlers
.GetNetBufferListDestinations(
174 switchContext
->NdisSwitchContext
, curNbl
,
175 &ovsFwdCtx
->destinationPorts
);
177 ASSERT(ovsFwdCtx
->destinationPorts
);
178 /* Ensure that none of the elements are consumed yet. */
179 ASSERT(ovsFwdCtx
->destinationPorts
->NumElements
==
180 fwdDetail
->NumAvailableDestinations
);
182 ovsFwdCtx
->destinationPorts
= NULL
;
184 ovsFwdCtx
->destPortsSizeIn
= fwdDetail
->NumAvailableDestinations
;
185 ovsFwdCtx
->destPortsSizeOut
= 0;
186 ovsFwdCtx
->srcVportNo
= srcVportNo
;
187 ovsFwdCtx
->sendFlags
= sendFlags
;
189 ovsFwdCtx
->layers
= *layers
;
191 RtlZeroMemory(&ovsFwdCtx
->layers
, sizeof ovsFwdCtx
->layers
);
193 if (resetTunnelInfo
) {
194 ovsFwdCtx
->tunnelTxNic
= NULL
;
195 ovsFwdCtx
->tunnelRxNic
= NULL
;
196 RtlZeroMemory(&ovsFwdCtx
->tunKey
, sizeof ovsFwdCtx
->tunKey
);
199 return NDIS_STATUS_SUCCESS
;
203 * --------------------------------------------------------------------------
204 * OvsDetectTunnelRxPkt --
205 * Utility function for an RX packet to detect its tunnel type.
208 * True - if the tunnel type was detected.
209 * False - if not a tunnel packet or tunnel type not supported.
210 * --------------------------------------------------------------------------
212 static __inline BOOLEAN
213 OvsDetectTunnelRxPkt(OvsForwardingContext
*ovsFwdCtx
,
214 const OvsFlowKey
*flowKey
)
216 POVS_VPORT_ENTRY tunnelVport
= NULL
;
218 /* XXX: we should also check for the length of the UDP payload to pick
219 * packets only if they are at least VXLAN header size.
223 * For some of the tunnel types such as GRE, the dstPort is not applicable
224 * since GRE does not have a L4 port. We use '0' for convenience.
226 if (!flowKey
->ipKey
.nwFrag
) {
227 UINT16 dstPort
= htons(flowKey
->ipKey
.l4
.tpDst
);
229 ASSERT(flowKey
->ipKey
.nwProto
!= IPPROTO_GRE
|| dstPort
== 0);
232 OvsFindTunnelVportByDstPortAndNWProto(ovsFwdCtx
->switchContext
,
234 flowKey
->ipKey
.nwProto
);
236 switch(tunnelVport
->ovsType
) {
237 case OVS_VPORT_TYPE_STT
:
238 ovsActionStats
.rxStt
++;
240 case OVS_VPORT_TYPE_VXLAN
:
241 ovsActionStats
.rxVxlan
++;
243 case OVS_VPORT_TYPE_GENEVE
:
244 ovsActionStats
.rxGeneve
++;
246 case OVS_VPORT_TYPE_GRE
:
247 ovsActionStats
.rxGre
++;
253 // We might get tunnel packets even before the tunnel gets initialized.
255 ASSERT(ovsFwdCtx
->tunnelRxNic
== NULL
);
256 ovsFwdCtx
->tunnelRxNic
= tunnelVport
;
264 * --------------------------------------------------------------------------
265 * OvsDetectTunnelPkt --
266 * Utility function to detect if a packet is to be subjected to
267 * tunneling (Tx) or de-tunneling (Rx). Various factors such as source
268 * port, destination port, packet contents, and previously setup tunnel
272 * True - If the packet is to be subjected to tunneling.
273 * In case of invalid tunnel context, the tunneling functionality is
274 * a no-op and is completed within this function itself by consuming
275 * all of the tunneling context.
276 * False - If not a tunnel packet or tunnel type not supported. Caller should
277 * process the packet as a non-tunnel packet.
278 * --------------------------------------------------------------------------
280 static __inline BOOLEAN
281 OvsDetectTunnelPkt(OvsForwardingContext
*ovsFwdCtx
,
282 const POVS_VPORT_ENTRY dstVport
,
283 const OvsFlowKey
*flowKey
)
285 if (OvsIsInternalVportType(dstVport
->ovsType
)) {
288 * The source of NBL during tunneling Rx could be the external
289 * port or if it is being executed from userspace, the source port is
292 BOOLEAN validSrcPort
=
293 (ovsFwdCtx
->fwdDetail
->SourcePortId
==
294 ovsFwdCtx
->switchContext
->virtualExternalPortId
) ||
295 (ovsFwdCtx
->fwdDetail
->SourcePortId
==
296 NDIS_SWITCH_DEFAULT_PORT_ID
);
298 if (validSrcPort
&& OvsDetectTunnelRxPkt(ovsFwdCtx
, flowKey
)) {
299 ASSERT(ovsFwdCtx
->tunnelTxNic
== NULL
);
300 ASSERT(ovsFwdCtx
->tunnelRxNic
!= NULL
);
303 } else if (OvsIsTunnelVportType(dstVport
->ovsType
)) {
304 ASSERT(ovsFwdCtx
->tunnelRxNic
== NULL
);
308 * The destination port is a tunnel port. Encapsulation must be
309 * performed only on packets that originate from:
311 * - a bridge-internal port (packets generated from userspace)
314 * If the packet will not be encapsulated, consume the tunnel context
317 if (ovsFwdCtx
->srcVportNo
!= OVS_DPPORT_NUMBER_INVALID
) {
319 POVS_VPORT_ENTRY vport
= OvsFindVportByPortNo(
320 ovsFwdCtx
->switchContext
, ovsFwdCtx
->srcVportNo
);
323 (vport
->ovsType
!= OVS_VPORT_TYPE_NETDEV
&&
324 vport
->ovsType
!= OVS_VPORT_TYPE_INTERNAL
&&
325 !OvsIsTunnelVportType(vport
->ovsType
))) {
326 ovsFwdCtx
->tunKey
.dst
= 0;
330 /* Tunnel the packet only if tunnel context is set. */
331 if (ovsFwdCtx
->tunKey
.dst
!= 0) {
332 switch(dstVport
->ovsType
) {
333 case OVS_VPORT_TYPE_GRE
:
334 ovsActionStats
.txGre
++;
336 case OVS_VPORT_TYPE_VXLAN
:
337 ovsActionStats
.txVxlan
++;
339 case OVS_VPORT_TYPE_STT
:
340 ovsActionStats
.txStt
++;
342 case OVS_VPORT_TYPE_GENEVE
:
343 ovsActionStats
.txGeneve
++;
346 ovsFwdCtx
->tunnelTxNic
= dstVport
;
357 * --------------------------------------------------------------------------
359 * Add the specified destination vport into the forwarding context. If the
360 * vport is a VIF/external port, it is added directly to the NBL. If it is
361 * a tunneling port, it is NOT added to the NBL.
364 * NDIS_STATUS_SUCCESS on success
365 * Other NDIS_STATUS upon failure.
366 * --------------------------------------------------------------------------
368 static __inline NDIS_STATUS
369 OvsAddPorts(OvsForwardingContext
*ovsFwdCtx
,
371 NDIS_SWITCH_PORT_ID dstPortId
,
372 BOOLEAN preserveVLAN
,
373 BOOLEAN preservePriority
)
375 POVS_VPORT_ENTRY vport
;
376 PNDIS_SWITCH_PORT_DESTINATION fwdPort
;
378 POVS_SWITCH_CONTEXT switchContext
= ovsFwdCtx
->switchContext
;
381 * We hold the dispatch lock that protects the list of vports, so vports
382 * validated here can be added as destinations safely before we call into
385 * Some of the vports can be tunnelled ports as well in which case
386 * they should be added to a separate list of tunnelled destination ports
387 * instead of the VIF ports. The context for the tunnel is settable
388 * in OvsForwardingContext.
390 vport
= OvsFindVportByPortNo(ovsFwdCtx
->switchContext
, dstPortId
);
391 if (vport
== NULL
|| vport
->ovsState
!= OVS_STATE_CONNECTED
) {
393 * There may be some latency between a port disappearing, and userspace
394 * updating the recalculated flows. In the meantime, handle invalid
397 ovsActionStats
.noVport
++;
398 return NDIS_STATUS_SUCCESS
;
400 ASSERT(vport
->nicState
== NdisSwitchNicStateConnected
);
401 vport
->stats
.txPackets
++;
402 vport
->stats
.txBytes
+=
403 NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
));
405 if (OvsDetectTunnelPkt(ovsFwdCtx
, vport
, flowKey
)) {
406 return NDIS_STATUS_SUCCESS
;
409 if (ovsFwdCtx
->destPortsSizeOut
== ovsFwdCtx
->destPortsSizeIn
) {
410 if (ovsFwdCtx
->destPortsSizeIn
== 0) {
411 ASSERT(ovsFwdCtx
->destinationPorts
== NULL
);
412 ASSERT(ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
== 0);
414 switchContext
->NdisSwitchHandlers
.GrowNetBufferListDestinations(
415 switchContext
->NdisSwitchContext
, ovsFwdCtx
->curNbl
,
416 OVS_DEST_PORTS_ARRAY_MIN_SIZE
,
417 &ovsFwdCtx
->destinationPorts
);
418 if (status
!= NDIS_STATUS_SUCCESS
) {
419 ovsActionStats
.cannotGrowDest
++;
422 ovsFwdCtx
->destPortsSizeIn
=
423 ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
;
424 ASSERT(ovsFwdCtx
->destinationPorts
);
426 ASSERT(ovsFwdCtx
->destinationPorts
!= NULL
);
429 * A ULONG value that specifies the total number of
430 * NDIS_SWITCH_PORT_DESTINATION elements in the
431 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure.
434 * A ULONG value that specifies the number of
435 * NDIS_SWITCH_PORT_DESTINATION elements in the
436 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure that
437 * specify port destinations.
439 * NumAvailableDestinations:
440 * A value that specifies the number of unused extensible switch
441 * destination ports elements within an NET_BUFFER_LIST structure.
443 ASSERT(ovsFwdCtx
->destinationPorts
->NumElements
==
444 ovsFwdCtx
->destPortsSizeIn
);
445 ASSERT(ovsFwdCtx
->destinationPorts
->NumDestinations
==
446 ovsFwdCtx
->destPortsSizeOut
-
447 ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
);
448 ASSERT(ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
> 0);
450 * Before we grow the array of destination ports, the current set
451 * of ports needs to be committed. Only the ports added since the
452 * last commit need to be part of the new update.
454 status
= switchContext
->NdisSwitchHandlers
.UpdateNetBufferListDestinations(
455 switchContext
->NdisSwitchContext
, ovsFwdCtx
->curNbl
,
456 ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
,
457 ovsFwdCtx
->destinationPorts
);
458 if (status
!= NDIS_STATUS_SUCCESS
) {
459 ovsActionStats
.cannotGrowDest
++;
462 ASSERT(ovsFwdCtx
->destinationPorts
->NumElements
==
463 ovsFwdCtx
->destPortsSizeIn
);
464 ASSERT(ovsFwdCtx
->destinationPorts
->NumDestinations
==
465 ovsFwdCtx
->destPortsSizeOut
);
466 ASSERT(ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
== 0);
468 status
= switchContext
->NdisSwitchHandlers
.GrowNetBufferListDestinations(
469 switchContext
->NdisSwitchContext
, ovsFwdCtx
->curNbl
,
470 ovsFwdCtx
->destPortsSizeIn
, &ovsFwdCtx
->destinationPorts
);
471 if (status
!= NDIS_STATUS_SUCCESS
) {
472 ovsActionStats
.cannotGrowDest
++;
475 ASSERT(ovsFwdCtx
->destinationPorts
!= NULL
);
476 ovsFwdCtx
->destPortsSizeIn
<<= 1;
480 ASSERT(ovsFwdCtx
->destPortsSizeOut
< ovsFwdCtx
->destPortsSizeIn
);
482 NDIS_SWITCH_PORT_DESTINATION_AT_ARRAY_INDEX(ovsFwdCtx
->destinationPorts
,
483 ovsFwdCtx
->destPortsSizeOut
);
485 fwdPort
->PortId
= vport
->portId
;
486 fwdPort
->NicIndex
= vport
->nicIndex
;
487 fwdPort
->IsExcluded
= 0;
488 fwdPort
->PreserveVLAN
= preserveVLAN
;
489 fwdPort
->PreservePriority
= preservePriority
;
490 ovsFwdCtx
->destPortsSizeOut
+= 1;
492 return NDIS_STATUS_SUCCESS
;
497 * --------------------------------------------------------------------------
498 * OvsClearTunTxCtx --
499 * Utility function to clear tx tunneling context.
500 * --------------------------------------------------------------------------
503 OvsClearTunTxCtx(OvsForwardingContext
*ovsFwdCtx
)
505 ovsFwdCtx
->tunnelTxNic
= NULL
;
506 ovsFwdCtx
->tunKey
.dst
= 0;
511 * --------------------------------------------------------------------------
512 * OvsClearTunRxCtx --
513 * Utility function to clear rx tunneling context.
514 * --------------------------------------------------------------------------
517 OvsClearTunRxCtx(OvsForwardingContext
*ovsFwdCtx
)
519 ovsFwdCtx
->tunnelRxNic
= NULL
;
520 ovsFwdCtx
->tunKey
.dst
= 0;
525 * --------------------------------------------------------------------------
526 * OvsCompleteNBLForwardingCtx --
527 * This utility function is responsible for freeing/completing an NBL - either
528 * by adding it to a completion list or by freeing it.
531 * It also resets the necessary fields in 'ovsFwdCtx'.
532 * --------------------------------------------------------------------------
535 OvsCompleteNBLForwardingCtx(OvsForwardingContext
*ovsFwdCtx
,
538 NDIS_STRING filterReason
;
540 RtlInitUnicodeString(&filterReason
, dropReason
);
541 if (ovsFwdCtx
->completionList
) {
542 OvsAddPktCompletionList(ovsFwdCtx
->completionList
, TRUE
,
543 ovsFwdCtx
->fwdDetail
->SourcePortId
, ovsFwdCtx
->curNbl
, 1,
545 ovsFwdCtx
->curNbl
= NULL
;
547 /* If there is no completionList, we assume this is ovs created NBL */
548 ovsFwdCtx
->curNbl
= OvsCompleteNBL(ovsFwdCtx
->switchContext
,
549 ovsFwdCtx
->curNbl
, TRUE
);
550 ASSERT(ovsFwdCtx
->curNbl
== NULL
);
552 /* XXX: these can be made debug only to save cycles. Ideally the pipeline
553 * using these fields should reset the values at the end of the pipeline. */
554 ovsFwdCtx
->destPortsSizeOut
= 0;
555 ovsFwdCtx
->tunnelTxNic
= NULL
;
556 ovsFwdCtx
->tunnelRxNic
= NULL
;
560 * --------------------------------------------------------------------------
561 * OvsDoFlowLookupOutput --
562 * Function to be used for the second stage of a tunneling workflow, ie.:
563 * - On the encapsulated packet on Tx path, to do a flow extract, flow
564 * lookup and excuting the actions.
565 * - On the decapsulated packet on Rx path, to do a flow extract, flow
566 * lookup and excuting the actions.
568 * XXX: It is assumed that the NBL in 'ovsFwdCtx' is owned by OVS. This is
569 * until the new buffer management framework is adopted.
572 * The NBL in 'ovsFwdCtx' is consumed.
573 * --------------------------------------------------------------------------
575 static __inline NDIS_STATUS
576 OvsDoFlowLookupOutput(OvsForwardingContext
*ovsFwdCtx
)
578 OvsFlowKey key
= { 0 };
579 OvsFlow
*flow
= NULL
;
581 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
582 POVS_VPORT_ENTRY vport
=
583 OvsFindVportByPortNo(ovsFwdCtx
->switchContext
, ovsFwdCtx
->srcVportNo
);
584 if (vport
== NULL
|| vport
->ovsState
!= OVS_STATE_CONNECTED
) {
585 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
586 L
"OVS-Dropped due to internal/tunnel port removal");
587 ovsActionStats
.noVport
++;
588 return NDIS_STATUS_SUCCESS
;
590 ASSERT(vport
->nicState
== NdisSwitchNicStateConnected
);
592 /* Assert that in the Rx direction, key is always setup. */
593 ASSERT(ovsFwdCtx
->tunnelRxNic
== NULL
|| ovsFwdCtx
->tunKey
.dst
!= 0);
595 OvsExtractFlow(ovsFwdCtx
->curNbl
, ovsFwdCtx
->srcVportNo
,
596 &key
, &ovsFwdCtx
->layers
,
597 ovsFwdCtx
->tunKey
.dst
!= 0 ? &ovsFwdCtx
->tunKey
: NULL
);
598 if (status
!= NDIS_STATUS_SUCCESS
) {
599 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
600 L
"OVS-Flow extract failed");
601 ovsActionStats
.failedFlowExtract
++;
605 flow
= OvsLookupFlow(&ovsFwdCtx
->switchContext
->datapath
, &key
, &hash
, FALSE
);
607 OvsFlowUsed(flow
, ovsFwdCtx
->curNbl
, &ovsFwdCtx
->layers
);
608 ovsFwdCtx
->switchContext
->datapath
.hits
++;
609 status
= OvsDoExecuteActions(ovsFwdCtx
->switchContext
,
610 ovsFwdCtx
->completionList
,
612 ovsFwdCtx
->srcVportNo
,
613 ovsFwdCtx
->sendFlags
,
614 &key
, &hash
, &ovsFwdCtx
->layers
,
615 flow
->actions
, flow
->actionsLen
);
616 ovsFwdCtx
->curNbl
= NULL
;
618 LIST_ENTRY missedPackets
;
620 ovsFwdCtx
->switchContext
->datapath
.misses
++;
621 InitializeListHead(&missedPackets
);
622 status
= OvsCreateAndAddPackets(NULL
, 0, OVS_PACKET_CMD_MISS
, vport
,
623 &key
,ovsFwdCtx
->curNbl
,
624 FALSE
, &ovsFwdCtx
->layers
,
625 ovsFwdCtx
->switchContext
, &missedPackets
, &num
);
627 OvsQueuePackets(&missedPackets
, num
);
629 if (status
== NDIS_STATUS_SUCCESS
) {
630 /* Complete the packet since it was copied to user buffer. */
631 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
632 L
"OVS-Dropped since packet was copied to userspace");
633 ovsActionStats
.flowMiss
++;
634 status
= NDIS_STATUS_SUCCESS
;
636 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
637 L
"OVS-Dropped due to failure to queue to userspace");
638 status
= NDIS_STATUS_FAILURE
;
639 ovsActionStats
.failedFlowMiss
++;
647 * --------------------------------------------------------------------------
649 * The start function for Tx tunneling - encapsulates the packet, and
650 * outputs the packet on the PIF bridge.
653 * The NBL in 'ovsFwdCtx' is consumed.
654 * --------------------------------------------------------------------------
656 static __inline NDIS_STATUS
657 OvsTunnelPortTx(OvsForwardingContext
*ovsFwdCtx
)
659 NDIS_STATUS status
= NDIS_STATUS_FAILURE
;
660 PNET_BUFFER_LIST newNbl
= NULL
;
662 NDIS_SWITCH_NIC_INDEX srcNicIndex
;
663 NDIS_SWITCH_PORT_ID srcPortId
;
666 * Setup the source port to be the internal port to as to facilitate the
667 * second OvsLookupFlow.
669 if (ovsFwdCtx
->switchContext
->countInternalVports
<= 0 ||
670 ovsFwdCtx
->switchContext
->virtualExternalVport
== NULL
) {
671 OvsClearTunTxCtx(ovsFwdCtx
);
672 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
673 L
"OVS-Dropped since either internal or external port is absent");
674 return NDIS_STATUS_FAILURE
;
677 OVS_FWD_INFO switchFwdInfo
= { 0 };
678 /* Apply the encapsulation. The encapsulation will not consume the NBL. */
679 switch(ovsFwdCtx
->tunnelTxNic
->ovsType
) {
680 case OVS_VPORT_TYPE_GRE
:
681 status
= OvsEncapGre(ovsFwdCtx
->tunnelTxNic
, ovsFwdCtx
->curNbl
,
682 &ovsFwdCtx
->tunKey
, ovsFwdCtx
->switchContext
,
683 &ovsFwdCtx
->layers
, &newNbl
, &switchFwdInfo
);
685 case OVS_VPORT_TYPE_VXLAN
:
686 status
= OvsEncapVxlan(ovsFwdCtx
->tunnelTxNic
, ovsFwdCtx
->curNbl
,
687 &ovsFwdCtx
->tunKey
, ovsFwdCtx
->switchContext
,
688 &ovsFwdCtx
->layers
, &newNbl
, &switchFwdInfo
);
690 case OVS_VPORT_TYPE_STT
:
691 status
= OvsEncapStt(ovsFwdCtx
->tunnelTxNic
, ovsFwdCtx
->curNbl
,
692 &ovsFwdCtx
->tunKey
, ovsFwdCtx
->switchContext
,
693 &ovsFwdCtx
->layers
, &newNbl
, &switchFwdInfo
);
695 case OVS_VPORT_TYPE_GENEVE
:
696 status
= OvsEncapGeneve(ovsFwdCtx
->tunnelTxNic
, ovsFwdCtx
->curNbl
,
697 &ovsFwdCtx
->tunKey
, ovsFwdCtx
->switchContext
,
698 &ovsFwdCtx
->layers
, &newNbl
, &switchFwdInfo
);
701 ASSERT(! "Tx: Unhandled tunnel type");
704 /* Reset the tunnel context so that it doesn't get used after this point. */
705 OvsClearTunTxCtx(ovsFwdCtx
);
707 if (status
== NDIS_STATUS_SUCCESS
&& switchFwdInfo
.vport
!= NULL
) {
710 * Save the 'srcVportNo', 'srcPortId', 'srcNicIndex' so that
711 * this can be applied to the new NBL later on.
713 srcVportNo
= switchFwdInfo
.vport
->portNo
;
714 srcPortId
= switchFwdInfo
.vport
->portId
;
715 srcNicIndex
= switchFwdInfo
.vport
->nicIndex
;
717 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
718 L
"Complete after cloning NBL for encapsulation");
719 status
= OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
,
720 newNbl
, srcVportNo
, 0,
721 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
722 ovsFwdCtx
->completionList
,
723 &ovsFwdCtx
->layers
, FALSE
);
724 ovsFwdCtx
->curNbl
= newNbl
;
725 /* Update the forwarding detail for the new NBL */
726 ovsFwdCtx
->fwdDetail
->SourcePortId
= srcPortId
;
727 ovsFwdCtx
->fwdDetail
->SourceNicIndex
= srcNicIndex
;
728 status
= OvsDoFlowLookupOutput(ovsFwdCtx
);
729 ASSERT(ovsFwdCtx
->curNbl
== NULL
);
732 * XXX: Temporary freeing of the packet until we register a
733 * callback to IP helper.
735 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
736 L
"OVS-Dropped due to encap failure");
737 ovsActionStats
.failedEncap
++;
738 status
= NDIS_STATUS_SUCCESS
;
745 * --------------------------------------------------------------------------
747 * Decapsulate the incoming NBL based on the tunnel type and goes through
748 * the flow lookup for the inner packet.
750 * Note: IP checksum is validate here, but L4 checksum validation needs
751 * to be done by the corresponding tunnel types.
754 * The NBL in 'ovsFwdCtx' is consumed.
755 * --------------------------------------------------------------------------
757 static __inline NDIS_STATUS
758 OvsTunnelPortRx(OvsForwardingContext
*ovsFwdCtx
)
760 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
761 PNET_BUFFER_LIST newNbl
= NULL
;
762 POVS_VPORT_ENTRY tunnelRxVport
= ovsFwdCtx
->tunnelRxNic
;
763 PCWSTR dropReason
= L
"OVS-dropped due to new decap packet";
765 if (OvsValidateIPChecksum(ovsFwdCtx
->curNbl
, &ovsFwdCtx
->layers
)
766 != NDIS_STATUS_SUCCESS
) {
767 ovsActionStats
.failedChecksum
++;
768 OVS_LOG_INFO("Packet dropped due to IP checksum failure.");
773 * Decap port functions should return a new NBL if it was copied, and
774 * this new NBL should be setup as the ovsFwdCtx->curNbl.
777 switch(tunnelRxVport
->ovsType
) {
778 case OVS_VPORT_TYPE_GRE
:
779 status
= OvsDecapGre(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
780 &ovsFwdCtx
->tunKey
, &newNbl
);
782 case OVS_VPORT_TYPE_VXLAN
:
783 status
= OvsDecapVxlan(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
784 &ovsFwdCtx
->tunKey
, &newNbl
);
786 case OVS_VPORT_TYPE_STT
:
787 status
= OvsDecapStt(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
788 &ovsFwdCtx
->tunKey
, &newNbl
);
789 if (status
== NDIS_STATUS_SUCCESS
&& newNbl
== NULL
) {
790 /* This was an STT-LSO Fragment */
791 dropReason
= L
"OVS-STT segment is cached";
794 case OVS_VPORT_TYPE_GENEVE
:
795 status
= OvsDecapGeneve(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
796 &ovsFwdCtx
->tunKey
, &newNbl
);
799 OVS_LOG_ERROR("Rx: Unhandled tunnel type: %d\n",
800 tunnelRxVport
->ovsType
);
801 ASSERT(! "Rx: Unhandled tunnel type");
802 status
= NDIS_STATUS_NOT_SUPPORTED
;
805 if (status
!= NDIS_STATUS_SUCCESS
) {
806 ovsActionStats
.failedDecap
++;
811 * tunnelRxNic and other fields will be cleared, re-init the context
814 OvsCompleteNBLForwardingCtx(ovsFwdCtx
, dropReason
);
817 /* Decapsulated packet is in a new NBL */
818 ovsFwdCtx
->tunnelRxNic
= tunnelRxVport
;
819 OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
,
820 newNbl
, tunnelRxVport
->portNo
, 0,
821 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
822 ovsFwdCtx
->completionList
,
823 &ovsFwdCtx
->layers
, FALSE
);
826 * Set the NBL's SourcePortId and SourceNicIndex to default values to
827 * keep NDIS happy when we forward the packet.
829 ovsFwdCtx
->fwdDetail
->SourcePortId
= NDIS_SWITCH_DEFAULT_PORT_ID
;
830 ovsFwdCtx
->fwdDetail
->SourceNicIndex
= 0;
832 status
= OvsDoFlowLookupOutput(ovsFwdCtx
);
834 ASSERT(ovsFwdCtx
->curNbl
== NULL
);
835 OvsClearTunRxCtx(ovsFwdCtx
);
840 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
841 L
"OVS-dropped due to decap failure");
842 OvsClearTunRxCtx(ovsFwdCtx
);
848 * --------------------------------------------------------------------------
849 * OvsOutputForwardingCtx --
850 * This function outputs an NBL to NDIS or to a tunneling pipeline based on
851 * the ports added so far into 'ovsFwdCtx'.
854 * This function consumes the NBL - either by forwarding it successfully to
855 * NDIS, or adding it to the completion list in 'ovsFwdCtx', or freeing it.
857 * Also makes sure that the list of destination ports - tunnel or otherwise is
859 * --------------------------------------------------------------------------
861 static __inline NDIS_STATUS
862 OvsOutputForwardingCtx(OvsForwardingContext
*ovsFwdCtx
)
864 NDIS_STATUS status
= STATUS_SUCCESS
;
865 POVS_SWITCH_CONTEXT switchContext
= ovsFwdCtx
->switchContext
;
869 * Handle the case where the some of the destination ports are tunneled
870 * ports - the non-tunneled ports get a unmodified copy of the NBL, and the
871 * tunneling pipeline starts when we output the packet to tunneled port.
873 if (ovsFwdCtx
->destPortsSizeOut
> 0) {
874 PNET_BUFFER_LIST newNbl
= NULL
;
876 UINT32 portsToUpdate
=
877 ovsFwdCtx
->fwdDetail
->NumAvailableDestinations
-
878 (ovsFwdCtx
->destPortsSizeIn
- ovsFwdCtx
->destPortsSizeOut
);
880 ASSERT(ovsFwdCtx
->destinationPorts
!= NULL
);
883 * Create a copy of the packet in order to do encap on it later. Also,
884 * don't copy the offload context since the encap'd packet has a
885 * different set of headers. This will change when we implement offloads
886 * before doing encapsulation.
888 if (ovsFwdCtx
->tunnelTxNic
!= NULL
|| ovsFwdCtx
->tunnelRxNic
!= NULL
) {
889 nb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
890 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
891 0, 0, TRUE
/*copy NBL info*/);
892 if (newNbl
== NULL
) {
893 status
= NDIS_STATUS_RESOURCES
;
894 ovsActionStats
.noCopiedNbl
++;
895 dropReason
= L
"Dropped due to failure to create NBL copy.";
900 /* It does not seem like we'll get here unless 'portsToUpdate' > 0. */
901 ASSERT(portsToUpdate
> 0);
902 status
= switchContext
->NdisSwitchHandlers
.UpdateNetBufferListDestinations(
903 switchContext
->NdisSwitchContext
, ovsFwdCtx
->curNbl
,
904 portsToUpdate
, ovsFwdCtx
->destinationPorts
);
905 if (status
!= NDIS_STATUS_SUCCESS
) {
906 OvsCompleteNBL(ovsFwdCtx
->switchContext
, newNbl
, TRUE
);
907 ovsActionStats
.cannotGrowDest
++;
908 dropReason
= L
"Dropped due to failure to update destinations.";
912 OvsSendNBLIngress(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
913 ovsFwdCtx
->sendFlags
);
914 /* End this pipeline by resetting the corresponding context. */
915 ovsFwdCtx
->destPortsSizeOut
= 0;
916 ovsFwdCtx
->curNbl
= NULL
;
918 status
= OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
,
919 newNbl
, ovsFwdCtx
->srcVportNo
, 0,
920 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
921 ovsFwdCtx
->completionList
,
922 &ovsFwdCtx
->layers
, FALSE
);
923 if (status
!= NDIS_STATUS_SUCCESS
) {
924 dropReason
= L
"Dropped due to resouces.";
930 if (ovsFwdCtx
->tunnelTxNic
!= NULL
) {
931 status
= OvsTunnelPortTx(ovsFwdCtx
);
932 ASSERT(ovsFwdCtx
->tunnelTxNic
== NULL
);
933 ASSERT(ovsFwdCtx
->tunKey
.dst
== 0);
934 } else if (ovsFwdCtx
->tunnelRxNic
!= NULL
) {
935 status
= OvsTunnelPortRx(ovsFwdCtx
);
936 ASSERT(ovsFwdCtx
->tunnelRxNic
== NULL
);
937 ASSERT(ovsFwdCtx
->tunKey
.dst
== 0);
939 ASSERT(ovsFwdCtx
->curNbl
== NULL
);
944 if (status
!= NDIS_STATUS_SUCCESS
) {
945 OvsCompleteNBLForwardingCtx(ovsFwdCtx
, dropReason
);
953 * --------------------------------------------------------------------------
954 * OvsLookupFlowOutput --
955 * Utility function for external callers to do flow extract, lookup,
956 * actions execute on a given NBL.
958 * Note: If this is being used from a callback function, make sure that the
959 * arguments specified are still valid in the asynchronous context.
962 * This function consumes the NBL.
963 * --------------------------------------------------------------------------
966 OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext
,
968 PNET_BUFFER_LIST curNbl
,
969 POVS_VPORT_ENTRY internalVport
)
972 OvsForwardingContext ovsFwdCtx
;
974 /* XXX: make sure comp list was not a stack variable previously. */
975 OvsCompletionList
*completionList
= (OvsCompletionList
*)compList
;
978 * XXX: can internal port disappear while we are busy doing ARP resolution?
979 * It could, but will we get this callback from IP helper in that case. Need
982 ASSERT(switchContext
->countInternalVports
> 0);
983 status
= OvsInitForwardingCtx(&ovsFwdCtx
, switchContext
, curNbl
,
984 internalVport
->portNo
, 0,
985 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl
),
986 completionList
, NULL
, TRUE
);
987 if (status
!= NDIS_STATUS_SUCCESS
) {
988 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
,
989 L
"OVS-Dropped due to resources");
995 * XXX: We need to acquire the dispatch lock and the datapath lock.
998 OvsDoFlowLookupOutput(&ovsFwdCtx
);
1003 * --------------------------------------------------------------------------
1004 * OvsOutputBeforeSetAction --
1005 * Function to be called to complete one set of actions on an NBL, before
1006 * we start the next one.
1007 * --------------------------------------------------------------------------
1009 static __inline NDIS_STATUS
1010 OvsOutputBeforeSetAction(OvsForwardingContext
*ovsFwdCtx
)
1012 PNET_BUFFER_LIST newNbl
;
1016 * Create a copy and work on the copy after this point. The original NBL is
1017 * forwarded. One reason to not use the copy for forwarding is that
1018 * ports have already been added to the original NBL, and it might be
1019 * inefficient/impossible to remove/re-add them to the copy. There's no
1020 * notion of removing the ports, the ports need to be marked as
1021 * "isExcluded". There's seems no real advantage to retaining the original
1022 * and sending out the copy instead.
1024 * XXX: We are copying the offload context here. This is to handle actions
1026 * outport, pop_vlan(), outport, push_vlan(), outport
1028 * copy size needs to include inner ether + IP + TCP, need to revisit
1029 * if we support IP options.
1030 * XXX Head room needs to include the additional encap.
1031 * XXX copySize check is not considering multiple NBs.
1033 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1034 0, 0, TRUE
/*copy NBL info*/);
1036 ASSERT(ovsFwdCtx
->destPortsSizeOut
> 0 ||
1037 ovsFwdCtx
->tunnelTxNic
!= NULL
|| ovsFwdCtx
->tunnelRxNic
!= NULL
);
1039 /* Send the original packet out and save the original source port number */
1040 UINT32 tempVportNo
= ovsFwdCtx
->srcVportNo
;
1041 status
= OvsOutputForwardingCtx(ovsFwdCtx
);
1042 ASSERT(ovsFwdCtx
->curNbl
== NULL
);
1043 ASSERT(ovsFwdCtx
->destPortsSizeOut
== 0);
1044 ASSERT(ovsFwdCtx
->tunnelRxNic
== NULL
);
1045 ASSERT(ovsFwdCtx
->tunnelTxNic
== NULL
);
1047 /* If we didn't make a copy, can't continue. */
1048 if (newNbl
== NULL
) {
1049 ovsActionStats
.noCopiedNbl
++;
1050 return NDIS_STATUS_RESOURCES
;
1053 /* Finish the remaining actions with the new NBL */
1054 if (status
!= NDIS_STATUS_SUCCESS
) {
1055 OvsCompleteNBL(ovsFwdCtx
->switchContext
, newNbl
, TRUE
);
1057 status
= OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
,
1058 newNbl
, tempVportNo
, 0,
1059 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
1060 ovsFwdCtx
->completionList
,
1061 &ovsFwdCtx
->layers
, FALSE
);
1069 * --------------------------------------------------------------------------
1070 * OvsPopFieldInPacketBuf --
1071 * Function to pop a specified field of length 'shiftLength' located at
1072 * 'shiftOffset' from the Ethernet header. The data on the left of the
1073 * 'shiftOffset' is right shifted.
1075 * Returns a pointer to the new start in 'bufferData'.
1076 * --------------------------------------------------------------------------
1078 static __inline NDIS_STATUS
1079 OvsPopFieldInPacketBuf(OvsForwardingContext
*ovsFwdCtx
,
1087 UINT32 packetLen
, mdlLen
;
1088 PNET_BUFFER_LIST newNbl
;
1091 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1092 0, 0, TRUE
/* copy NBL info */);
1094 ovsActionStats
.noCopiedNbl
++;
1095 return NDIS_STATUS_RESOURCES
;
1098 /* Complete the original NBL and create a copy to modify. */
1099 OvsCompleteNBLForwardingCtx(ovsFwdCtx
, L
"OVS-Dropped due to copy");
1101 status
= OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
, newNbl
,
1102 ovsFwdCtx
->srcVportNo
, 0,
1103 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
1104 NULL
, &ovsFwdCtx
->layers
, FALSE
);
1105 if (status
!= NDIS_STATUS_SUCCESS
) {
1106 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
1107 L
"Dropped due to resouces");
1108 return NDIS_STATUS_RESOURCES
;
1111 curNb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
1112 packetLen
= NET_BUFFER_DATA_LENGTH(curNb
);
1113 ASSERT(curNb
->Next
== NULL
);
1114 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
1115 NdisQueryMdl(curMdl
, &bufferStart
, &mdlLen
, LowPagePriority
);
1117 return NDIS_STATUS_RESOURCES
;
1119 mdlLen
-= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1120 /* Bail out if L2 + shiftLength is not contiguous in the first buffer. */
1121 if (MIN(packetLen
, mdlLen
) < sizeof(EthHdr
) + shiftLength
) {
1123 return NDIS_STATUS_FAILURE
;
1125 bufferStart
+= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1126 /* XXX At the momemnt !bufferData means it should be treated as VLAN. We
1127 * should split the function and refactor. */
1129 EthHdr
*ethHdr
= (EthHdr
*)bufferStart
;
1130 /* If the frame is not VLAN make it a no op */
1131 if (ethHdr
->Type
!= ETH_TYPE_802_1PQ_NBO
) {
1132 return NDIS_STATUS_SUCCESS
;
1135 RtlMoveMemory(bufferStart
+ shiftLength
, bufferStart
, shiftOffset
);
1136 NdisAdvanceNetBufferDataStart(curNb
, shiftLength
, FALSE
, NULL
);
1139 *bufferData
= bufferStart
+ shiftLength
;
1142 return NDIS_STATUS_SUCCESS
;
1147 * --------------------------------------------------------------------------
1148 * OvsPopVlanInPktBuf --
1149 * Function to pop a VLAN tag when the tag is in the packet buffer.
1150 * --------------------------------------------------------------------------
1152 static __inline NDIS_STATUS
1153 OvsPopVlanInPktBuf(OvsForwardingContext
*ovsFwdCtx
)
1156 * Declare a dummy vlanTag structure since we need to compute the size
1157 * of shiftLength. The NDIS one is a unionized structure.
1159 NDIS_PACKET_8021Q_INFO vlanTag
= {0};
1160 UINT32 shiftLength
= sizeof(vlanTag
.TagHeader
);
1161 UINT32 shiftOffset
= sizeof(DL_EUI48
) + sizeof(DL_EUI48
);
1163 return OvsPopFieldInPacketBuf(ovsFwdCtx
, shiftOffset
, shiftLength
, NULL
);
1168 * --------------------------------------------------------------------------
1169 * OvsActionMplsPop --
1170 * Function to pop the first MPLS label from the current packet.
1171 * --------------------------------------------------------------------------
1173 static __inline NDIS_STATUS
1174 OvsActionMplsPop(OvsForwardingContext
*ovsFwdCtx
,
1178 OVS_PACKET_HDR_INFO
*layers
= &ovsFwdCtx
->layers
;
1179 EthHdr
*ethHdr
= NULL
;
1181 status
= OvsPopFieldInPacketBuf(ovsFwdCtx
, sizeof(*ethHdr
),
1182 MPLS_HLEN
, (PUINT8
*)ðHdr
);
1183 if (status
== NDIS_STATUS_SUCCESS
) {
1184 if (ethHdr
&& OvsEthertypeIsMpls(ethHdr
->Type
)) {
1185 ethHdr
->Type
= ethertype
;
1188 layers
->l3Offset
-= MPLS_HLEN
;
1189 layers
->l4Offset
-= MPLS_HLEN
;
1197 * --------------------------------------------------------------------------
1198 * OvsActionMplsPush --
1199 * Function to push the MPLS label into the current packet.
1200 * --------------------------------------------------------------------------
1202 static __inline NDIS_STATUS
1203 OvsActionMplsPush(OvsForwardingContext
*ovsFwdCtx
,
1204 const struct ovs_action_push_mpls
*mpls
)
1207 PNET_BUFFER curNb
= NULL
;
1209 PUINT8 bufferStart
= NULL
;
1210 OVS_PACKET_HDR_INFO
*layers
= &ovsFwdCtx
->layers
;
1211 EthHdr
*ethHdr
= NULL
;
1212 MPLSHdr
*mplsHdr
= NULL
;
1213 UINT32 mdlLen
= 0, curMdlOffset
= 0;
1214 PNET_BUFFER_LIST newNbl
;
1216 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1217 layers
->l3Offset
, MPLS_HLEN
, TRUE
);
1219 ovsActionStats
.noCopiedNbl
++;
1220 return NDIS_STATUS_RESOURCES
;
1222 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
1223 L
"Complete after partial copy.");
1225 status
= OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
,
1226 newNbl
, ovsFwdCtx
->srcVportNo
, 0,
1227 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
1228 NULL
, &ovsFwdCtx
->layers
, FALSE
);
1229 if (status
!= NDIS_STATUS_SUCCESS
) {
1230 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
1231 L
"OVS-Dropped due to resources");
1232 return NDIS_STATUS_RESOURCES
;
1235 curNb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
1236 ASSERT(curNb
->Next
== NULL
);
1238 status
= NdisRetreatNetBufferDataStart(curNb
, MPLS_HLEN
, 0, NULL
);
1239 if (status
!= NDIS_STATUS_SUCCESS
) {
1243 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
1244 NdisQueryMdl(curMdl
, &bufferStart
, &mdlLen
, LowPagePriority
);
1246 ovsActionStats
.noResource
++;
1247 return NDIS_STATUS_RESOURCES
;
1250 curMdlOffset
= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1251 mdlLen
-= curMdlOffset
;
1252 ASSERT(mdlLen
>= MPLS_HLEN
);
1254 ethHdr
= (EthHdr
*)(bufferStart
+ curMdlOffset
);
1255 RtlMoveMemory(ethHdr
, (UINT8
*)ethHdr
+ MPLS_HLEN
, sizeof(*ethHdr
));
1256 ethHdr
->Type
= mpls
->mpls_ethertype
;
1258 mplsHdr
= (MPLSHdr
*)(ethHdr
+ 1);
1259 mplsHdr
->lse
= mpls
->mpls_lse
;
1261 layers
->l3Offset
+= MPLS_HLEN
;
1262 layers
->l4Offset
+= MPLS_HLEN
;
1264 return NDIS_STATUS_SUCCESS
;
1268 *----------------------------------------------------------------------------
1269 * OvsUpdateEthHeader --
1270 * Updates the ethernet header in ovsFwdCtx.curNbl inline based on the
1272 *----------------------------------------------------------------------------
1274 static __inline NDIS_STATUS
1275 OvsUpdateEthHeader(OvsForwardingContext
*ovsFwdCtx
,
1276 const struct ovs_key_ethernet
*ethAttr
)
1282 UINT32 packetLen
, mdlLen
;
1284 curNb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
1285 ASSERT(curNb
->Next
== NULL
);
1286 packetLen
= NET_BUFFER_DATA_LENGTH(curNb
);
1287 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
1288 NdisQueryMdl(curMdl
, &bufferStart
, &mdlLen
, LowPagePriority
);
1290 ovsActionStats
.noResource
++;
1291 return NDIS_STATUS_RESOURCES
;
1293 mdlLen
-= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1295 /* Bail out if the L2 header is not in a contiguous buffer. */
1296 if (MIN(packetLen
, mdlLen
) < sizeof *ethHdr
) {
1298 return NDIS_STATUS_FAILURE
;
1300 ethHdr
= (EthHdr
*)(bufferStart
+ NET_BUFFER_CURRENT_MDL_OFFSET(curNb
));
1302 RtlCopyMemory(ethHdr
->Destination
, ethAttr
->eth_dst
,
1303 sizeof ethHdr
->Destination
);
1304 RtlCopyMemory(ethHdr
->Source
, ethAttr
->eth_src
, sizeof ethHdr
->Source
);
1306 return NDIS_STATUS_SUCCESS
;
1310 *----------------------------------------------------------------------------
1311 * OvsGetHeaderBySize --
1312 * Tries to retrieve a continuous buffer from 'ovsFwdCtx->curnbl' of size
1314 * If the original buffer is insufficient it will, try to clone the net
1315 * buffer list and force the size.
1316 * Returns 'NULL' on failure or a pointer to the first byte of the data
1317 * in the first net buffer of the net buffer list 'nbl'.
1318 *----------------------------------------------------------------------------
1320 PUINT8
OvsGetHeaderBySize(OvsForwardingContext
*ovsFwdCtx
,
1324 UINT32 mdlLen
, packetLen
;
1329 curNb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
1330 ASSERT(curNb
->Next
== NULL
);
1331 packetLen
= NET_BUFFER_DATA_LENGTH(curNb
);
1332 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
1333 NdisQueryMdl(curMdl
, &start
, &mdlLen
, LowPagePriority
);
1335 ovsActionStats
.noResource
++;
1339 curMdlOffset
= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1340 mdlLen
-= curMdlOffset
;
1341 ASSERT((INT
)mdlLen
>= 0);
1343 /* Count of number of bytes of valid data there are in the first MDL. */
1344 mdlLen
= MIN(packetLen
, mdlLen
);
1345 if (mdlLen
< size
) {
1346 PNET_BUFFER_LIST newNbl
;
1348 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1349 size
, 0, TRUE
/*copy NBL info*/);
1351 ovsActionStats
.noCopiedNbl
++;
1354 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
1355 L
"Complete after partial copy.");
1357 status
= OvsInitForwardingCtx(ovsFwdCtx
, ovsFwdCtx
->switchContext
,
1358 newNbl
, ovsFwdCtx
->srcVportNo
, 0,
1359 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl
),
1360 NULL
, &ovsFwdCtx
->layers
, FALSE
);
1362 if (status
!= NDIS_STATUS_SUCCESS
) {
1363 OvsCompleteNBLForwardingCtx(ovsFwdCtx
,
1364 L
"OVS-Dropped due to resources");
1368 curNb
= NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
);
1369 ASSERT(curNb
->Next
== NULL
);
1370 curMdl
= NET_BUFFER_CURRENT_MDL(curNb
);
1371 NdisQueryMdl(curMdl
, &start
, &mdlLen
, LowPagePriority
);
1373 ovsActionStats
.noResource
++;
1376 curMdlOffset
= NET_BUFFER_CURRENT_MDL_OFFSET(curNb
);
1377 mdlLen
-= curMdlOffset
;
1378 ASSERT(mdlLen
>= size
);
1381 return start
+ curMdlOffset
;
1385 *----------------------------------------------------------------------------
1386 * OvsUpdateUdpPorts --
1387 * Updates the UDP source or destination port in ovsFwdCtx.curNbl inline
1388 * based on the specified key.
1389 *----------------------------------------------------------------------------
1391 static __inline NDIS_STATUS
1392 OvsUpdateUdpPorts(OvsForwardingContext
*ovsFwdCtx
,
1393 const struct ovs_key_udp
*udpAttr
)
1396 OVS_PACKET_HDR_INFO
*layers
= &ovsFwdCtx
->layers
;
1397 UDPHdr
*udpHdr
= NULL
;
1399 ASSERT(layers
->value
!= 0);
1401 if (!layers
->isUdp
) {
1402 ovsActionStats
.noCopiedNbl
++;
1403 return NDIS_STATUS_FAILURE
;
1406 bufferStart
= OvsGetHeaderBySize(ovsFwdCtx
, layers
->l7Offset
);
1408 return NDIS_STATUS_RESOURCES
;
1411 udpHdr
= (UDPHdr
*)(bufferStart
+ layers
->l4Offset
);
1412 if (udpHdr
->check
) {
1413 if (udpHdr
->source
!= udpAttr
->udp_src
) {
1414 udpHdr
->check
= ChecksumUpdate16(udpHdr
->check
, udpHdr
->source
,
1416 udpHdr
->source
= udpAttr
->udp_src
;
1418 if (udpHdr
->dest
!= udpAttr
->udp_dst
) {
1419 udpHdr
->check
= ChecksumUpdate16(udpHdr
->check
, udpHdr
->dest
,
1421 udpHdr
->dest
= udpAttr
->udp_dst
;
1424 udpHdr
->source
= udpAttr
->udp_src
;
1425 udpHdr
->dest
= udpAttr
->udp_dst
;
1428 return NDIS_STATUS_SUCCESS
;
1432 *----------------------------------------------------------------------------
1433 * OvsUpdateTcpPorts --
1434 * Updates the TCP source or destination port in ovsFwdCtx.curNbl inline
1435 * based on the specified key.
1436 *----------------------------------------------------------------------------
1438 static __inline NDIS_STATUS
1439 OvsUpdateTcpPorts(OvsForwardingContext
*ovsFwdCtx
,
1440 const struct ovs_key_tcp
*tcpAttr
)
1443 OVS_PACKET_HDR_INFO
*layers
= &ovsFwdCtx
->layers
;
1444 TCPHdr
*tcpHdr
= NULL
;
1446 ASSERT(layers
->value
!= 0);
1448 if (!layers
->isTcp
) {
1449 ovsActionStats
.noCopiedNbl
++;
1450 return NDIS_STATUS_FAILURE
;
1453 bufferStart
= OvsGetHeaderBySize(ovsFwdCtx
, layers
->l7Offset
);
1455 return NDIS_STATUS_RESOURCES
;
1458 tcpHdr
= (TCPHdr
*)(bufferStart
+ layers
->l4Offset
);
1460 if (tcpHdr
->source
!= tcpAttr
->tcp_src
) {
1461 tcpHdr
->check
= ChecksumUpdate16(tcpHdr
->check
, tcpHdr
->source
,
1463 tcpHdr
->source
= tcpAttr
->tcp_src
;
1465 if (tcpHdr
->dest
!= tcpAttr
->tcp_dst
) {
1466 tcpHdr
->check
= ChecksumUpdate16(tcpHdr
->check
, tcpHdr
->dest
,
1468 tcpHdr
->dest
= tcpAttr
->tcp_dst
;
1471 return NDIS_STATUS_SUCCESS
;
1475 *----------------------------------------------------------------------------
1476 * OvsUpdateIPv4Header --
1477 * Updates the IPv4 header in ovsFwdCtx.curNbl inline based on the
1479 *----------------------------------------------------------------------------
1481 static __inline NDIS_STATUS
1482 OvsUpdateIPv4Header(OvsForwardingContext
*ovsFwdCtx
,
1483 const struct ovs_key_ipv4
*ipAttr
)
1487 OVS_PACKET_HDR_INFO
*layers
= &ovsFwdCtx
->layers
;
1489 TCPHdr
*tcpHdr
= NULL
;
1490 UDPHdr
*udpHdr
= NULL
;
1492 ASSERT(layers
->value
!= 0);
1494 if (layers
->isTcp
|| layers
->isUdp
) {
1495 hdrSize
= layers
->l4Offset
+
1496 layers
->isTcp
? sizeof (*tcpHdr
) : sizeof (*udpHdr
);
1498 hdrSize
= layers
->l3Offset
+ sizeof (*ipHdr
);
1501 bufferStart
= OvsGetHeaderBySize(ovsFwdCtx
, hdrSize
);
1503 return NDIS_STATUS_RESOURCES
;
1506 ipHdr
= (IPHdr
*)(bufferStart
+ layers
->l3Offset
);
1508 if (layers
->isTcp
) {
1509 tcpHdr
= (TCPHdr
*)(bufferStart
+ layers
->l4Offset
);
1510 } else if (layers
->isUdp
) {
1511 udpHdr
= (UDPHdr
*)(bufferStart
+ layers
->l4Offset
);
1515 * Adjust the IP header inline as dictated by the action, and also update
1516 * the IP and the TCP checksum for the data modified.
1518 * In the future, this could be optimized to make one call to
1519 * ChecksumUpdate32(). Ignoring this for now, since for the most common
1520 * case, we only update the TTL.
1522 if (ipHdr
->saddr
!= ipAttr
->ipv4_src
) {
1524 tcpHdr
->check
= ChecksumUpdate32(tcpHdr
->check
, ipHdr
->saddr
,
1526 } else if (udpHdr
&& udpHdr
->check
) {
1527 udpHdr
->check
= ChecksumUpdate32(udpHdr
->check
, ipHdr
->saddr
,
1531 if (ipHdr
->check
!= 0) {
1532 ipHdr
->check
= ChecksumUpdate32(ipHdr
->check
, ipHdr
->saddr
,
1535 ipHdr
->saddr
= ipAttr
->ipv4_src
;
1537 if (ipHdr
->daddr
!= ipAttr
->ipv4_dst
) {
1539 tcpHdr
->check
= ChecksumUpdate32(tcpHdr
->check
, ipHdr
->daddr
,
1541 } else if (udpHdr
&& udpHdr
->check
) {
1542 udpHdr
->check
= ChecksumUpdate32(udpHdr
->check
, ipHdr
->daddr
,
1546 if (ipHdr
->check
!= 0) {
1547 ipHdr
->check
= ChecksumUpdate32(ipHdr
->check
, ipHdr
->daddr
,
1550 ipHdr
->daddr
= ipAttr
->ipv4_dst
;
1552 if (ipHdr
->protocol
!= ipAttr
->ipv4_proto
) {
1553 UINT16 oldProto
= (ipHdr
->protocol
<< 16) & 0xff00;
1554 UINT16 newProto
= (ipAttr
->ipv4_proto
<< 16) & 0xff00;
1556 tcpHdr
->check
= ChecksumUpdate16(tcpHdr
->check
, oldProto
, newProto
);
1557 } else if (udpHdr
&& udpHdr
->check
) {
1558 udpHdr
->check
= ChecksumUpdate16(udpHdr
->check
, oldProto
, newProto
);
1561 if (ipHdr
->check
!= 0) {
1562 ipHdr
->check
= ChecksumUpdate16(ipHdr
->check
, oldProto
, newProto
);
1564 ipHdr
->protocol
= ipAttr
->ipv4_proto
;
1566 if (ipHdr
->ttl
!= ipAttr
->ipv4_ttl
) {
1567 UINT16 oldTtl
= (ipHdr
->ttl
) & 0xff;
1568 UINT16 newTtl
= (ipAttr
->ipv4_ttl
) & 0xff;
1569 if (ipHdr
->check
!= 0) {
1570 ipHdr
->check
= ChecksumUpdate16(ipHdr
->check
, oldTtl
, newTtl
);
1572 ipHdr
->ttl
= ipAttr
->ipv4_ttl
;
1575 return NDIS_STATUS_SUCCESS
;
1579 * --------------------------------------------------------------------------
1580 * OvsExecuteSetAction --
1581 * Executes a set() action, but storing the actions into 'ovsFwdCtx'
1582 * --------------------------------------------------------------------------
1584 static __inline NDIS_STATUS
1585 OvsExecuteSetAction(OvsForwardingContext
*ovsFwdCtx
,
1590 enum ovs_key_attr type
= NlAttrType(a
);
1591 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
1594 case OVS_KEY_ATTR_ETHERNET
:
1595 status
= OvsUpdateEthHeader(ovsFwdCtx
,
1596 NlAttrGetUnspec(a
, sizeof(struct ovs_key_ethernet
)));
1599 case OVS_KEY_ATTR_IPV4
:
1600 status
= OvsUpdateIPv4Header(ovsFwdCtx
,
1601 NlAttrGetUnspec(a
, sizeof(struct ovs_key_ipv4
)));
1604 case OVS_KEY_ATTR_TUNNEL
:
1606 OvsIPv4TunnelKey tunKey
;
1607 NTSTATUS convertStatus
= OvsTunnelAttrToIPv4TunnelKey((PNL_ATTR
)a
, &tunKey
);
1608 status
= SUCCEEDED(convertStatus
) ? NDIS_STATUS_SUCCESS
: NDIS_STATUS_FAILURE
;
1609 ASSERT(status
== NDIS_STATUS_SUCCESS
);
1610 tunKey
.flow_hash
= (uint16
)(hash
? *hash
: OvsHashFlow(key
));
1611 tunKey
.dst_port
= key
->ipKey
.l4
.tpDst
;
1612 RtlCopyMemory(&ovsFwdCtx
->tunKey
, &tunKey
, sizeof ovsFwdCtx
->tunKey
);
1616 case OVS_KEY_ATTR_UDP
:
1617 status
= OvsUpdateUdpPorts(ovsFwdCtx
,
1618 NlAttrGetUnspec(a
, sizeof(struct ovs_key_udp
)));
1621 case OVS_KEY_ATTR_TCP
:
1622 status
= OvsUpdateTcpPorts(ovsFwdCtx
,
1623 NlAttrGetUnspec(a
, sizeof(struct ovs_key_tcp
)));
1627 OVS_LOG_INFO("Unhandled attribute %#x", type
);
1634 * --------------------------------------------------------------------------
1635 * OvsExecuteRecirc --
1636 * The function adds a deferred action to allow the current packet, nbl,
1637 * to re-enter datapath packet processing.
1638 * --------------------------------------------------------------------------
1641 OvsExecuteRecirc(OvsForwardingContext
*ovsFwdCtx
,
1643 const PNL_ATTR actions
,
1646 POVS_DEFERRED_ACTION deferredAction
= NULL
;
1647 PNET_BUFFER_LIST newNbl
= NULL
;
1649 if (!NlAttrIsLast(actions
, rem
)) {
1651 * Recirc action is the not the last action of the action list, so we
1652 * need to clone the packet.
1654 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1655 0, 0, TRUE
/*copy NBL info*/);
1657 * Skip the recirc action when out of memory, but continue on with the
1658 * rest of the action list.
1660 if (newNbl
== NULL
) {
1661 ovsActionStats
.noCopiedNbl
++;
1662 return NDIS_STATUS_SUCCESS
;
1667 deferredAction
= OvsAddDeferredActions(newNbl
, key
, NULL
);
1669 deferredAction
= OvsAddDeferredActions(ovsFwdCtx
->curNbl
, key
, NULL
);
1672 if (deferredAction
) {
1673 deferredAction
->key
.recircId
= NlAttrGetU32(actions
);
1676 ovsActionStats
.deferredActionsQueueFull
++;
1677 OvsCompleteNBL(ovsFwdCtx
->switchContext
, newNbl
, TRUE
);
1681 return NDIS_STATUS_SUCCESS
;
1685 * --------------------------------------------------------------------------
1687 * The function updates datapath hash read from userspace.
1688 * --------------------------------------------------------------------------
1691 OvsExecuteHash(OvsFlowKey
*key
,
1692 const PNL_ATTR attr
)
1694 struct ovs_action_hash
*hash_act
= NlAttrData(attr
);
1697 hash
= (UINT32
)OvsHashFlow(key
);
1698 hash
= OvsJhashWords(&hash
, 1, hash_act
->hash_basis
);
1706 * --------------------------------------------------------------------------
1707 * OvsOutputUserspaceAction --
1708 * This function sends the packet to userspace according to nested
1709 * %OVS_USERSPACE_ATTR_* attributes.
1710 * --------------------------------------------------------------------------
1712 static __inline NDIS_STATUS
1713 OvsOutputUserspaceAction(OvsForwardingContext
*ovsFwdCtx
,
1715 const PNL_ATTR attr
)
1717 NTSTATUS status
= NDIS_STATUS_SUCCESS
;
1718 PNL_ATTR userdataAttr
;
1720 POVS_PACKET_QUEUE_ELEM elem
;
1721 POVS_PACKET_HDR_INFO layers
= &ovsFwdCtx
->layers
;
1722 BOOLEAN isRecv
= FALSE
;
1724 POVS_VPORT_ENTRY vport
= OvsFindVportByPortNo(ovsFwdCtx
->switchContext
,
1725 ovsFwdCtx
->srcVportNo
);
1728 if (vport
->isExternal
||
1729 OvsIsTunnelVportType(vport
->ovsType
)) {
1734 queueAttr
= NlAttrFindNested(attr
, OVS_USERSPACE_ATTR_PID
);
1735 userdataAttr
= NlAttrFindNested(attr
, OVS_USERSPACE_ATTR_USERDATA
);
1737 elem
= OvsCreateQueueNlPacket(NlAttrData(userdataAttr
),
1738 NlAttrGetSize(userdataAttr
),
1739 OVS_PACKET_CMD_ACTION
,
1740 vport
, key
, ovsFwdCtx
->curNbl
,
1741 NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx
->curNbl
),
1745 LIST_ENTRY missedPackets
;
1746 InitializeListHead(&missedPackets
);
1747 InsertTailList(&missedPackets
, &elem
->link
);
1748 OvsQueuePackets(&missedPackets
, 1);
1750 status
= NDIS_STATUS_FAILURE
;
1757 * --------------------------------------------------------------------------
1758 * OvsExecuteSampleAction --
1759 * Executes actions based on probability, as specified in the nested
1760 * %OVS_SAMPLE_ATTR_* attributes.
1761 * --------------------------------------------------------------------------
1763 static __inline NDIS_STATUS
1764 OvsExecuteSampleAction(OvsForwardingContext
*ovsFwdCtx
,
1766 const PNL_ATTR attr
)
1768 PNET_BUFFER_LIST newNbl
= NULL
;
1769 PNL_ATTR actionsList
= NULL
;
1774 NL_ATTR_FOR_EACH_UNSAFE(a
, rem
, NlAttrData(attr
), NlAttrGetSize(attr
)) {
1775 switch (NlAttrType(a
)) {
1776 case OVS_SAMPLE_ATTR_PROBABILITY
:
1778 UINT32 probability
= NlAttrGetU32(a
);
1780 if (!probability
|| Rand() > probability
) {
1785 case OVS_SAMPLE_ATTR_ACTIONS
:
1792 rem
= NlAttrGetSize(actionsList
);
1793 a
= (PNL_ATTR
)NlAttrData(actionsList
);
1797 /* Actions list is empty, do nothing */
1798 return STATUS_SUCCESS
;
1802 * The only known usage of sample action is having a single user-space
1803 * action. Treat this usage as a special case.
1805 if (NlAttrType(a
) == OVS_ACTION_ATTR_USERSPACE
&&
1806 NlAttrIsLast(a
, rem
)) {
1807 return OvsOutputUserspaceAction(ovsFwdCtx
, key
, a
);
1810 newNbl
= OvsPartialCopyNBL(ovsFwdCtx
->switchContext
, ovsFwdCtx
->curNbl
,
1811 0, 0, TRUE
/*copy NBL info*/);
1812 if (newNbl
== NULL
) {
1814 * Skip the sample action when out of memory, but continue on with the
1815 * rest of the action list.
1817 ovsActionStats
.noCopiedNbl
++;
1818 return STATUS_SUCCESS
;
1821 if (!OvsAddDeferredActions(newNbl
, key
, a
)) {
1823 "Deferred actions limit reached, dropping sample action.");
1824 OvsCompleteNBL(ovsFwdCtx
->switchContext
, newNbl
, TRUE
);
1827 return STATUS_SUCCESS
;
1831 * --------------------------------------------------------------------------
1832 * OvsDoExecuteActions --
1833 * Interpret and execute the specified 'actions' on the specified packet
1834 * 'curNbl'. The expectation is that if the packet needs to be dropped
1835 * (completed) for some reason, it is added to 'completionList' so that the
1836 * caller can complete the packet. If 'completionList' is NULL, the NBL is
1837 * assumed to be generated by OVS and freed up. Otherwise, the function
1838 * consumes the NBL by generating a NDIS send indication for the packet.
1840 * There are one or more of "clone" NBLs that may get generated while
1841 * executing the actions. Upon any failures, the "cloned" NBLs are freed up,
1842 * and the caller does not have to worry about them.
1844 * Success or failure is returned based on whether the specified actions
1845 * were executed successfully on the packet or not.
1846 * --------------------------------------------------------------------------
1849 OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext
,
1850 OvsCompletionList
*completionList
,
1851 PNET_BUFFER_LIST curNbl
,
1856 OVS_PACKET_HDR_INFO
*layers
,
1857 const PNL_ATTR actions
,
1863 OvsForwardingContext ovsFwdCtx
;
1864 PCWSTR dropReason
= L
"";
1866 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail
=
1867 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl
);
1869 /* XXX: ASSERT that the flow table lock is held. */
1870 status
= OvsInitForwardingCtx(&ovsFwdCtx
, switchContext
, curNbl
, portNo
,
1871 sendFlags
, fwdDetail
, completionList
,
1873 if (status
!= NDIS_STATUS_SUCCESS
) {
1874 dropReason
= L
"OVS-initing destination port list failed";
1878 if (actionsLen
== 0) {
1879 dropReason
= L
"OVS-Dropped due to Flow action";
1880 ovsActionStats
.zeroActionLen
++;
1884 NL_ATTR_FOR_EACH_UNSAFE (a
, rem
, actions
, actionsLen
) {
1885 switch(NlAttrType(a
)) {
1886 case OVS_ACTION_ATTR_OUTPUT
:
1887 dstPortID
= NlAttrGetU32(a
);
1888 status
= OvsAddPorts(&ovsFwdCtx
, key
, dstPortID
,
1890 if (status
!= NDIS_STATUS_SUCCESS
) {
1891 dropReason
= L
"OVS-adding destination port failed";
1896 case OVS_ACTION_ATTR_PUSH_VLAN
:
1898 struct ovs_action_push_vlan
*vlan
;
1900 PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTag
;
1902 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
1903 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1904 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1905 if (status
!= NDIS_STATUS_SUCCESS
) {
1906 dropReason
= L
"OVS-adding destination failed";
1911 vlanTagValue
= NET_BUFFER_LIST_INFO(ovsFwdCtx
.curNbl
,
1912 Ieee8021QNetBufferListInfo
);
1913 if (vlanTagValue
!= NULL
) {
1915 * XXX: We don't support double VLAN tag offload. In such cases,
1916 * we need to insert the existing one into the packet buffer,
1917 * and add the new one as offload. This will take care of
1918 * guest tag-in-tag case as well as OVS rules that specify
1923 vlanTag
= (PNDIS_NET_BUFFER_LIST_8021Q_INFO
)(PVOID
*)&vlanTagValue
;
1924 vlan
= (struct ovs_action_push_vlan
*)NlAttrGet((const PNL_ATTR
)a
);
1925 vlanTag
->TagHeader
.VlanId
= ntohs(vlan
->vlan_tci
) & 0xfff;
1926 vlanTag
->TagHeader
.UserPriority
= ntohs(vlan
->vlan_tci
) >> 13;
1928 NET_BUFFER_LIST_INFO(ovsFwdCtx
.curNbl
,
1929 Ieee8021QNetBufferListInfo
) = vlanTagValue
;
1934 case OVS_ACTION_ATTR_POP_VLAN
:
1936 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
1937 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1938 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1939 if (status
!= NDIS_STATUS_SUCCESS
) {
1940 dropReason
= L
"OVS-adding destination failed";
1945 if (NET_BUFFER_LIST_INFO(ovsFwdCtx
.curNbl
,
1946 Ieee8021QNetBufferListInfo
) != 0) {
1947 NET_BUFFER_LIST_INFO(ovsFwdCtx
.curNbl
,
1948 Ieee8021QNetBufferListInfo
) = 0;
1951 * The VLAN tag is inserted into the packet buffer. Pop the tag
1952 * by packet buffer modification.
1954 status
= OvsPopVlanInPktBuf(&ovsFwdCtx
);
1955 if (status
!= NDIS_STATUS_SUCCESS
) {
1956 dropReason
= L
"OVS-pop vlan action failed";
1963 case OVS_ACTION_ATTR_PUSH_MPLS
:
1965 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
1966 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1967 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1968 if (status
!= NDIS_STATUS_SUCCESS
) {
1969 dropReason
= L
"OVS-adding destination failed";
1974 status
= OvsActionMplsPush(&ovsFwdCtx
,
1975 (struct ovs_action_push_mpls
*)NlAttrGet
1976 ((const PNL_ATTR
)a
));
1977 if (status
!= NDIS_STATUS_SUCCESS
) {
1978 dropReason
= L
"OVS-push MPLS action failed";
1981 layers
->l3Offset
+= MPLS_HLEN
;
1982 layers
->l4Offset
+= MPLS_HLEN
;
1986 case OVS_ACTION_ATTR_POP_MPLS
:
1988 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
1989 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
1990 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
1991 if (status
!= NDIS_STATUS_SUCCESS
) {
1992 dropReason
= L
"OVS-adding destination failed";
1997 status
= OvsActionMplsPop(&ovsFwdCtx
, NlAttrGetBe16(a
));
1998 if (status
!= NDIS_STATUS_SUCCESS
) {
1999 dropReason
= L
"OVS-pop MPLS action failed";
2002 layers
->l3Offset
-= MPLS_HLEN
;
2003 layers
->l4Offset
-= MPLS_HLEN
;
2007 case OVS_ACTION_ATTR_HASH
:
2009 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
2010 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
2011 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
2012 if (status
!= NDIS_STATUS_SUCCESS
) {
2013 dropReason
= L
"OVS-adding destination failed";
2018 OvsExecuteHash(key
, (const PNL_ATTR
)a
);
2023 case OVS_ACTION_ATTR_CT
:
2025 if (ovsFwdCtx
.destPortsSizeOut
> 0
2026 || ovsFwdCtx
.tunnelTxNic
!= NULL
2027 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
2028 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
2029 if (status
!= NDIS_STATUS_SUCCESS
) {
2030 dropReason
= L
"OVS-adding destination failed";
2035 status
= OvsExecuteConntrackAction(ovsFwdCtx
.curNbl
, layers
,
2036 key
, (const PNL_ATTR
)a
);
2037 if (status
!= NDIS_STATUS_SUCCESS
) {
2038 OVS_LOG_ERROR("CT Action failed");
2039 dropReason
= L
"OVS-conntrack action failed";
2045 case OVS_ACTION_ATTR_RECIRC
:
2047 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
2048 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
2049 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
2050 if (status
!= NDIS_STATUS_SUCCESS
) {
2051 dropReason
= L
"OVS-adding destination failed";
2056 status
= OvsExecuteRecirc(&ovsFwdCtx
, key
, (const PNL_ATTR
)a
, rem
);
2057 if (status
!= NDIS_STATUS_SUCCESS
) {
2058 dropReason
= L
"OVS-recirculation action failed";
2062 if (NlAttrIsLast(a
, rem
)) {
2068 case OVS_ACTION_ATTR_USERSPACE
:
2070 status
= OvsOutputUserspaceAction(&ovsFwdCtx
, key
,
2072 if (status
!= NDIS_STATUS_SUCCESS
) {
2073 dropReason
= L
"OVS-Dropped due to failure to queue to "
2077 dropReason
= L
"OVS-Completed since packet was copied to "
2081 case OVS_ACTION_ATTR_SET
:
2083 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
2084 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
2085 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
2086 if (status
!= NDIS_STATUS_SUCCESS
) {
2087 dropReason
= L
"OVS-adding destination failed";
2092 status
= OvsExecuteSetAction(&ovsFwdCtx
, key
, hash
,
2093 (const PNL_ATTR
)NlAttrGet
2094 ((const PNL_ATTR
)a
));
2095 if (status
!= NDIS_STATUS_SUCCESS
) {
2096 dropReason
= L
"OVS-set action failed";
2101 case OVS_ACTION_ATTR_SAMPLE
:
2103 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
2104 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
2105 status
= OvsOutputBeforeSetAction(&ovsFwdCtx
);
2106 if (status
!= NDIS_STATUS_SUCCESS
) {
2107 dropReason
= L
"OVS-adding destination failed";
2112 status
= OvsExecuteSampleAction(&ovsFwdCtx
, key
,
2114 if (status
!= NDIS_STATUS_SUCCESS
) {
2115 dropReason
= L
"OVS-sample action failed";
2121 status
= NDIS_STATUS_NOT_SUPPORTED
;
2126 if (ovsFwdCtx
.destPortsSizeOut
> 0 || ovsFwdCtx
.tunnelTxNic
!= NULL
2127 || ovsFwdCtx
.tunnelRxNic
!= NULL
) {
2128 status
= OvsOutputForwardingCtx(&ovsFwdCtx
);
2129 ASSERT(ovsFwdCtx
.curNbl
== NULL
);
2132 ASSERT(ovsFwdCtx
.destPortsSizeOut
== 0);
2133 ASSERT(ovsFwdCtx
.tunnelRxNic
== NULL
);
2134 ASSERT(ovsFwdCtx
.tunnelTxNic
== NULL
);
2138 * If curNbl != NULL, it implies the NBL has not been not freed up so far.
2140 if (ovsFwdCtx
.curNbl
) {
2141 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
, dropReason
);
2149 * --------------------------------------------------------------------------
2150 * OvsActionsExecute --
2151 * The function interprets and executes the specified 'actions' on the
2152 * specified packet 'curNbl'. See 'OvsDoExecuteActions' description for
2155 * Also executes deferred actions added by recirculation or sample
2157 * --------------------------------------------------------------------------
2160 OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext
,
2161 OvsCompletionList
*completionList
,
2162 PNET_BUFFER_LIST curNbl
,
2167 OVS_PACKET_HDR_INFO
*layers
,
2168 const PNL_ATTR actions
,
2173 status
= OvsDoExecuteActions(switchContext
, completionList
, curNbl
,
2174 portNo
, sendFlags
, key
, hash
, layers
,
2175 actions
, actionsLen
);
2177 if (status
== STATUS_SUCCESS
) {
2178 status
= OvsProcessDeferredActions(switchContext
, completionList
,
2179 portNo
, sendFlags
, layers
);
2186 * --------------------------------------------------------------------------
2188 * The function processes the packet 'curNbl' that re-entered datapath
2189 * packet processing after a recirculation action.
2190 * --------------------------------------------------------------------------
2193 OvsDoRecirc(POVS_SWITCH_CONTEXT switchContext
,
2194 OvsCompletionList
*completionList
,
2195 PNET_BUFFER_LIST curNbl
,
2198 OVS_PACKET_HDR_INFO
*layers
)
2202 OvsForwardingContext ovsFwdCtx
= { 0 };
2206 OvsInitForwardingCtx(&ovsFwdCtx
, switchContext
, curNbl
,
2208 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl
),
2209 completionList
, layers
, TRUE
);
2211 flow
= OvsLookupFlow(&ovsFwdCtx
.switchContext
->datapath
, key
, &hash
, FALSE
);
2213 UINT32 level
= OvsDeferredActionsLevelGet();
2215 if (level
> DEFERRED_ACTION_EXEC_LEVEL
) {
2216 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
,
2217 L
"OVS-Dropped due to deferred actions execution level limit \
2219 ovsActionStats
.deferredActionsExecLimit
++;
2220 ovsFwdCtx
.curNbl
= NULL
;
2221 return NDIS_STATUS_FAILURE
;
2224 OvsFlowUsed(flow
, ovsFwdCtx
.curNbl
, &ovsFwdCtx
.layers
);
2225 ovsFwdCtx
.switchContext
->datapath
.hits
++;
2227 OvsDeferredActionsLevelInc();
2229 status
= OvsDoExecuteActions(ovsFwdCtx
.switchContext
,
2230 ovsFwdCtx
.completionList
,
2232 ovsFwdCtx
.srcVportNo
,
2233 ovsFwdCtx
.sendFlags
,
2234 key
, &hash
, &ovsFwdCtx
.layers
,
2235 flow
->actions
, flow
->actionsLen
);
2236 ovsFwdCtx
.curNbl
= NULL
;
2238 OvsDeferredActionsLevelDec();
2240 POVS_VPORT_ENTRY vport
= NULL
;
2241 LIST_ENTRY missedPackets
;
2244 ovsFwdCtx
.switchContext
->datapath
.misses
++;
2245 InitializeListHead(&missedPackets
);
2246 vport
= OvsFindVportByPortNo(switchContext
, srcPortNo
);
2247 if (vport
== NULL
|| vport
->ovsState
!= OVS_STATE_CONNECTED
) {
2248 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
,
2249 L
"OVS-Dropped due to port removal");
2250 ovsActionStats
.noVport
++;
2251 return NDIS_STATUS_SUCCESS
;
2253 status
= OvsCreateAndAddPackets(NULL
, 0, OVS_PACKET_CMD_MISS
,
2254 vport
, key
, ovsFwdCtx
.curNbl
,
2256 switchContext
->virtualExternalPortId
,
2258 ovsFwdCtx
.switchContext
,
2259 &missedPackets
, &num
);
2261 OvsQueuePackets(&missedPackets
, num
);
2263 if (status
== NDIS_STATUS_SUCCESS
) {
2264 /* Complete the packet since it was copied to user buffer. */
2265 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
,
2266 L
"OVS-Dropped since packet was copied to userspace");
2267 ovsActionStats
.flowMiss
++;
2269 OvsCompleteNBLForwardingCtx(&ovsFwdCtx
,
2270 L
"OVS-Dropped due to failure to queue to userspace");
2271 ovsActionStats
.failedFlowMiss
++;
2272 status
= NDIS_STATUS_FAILURE
;