]> git.proxmox.com Git - ovs.git/blame - datapath-windows/ovsext/Actions.c
datapath: Backport: vxlan: interpret IP headers for ECN correctly
[ovs.git] / datapath-windows / ovsext / Actions.c
CommitLineData
c803536e
SS
1/*
2 * Copyright (c) 2014 VMware, Inc.
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
fa1324c9
SG
19#include "Switch.h"
20#include "Vport.h"
21#include "Event.h"
22#include "User.h"
23#include "NetProto.h"
24#include "Flow.h"
25#include "Vxlan.h"
022c2040 26#include "Stt.h"
fa1324c9
SG
27#include "Checksum.h"
28#include "PacketIO.h"
c803536e 29
c803536e
SS
30#ifdef OVS_DBG_MOD
31#undef OVS_DBG_MOD
32#endif
33#define OVS_DBG_MOD OVS_DBG_ACTION
fa1324c9 34#include "Debug.h"
c803536e
SS
35
36typedef struct _OVS_ACTION_STATS {
37 UINT64 rxVxlan;
38 UINT64 txVxlan;
022c2040
EE
39 UINT64 rxStt;
40 UINT64 txStt;
c803536e
SS
41 UINT64 flowMiss;
42 UINT64 flowUserspace;
43 UINT64 txTcp;
44 UINT32 failedFlowMiss;
45 UINT32 noVport;
46 UINT32 failedFlowExtract;
47 UINT32 noResource;
48 UINT32 noCopiedNbl;
49 UINT32 failedEncap;
50 UINT32 failedDecap;
51 UINT32 cannotGrowDest;
52 UINT32 zeroActionLen;
53 UINT32 failedChecksum;
54} OVS_ACTION_STATS, *POVS_ACTION_STATS;
55
56OVS_ACTION_STATS ovsActionStats;
57
58/*
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.
64 */
65#define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2
66typedef 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. */
75 ULONG sendFlags;
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;
80 /*
81 * If 'curNbl' is not owned by OVS, they need to be tracked, if they need to
82 * be freed/completed.
83 */
84 OvsCompletionList *completionList;
85 /*
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.
88 */
89 UINT32 srcVportNo;
90
91 /*
92 * Tunnel key:
93 * - specified in actions during tunneling Tx
94 * - extracted from an NBL during tunneling Rx
95 */
96 OvsIPv4TunnelKey tunKey;
97
98 /*
99 * Tunneling - Tx:
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.
102 */
103 POVS_VPORT_ENTRY tunnelTxNic;
104
105 /*
106 * Tunneling - Rx:
107 * Points to the Internal port on the PIF Bridge, if the packet needs to be
108 * de-tunneled.
109 */
110 POVS_VPORT_ENTRY tunnelRxNic;
111
112 /* header information */
113 OVS_PACKET_HDR_INFO layers;
114} OvsForwardingContext;
115
116
117/*
118 * --------------------------------------------------------------------------
119 * OvsInitForwardingCtx --
120 * Function to init/re-init the 'ovsFwdCtx' context as the actions pipeline
121 * is being executed.
122 *
123 * Result:
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 * --------------------------------------------------------------------------
129 */
130static __inline NDIS_STATUS
131OvsInitForwardingCtx(OvsForwardingContext *ovsFwdCtx,
132 POVS_SWITCH_CONTEXT switchContext,
133 PNET_BUFFER_LIST curNbl,
134 UINT32 srcVportNo,
135 ULONG sendFlags,
136 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail,
137 OvsCompletionList *completionList,
138 OVS_PACKET_HDR_INFO *layers,
139 BOOLEAN resetTunnelInfo)
140{
141 ASSERT(ovsFwdCtx);
142 ASSERT(switchContext);
143 ASSERT(curNbl);
144 ASSERT(fwdDetail);
145
146 /*
147 * Set values for curNbl and switchContext so upon failures, we have enough
148 * information to do cleanup.
149 */
150 ovsFwdCtx->curNbl = curNbl;
151 ovsFwdCtx->switchContext = switchContext;
152 ovsFwdCtx->completionList = completionList;
153 ovsFwdCtx->fwdDetail = fwdDetail;
154
155 if (fwdDetail->NumAvailableDestinations > 0) {
156 /*
157 * XXX: even though MSDN says GetNetBufferListDestinations() returns
158 * NDIS_STATUS, the header files say otherwise.
159 */
160 switchContext->NdisSwitchHandlers.GetNetBufferListDestinations(
161 switchContext->NdisSwitchContext, curNbl,
162 &ovsFwdCtx->destinationPorts);
163
164 ASSERT(ovsFwdCtx->destinationPorts);
165 /* Ensure that none of the elements are consumed yet. */
166 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
167 fwdDetail->NumAvailableDestinations);
168 } else {
169 ovsFwdCtx->destinationPorts = NULL;
170 }
171 ovsFwdCtx->destPortsSizeIn = fwdDetail->NumAvailableDestinations;
172 ovsFwdCtx->destPortsSizeOut = 0;
173 ovsFwdCtx->srcVportNo = srcVportNo;
174 ovsFwdCtx->sendFlags = sendFlags;
175 if (layers) {
176 ovsFwdCtx->layers = *layers;
177 } else {
178 RtlZeroMemory(&ovsFwdCtx->layers, sizeof ovsFwdCtx->layers);
179 }
180 if (resetTunnelInfo) {
181 ovsFwdCtx->tunnelTxNic = NULL;
182 ovsFwdCtx->tunnelRxNic = NULL;
183 RtlZeroMemory(&ovsFwdCtx->tunKey, sizeof ovsFwdCtx->tunKey);
184 }
185
186 return NDIS_STATUS_SUCCESS;
187}
188
189/*
190 * --------------------------------------------------------------------------
191 * OvsDetectTunnelRxPkt --
192 * Utility function for an RX packet to detect its tunnel type.
193 *
194 * Result:
195 * True - if the tunnel type was detected.
196 * False - if not a tunnel packet or tunnel type not supported.
197 * --------------------------------------------------------------------------
198 */
199static __inline BOOLEAN
200OvsDetectTunnelRxPkt(OvsForwardingContext *ovsFwdCtx,
201 const OvsFlowKey *flowKey)
202{
203 POVS_VPORT_ENTRY tunnelVport = NULL;
204
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.
207 */
208 if (!flowKey->ipKey.nwFrag &&
ffde5f8f 209 flowKey->ipKey.nwProto == IPPROTO_UDP) {
0b623ad5 210 UINT16 dstPort = ntohs(flowKey->ipKey.l4.tpDst);
ffde5f8f 211 tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext,
022c2040
EE
212 dstPort,
213 OVS_VPORT_TYPE_VXLAN);
214 if (tunnelVport) {
215 ovsActionStats.rxVxlan++;
216 }
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,
221 dstPort,
222 OVS_VPORT_TYPE_STT);
223 if (tunnelVport) {
224 ovsActionStats.rxStt++;
225 }
c803536e
SS
226 }
227
022c2040 228
c803536e
SS
229 // We might get tunnel packets even before the tunnel gets initialized.
230 if (tunnelVport) {
231 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
232 ovsFwdCtx->tunnelRxNic = tunnelVport;
233 return TRUE;
234 }
235
236 return FALSE;
237}
238
239/*
240 * --------------------------------------------------------------------------
241 * OvsDetectTunnelPkt --
b2d9d3e8
NR
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
245 * context are used.
c803536e
SS
246 *
247 * Result:
b2d9d3e8
NR
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.
c803536e
SS
254 * --------------------------------------------------------------------------
255 */
256static __inline BOOLEAN
257OvsDetectTunnelPkt(OvsForwardingContext *ovsFwdCtx,
258 const POVS_VPORT_ENTRY dstVport,
259 const OvsFlowKey *flowKey)
260{
c803536e 261 if (OvsIsInternalVportType(dstVport->ovsType)) {
b2d9d3e8
NR
262 /*
263 * Rx:
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
266 * default port.
267 */
7434992b
NR
268 BOOLEAN validSrcPort =
269 (ovsFwdCtx->fwdDetail->SourcePortId ==
270 ovsFwdCtx->switchContext->virtualExternalPortId) ||
271 (ovsFwdCtx->fwdDetail->SourcePortId ==
272 NDIS_SWITCH_DEFAULT_PORT_ID);
c803536e
SS
273
274 if (validSrcPort && OvsDetectTunnelRxPkt(ovsFwdCtx, flowKey)) {
275 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
276 ASSERT(ovsFwdCtx->tunnelRxNic != NULL);
277 return TRUE;
278 }
279 } else if (OvsIsTunnelVportType(dstVport->ovsType)) {
280 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
281 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
b2d9d3e8
NR
282
283 /*
284 * Tx:
285 * The destination port is a tunnel port. Encapsulation must be
30bc8153
NR
286 * performed only on packets that originate from:
287 * - a VIF port
288 * - a bridge-internal port (packets generated from userspace)
289 * - no port.
b2d9d3e8
NR
290 *
291 * If the packet will not be encapsulated, consume the tunnel context
292 * by clearing it.
293 */
12e888ba 294 if (ovsFwdCtx->srcVportNo != OVS_DPPORT_NUMBER_INVALID) {
429d4556
AS
295
296 POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(
297 ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
298
30bc8153
NR
299 if (!vport ||
300 (vport->ovsType != OVS_VPORT_TYPE_NETDEV &&
301 !OvsIsBridgeInternalVport(vport))) {
429d4556
AS
302 ovsFwdCtx->tunKey.dst = 0;
303 }
b2d9d3e8
NR
304 }
305
306 /* Tunnel the packet only if tunnel context is set. */
307 if (ovsFwdCtx->tunKey.dst != 0) {
022c2040
EE
308 switch(dstVport->ovsType) {
309 case OVS_VPORT_TYPE_VXLAN:
310 ovsActionStats.txVxlan++;
311 break;
312 case OVS_VPORT_TYPE_STT:
313 ovsActionStats.txStt++;
314 break;
315 }
b2d9d3e8
NR
316 ovsFwdCtx->tunnelTxNic = dstVport;
317 }
318
c803536e
SS
319 return TRUE;
320 }
321
322 return FALSE;
323}
324
325
326/*
327 * --------------------------------------------------------------------------
328 * OvsAddPorts --
329 * Add the specified destination vport into the forwarding context. If the
330 * vport is a VIF/external port, it is added directly to the NBL. If it is
331 * a tunneling port, it is NOT added to the NBL.
332 *
333 * Result:
334 * NDIS_STATUS_SUCCESS on success
335 * Other NDIS_STATUS upon failure.
336 * --------------------------------------------------------------------------
337 */
338static __inline NDIS_STATUS
339OvsAddPorts(OvsForwardingContext *ovsFwdCtx,
340 OvsFlowKey *flowKey,
341 NDIS_SWITCH_PORT_ID dstPortId,
342 BOOLEAN preserveVLAN,
343 BOOLEAN preservePriority)
344{
345 POVS_VPORT_ENTRY vport;
346 PNDIS_SWITCH_PORT_DESTINATION fwdPort;
347 NDIS_STATUS status;
348 POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext;
349
350 /*
351 * We hold the dispatch lock that protects the list of vports, so vports
352 * validated here can be added as destinations safely before we call into
353 * NDIS.
354 *
355 * Some of the vports can be tunnelled ports as well in which case
356 * they should be added to a separate list of tunnelled destination ports
357 * instead of the VIF ports. The context for the tunnel is settable
358 * in OvsForwardingContext.
359 */
360 vport = OvsFindVportByPortNo(ovsFwdCtx->switchContext, dstPortId);
361 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
362 /*
363 * There may be some latency between a port disappearing, and userspace
364 * updating the recalculated flows. In the meantime, handle invalid
365 * ports gracefully.
366 */
367 ovsActionStats.noVport++;
368 return NDIS_STATUS_SUCCESS;
369 }
370 ASSERT(vport->nicState == NdisSwitchNicStateConnected);
371 vport->stats.txPackets++;
372 vport->stats.txBytes +=
373 NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl));
374
7434992b
NR
375 if (OvsIsBridgeInternalVport(vport)) {
376 return NDIS_STATUS_SUCCESS;
377 }
378
c803536e 379 if (OvsDetectTunnelPkt(ovsFwdCtx, vport, flowKey)) {
c803536e
SS
380 return NDIS_STATUS_SUCCESS;
381 }
382
383 if (ovsFwdCtx->destPortsSizeOut == ovsFwdCtx->destPortsSizeIn) {
384 if (ovsFwdCtx->destPortsSizeIn == 0) {
385 ASSERT(ovsFwdCtx->destinationPorts == NULL);
386 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0);
387 status =
388 switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations(
389 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
390 OVS_DEST_PORTS_ARRAY_MIN_SIZE,
391 &ovsFwdCtx->destinationPorts);
392 if (status != NDIS_STATUS_SUCCESS) {
393 ovsActionStats.cannotGrowDest++;
394 return status;
395 }
396 ovsFwdCtx->destPortsSizeIn =
397 ovsFwdCtx->fwdDetail->NumAvailableDestinations;
398 ASSERT(ovsFwdCtx->destinationPorts);
399 } else {
400 ASSERT(ovsFwdCtx->destinationPorts != NULL);
401 /*
402 * NumElements:
403 * A ULONG value that specifies the total number of
404 * NDIS_SWITCH_PORT_DESTINATION elements in the
405 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure.
406 *
407 * NumDestinations:
408 * A ULONG value that specifies the number of
409 * NDIS_SWITCH_PORT_DESTINATION elements in the
410 * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure that
411 * specify port destinations.
412 *
413 * NumAvailableDestinations:
414 * A value that specifies the number of unused extensible switch
415 * destination ports elements within an NET_BUFFER_LIST structure.
416 */
417 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
418 ovsFwdCtx->destPortsSizeIn);
419 ASSERT(ovsFwdCtx->destinationPorts->NumDestinations ==
420 ovsFwdCtx->destPortsSizeOut -
421 ovsFwdCtx->fwdDetail->NumAvailableDestinations);
422 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations > 0);
423 /*
424 * Before we grow the array of destination ports, the current set
425 * of ports needs to be committed. Only the ports added since the
426 * last commit need to be part of the new update.
427 */
428 status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations(
429 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
430 ovsFwdCtx->fwdDetail->NumAvailableDestinations,
431 ovsFwdCtx->destinationPorts);
432 if (status != NDIS_STATUS_SUCCESS) {
433 ovsActionStats.cannotGrowDest++;
434 return status;
435 }
436 ASSERT(ovsFwdCtx->destinationPorts->NumElements ==
437 ovsFwdCtx->destPortsSizeIn);
438 ASSERT(ovsFwdCtx->destinationPorts->NumDestinations ==
439 ovsFwdCtx->destPortsSizeOut);
440 ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0);
441
442 status = switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations(
443 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
444 ovsFwdCtx->destPortsSizeIn, &ovsFwdCtx->destinationPorts);
445 if (status != NDIS_STATUS_SUCCESS) {
446 ovsActionStats.cannotGrowDest++;
447 return status;
448 }
449 ASSERT(ovsFwdCtx->destinationPorts != NULL);
450 ovsFwdCtx->destPortsSizeIn <<= 1;
451 }
452 }
453
454 ASSERT(ovsFwdCtx->destPortsSizeOut < ovsFwdCtx->destPortsSizeIn);
455 fwdPort =
456 NDIS_SWITCH_PORT_DESTINATION_AT_ARRAY_INDEX(ovsFwdCtx->destinationPorts,
457 ovsFwdCtx->destPortsSizeOut);
458
459 fwdPort->PortId = vport->portId;
460 fwdPort->NicIndex = vport->nicIndex;
461 fwdPort->IsExcluded = 0;
462 fwdPort->PreserveVLAN = preserveVLAN;
463 fwdPort->PreservePriority = preservePriority;
464 ovsFwdCtx->destPortsSizeOut += 1;
465
466 return NDIS_STATUS_SUCCESS;
467}
468
469
470/*
471 * --------------------------------------------------------------------------
472 * OvsClearTunTxCtx --
473 * Utility function to clear tx tunneling context.
474 * --------------------------------------------------------------------------
475 */
476static __inline VOID
477OvsClearTunTxCtx(OvsForwardingContext *ovsFwdCtx)
478{
479 ovsFwdCtx->tunnelTxNic = NULL;
480 ovsFwdCtx->tunKey.dst = 0;
481}
482
483
484/*
485 * --------------------------------------------------------------------------
486 * OvsClearTunRxCtx --
487 * Utility function to clear rx tunneling context.
488 * --------------------------------------------------------------------------
489 */
490static __inline VOID
491OvsClearTunRxCtx(OvsForwardingContext *ovsFwdCtx)
492{
493 ovsFwdCtx->tunnelRxNic = NULL;
494 ovsFwdCtx->tunKey.dst = 0;
495}
496
497
498/*
499 * --------------------------------------------------------------------------
500 * OvsCompleteNBLForwardingCtx --
501 * This utility function is responsible for freeing/completing an NBL - either
502 * by adding it to a completion list or by freeing it.
503 *
504 * Side effects:
505 * It also resets the necessary fields in 'ovsFwdCtx'.
506 * --------------------------------------------------------------------------
507 */
508static __inline VOID
509OvsCompleteNBLForwardingCtx(OvsForwardingContext *ovsFwdCtx,
510 PCWSTR dropReason)
511{
512 NDIS_STRING filterReason;
513
514 RtlInitUnicodeString(&filterReason, dropReason);
515 if (ovsFwdCtx->completionList) {
516 OvsAddPktCompletionList(ovsFwdCtx->completionList, TRUE,
517 ovsFwdCtx->fwdDetail->SourcePortId, ovsFwdCtx->curNbl, 1,
518 &filterReason);
519 ovsFwdCtx->curNbl = NULL;
520 } else {
521 /* If there is no completionList, we assume this is ovs created NBL */
522 ovsFwdCtx->curNbl = OvsCompleteNBL(ovsFwdCtx->switchContext,
523 ovsFwdCtx->curNbl, TRUE);
524 ASSERT(ovsFwdCtx->curNbl == NULL);
525 }
526 /* XXX: these can be made debug only to save cycles. Ideally the pipeline
527 * using these fields should reset the values at the end of the pipeline. */
528 ovsFwdCtx->destPortsSizeOut = 0;
529 ovsFwdCtx->tunnelTxNic = NULL;
530 ovsFwdCtx->tunnelRxNic = NULL;
531}
532
533/*
534 * --------------------------------------------------------------------------
535 * OvsDoFlowLookupOutput --
536 * Function to be used for the second stage of a tunneling workflow, ie.:
537 * - On the encapsulated packet on Tx path, to do a flow extract, flow
538 * lookup and excuting the actions.
539 * - On the decapsulated packet on Rx path, to do a flow extract, flow
540 * lookup and excuting the actions.
541 *
542 * XXX: It is assumed that the NBL in 'ovsFwdCtx' is owned by OVS. This is
543 * until the new buffer management framework is adopted.
544 *
545 * Side effects:
546 * The NBL in 'ovsFwdCtx' is consumed.
547 * --------------------------------------------------------------------------
548 */
549static __inline NDIS_STATUS
550OvsDoFlowLookupOutput(OvsForwardingContext *ovsFwdCtx)
551{
552 OvsFlowKey key;
553 OvsFlow *flow;
554 UINT64 hash;
555 NDIS_STATUS status;
556 POVS_VPORT_ENTRY vport =
557 OvsFindVportByPortNo(ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
558 if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
559 ASSERT(FALSE); // XXX: let's catch this for now
560 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
561 L"OVS-Dropped due to internal/tunnel port removal");
562 ovsActionStats.noVport++;
563 return NDIS_STATUS_SUCCESS;
564 }
565 ASSERT(vport->nicState == NdisSwitchNicStateConnected);
566
567 /* Assert that in the Rx direction, key is always setup. */
568 ASSERT(ovsFwdCtx->tunnelRxNic == NULL || ovsFwdCtx->tunKey.dst != 0);
4c470e88
SV
569 status =
570 OvsExtractFlow(ovsFwdCtx->curNbl, ovsFwdCtx->srcVportNo,
571 &key, &ovsFwdCtx->layers,
572 ovsFwdCtx->tunKey.dst != 0 ? &ovsFwdCtx->tunKey : NULL);
c803536e
SS
573 if (status != NDIS_STATUS_SUCCESS) {
574 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
575 L"OVS-Flow extract failed");
576 ovsActionStats.failedFlowExtract++;
577 return status;
578 }
579
580 flow = OvsLookupFlow(&ovsFwdCtx->switchContext->datapath, &key, &hash, FALSE);
581 if (flow) {
582 OvsFlowUsed(flow, ovsFwdCtx->curNbl, &ovsFwdCtx->layers);
583 ovsFwdCtx->switchContext->datapath.hits++;
584 status = OvsActionsExecute(ovsFwdCtx->switchContext,
4c470e88
SV
585 ovsFwdCtx->completionList, ovsFwdCtx->curNbl,
586 ovsFwdCtx->srcVportNo, ovsFwdCtx->sendFlags,
587 &key, &hash, &ovsFwdCtx->layers,
588 flow->actions, flow->actionsLen);
c803536e
SS
589 ovsFwdCtx->curNbl = NULL;
590 } else {
591 LIST_ENTRY missedPackets;
592 UINT32 num = 0;
593 ovsFwdCtx->switchContext->datapath.misses++;
594 InitializeListHead(&missedPackets);
4c470e88 595 status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS, vport,
640ebde7 596 &key,ovsFwdCtx->curNbl,
a422ea1d 597 FALSE, &ovsFwdCtx->layers,
640ebde7 598 ovsFwdCtx->switchContext, &missedPackets, &num);
c803536e 599 if (num) {
4a3c9b70 600 OvsQueuePackets(&missedPackets, num);
c803536e
SS
601 }
602 if (status == NDIS_STATUS_SUCCESS) {
603 /* Complete the packet since it was copied to user buffer. */
604 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
605 L"OVS-Dropped since packet was copied to userspace");
606 ovsActionStats.flowMiss++;
607 status = NDIS_STATUS_SUCCESS;
608 } else {
609 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
610 L"OVS-Dropped due to failure to queue to userspace");
611 status = NDIS_STATUS_FAILURE;
612 ovsActionStats.failedFlowMiss++;
613 }
614 }
615
616 return status;
617}
618
619/*
620 * --------------------------------------------------------------------------
621 * OvsTunnelPortTx --
622 * The start function for Tx tunneling - encapsulates the packet, and
623 * outputs the packet on the PIF bridge.
624 *
625 * Side effects:
626 * The NBL in 'ovsFwdCtx' is consumed.
627 * --------------------------------------------------------------------------
628 */
629static __inline NDIS_STATUS
630OvsTunnelPortTx(OvsForwardingContext *ovsFwdCtx)
631{
632 NDIS_STATUS status = NDIS_STATUS_FAILURE;
633 PNET_BUFFER_LIST newNbl = NULL;
634
635 /*
636 * Setup the source port to be the internal port to as to facilitate the
637 * second OvsLookupFlow.
638 */
022c2040
EE
639 if (ovsFwdCtx->switchContext->internalVport == NULL ||
640 ovsFwdCtx->switchContext->virtualExternalVport == NULL) {
ad0d70d2
EE
641 OvsClearTunTxCtx(ovsFwdCtx);
642 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
022c2040 643 L"OVS-Dropped since either internal or external port is absent");
ad0d70d2
EE
644 return NDIS_STATUS_FAILURE;
645 }
c803536e
SS
646 ovsFwdCtx->srcVportNo =
647 ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->portNo;
648
649 ovsFwdCtx->fwdDetail->SourcePortId = ovsFwdCtx->switchContext->internalPortId;
650 ovsFwdCtx->fwdDetail->SourceNicIndex =
651 ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->nicIndex;
652
653 /* Do the encap. Encap function does not consume the NBL. */
654 switch(ovsFwdCtx->tunnelTxNic->ovsType) {
e00afcf6 655 case OVS_VPORT_TYPE_VXLAN:
0b623ad5
NR
656 status = OvsEncapVxlan(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
657 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
c803536e
SS
658 &ovsFwdCtx->layers, &newNbl);
659 break;
022c2040
EE
660 case OVS_VPORT_TYPE_STT:
661 status = OvsEncapStt(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
0b623ad5 662 &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
022c2040
EE
663 &ovsFwdCtx->layers, &newNbl);
664 break;
c803536e
SS
665 default:
666 ASSERT(! "Tx: Unhandled tunnel type");
667 }
668
669 /* Reset the tunnel context so that it doesn't get used after this point. */
670 OvsClearTunTxCtx(ovsFwdCtx);
671
672 if (status == NDIS_STATUS_SUCCESS) {
673 ASSERT(newNbl);
674 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
675 L"Complete after cloning NBL for encapsulation");
676 ovsFwdCtx->curNbl = newNbl;
677 status = OvsDoFlowLookupOutput(ovsFwdCtx);
678 ASSERT(ovsFwdCtx->curNbl == NULL);
679 } else {
680 /*
681 * XXX: Temporary freeing of the packet until we register a
682 * callback to IP helper.
683 */
684 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
685 L"OVS-Dropped due to encap failure");
686 ovsActionStats.failedEncap++;
687 status = NDIS_STATUS_SUCCESS;
688 }
689
690 return status;
691}
692
693/*
694 * --------------------------------------------------------------------------
695 * OvsTunnelPortRx --
696 * Decapsulate the incoming NBL based on the tunnel type and goes through
697 * the flow lookup for the inner packet.
698 *
699 * Note: IP checksum is validate here, but L4 checksum validation needs
700 * to be done by the corresponding tunnel types.
701 *
702 * Side effects:
703 * The NBL in 'ovsFwdCtx' is consumed.
704 * --------------------------------------------------------------------------
705 */
706static __inline NDIS_STATUS
707OvsTunnelPortRx(OvsForwardingContext *ovsFwdCtx)
708{
709 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
710 PNET_BUFFER_LIST newNbl = NULL;
711 POVS_VPORT_ENTRY tunnelRxVport = ovsFwdCtx->tunnelRxNic;
a422ea1d 712 PCWSTR dropReason = L"OVS-dropped due to new decap packet";
c803536e
SS
713
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.");
718 goto dropNbl;
719 }
720
022c2040
EE
721 /*
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.
724 */
725
c803536e 726 switch(tunnelRxVport->ovsType) {
e00afcf6 727 case OVS_VPORT_TYPE_VXLAN:
022c2040
EE
728 status = OvsDecapVxlan(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
729 &ovsFwdCtx->tunKey, &newNbl);
730 break;
731 case OVS_VPORT_TYPE_STT:
732 status = OvsDecapStt(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
733 &ovsFwdCtx->tunKey, &newNbl);
a422ea1d
SV
734 if (status == NDIS_STATUS_SUCCESS && newNbl == NULL) {
735 /* This was an STT-LSO Fragment */
736 dropReason = L"OVS-STT segment is cached";
737 }
c803536e
SS
738 break;
739 default:
740 OVS_LOG_ERROR("Rx: Unhandled tunnel type: %d\n",
741 tunnelRxVport->ovsType);
742 ASSERT(! "Rx: Unhandled tunnel type");
743 status = NDIS_STATUS_NOT_SUPPORTED;
744 }
745
746 if (status != NDIS_STATUS_SUCCESS) {
747 ovsActionStats.failedDecap++;
748 goto dropNbl;
749 }
750
751 /*
752 * tunnelRxNic and other fields will be cleared, re-init the context
753 * before usage.
754 */
a422ea1d 755 OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason);
c803536e 756
a422ea1d
SV
757 if (newNbl) {
758 /* Decapsulated packet is in a new NBL */
759 ovsFwdCtx->tunnelRxNic = tunnelRxVport;
760 OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
761 newNbl, tunnelRxVport->portNo, 0,
762 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
763 ovsFwdCtx->completionList,
764 &ovsFwdCtx->layers, FALSE);
c803536e 765
a422ea1d
SV
766 /*
767 * Set the NBL's SourcePortId and SourceNicIndex to default values to
768 * keep NDIS happy when we forward the packet.
769 */
770 ovsFwdCtx->fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
771 ovsFwdCtx->fwdDetail->SourceNicIndex = 0;
c803536e 772
a422ea1d
SV
773 status = OvsDoFlowLookupOutput(ovsFwdCtx);
774 }
c803536e
SS
775 ASSERT(ovsFwdCtx->curNbl == NULL);
776 OvsClearTunRxCtx(ovsFwdCtx);
777
778 return status;
779
780dropNbl:
781 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
782 L"OVS-dropped due to decap failure");
783 OvsClearTunRxCtx(ovsFwdCtx);
784 return status;
785}
786
787
788/*
789 * --------------------------------------------------------------------------
790 * OvsOutputForwardingCtx --
791 * This function outputs an NBL to NDIS or to a tunneling pipeline based on
792 * the ports added so far into 'ovsFwdCtx'.
793 *
794 * Side effects:
795 * This function consumes the NBL - either by forwarding it successfully to
796 * NDIS, or adding it to the completion list in 'ovsFwdCtx', or freeing it.
797 *
798 * Also makes sure that the list of destination ports - tunnel or otherwise is
799 * drained.
800 * --------------------------------------------------------------------------
801 */
802static __inline NDIS_STATUS
803OvsOutputForwardingCtx(OvsForwardingContext *ovsFwdCtx)
804{
805 NDIS_STATUS status = STATUS_SUCCESS;
806 POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext;
eda457f1 807 PCWSTR dropReason;
c803536e
SS
808
809 /*
810 * Handle the case where the some of the destination ports are tunneled
811 * ports - the non-tunneled ports get a unmodified copy of the NBL, and the
812 * tunneling pipeline starts when we output the packet to tunneled port.
813 */
814 if (ovsFwdCtx->destPortsSizeOut > 0) {
815 PNET_BUFFER_LIST newNbl = NULL;
816 PNET_BUFFER nb;
817 UINT32 portsToUpdate =
818 ovsFwdCtx->fwdDetail->NumAvailableDestinations -
819 (ovsFwdCtx->destPortsSizeIn - ovsFwdCtx->destPortsSizeOut);
820
821 ASSERT(ovsFwdCtx->destinationPorts != NULL);
822
823 /*
824 * Create a copy of the packet in order to do encap on it later. Also,
825 * don't copy the offload context since the encap'd packet has a
826 * different set of headers. This will change when we implement offloads
827 * before doing encapsulation.
828 */
829 if (ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL) {
830 nb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
831 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
832 0, 0, TRUE /*copy NBL info*/);
833 if (newNbl == NULL) {
834 status = NDIS_STATUS_RESOURCES;
835 ovsActionStats.noCopiedNbl++;
eda457f1 836 dropReason = L"Dropped due to failure to create NBL copy.";
c803536e
SS
837 goto dropit;
838 }
839 }
840
841 /* It does not seem like we'll get here unless 'portsToUpdate' > 0. */
842 ASSERT(portsToUpdate > 0);
843 status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations(
844 switchContext->NdisSwitchContext, ovsFwdCtx->curNbl,
845 portsToUpdate, ovsFwdCtx->destinationPorts);
846 if (status != NDIS_STATUS_SUCCESS) {
847 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
848 ovsActionStats.cannotGrowDest++;
eda457f1 849 dropReason = L"Dropped due to failure to update destinations.";
c803536e
SS
850 goto dropit;
851 }
852
853 OvsSendNBLIngress(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
854 ovsFwdCtx->sendFlags);
855 /* End this pipeline by resetting the corresponding context. */
856 ovsFwdCtx->destPortsSizeOut = 0;
857 ovsFwdCtx->curNbl = NULL;
858 if (newNbl) {
859 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
860 newNbl, ovsFwdCtx->srcVportNo, 0,
861 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
862 ovsFwdCtx->completionList,
863 &ovsFwdCtx->layers, FALSE);
864 if (status != NDIS_STATUS_SUCCESS) {
eda457f1 865 dropReason = L"Dropped due to resouces.";
c803536e
SS
866 goto dropit;
867 }
868 }
869 }
870
871 if (ovsFwdCtx->tunnelTxNic != NULL) {
872 status = OvsTunnelPortTx(ovsFwdCtx);
873 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
874 ASSERT(ovsFwdCtx->tunKey.dst == 0);
875 } else if (ovsFwdCtx->tunnelRxNic != NULL) {
876 status = OvsTunnelPortRx(ovsFwdCtx);
877 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
878 ASSERT(ovsFwdCtx->tunKey.dst == 0);
879 }
880 ASSERT(ovsFwdCtx->curNbl == NULL);
881
882 return status;
883
884dropit:
885 if (status != NDIS_STATUS_SUCCESS) {
eda457f1 886 OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason);
c803536e
SS
887 }
888
889 return status;
890}
891
892
893/*
894 * --------------------------------------------------------------------------
895 * OvsLookupFlowOutput --
896 * Utility function for external callers to do flow extract, lookup,
897 * actions execute on a given NBL.
898 *
899 * Note: If this is being used from a callback function, make sure that the
900 * arguments specified are still valid in the asynchronous context.
901 *
902 * Side effects:
903 * This function consumes the NBL.
904 * --------------------------------------------------------------------------
905 */
906VOID
907OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext,
908 VOID *compList,
909 PNET_BUFFER_LIST curNbl)
910{
911 NDIS_STATUS status;
912 OvsForwardingContext ovsFwdCtx;
913 POVS_VPORT_ENTRY internalVport =
914 (POVS_VPORT_ENTRY)switchContext->internalVport;
915
916 /* XXX: make sure comp list was not a stack variable previously. */
917 OvsCompletionList *completionList = (OvsCompletionList *)compList;
918
919 /*
920 * XXX: can internal port disappear while we are busy doing ARP resolution?
921 * It could, but will we get this callback from IP helper in that case. Need
922 * to check.
923 */
924 ASSERT(switchContext->internalVport);
925 status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl,
926 internalVport->portNo, 0,
927 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl),
928 completionList, NULL, TRUE);
929 if (status != NDIS_STATUS_SUCCESS) {
930 OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
931 L"OVS-Dropped due to resources");
932 return;
933 }
934
935 ASSERT(FALSE);
936 /*
937 * XXX: We need to acquire the dispatch lock and the datapath lock.
938 */
939
940 OvsDoFlowLookupOutput(&ovsFwdCtx);
941}
942
943
944/*
945 * --------------------------------------------------------------------------
946 * OvsOutputBeforeSetAction --
947 * Function to be called to complete one set of actions on an NBL, before
948 * we start the next one.
949 * --------------------------------------------------------------------------
950 */
951static __inline NDIS_STATUS
952OvsOutputBeforeSetAction(OvsForwardingContext *ovsFwdCtx)
953{
954 PNET_BUFFER_LIST newNbl;
955 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
c803536e
SS
956
957 /*
958 * Create a copy and work on the copy after this point. The original NBL is
959 * forwarded. One reason to not use the copy for forwarding is that
960 * ports have already been added to the original NBL, and it might be
961 * inefficient/impossible to remove/re-add them to the copy. There's no
962 * notion of removing the ports, the ports need to be marked as
963 * "isExcluded". There's seems no real advantage to retaining the original
964 * and sending out the copy instead.
965 *
966 * XXX: We are copying the offload context here. This is to handle actions
967 * such as:
968 * outport, pop_vlan(), outport, push_vlan(), outport
969 *
970 * copy size needs to include inner ether + IP + TCP, need to revisit
971 * if we support IP options.
972 * XXX Head room needs to include the additional encap.
973 * XXX copySize check is not considering multiple NBs.
974 */
c803536e
SS
975 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
976 0, 0, TRUE /*copy NBL info*/);
977
978 ASSERT(ovsFwdCtx->destPortsSizeOut > 0 ||
979 ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL);
980
ba472491
AS
981 /* Send the original packet out and save the original source port number */
982 UINT32 tempVportNo = ovsFwdCtx->srcVportNo;
c803536e
SS
983 status = OvsOutputForwardingCtx(ovsFwdCtx);
984 ASSERT(ovsFwdCtx->curNbl == NULL);
985 ASSERT(ovsFwdCtx->destPortsSizeOut == 0);
986 ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
987 ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
988
989 /* If we didn't make a copy, can't continue. */
990 if (newNbl == NULL) {
991 ovsActionStats.noCopiedNbl++;
992 return NDIS_STATUS_RESOURCES;
993 }
994
995 /* Finish the remaining actions with the new NBL */
996 if (status != NDIS_STATUS_SUCCESS) {
997 OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
998 } else {
999 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
ba472491 1000 newNbl, tempVportNo, 0,
c803536e
SS
1001 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1002 ovsFwdCtx->completionList,
1003 &ovsFwdCtx->layers, FALSE);
1004 }
1005
1006 return status;
1007}
1008
1009
1010/*
1011 * --------------------------------------------------------------------------
1012 * OvsPopVlanInPktBuf --
1013 * Function to pop a VLAN tag when the tag is in the packet buffer.
1014 * --------------------------------------------------------------------------
1015 */
1016static __inline NDIS_STATUS
1017OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx)
1018{
1019 PNET_BUFFER curNb;
1020 PMDL curMdl;
1021 PUINT8 bufferStart;
1022 ULONG dataLength = sizeof (DL_EUI48) + sizeof (DL_EUI48);
1023 UINT32 packetLen, mdlLen;
1024 PNET_BUFFER_LIST newNbl;
1025 NDIS_STATUS status;
1026
1027 /*
1028 * Declare a dummy vlanTag structure since we need to compute the size
1029 * of shiftLength. The NDIS one is a unionized structure.
1030 */
1031 NDIS_PACKET_8021Q_INFO vlanTag = {0};
1032 ULONG shiftLength = sizeof (vlanTag.TagHeader);
1033 PUINT8 tempBuffer[sizeof (DL_EUI48) + sizeof (DL_EUI48)];
1034
1035 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1036 0, 0, TRUE /* copy NBL info */);
1037 if (!newNbl) {
1038 ovsActionStats.noCopiedNbl++;
1039 return NDIS_STATUS_RESOURCES;
1040 }
1041
1042 /* Complete the original NBL and create a copy to modify. */
1043 OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to copy");
1044
1045 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
1046 newNbl, ovsFwdCtx->srcVportNo, 0,
1047 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1048 NULL, &ovsFwdCtx->layers, FALSE);
1049 if (status != NDIS_STATUS_SUCCESS) {
1050 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1051 L"Dropped due to resouces");
1052 return NDIS_STATUS_RESOURCES;
1053 }
1054
1055 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1056 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1057 ASSERT(curNb->Next == NULL);
1058 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1059 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1060 if (!bufferStart) {
1061 return NDIS_STATUS_RESOURCES;
1062 }
1063 mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1064 /* Bail out if L2 + VLAN header is not contiguous in the first buffer. */
1065 if (MIN(packetLen, mdlLen) < sizeof (EthHdr) + shiftLength) {
1066 ASSERT(FALSE);
1067 return NDIS_STATUS_FAILURE;
1068 }
1069 bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1070 RtlCopyMemory(tempBuffer, bufferStart, dataLength);
1071 RtlCopyMemory(bufferStart + shiftLength, tempBuffer, dataLength);
1072 NdisAdvanceNetBufferDataStart(curNb, shiftLength, FALSE, NULL);
1073
1074 return NDIS_STATUS_SUCCESS;
1075}
1076
1077/*
1078 * --------------------------------------------------------------------------
1079 * OvsTunnelAttrToIPv4TunnelKey --
1080 * Convert tunnel attribute to OvsIPv4TunnelKey.
1081 * --------------------------------------------------------------------------
1082 */
1083static __inline NDIS_STATUS
d838e577 1084OvsTunnelAttrToIPv4TunnelKey(PNL_ATTR attr,
c803536e
SS
1085 OvsIPv4TunnelKey *tunKey)
1086{
d838e577 1087 PNL_ATTR a;
c803536e
SS
1088 INT rem;
1089
1090 tunKey->attr[0] = 0;
1091 tunKey->attr[1] = 0;
1092 tunKey->attr[2] = 0;
d838e577 1093 ASSERT(NlAttrType(attr) == OVS_KEY_ATTR_TUNNEL);
c803536e 1094
d838e577
AS
1095 NL_ATTR_FOR_EACH_UNSAFE (a, rem, NlAttrData(attr),
1096 NlAttrGetSize(attr)) {
1097 switch (NlAttrType(a)) {
c803536e 1098 case OVS_TUNNEL_KEY_ATTR_ID:
d838e577 1099 tunKey->tunnelId = NlAttrGetBe64(a);
c803536e
SS
1100 tunKey->flags |= OVS_TNL_F_KEY;
1101 break;
1102 case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
d838e577 1103 tunKey->src = NlAttrGetBe32(a);
c803536e
SS
1104 break;
1105 case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
d838e577 1106 tunKey->dst = NlAttrGetBe32(a);
c803536e
SS
1107 break;
1108 case OVS_TUNNEL_KEY_ATTR_TOS:
d838e577 1109 tunKey->tos = NlAttrGetU8(a);
c803536e
SS
1110 break;
1111 case OVS_TUNNEL_KEY_ATTR_TTL:
d838e577 1112 tunKey->ttl = NlAttrGetU8(a);
c803536e
SS
1113 break;
1114 case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
1115 tunKey->flags |= OVS_TNL_F_DONT_FRAGMENT;
1116 break;
1117 case OVS_TUNNEL_KEY_ATTR_CSUM:
1118 tunKey->flags |= OVS_TNL_F_CSUM;
1119 break;
1120 default:
1121 ASSERT(0);
1122 }
1123 }
1124
1125 return NDIS_STATUS_SUCCESS;
1126}
1127
1128/*
1129 *----------------------------------------------------------------------------
1130 * OvsUpdateEthHeader --
1131 * Updates the ethernet header in ovsFwdCtx.curNbl inline based on the
1132 * specified key.
1133 *----------------------------------------------------------------------------
1134 */
1135static __inline NDIS_STATUS
1136OvsUpdateEthHeader(OvsForwardingContext *ovsFwdCtx,
1137 const struct ovs_key_ethernet *ethAttr)
1138{
1139 PNET_BUFFER curNb;
1140 PMDL curMdl;
1141 PUINT8 bufferStart;
1142 EthHdr *ethHdr;
1143 UINT32 packetLen, mdlLen;
1144
1145 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1146 ASSERT(curNb->Next == NULL);
1147 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1148 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1149 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1150 if (!bufferStart) {
1151 ovsActionStats.noResource++;
1152 return NDIS_STATUS_RESOURCES;
1153 }
1154 mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1155 ASSERT(mdlLen > 0);
1156 /* Bail out if the L2 header is not in a contiguous buffer. */
1157 if (MIN(packetLen, mdlLen) < sizeof *ethHdr) {
1158 ASSERT(FALSE);
1159 return NDIS_STATUS_FAILURE;
1160 }
1161 ethHdr = (EthHdr *)(bufferStart + NET_BUFFER_CURRENT_MDL_OFFSET(curNb));
1162
1163 RtlCopyMemory(ethHdr->Destination, ethAttr->eth_dst,
1164 sizeof ethHdr->Destination);
1165 RtlCopyMemory(ethHdr->Source, ethAttr->eth_src, sizeof ethHdr->Source);
1166
1167 return NDIS_STATUS_SUCCESS;
1168}
1169
1170/*
1171 *----------------------------------------------------------------------------
1172 * OvsUpdateIPv4Header --
1173 * Updates the IPv4 header in ovsFwdCtx.curNbl inline based on the
1174 * specified key.
1175 *----------------------------------------------------------------------------
1176 */
1177static __inline NDIS_STATUS
1178OvsUpdateIPv4Header(OvsForwardingContext *ovsFwdCtx,
1179 const struct ovs_key_ipv4 *ipAttr)
1180{
1181 PNET_BUFFER curNb;
1182 PMDL curMdl;
1183 ULONG curMdlOffset;
1184 PUINT8 bufferStart;
1185 UINT32 mdlLen, hdrSize, packetLen;
1186 OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
1187 NDIS_STATUS status;
1188 IPHdr *ipHdr;
1189 TCPHdr *tcpHdr = NULL;
1190 UDPHdr *udpHdr = NULL;
1191
1192 ASSERT(layers->value != 0);
1193
1194 /*
1195 * Peek into the MDL to get a handle to the IP header and if required
1196 * the TCP/UDP header as well. We check if the required headers are in one
1197 * contiguous MDL, and if not, we copy them over to one MDL.
1198 */
1199 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1200 ASSERT(curNb->Next == NULL);
1201 packetLen = NET_BUFFER_DATA_LENGTH(curNb);
1202 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1203 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1204 if (!bufferStart) {
1205 ovsActionStats.noResource++;
1206 return NDIS_STATUS_RESOURCES;
1207 }
1208 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1209 mdlLen -= curMdlOffset;
1210 ASSERT((INT)mdlLen >= 0);
1211
1212 if (layers->isTcp || layers->isUdp) {
1213 hdrSize = layers->l4Offset +
1214 layers->isTcp ? sizeof (*tcpHdr) : sizeof (*udpHdr);
1215 } else {
1216 hdrSize = layers->l3Offset + sizeof (*ipHdr);
1217 }
1218
1219 /* Count of number of bytes of valid data there are in the first MDL. */
1220 mdlLen = MIN(packetLen, mdlLen);
1221 if (mdlLen < hdrSize) {
1222 PNET_BUFFER_LIST newNbl;
1223 newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
1224 hdrSize, 0, TRUE /*copy NBL info*/);
1225 if (!newNbl) {
1226 ovsActionStats.noCopiedNbl++;
1227 return NDIS_STATUS_RESOURCES;
1228 }
1229 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1230 L"Complete after partial copy.");
1231
1232 status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext,
1233 newNbl, ovsFwdCtx->srcVportNo, 0,
1234 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl),
1235 NULL, &ovsFwdCtx->layers, FALSE);
1236 if (status != NDIS_STATUS_SUCCESS) {
1237 OvsCompleteNBLForwardingCtx(ovsFwdCtx,
1238 L"OVS-Dropped due to resources");
1239 return NDIS_STATUS_RESOURCES;
1240 }
1241
1242 curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
1243 ASSERT(curNb->Next == NULL);
1244 curMdl = NET_BUFFER_CURRENT_MDL(curNb);
1245 NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority);
1246 if (!curMdl) {
1247 ovsActionStats.noResource++;
1248 return NDIS_STATUS_RESOURCES;
1249 }
1250 curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
1251 mdlLen -= curMdlOffset;
1252 ASSERT(mdlLen >= hdrSize);
1253 }
1254
1255 ipHdr = (IPHdr *)(bufferStart + curMdlOffset + layers->l3Offset);
1256
1257 if (layers->isTcp) {
1258 tcpHdr = (TCPHdr *)(bufferStart + curMdlOffset + layers->l4Offset);
1259 } else if (layers->isUdp) {
1260 udpHdr = (UDPHdr *)(bufferStart + curMdlOffset + layers->l4Offset);
1261 }
1262
1263 /*
1264 * Adjust the IP header inline as dictated by the action, nad also update
1265 * the IP and the TCP checksum for the data modified.
1266 *
1267 * In the future, this could be optimized to make one call to
1268 * ChecksumUpdate32(). Ignoring this for now, since for the most common
1269 * case, we only update the TTL.
1270 */
1271 if (ipHdr->saddr != ipAttr->ipv4_src) {
1272 if (tcpHdr) {
1273 tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->saddr,
1274 ipAttr->ipv4_src);
1275 } else if (udpHdr && udpHdr->check) {
1276 udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->saddr,
1277 ipAttr->ipv4_src);
1278 }
1279
1280 if (ipHdr->check != 0) {
1281 ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->saddr,
1282 ipAttr->ipv4_src);
1283 }
1284 ipHdr->saddr = ipAttr->ipv4_src;
1285 }
1286 if (ipHdr->daddr != ipAttr->ipv4_dst) {
1287 if (tcpHdr) {
1288 tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->daddr,
1289 ipAttr->ipv4_dst);
1290 } else if (udpHdr && udpHdr->check) {
1291 udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->daddr,
1292 ipAttr->ipv4_dst);
1293 }
1294
1295 if (ipHdr->check != 0) {
1296 ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->daddr,
1297 ipAttr->ipv4_dst);
1298 }
1299 ipHdr->daddr = ipAttr->ipv4_dst;
1300 }
1301 if (ipHdr->protocol != ipAttr->ipv4_proto) {
1302 UINT16 oldProto = (ipHdr->protocol << 16) & 0xff00;
1303 UINT16 newProto = (ipAttr->ipv4_proto << 16) & 0xff00;
1304 if (tcpHdr) {
1305 tcpHdr->check = ChecksumUpdate16(tcpHdr->check, oldProto, newProto);
1306 } else if (udpHdr && udpHdr->check) {
1307 udpHdr->check = ChecksumUpdate16(udpHdr->check, oldProto, newProto);
1308 }
1309
1310 if (ipHdr->check != 0) {
1311 ipHdr->check = ChecksumUpdate16(ipHdr->check, oldProto, newProto);
1312 }
1313 ipHdr->protocol = ipAttr->ipv4_proto;
1314 }
1315 if (ipHdr->ttl != ipAttr->ipv4_ttl) {
1316 UINT16 oldTtl = (ipHdr->ttl) & 0xff;
1317 UINT16 newTtl = (ipAttr->ipv4_ttl) & 0xff;
1318 if (ipHdr->check != 0) {
1319 ipHdr->check = ChecksumUpdate16(ipHdr->check, oldTtl, newTtl);
1320 }
1321 ipHdr->ttl = ipAttr->ipv4_ttl;
1322 }
1323
1324 return NDIS_STATUS_SUCCESS;
1325}
1326
1327/*
1328 * --------------------------------------------------------------------------
1329 * OvsExecuteSetAction --
1330 * Executes a set() action, but storing the actions into 'ovsFwdCtx'
1331 * --------------------------------------------------------------------------
1332 */
1333static __inline NDIS_STATUS
1334OvsExecuteSetAction(OvsForwardingContext *ovsFwdCtx,
1335 OvsFlowKey *key,
1336 UINT64 *hash,
d838e577 1337 const PNL_ATTR a)
c803536e 1338{
d838e577 1339 enum ovs_key_attr type = NlAttrType(a);
c803536e
SS
1340 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1341
1342 switch (type) {
1343 case OVS_KEY_ATTR_ETHERNET:
1344 status = OvsUpdateEthHeader(ovsFwdCtx,
d838e577 1345 NlAttrGetUnspec(a, sizeof(struct ovs_key_ethernet)));
c803536e
SS
1346 break;
1347
1348 case OVS_KEY_ATTR_IPV4:
1349 status = OvsUpdateIPv4Header(ovsFwdCtx,
d838e577 1350 NlAttrGetUnspec(a, sizeof(struct ovs_key_ipv4)));
c803536e
SS
1351 break;
1352
1353 case OVS_KEY_ATTR_TUNNEL:
1354 {
1355 OvsIPv4TunnelKey tunKey;
022c2040 1356 status = OvsTunnelAttrToIPv4TunnelKey((PNL_ATTR)a, &tunKey);
c803536e
SS
1357 ASSERT(status == NDIS_STATUS_SUCCESS);
1358 tunKey.flow_hash = (uint16)(hash ? *hash : OvsHashFlow(key));
ffde5f8f 1359 tunKey.dst_port = key->ipKey.l4.tpDst;
c803536e 1360 RtlCopyMemory(&ovsFwdCtx->tunKey, &tunKey, sizeof ovsFwdCtx->tunKey);
c803536e
SS
1361 break;
1362 }
1363 case OVS_KEY_ATTR_SKB_MARK:
1364 /* XXX: Not relevant to Hyper-V. Return OK */
1365 break;
1366 case OVS_KEY_ATTR_UNSPEC:
1367 case OVS_KEY_ATTR_ENCAP:
1368 case OVS_KEY_ATTR_ETHERTYPE:
1369 case OVS_KEY_ATTR_IN_PORT:
1370 case OVS_KEY_ATTR_VLAN:
1371 case OVS_KEY_ATTR_ICMP:
1372 case OVS_KEY_ATTR_ICMPV6:
1373 case OVS_KEY_ATTR_ARP:
1374 case OVS_KEY_ATTR_ND:
1375 case __OVS_KEY_ATTR_MAX:
1376 default:
1377 OVS_LOG_INFO("Unhandled attribute %#x", type);
1378 ASSERT(FALSE);
1379 }
1380 return status;
1381}
1382
1383/*
1384 * --------------------------------------------------------------------------
1385 * OvsActionsExecute --
1386 * Interpret and execute the specified 'actions' on the specifed packet
1387 * 'curNbl'. The expectation is that if the packet needs to be dropped
1388 * (completed) for some reason, it is added to 'completionList' so that the
1389 * caller can complete the packet. If 'completionList' is NULL, the NBL is
1390 * assumed to be generated by OVS and freed up. Otherwise, the function
1391 * consumes the NBL by generating a NDIS send indication for the packet.
1392 *
1393 * There are one or more of "clone" NBLs that may get generated while
1394 * executing the actions. Upon any failures, the "cloned" NBLs are freed up,
1395 * and the caller does not have to worry about them.
1396 *
1397 * Success or failure is returned based on whether the specified actions
1398 * were executed successfully on the packet or not.
1399 * --------------------------------------------------------------------------
1400 */
1401NDIS_STATUS
1402OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
1403 OvsCompletionList *completionList,
1404 PNET_BUFFER_LIST curNbl,
1405 UINT32 portNo,
1406 ULONG sendFlags,
1407 OvsFlowKey *key,
1408 UINT64 *hash,
1409 OVS_PACKET_HDR_INFO *layers,
d838e577 1410 const PNL_ATTR actions,
c803536e
SS
1411 INT actionsLen)
1412{
d838e577 1413 PNL_ATTR a;
c803536e
SS
1414 INT rem;
1415 UINT32 dstPortID;
1416 OvsForwardingContext ovsFwdCtx;
1417 PCWSTR dropReason = L"";
1418 NDIS_STATUS status;
1419 PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail =
1420 NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl);
1421
1422 /* XXX: ASSERT that the flow table lock is held. */
1423 status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl, portNo,
1424 sendFlags, fwdDetail, completionList,
1425 layers, TRUE);
1426 if (status != NDIS_STATUS_SUCCESS) {
1427 dropReason = L"OVS-initing destination port list failed";
1428 goto dropit;
1429 }
1430
1431 if (actionsLen == 0) {
1432 dropReason = L"OVS-Dropped due to Flow action";
1433 ovsActionStats.zeroActionLen++;
1434 goto dropit;
1435 }
1436
1437 NL_ATTR_FOR_EACH_UNSAFE (a, rem, actions, actionsLen) {
d838e577 1438 switch(NlAttrType(a)) {
c803536e 1439 case OVS_ACTION_ATTR_OUTPUT:
d838e577 1440 dstPortID = NlAttrGetU32(a);
c803536e
SS
1441 status = OvsAddPorts(&ovsFwdCtx, key, dstPortID,
1442 TRUE, TRUE);
1443 if (status != NDIS_STATUS_SUCCESS) {
1444 dropReason = L"OVS-adding destination port failed";
1445 goto dropit;
1446 }
1447 break;
1448
1449 case OVS_ACTION_ATTR_PUSH_VLAN:
1450 {
1451 struct ovs_action_push_vlan *vlan;
1452 PVOID vlanTagValue;
1453 PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTag;
1454
1455 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1456 || ovsFwdCtx.tunnelRxNic != NULL) {
1457 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1458 if (status != NDIS_STATUS_SUCCESS) {
1459 dropReason = L"OVS-adding destination failed";
1460 goto dropit;
1461 }
1462 }
1463
1464 vlanTagValue = NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1465 Ieee8021QNetBufferListInfo);
1466 if (vlanTagValue != NULL) {
1467 /*
1468 * XXX: We don't support double VLAN tag offload. In such cases,
1469 * we need to insert the existing one into the packet buffer,
1470 * and add the new one as offload. This will take care of
1471 * guest tag-in-tag case as well as OVS rules that specify
1472 * tag-in-tag.
1473 */
1474 } else {
1475 vlanTagValue = 0;
1476 vlanTag = (PNDIS_NET_BUFFER_LIST_8021Q_INFO)(PVOID *)&vlanTagValue;
d838e577 1477 vlan = (struct ovs_action_push_vlan *)NlAttrGet((const PNL_ATTR)a);
c803536e
SS
1478 vlanTag->TagHeader.VlanId = ntohs(vlan->vlan_tci) & 0xfff;
1479 vlanTag->TagHeader.UserPriority = ntohs(vlan->vlan_tci) >> 13;
1480
1481 NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1482 Ieee8021QNetBufferListInfo) = vlanTagValue;
1483 }
1484 break;
1485 }
1486
1487 case OVS_ACTION_ATTR_POP_VLAN:
1488 {
1489 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1490 || ovsFwdCtx.tunnelRxNic != NULL) {
1491 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1492 if (status != NDIS_STATUS_SUCCESS) {
1493 dropReason = L"OVS-adding destination failed";
1494 goto dropit;
1495 }
1496 }
1497
1498 if (NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1499 Ieee8021QNetBufferListInfo) != 0) {
1500 NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl,
1501 Ieee8021QNetBufferListInfo) = 0;
1502 } else {
1503 /*
1504 * The VLAN tag is inserted into the packet buffer. Pop the tag
1505 * by packet buffer modification.
1506 */
1507 status = OvsPopVlanInPktBuf(&ovsFwdCtx);
1508 if (status != NDIS_STATUS_SUCCESS) {
1509 dropReason = L"OVS-pop vlan action failed";
1510 goto dropit;
1511 }
1512 }
1513 break;
1514 }
1515
1516 case OVS_ACTION_ATTR_USERSPACE:
1517 {
d838e577
AS
1518 PNL_ATTR userdataAttr;
1519 PNL_ATTR queueAttr;
c803536e 1520 POVS_PACKET_QUEUE_ELEM elem;
429d4556
AS
1521 BOOLEAN isRecv = FALSE;
1522
1523 POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(switchContext,
1524 portNo);
1525
1526 if (vport) {
1527 if (vport->isExternal ||
1528 OvsIsTunnelVportType(vport->ovsType)) {
1529 isRecv = TRUE;
1530 }
1531 }
c803536e 1532
d838e577
AS
1533 queueAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_PID);
1534 userdataAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_USERDATA);
c803536e 1535
640ebde7
EE
1536 elem = OvsCreateQueueNlPacket((PVOID)userdataAttr,
1537 userdataAttr->nlaLen,
1538 OVS_PACKET_CMD_ACTION,
4c470e88 1539 vport, key, ovsFwdCtx.curNbl,
640ebde7
EE
1540 NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx.curNbl),
1541 isRecv,
1542 layers);
c803536e
SS
1543 if (elem) {
1544 LIST_ENTRY missedPackets;
1545 InitializeListHead(&missedPackets);
1546 InsertTailList(&missedPackets, &elem->link);
4a3c9b70 1547 OvsQueuePackets(&missedPackets, 1);
c803536e
SS
1548 dropReason = L"OVS-Completed since packet was copied to "
1549 L"userspace";
1550 } else {
1551 dropReason = L"OVS-Dropped due to failure to queue to "
1552 L"userspace";
1553 goto dropit;
1554 }
1555 break;
1556 }
1557 case OVS_ACTION_ATTR_SET:
1558 {
1559 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1560 || ovsFwdCtx.tunnelRxNic != NULL) {
1561 status = OvsOutputBeforeSetAction(&ovsFwdCtx);
1562 if (status != NDIS_STATUS_SUCCESS) {
1563 dropReason = L"OVS-adding destination failed";
1564 goto dropit;
1565 }
1566 }
1567
1568 status = OvsExecuteSetAction(&ovsFwdCtx, key, hash,
d838e577
AS
1569 (const PNL_ATTR)NlAttrGet
1570 ((const PNL_ATTR)a));
c803536e
SS
1571 if (status != NDIS_STATUS_SUCCESS) {
1572 dropReason = L"OVS-set action failed";
1573 goto dropit;
1574 }
1575 break;
1576 }
1577 case OVS_ACTION_ATTR_SAMPLE:
c803536e 1578 default:
7c5d9f17 1579 status = NDIS_STATUS_NOT_SUPPORTED;
c803536e
SS
1580 break;
1581 }
1582 }
1583
1584 if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
1585 || ovsFwdCtx.tunnelRxNic != NULL) {
1586 status = OvsOutputForwardingCtx(&ovsFwdCtx);
1587 ASSERT(ovsFwdCtx.curNbl == NULL);
1588 }
1589
1590 ASSERT(ovsFwdCtx.destPortsSizeOut == 0);
1591 ASSERT(ovsFwdCtx.tunnelRxNic == NULL);
1592 ASSERT(ovsFwdCtx.tunnelTxNic == NULL);
1593
1594dropit:
1595 /*
1596 * If curNbl != NULL, it implies the NBL has not been not freed up so far.
1597 */
1598 if (ovsFwdCtx.curNbl) {
1599 OvsCompleteNBLForwardingCtx(&ovsFwdCtx, dropReason);
1600 }
1601
1602 return status;
1603}