]>
Commit | Line | Data |
---|---|---|
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 | |
36 | typedef 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 | ||
56 | OVS_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 | |
66 | typedef struct OvsForwardingContext { | |
67 | POVS_SWITCH_CONTEXT switchContext; | |
68 | /* The NBL currently used in the pipeline. */ | |
69 | PNET_BUFFER_LIST curNbl; | |
70 | /* NDIS forwarding detail for 'curNbl'. */ | |
71 | PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; | |
72 | /* Array of destination ports for 'curNbl'. */ | |
73 | PNDIS_SWITCH_FORWARDING_DESTINATION_ARRAY destinationPorts; | |
74 | /* send flags while sending 'curNbl' into NDIS. */ | |
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 | */ | |
130 | static __inline NDIS_STATUS | |
131 | OvsInitForwardingCtx(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 | */ | |
199 | static __inline BOOLEAN | |
200 | OvsDetectTunnelRxPkt(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 | */ | |
256 | static __inline BOOLEAN | |
257 | OvsDetectTunnelPkt(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 | */ | |
338 | static __inline NDIS_STATUS | |
339 | OvsAddPorts(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 | */ | |
476 | static __inline VOID | |
477 | OvsClearTunTxCtx(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 | */ | |
490 | static __inline VOID | |
491 | OvsClearTunRxCtx(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 | */ | |
508 | static __inline VOID | |
509 | OvsCompleteNBLForwardingCtx(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 | */ | |
549 | static __inline NDIS_STATUS | |
550 | OvsDoFlowLookupOutput(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 | */ | |
629 | static __inline NDIS_STATUS | |
630 | OvsTunnelPortTx(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 | */ | |
706 | static __inline NDIS_STATUS | |
707 | OvsTunnelPortRx(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 | ||
780 | dropNbl: | |
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 | */ | |
802 | static __inline NDIS_STATUS | |
803 | OvsOutputForwardingCtx(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 | ||
884 | dropit: | |
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 | */ | |
906 | VOID | |
907 | OvsLookupFlowOutput(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 | */ | |
951 | static __inline NDIS_STATUS | |
952 | OvsOutputBeforeSetAction(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 | */ | |
1016 | static __inline NDIS_STATUS | |
1017 | OvsPopVlanInPktBuf(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 | */ | |
1083 | static __inline NDIS_STATUS | |
d838e577 | 1084 | OvsTunnelAttrToIPv4TunnelKey(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 | */ | |
1135 | static __inline NDIS_STATUS | |
1136 | OvsUpdateEthHeader(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 | */ | |
1177 | static __inline NDIS_STATUS | |
1178 | OvsUpdateIPv4Header(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 | */ | |
1333 | static __inline NDIS_STATUS | |
1334 | OvsExecuteSetAction(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 | */ | |
1401 | NDIS_STATUS | |
1402 | OvsActionsExecute(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 | ||
1594 | dropit: | |
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 | } |