2 * Copyright (c) 2014 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.
33 #define OVS_DBG_MOD OVS_DBG_ACTION
36 typedef struct _OVS_ACTION_STATS {
44 UINT32 failedFlowMiss;
46 UINT32 failedFlowExtract;
51 UINT32 cannotGrowDest;
53 UINT32 failedChecksum;
54 } OVS_ACTION_STATS, *POVS_ACTION_STATS;
56 OVS_ACTION_STATS ovsActionStats;
59 * There a lot of data that needs to be maintained while executing the pipeline
60 * as dictated by the actions of a flow, across different functions at different
61 * levels. Such data is put together in a 'context' structure. Care should be
62 * exercised while adding new members to the structure - only add ones that get
63 * used across multiple stages in the pipeline/get used in multiple functions.
65 #define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2
66 typedef struct OvsForwardingContext {
67 POVS_SWITCH_CONTEXT switchContext;
68 /* The NBL currently used in the pipeline. */
69 PNET_BUFFER_LIST curNbl;
70 /* NDIS forwarding detail for 'curNbl'. */
71 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
72 /* Array of destination ports for 'curNbl'. */
73 PNDIS_SWITCH_FORWARDING_DESTINATION_ARRAY destinationPorts;
74 /* send flags while sending 'curNbl' into NDIS. */
76 /* Total number of output ports, used + unused, in 'curNbl'. */
77 UINT32 destPortsSizeIn;
78 /* Total number of used output ports in 'curNbl'. */
79 UINT32 destPortsSizeOut;
81 * If 'curNbl' is not owned by OVS, they need to be tracked, if they need to
84 OvsCompletionList *completionList;
86 * vport number of 'curNbl' when it is passed from the PIF bridge to the INT
87 * bridge. ie. during tunneling on the Rx side.
93 * - specified in actions during tunneling Tx
94 * - extracted from an NBL during tunneling Rx
96 OvsIPv4TunnelKey tunKey;
100 * To store the output port, when it is a tunneled port. We don't foresee
101 * multiple tunneled ports as outport for any given NBL.
103 POVS_VPORT_ENTRY tunnelTxNic;
107 * Points to the Internal port on the PIF Bridge, if the packet needs to be
110 POVS_VPORT_ENTRY tunnelRxNic;
112 /* header information */
113 OVS_PACKET_HDR_INFO layers;
114 } OvsForwardingContext;
118 * --------------------------------------------------------------------------
119 * OvsInitForwardingCtx --
120 * Function to init/re-init the 'ovsFwdCtx' context as the actions pipeline
124 * NDIS_STATUS_SUCCESS on success
125 * Other NDIS_STATUS upon failure. Upon failure, it is safe to call
126 * OvsCompleteNBLForwardingCtx(), since 'ovsFwdCtx' has been initialized
127 * enough for OvsCompleteNBLForwardingCtx() to do its work.
128 * --------------------------------------------------------------------------
130 static __inline NDIS_STATUS
131 OvsInitForwardingCtx(OvsForwardingContext *ovsFwdCtx,
132 POVS_SWITCH_CONTEXT switchContext,
133 PNET_BUFFER_LIST curNbl,
136 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail,
137 OvsCompletionList *completionList,
138 OVS_PACKET_HDR_INFO *layers,
139 BOOLEAN resetTunnelInfo)
142 ASSERT(switchContext);
147 * Set values for curNbl and switchContext so upon failures, we have enough
148 * information to do cleanup.
150 ovsFwdCtx->curNbl = curNbl;
151 ovsFwdCtx->switchContext = switchContext;
152 ovsFwdCtx->completionList = completionList;
153 ovsFwdCtx->fwdDetail = fwdDetail;
155 if (fwdDetail->NumAvailableDestinations > 0) {
157 * XXX: even though MSDN says GetNetBufferListDestinations() returns
158 * NDIS_STATUS, the header files say otherwise.
160 switchContext->NdisSwitchHandlers.GetNetBufferListDestinations(
161 switchContext->NdisSwitchContext, curNbl,
162 &ovsFwdCtx->destinationPorts);
164 ASSERT(ovsFwdCtx->destinationPorts);
165 /* Ensure that none of the elements are consumed yet. */
166 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
167 fwdDetail->NumAvailableDestinations);
169 ovsFwdCtx->destinationPorts = NULL;
171 ovsFwdCtx->destPortsSizeIn = fwdDetail->NumAvailableDestinations;
172 ovsFwdCtx->destPortsSizeOut = 0;
173 ovsFwdCtx->srcVportNo = srcVportNo;
174 ovsFwdCtx->sendFlags = sendFlags;
176 ovsFwdCtx->layers = *layers;
178 RtlZeroMemory(&ovsFwdCtx->layers, sizeof ovsFwdCtx->layers);
180 if (resetTunnelInfo) {
181 ovsFwdCtx->tunnelTxNic = NULL;
182 ovsFwdCtx->tunnelRxNic = NULL;
183 RtlZeroMemory(&ovsFwdCtx->tunKey, sizeof ovsFwdCtx->tunKey);
186 return NDIS_STATUS_SUCCESS;
190 * --------------------------------------------------------------------------
191 * OvsDetectTunnelRxPkt --
192 * Utility function for an RX packet to detect its tunnel type.
195 * True - if the tunnel type was detected.
196 * False - if not a tunnel packet or tunnel type not supported.
197 * --------------------------------------------------------------------------
199 static __inline BOOLEAN
200 OvsDetectTunnelRxPkt(OvsForwardingContext *ovsFwdCtx,
201 const OvsFlowKey *flowKey)
203 POVS_VPORT_ENTRY tunnelVport = NULL;
205 /* XXX: we should also check for the length of the UDP payload to pick
206 * packets only if they are at least VXLAN header size.
208 if (!flowKey->ipKey.nwFrag &&
209 flowKey->ipKey.nwProto == IPPROTO_UDP) {
210 UINT16 dstPort = ntohs(flowKey->ipKey.l4.tpDst);
211 tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
213 OVS_VPORT_TYPE_VXLAN);
215 ovsActionStats.rxVxlan++;
217 } else if (!flowKey->ipKey.nwFrag &&
218 flowKey->ipKey.nwProto == IPPROTO_TCP) {
219 UINT16 dstPort = htons(flowKey->ipKey.l4.tpDst);
220 tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
224 ovsActionStats.rxStt++;
229 // We might get tunnel packets even before the tunnel gets initialized.
231 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
232 ovsFwdCtx->tunnelRxNic = tunnelVport;
240 * --------------------------------------------------------------------------
241 * OvsDetectTunnelPkt --
242 * Utility function to detect if a packet is to be subjected to
243 * tunneling (Tx) or de-tunneling (Rx). Various factors such as source
244 * port, destination port, packet contents, and previously setup tunnel
248 * True - If the packet is to be subjected to tunneling.
249 * In case of invalid tunnel context, the tunneling functionality is
250 * a no-op and is completed within this function itself by consuming
251 * all of the tunneling context.
252 * False - If not a tunnel packet or tunnel type not supported. Caller should
253 * process the packet as a non-tunnel packet.
254 * --------------------------------------------------------------------------
256 static __inline BOOLEAN
257 OvsDetectTunnelPkt(OvsForwardingContext *ovsFwdCtx,
258 const POVS_VPORT_ENTRY dstVport,
259 const OvsFlowKey *flowKey)
261 if (OvsIsInternalVportType(dstVport->ovsType)) {
264 * The source of NBL during tunneling Rx could be the external
265 * port or if it is being executed from userspace, the source port is
268 BOOLEAN validSrcPort =
269 (ovsFwdCtx->fwdDetail->SourcePortId ==
270 ovsFwdCtx->switchContext->virtualExternalPortId) ||
271 (ovsFwdCtx->fwdDetail->SourcePortId ==
272 NDIS_SWITCH_DEFAULT_PORT_ID);
274 if (validSrcPort && OvsDetectTunnelRxPkt(ovsFwdCtx, flowKey)) {
275 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
276 ASSERT(ovsFwdCtx->tunnelRxNic != NULL);
279 } else if (OvsIsTunnelVportType(dstVport->ovsType)) {
280 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
281 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
285 * The destination port is a tunnel port. Encapsulation must be
286 * performed only on packets that originate from:
288 * - a bridge-internal port (packets generated from userspace)
291 * If the packet will not be encapsulated, consume the tunnel context
294 if (ovsFwdCtx->srcVportNo != OVS_DEFAULT_PORT_NO) {
296 POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(
297 ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
300 (vport->ovsType != OVS_VPORT_TYPE_NETDEV &&
301 !OvsIsBridgeInternalVport(vport))) {
302 ovsFwdCtx->tunKey.dst = 0;
306 /* Tunnel the packet only if tunnel context is set. */
307 if (ovsFwdCtx->tunKey.dst != 0) {
308 switch(dstVport->ovsType) {
309 case OVS_VPORT_TYPE_VXLAN:
310 ovsActionStats.txVxlan++;
312 case OVS_VPORT_TYPE_STT:
313 ovsActionStats.txStt++;
316 ovsActionStats.txVxlan++;
317 ovsFwdCtx->tunnelTxNic = dstVport;
328 * --------------------------------------------------------------------------
330 * Add the specified destination vport into the forwarding context. If the
331 * vport is a VIF/external port, it is added directly to the NBL. If it is
332 * a tunneling port, it is NOT added to the NBL.
335 * NDIS_STATUS_SUCCESS on success
336 * Other NDIS_STATUS upon failure.
337 * --------------------------------------------------------------------------
339 static __inline NDIS_STATUS
340 OvsAddPorts(OvsForwardingContext *ovsFwdCtx,
342 NDIS_SWITCH_PORT_ID dstPortId,
343 BOOLEAN preserveVLAN,
344 BOOLEAN preservePriority)
346 POVS_VPORT_ENTRY vport;
347 PNDIS_SWITCH_PORT_DESTINATION fwdPort;
349 POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext;
352 * We hold the dispatch lock that protects the list of vports, so vports
353 * validated here can be added as destinations safely before we call into
356 * Some of the vports can be tunnelled ports as well in which case
357 * they should be added to a separate list of tunnelled destination ports
358 * instead of the VIF ports. The context for the tunnel is settable
359 * in OvsForwardingContext.
361 vport = OvsFindVportByPortNo(ovsFwdCtx->switchContext, dstPortId);
362 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
364 * There may be some latency between a port disappearing, and userspace
365 * updating the recalculated flows. In the meantime, handle invalid
368 ovsActionStats.noVport++;
369 return NDIS_STATUS_SUCCESS;
371 ASSERT(vport->nicState == NdisSwitchNicStateConnected);
372 vport->stats.txPackets++;
373 vport->stats.txBytes +=
374 NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl));
376 if (OvsIsBridgeInternalVport(vport)) {
377 return NDIS_STATUS_SUCCESS;
380 if (OvsDetectTunnelPkt(ovsFwdCtx, vport, flowKey)) {
381 return NDIS_STATUS_SUCCESS;
384 if (ovsFwdCtx->destPortsSizeOut == ovsFwdCtx->destPortsSizeIn) {
385 if (ovsFwdCtx->destPortsSizeIn == 0) {
386 ASSERT(ovsFwdCtx->destinationPorts == NULL);
387 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0);
389 switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations(
390 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
391 OVS_DEST_PORTS_ARRAY_MIN_SIZE,
392 &ovsFwdCtx->destinationPorts);
393 if (status != NDIS_STATUS_SUCCESS) {
394 ovsActionStats.cannotGrowDest++;
397 ovsFwdCtx->destPortsSizeIn =
398 ovsFwdCtx->fwdDetail->NumAvailableDestinations;
399 ASSERT(ovsFwdCtx->destinationPorts);
401 ASSERT(ovsFwdCtx->destinationPorts != NULL);
404 * A ULONG value that specifies the total number of
405 * NDIS_SWITCH_PORT_DESTINATION elements in the
406 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure.
409 * A ULONG value that specifies the number of
410 * NDIS_SWITCH_PORT_DESTINATION elements in the
411 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure that
412 * specify port destinations.
414 * NumAvailableDestinations:
415 * A value that specifies the number of unused extensible switch
416 * destination ports elements within an NET_BUFFER_LIST structure.
418 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
419 ovsFwdCtx->destPortsSizeIn);
420 ASSERT(ovsFwdCtx->destinationPorts->NumDestinations ==
421 ovsFwdCtx->destPortsSizeOut -
422 ovsFwdCtx->fwdDetail->NumAvailableDestinations);
423 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations > 0);
425 * Before we grow the array of destination ports, the current set
426 * of ports needs to be committed. Only the ports added since the
427 * last commit need to be part of the new update.
429 status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations(
430 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
431 ovsFwdCtx->fwdDetail->NumAvailableDestinations,
432 ovsFwdCtx->destinationPorts);
433 if (status != NDIS_STATUS_SUCCESS) {
434 ovsActionStats.cannotGrowDest++;
437 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
438 ovsFwdCtx->destPortsSizeIn);
439 ASSERT(ovsFwdCtx->destinationPorts->NumDestinations ==
440 ovsFwdCtx->destPortsSizeOut);
441 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0);
443 status = switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations(
444 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
445 ovsFwdCtx->destPortsSizeIn, &ovsFwdCtx->destinationPorts);
446 if (status != NDIS_STATUS_SUCCESS) {
447 ovsActionStats.cannotGrowDest++;
450 ASSERT(ovsFwdCtx->destinationPorts != NULL);
451 ovsFwdCtx->destPortsSizeIn <<= 1;
455 ASSERT(ovsFwdCtx->destPortsSizeOut < ovsFwdCtx->destPortsSizeIn);
457 NDIS_SWITCH_PORT_DESTINATION_AT_ARRAY_INDEX(ovsFwdCtx->destinationPorts,
458 ovsFwdCtx->destPortsSizeOut);
460 fwdPort->PortId = vport->portId;
461 fwdPort->NicIndex = vport->nicIndex;
462 fwdPort->IsExcluded = 0;
463 fwdPort->PreserveVLAN = preserveVLAN;
464 fwdPort->PreservePriority = preservePriority;
465 ovsFwdCtx->destPortsSizeOut += 1;
467 return NDIS_STATUS_SUCCESS;
472 * --------------------------------------------------------------------------
473 * OvsClearTunTxCtx --
474 * Utility function to clear tx tunneling context.
475 * --------------------------------------------------------------------------
478 OvsClearTunTxCtx(OvsForwardingContext *ovsFwdCtx)
480 ovsFwdCtx->tunnelTxNic = NULL;
481 ovsFwdCtx->tunKey.dst = 0;
486 * --------------------------------------------------------------------------
487 * OvsClearTunRxCtx --
488 * Utility function to clear rx tunneling context.
489 * --------------------------------------------------------------------------
492 OvsClearTunRxCtx(OvsForwardingContext *ovsFwdCtx)
494 ovsFwdCtx->tunnelRxNic = NULL;
495 ovsFwdCtx->tunKey.dst = 0;
500 * --------------------------------------------------------------------------
501 * OvsCompleteNBLForwardingCtx --
502 * This utility function is responsible for freeing/completing an NBL - either
503 * by adding it to a completion list or by freeing it.
506 * It also resets the necessary fields in 'ovsFwdCtx'.
507 * --------------------------------------------------------------------------
510 OvsCompleteNBLForwardingCtx(OvsForwardingContext *ovsFwdCtx,
513 NDIS_STRING filterReason;
515 RtlInitUnicodeString(&filterReason, dropReason);
516 if (ovsFwdCtx->completionList) {
517 OvsAddPktCompletionList(ovsFwdCtx->completionList, TRUE,
518 ovsFwdCtx->fwdDetail->SourcePortId, ovsFwdCtx->curNbl, 1,
520 ovsFwdCtx->curNbl = NULL;
522 /* If there is no completionList, we assume this is ovs created NBL */
523 ovsFwdCtx->curNbl = OvsCompleteNBL(ovsFwdCtx->switchContext,
524 ovsFwdCtx->curNbl, TRUE);
525 ASSERT(ovsFwdCtx->curNbl == NULL);
527 /* XXX: these can be made debug only to save cycles. Ideally the pipeline
528 * using these fields should reset the values at the end of the pipeline. */
529 ovsFwdCtx->destPortsSizeOut = 0;
530 ovsFwdCtx->tunnelTxNic = NULL;
531 ovsFwdCtx->tunnelRxNic = NULL;
535 * --------------------------------------------------------------------------
536 * OvsDoFlowLookupOutput --
537 * Function to be used for the second stage of a tunneling workflow, ie.:
538 * - On the encapsulated packet on Tx path, to do a flow extract, flow
539 * lookup and excuting the actions.
540 * - On the decapsulated packet on Rx path, to do a flow extract, flow
541 * lookup and excuting the actions.
543 * XXX: It is assumed that the NBL in 'ovsFwdCtx' is owned by OVS. This is
544 * until the new buffer management framework is adopted.
547 * The NBL in 'ovsFwdCtx' is consumed.
548 * --------------------------------------------------------------------------
550 static __inline NDIS_STATUS
551 OvsDoFlowLookupOutput(OvsForwardingContext *ovsFwdCtx)
557 POVS_VPORT_ENTRY vport =
558 OvsFindVportByPortNo(ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
559 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
560 ASSERT(FALSE); // XXX: let's catch this for now
561 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
562 L"OVS-Dropped due to internal/tunnel port removal");
563 ovsActionStats.noVport++;
564 return NDIS_STATUS_SUCCESS;
566 ASSERT(vport->nicState == NdisSwitchNicStateConnected);
568 /* Assert that in the Rx direction, key is always setup. */
569 ASSERT(ovsFwdCtx->tunnelRxNic == NULL || ovsFwdCtx->tunKey.dst != 0);
570 status = OvsExtractFlow(ovsFwdCtx->curNbl, ovsFwdCtx->srcVportNo,
571 &key, &ovsFwdCtx->layers, ovsFwdCtx->tunKey.dst != 0 ?
572 &ovsFwdCtx->tunKey : NULL);
573 if (status != NDIS_STATUS_SUCCESS) {
574 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
575 L"OVS-Flow extract failed");
576 ovsActionStats.failedFlowExtract++;
580 flow = OvsLookupFlow(&ovsFwdCtx->switchContext->datapath, &key, &hash, FALSE);
582 OvsFlowUsed(flow, ovsFwdCtx->curNbl, &ovsFwdCtx->layers);
583 ovsFwdCtx->switchContext->datapath.hits++;
584 status = OvsActionsExecute(ovsFwdCtx->switchContext,
585 ovsFwdCtx->completionList, ovsFwdCtx->curNbl,
586 ovsFwdCtx->srcVportNo, ovsFwdCtx->sendFlags,
587 &key, &hash, &ovsFwdCtx->layers,
588 flow->actions, flow->actionsLen);
589 ovsFwdCtx->curNbl = NULL;
591 LIST_ENTRY missedPackets;
593 ovsFwdCtx->switchContext->datapath.misses++;
594 InitializeListHead(&missedPackets);
595 status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS,
596 ovsFwdCtx->srcVportNo,
597 &key,ovsFwdCtx->curNbl,
598 ovsFwdCtx->tunnelRxNic != NULL, &ovsFwdCtx->layers,
599 ovsFwdCtx->switchContext, &missedPackets, &num);
601 OvsQueuePackets(&missedPackets, num);
603 if (status == NDIS_STATUS_SUCCESS) {
604 /* Complete the packet since it was copied to user buffer. */
605 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
606 L"OVS-Dropped since packet was copied to userspace");
607 ovsActionStats.flowMiss++;
608 status = NDIS_STATUS_SUCCESS;
610 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
611 L"OVS-Dropped due to failure to queue to userspace");
612 status = NDIS_STATUS_FAILURE;
613 ovsActionStats.failedFlowMiss++;
621 * --------------------------------------------------------------------------
623 * The start function for Tx tunneling - encapsulates the packet, and
624 * outputs the packet on the PIF bridge.
627 * The NBL in 'ovsFwdCtx' is consumed.
628 * --------------------------------------------------------------------------
630 static __inline NDIS_STATUS
631 OvsTunnelPortTx(OvsForwardingContext *ovsFwdCtx)
633 NDIS_STATUS status = NDIS_STATUS_FAILURE;
634 PNET_BUFFER_LIST newNbl = NULL;
637 * Setup the source port to be the internal port to as to facilitate the
638 * second OvsLookupFlow.
640 if (ovsFwdCtx->switchContext->internalVport == NULL ||
641 ovsFwdCtx->switchContext->virtualExternalVport == NULL) {
642 OvsClearTunTxCtx(ovsFwdCtx);
643 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
644 L"OVS-Dropped since either internal or external port is absent");
645 return NDIS_STATUS_FAILURE;
647 ovsFwdCtx->srcVportNo =
648 ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->portNo;
650 ovsFwdCtx->fwdDetail->SourcePortId = ovsFwdCtx->switchContext->internalPortId;
651 ovsFwdCtx->fwdDetail->SourceNicIndex =
652 ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->nicIndex;
654 /* Do the encap. Encap function does not consume the NBL. */
655 switch(ovsFwdCtx->tunnelTxNic->ovsType) {
656 case OVS_VPORT_TYPE_VXLAN:
657 status = OvsEncapVxlan(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
658 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
659 &ovsFwdCtx->layers, &newNbl);
661 case OVS_VPORT_TYPE_STT:
662 status = OvsEncapStt(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
663 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
664 &ovsFwdCtx->layers, &newNbl);
667 ASSERT(! "Tx: Unhandled tunnel type");
670 /* Reset the tunnel context so that it doesn't get used after this point. */
671 OvsClearTunTxCtx(ovsFwdCtx);
673 if (status == NDIS_STATUS_SUCCESS) {
675 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
676 L"Complete after cloning NBL for encapsulation");
677 ovsFwdCtx->curNbl = newNbl;
678 status = OvsDoFlowLookupOutput(ovsFwdCtx);
679 ASSERT(ovsFwdCtx->curNbl == NULL);
682 * XXX: Temporary freeing of the packet until we register a
683 * callback to IP helper.
685 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
686 L"OVS-Dropped due to encap failure");
687 ovsActionStats.failedEncap++;
688 status = NDIS_STATUS_SUCCESS;
695 * --------------------------------------------------------------------------
697 * Decapsulate the incoming NBL based on the tunnel type and goes through
698 * the flow lookup for the inner packet.
700 * Note: IP checksum is validate here, but L4 checksum validation needs
701 * to be done by the corresponding tunnel types.
704 * The NBL in 'ovsFwdCtx' is consumed.
705 * --------------------------------------------------------------------------
707 static __inline NDIS_STATUS
708 OvsTunnelPortRx(OvsForwardingContext *ovsFwdCtx)
710 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
711 PNET_BUFFER_LIST newNbl = NULL;
712 POVS_VPORT_ENTRY tunnelRxVport = ovsFwdCtx->tunnelRxNic;
714 if (OvsValidateIPChecksum(ovsFwdCtx->curNbl, &ovsFwdCtx->layers)
715 != NDIS_STATUS_SUCCESS) {
716 ovsActionStats.failedChecksum++;
717 OVS_LOG_INFO("Packet dropped due to IP checksum failure.");
722 * Decap port functions should return a new NBL if it was copied, and
723 * this new NBL should be setup as the ovsFwdCtx->curNbl.
726 switch(tunnelRxVport->ovsType) {
727 case OVS_VPORT_TYPE_VXLAN:
728 status = OvsDecapVxlan(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
729 &ovsFwdCtx->tunKey, &newNbl);
731 case OVS_VPORT_TYPE_STT:
732 status = OvsDecapStt(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
733 &ovsFwdCtx->tunKey, &newNbl);
736 OVS_LOG_ERROR("Rx: Unhandled tunnel type: %d\n",
737 tunnelRxVport->ovsType);
738 ASSERT(! "Rx: Unhandled tunnel type");
739 status = NDIS_STATUS_NOT_SUPPORTED;
742 if (status != NDIS_STATUS_SUCCESS) {
743 ovsActionStats.failedDecap++;
748 * tunnelRxNic and other fields will be cleared, re-init the context
751 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
752 L"OVS-dropped due to new decap packet");
754 /* Decapsulated packet is in a new NBL */
755 ovsFwdCtx->tunnelRxNic = tunnelRxVport;
756 OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
757 newNbl, tunnelRxVport->portNo, 0,
758 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
759 ovsFwdCtx->completionList,
760 &ovsFwdCtx->layers, FALSE);
763 * Set the NBL's SourcePortId and SourceNicIndex to default values to
764 * keep NDIS happy when we forward the packet.
766 ovsFwdCtx->fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
767 ovsFwdCtx->fwdDetail->SourceNicIndex = 0;
769 status = OvsDoFlowLookupOutput(ovsFwdCtx);
770 ASSERT(ovsFwdCtx->curNbl == NULL);
771 OvsClearTunRxCtx(ovsFwdCtx);
776 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
777 L"OVS-dropped due to decap failure");
778 OvsClearTunRxCtx(ovsFwdCtx);
784 * --------------------------------------------------------------------------
785 * OvsOutputForwardingCtx --
786 * This function outputs an NBL to NDIS or to a tunneling pipeline based on
787 * the ports added so far into 'ovsFwdCtx'.
790 * This function consumes the NBL - either by forwarding it successfully to
791 * NDIS, or adding it to the completion list in 'ovsFwdCtx', or freeing it.
793 * Also makes sure that the list of destination ports - tunnel or otherwise is
795 * --------------------------------------------------------------------------
797 static __inline NDIS_STATUS
798 OvsOutputForwardingCtx(OvsForwardingContext *ovsFwdCtx)
800 NDIS_STATUS status = STATUS_SUCCESS;
801 POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext;
805 * Handle the case where the some of the destination ports are tunneled
806 * ports - the non-tunneled ports get a unmodified copy of the NBL, and the
807 * tunneling pipeline starts when we output the packet to tunneled port.
809 if (ovsFwdCtx->destPortsSizeOut > 0) {
810 PNET_BUFFER_LIST newNbl = NULL;
812 UINT32 portsToUpdate =
813 ovsFwdCtx->fwdDetail->NumAvailableDestinations -
814 (ovsFwdCtx->destPortsSizeIn - ovsFwdCtx->destPortsSizeOut);
816 ASSERT(ovsFwdCtx->destinationPorts != NULL);
819 * Create a copy of the packet in order to do encap on it later. Also,
820 * don't copy the offload context since the encap'd packet has a
821 * different set of headers. This will change when we implement offloads
822 * before doing encapsulation.
824 if (ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL) {
825 nb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
826 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
827 0, 0, TRUE /*copy NBL info*/);
828 if (newNbl == NULL) {
829 status = NDIS_STATUS_RESOURCES;
830 ovsActionStats.noCopiedNbl++;
831 dropReason = L"Dropped due to failure to create NBL copy.";
836 /* It does not seem like we'll get here unless 'portsToUpdate' > 0. */
837 ASSERT(portsToUpdate > 0);
838 status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations(
839 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
840 portsToUpdate, ovsFwdCtx->destinationPorts);
841 if (status != NDIS_STATUS_SUCCESS) {
842 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
843 ovsActionStats.cannotGrowDest++;
844 dropReason = L"Dropped due to failure to update destinations.";
848 OvsSendNBLIngress(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
849 ovsFwdCtx->sendFlags);
850 /* End this pipeline by resetting the corresponding context. */
851 ovsFwdCtx->destPortsSizeOut = 0;
852 ovsFwdCtx->curNbl = NULL;
854 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
855 newNbl, ovsFwdCtx->srcVportNo, 0,
856 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
857 ovsFwdCtx->completionList,
858 &ovsFwdCtx->layers, FALSE);
859 if (status != NDIS_STATUS_SUCCESS) {
860 dropReason = L"Dropped due to resouces.";
866 if (ovsFwdCtx->tunnelTxNic != NULL) {
867 status = OvsTunnelPortTx(ovsFwdCtx);
868 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
869 ASSERT(ovsFwdCtx->tunKey.dst == 0);
870 } else if (ovsFwdCtx->tunnelRxNic != NULL) {
871 status = OvsTunnelPortRx(ovsFwdCtx);
872 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
873 ASSERT(ovsFwdCtx->tunKey.dst == 0);
875 ASSERT(ovsFwdCtx->curNbl == NULL);
880 if (status != NDIS_STATUS_SUCCESS) {
881 OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason);
889 * --------------------------------------------------------------------------
890 * OvsLookupFlowOutput --
891 * Utility function for external callers to do flow extract, lookup,
892 * actions execute on a given NBL.
894 * Note: If this is being used from a callback function, make sure that the
895 * arguments specified are still valid in the asynchronous context.
898 * This function consumes the NBL.
899 * --------------------------------------------------------------------------
902 OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext,
904 PNET_BUFFER_LIST curNbl)
907 OvsForwardingContext ovsFwdCtx;
908 POVS_VPORT_ENTRY internalVport =
909 (POVS_VPORT_ENTRY)switchContext->internalVport;
911 /* XXX: make sure comp list was not a stack variable previously. */
912 OvsCompletionList *completionList = (OvsCompletionList *)compList;
915 * XXX: can internal port disappear while we are busy doing ARP resolution?
916 * It could, but will we get this callback from IP helper in that case. Need
919 ASSERT(switchContext->internalVport);
920 status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl,
921 internalVport->portNo, 0,
922 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl),
923 completionList, NULL, TRUE);
924 if (status != NDIS_STATUS_SUCCESS) {
925 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
926 L"OVS-Dropped due to resources");
932 * XXX: We need to acquire the dispatch lock and the datapath lock.
935 OvsDoFlowLookupOutput(&ovsFwdCtx);
940 * --------------------------------------------------------------------------
941 * OvsOutputBeforeSetAction --
942 * Function to be called to complete one set of actions on an NBL, before
943 * we start the next one.
944 * --------------------------------------------------------------------------
946 static __inline NDIS_STATUS
947 OvsOutputBeforeSetAction(OvsForwardingContext *ovsFwdCtx)
949 PNET_BUFFER_LIST newNbl;
950 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
954 * Create a copy and work on the copy after this point. The original NBL is
955 * forwarded. One reason to not use the copy for forwarding is that
956 * ports have already been added to the original NBL, and it might be
957 * inefficient/impossible to remove/re-add them to the copy. There's no
958 * notion of removing the ports, the ports need to be marked as
959 * "isExcluded". There's seems no real advantage to retaining the original
960 * and sending out the copy instead.
962 * XXX: We are copying the offload context here. This is to handle actions
964 * outport, pop_vlan(), outport, push_vlan(), outport
966 * copy size needs to include inner ether + IP + TCP, need to revisit
967 * if we support IP options.
968 * XXX Head room needs to include the additional encap.
969 * XXX copySize check is not considering multiple NBs.
971 nb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
972 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
973 0, 0, TRUE /*copy NBL info*/);
975 ASSERT(ovsFwdCtx->destPortsSizeOut > 0 ||
976 ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL);
978 /* Send the original packet out */
979 status = OvsOutputForwardingCtx(ovsFwdCtx);
980 ASSERT(ovsFwdCtx->curNbl == NULL);
981 ASSERT(ovsFwdCtx->destPortsSizeOut == 0);
982 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
983 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
985 /* If we didn't make a copy, can't continue. */
986 if (newNbl == NULL) {
987 ovsActionStats.noCopiedNbl++;
988 return NDIS_STATUS_RESOURCES;
991 /* Finish the remaining actions with the new NBL */
992 if (status != NDIS_STATUS_SUCCESS) {
993 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
995 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
996 newNbl, ovsFwdCtx->srcVportNo, 0,
997 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
998 ovsFwdCtx->completionList,
999 &ovsFwdCtx->layers, FALSE);
1007 * --------------------------------------------------------------------------
1008 * OvsPopVlanInPktBuf --
1009 * Function to pop a VLAN tag when the tag is in the packet buffer.
1010 * --------------------------------------------------------------------------
1012 static __inline NDIS_STATUS
1013 OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
1018 ULONG dataLength = sizeof (DL_EUI48) + sizeof (DL_EUI48);
1019 UINT32 packetLen, mdlLen;
1020 PNET_BUFFER_LIST newNbl;
1024 * Declare a dummy vlanTag structure since we need to compute the size
1025 * of shiftLength. The NDIS one is a unionized structure.
1027 NDIS_PACKET_8021Q_INFO vlanTag = {0};
1028 ULONG shiftLength = sizeof (vlanTag.TagHeader);
1029 PUINT8 tempBuffer[sizeof (DL_EUI48) + sizeof (DL_EUI48)];
1031 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1032 0, 0, TRUE /* copy NBL info */);
1034 ovsActionStats.noCopiedNbl++;
1035 return NDIS_STATUS_RESOURCES;
1038 /* Complete the original NBL and create a copy to modify. */
1039 OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to copy");
1041 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
1042 newNbl, ovsFwdCtx->srcVportNo, 0,
1043 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1044 NULL, &ovsFwdCtx->layers, FALSE);
1045 if (status != NDIS_STATUS_SUCCESS) {
1046 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1047 L"Dropped due to resouces");
1048 return NDIS_STATUS_RESOURCES;
1051 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1052 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1053 ASSERT(curNb->Next == NULL);
1054 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1055 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1057 return NDIS_STATUS_RESOURCES;
1059 mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1060 /* Bail out if L2 + VLAN header is not contiguous in the first buffer. */
1061 if (MIN(packetLen, mdlLen) < sizeof (EthHdr) + shiftLength) {
1063 return NDIS_STATUS_FAILURE;
1065 bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1066 RtlCopyMemory(tempBuffer, bufferStart, dataLength);
1067 RtlCopyMemory(bufferStart + shiftLength, tempBuffer, dataLength);
1068 NdisAdvanceNetBufferDataStart(curNb, shiftLength, FALSE, NULL);
1070 return NDIS_STATUS_SUCCESS;
1074 * --------------------------------------------------------------------------
1075 * OvsTunnelAttrToIPv4TunnelKey --
1076 * Convert tunnel attribute to OvsIPv4TunnelKey.
1077 * --------------------------------------------------------------------------
1079 static __inline NDIS_STATUS
1080 OvsTunnelAttrToIPv4TunnelKey(PNL_ATTR attr,
1081 OvsIPv4TunnelKey *tunKey)
1086 tunKey->attr[0] = 0;
1087 tunKey->attr[1] = 0;
1088 tunKey->attr[2] = 0;
1089 ASSERT(NlAttrType(attr) == OVS_KEY_ATTR_TUNNEL);
1091 NL_ATTR_FOR_EACH_UNSAFE (a, rem, NlAttrData(attr),
1092 NlAttrGetSize(attr)) {
1093 switch (NlAttrType(a)) {
1094 case OVS_TUNNEL_KEY_ATTR_ID:
1095 tunKey->tunnelId = NlAttrGetBe64(a);
1096 tunKey->flags |= OVS_TNL_F_KEY;
1098 case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
1099 tunKey->src = NlAttrGetBe32(a);
1101 case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
1102 tunKey->dst = NlAttrGetBe32(a);
1104 case OVS_TUNNEL_KEY_ATTR_TOS:
1105 tunKey->tos = NlAttrGetU8(a);
1107 case OVS_TUNNEL_KEY_ATTR_TTL:
1108 tunKey->ttl = NlAttrGetU8(a);
1110 case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
1111 tunKey->flags |= OVS_TNL_F_DONT_FRAGMENT;
1113 case OVS_TUNNEL_KEY_ATTR_CSUM:
1114 tunKey->flags |= OVS_TNL_F_CSUM;
1121 return NDIS_STATUS_SUCCESS;
1125 *----------------------------------------------------------------------------
1126 * OvsUpdateEthHeader --
1127 * Updates the ethernet header in ovsFwdCtx.curNbl inline based on the
1129 *----------------------------------------------------------------------------
1131 static __inline NDIS_STATUS
1132 OvsUpdateEthHeader(OvsForwardingContext *ovsFwdCtx,
1133 const struct ovs_key_ethernet *ethAttr)
1139 UINT32 packetLen, mdlLen;
1141 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1142 ASSERT(curNb->Next == NULL);
1143 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1144 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1145 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1147 ovsActionStats.noResource++;
1148 return NDIS_STATUS_RESOURCES;
1150 mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1152 /* Bail out if the L2 header is not in a contiguous buffer. */
1153 if (MIN(packetLen, mdlLen) < sizeof *ethHdr) {
1155 return NDIS_STATUS_FAILURE;
1157 ethHdr = (EthHdr *)(bufferStart + NET_BUFFER_CURRENT_MDL_OFFSET(curNb));
1159 RtlCopyMemory(ethHdr->Destination, ethAttr->eth_dst,
1160 sizeof ethHdr->Destination);
1161 RtlCopyMemory(ethHdr->Source, ethAttr->eth_src, sizeof ethHdr->Source);
1163 return NDIS_STATUS_SUCCESS;
1167 *----------------------------------------------------------------------------
1168 * OvsUpdateIPv4Header --
1169 * Updates the IPv4 header in ovsFwdCtx.curNbl inline based on the
1171 *----------------------------------------------------------------------------
1173 static __inline NDIS_STATUS
1174 OvsUpdateIPv4Header(OvsForwardingContext *ovsFwdCtx,
1175 const struct ovs_key_ipv4 *ipAttr)
1181 UINT32 mdlLen, hdrSize, packetLen;
1182 OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
1185 TCPHdr *tcpHdr = NULL;
1186 UDPHdr *udpHdr = NULL;
1188 ASSERT(layers->value != 0);
1191 * Peek into the MDL to get a handle to the IP header and if required
1192 * the TCP/UDP header as well. We check if the required headers are in one
1193 * contiguous MDL, and if not, we copy them over to one MDL.
1195 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1196 ASSERT(curNb->Next == NULL);
1197 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1198 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1199 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1201 ovsActionStats.noResource++;
1202 return NDIS_STATUS_RESOURCES;
1204 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1205 mdlLen -= curMdlOffset;
1206 ASSERT((INT)mdlLen >= 0);
1208 if (layers->isTcp || layers->isUdp) {
1209 hdrSize = layers->l4Offset +
1210 layers->isTcp ? sizeof (*tcpHdr) : sizeof (*udpHdr);
1212 hdrSize = layers->l3Offset + sizeof (*ipHdr);
1215 /* Count of number of bytes of valid data there are in the first MDL. */
1216 mdlLen = MIN(packetLen, mdlLen);
1217 if (mdlLen < hdrSize) {
1218 PNET_BUFFER_LIST newNbl;
1219 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1220 hdrSize, 0, TRUE /*copy NBL info*/);
1222 ovsActionStats.noCopiedNbl++;
1223 return NDIS_STATUS_RESOURCES;
1225 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1226 L"Complete after partial copy.");
1228 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
1229 newNbl, ovsFwdCtx->srcVportNo, 0,
1230 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1231 NULL, &ovsFwdCtx->layers, FALSE);
1232 if (status != NDIS_STATUS_SUCCESS) {
1233 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1234 L"OVS-Dropped due to resources");
1235 return NDIS_STATUS_RESOURCES;
1238 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1239 ASSERT(curNb->Next == NULL);
1240 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1241 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1243 ovsActionStats.noResource++;
1244 return NDIS_STATUS_RESOURCES;
1246 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1247 mdlLen -= curMdlOffset;
1248 ASSERT(mdlLen >= hdrSize);
1251 ipHdr = (IPHdr *)(bufferStart + curMdlOffset + layers->l3Offset);
1253 if (layers->isTcp) {
1254 tcpHdr = (TCPHdr *)(bufferStart + curMdlOffset + layers->l4Offset);
1255 } else if (layers->isUdp) {
1256 udpHdr = (UDPHdr *)(bufferStart + curMdlOffset + layers->l4Offset);
1260 * Adjust the IP header inline as dictated by the action, nad also update
1261 * the IP and the TCP checksum for the data modified.
1263 * In the future, this could be optimized to make one call to
1264 * ChecksumUpdate32(). Ignoring this for now, since for the most common
1265 * case, we only update the TTL.
1267 if (ipHdr->saddr != ipAttr->ipv4_src) {
1269 tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->saddr,
1271 } else if (udpHdr && udpHdr->check) {
1272 udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->saddr,
1276 if (ipHdr->check != 0) {
1277 ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->saddr,
1280 ipHdr->saddr = ipAttr->ipv4_src;
1282 if (ipHdr->daddr != ipAttr->ipv4_dst) {
1284 tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->daddr,
1286 } else if (udpHdr && udpHdr->check) {
1287 udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->daddr,
1291 if (ipHdr->check != 0) {
1292 ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->daddr,
1295 ipHdr->daddr = ipAttr->ipv4_dst;
1297 if (ipHdr->protocol != ipAttr->ipv4_proto) {
1298 UINT16 oldProto = (ipHdr->protocol << 16) & 0xff00;
1299 UINT16 newProto = (ipAttr->ipv4_proto << 16) & 0xff00;
1301 tcpHdr->check = ChecksumUpdate16(tcpHdr->check, oldProto, newProto);
1302 } else if (udpHdr && udpHdr->check) {
1303 udpHdr->check = ChecksumUpdate16(udpHdr->check, oldProto, newProto);
1306 if (ipHdr->check != 0) {
1307 ipHdr->check = ChecksumUpdate16(ipHdr->check, oldProto, newProto);
1309 ipHdr->protocol = ipAttr->ipv4_proto;
1311 if (ipHdr->ttl != ipAttr->ipv4_ttl) {
1312 UINT16 oldTtl = (ipHdr->ttl) & 0xff;
1313 UINT16 newTtl = (ipAttr->ipv4_ttl) & 0xff;
1314 if (ipHdr->check != 0) {
1315 ipHdr->check = ChecksumUpdate16(ipHdr->check, oldTtl, newTtl);
1317 ipHdr->ttl = ipAttr->ipv4_ttl;
1320 return NDIS_STATUS_SUCCESS;
1324 * --------------------------------------------------------------------------
1325 * OvsExecuteSetAction --
1326 * Executes a set() action, but storing the actions into 'ovsFwdCtx'
1327 * --------------------------------------------------------------------------
1329 static __inline NDIS_STATUS
1330 OvsExecuteSetAction(OvsForwardingContext *ovsFwdCtx,
1335 enum ovs_key_attr type = NlAttrType(a);
1336 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1339 case OVS_KEY_ATTR_ETHERNET:
1340 status = OvsUpdateEthHeader(ovsFwdCtx,
1341 NlAttrGetUnspec(a, sizeof(struct ovs_key_ethernet)));
1344 case OVS_KEY_ATTR_IPV4:
1345 status = OvsUpdateIPv4Header(ovsFwdCtx,
1346 NlAttrGetUnspec(a, sizeof(struct ovs_key_ipv4)));
1349 case OVS_KEY_ATTR_TUNNEL:
1351 OvsIPv4TunnelKey tunKey;
1352 status = OvsTunnelAttrToIPv4TunnelKey((PNL_ATTR)a, &tunKey);
1353 ASSERT(status == NDIS_STATUS_SUCCESS);
1354 tunKey.flow_hash = (uint16)(hash ? *hash : OvsHashFlow(key));
1355 tunKey.dst_port = key->ipKey.l4.tpDst;
1356 RtlCopyMemory(&ovsFwdCtx->tunKey, &tunKey, sizeof ovsFwdCtx->tunKey);
1359 case OVS_KEY_ATTR_SKB_MARK:
1360 /* XXX: Not relevant to Hyper-V. Return OK */
1362 case OVS_KEY_ATTR_UNSPEC:
1363 case OVS_KEY_ATTR_ENCAP:
1364 case OVS_KEY_ATTR_ETHERTYPE:
1365 case OVS_KEY_ATTR_IN_PORT:
1366 case OVS_KEY_ATTR_VLAN:
1367 case OVS_KEY_ATTR_ICMP:
1368 case OVS_KEY_ATTR_ICMPV6:
1369 case OVS_KEY_ATTR_ARP:
1370 case OVS_KEY_ATTR_ND:
1371 case __OVS_KEY_ATTR_MAX:
1373 OVS_LOG_INFO("Unhandled attribute %#x", type);
1380 * --------------------------------------------------------------------------
1381 * OvsActionsExecute --
1382 * Interpret and execute the specified 'actions' on the specifed packet
1383 * 'curNbl'. The expectation is that if the packet needs to be dropped
1384 * (completed) for some reason, it is added to 'completionList' so that the
1385 * caller can complete the packet. If 'completionList' is NULL, the NBL is
1386 * assumed to be generated by OVS and freed up. Otherwise, the function
1387 * consumes the NBL by generating a NDIS send indication for the packet.
1389 * There are one or more of "clone" NBLs that may get generated while
1390 * executing the actions. Upon any failures, the "cloned" NBLs are freed up,
1391 * and the caller does not have to worry about them.
1393 * Success or failure is returned based on whether the specified actions
1394 * were executed successfully on the packet or not.
1395 * --------------------------------------------------------------------------
1398 OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
1399 OvsCompletionList *completionList,
1400 PNET_BUFFER_LIST curNbl,
1405 OVS_PACKET_HDR_INFO *layers,
1406 const PNL_ATTR actions,
1412 OvsForwardingContext ovsFwdCtx;
1413 PCWSTR dropReason = L"";
1415 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail =
1416 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl);
1418 /* XXX: ASSERT that the flow table lock is held. */
1419 status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl, portNo,
1420 sendFlags, fwdDetail, completionList,
1422 if (status != NDIS_STATUS_SUCCESS) {
1423 dropReason = L"OVS-initing destination port list failed";
1427 if (actionsLen == 0) {
1428 dropReason = L"OVS-Dropped due to Flow action";
1429 ovsActionStats.zeroActionLen++;
1433 NL_ATTR_FOR_EACH_UNSAFE (a, rem, actions, actionsLen) {
1434 switch(NlAttrType(a)) {
1435 case OVS_ACTION_ATTR_OUTPUT:
1436 dstPortID = NlAttrGetU32(a);
1437 status = OvsAddPorts(&ovsFwdCtx, key, dstPortID,
1439 if (status != NDIS_STATUS_SUCCESS) {
1440 dropReason = L"OVS-adding destination port failed";
1445 case OVS_ACTION_ATTR_PUSH_VLAN:
1447 struct ovs_action_push_vlan *vlan;
1449 PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTag;
1451 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1452 || ovsFwdCtx.tunnelRxNic != NULL) {
1453 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1454 if (status != NDIS_STATUS_SUCCESS) {
1455 dropReason = L"OVS-adding destination failed";
1460 vlanTagValue = NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1461 Ieee8021QNetBufferListInfo);
1462 if (vlanTagValue != NULL) {
1464 * XXX: We don't support double VLAN tag offload. In such cases,
1465 * we need to insert the existing one into the packet buffer,
1466 * and add the new one as offload. This will take care of
1467 * guest tag-in-tag case as well as OVS rules that specify
1472 vlanTag = (PNDIS_NET_BUFFER_LIST_8021Q_INFO)(PVOID *)&vlanTagValue;
1473 vlan = (struct ovs_action_push_vlan *)NlAttrGet((const PNL_ATTR)a);
1474 vlanTag->TagHeader.VlanId = ntohs(vlan->vlan_tci) & 0xfff;
1475 vlanTag->TagHeader.UserPriority = ntohs(vlan->vlan_tci) >> 13;
1477 NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1478 Ieee8021QNetBufferListInfo) = vlanTagValue;
1483 case OVS_ACTION_ATTR_POP_VLAN:
1485 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1486 || ovsFwdCtx.tunnelRxNic != NULL) {
1487 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1488 if (status != NDIS_STATUS_SUCCESS) {
1489 dropReason = L"OVS-adding destination failed";
1494 if (NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1495 Ieee8021QNetBufferListInfo) != 0) {
1496 NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1497 Ieee8021QNetBufferListInfo) = 0;
1500 * The VLAN tag is inserted into the packet buffer. Pop the tag
1501 * by packet buffer modification.
1503 status = OvsPopVlanInPktBuf(&ovsFwdCtx);
1504 if (status != NDIS_STATUS_SUCCESS) {
1505 dropReason = L"OVS-pop vlan action failed";
1512 case OVS_ACTION_ATTR_USERSPACE:
1514 PNL_ATTR userdataAttr;
1516 POVS_PACKET_QUEUE_ELEM elem;
1517 BOOLEAN isRecv = FALSE;
1519 POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(switchContext,
1523 if (vport->isExternal ||
1524 OvsIsTunnelVportType(vport->ovsType)) {
1529 queueAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_PID);
1530 userdataAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_USERDATA);
1532 elem = OvsCreateQueueNlPacket((PVOID)userdataAttr,
1533 userdataAttr->nlaLen,
1534 OVS_PACKET_CMD_ACTION,
1535 portNo, key,ovsFwdCtx.curNbl,
1536 NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx.curNbl),
1540 LIST_ENTRY missedPackets;
1541 InitializeListHead(&missedPackets);
1542 InsertTailList(&missedPackets, &elem->link);
1543 OvsQueuePackets(&missedPackets, 1);
1544 dropReason = L"OVS-Completed since packet was copied to "
1547 dropReason = L"OVS-Dropped due to failure to queue to "
1553 case OVS_ACTION_ATTR_SET:
1555 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1556 || ovsFwdCtx.tunnelRxNic != NULL) {
1557 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1558 if (status != NDIS_STATUS_SUCCESS) {
1559 dropReason = L"OVS-adding destination failed";
1564 status = OvsExecuteSetAction(&ovsFwdCtx, key, hash,
1565 (const PNL_ATTR)NlAttrGet
1566 ((const PNL_ATTR)a));
1567 if (status != NDIS_STATUS_SUCCESS) {
1568 dropReason = L"OVS-set action failed";
1573 case OVS_ACTION_ATTR_SAMPLE:
1575 status = NDIS_STATUS_NOT_SUPPORTED;
1580 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1581 || ovsFwdCtx.tunnelRxNic != NULL) {
1582 status = OvsOutputForwardingCtx(&ovsFwdCtx);
1583 ASSERT(ovsFwdCtx.curNbl == NULL);
1586 ASSERT(ovsFwdCtx.destPortsSizeOut == 0);
1587 ASSERT(ovsFwdCtx.tunnelRxNic == NULL);
1588 ASSERT(ovsFwdCtx.tunnelTxNic == NULL);
1592 * If curNbl != NULL, it implies the NBL has not been not freed up so far.
1594 if (ovsFwdCtx.curNbl) {
1595 OvsCompleteNBLForwardingCtx(&ovsFwdCtx, dropReason);