]>
Commit | Line | Data |
---|---|---|
c803536e | 1 | /* |
7b383a56 | 2 | * Copyright (c) 2014, 2016 VMware, Inc. |
c803536e SS |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | /* | |
18 | * OvsUser.c | |
19 | * Manage packet queue for packet miss for userAction. | |
20 | */ | |
21 | ||
22 | ||
23 | #include "precomp.h" | |
24 | ||
ee25964a | 25 | #include "Actions.h" |
ae1fd386 | 26 | #include "Datapath.h" |
7b383a56 AS |
27 | #include "Debug.h" |
28 | #include "Event.h" | |
fa1324c9 | 29 | #include "Flow.h" |
65ae4384 | 30 | #include "Jhash.h" |
7b383a56 AS |
31 | #include "NetProto.h" |
32 | #include "Offload.h" | |
33 | #include "PacketIO.h" | |
34 | #include "Switch.h" | |
35 | #include "TunnelIntf.h" | |
36 | #include "User.h" | |
37 | #include "Vport.h" | |
c803536e SS |
38 | |
39 | #ifdef OVS_DBG_MOD | |
40 | #undef OVS_DBG_MOD | |
41 | #endif | |
42 | #define OVS_DBG_MOD OVS_DBG_USER | |
c803536e | 43 | |
c803536e SS |
44 | POVS_PACKET_QUEUE_ELEM OvsGetNextPacket(POVS_OPEN_INSTANCE instance); |
45 | extern PNDIS_SPIN_LOCK gOvsCtrlLock; | |
46 | extern POVS_SWITCH_CONTEXT gOvsSwitchContext; | |
47 | OVS_USER_STATS ovsUserStats; | |
48 | ||
3fd9ec0b NR |
49 | static VOID _MapNlAttrToOvsPktExec(PNL_MSG_HDR nlMsgHdr, PNL_ATTR *nlAttrs, |
50 | PNL_ATTR *keyAttrs, | |
51 | OvsPacketExecute *execute); | |
efa753a8 | 52 | extern NL_POLICY nlFlowKeyPolicy[]; |
b8b00f0c | 53 | extern UINT32 nlFlowKeyPolicyLen; |
3871d4fb NR |
54 | extern NL_POLICY nlFlowTunnelKeyPolicy[]; |
55 | extern UINT32 nlFlowTunnelKeyPolicyLen; | |
7fba2303 | 56 | DRIVER_CANCEL OvsCancelIrpDatapath; |
c803536e | 57 | |
f0187396 AS |
58 | _IRQL_raises_(DISPATCH_LEVEL) |
59 | _IRQL_saves_global_(OldIrql, gOvsSwitchContext->pidHashLock) | |
60 | _Acquires_lock_(gOvsSwitchContext->pidHashLock) | |
4a3c9b70 AS |
61 | static __inline VOID |
62 | OvsAcquirePidHashLock() | |
63 | { | |
64 | NdisAcquireSpinLock(&(gOvsSwitchContext->pidHashLock)); | |
65 | } | |
66 | ||
cfc854f0 AS |
67 | _IRQL_requires_(DISPATCH_LEVEL) |
68 | _IRQL_restores_global_(OldIrql, gOvsSwitchContext->pidHashLock) | |
69 | _Requires_lock_held_(gOvsSwitchContext->pidHashLock) | |
70 | _Releases_lock_(gOvsSwitchContext->pidHashLock) | |
4a3c9b70 AS |
71 | static __inline VOID |
72 | OvsReleasePidHashLock() | |
73 | { | |
74 | NdisReleaseSpinLock(&(gOvsSwitchContext->pidHashLock)); | |
75 | } | |
76 | ||
77 | ||
c803536e SS |
78 | static VOID |
79 | OvsPurgePacketQueue(POVS_USER_PACKET_QUEUE queue, | |
80 | POVS_OPEN_INSTANCE instance) | |
81 | { | |
82 | PLIST_ENTRY link, next; | |
83 | LIST_ENTRY tmp; | |
84 | POVS_PACKET_QUEUE_ELEM elem; | |
85 | ||
86 | InitializeListHead(&tmp); | |
87 | NdisAcquireSpinLock(&queue->queueLock); | |
88 | if (queue->instance != instance) { | |
89 | NdisReleaseSpinLock(&queue->queueLock); | |
90 | return; | |
91 | } | |
92 | ||
93 | if (queue->numPackets) { | |
94 | OvsAppendList(&tmp, &queue->packetList); | |
95 | queue->numPackets = 0; | |
96 | } | |
97 | NdisReleaseSpinLock(&queue->queueLock); | |
98 | LIST_FORALL_SAFE(&tmp, link, next) { | |
99 | RemoveEntryList(link); | |
100 | elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); | |
d016f841 | 101 | OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG); |
c803536e SS |
102 | } |
103 | } | |
104 | ||
c803536e SS |
105 | VOID |
106 | OvsCleanupPacketQueue(POVS_OPEN_INSTANCE instance) | |
107 | { | |
108 | POVS_USER_PACKET_QUEUE queue; | |
109 | POVS_PACKET_QUEUE_ELEM elem; | |
110 | PLIST_ENTRY link, next; | |
111 | LIST_ENTRY tmp; | |
112 | PIRP irp = NULL; | |
113 | ||
777d2bbd | 114 | ASSERT(instance); |
c803536e SS |
115 | InitializeListHead(&tmp); |
116 | queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue; | |
117 | if (queue) { | |
118 | PDRIVER_CANCEL cancelRoutine; | |
119 | NdisAcquireSpinLock(&queue->queueLock); | |
ae1fd386 EE |
120 | ASSERT(queue->instance == instance); |
121 | /* XXX Should not happen */ | |
c803536e SS |
122 | if (queue->instance != instance) { |
123 | NdisReleaseSpinLock(&queue->queueLock); | |
ae1fd386 | 124 | NdisFreeSpinLock(&queue->queueLock); |
c803536e SS |
125 | return; |
126 | } | |
127 | ||
128 | if (queue->numPackets) { | |
129 | OvsAppendList(&tmp, &queue->packetList); | |
130 | queue->numPackets = 0; | |
131 | } | |
132 | queue->instance = NULL; | |
c803536e SS |
133 | instance->packetQueue = NULL; |
134 | irp = queue->pendingIrp; | |
135 | queue->pendingIrp = NULL; | |
136 | if (irp) { | |
137 | cancelRoutine = IoSetCancelRoutine(irp, NULL); | |
138 | if (cancelRoutine == NULL) { | |
139 | irp = NULL; | |
140 | } | |
141 | } | |
142 | NdisReleaseSpinLock(&queue->queueLock); | |
ae1fd386 | 143 | NdisFreeSpinLock(&queue->queueLock); |
c803536e SS |
144 | } |
145 | LIST_FORALL_SAFE(&tmp, link, next) { | |
146 | RemoveEntryList(link); | |
147 | elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); | |
d016f841 | 148 | OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG); |
c803536e SS |
149 | } |
150 | if (irp) { | |
151 | OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS); | |
152 | } | |
ae1fd386 | 153 | if (queue) { |
d016f841 | 154 | OvsFreeMemoryWithTag(queue, OVS_USER_POOL_TAG); |
ae1fd386 | 155 | } |
777d2bbd | 156 | |
3b60a203 | 157 | /* Verify if gOvsSwitchContext exists. */ |
3b60a203 SV |
158 | if (gOvsSwitchContext) { |
159 | /* Remove the instance from pidHashArray */ | |
160 | OvsAcquirePidHashLock(); | |
161 | OvsDelPidInstance(gOvsSwitchContext, instance->pid); | |
162 | OvsReleasePidHashLock(); | |
163 | } | |
c803536e SS |
164 | } |
165 | ||
166 | NTSTATUS | |
ae1fd386 EE |
167 | OvsSubscribeDpIoctl(PVOID instanceP, |
168 | UINT32 pid, | |
169 | UINT8 join) | |
c803536e | 170 | { |
c803536e | 171 | POVS_USER_PACKET_QUEUE queue; |
ae1fd386 EE |
172 | POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)instanceP; |
173 | ||
174 | if (instance->packetQueue && !join) { | |
175 | /* unsubscribe */ | |
c803536e | 176 | OvsCleanupPacketQueue(instance); |
ae1fd386 | 177 | } else if (instance->packetQueue == NULL && join) { |
d016f841 SV |
178 | queue = (POVS_USER_PACKET_QUEUE) OvsAllocateMemoryWithTag( |
179 | sizeof *queue, OVS_USER_POOL_TAG); | |
ae1fd386 EE |
180 | if (queue == NULL) { |
181 | return STATUS_NO_MEMORY; | |
c803536e | 182 | } |
75752ef5 | 183 | InitializeListHead(&(instance->pidLink)); |
ae1fd386 EE |
184 | instance->packetQueue = queue; |
185 | RtlZeroMemory(queue, sizeof (*queue)); | |
186 | NdisAllocateSpinLock(&queue->queueLock); | |
187 | NdisAcquireSpinLock(&queue->queueLock); | |
188 | InitializeListHead(&queue->packetList); | |
189 | queue->pid = pid; | |
c803536e SS |
190 | queue->instance = instance; |
191 | instance->packetQueue = queue; | |
c803536e | 192 | NdisReleaseSpinLock(&queue->queueLock); |
10023cb5 | 193 | |
4a3c9b70 | 194 | OvsAcquirePidHashLock(); |
10023cb5 AS |
195 | /* Insert the instance to pidHashArray */ |
196 | OvsAddPidInstance(gOvsSwitchContext, pid, instance); | |
4a3c9b70 | 197 | OvsReleasePidHashLock(); |
10023cb5 | 198 | |
c803536e | 199 | } else { |
ae1fd386 | 200 | /* user mode should call only once for subscribe */ |
c803536e SS |
201 | return STATUS_INVALID_PARAMETER; |
202 | } | |
10023cb5 | 203 | |
c803536e SS |
204 | return STATUS_SUCCESS; |
205 | } | |
206 | ||
207 | ||
208 | NTSTATUS | |
209 | OvsReadDpIoctl(PFILE_OBJECT fileObject, | |
210 | PVOID outputBuffer, | |
211 | UINT32 outputLength, | |
212 | UINT32 *replyLen) | |
213 | { | |
214 | POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; | |
215 | POVS_PACKET_QUEUE_ELEM elem; | |
216 | UINT32 len; | |
217 | ||
218 | #define TCP_CSUM_OFFSET 16 | |
219 | #define UDP_CSUM_OFFSET 6 | |
220 | ASSERT(instance); | |
221 | ||
222 | if (instance->packetQueue == NULL) { | |
223 | return STATUS_INVALID_PARAMETER; | |
224 | } | |
225 | if (outputLength < (sizeof (OVS_PACKET_INFO) + OVS_MIN_PACKET_SIZE)) { | |
226 | return STATUS_BUFFER_TOO_SMALL; | |
227 | } | |
228 | ||
229 | elem = OvsGetNextPacket(instance); | |
230 | if (elem) { | |
231 | /* | |
232 | * XXX revisit this later | |
233 | */ | |
234 | len = elem->packet.totalLen > outputLength ? outputLength : | |
235 | elem->packet.totalLen; | |
236 | ||
237 | if ((elem->hdrInfo.tcpCsumNeeded || elem->hdrInfo.udpCsumNeeded) && | |
238 | len == elem->packet.totalLen) { | |
239 | UINT16 sum, *ptr; | |
0d2cb708 EE |
240 | UINT16 size = (UINT16)(elem->packet.payload - elem->packet.data + |
241 | elem->hdrInfo.l4Offset); | |
242 | RtlCopyMemory(outputBuffer, &elem->packet.data, size); | |
243 | ASSERT(len - size >= elem->hdrInfo.l4PayLoad); | |
c803536e | 244 | sum = CopyAndCalculateChecksum((UINT8 *)outputBuffer + size, |
0d2cb708 | 245 | (UINT8 *)&elem->packet.data + size, |
c803536e SS |
246 | elem->hdrInfo.l4PayLoad, 0); |
247 | ptr =(UINT16 *)((UINT8 *)outputBuffer + size + | |
248 | (elem->hdrInfo.tcpCsumNeeded ? | |
249 | TCP_CSUM_OFFSET : UDP_CSUM_OFFSET)); | |
250 | *ptr = sum; | |
251 | ovsUserStats.l4Csum++; | |
252 | } else { | |
befa5b43 | 253 | RtlCopyMemory(outputBuffer, &elem->packet.data, len); |
c803536e SS |
254 | } |
255 | ||
256 | *replyLen = len; | |
d016f841 | 257 | OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG); |
c803536e SS |
258 | } |
259 | return STATUS_SUCCESS; | |
260 | } | |
261 | ||
094a1315 AS |
262 | /* |
263 | *---------------------------------------------------------------------------- | |
264 | * OvsNlExecuteCmdHandler -- | |
265 | * Handler for OVS_PACKET_CMD_EXECUTE command. | |
266 | *---------------------------------------------------------------------------- | |
267 | */ | |
268 | NTSTATUS | |
269 | OvsNlExecuteCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, | |
270 | UINT32 *replyLen) | |
271 | { | |
efa753a8 AS |
272 | NTSTATUS status = STATUS_SUCCESS; |
273 | POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; | |
274 | POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; | |
275 | PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); | |
276 | PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); | |
277 | POVS_HDR ovsHdr = &(msgIn->ovsHdr); | |
278 | ||
279 | PNL_ATTR nlAttrs[__OVS_PACKET_ATTR_MAX]; | |
280 | PNL_ATTR keyAttrs[__OVS_KEY_ATTR_MAX] = {NULL}; | |
281 | ||
282 | UINT32 attrOffset = NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN; | |
283 | UINT32 keyAttrOffset = 0; | |
284 | OvsPacketExecute execute; | |
285 | NL_ERROR nlError = NL_ERROR_SUCCESS; | |
286 | NL_BUFFER nlBuf; | |
287 | ||
288 | static const NL_POLICY nlPktExecPolicy[] = { | |
289 | [OVS_PACKET_ATTR_PACKET] = {.type = NL_A_UNSPEC, .optional = FALSE}, | |
290 | [OVS_PACKET_ATTR_KEY] = {.type = NL_A_UNSPEC, .optional = FALSE}, | |
291 | [OVS_PACKET_ATTR_ACTIONS] = {.type = NL_A_UNSPEC, .optional = FALSE}, | |
292 | [OVS_PACKET_ATTR_USERDATA] = {.type = NL_A_UNSPEC, .optional = TRUE}, | |
293 | [OVS_PACKET_ATTR_EGRESS_TUN_KEY] = {.type = NL_A_UNSPEC, | |
39ccaaf7 AK |
294 | .optional = TRUE}, |
295 | [OVS_PACKET_ATTR_MRU] = { .type = NL_A_U16, .optional = TRUE } | |
efa753a8 AS |
296 | }; |
297 | ||
298 | RtlZeroMemory(&execute, sizeof(OvsPacketExecute)); | |
299 | ||
300 | /* Get all the top level Flow attributes */ | |
301 | if ((NlAttrParse(nlMsgHdr, attrOffset, NlMsgAttrsLen(nlMsgHdr), | |
b8b00f0c SV |
302 | nlPktExecPolicy, ARRAY_SIZE(nlPktExecPolicy), |
303 | nlAttrs, ARRAY_SIZE(nlAttrs))) | |
efa753a8 AS |
304 | != TRUE) { |
305 | OVS_LOG_ERROR("Attr Parsing failed for msg: %p", | |
306 | nlMsgHdr); | |
307 | status = STATUS_UNSUCCESSFUL; | |
308 | goto done; | |
309 | } | |
310 | ||
311 | keyAttrOffset = (UINT32)((PCHAR)nlAttrs[OVS_PACKET_ATTR_KEY] - | |
312 | (PCHAR)nlMsgHdr); | |
313 | ||
314 | /* Get flow keys attributes */ | |
315 | if ((NlAttrParseNested(nlMsgHdr, keyAttrOffset, | |
316 | NlAttrLen(nlAttrs[OVS_PACKET_ATTR_KEY]), | |
b8b00f0c SV |
317 | nlFlowKeyPolicy, nlFlowKeyPolicyLen, |
318 | keyAttrs, ARRAY_SIZE(keyAttrs))) != TRUE) { | |
efa753a8 AS |
319 | OVS_LOG_ERROR("Key Attr Parsing failed for msg: %p", nlMsgHdr); |
320 | status = STATUS_UNSUCCESSFUL; | |
321 | goto done; | |
322 | } | |
323 | ||
c8915346 AK |
324 | if (keyAttrs[OVS_KEY_ATTR_ENCAP]) { |
325 | UINT32 encapOffset = 0; | |
326 | PNL_ATTR encapAttrs[__OVS_KEY_ATTR_MAX]; | |
327 | encapOffset = (UINT32)((PCHAR)(keyAttrs[OVS_KEY_ATTR_ENCAP]) | |
328 | - (PCHAR)nlMsgHdr); | |
329 | ||
330 | if ((NlAttrParseNested(nlMsgHdr, encapOffset, | |
331 | NlAttrLen(keyAttrs[OVS_KEY_ATTR_ENCAP]), | |
332 | nlFlowKeyPolicy, | |
333 | nlFlowKeyPolicyLen, | |
334 | encapAttrs, ARRAY_SIZE(encapAttrs))) | |
335 | != TRUE) { | |
336 | OVS_LOG_ERROR("Encap Key Attr Parsing failed for msg: %p", | |
337 | nlMsgHdr); | |
338 | status = STATUS_UNSUCCESSFUL; | |
339 | goto done; | |
340 | } | |
341 | } | |
342 | ||
efa753a8 AS |
343 | execute.dpNo = ovsHdr->dp_ifindex; |
344 | ||
3fd9ec0b | 345 | _MapNlAttrToOvsPktExec(nlMsgHdr, nlAttrs, keyAttrs, &execute); |
efa753a8 AS |
346 | |
347 | status = OvsExecuteDpIoctl(&execute); | |
348 | ||
7c5d9f17 | 349 | /* Default reply that we want to send */ |
efa753a8 | 350 | if (status == STATUS_SUCCESS) { |
1ad44ad4 NR |
351 | BOOLEAN ok; |
352 | ||
efa753a8 AS |
353 | NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, |
354 | usrParamsCtx->outputLength); | |
355 | ||
356 | /* Prepare nl Msg headers */ | |
1ad44ad4 | 357 | ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, |
efa753a8 AS |
358 | nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, |
359 | genlMsgHdr->cmd, OVS_PACKET_VERSION, | |
360 | ovsHdr->dp_ifindex); | |
361 | ||
1ad44ad4 | 362 | if (ok) { |
efa753a8 | 363 | *replyLen = msgOut->nlMsg.nlmsgLen; |
1ad44ad4 NR |
364 | } else { |
365 | status = STATUS_INVALID_BUFFER_SIZE; | |
efa753a8 | 366 | } |
7c5d9f17 AS |
367 | } else { |
368 | /* Map NTSTATUS to NL_ERROR */ | |
369 | nlError = NlMapStatusToNlErr(status); | |
370 | ||
371 | /* As of now there are no transactional errors in the implementation. | |
372 | * Once we have them then we need to map status to correct | |
373 | * nlError value, so that below mentioned code gets hit. */ | |
374 | if ((nlError != NL_ERROR_SUCCESS) && | |
375 | (usrParamsCtx->outputBuffer)) { | |
376 | ||
377 | POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) | |
378 | usrParamsCtx->outputBuffer; | |
e6b298ef | 379 | |
7e98ed23 NR |
380 | ASSERT(msgError); |
381 | NlBuildErrorMsg(msgIn, msgError, nlError, replyLen); | |
7c5d9f17 AS |
382 | status = STATUS_SUCCESS; |
383 | goto done; | |
384 | } | |
efa753a8 AS |
385 | } |
386 | ||
387 | done: | |
388 | return status; | |
389 | } | |
390 | ||
391 | /* | |
392 | *---------------------------------------------------------------------------- | |
393 | * _MapNlAttrToOvsPktExec -- | |
394 | * Maps input Netlink attributes to OvsPacketExecute. | |
395 | *---------------------------------------------------------------------------- | |
396 | */ | |
397 | static VOID | |
3fd9ec0b NR |
398 | _MapNlAttrToOvsPktExec(PNL_MSG_HDR nlMsgHdr, PNL_ATTR *nlAttrs, |
399 | PNL_ATTR *keyAttrs, OvsPacketExecute *execute) | |
efa753a8 AS |
400 | { |
401 | execute->packetBuf = NlAttrGet(nlAttrs[OVS_PACKET_ATTR_PACKET]); | |
402 | execute->packetLen = NlAttrGetSize(nlAttrs[OVS_PACKET_ATTR_PACKET]); | |
094a1315 | 403 | |
3fd9ec0b NR |
404 | execute->nlMsgHdr = nlMsgHdr; |
405 | ||
efa753a8 AS |
406 | execute->actions = NlAttrGet(nlAttrs[OVS_PACKET_ATTR_ACTIONS]); |
407 | execute->actionsLen = NlAttrGetSize(nlAttrs[OVS_PACKET_ATTR_ACTIONS]); | |
094a1315 | 408 | |
3871d4fb | 409 | ASSERT(keyAttrs[OVS_KEY_ATTR_IN_PORT]); |
efa753a8 | 410 | execute->inPort = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_IN_PORT]); |
877a19b7 | 411 | execute->keyAttrs = keyAttrs; |
39ccaaf7 AK |
412 | |
413 | if (nlAttrs[OVS_PACKET_ATTR_MRU]) { | |
414 | execute->mru = NlAttrGetU16(nlAttrs[OVS_PACKET_ATTR_MRU]); | |
415 | } | |
094a1315 AS |
416 | } |
417 | ||
c803536e | 418 | NTSTATUS |
a74933bc | 419 | OvsExecuteDpIoctl(OvsPacketExecute *execute) |
c803536e SS |
420 | { |
421 | NTSTATUS status = STATUS_SUCCESS; | |
ee25964a | 422 | NTSTATUS ndisStatus = STATUS_SUCCESS; |
c803536e | 423 | LOCK_STATE_EX lockState; |
ee25964a SV |
424 | PNET_BUFFER_LIST pNbl = NULL; |
425 | PNL_ATTR actions = NULL; | |
c803536e | 426 | PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; |
ee25964a SV |
427 | OvsFlowKey key = { 0 }; |
428 | OVS_PACKET_HDR_INFO layers = { 0 }; | |
429 | POVS_VPORT_ENTRY vport = NULL; | |
3871d4fb NR |
430 | PNL_ATTR tunnelAttrs[__OVS_TUNNEL_KEY_ATTR_MAX]; |
431 | OvsFlowKey tempTunKey = {0}; | |
39ccaaf7 | 432 | POVS_BUFFER_CONTEXT ctx; |
c803536e | 433 | |
c803536e SS |
434 | if (execute->packetLen == 0) { |
435 | status = STATUS_INVALID_PARAMETER; | |
3ed424e5 | 436 | goto exit; |
c803536e SS |
437 | } |
438 | ||
a74933bc AS |
439 | actions = execute->actions; |
440 | ||
441 | ASSERT(actions); | |
c803536e SS |
442 | |
443 | /* | |
444 | * Allocate the NBL, copy the data from the userspace buffer. Allocate | |
445 | * also, the forwarding context for the packet. | |
446 | */ | |
9a80ee14 SV |
447 | pNbl = OvsAllocateNBLFromBuffer(gOvsSwitchContext, execute->packetBuf, |
448 | execute->packetLen); | |
c803536e SS |
449 | if (pNbl == NULL) { |
450 | status = STATUS_NO_MEMORY; | |
3ed424e5 | 451 | goto exit; |
c803536e SS |
452 | } |
453 | ||
454 | fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(pNbl); | |
c803536e SS |
455 | // XXX: Figure out if any of the other members of fwdDetail need to be set. |
456 | ||
877a19b7 SV |
457 | status = OvsGetFlowMetadata(&key, execute->keyAttrs); |
458 | if (status != STATUS_SUCCESS) { | |
459 | goto dropit; | |
460 | } | |
461 | ||
3871d4fb NR |
462 | if (execute->keyAttrs[OVS_KEY_ATTR_TUNNEL]) { |
463 | UINT32 tunnelKeyAttrOffset; | |
464 | ||
465 | tunnelKeyAttrOffset = (UINT32)((PCHAR) | |
466 | (execute->keyAttrs[OVS_KEY_ATTR_TUNNEL]) | |
467 | - (PCHAR)execute->nlMsgHdr); | |
468 | ||
469 | /* Get tunnel keys attributes */ | |
470 | if ((NlAttrParseNested(execute->nlMsgHdr, tunnelKeyAttrOffset, | |
471 | NlAttrLen(execute->keyAttrs[OVS_KEY_ATTR_TUNNEL]), | |
472 | nlFlowTunnelKeyPolicy, nlFlowTunnelKeyPolicyLen, | |
473 | tunnelAttrs, ARRAY_SIZE(tunnelAttrs))) | |
474 | != TRUE) { | |
475 | OVS_LOG_ERROR("Tunnel key Attr Parsing failed for msg: %p", | |
476 | execute->nlMsgHdr); | |
477 | status = STATUS_INVALID_PARAMETER; | |
478 | goto dropit; | |
479 | } | |
480 | ||
481 | MapTunAttrToFlowPut(execute->keyAttrs, tunnelAttrs, &tempTunKey); | |
482 | } | |
483 | ||
484 | ndisStatus = OvsExtractFlow(pNbl, execute->inPort, &key, &layers, | |
485 | tempTunKey.tunKey.dst == 0 ? NULL : &tempTunKey.tunKey); | |
486 | ||
9d71ade0 SR |
487 | if (ndisStatus != NDIS_STATUS_SUCCESS) { |
488 | /* Invalid network header */ | |
489 | goto dropit; | |
490 | } | |
491 | ||
39ccaaf7 AK |
492 | ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(pNbl); |
493 | ctx->mru = execute->mru; | |
494 | ||
c803536e | 495 | if (ndisStatus == NDIS_STATUS_SUCCESS) { |
3ed424e5 | 496 | NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0); |
3cdf29c5 AGS |
497 | vport = OvsFindVportByPortNo(gOvsSwitchContext, execute->inPort); |
498 | if (vport) { | |
499 | fwdDetail->SourcePortId = vport->portId; | |
500 | fwdDetail->SourceNicIndex = vport->nicIndex; | |
501 | } else { | |
502 | fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID; | |
503 | fwdDetail->SourceNicIndex = 0; | |
504 | } | |
c803536e | 505 | ndisStatus = OvsActionsExecute(gOvsSwitchContext, NULL, pNbl, |
c5c2c273 | 506 | vport ? vport->portNo : |
12e888ba | 507 | OVS_DPPORT_NUMBER_INVALID, |
b2d9d3e8 NR |
508 | NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP, |
509 | &key, NULL, &layers, actions, | |
510 | execute->actionsLen); | |
c803536e SS |
511 | pNbl = NULL; |
512 | NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); | |
513 | } | |
514 | if (ndisStatus != NDIS_STATUS_SUCCESS) { | |
7c5d9f17 AS |
515 | if (ndisStatus == NDIS_STATUS_NOT_SUPPORTED) { |
516 | status = STATUS_NOT_SUPPORTED; | |
517 | } else { | |
518 | status = STATUS_UNSUCCESSFUL; | |
519 | } | |
c803536e SS |
520 | } |
521 | ||
877a19b7 | 522 | dropit: |
c803536e SS |
523 | if (pNbl) { |
524 | OvsCompleteNBL(gOvsSwitchContext, pNbl, TRUE); | |
525 | } | |
3ed424e5 | 526 | exit: |
c803536e SS |
527 | return status; |
528 | } | |
529 | ||
530 | ||
531 | NTSTATUS | |
532 | OvsPurgeDpIoctl(PFILE_OBJECT fileObject) | |
533 | { | |
534 | POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; | |
535 | POVS_USER_PACKET_QUEUE queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue; | |
536 | ||
537 | if (queue == NULL) { | |
538 | return STATUS_INVALID_PARAMETER; | |
539 | } | |
540 | OvsPurgePacketQueue(queue, instance); | |
541 | return STATUS_SUCCESS; | |
542 | } | |
543 | ||
544 | VOID | |
545 | OvsCancelIrpDatapath(PDEVICE_OBJECT deviceObject, | |
546 | PIRP irp) | |
547 | { | |
548 | PIO_STACK_LOCATION irpSp; | |
549 | PFILE_OBJECT fileObject; | |
550 | POVS_OPEN_INSTANCE instance; | |
551 | POVS_USER_PACKET_QUEUE queue = NULL; | |
552 | ||
553 | UNREFERENCED_PARAMETER(deviceObject); | |
554 | ||
555 | IoReleaseCancelSpinLock(irp->CancelIrql); | |
556 | irpSp = IoGetCurrentIrpStackLocation(irp); | |
557 | fileObject = irpSp->FileObject; | |
558 | ||
559 | if (fileObject == NULL) { | |
560 | goto done; | |
561 | } | |
562 | NdisAcquireSpinLock(gOvsCtrlLock); | |
563 | instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; | |
564 | if (instance) { | |
565 | queue = instance->packetQueue; | |
566 | } | |
567 | if (instance == NULL || queue == NULL) { | |
568 | NdisReleaseSpinLock(gOvsCtrlLock); | |
569 | goto done; | |
570 | } | |
571 | NdisReleaseSpinLock(gOvsCtrlLock); | |
572 | NdisAcquireSpinLock(&queue->queueLock); | |
573 | if (queue->pendingIrp == irp) { | |
574 | queue->pendingIrp = NULL; | |
575 | } | |
576 | NdisReleaseSpinLock(&queue->queueLock); | |
577 | done: | |
578 | OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED); | |
579 | } | |
580 | ||
581 | ||
582 | NTSTATUS | |
583 | OvsWaitDpIoctl(PIRP irp, PFILE_OBJECT fileObject) | |
584 | { | |
585 | POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; | |
586 | POVS_USER_PACKET_QUEUE queue = | |
587 | (POVS_USER_PACKET_QUEUE)instance->packetQueue; | |
588 | NTSTATUS status = STATUS_SUCCESS; | |
589 | BOOLEAN cancelled = FALSE; | |
590 | ||
591 | if (queue == NULL) { | |
592 | return STATUS_INVALID_PARAMETER; | |
593 | } | |
594 | NdisAcquireSpinLock(&queue->queueLock); | |
595 | if (queue->instance != instance) { | |
596 | NdisReleaseSpinLock(&queue->queueLock); | |
597 | return STATUS_INVALID_PARAMETER; | |
598 | } | |
599 | if (queue->pendingIrp) { | |
600 | NdisReleaseSpinLock(&queue->queueLock); | |
601 | return STATUS_DEVICE_BUSY; | |
602 | } | |
603 | if (queue->numPackets == 0) { | |
604 | PDRIVER_CANCEL cancelRoutine; | |
605 | IoMarkIrpPending(irp); | |
606 | IoSetCancelRoutine(irp, OvsCancelIrpDatapath); | |
607 | if (irp->Cancel) { | |
608 | cancelRoutine = IoSetCancelRoutine(irp, NULL); | |
609 | if (cancelRoutine) { | |
610 | cancelled = TRUE; | |
611 | } | |
612 | } else { | |
613 | queue->pendingIrp = irp; | |
614 | } | |
615 | status = STATUS_PENDING; | |
616 | } | |
617 | NdisReleaseSpinLock(&queue->queueLock); | |
618 | if (cancelled) { | |
619 | OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED); | |
620 | OVS_LOG_INFO("Datapath IRP cancelled: %p", irp); | |
621 | } | |
622 | return status; | |
623 | } | |
624 | ||
625 | ||
626 | POVS_PACKET_QUEUE_ELEM | |
627 | OvsGetNextPacket(POVS_OPEN_INSTANCE instance) | |
628 | { | |
629 | POVS_USER_PACKET_QUEUE queue; | |
630 | PLIST_ENTRY link; | |
631 | queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue; | |
632 | if (queue == NULL) { | |
633 | return NULL; | |
634 | } | |
635 | NdisAcquireSpinLock(&queue->queueLock); | |
636 | if (queue->instance != instance || queue->numPackets == 0) { | |
637 | NdisReleaseSpinLock(&queue->queueLock); | |
638 | return NULL; | |
639 | } | |
640 | link = RemoveHeadList(&queue->packetList); | |
641 | queue->numPackets--; | |
642 | NdisReleaseSpinLock(&queue->queueLock); | |
643 | return CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); | |
644 | } | |
645 | ||
d0ce1160 AS |
646 | /* |
647 | * --------------------------------------------------------------------------- | |
648 | * Given a pid, returns the corresponding USER_PACKET_QUEUE. | |
d0ce1160 AS |
649 | * --------------------------------------------------------------------------- |
650 | */ | |
c803536e | 651 | POVS_USER_PACKET_QUEUE |
ae1fd386 | 652 | OvsGetQueue(UINT32 pid) |
c803536e | 653 | { |
d0ce1160 AS |
654 | POVS_OPEN_INSTANCE instance; |
655 | POVS_USER_PACKET_QUEUE ret = NULL; | |
656 | ||
657 | instance = OvsGetPidInstance(gOvsSwitchContext, pid); | |
658 | ||
659 | if (instance) { | |
660 | ret = instance->packetQueue; | |
661 | } | |
662 | ||
663 | return ret; | |
c803536e SS |
664 | } |
665 | ||
65ae4384 AS |
666 | /* |
667 | * --------------------------------------------------------------------------- | |
668 | * Given a pid, returns the corresponding instance. | |
4a3c9b70 | 669 | * pidHashLock must be acquired before calling this API. |
65ae4384 AS |
670 | * --------------------------------------------------------------------------- |
671 | */ | |
672 | POVS_OPEN_INSTANCE | |
673 | OvsGetPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid) | |
674 | { | |
675 | POVS_OPEN_INSTANCE instance; | |
676 | PLIST_ENTRY head, link; | |
677 | UINT32 hash = OvsJhashBytes((const VOID *)&pid, sizeof(pid), | |
678 | OVS_HASH_BASIS); | |
679 | head = &(switchContext->pidHashArray[hash & OVS_PID_MASK]); | |
680 | LIST_FORALL(head, link) { | |
681 | instance = CONTAINING_RECORD(link, OVS_OPEN_INSTANCE, pidLink); | |
682 | if (instance->pid == pid) { | |
683 | return instance; | |
684 | } | |
685 | } | |
686 | return NULL; | |
687 | } | |
688 | ||
689 | /* | |
690 | * --------------------------------------------------------------------------- | |
691 | * Given a pid and an instance. This API adds instance to pidHashArray. | |
4a3c9b70 | 692 | * pidHashLock must be acquired before calling this API. |
65ae4384 AS |
693 | * --------------------------------------------------------------------------- |
694 | */ | |
695 | VOID | |
696 | OvsAddPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid, | |
697 | POVS_OPEN_INSTANCE instance) | |
698 | { | |
699 | PLIST_ENTRY head; | |
700 | UINT32 hash = OvsJhashBytes((const VOID *)&pid, sizeof(pid), | |
701 | OVS_HASH_BASIS); | |
702 | head = &(switchContext->pidHashArray[hash & OVS_PID_MASK]); | |
703 | InsertHeadList(head, &(instance->pidLink)); | |
704 | } | |
705 | ||
706 | /* | |
707 | * --------------------------------------------------------------------------- | |
708 | * Given a pid and an instance. This API removes instance from pidHashArray. | |
4a3c9b70 | 709 | * pidHashLock must be acquired before calling this API. |
65ae4384 AS |
710 | * --------------------------------------------------------------------------- |
711 | */ | |
712 | VOID | |
713 | OvsDelPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid) | |
714 | { | |
715 | POVS_OPEN_INSTANCE instance = OvsGetPidInstance(switchContext, pid); | |
716 | ||
717 | if (instance) { | |
718 | RemoveEntryList(&(instance->pidLink)); | |
719 | } | |
720 | } | |
721 | ||
c803536e | 722 | VOID |
4a3c9b70 | 723 | OvsQueuePackets(PLIST_ENTRY packetList, |
c803536e SS |
724 | UINT32 numElems) |
725 | { | |
4a3c9b70 | 726 | POVS_USER_PACKET_QUEUE upcallQueue = NULL; |
c803536e | 727 | POVS_PACKET_QUEUE_ELEM elem; |
c803536e SS |
728 | PLIST_ENTRY link; |
729 | UINT32 num = 0; | |
4a3c9b70 | 730 | LIST_ENTRY dropPackets; |
c803536e | 731 | |
4a3c9b70 | 732 | OVS_LOG_LOUD("Enter: numELems: %u", numElems); |
c803536e | 733 | |
4a3c9b70 | 734 | InitializeListHead(&dropPackets); |
c803536e | 735 | |
c803536e SS |
736 | while (!IsListEmpty(packetList)) { |
737 | link = RemoveHeadList(packetList); | |
738 | elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); | |
4a3c9b70 AS |
739 | |
740 | ASSERT(elem); | |
741 | ||
742 | OvsAcquirePidHashLock(); | |
743 | ||
744 | upcallQueue = OvsGetQueue(elem->upcallPid); | |
745 | if (!upcallQueue) { | |
746 | /* No upcall queue found, drop this packet. */ | |
747 | InsertTailList(&dropPackets, &elem->link); | |
748 | } else { | |
749 | NdisAcquireSpinLock(&upcallQueue->queueLock); | |
750 | ||
751 | if (upcallQueue->instance == NULL) { | |
752 | InsertTailList(&dropPackets, &elem->link); | |
753 | } else { | |
754 | InsertTailList(&upcallQueue->packetList, &elem->link); | |
755 | upcallQueue->numPackets++; | |
756 | if (upcallQueue->pendingIrp) { | |
57d7a5f5 | 757 | PIRP irp = upcallQueue->pendingIrp; |
4a3c9b70 | 758 | PDRIVER_CANCEL cancelRoutine; |
4a3c9b70 AS |
759 | upcallQueue->pendingIrp = NULL; |
760 | cancelRoutine = IoSetCancelRoutine(irp, NULL); | |
57d7a5f5 NR |
761 | if (cancelRoutine != NULL) { |
762 | OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS); | |
4a3c9b70 AS |
763 | } |
764 | } | |
765 | } | |
4a3c9b70 AS |
766 | NdisReleaseSpinLock(&upcallQueue->queueLock); |
767 | } | |
4a3c9b70 AS |
768 | OvsReleasePidHashLock(); |
769 | } | |
770 | ||
771 | while (!IsListEmpty(&dropPackets)) { | |
772 | link = RemoveHeadList(&dropPackets); | |
773 | elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); | |
d016f841 | 774 | OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG); |
c803536e SS |
775 | num++; |
776 | } | |
4a3c9b70 | 777 | |
c803536e SS |
778 | OVS_LOG_LOUD("Exit: drop %u packets", num); |
779 | } | |
780 | ||
c803536e SS |
781 | /* |
782 | *---------------------------------------------------------------------------- | |
783 | * OvsCreateAndAddPackets -- | |
784 | * | |
785 | * Create a packet and forwarded to user space. | |
786 | * | |
787 | * This function would fragment packet if needed, and queue | |
788 | * each segment to user space. | |
789 | *---------------------------------------------------------------------------- | |
790 | */ | |
791 | NTSTATUS | |
640ebde7 | 792 | OvsCreateAndAddPackets(PVOID userData, |
c803536e SS |
793 | UINT32 userDataLen, |
794 | UINT32 cmd, | |
4c470e88 | 795 | POVS_VPORT_ENTRY vport, |
640ebde7 | 796 | OvsFlowKey *key, |
c803536e SS |
797 | PNET_BUFFER_LIST nbl, |
798 | BOOLEAN isRecv, | |
799 | POVS_PACKET_HDR_INFO hdrInfo, | |
800 | POVS_SWITCH_CONTEXT switchContext, | |
801 | LIST_ENTRY *list, | |
802 | UINT32 *num) | |
803 | { | |
804 | POVS_PACKET_QUEUE_ELEM elem; | |
805 | PNET_BUFFER_LIST newNbl = NULL; | |
806 | PNET_BUFFER nb; | |
807 | ||
808 | if (hdrInfo->isTcp) { | |
809 | NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO tsoInfo; | |
810 | UINT32 packetLength; | |
811 | ||
c3e85147 PB |
812 | tsoInfo.Value = NET_BUFFER_LIST_INFO(nbl, |
813 | TcpLargeSendNetBufferListInfo); | |
c803536e SS |
814 | nb = NET_BUFFER_LIST_FIRST_NB(nbl); |
815 | packetLength = NET_BUFFER_DATA_LENGTH(nb); | |
816 | ||
817 | OVS_LOG_TRACE("MSS %u packet len %u", | |
818 | tsoInfo.LsoV1Transmit.MSS, packetLength); | |
819 | if (tsoInfo.LsoV1Transmit.MSS) { | |
820 | OVS_LOG_TRACE("l4Offset %d", hdrInfo->l4Offset); | |
821 | newNbl = OvsTcpSegmentNBL(switchContext, nbl, hdrInfo, | |
ac8df9f6 | 822 | tsoInfo.LsoV1Transmit.MSS , 0, FALSE); |
c803536e SS |
823 | if (newNbl == NULL) { |
824 | return NDIS_STATUS_FAILURE; | |
825 | } | |
826 | nbl = newNbl; | |
827 | } | |
828 | } | |
829 | ||
830 | nb = NET_BUFFER_LIST_FIRST_NB(nbl); | |
831 | while (nb) { | |
640ebde7 | 832 | elem = OvsCreateQueueNlPacket(userData, userDataLen, |
e14fbdbb | 833 | cmd, vport, key, NULL, nbl, nb, |
c803536e SS |
834 | isRecv, hdrInfo); |
835 | if (elem) { | |
836 | InsertTailList(list, &elem->link); | |
837 | (*num)++; | |
838 | } | |
839 | nb = NET_BUFFER_NEXT_NB(nb); | |
840 | } | |
841 | if (newNbl) { | |
842 | OvsCompleteNBL(switchContext, newNbl, TRUE); | |
843 | } | |
844 | return NDIS_STATUS_SUCCESS; | |
845 | } | |
1293a628 EE |
846 | |
847 | static __inline UINT32 | |
848 | OvsGetUpcallMsgSize(PVOID userData, | |
849 | UINT32 userDataLen, | |
850 | OvsIPv4TunnelKey *tunnelKey, | |
851 | UINT32 payload) | |
852 | { | |
853 | UINT32 size = NLMSG_ALIGN(sizeof(struct ovs_header)) + | |
854 | NlAttrSize(payload) + | |
855 | NlAttrSize(OvsFlowKeyAttrSize()); | |
856 | ||
857 | /* OVS_PACKET_ATTR_USERDATA */ | |
858 | if (userData) { | |
859 | size += NlAttrTotalSize(userDataLen); | |
860 | } | |
861 | /* OVS_PACKET_ATTR_EGRESS_TUN_KEY */ | |
898dcef1 | 862 | /* Is it included in the flow key attr XXX */ |
1293a628 EE |
863 | if (tunnelKey) { |
864 | size += NlAttrTotalSize(OvsTunKeyAttrSize()); | |
865 | } | |
866 | return size; | |
867 | } | |
868 | ||
869 | /* | |
870 | *---------------------------------------------------------------------------- | |
871 | * This function completes the IP Header csum. record the L4 payload offset and | |
872 | * if there is a need to calculate the TCP or UDP csum. The actual csum will be | |
873 | * caluculated simopultaneossly with the copy of the payload to the destination | |
874 | * buffer when the packet is read. | |
875 | *---------------------------------------------------------------------------- | |
876 | */ | |
877 | static VOID | |
878 | OvsCompletePacketHeader(UINT8 *packet, | |
879 | BOOLEAN isRecv, | |
880 | NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo, | |
881 | POVS_PACKET_HDR_INFO hdrInfoIn, | |
882 | POVS_PACKET_HDR_INFO hdrInfoOut) | |
883 | { | |
884 | if ((isRecv && csumInfo.Receive.IpChecksumValueInvalid) || | |
885 | (!isRecv && csumInfo.Transmit.IsIPv4 && | |
886 | csumInfo.Transmit.IpHeaderChecksum)) { | |
887 | PIPV4_HEADER ipHdr = (PIPV4_HEADER)(packet + hdrInfoOut->l3Offset); | |
888 | ASSERT(hdrInfoIn->isIPv4); | |
889 | ASSERT(ipHdr->Version == 4); | |
890 | ipHdr->HeaderChecksum = IPChecksum((UINT8 *)ipHdr, | |
891 | ipHdr->HeaderLength << 2, | |
892 | (UINT16)~ipHdr->HeaderChecksum); | |
893 | ovsUserStats.ipCsum++; | |
894 | } | |
895 | ASSERT(hdrInfoIn->tcpCsumNeeded == 0 && hdrInfoOut->udpCsumNeeded == 0); | |
896 | /* | |
897 | * calculate TCP/UDP pseudo checksum | |
898 | */ | |
899 | if (isRecv && csumInfo.Receive.TcpChecksumValueInvalid) { | |
900 | /* | |
901 | * Only this case, we need to reclaculate pseudo checksum | |
902 | * all other cases, it is assumed the pseudo checksum is | |
903 | * filled already. | |
904 | * | |
905 | */ | |
906 | PTCP_HDR tcpHdr = (PTCP_HDR)(packet + hdrInfoIn->l4Offset); | |
907 | if (hdrInfoIn->isIPv4) { | |
908 | PIPV4_HEADER ipHdr = (PIPV4_HEADER)(packet + hdrInfoIn->l3Offset); | |
909 | hdrInfoOut->l4PayLoad = (UINT16)(ntohs(ipHdr->TotalLength) - | |
910 | (ipHdr->HeaderLength << 2)); | |
911 | tcpHdr->th_sum = IPPseudoChecksum((UINT32 *)&ipHdr->SourceAddress, | |
912 | (UINT32 *)&ipHdr->DestinationAddress, | |
913 | IPPROTO_TCP, hdrInfoOut->l4PayLoad); | |
914 | } else { | |
c3e85147 PB |
915 | PIPV6_HEADER ipv6Hdr = (PIPV6_HEADER)(packet + |
916 | hdrInfoIn->l3Offset); | |
1293a628 EE |
917 | hdrInfoOut->l4PayLoad = |
918 | (UINT16)(ntohs(ipv6Hdr->PayloadLength) + | |
919 | hdrInfoIn->l3Offset + sizeof(IPV6_HEADER)- | |
920 | hdrInfoIn->l4Offset); | |
921 | ASSERT(hdrInfoIn->isIPv6); | |
922 | tcpHdr->th_sum = | |
923 | IPv6PseudoChecksum((UINT32 *)&ipv6Hdr->SourceAddress, | |
924 | (UINT32 *)&ipv6Hdr->DestinationAddress, | |
925 | IPPROTO_TCP, hdrInfoOut->l4PayLoad); | |
926 | } | |
927 | hdrInfoOut->tcpCsumNeeded = 1; | |
928 | ovsUserStats.recalTcpCsum++; | |
929 | } else if (!isRecv) { | |
c3e85147 | 930 | if (hdrInfoIn->isTcp && csumInfo.Transmit.TcpChecksum) { |
1293a628 | 931 | hdrInfoOut->tcpCsumNeeded = 1; |
c3e85147 | 932 | } else if (hdrInfoIn->isUdp && csumInfo.Transmit.UdpChecksum) { |
1293a628 EE |
933 | hdrInfoOut->udpCsumNeeded = 1; |
934 | } | |
935 | if (hdrInfoOut->tcpCsumNeeded || hdrInfoOut->udpCsumNeeded) { | |
936 | #ifdef DBG | |
937 | UINT16 sum, *ptr; | |
938 | UINT8 proto = | |
939 | hdrInfoOut->tcpCsumNeeded ? IPPROTO_TCP : IPPROTO_UDP; | |
940 | #endif | |
941 | if (hdrInfoIn->isIPv4) { | |
c3e85147 PB |
942 | PIPV4_HEADER ipHdr = (PIPV4_HEADER)(packet + |
943 | hdrInfoIn->l3Offset); | |
1293a628 EE |
944 | hdrInfoOut->l4PayLoad = (UINT16)(ntohs(ipHdr->TotalLength) - |
945 | (ipHdr->HeaderLength << 2)); | |
946 | #ifdef DBG | |
947 | sum = IPPseudoChecksum((UINT32 *)&ipHdr->SourceAddress, | |
948 | (UINT32 *)&ipHdr->DestinationAddress, | |
949 | proto, hdrInfoOut->l4PayLoad); | |
950 | #endif | |
951 | } else { | |
952 | PIPV6_HEADER ipv6Hdr = (PIPV6_HEADER)(packet + | |
953 | hdrInfoIn->l3Offset); | |
954 | hdrInfoOut->l4PayLoad = | |
955 | (UINT16)(ntohs(ipv6Hdr->PayloadLength) + | |
956 | hdrInfoIn->l3Offset + sizeof(IPV6_HEADER)- | |
957 | hdrInfoIn->l4Offset); | |
958 | ASSERT(hdrInfoIn->isIPv6); | |
959 | #ifdef DBG | |
960 | sum = IPv6PseudoChecksum((UINT32 *)&ipv6Hdr->SourceAddress, | |
961 | (UINT32 *)&ipv6Hdr->DestinationAddress, | |
962 | proto, hdrInfoOut->l4PayLoad); | |
963 | #endif | |
964 | } | |
965 | #ifdef DBG | |
966 | ptr = (UINT16 *)(packet + hdrInfoIn->l4Offset + | |
967 | (hdrInfoOut->tcpCsumNeeded ? | |
968 | TCP_CSUM_OFFSET : UDP_CSUM_OFFSET)); | |
969 | ASSERT(*ptr == sum); | |
970 | #endif | |
971 | } | |
972 | } | |
973 | } | |
974 | ||
975 | static NTSTATUS | |
976 | OvsGetPid(POVS_VPORT_ENTRY vport, PNET_BUFFER nb, UINT32 *pid) | |
977 | { | |
978 | UNREFERENCED_PARAMETER(nb); | |
979 | ||
4a3c9b70 AS |
980 | ASSERT(vport); |
981 | ||
1293a628 EE |
982 | /* XXX select a pid from an array of pids using a flow based hash */ |
983 | *pid = vport->upcallPid; | |
984 | return STATUS_SUCCESS; | |
985 | } | |
986 | ||
987 | /* | |
988 | *---------------------------------------------------------------------------- | |
989 | * OvsCreateQueueNlPacket -- | |
990 | * | |
991 | * Create a packet which will be forwarded to user space. | |
992 | * | |
993 | * InputParameter: | |
994 | * userData: when cmd is user action, this field contain | |
995 | * user action data. | |
996 | * userDataLen: as name indicated | |
997 | * cmd: either miss or user action | |
998 | * inPort: datapath port id from which the packet is received. | |
999 | * key: flow Key with a tunnel key if available | |
1000 | * nbl: the NET_BUFFER_LIST which contain the packet | |
1001 | * nb: the packet | |
1002 | * isRecv: This is used to decide how to interprete the csum info | |
1003 | * hdrInfo: include hdr info initialized during flow extraction. | |
1004 | * | |
1005 | * Results: | |
1006 | * NULL if fail to create the packet | |
1007 | * The packet element otherwise | |
1008 | *---------------------------------------------------------------------------- | |
1009 | */ | |
1010 | POVS_PACKET_QUEUE_ELEM | |
1011 | OvsCreateQueueNlPacket(PVOID userData, | |
1012 | UINT32 userDataLen, | |
1013 | UINT32 cmd, | |
4c470e88 | 1014 | POVS_VPORT_ENTRY vport, |
1293a628 | 1015 | OvsFlowKey *key, |
e14fbdbb | 1016 | OvsIPv4TunnelKey *tunnelKey, |
1293a628 EE |
1017 | PNET_BUFFER_LIST nbl, |
1018 | PNET_BUFFER nb, | |
1019 | BOOLEAN isRecv, | |
1020 | POVS_PACKET_HDR_INFO hdrInfo) | |
1021 | { | |
1022 | #define VLAN_TAG_SIZE 4 | |
205b80eb | 1023 | UINT32 allocLen, dataLen, extraLen = 0; |
1293a628 EE |
1024 | POVS_PACKET_QUEUE_ELEM elem; |
1025 | UINT8 *src, *dst; | |
1026 | NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; | |
205b80eb AK |
1027 | PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanInfo = NULL; |
1028 | PVOID vlanTag; | |
1293a628 EE |
1029 | UINT32 pid; |
1030 | UINT32 nlMsgSize; | |
1031 | NL_BUFFER nlBuf; | |
befa5b43 | 1032 | PNL_MSG_HDR nlMsg; |
39ccaaf7 | 1033 | POVS_BUFFER_CONTEXT ctx; |
1293a628 | 1034 | |
1293a628 | 1035 | if (vport == NULL){ |
befa5b43 | 1036 | /* No vport is not fatal. */ |
1293a628 EE |
1037 | return NULL; |
1038 | } | |
1039 | ||
0e55eddc AS |
1040 | OvsGetPid(vport, nb, &pid); |
1041 | ||
1042 | if (!pid) { | |
1293a628 EE |
1043 | /* |
1044 | * There is no userspace queue created yet, so there is no point for | |
1045 | * creating a new packet to be queued. | |
1046 | */ | |
1047 | return NULL; | |
1048 | } | |
1049 | ||
1050 | csumInfo.Value = NET_BUFFER_LIST_INFO(nbl, TcpIpChecksumNetBufferListInfo); | |
1051 | ||
1052 | if (isRecv && (csumInfo.Receive.TcpChecksumFailed || | |
c3e85147 PB |
1053 | (csumInfo.Receive.UdpChecksumFailed && !hdrInfo->udpCsumZero) || |
1054 | csumInfo.Receive.IpChecksumFailed)) { | |
1293a628 EE |
1055 | OVS_LOG_INFO("Packet dropped due to checksum failure."); |
1056 | ovsUserStats.dropDuetoChecksum++; | |
1057 | return NULL; | |
1058 | } | |
1059 | ||
205b80eb AK |
1060 | vlanTag = NET_BUFFER_LIST_INFO(nbl, Ieee8021QNetBufferListInfo); |
1061 | if (vlanTag) { | |
1062 | vlanInfo = (PNDIS_NET_BUFFER_LIST_8021Q_INFO)(PVOID *)&vlanTag; | |
1063 | if (vlanInfo->Value) { | |
1064 | extraLen = VLAN_TAG_SIZE; | |
1065 | } | |
1066 | } | |
1293a628 EE |
1067 | |
1068 | dataLen = NET_BUFFER_DATA_LENGTH(nb); | |
1069 | ||
1070 | if (NlAttrSize(dataLen) > MAXUINT16) { | |
1071 | return NULL; | |
1072 | } | |
1073 | ||
1074 | nlMsgSize = OvsGetUpcallMsgSize(userData, userDataLen, tunnelKey, | |
1075 | dataLen + extraLen); | |
1076 | ||
1077 | allocLen = sizeof (OVS_PACKET_QUEUE_ELEM) + nlMsgSize; | |
d016f841 SV |
1078 | elem = (POVS_PACKET_QUEUE_ELEM)OvsAllocateMemoryWithTag(allocLen, |
1079 | OVS_USER_POOL_TAG); | |
1293a628 EE |
1080 | if (elem == NULL) { |
1081 | ovsUserStats.dropDuetoResource++; | |
1082 | return NULL; | |
1083 | } | |
1084 | elem->hdrInfo.value = hdrInfo->value; | |
4a3c9b70 | 1085 | elem->upcallPid = pid; |
1293a628 EE |
1086 | elem->packet.totalLen = nlMsgSize; |
1087 | /* XXX remove queueid */ | |
1088 | elem->packet.queue = 0; | |
1089 | /* XXX no need as the length is already in the NL attrib */ | |
1090 | elem->packet.userDataLen = userDataLen; | |
4c470e88 | 1091 | elem->packet.inPort = vport->portNo; |
1293a628 EE |
1092 | elem->packet.cmd = cmd; |
1093 | if (cmd == (UINT32)OVS_PACKET_CMD_MISS) { | |
1094 | ovsUserStats.miss++; | |
1095 | } else if (cmd == (UINT32)OVS_PACKET_CMD_ACTION) { | |
1096 | ovsUserStats.action++; | |
1097 | } else { | |
1098 | ASSERT(FALSE); | |
1099 | goto fail; | |
1100 | } | |
1101 | /* XXX Should we have both packetLen and TotalLen*/ | |
1102 | elem->packet.packetLen = dataLen + extraLen; | |
1103 | ||
1104 | NlBufInit(&nlBuf, (PCHAR)elem->packet.data, nlMsgSize); | |
1105 | ||
1106 | /* | |
1107 | * Initialize the OVS header | |
1108 | * Since we are pre allocating memory for the NL buffer | |
1109 | * the attribute settings should not fail | |
1110 | */ | |
1ad44ad4 | 1111 | if (!NlFillOvsMsg(&nlBuf, OVS_WIN_NL_PACKET_FAMILY_ID, 0, |
1293a628 | 1112 | 0, pid, (UINT8)cmd, OVS_PACKET_VERSION, |
1ad44ad4 | 1113 | gOvsSwitchContext->dpNo)) { |
1293a628 EE |
1114 | goto fail; |
1115 | } | |
1116 | ||
1117 | if (MapFlowKeyToNlKey(&nlBuf, key, OVS_PACKET_ATTR_KEY, | |
1118 | OVS_KEY_ATTR_TUNNEL) != STATUS_SUCCESS) { | |
1119 | goto fail; | |
1120 | } | |
1121 | ||
39ccaaf7 AK |
1122 | /* Set MRU attribute */ |
1123 | ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); | |
1124 | if (ctx->mru != 0) { | |
1125 | if (!NlMsgPutTailU16(&nlBuf, OVS_PACKET_ATTR_MRU, (UINT16)ctx->mru)) { | |
1126 | goto fail; | |
1127 | } | |
1128 | } | |
1129 | ||
e14fbdbb AH |
1130 | /* Set OVS_PACKET_ATTR_EGRESS_TUN_KEY attribute */ |
1131 | if (tunnelKey) { | |
1132 | if (MapFlowTunKeyToNlKey(&nlBuf, tunnelKey, | |
1133 | OVS_PACKET_ATTR_EGRESS_TUN_KEY) != STATUS_SUCCESS) { | |
1134 | goto fail; | |
1135 | } | |
1136 | } | |
1293a628 EE |
1137 | if (userData){ |
1138 | if (!NlMsgPutTailUnspec(&nlBuf, OVS_PACKET_ATTR_USERDATA, | |
1139 | userData, (UINT16)userDataLen)) { | |
1140 | goto fail; | |
1141 | } | |
1142 | } | |
1143 | ||
1144 | /* | |
1145 | * Make space for the payload to be copied and set the attribute | |
1146 | * XXX Uninit set initilizes the buffer with xero, we don't actually need | |
1147 | * that the payload to be initailized | |
1148 | */ | |
1149 | dst = (UINT8 *)NlMsgPutTailUnspecUninit(&nlBuf, OVS_PACKET_ATTR_PACKET, | |
1150 | (UINT16)(dataLen + extraLen)); | |
1151 | if (!dst) { | |
1152 | goto fail; | |
1153 | } | |
1154 | ||
1155 | /* Store the payload for csum calculation when packet is read */ | |
1156 | elem->packet.payload = dst; | |
1157 | dst += extraLen; | |
1158 | ||
1159 | src = NdisGetDataBuffer(nb, dataLen, dst, 1, 0); | |
1160 | if (src == NULL) { | |
1161 | ovsUserStats.dropDuetoResource++; | |
1162 | goto fail; | |
1163 | } else if (src != dst) { | |
1164 | /* Copy the data from the NDIS buffer to dst. */ | |
1165 | RtlCopyMemory(dst, src, dataLen); | |
1166 | } | |
1167 | ||
1168 | /* Set csum if was offloaded */ | |
1169 | OvsCompletePacketHeader(dst, isRecv, csumInfo, hdrInfo, &elem->hdrInfo); | |
1170 | ||
1171 | /* | |
1172 | * Finally insert VLAN tag | |
1173 | */ | |
1174 | if (extraLen) { | |
1175 | dst = elem->packet.payload; | |
1176 | src = dst + extraLen; | |
1177 | ((UINT32 *)dst)[0] = ((UINT32 *)src)[0]; | |
1178 | ((UINT32 *)dst)[1] = ((UINT32 *)src)[1]; | |
1179 | ((UINT32 *)dst)[2] = ((UINT32 *)src)[2]; | |
1180 | dst += 12; | |
1181 | ((UINT16 *)dst)[0] = htons(0x8100); | |
205b80eb AK |
1182 | ((UINT16 *)dst)[1] = htons(vlanInfo->TagHeader.VlanId | |
1183 | (vlanInfo->TagHeader.CanonicalFormatId << 12) | | |
1184 | (vlanInfo->TagHeader.UserPriority << 13)); | |
1293a628 EE |
1185 | elem->hdrInfo.l3Offset += VLAN_TAG_SIZE; |
1186 | elem->hdrInfo.l4Offset += VLAN_TAG_SIZE; | |
1187 | ovsUserStats.vlanInsert++; | |
1188 | } | |
befa5b43 NR |
1189 | |
1190 | nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuf, 0, 0); | |
1191 | nlMsg->nlmsgLen = NlBufSize(&nlBuf); | |
1192 | /* 'totalLen' should be size of valid data. */ | |
1193 | elem->packet.totalLen = nlMsg->nlmsgLen; | |
1194 | ||
1293a628 EE |
1195 | return elem; |
1196 | fail: | |
d016f841 | 1197 | OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG); |
1293a628 EE |
1198 | return NULL; |
1199 | } | |
6375650a NR |
1200 | |
1201 | /* | |
1202 | * -------------------------------------------------------------------------- | |
1203 | * Handler for the subscription for a packet queue | |
1204 | * -------------------------------------------------------------------------- | |
1205 | */ | |
1206 | NTSTATUS | |
1207 | OvsSubscribePacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, | |
ad61c98d | 1208 | UINT32 *replyLen) |
6375650a NR |
1209 | { |
1210 | NDIS_STATUS status; | |
1211 | BOOLEAN rc; | |
1212 | UINT8 join; | |
1213 | UINT32 pid; | |
1214 | const NL_POLICY policy[] = { | |
1215 | [OVS_NL_ATTR_PACKET_PID] = {.type = NL_A_U32 }, | |
1216 | [OVS_NL_ATTR_PACKET_SUBSCRIBE] = {.type = NL_A_U8 } | |
1217 | }; | |
1218 | PNL_ATTR attrs[ARRAY_SIZE(policy)]; | |
1219 | ||
1220 | UNREFERENCED_PARAMETER(replyLen); | |
1221 | ||
1222 | POVS_OPEN_INSTANCE instance = | |
1223 | (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; | |
1224 | POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; | |
1225 | ||
1226 | rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn), | |
1227 | NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, ARRAY_SIZE(policy), | |
1228 | attrs, ARRAY_SIZE(attrs)); | |
1229 | if (!rc) { | |
1230 | status = STATUS_INVALID_PARAMETER; | |
1231 | goto done; | |
1232 | } | |
1233 | ||
ad61c98d | 1234 | join = NlAttrGetU8(attrs[OVS_NL_ATTR_PACKET_SUBSCRIBE]); |
6375650a NR |
1235 | pid = NlAttrGetU32(attrs[OVS_NL_ATTR_PACKET_PID]); |
1236 | ||
1237 | /* The socket subscribed with must be the same socket we perform receive*/ | |
1238 | ASSERT(pid == instance->pid); | |
1239 | ||
1240 | status = OvsSubscribeDpIoctl(instance, pid, join); | |
1241 | ||
1242 | /* | |
1243 | * XXX Need to add this instance to a global data structure | |
1244 | * which hold all packet based instances. The data structure (hash) | |
1245 | * should be searched through the pid field of the instance for | |
1246 | * placing the missed packet into the correct queue | |
1247 | */ | |
1248 | done: | |
1249 | return status; | |
1250 | } | |
1251 | ||
1252 | /* | |
1253 | * -------------------------------------------------------------------------- | |
1254 | * Handler for queueing an IRP used for missed packet notification. The IRP is | |
1255 | * completed when a packet received and mismatched. STATUS_PENDING is returned | |
1256 | * on success. User mode keep a pending IRP at all times. | |
1257 | * -------------------------------------------------------------------------- | |
1258 | */ | |
1259 | NTSTATUS | |
1260 | OvsPendPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, | |
1261 | UINT32 *replyLen) | |
1262 | { | |
1263 | UNREFERENCED_PARAMETER(replyLen); | |
1264 | ||
1265 | POVS_OPEN_INSTANCE instance = | |
1266 | (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; | |
1267 | ||
1268 | /* | |
1269 | * XXX access to packet queue must be through acquiring a lock as user mode | |
1270 | * could unsubscribe and the instnace will be freed. | |
1271 | */ | |
1272 | return OvsWaitDpIoctl(usrParamsCtx->irp, instance->fileObject); | |
1273 | } | |
1274 | ||
1275 | /* | |
1276 | * -------------------------------------------------------------------------- | |
1277 | * Handler for reading missed pacckets from the driver event queue. This | |
1278 | * handler is executed when user modes issues a socket receive on a socket | |
1279 | * -------------------------------------------------------------------------- | |
1280 | */ | |
1281 | NTSTATUS | |
1282 | OvsReadPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, | |
1283 | UINT32 *replyLen) | |
1284 | { | |
1285 | #ifdef DBG | |
1286 | POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; | |
1287 | #endif | |
1288 | POVS_OPEN_INSTANCE instance = | |
1289 | (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; | |
1290 | NTSTATUS status; | |
1291 | ||
1292 | ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP); | |
1293 | ||
1294 | /* Should never read events with a dump socket */ | |
1295 | ASSERT(instance->dumpState.ovsMsg == NULL); | |
1296 | ||
1297 | /* Must have an packet queue */ | |
1298 | ASSERT(instance->packetQueue != NULL); | |
1299 | ||
1300 | /* Output buffer has been validated while validating read dev op. */ | |
1301 | ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut); | |
1302 | ||
1303 | /* Read a packet from the instance queue */ | |
1304 | status = OvsReadDpIoctl(instance->fileObject, usrParamsCtx->outputBuffer, | |
1305 | usrParamsCtx->outputLength, replyLen); | |
1306 | return status; | |
12e888ba | 1307 | } |