]> git.proxmox.com Git - ovs.git/blame - datapath-windows/ovsext/Actions.c
ofp-actions: Make ofpact_finish() harder to misuse.
[ovs.git] / datapath-windows / ovsext / Actions.c
CommitLineData
c803536e 1/*
7b383a56 2 * Copyright (c) 2014, 2016 VMware, Inc.
c803536e
SS
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "precomp.h"
18
ee25964a 19#include "Actions.h"
7b383a56 20#include "Debug.h"
fa1324c9 21#include "Event.h"
fa1324c9 22#include "Flow.h"
85571a3d 23#include "Gre.h"
245eedef 24#include "Jhash.h"
5874d571 25#include "Mpls.h"
85571a3d 26#include "NetProto.h"
7b383a56 27#include "Offload.h"
fa1324c9 28#include "PacketIO.h"
ee25964a 29#include "Recirc.h"
85571a3d
AS
30#include "Stt.h"
31#include "Switch.h"
32#include "User.h"
33#include "Vport.h"
34#include "Vxlan.h"
c803536e 35
c803536e
SS
36#ifdef OVS_DBG_MOD
37#undef OVS_DBG_MOD
38#endif
39#define OVS_DBG_MOD OVS_DBG_ACTION
c803536e 40
ee25964a
SV
41#define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2
42
c803536e 43typedef struct _OVS_ACTION_STATS {
85571a3d
AS
44 UINT64 rxGre;
45 UINT64 txGre;
c803536e
SS
46 UINT64 rxVxlan;
47 UINT64 txVxlan;
022c2040
EE
48 UINT64 rxStt;
49 UINT64 txStt;
c803536e
SS
50 UINT64 flowMiss;
51 UINT64 flowUserspace;
52 UINT64 txTcp;
53 UINT32 failedFlowMiss;
54 UINT32 noVport;
55 UINT32 failedFlowExtract;
56 UINT32 noResource;
57 UINT32 noCopiedNbl;
58 UINT32 failedEncap;
59 UINT32 failedDecap;
60 UINT32 cannotGrowDest;
61 UINT32 zeroActionLen;
62 UINT32 failedChecksum;
ee25964a
SV
63 UINT32 deferredActionsQueueFull;
64 UINT32 deferredActionsExecLimit;
c803536e
SS
65} OVS_ACTION_STATS, *POVS_ACTION_STATS;
66
67OVS_ACTION_STATS ovsActionStats;
68
69/*
70 * There a lot of data that needs to be maintained while executing the pipeline
71 * as dictated by the actions of a flow, across different functions at different
72 * levels. Such data is put together in a 'context' structure. Care should be
73 * exercised while adding new members to the structure - only add ones that get
74 * used across multiple stages in the pipeline/get used in multiple functions.
75 */
c803536e
SS
76typedef struct OvsForwardingContext {
77 POVS_SWITCH_CONTEXT switchContext;
78 /* The NBL currently used in the pipeline. */
79 PNET_BUFFER_LIST curNbl;
80 /* NDIS forwarding detail for 'curNbl'. */
81 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
82 /* Array of destination ports for 'curNbl'. */
83 PNDIS_SWITCH_FORWARDING_DESTINATION_ARRAY destinationPorts;
84 /* send flags while sending 'curNbl' into NDIS. */
85 ULONG sendFlags;
86 /* Total number of output ports, used + unused, in 'curNbl'. */
87 UINT32 destPortsSizeIn;
88 /* Total number of used output ports in 'curNbl'. */
89 UINT32 destPortsSizeOut;
90 /*
91 * If 'curNbl' is not owned by OVS, they need to be tracked, if they need to
92 * be freed/completed.
93 */
94 OvsCompletionList *completionList;
95 /*
96 * vport number of 'curNbl' when it is passed from the PIF bridge to the INT
97 * bridge. ie. during tunneling on the Rx side.
98 */
99 UINT32 srcVportNo;
100
101 /*
102 * Tunnel key:
103 * - specified in actions during tunneling Tx
104 * - extracted from an NBL during tunneling Rx
105 */
106 OvsIPv4TunnelKey tunKey;
107
ee25964a 108 /*
c803536e
SS
109 * Tunneling - Tx:
110 * To store the output port, when it is a tunneled port. We don't foresee
111 * multiple tunneled ports as outport for any given NBL.
112 */
113 POVS_VPORT_ENTRY tunnelTxNic;
114
115 /*
116 * Tunneling - Rx:
117 * Points to the Internal port on the PIF Bridge, if the packet needs to be
118 * de-tunneled.
119 */
120 POVS_VPORT_ENTRY tunnelRxNic;
121
122 /* header information */
123 OVS_PACKET_HDR_INFO layers;
124} OvsForwardingContext;
125
c803536e
SS
126/*
127 * --------------------------------------------------------------------------
128 * OvsInitForwardingCtx --
129 * Function to init/re-init the 'ovsFwdCtx' context as the actions pipeline
130 * is being executed.
131 *
132 * Result:
133 * NDIS_STATUS_SUCCESS on success
134 * Other NDIS_STATUS upon failure. Upon failure, it is safe to call
135 * OvsCompleteNBLForwardingCtx(), since 'ovsFwdCtx' has been initialized
136 * enough for OvsCompleteNBLForwardingCtx() to do its work.
137 * --------------------------------------------------------------------------
138 */
139static __inline NDIS_STATUS
140OvsInitForwardingCtx(OvsForwardingContext *ovsFwdCtx,
141 POVS_SWITCH_CONTEXT switchContext,
142 PNET_BUFFER_LIST curNbl,
143 UINT32 srcVportNo,
144 ULONG sendFlags,
145 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail,
146 OvsCompletionList *completionList,
147 OVS_PACKET_HDR_INFO *layers,
148 BOOLEAN resetTunnelInfo)
149{
150 ASSERT(ovsFwdCtx);
151 ASSERT(switchContext);
152 ASSERT(curNbl);
153 ASSERT(fwdDetail);
154
155 /*
156 * Set values for curNbl and switchContext so upon failures, we have enough
157 * information to do cleanup.
158 */
159 ovsFwdCtx->curNbl = curNbl;
160 ovsFwdCtx->switchContext = switchContext;
161 ovsFwdCtx->completionList = completionList;
162 ovsFwdCtx->fwdDetail = fwdDetail;
163
164 if (fwdDetail->NumAvailableDestinations > 0) {
165 /*
166 * XXX: even though MSDN says GetNetBufferListDestinations() returns
167 * NDIS_STATUS, the header files say otherwise.
168 */
169 switchContext->NdisSwitchHandlers.GetNetBufferListDestinations(
170 switchContext->NdisSwitchContext, curNbl,
171 &ovsFwdCtx->destinationPorts);
172
173 ASSERT(ovsFwdCtx->destinationPorts);
174 /* Ensure that none of the elements are consumed yet. */
175 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
176 fwdDetail->NumAvailableDestinations);
177 } else {
178 ovsFwdCtx->destinationPorts = NULL;
179 }
180 ovsFwdCtx->destPortsSizeIn = fwdDetail->NumAvailableDestinations;
181 ovsFwdCtx->destPortsSizeOut = 0;
182 ovsFwdCtx->srcVportNo = srcVportNo;
183 ovsFwdCtx->sendFlags = sendFlags;
184 if (layers) {
185 ovsFwdCtx->layers = *layers;
186 } else {
187 RtlZeroMemory(&ovsFwdCtx->layers, sizeof ovsFwdCtx->layers);
188 }
189 if (resetTunnelInfo) {
190 ovsFwdCtx->tunnelTxNic = NULL;
191 ovsFwdCtx->tunnelRxNic = NULL;
192 RtlZeroMemory(&ovsFwdCtx->tunKey, sizeof ovsFwdCtx->tunKey);
193 }
194
195 return NDIS_STATUS_SUCCESS;
196}
197
198/*
199 * --------------------------------------------------------------------------
200 * OvsDetectTunnelRxPkt --
201 * Utility function for an RX packet to detect its tunnel type.
202 *
203 * Result:
204 * True - if the tunnel type was detected.
205 * False - if not a tunnel packet or tunnel type not supported.
206 * --------------------------------------------------------------------------
207 */
208static __inline BOOLEAN
209OvsDetectTunnelRxPkt(OvsForwardingContext *ovsFwdCtx,
210 const OvsFlowKey *flowKey)
211{
212 POVS_VPORT_ENTRY tunnelVport = NULL;
213
214 /* XXX: we should also check for the length of the UDP payload to pick
215 * packets only if they are at least VXLAN header size.
216 */
85571a3d 217 if (!flowKey->ipKey.nwFrag) {
022c2040 218 UINT16 dstPort = htons(flowKey->ipKey.l4.tpDst);
85571a3d
AS
219 switch (flowKey->ipKey.nwProto) {
220 case IPPROTO_GRE:
221 tunnelVport = OvsFindTunnelVportByPortType(ovsFwdCtx->switchContext,
222 OVS_VPORT_TYPE_GRE);
223 if (tunnelVport) {
224 ovsActionStats.rxGre++;
225 }
226 break;
227 case IPPROTO_TCP:
228 tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
229 dstPort,
230 OVS_VPORT_TYPE_STT);
231 if (tunnelVport) {
232 ovsActionStats.rxStt++;
233 }
234 break;
235 case IPPROTO_UDP:
236 tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
237 dstPort,
238 OVS_VPORT_TYPE_VXLAN);
239 if (tunnelVport) {
240 ovsActionStats.rxVxlan++;
241 }
242 break;
022c2040 243 }
c803536e
SS
244 }
245
246 // We might get tunnel packets even before the tunnel gets initialized.
247 if (tunnelVport) {
248 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
249 ovsFwdCtx->tunnelRxNic = tunnelVport;
250 return TRUE;
251 }
252
253 return FALSE;
254}
255
256/*
257 * --------------------------------------------------------------------------
258 * OvsDetectTunnelPkt --
b2d9d3e8
NR
259 * Utility function to detect if a packet is to be subjected to
260 * tunneling (Tx) or de-tunneling (Rx). Various factors such as source
261 * port, destination port, packet contents, and previously setup tunnel
262 * context are used.
c803536e
SS
263 *
264 * Result:
b2d9d3e8
NR
265 * True - If the packet is to be subjected to tunneling.
266 * In case of invalid tunnel context, the tunneling functionality is
267 * a no-op and is completed within this function itself by consuming
268 * all of the tunneling context.
269 * False - If not a tunnel packet or tunnel type not supported. Caller should
270 * process the packet as a non-tunnel packet.
c803536e
SS
271 * --------------------------------------------------------------------------
272 */
273static __inline BOOLEAN
274OvsDetectTunnelPkt(OvsForwardingContext *ovsFwdCtx,
275 const POVS_VPORT_ENTRY dstVport,
276 const OvsFlowKey *flowKey)
277{
c803536e 278 if (OvsIsInternalVportType(dstVport->ovsType)) {
b2d9d3e8
NR
279 /*
280 * Rx:
281 * The source of NBL during tunneling Rx could be the external
282 * port or if it is being executed from userspace, the source port is
283 * default port.
284 */
7434992b
NR
285 BOOLEAN validSrcPort =
286 (ovsFwdCtx->fwdDetail->SourcePortId ==
287 ovsFwdCtx->switchContext->virtualExternalPortId) ||
288 (ovsFwdCtx->fwdDetail->SourcePortId ==
289 NDIS_SWITCH_DEFAULT_PORT_ID);
c803536e
SS
290
291 if (validSrcPort && OvsDetectTunnelRxPkt(ovsFwdCtx, flowKey)) {
292 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
293 ASSERT(ovsFwdCtx->tunnelRxNic != NULL);
294 return TRUE;
295 }
296 } else if (OvsIsTunnelVportType(dstVport->ovsType)) {
297 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
298 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
b2d9d3e8
NR
299
300 /*
301 * Tx:
302 * The destination port is a tunnel port. Encapsulation must be
30bc8153
NR
303 * performed only on packets that originate from:
304 * - a VIF port
305 * - a bridge-internal port (packets generated from userspace)
306 * - no port.
b2d9d3e8
NR
307 *
308 * If the packet will not be encapsulated, consume the tunnel context
309 * by clearing it.
310 */
12e888ba 311 if (ovsFwdCtx->srcVportNo != OVS_DPPORT_NUMBER_INVALID) {
429d4556
AS
312
313 POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(
314 ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
315
30bc8153
NR
316 if (!vport ||
317 (vport->ovsType != OVS_VPORT_TYPE_NETDEV &&
318 !OvsIsBridgeInternalVport(vport))) {
429d4556
AS
319 ovsFwdCtx->tunKey.dst = 0;
320 }
b2d9d3e8
NR
321 }
322
323 /* Tunnel the packet only if tunnel context is set. */
324 if (ovsFwdCtx->tunKey.dst != 0) {
022c2040 325 switch(dstVport->ovsType) {
85571a3d
AS
326 case OVS_VPORT_TYPE_GRE:
327 ovsActionStats.txGre++;
328 break;
022c2040
EE
329 case OVS_VPORT_TYPE_VXLAN:
330 ovsActionStats.txVxlan++;
331 break;
332 case OVS_VPORT_TYPE_STT:
333 ovsActionStats.txStt++;
334 break;
335 }
b2d9d3e8
NR
336 ovsFwdCtx->tunnelTxNic = dstVport;
337 }
338
c803536e
SS
339 return TRUE;
340 }
341
342 return FALSE;
343}
344
345
346/*
347 * --------------------------------------------------------------------------
348 * OvsAddPorts --
349 * Add the specified destination vport into the forwarding context. If the
350 * vport is a VIF/external port, it is added directly to the NBL. If it is
351 * a tunneling port, it is NOT added to the NBL.
352 *
353 * Result:
354 * NDIS_STATUS_SUCCESS on success
355 * Other NDIS_STATUS upon failure.
356 * --------------------------------------------------------------------------
357 */
358static __inline NDIS_STATUS
359OvsAddPorts(OvsForwardingContext *ovsFwdCtx,
360 OvsFlowKey *flowKey,
361 NDIS_SWITCH_PORT_ID dstPortId,
362 BOOLEAN preserveVLAN,
363 BOOLEAN preservePriority)
364{
365 POVS_VPORT_ENTRY vport;
366 PNDIS_SWITCH_PORT_DESTINATION fwdPort;
367 NDIS_STATUS status;
368 POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext;
369
370 /*
371 * We hold the dispatch lock that protects the list of vports, so vports
372 * validated here can be added as destinations safely before we call into
373 * NDIS.
374 *
375 * Some of the vports can be tunnelled ports as well in which case
376 * they should be added to a separate list of tunnelled destination ports
377 * instead of the VIF ports. The context for the tunnel is settable
378 * in OvsForwardingContext.
379 */
380 vport = OvsFindVportByPortNo(ovsFwdCtx->switchContext, dstPortId);
381 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
382 /*
383 * There may be some latency between a port disappearing, and userspace
384 * updating the recalculated flows. In the meantime, handle invalid
385 * ports gracefully.
386 */
387 ovsActionStats.noVport++;
388 return NDIS_STATUS_SUCCESS;
389 }
390 ASSERT(vport->nicState == NdisSwitchNicStateConnected);
391 vport->stats.txPackets++;
392 vport->stats.txBytes +=
393 NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl));
394
7434992b
NR
395 if (OvsIsBridgeInternalVport(vport)) {
396 return NDIS_STATUS_SUCCESS;
397 }
398
c803536e 399 if (OvsDetectTunnelPkt(ovsFwdCtx, vport, flowKey)) {
c803536e
SS
400 return NDIS_STATUS_SUCCESS;
401 }
402
403 if (ovsFwdCtx->destPortsSizeOut == ovsFwdCtx->destPortsSizeIn) {
404 if (ovsFwdCtx->destPortsSizeIn == 0) {
405 ASSERT(ovsFwdCtx->destinationPorts == NULL);
406 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0);
407 status =
408 switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations(
409 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
410 OVS_DEST_PORTS_ARRAY_MIN_SIZE,
411 &ovsFwdCtx->destinationPorts);
412 if (status != NDIS_STATUS_SUCCESS) {
413 ovsActionStats.cannotGrowDest++;
414 return status;
415 }
416 ovsFwdCtx->destPortsSizeIn =
417 ovsFwdCtx->fwdDetail->NumAvailableDestinations;
418 ASSERT(ovsFwdCtx->destinationPorts);
419 } else {
420 ASSERT(ovsFwdCtx->destinationPorts != NULL);
421 /*
422 * NumElements:
423 * A ULONG value that specifies the total number of
424 * NDIS_SWITCH_PORT_DESTINATION elements in the
425 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure.
426 *
427 * NumDestinations:
428 * A ULONG value that specifies the number of
429 * NDIS_SWITCH_PORT_DESTINATION elements in the
430 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure that
431 * specify port destinations.
432 *
433 * NumAvailableDestinations:
434 * A value that specifies the number of unused extensible switch
435 * destination ports elements within an NET_BUFFER_LIST structure.
436 */
437 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
438 ovsFwdCtx->destPortsSizeIn);
439 ASSERT(ovsFwdCtx->destinationPorts->NumDestinations ==
440 ovsFwdCtx->destPortsSizeOut -
441 ovsFwdCtx->fwdDetail->NumAvailableDestinations);
442 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations > 0);
443 /*
444 * Before we grow the array of destination ports, the current set
445 * of ports needs to be committed. Only the ports added since the
446 * last commit need to be part of the new update.
447 */
448 status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations(
449 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
450 ovsFwdCtx->fwdDetail->NumAvailableDestinations,
451 ovsFwdCtx->destinationPorts);
452 if (status != NDIS_STATUS_SUCCESS) {
453 ovsActionStats.cannotGrowDest++;
454 return status;
455 }
456 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
457 ovsFwdCtx->destPortsSizeIn);
458 ASSERT(ovsFwdCtx->destinationPorts->NumDestinations ==
459 ovsFwdCtx->destPortsSizeOut);
460 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0);
461
462 status = switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations(
463 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
464 ovsFwdCtx->destPortsSizeIn, &ovsFwdCtx->destinationPorts);
465 if (status != NDIS_STATUS_SUCCESS) {
466 ovsActionStats.cannotGrowDest++;
467 return status;
468 }
469 ASSERT(ovsFwdCtx->destinationPorts != NULL);
470 ovsFwdCtx->destPortsSizeIn <<= 1;
471 }
472 }
473
474 ASSERT(ovsFwdCtx->destPortsSizeOut < ovsFwdCtx->destPortsSizeIn);
475 fwdPort =
476 NDIS_SWITCH_PORT_DESTINATION_AT_ARRAY_INDEX(ovsFwdCtx->destinationPorts,
477 ovsFwdCtx->destPortsSizeOut);
478
479 fwdPort->PortId = vport->portId;
480 fwdPort->NicIndex = vport->nicIndex;
481 fwdPort->IsExcluded = 0;
482 fwdPort->PreserveVLAN = preserveVLAN;
483 fwdPort->PreservePriority = preservePriority;
484 ovsFwdCtx->destPortsSizeOut += 1;
485
486 return NDIS_STATUS_SUCCESS;
487}
488
489
490/*
491 * --------------------------------------------------------------------------
492 * OvsClearTunTxCtx --
493 * Utility function to clear tx tunneling context.
494 * --------------------------------------------------------------------------
495 */
496static __inline VOID
497OvsClearTunTxCtx(OvsForwardingContext *ovsFwdCtx)
498{
499 ovsFwdCtx->tunnelTxNic = NULL;
500 ovsFwdCtx->tunKey.dst = 0;
501}
502
503
504/*
505 * --------------------------------------------------------------------------
506 * OvsClearTunRxCtx --
507 * Utility function to clear rx tunneling context.
508 * --------------------------------------------------------------------------
509 */
510static __inline VOID
511OvsClearTunRxCtx(OvsForwardingContext *ovsFwdCtx)
512{
513 ovsFwdCtx->tunnelRxNic = NULL;
514 ovsFwdCtx->tunKey.dst = 0;
515}
516
517
518/*
519 * --------------------------------------------------------------------------
520 * OvsCompleteNBLForwardingCtx --
521 * This utility function is responsible for freeing/completing an NBL - either
522 * by adding it to a completion list or by freeing it.
523 *
524 * Side effects:
525 * It also resets the necessary fields in 'ovsFwdCtx'.
526 * --------------------------------------------------------------------------
527 */
528static __inline VOID
529OvsCompleteNBLForwardingCtx(OvsForwardingContext *ovsFwdCtx,
530 PCWSTR dropReason)
531{
532 NDIS_STRING filterReason;
533
534 RtlInitUnicodeString(&filterReason, dropReason);
535 if (ovsFwdCtx->completionList) {
536 OvsAddPktCompletionList(ovsFwdCtx->completionList, TRUE,
537 ovsFwdCtx->fwdDetail->SourcePortId, ovsFwdCtx->curNbl, 1,
538 &filterReason);
539 ovsFwdCtx->curNbl = NULL;
540 } else {
541 /* If there is no completionList, we assume this is ovs created NBL */
542 ovsFwdCtx->curNbl = OvsCompleteNBL(ovsFwdCtx->switchContext,
543 ovsFwdCtx->curNbl, TRUE);
544 ASSERT(ovsFwdCtx->curNbl == NULL);
545 }
546 /* XXX: these can be made debug only to save cycles. Ideally the pipeline
547 * using these fields should reset the values at the end of the pipeline. */
548 ovsFwdCtx->destPortsSizeOut = 0;
549 ovsFwdCtx->tunnelTxNic = NULL;
550 ovsFwdCtx->tunnelRxNic = NULL;
551}
552
553/*
554 * --------------------------------------------------------------------------
555 * OvsDoFlowLookupOutput --
556 * Function to be used for the second stage of a tunneling workflow, ie.:
557 * - On the encapsulated packet on Tx path, to do a flow extract, flow
558 * lookup and excuting the actions.
559 * - On the decapsulated packet on Rx path, to do a flow extract, flow
560 * lookup and excuting the actions.
561 *
562 * XXX: It is assumed that the NBL in 'ovsFwdCtx' is owned by OVS. This is
563 * until the new buffer management framework is adopted.
564 *
565 * Side effects:
566 * The NBL in 'ovsFwdCtx' is consumed.
567 * --------------------------------------------------------------------------
568 */
569static __inline NDIS_STATUS
570OvsDoFlowLookupOutput(OvsForwardingContext *ovsFwdCtx)
571{
ee25964a
SV
572 OvsFlowKey key = { 0 };
573 OvsFlow *flow = NULL;
574 UINT64 hash = 0;
575 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
c803536e
SS
576 POVS_VPORT_ENTRY vport =
577 OvsFindVportByPortNo(ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
578 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
c803536e
SS
579 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
580 L"OVS-Dropped due to internal/tunnel port removal");
581 ovsActionStats.noVport++;
582 return NDIS_STATUS_SUCCESS;
583 }
584 ASSERT(vport->nicState == NdisSwitchNicStateConnected);
585
586 /* Assert that in the Rx direction, key is always setup. */
587 ASSERT(ovsFwdCtx->tunnelRxNic == NULL || ovsFwdCtx->tunKey.dst != 0);
4c470e88
SV
588 status =
589 OvsExtractFlow(ovsFwdCtx->curNbl, ovsFwdCtx->srcVportNo,
590 &key, &ovsFwdCtx->layers,
591 ovsFwdCtx->tunKey.dst != 0 ? &ovsFwdCtx->tunKey : NULL);
c803536e
SS
592 if (status != NDIS_STATUS_SUCCESS) {
593 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
594 L"OVS-Flow extract failed");
595 ovsActionStats.failedFlowExtract++;
596 return status;
597 }
598
599 flow = OvsLookupFlow(&ovsFwdCtx->switchContext->datapath, &key, &hash, FALSE);
600 if (flow) {
601 OvsFlowUsed(flow, ovsFwdCtx->curNbl, &ovsFwdCtx->layers);
602 ovsFwdCtx->switchContext->datapath.hits++;
ee25964a
SV
603 status = OvsDoExecuteActions(ovsFwdCtx->switchContext,
604 ovsFwdCtx->completionList,
605 ovsFwdCtx->curNbl,
606 ovsFwdCtx->srcVportNo,
607 ovsFwdCtx->sendFlags,
608 &key, &hash, &ovsFwdCtx->layers,
609 flow->actions, flow->actionsLen);
c803536e
SS
610 ovsFwdCtx->curNbl = NULL;
611 } else {
612 LIST_ENTRY missedPackets;
613 UINT32 num = 0;
614 ovsFwdCtx->switchContext->datapath.misses++;
615 InitializeListHead(&missedPackets);
4c470e88 616 status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS, vport,
640ebde7 617 &key,ovsFwdCtx->curNbl,
a422ea1d 618 FALSE, &ovsFwdCtx->layers,
640ebde7 619 ovsFwdCtx->switchContext, &missedPackets, &num);
c803536e 620 if (num) {
4a3c9b70 621 OvsQueuePackets(&missedPackets, num);
c803536e
SS
622 }
623 if (status == NDIS_STATUS_SUCCESS) {
624 /* Complete the packet since it was copied to user buffer. */
625 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
626 L"OVS-Dropped since packet was copied to userspace");
627 ovsActionStats.flowMiss++;
628 status = NDIS_STATUS_SUCCESS;
629 } else {
630 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
631 L"OVS-Dropped due to failure to queue to userspace");
632 status = NDIS_STATUS_FAILURE;
633 ovsActionStats.failedFlowMiss++;
634 }
635 }
636
637 return status;
638}
639
640/*
641 * --------------------------------------------------------------------------
642 * OvsTunnelPortTx --
643 * The start function for Tx tunneling - encapsulates the packet, and
644 * outputs the packet on the PIF bridge.
645 *
646 * Side effects:
647 * The NBL in 'ovsFwdCtx' is consumed.
648 * --------------------------------------------------------------------------
649 */
650static __inline NDIS_STATUS
651OvsTunnelPortTx(OvsForwardingContext *ovsFwdCtx)
652{
653 NDIS_STATUS status = NDIS_STATUS_FAILURE;
654 PNET_BUFFER_LIST newNbl = NULL;
655
656 /*
657 * Setup the source port to be the internal port to as to facilitate the
658 * second OvsLookupFlow.
659 */
022c2040
EE
660 if (ovsFwdCtx->switchContext->internalVport == NULL ||
661 ovsFwdCtx->switchContext->virtualExternalVport == NULL) {
ad0d70d2
EE
662 OvsClearTunTxCtx(ovsFwdCtx);
663 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
022c2040 664 L"OVS-Dropped since either internal or external port is absent");
ad0d70d2
EE
665 return NDIS_STATUS_FAILURE;
666 }
c803536e
SS
667 ovsFwdCtx->srcVportNo =
668 ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->portNo;
669
670 ovsFwdCtx->fwdDetail->SourcePortId = ovsFwdCtx->switchContext->internalPortId;
671 ovsFwdCtx->fwdDetail->SourceNicIndex =
672 ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->nicIndex;
673
674 /* Do the encap. Encap function does not consume the NBL. */
675 switch(ovsFwdCtx->tunnelTxNic->ovsType) {
85571a3d
AS
676 case OVS_VPORT_TYPE_GRE:
677 status = OvsEncapGre(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
678 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
679 &ovsFwdCtx->layers, &newNbl);
680 break;
e00afcf6 681 case OVS_VPORT_TYPE_VXLAN:
0b623ad5
NR
682 status = OvsEncapVxlan(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
683 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
c803536e
SS
684 &ovsFwdCtx->layers, &newNbl);
685 break;
022c2040
EE
686 case OVS_VPORT_TYPE_STT:
687 status = OvsEncapStt(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
0b623ad5 688 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
022c2040
EE
689 &ovsFwdCtx->layers, &newNbl);
690 break;
c803536e
SS
691 default:
692 ASSERT(! "Tx: Unhandled tunnel type");
693 }
694
695 /* Reset the tunnel context so that it doesn't get used after this point. */
696 OvsClearTunTxCtx(ovsFwdCtx);
697
698 if (status == NDIS_STATUS_SUCCESS) {
699 ASSERT(newNbl);
700 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
701 L"Complete after cloning NBL for encapsulation");
702 ovsFwdCtx->curNbl = newNbl;
703 status = OvsDoFlowLookupOutput(ovsFwdCtx);
704 ASSERT(ovsFwdCtx->curNbl == NULL);
705 } else {
706 /*
707 * XXX: Temporary freeing of the packet until we register a
708 * callback to IP helper.
709 */
710 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
711 L"OVS-Dropped due to encap failure");
712 ovsActionStats.failedEncap++;
713 status = NDIS_STATUS_SUCCESS;
714 }
715
716 return status;
717}
718
719/*
720 * --------------------------------------------------------------------------
721 * OvsTunnelPortRx --
722 * Decapsulate the incoming NBL based on the tunnel type and goes through
723 * the flow lookup for the inner packet.
724 *
725 * Note: IP checksum is validate here, but L4 checksum validation needs
726 * to be done by the corresponding tunnel types.
727 *
728 * Side effects:
729 * The NBL in 'ovsFwdCtx' is consumed.
730 * --------------------------------------------------------------------------
731 */
732static __inline NDIS_STATUS
733OvsTunnelPortRx(OvsForwardingContext *ovsFwdCtx)
734{
735 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
736 PNET_BUFFER_LIST newNbl = NULL;
737 POVS_VPORT_ENTRY tunnelRxVport = ovsFwdCtx->tunnelRxNic;
a422ea1d 738 PCWSTR dropReason = L"OVS-dropped due to new decap packet";
c803536e
SS
739
740 if (OvsValidateIPChecksum(ovsFwdCtx->curNbl, &ovsFwdCtx->layers)
741 != NDIS_STATUS_SUCCESS) {
742 ovsActionStats.failedChecksum++;
743 OVS_LOG_INFO("Packet dropped due to IP checksum failure.");
744 goto dropNbl;
745 }
746
022c2040
EE
747 /*
748 * Decap port functions should return a new NBL if it was copied, and
749 * this new NBL should be setup as the ovsFwdCtx->curNbl.
750 */
751
c803536e 752 switch(tunnelRxVport->ovsType) {
85571a3d
AS
753 case OVS_VPORT_TYPE_GRE:
754 status = OvsDecapGre(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
755 &ovsFwdCtx->tunKey, &newNbl);
756 break;
e00afcf6 757 case OVS_VPORT_TYPE_VXLAN:
022c2040
EE
758 status = OvsDecapVxlan(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
759 &ovsFwdCtx->tunKey, &newNbl);
760 break;
761 case OVS_VPORT_TYPE_STT:
762 status = OvsDecapStt(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
763 &ovsFwdCtx->tunKey, &newNbl);
a422ea1d
SV
764 if (status == NDIS_STATUS_SUCCESS && newNbl == NULL) {
765 /* This was an STT-LSO Fragment */
766 dropReason = L"OVS-STT segment is cached";
767 }
c803536e
SS
768 break;
769 default:
770 OVS_LOG_ERROR("Rx: Unhandled tunnel type: %d\n",
771 tunnelRxVport->ovsType);
772 ASSERT(! "Rx: Unhandled tunnel type");
773 status = NDIS_STATUS_NOT_SUPPORTED;
774 }
775
776 if (status != NDIS_STATUS_SUCCESS) {
777 ovsActionStats.failedDecap++;
778 goto dropNbl;
779 }
780
781 /*
782 * tunnelRxNic and other fields will be cleared, re-init the context
783 * before usage.
784 */
a422ea1d 785 OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason);
c803536e 786
a422ea1d
SV
787 if (newNbl) {
788 /* Decapsulated packet is in a new NBL */
789 ovsFwdCtx->tunnelRxNic = tunnelRxVport;
790 OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
791 newNbl, tunnelRxVport->portNo, 0,
792 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
793 ovsFwdCtx->completionList,
794 &ovsFwdCtx->layers, FALSE);
c803536e 795
a422ea1d
SV
796 /*
797 * Set the NBL's SourcePortId and SourceNicIndex to default values to
798 * keep NDIS happy when we forward the packet.
799 */
800 ovsFwdCtx->fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
801 ovsFwdCtx->fwdDetail->SourceNicIndex = 0;
c803536e 802
a422ea1d
SV
803 status = OvsDoFlowLookupOutput(ovsFwdCtx);
804 }
c803536e
SS
805 ASSERT(ovsFwdCtx->curNbl == NULL);
806 OvsClearTunRxCtx(ovsFwdCtx);
807
808 return status;
809
810dropNbl:
811 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
812 L"OVS-dropped due to decap failure");
813 OvsClearTunRxCtx(ovsFwdCtx);
814 return status;
815}
816
817
818/*
819 * --------------------------------------------------------------------------
820 * OvsOutputForwardingCtx --
821 * This function outputs an NBL to NDIS or to a tunneling pipeline based on
822 * the ports added so far into 'ovsFwdCtx'.
823 *
824 * Side effects:
825 * This function consumes the NBL - either by forwarding it successfully to
826 * NDIS, or adding it to the completion list in 'ovsFwdCtx', or freeing it.
827 *
828 * Also makes sure that the list of destination ports - tunnel or otherwise is
829 * drained.
830 * --------------------------------------------------------------------------
831 */
832static __inline NDIS_STATUS
833OvsOutputForwardingCtx(OvsForwardingContext *ovsFwdCtx)
834{
835 NDIS_STATUS status = STATUS_SUCCESS;
836 POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext;
eda457f1 837 PCWSTR dropReason;
c803536e
SS
838
839 /*
840 * Handle the case where the some of the destination ports are tunneled
841 * ports - the non-tunneled ports get a unmodified copy of the NBL, and the
842 * tunneling pipeline starts when we output the packet to tunneled port.
843 */
844 if (ovsFwdCtx->destPortsSizeOut > 0) {
845 PNET_BUFFER_LIST newNbl = NULL;
846 PNET_BUFFER nb;
847 UINT32 portsToUpdate =
848 ovsFwdCtx->fwdDetail->NumAvailableDestinations -
849 (ovsFwdCtx->destPortsSizeIn - ovsFwdCtx->destPortsSizeOut);
850
851 ASSERT(ovsFwdCtx->destinationPorts != NULL);
852
853 /*
854 * Create a copy of the packet in order to do encap on it later. Also,
855 * don't copy the offload context since the encap'd packet has a
856 * different set of headers. This will change when we implement offloads
857 * before doing encapsulation.
858 */
859 if (ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL) {
860 nb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
861 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
862 0, 0, TRUE /*copy NBL info*/);
863 if (newNbl == NULL) {
864 status = NDIS_STATUS_RESOURCES;
865 ovsActionStats.noCopiedNbl++;
eda457f1 866 dropReason = L"Dropped due to failure to create NBL copy.";
c803536e
SS
867 goto dropit;
868 }
869 }
870
871 /* It does not seem like we'll get here unless 'portsToUpdate' > 0. */
872 ASSERT(portsToUpdate > 0);
873 status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations(
874 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
875 portsToUpdate, ovsFwdCtx->destinationPorts);
876 if (status != NDIS_STATUS_SUCCESS) {
877 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
878 ovsActionStats.cannotGrowDest++;
eda457f1 879 dropReason = L"Dropped due to failure to update destinations.";
c803536e
SS
880 goto dropit;
881 }
882
883 OvsSendNBLIngress(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
884 ovsFwdCtx->sendFlags);
885 /* End this pipeline by resetting the corresponding context. */
886 ovsFwdCtx->destPortsSizeOut = 0;
887 ovsFwdCtx->curNbl = NULL;
888 if (newNbl) {
889 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
890 newNbl, ovsFwdCtx->srcVportNo, 0,
891 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
892 ovsFwdCtx->completionList,
893 &ovsFwdCtx->layers, FALSE);
894 if (status != NDIS_STATUS_SUCCESS) {
eda457f1 895 dropReason = L"Dropped due to resouces.";
c803536e
SS
896 goto dropit;
897 }
898 }
899 }
900
901 if (ovsFwdCtx->tunnelTxNic != NULL) {
902 status = OvsTunnelPortTx(ovsFwdCtx);
903 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
904 ASSERT(ovsFwdCtx->tunKey.dst == 0);
905 } else if (ovsFwdCtx->tunnelRxNic != NULL) {
906 status = OvsTunnelPortRx(ovsFwdCtx);
907 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
908 ASSERT(ovsFwdCtx->tunKey.dst == 0);
909 }
910 ASSERT(ovsFwdCtx->curNbl == NULL);
911
912 return status;
913
914dropit:
915 if (status != NDIS_STATUS_SUCCESS) {
eda457f1 916 OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason);
c803536e
SS
917 }
918
919 return status;
920}
921
922
923/*
924 * --------------------------------------------------------------------------
925 * OvsLookupFlowOutput --
926 * Utility function for external callers to do flow extract, lookup,
927 * actions execute on a given NBL.
928 *
929 * Note: If this is being used from a callback function, make sure that the
930 * arguments specified are still valid in the asynchronous context.
931 *
932 * Side effects:
933 * This function consumes the NBL.
934 * --------------------------------------------------------------------------
935 */
936VOID
937OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext,
938 VOID *compList,
939 PNET_BUFFER_LIST curNbl)
940{
941 NDIS_STATUS status;
942 OvsForwardingContext ovsFwdCtx;
943 POVS_VPORT_ENTRY internalVport =
944 (POVS_VPORT_ENTRY)switchContext->internalVport;
945
946 /* XXX: make sure comp list was not a stack variable previously. */
947 OvsCompletionList *completionList = (OvsCompletionList *)compList;
948
949 /*
950 * XXX: can internal port disappear while we are busy doing ARP resolution?
951 * It could, but will we get this callback from IP helper in that case. Need
952 * to check.
953 */
954 ASSERT(switchContext->internalVport);
955 status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl,
956 internalVport->portNo, 0,
957 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl),
958 completionList, NULL, TRUE);
959 if (status != NDIS_STATUS_SUCCESS) {
960 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
961 L"OVS-Dropped due to resources");
962 return;
963 }
964
965 ASSERT(FALSE);
966 /*
967 * XXX: We need to acquire the dispatch lock and the datapath lock.
968 */
969
970 OvsDoFlowLookupOutput(&ovsFwdCtx);
971}
972
973
974/*
975 * --------------------------------------------------------------------------
976 * OvsOutputBeforeSetAction --
977 * Function to be called to complete one set of actions on an NBL, before
978 * we start the next one.
979 * --------------------------------------------------------------------------
980 */
981static __inline NDIS_STATUS
982OvsOutputBeforeSetAction(OvsForwardingContext *ovsFwdCtx)
983{
984 PNET_BUFFER_LIST newNbl;
985 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
c803536e
SS
986
987 /*
988 * Create a copy and work on the copy after this point. The original NBL is
989 * forwarded. One reason to not use the copy for forwarding is that
990 * ports have already been added to the original NBL, and it might be
991 * inefficient/impossible to remove/re-add them to the copy. There's no
992 * notion of removing the ports, the ports need to be marked as
993 * "isExcluded". There's seems no real advantage to retaining the original
994 * and sending out the copy instead.
995 *
996 * XXX: We are copying the offload context here. This is to handle actions
997 * such as:
998 * outport, pop_vlan(), outport, push_vlan(), outport
999 *
1000 * copy size needs to include inner ether + IP + TCP, need to revisit
1001 * if we support IP options.
1002 * XXX Head room needs to include the additional encap.
1003 * XXX copySize check is not considering multiple NBs.
1004 */
c803536e
SS
1005 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1006 0, 0, TRUE /*copy NBL info*/);
1007
1008 ASSERT(ovsFwdCtx->destPortsSizeOut > 0 ||
1009 ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL);
1010
ba472491
AS
1011 /* Send the original packet out and save the original source port number */
1012 UINT32 tempVportNo = ovsFwdCtx->srcVportNo;
c803536e
SS
1013 status = OvsOutputForwardingCtx(ovsFwdCtx);
1014 ASSERT(ovsFwdCtx->curNbl == NULL);
1015 ASSERT(ovsFwdCtx->destPortsSizeOut == 0);
1016 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
1017 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
1018
1019 /* If we didn't make a copy, can't continue. */
1020 if (newNbl == NULL) {
1021 ovsActionStats.noCopiedNbl++;
1022 return NDIS_STATUS_RESOURCES;
1023 }
1024
1025 /* Finish the remaining actions with the new NBL */
1026 if (status != NDIS_STATUS_SUCCESS) {
1027 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
1028 } else {
1029 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
ba472491 1030 newNbl, tempVportNo, 0,
c803536e
SS
1031 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1032 ovsFwdCtx->completionList,
1033 &ovsFwdCtx->layers, FALSE);
1034 }
1035
1036 return status;
1037}
1038
1039
1040/*
1041 * --------------------------------------------------------------------------
5874d571
SV
1042 * OvsPopFieldInPacketBuf --
1043 * Function to pop a specified field of length 'shiftLength' located at
1044 * 'shiftOffset' from the ethernet header. The data on the left of the
1045 * 'shiftOffset' is right shifted.
1046 *
1047 * Returns a pointer to the new start in 'bufferData'.
c803536e
SS
1048 * --------------------------------------------------------------------------
1049 */
1050static __inline NDIS_STATUS
5874d571
SV
1051OvsPopFieldInPacketBuf(OvsForwardingContext *ovsFwdCtx,
1052 UINT32 shiftOffset,
1053 UINT32 shiftLength,
1054 PUINT8 *bufferData)
c803536e
SS
1055{
1056 PNET_BUFFER curNb;
1057 PMDL curMdl;
1058 PUINT8 bufferStart;
c803536e
SS
1059 UINT32 packetLen, mdlLen;
1060 PNET_BUFFER_LIST newNbl;
1061 NDIS_STATUS status;
5874d571 1062 PUINT8 tempBuffer[ETH_HEADER_LENGTH];
c803536e 1063
5874d571 1064 ASSERT(shiftOffset > ETH_ADDR_LENGTH);
c803536e
SS
1065
1066 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1067 0, 0, TRUE /* copy NBL info */);
1068 if (!newNbl) {
1069 ovsActionStats.noCopiedNbl++;
1070 return NDIS_STATUS_RESOURCES;
1071 }
1072
1073 /* Complete the original NBL and create a copy to modify. */
1074 OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to copy");
1075
5874d571
SV
1076 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext, newNbl,
1077 ovsFwdCtx->srcVportNo, 0,
c803536e
SS
1078 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1079 NULL, &ovsFwdCtx->layers, FALSE);
1080 if (status != NDIS_STATUS_SUCCESS) {
1081 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1082 L"Dropped due to resouces");
1083 return NDIS_STATUS_RESOURCES;
1084 }
1085
1086 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1087 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1088 ASSERT(curNb->Next == NULL);
1089 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1090 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1091 if (!bufferStart) {
1092 return NDIS_STATUS_RESOURCES;
1093 }
1094 mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
5874d571
SV
1095 /* Bail out if L2 + shiftLength is not contiguous in the first buffer. */
1096 if (MIN(packetLen, mdlLen) < sizeof(EthHdr) + shiftLength) {
c803536e
SS
1097 ASSERT(FALSE);
1098 return NDIS_STATUS_FAILURE;
1099 }
1100 bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
5874d571
SV
1101 RtlCopyMemory(tempBuffer, bufferStart, shiftOffset);
1102 RtlCopyMemory(bufferStart + shiftLength, tempBuffer, shiftOffset);
c803536e
SS
1103 NdisAdvanceNetBufferDataStart(curNb, shiftLength, FALSE, NULL);
1104
5874d571
SV
1105 if (bufferData) {
1106 *bufferData = bufferStart + shiftLength;
1107 }
1108
1109 return NDIS_STATUS_SUCCESS;
1110}
1111
1112
1113/*
1114 * --------------------------------------------------------------------------
1115 * OvsPopVlanInPktBuf --
1116 * Function to pop a VLAN tag when the tag is in the packet buffer.
1117 * --------------------------------------------------------------------------
1118 */
1119static __inline NDIS_STATUS
1120OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
1121{
1122 /*
1123 * Declare a dummy vlanTag structure since we need to compute the size
1124 * of shiftLength. The NDIS one is a unionized structure.
1125 */
1126 NDIS_PACKET_8021Q_INFO vlanTag = {0};
1127 UINT32 shiftLength = sizeof(vlanTag.TagHeader);
1128 UINT32 shiftOffset = sizeof(DL_EUI48) + sizeof(DL_EUI48);
1129
1130 return OvsPopFieldInPacketBuf(ovsFwdCtx, shiftOffset, shiftLength, NULL);
1131}
1132
1133
1134/*
1135 * --------------------------------------------------------------------------
1136 * OvsActionMplsPop --
1137 * Function to pop the first MPLS label from the current packet.
1138 * --------------------------------------------------------------------------
1139 */
1140static __inline NDIS_STATUS
1141OvsActionMplsPop(OvsForwardingContext *ovsFwdCtx,
1142 ovs_be16 ethertype)
1143{
1144 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1145 OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
1146 EthHdr *ethHdr = NULL;
1147
1148 status = OvsPopFieldInPacketBuf(ovsFwdCtx, sizeof(*ethHdr),
1149 MPLS_HLEN, (PUINT8*)&ethHdr);
1150 if (status == NDIS_STATUS_SUCCESS) {
1151 if (ethHdr && OvsEthertypeIsMpls(ethHdr->Type)) {
1152 ethHdr->Type = ethertype;
1153 }
1154
1155 layers->l3Offset -= MPLS_HLEN;
1156 layers->l4Offset -= MPLS_HLEN;
1157 }
1158
1159 return status;
1160}
1161
1162
1163/*
1164 * --------------------------------------------------------------------------
1165 * OvsActionMplsPush --
1166 * Function to push the MPLS label into the current packet.
1167 * --------------------------------------------------------------------------
1168 */
1169static __inline NDIS_STATUS
1170OvsActionMplsPush(OvsForwardingContext *ovsFwdCtx,
1171 const struct ovs_action_push_mpls *mpls)
1172{
1173 NDIS_STATUS status;
1174 PNET_BUFFER curNb = NULL;
1175 PMDL curMdl = NULL;
1176 PUINT8 bufferStart = NULL;
1177 OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
1178 EthHdr *ethHdr = NULL;
1179 MPLSHdr *mplsHdr = NULL;
1180 UINT32 mdlLen = 0, curMdlOffset = 0;
1181 PNET_BUFFER_LIST newNbl;
1182
1183 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1184 layers->l3Offset, MPLS_HLEN, TRUE);
1185 if (!newNbl) {
1186 ovsActionStats.noCopiedNbl++;
1187 return NDIS_STATUS_RESOURCES;
1188 }
1189 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1190 L"Complete after partial copy.");
1191
1192 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
1193 newNbl, ovsFwdCtx->srcVportNo, 0,
1194 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1195 NULL, &ovsFwdCtx->layers, FALSE);
1196 if (status != NDIS_STATUS_SUCCESS) {
1197 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1198 L"OVS-Dropped due to resources");
1199 return NDIS_STATUS_RESOURCES;
1200 }
1201
1202 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1203 ASSERT(curNb->Next == NULL);
1204
1205 status = NdisRetreatNetBufferDataStart(curNb, MPLS_HLEN, 0, NULL);
1206 if (status != NDIS_STATUS_SUCCESS) {
1207 return status;
1208 }
1209
1210 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1211 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1212 if (!curMdl) {
1213 ovsActionStats.noResource++;
1214 return NDIS_STATUS_RESOURCES;
1215 }
1216
1217 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1218 mdlLen -= curMdlOffset;
1219 ASSERT(mdlLen >= MPLS_HLEN);
1220
1221 ethHdr = (EthHdr *)(bufferStart + curMdlOffset);
1222 RtlMoveMemory(ethHdr, (UINT8*)ethHdr + MPLS_HLEN, sizeof(*ethHdr));
1223 ethHdr->Type = mpls->mpls_ethertype;
1224
1225 mplsHdr = (MPLSHdr *)(ethHdr + 1);
1226 mplsHdr->lse = mpls->mpls_lse;
1227
1228 layers->l3Offset += MPLS_HLEN;
1229 layers->l4Offset += MPLS_HLEN;
1230
c803536e
SS
1231 return NDIS_STATUS_SUCCESS;
1232}
1233
1234/*
1235 * --------------------------------------------------------------------------
1236 * OvsTunnelAttrToIPv4TunnelKey --
1237 * Convert tunnel attribute to OvsIPv4TunnelKey.
1238 * --------------------------------------------------------------------------
1239 */
1240static __inline NDIS_STATUS
d838e577 1241OvsTunnelAttrToIPv4TunnelKey(PNL_ATTR attr,
c803536e
SS
1242 OvsIPv4TunnelKey *tunKey)
1243{
d838e577 1244 PNL_ATTR a;
c803536e
SS
1245 INT rem;
1246
1247 tunKey->attr[0] = 0;
1248 tunKey->attr[1] = 0;
1249 tunKey->attr[2] = 0;
d838e577 1250 ASSERT(NlAttrType(attr) == OVS_KEY_ATTR_TUNNEL);
c803536e 1251
d838e577
AS
1252 NL_ATTR_FOR_EACH_UNSAFE (a, rem, NlAttrData(attr),
1253 NlAttrGetSize(attr)) {
1254 switch (NlAttrType(a)) {
c803536e 1255 case OVS_TUNNEL_KEY_ATTR_ID:
d838e577 1256 tunKey->tunnelId = NlAttrGetBe64(a);
c803536e
SS
1257 tunKey->flags |= OVS_TNL_F_KEY;
1258 break;
1259 case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
d838e577 1260 tunKey->src = NlAttrGetBe32(a);
c803536e
SS
1261 break;
1262 case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
d838e577 1263 tunKey->dst = NlAttrGetBe32(a);
c803536e
SS
1264 break;
1265 case OVS_TUNNEL_KEY_ATTR_TOS:
d838e577 1266 tunKey->tos = NlAttrGetU8(a);
c803536e
SS
1267 break;
1268 case OVS_TUNNEL_KEY_ATTR_TTL:
d838e577 1269 tunKey->ttl = NlAttrGetU8(a);
c803536e
SS
1270 break;
1271 case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
1272 tunKey->flags |= OVS_TNL_F_DONT_FRAGMENT;
1273 break;
1274 case OVS_TUNNEL_KEY_ATTR_CSUM:
1275 tunKey->flags |= OVS_TNL_F_CSUM;
1276 break;
1277 default:
1278 ASSERT(0);
1279 }
1280 }
1281
1282 return NDIS_STATUS_SUCCESS;
1283}
1284
1285/*
1286 *----------------------------------------------------------------------------
1287 * OvsUpdateEthHeader --
1288 * Updates the ethernet header in ovsFwdCtx.curNbl inline based on the
1289 * specified key.
1290 *----------------------------------------------------------------------------
1291 */
1292static __inline NDIS_STATUS
1293OvsUpdateEthHeader(OvsForwardingContext *ovsFwdCtx,
1294 const struct ovs_key_ethernet *ethAttr)
1295{
1296 PNET_BUFFER curNb;
1297 PMDL curMdl;
1298 PUINT8 bufferStart;
1299 EthHdr *ethHdr;
1300 UINT32 packetLen, mdlLen;
1301
1302 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1303 ASSERT(curNb->Next == NULL);
1304 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1305 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1306 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1307 if (!bufferStart) {
1308 ovsActionStats.noResource++;
1309 return NDIS_STATUS_RESOURCES;
1310 }
1311 mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1312 ASSERT(mdlLen > 0);
1313 /* Bail out if the L2 header is not in a contiguous buffer. */
1314 if (MIN(packetLen, mdlLen) < sizeof *ethHdr) {
1315 ASSERT(FALSE);
1316 return NDIS_STATUS_FAILURE;
1317 }
1318 ethHdr = (EthHdr *)(bufferStart + NET_BUFFER_CURRENT_MDL_OFFSET(curNb));
1319
1320 RtlCopyMemory(ethHdr->Destination, ethAttr->eth_dst,
1321 sizeof ethHdr->Destination);
1322 RtlCopyMemory(ethHdr->Source, ethAttr->eth_src, sizeof ethHdr->Source);
1323
1324 return NDIS_STATUS_SUCCESS;
1325}
1326
1327/*
1328 *----------------------------------------------------------------------------
1329 * OvsUpdateIPv4Header --
1330 * Updates the IPv4 header in ovsFwdCtx.curNbl inline based on the
1331 * specified key.
1332 *----------------------------------------------------------------------------
1333 */
1334static __inline NDIS_STATUS
1335OvsUpdateIPv4Header(OvsForwardingContext *ovsFwdCtx,
1336 const struct ovs_key_ipv4 *ipAttr)
1337{
1338 PNET_BUFFER curNb;
1339 PMDL curMdl;
1340 ULONG curMdlOffset;
1341 PUINT8 bufferStart;
1342 UINT32 mdlLen, hdrSize, packetLen;
1343 OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
1344 NDIS_STATUS status;
1345 IPHdr *ipHdr;
1346 TCPHdr *tcpHdr = NULL;
1347 UDPHdr *udpHdr = NULL;
1348
1349 ASSERT(layers->value != 0);
1350
1351 /*
1352 * Peek into the MDL to get a handle to the IP header and if required
1353 * the TCP/UDP header as well. We check if the required headers are in one
1354 * contiguous MDL, and if not, we copy them over to one MDL.
1355 */
1356 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1357 ASSERT(curNb->Next == NULL);
1358 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1359 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1360 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1361 if (!bufferStart) {
1362 ovsActionStats.noResource++;
1363 return NDIS_STATUS_RESOURCES;
1364 }
1365 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1366 mdlLen -= curMdlOffset;
1367 ASSERT((INT)mdlLen >= 0);
1368
1369 if (layers->isTcp || layers->isUdp) {
1370 hdrSize = layers->l4Offset +
1371 layers->isTcp ? sizeof (*tcpHdr) : sizeof (*udpHdr);
1372 } else {
1373 hdrSize = layers->l3Offset + sizeof (*ipHdr);
1374 }
1375
1376 /* Count of number of bytes of valid data there are in the first MDL. */
1377 mdlLen = MIN(packetLen, mdlLen);
1378 if (mdlLen < hdrSize) {
1379 PNET_BUFFER_LIST newNbl;
1380 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1381 hdrSize, 0, TRUE /*copy NBL info*/);
1382 if (!newNbl) {
1383 ovsActionStats.noCopiedNbl++;
1384 return NDIS_STATUS_RESOURCES;
1385 }
1386 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1387 L"Complete after partial copy.");
1388
1389 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
1390 newNbl, ovsFwdCtx->srcVportNo, 0,
1391 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1392 NULL, &ovsFwdCtx->layers, FALSE);
1393 if (status != NDIS_STATUS_SUCCESS) {
1394 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1395 L"OVS-Dropped due to resources");
1396 return NDIS_STATUS_RESOURCES;
1397 }
1398
1399 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1400 ASSERT(curNb->Next == NULL);
1401 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1402 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1403 if (!curMdl) {
1404 ovsActionStats.noResource++;
1405 return NDIS_STATUS_RESOURCES;
1406 }
1407 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1408 mdlLen -= curMdlOffset;
1409 ASSERT(mdlLen >= hdrSize);
1410 }
1411
1412 ipHdr = (IPHdr *)(bufferStart + curMdlOffset + layers->l3Offset);
1413
1414 if (layers->isTcp) {
1415 tcpHdr = (TCPHdr *)(bufferStart + curMdlOffset + layers->l4Offset);
1416 } else if (layers->isUdp) {
1417 udpHdr = (UDPHdr *)(bufferStart + curMdlOffset + layers->l4Offset);
1418 }
1419
1420 /*
1421 * Adjust the IP header inline as dictated by the action, nad also update
1422 * the IP and the TCP checksum for the data modified.
1423 *
1424 * In the future, this could be optimized to make one call to
1425 * ChecksumUpdate32(). Ignoring this for now, since for the most common
1426 * case, we only update the TTL.
1427 */
1428 if (ipHdr->saddr != ipAttr->ipv4_src) {
1429 if (tcpHdr) {
1430 tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->saddr,
1431 ipAttr->ipv4_src);
1432 } else if (udpHdr && udpHdr->check) {
1433 udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->saddr,
1434 ipAttr->ipv4_src);
1435 }
1436
1437 if (ipHdr->check != 0) {
1438 ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->saddr,
1439 ipAttr->ipv4_src);
1440 }
1441 ipHdr->saddr = ipAttr->ipv4_src;
1442 }
1443 if (ipHdr->daddr != ipAttr->ipv4_dst) {
1444 if (tcpHdr) {
1445 tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->daddr,
1446 ipAttr->ipv4_dst);
1447 } else if (udpHdr && udpHdr->check) {
1448 udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->daddr,
1449 ipAttr->ipv4_dst);
1450 }
1451
1452 if (ipHdr->check != 0) {
1453 ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->daddr,
1454 ipAttr->ipv4_dst);
1455 }
1456 ipHdr->daddr = ipAttr->ipv4_dst;
1457 }
1458 if (ipHdr->protocol != ipAttr->ipv4_proto) {
1459 UINT16 oldProto = (ipHdr->protocol << 16) & 0xff00;
1460 UINT16 newProto = (ipAttr->ipv4_proto << 16) & 0xff00;
1461 if (tcpHdr) {
1462 tcpHdr->check = ChecksumUpdate16(tcpHdr->check, oldProto, newProto);
1463 } else if (udpHdr && udpHdr->check) {
1464 udpHdr->check = ChecksumUpdate16(udpHdr->check, oldProto, newProto);
1465 }
1466
1467 if (ipHdr->check != 0) {
1468 ipHdr->check = ChecksumUpdate16(ipHdr->check, oldProto, newProto);
1469 }
1470 ipHdr->protocol = ipAttr->ipv4_proto;
1471 }
1472 if (ipHdr->ttl != ipAttr->ipv4_ttl) {
1473 UINT16 oldTtl = (ipHdr->ttl) & 0xff;
1474 UINT16 newTtl = (ipAttr->ipv4_ttl) & 0xff;
1475 if (ipHdr->check != 0) {
1476 ipHdr->check = ChecksumUpdate16(ipHdr->check, oldTtl, newTtl);
1477 }
1478 ipHdr->ttl = ipAttr->ipv4_ttl;
1479 }
1480
1481 return NDIS_STATUS_SUCCESS;
1482}
1483
1484/*
1485 * --------------------------------------------------------------------------
1486 * OvsExecuteSetAction --
1487 * Executes a set() action, but storing the actions into 'ovsFwdCtx'
1488 * --------------------------------------------------------------------------
1489 */
1490static __inline NDIS_STATUS
1491OvsExecuteSetAction(OvsForwardingContext *ovsFwdCtx,
1492 OvsFlowKey *key,
1493 UINT64 *hash,
d838e577 1494 const PNL_ATTR a)
c803536e 1495{
d838e577 1496 enum ovs_key_attr type = NlAttrType(a);
c803536e
SS
1497 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1498
1499 switch (type) {
1500 case OVS_KEY_ATTR_ETHERNET:
1501 status = OvsUpdateEthHeader(ovsFwdCtx,
d838e577 1502 NlAttrGetUnspec(a, sizeof(struct ovs_key_ethernet)));
c803536e
SS
1503 break;
1504
1505 case OVS_KEY_ATTR_IPV4:
1506 status = OvsUpdateIPv4Header(ovsFwdCtx,
d838e577 1507 NlAttrGetUnspec(a, sizeof(struct ovs_key_ipv4)));
c803536e
SS
1508 break;
1509
1510 case OVS_KEY_ATTR_TUNNEL:
1511 {
1512 OvsIPv4TunnelKey tunKey;
022c2040 1513 status = OvsTunnelAttrToIPv4TunnelKey((PNL_ATTR)a, &tunKey);
c803536e
SS
1514 ASSERT(status == NDIS_STATUS_SUCCESS);
1515 tunKey.flow_hash = (uint16)(hash ? *hash : OvsHashFlow(key));
ffde5f8f 1516 tunKey.dst_port = key->ipKey.l4.tpDst;
c803536e 1517 RtlCopyMemory(&ovsFwdCtx->tunKey, &tunKey, sizeof ovsFwdCtx->tunKey);
c803536e
SS
1518 break;
1519 }
4ac06450 1520
c803536e 1521 default:
4ac06450 1522 OVS_LOG_INFO("Unhandled attribute %#x", type);
3819692e 1523 break;
c803536e
SS
1524 }
1525 return status;
1526}
1527
1528/*
1529 * --------------------------------------------------------------------------
ee25964a
SV
1530 * OvsExecuteRecirc --
1531 * The function adds a deferred action to allow the current packet, nbl,
1532 * to re-enter datapath packet processing.
1533 * --------------------------------------------------------------------------
1534 */
1535NDIS_STATUS
1536OvsExecuteRecirc(OvsForwardingContext *ovsFwdCtx,
1537 OvsFlowKey *key,
1538 const PNL_ATTR actions,
1539 int rem)
1540{
1541 POVS_DEFERRED_ACTION deferredAction = NULL;
1542 PNET_BUFFER_LIST newNbl = NULL;
1543
1544 if (!NlAttrIsLast(actions, rem)) {
1545 /*
1546 * Recirc action is the not the last action of the action list, so we
1547 * need to clone the packet.
1548 */
1549 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1550 0, 0, TRUE /*copy NBL info*/);
1551 /*
1552 * Skip the recirc action when out of memory, but continue on with the
1553 * rest of the action list.
1554 */
1555 if (newNbl == NULL) {
1556 ovsActionStats.noCopiedNbl++;
1557 return NDIS_STATUS_SUCCESS;
1558 }
1559 ovsFwdCtx->curNbl = newNbl;
1560 }
1561
1562 deferredAction = OvsAddDeferredActions(ovsFwdCtx->curNbl, key, NULL);
1563 if (deferredAction) {
1564 deferredAction->key.recircId = NlAttrGetU32(actions);
1565 } else {
1566 if (newNbl) {
1567 ovsActionStats.deferredActionsQueueFull++;
1568 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
1569 }
1570 }
1571
1572 return NDIS_STATUS_SUCCESS;
1573}
1574
245eedef
SV
1575/*
1576 * --------------------------------------------------------------------------
1577 * OvsExecuteHash --
1578 * The function updates datapath hash read from userspace.
1579 * --------------------------------------------------------------------------
1580 */
1581VOID
1582OvsExecuteHash(OvsFlowKey *key,
1583 const PNL_ATTR attr)
1584{
1585 struct ovs_action_hash *hash_act = NlAttrData(attr);
1586 UINT32 hash = 0;
1587
1588 hash = (UINT32)OvsHashFlow(key);
1589 hash = OvsJhashWords(&hash, 1, hash_act->hash_basis);
1590 if (!hash)
1591 hash = 1;
1592
1593 key->dpHash = hash;
1594}
1595
ee25964a
SV
1596/*
1597 * --------------------------------------------------------------------------
1598 * OvsDoExecuteActions --
1599 * Interpret and execute the specified 'actions' on the specified packet
c803536e
SS
1600 * 'curNbl'. The expectation is that if the packet needs to be dropped
1601 * (completed) for some reason, it is added to 'completionList' so that the
1602 * caller can complete the packet. If 'completionList' is NULL, the NBL is
1603 * assumed to be generated by OVS and freed up. Otherwise, the function
1604 * consumes the NBL by generating a NDIS send indication for the packet.
1605 *
1606 * There are one or more of "clone" NBLs that may get generated while
1607 * executing the actions. Upon any failures, the "cloned" NBLs are freed up,
1608 * and the caller does not have to worry about them.
1609 *
1610 * Success or failure is returned based on whether the specified actions
1611 * were executed successfully on the packet or not.
1612 * --------------------------------------------------------------------------
1613 */
1614NDIS_STATUS
ee25964a
SV
1615OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext,
1616 OvsCompletionList *completionList,
1617 PNET_BUFFER_LIST curNbl,
1618 UINT32 portNo,
1619 ULONG sendFlags,
1620 OvsFlowKey *key,
1621 UINT64 *hash,
1622 OVS_PACKET_HDR_INFO *layers,
1623 const PNL_ATTR actions,
1624 INT actionsLen)
c803536e 1625{
d838e577 1626 PNL_ATTR a;
c803536e
SS
1627 INT rem;
1628 UINT32 dstPortID;
1629 OvsForwardingContext ovsFwdCtx;
1630 PCWSTR dropReason = L"";
1631 NDIS_STATUS status;
1632 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail =
1633 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl);
1634
1635 /* XXX: ASSERT that the flow table lock is held. */
1636 status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl, portNo,
1637 sendFlags, fwdDetail, completionList,
1638 layers, TRUE);
1639 if (status != NDIS_STATUS_SUCCESS) {
1640 dropReason = L"OVS-initing destination port list failed";
1641 goto dropit;
1642 }
1643
1644 if (actionsLen == 0) {
1645 dropReason = L"OVS-Dropped due to Flow action";
1646 ovsActionStats.zeroActionLen++;
1647 goto dropit;
1648 }
1649
1650 NL_ATTR_FOR_EACH_UNSAFE (a, rem, actions, actionsLen) {
d838e577 1651 switch(NlAttrType(a)) {
c803536e 1652 case OVS_ACTION_ATTR_OUTPUT:
d838e577 1653 dstPortID = NlAttrGetU32(a);
c803536e
SS
1654 status = OvsAddPorts(&ovsFwdCtx, key, dstPortID,
1655 TRUE, TRUE);
1656 if (status != NDIS_STATUS_SUCCESS) {
1657 dropReason = L"OVS-adding destination port failed";
1658 goto dropit;
1659 }
1660 break;
1661
1662 case OVS_ACTION_ATTR_PUSH_VLAN:
1663 {
1664 struct ovs_action_push_vlan *vlan;
1665 PVOID vlanTagValue;
1666 PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTag;
1667
1668 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1669 || ovsFwdCtx.tunnelRxNic != NULL) {
1670 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1671 if (status != NDIS_STATUS_SUCCESS) {
1672 dropReason = L"OVS-adding destination failed";
1673 goto dropit;
1674 }
1675 }
1676
1677 vlanTagValue = NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1678 Ieee8021QNetBufferListInfo);
1679 if (vlanTagValue != NULL) {
1680 /*
1681 * XXX: We don't support double VLAN tag offload. In such cases,
1682 * we need to insert the existing one into the packet buffer,
1683 * and add the new one as offload. This will take care of
1684 * guest tag-in-tag case as well as OVS rules that specify
1685 * tag-in-tag.
1686 */
1687 } else {
1688 vlanTagValue = 0;
1689 vlanTag = (PNDIS_NET_BUFFER_LIST_8021Q_INFO)(PVOID *)&vlanTagValue;
d838e577 1690 vlan = (struct ovs_action_push_vlan *)NlAttrGet((const PNL_ATTR)a);
c803536e
SS
1691 vlanTag->TagHeader.VlanId = ntohs(vlan->vlan_tci) & 0xfff;
1692 vlanTag->TagHeader.UserPriority = ntohs(vlan->vlan_tci) >> 13;
1693
1694 NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1695 Ieee8021QNetBufferListInfo) = vlanTagValue;
1696 }
1697 break;
1698 }
1699
1700 case OVS_ACTION_ATTR_POP_VLAN:
1701 {
1702 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1703 || ovsFwdCtx.tunnelRxNic != NULL) {
1704 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1705 if (status != NDIS_STATUS_SUCCESS) {
1706 dropReason = L"OVS-adding destination failed";
1707 goto dropit;
1708 }
1709 }
1710
1711 if (NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1712 Ieee8021QNetBufferListInfo) != 0) {
1713 NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1714 Ieee8021QNetBufferListInfo) = 0;
1715 } else {
1716 /*
1717 * The VLAN tag is inserted into the packet buffer. Pop the tag
1718 * by packet buffer modification.
1719 */
1720 status = OvsPopVlanInPktBuf(&ovsFwdCtx);
1721 if (status != NDIS_STATUS_SUCCESS) {
1722 dropReason = L"OVS-pop vlan action failed";
1723 goto dropit;
1724 }
1725 }
1726 break;
1727 }
1728
5874d571
SV
1729 case OVS_ACTION_ATTR_PUSH_MPLS:
1730 {
1731 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1732 || ovsFwdCtx.tunnelRxNic != NULL) {
1733 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1734 if (status != NDIS_STATUS_SUCCESS) {
1735 dropReason = L"OVS-adding destination failed";
1736 goto dropit;
1737 }
1738 }
1739
1740 status = OvsActionMplsPush(&ovsFwdCtx,
1741 (struct ovs_action_push_mpls *)NlAttrGet
1742 ((const PNL_ATTR)a));
1743 if (status != NDIS_STATUS_SUCCESS) {
1744 dropReason = L"OVS-push MPLS action failed";
1745 goto dropit;
1746 }
1747 layers->l3Offset += MPLS_HLEN;
1748 layers->l4Offset += MPLS_HLEN;
1749 break;
1750 }
1751
1752 case OVS_ACTION_ATTR_POP_MPLS:
1753 {
1754 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1755 || ovsFwdCtx.tunnelRxNic != NULL) {
1756 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1757 if (status != NDIS_STATUS_SUCCESS) {
1758 dropReason = L"OVS-adding destination failed";
1759 goto dropit;
1760 }
1761 }
1762
1763 status = OvsActionMplsPop(&ovsFwdCtx, NlAttrGetBe16(a));
1764 if (status != NDIS_STATUS_SUCCESS) {
1765 dropReason = L"OVS-pop MPLS action failed";
1766 goto dropit;
1767 }
1768 layers->l3Offset -= MPLS_HLEN;
1769 layers->l4Offset -= MPLS_HLEN;
1770 break;
1771 }
1772
245eedef
SV
1773 case OVS_ACTION_ATTR_HASH:
1774 {
1775 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1776 || ovsFwdCtx.tunnelRxNic != NULL) {
1777 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1778 if (status != NDIS_STATUS_SUCCESS) {
1779 dropReason = L"OVS-adding destination failed";
1780 goto dropit;
1781 }
1782 }
1783
1784 OvsExecuteHash(key, (const PNL_ATTR)a);
1785
1786 break;
1787 }
1788
ee25964a
SV
1789 case OVS_ACTION_ATTR_RECIRC:
1790 {
1791 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1792 || ovsFwdCtx.tunnelRxNic != NULL) {
1793 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1794 if (status != NDIS_STATUS_SUCCESS) {
1795 dropReason = L"OVS-adding destination failed";
1796 goto dropit;
1797 }
1798 }
1799
1800 status = OvsExecuteRecirc(&ovsFwdCtx, key, (const PNL_ATTR)a, rem);
1801 if (status != NDIS_STATUS_SUCCESS) {
1802 dropReason = L"OVS-recirculation action failed";
1803 goto dropit;
1804 }
1805
1806 if (NlAttrIsLast(a, rem)) {
1807 goto exit;
1808 }
1809 break;
1810 }
1811
c803536e
SS
1812 case OVS_ACTION_ATTR_USERSPACE:
1813 {
d838e577
AS
1814 PNL_ATTR userdataAttr;
1815 PNL_ATTR queueAttr;
c803536e 1816 POVS_PACKET_QUEUE_ELEM elem;
429d4556
AS
1817 BOOLEAN isRecv = FALSE;
1818
1819 POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(switchContext,
1820 portNo);
1821
1822 if (vport) {
1823 if (vport->isExternal ||
1824 OvsIsTunnelVportType(vport->ovsType)) {
1825 isRecv = TRUE;
1826 }
1827 }
c803536e 1828
d838e577
AS
1829 queueAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_PID);
1830 userdataAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_USERDATA);
c803536e 1831
640ebde7
EE
1832 elem = OvsCreateQueueNlPacket((PVOID)userdataAttr,
1833 userdataAttr->nlaLen,
1834 OVS_PACKET_CMD_ACTION,
4c470e88 1835 vport, key, ovsFwdCtx.curNbl,
640ebde7
EE
1836 NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx.curNbl),
1837 isRecv,
1838 layers);
c803536e
SS
1839 if (elem) {
1840 LIST_ENTRY missedPackets;
1841 InitializeListHead(&missedPackets);
1842 InsertTailList(&missedPackets, &elem->link);
4a3c9b70 1843 OvsQueuePackets(&missedPackets, 1);
c803536e
SS
1844 dropReason = L"OVS-Completed since packet was copied to "
1845 L"userspace";
1846 } else {
1847 dropReason = L"OVS-Dropped due to failure to queue to "
1848 L"userspace";
1849 goto dropit;
1850 }
1851 break;
1852 }
1853 case OVS_ACTION_ATTR_SET:
1854 {
1855 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1856 || ovsFwdCtx.tunnelRxNic != NULL) {
1857 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1858 if (status != NDIS_STATUS_SUCCESS) {
1859 dropReason = L"OVS-adding destination failed";
1860 goto dropit;
1861 }
1862 }
1863
1864 status = OvsExecuteSetAction(&ovsFwdCtx, key, hash,
d838e577
AS
1865 (const PNL_ATTR)NlAttrGet
1866 ((const PNL_ATTR)a));
c803536e
SS
1867 if (status != NDIS_STATUS_SUCCESS) {
1868 dropReason = L"OVS-set action failed";
1869 goto dropit;
1870 }
1871 break;
1872 }
1873 case OVS_ACTION_ATTR_SAMPLE:
c803536e 1874 default:
7c5d9f17 1875 status = NDIS_STATUS_NOT_SUPPORTED;
c803536e
SS
1876 break;
1877 }
1878 }
1879
1880 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1881 || ovsFwdCtx.tunnelRxNic != NULL) {
1882 status = OvsOutputForwardingCtx(&ovsFwdCtx);
1883 ASSERT(ovsFwdCtx.curNbl == NULL);
1884 }
1885
1886 ASSERT(ovsFwdCtx.destPortsSizeOut == 0);
1887 ASSERT(ovsFwdCtx.tunnelRxNic == NULL);
1888 ASSERT(ovsFwdCtx.tunnelTxNic == NULL);
1889
1890dropit:
1891 /*
1892 * If curNbl != NULL, it implies the NBL has not been not freed up so far.
1893 */
1894 if (ovsFwdCtx.curNbl) {
1895 OvsCompleteNBLForwardingCtx(&ovsFwdCtx, dropReason);
1896 }
1897
ee25964a
SV
1898exit:
1899 return status;
1900}
1901
1902/*
1903 * --------------------------------------------------------------------------
1904 * OvsActionsExecute --
1905 * The function interprets and executes the specified 'actions' on the
1906 * specified packet 'curNbl'. See 'OvsDoExecuteActions' description for
1907 * more details.
1908 *
1909 * Also executes deferred actions added by recirculation or sample
1910 * actions.
1911 * --------------------------------------------------------------------------
1912 */
1913NDIS_STATUS
1914OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
1915 OvsCompletionList *completionList,
1916 PNET_BUFFER_LIST curNbl,
1917 UINT32 portNo,
1918 ULONG sendFlags,
1919 OvsFlowKey *key,
1920 UINT64 *hash,
1921 OVS_PACKET_HDR_INFO *layers,
1922 const PNL_ATTR actions,
1923 INT actionsLen)
1924{
1925 NDIS_STATUS status = STATUS_SUCCESS;
1926
1927 status = OvsDoExecuteActions(switchContext, completionList, curNbl,
1928 portNo, sendFlags, key, hash, layers,
1929 actions, actionsLen);
1930
1931 if (status == STATUS_SUCCESS) {
1932 status = OvsProcessDeferredActions(switchContext, completionList,
1933 portNo, sendFlags, layers);
1934 }
1935
1936 return status;
1937}
1938
1939/*
1940 * --------------------------------------------------------------------------
1941 * OvsDoRecirc --
1942 * The function processes the packet 'curNbl' that re-entered datapath
1943 * packet processing after a recirculation action.
1944 * --------------------------------------------------------------------------
1945 */
1946NDIS_STATUS
1947OvsDoRecirc(POVS_SWITCH_CONTEXT switchContext,
1948 OvsCompletionList *completionList,
1949 PNET_BUFFER_LIST curNbl,
1950 OvsFlowKey *key,
1951 UINT32 srcPortNo,
1952 OVS_PACKET_HDR_INFO *layers)
1953{
1954 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1955 OvsFlow *flow = NULL;
1956 OvsForwardingContext ovsFwdCtx = { 0 };
1957 UINT64 hash = 0;
1958 ASSERT(layers);
1959
1960 OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl,
1961 srcPortNo, 0,
1962 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl),
1963 completionList, layers, TRUE);
1964
1965 status = OvsExtractFlow(ovsFwdCtx.curNbl, ovsFwdCtx.srcVportNo, key,
1966 &ovsFwdCtx.layers, NULL);
1967 if (status != NDIS_STATUS_SUCCESS) {
1968 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
1969 L"OVS-Dropped due to extract flow failure");
1970 ovsActionStats.failedFlowMiss++;
1971 return NDIS_STATUS_FAILURE;
1972 }
1973
1974 flow = OvsLookupFlow(&ovsFwdCtx.switchContext->datapath, key, &hash, FALSE);
1975 if (flow) {
1976 UINT32 level = OvsDeferredActionsLevelGet();
1977
1978 if (level > DEFERRED_ACTION_EXEC_LEVEL) {
1979 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
1980 L"OVS-Dropped due to deferred actions execution level limit \
1981 reached");
1982 ovsActionStats.deferredActionsExecLimit++;
1983 ovsFwdCtx.curNbl = NULL;
1984 return NDIS_STATUS_FAILURE;
1985 }
1986
1987 OvsFlowUsed(flow, ovsFwdCtx.curNbl, &ovsFwdCtx.layers);
1988 ovsFwdCtx.switchContext->datapath.hits++;
1989
1990 OvsDeferredActionsLevelInc();
1991
1992 status = OvsDoExecuteActions(ovsFwdCtx.switchContext,
1993 ovsFwdCtx.completionList,
1994 ovsFwdCtx.curNbl,
1995 ovsFwdCtx.srcVportNo,
1996 ovsFwdCtx.sendFlags,
1997 key, &hash, &ovsFwdCtx.layers,
1998 flow->actions, flow->actionsLen);
1999 ovsFwdCtx.curNbl = NULL;
2000
2001 OvsDeferredActionsLevelDec();
2002 } else {
2003 POVS_VPORT_ENTRY vport = NULL;
2004 LIST_ENTRY missedPackets;
2005 UINT32 num = 0;
2006
2007 ovsFwdCtx.switchContext->datapath.misses++;
2008 InitializeListHead(&missedPackets);
2009 vport = OvsFindVportByPortNo(switchContext, srcPortNo);
2010 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
2011 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
2012 L"OVS-Dropped due to port removal");
2013 ovsActionStats.noVport++;
2014 return NDIS_STATUS_SUCCESS;
2015 }
2016 status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS,
2017 vport, key, ovsFwdCtx.curNbl,
e97d67f4 2018 vport->portId ==
ee25964a
SV
2019 switchContext->virtualExternalPortId,
2020 &ovsFwdCtx.layers,
2021 ovsFwdCtx.switchContext,
2022 &missedPackets, &num);
2023 if (num) {
2024 OvsQueuePackets(&missedPackets, num);
2025 }
2026 if (status == NDIS_STATUS_SUCCESS) {
2027 /* Complete the packet since it was copied to user buffer. */
2028 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
2029 L"OVS-Dropped since packet was copied to userspace");
2030 ovsActionStats.flowMiss++;
2031 } else {
2032 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
2033 L"OVS-Dropped due to failure to queue to userspace");
2034 ovsActionStats.failedFlowMiss++;
2035 status = NDIS_STATUS_FAILURE;
2036 }
2037 }
2038
c803536e
SS
2039 return status;
2040}