]>
Commit | Line | Data |
---|---|---|
0b5166dc AK |
1 | /* |
2 | * Copyright (c) 2017 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 "Conntrack.h" | |
18 | #include "Debug.h" | |
19 | #include "IpFragment.h" | |
20 | #include "Jhash.h" | |
21 | #include "Offload.h" | |
22 | #include "PacketParser.h" | |
23 | ||
24 | #ifdef OVS_DBG_MOD | |
25 | #undef OVS_DBG_MOD | |
26 | #endif | |
27 | #define OVS_DBG_MOD OVS_DBG_IPFRAG | |
b34cd611 AK |
28 | /* Based on MIN_FRAGMENT_SIZE.*/ |
29 | #define MAX_FRAGMENTS 164 | |
30 | #define MIN_FRAGMENT_SIZE 400 | |
31 | #define MAX_IPDATAGRAM_SIZE 65535 | |
0b5166dc AK |
32 | |
33 | /* Function declarations */ | |
17cf43bc | 34 | static KSTART_ROUTINE OvsIpFragmentEntryCleaner; |
0b5166dc AK |
35 | static VOID OvsIpFragmentEntryDelete(POVS_IPFRAG_ENTRY entry, BOOLEAN checkExpiry); |
36 | ||
37 | /* Global and static variables */ | |
38 | static OVS_IPFRAG_THREAD_CTX ipFragThreadCtx; | |
39 | static PNDIS_RW_LOCK_EX ovsIpFragmentHashLockObj; | |
40 | static UINT64 ipTotalEntries; | |
41 | static PLIST_ENTRY OvsIpFragTable; | |
42 | ||
43 | NDIS_STATUS | |
44 | OvsInitIpFragment(POVS_SWITCH_CONTEXT context) | |
45 | { | |
46 | ||
47 | NDIS_STATUS status; | |
48 | HANDLE threadHandle = NULL; | |
49 | ||
50 | /* Init the sync-lock */ | |
51 | ovsIpFragmentHashLockObj = NdisAllocateRWLock(context->NdisFilterHandle); | |
52 | if (ovsIpFragmentHashLockObj == NULL) { | |
53 | return STATUS_INSUFFICIENT_RESOURCES; | |
54 | } | |
55 | ||
56 | /* Init the Hash Buffer */ | |
57 | OvsIpFragTable = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY) | |
58 | * IP_FRAG_HASH_TABLE_SIZE, | |
59 | OVS_IPFRAG_POOL_TAG); | |
60 | if (OvsIpFragTable == NULL) { | |
61 | NdisFreeRWLock(ovsIpFragmentHashLockObj); | |
62 | ovsIpFragmentHashLockObj = NULL; | |
63 | return STATUS_INSUFFICIENT_RESOURCES; | |
64 | } | |
65 | ||
66 | for (int i = 0; i < IP_FRAG_HASH_TABLE_SIZE; i++) { | |
67 | InitializeListHead(&OvsIpFragTable[i]); | |
68 | } | |
69 | ||
70 | /* Init Cleaner Thread */ | |
71 | KeInitializeEvent(&ipFragThreadCtx.event, NotificationEvent, FALSE); | |
72 | status = PsCreateSystemThread(&threadHandle, SYNCHRONIZE, NULL, NULL, | |
73 | NULL, OvsIpFragmentEntryCleaner, | |
74 | &ipFragThreadCtx); | |
75 | ||
76 | if (status != STATUS_SUCCESS) { | |
77 | OvsFreeMemoryWithTag(OvsIpFragTable, OVS_IPFRAG_POOL_TAG); | |
78 | OvsIpFragTable = NULL; | |
79 | NdisFreeRWLock(ovsIpFragmentHashLockObj); | |
80 | ovsIpFragmentHashLockObj = NULL; | |
81 | return status; | |
82 | } | |
83 | ||
84 | ObReferenceObjectByHandle(threadHandle, SYNCHRONIZE, NULL, KernelMode, | |
85 | &ipFragThreadCtx.threadObject, NULL); | |
86 | ZwClose(threadHandle); | |
87 | threadHandle = NULL; | |
88 | return STATUS_SUCCESS; | |
89 | } | |
90 | ||
91 | static __inline UINT32 | |
92 | OvsGetIPFragmentHash(POVS_IPFRAG_KEY fragKey) | |
93 | { | |
94 | UINT32 arr[6]; | |
95 | arr[0] = (UINT32)fragKey->protocol; | |
96 | arr[1] = (UINT32)fragKey->id; | |
97 | arr[2] = (UINT32)fragKey->sAddr; | |
98 | arr[3] = (UINT32)fragKey->dAddr; | |
99 | arr[4] = (UINT32)((fragKey->tunnelId & 0xFFFFFFFF00000000LL) >> 32); | |
100 | arr[5] = (UINT32)(fragKey->tunnelId & 0xFFFFFFFFLL); | |
101 | return OvsJhashWords(arr, 6, OVS_HASH_BASIS); | |
102 | } | |
103 | ||
104 | static __inline POVS_IPFRAG_ENTRY | |
105 | OvsLookupIPFrag(POVS_IPFRAG_KEY fragKey, UINT32 hash) | |
106 | { | |
107 | POVS_IPFRAG_ENTRY entry; | |
108 | PLIST_ENTRY link; | |
109 | LOCK_STATE_EX lockState; | |
110 | ||
111 | NdisAcquireRWLockRead(ovsIpFragmentHashLockObj, &lockState, 0); | |
112 | LIST_FORALL(&OvsIpFragTable[hash & IP_FRAG_HASH_TABLE_MASK], link) { | |
113 | entry = CONTAINING_RECORD(link, OVS_IPFRAG_ENTRY, link); | |
114 | NdisAcquireSpinLock(&(entry->lockObj)); | |
115 | if (entry->fragKey.dAddr == fragKey->dAddr && | |
116 | entry->fragKey.sAddr == fragKey->sAddr && | |
117 | entry->fragKey.id == fragKey->id && | |
118 | entry->fragKey.protocol == fragKey->protocol && | |
119 | entry->fragKey.tunnelId == fragKey->tunnelId) { | |
120 | NdisReleaseSpinLock(&(entry->lockObj)); | |
121 | NdisReleaseRWLock(ovsIpFragmentHashLockObj, &lockState); | |
122 | return entry; | |
123 | } | |
124 | NdisReleaseSpinLock(&(entry->lockObj)); | |
125 | } | |
126 | NdisReleaseRWLock(ovsIpFragmentHashLockObj, &lockState); | |
127 | return NULL; | |
128 | } | |
129 | ||
130 | /* | |
131 | *---------------------------------------------------------------------------- | |
132 | * OvsIpv4Reassemble | |
133 | * Reassemble the ipv4 fragments and return newNbl on success. | |
134 | * Should be called after acquiring the lockObj for the entry. | |
135 | *---------------------------------------------------------------------------- | |
136 | */ | |
137 | NDIS_STATUS | |
138 | OvsIpv4Reassemble(POVS_SWITCH_CONTEXT switchContext, | |
139 | PNET_BUFFER_LIST *curNbl, | |
140 | OvsCompletionList *completionList, | |
141 | NDIS_SWITCH_PORT_ID sourcePort, | |
142 | POVS_IPFRAG_ENTRY entry, | |
143 | PNET_BUFFER_LIST *newNbl) | |
144 | { | |
145 | NDIS_STATUS status = NDIS_STATUS_SUCCESS; | |
146 | NDIS_STRING filterReason; | |
147 | POVS_BUFFER_CONTEXT ctx; | |
148 | PNET_BUFFER curNb; | |
149 | EthHdr *eth; | |
150 | IPHdr *ipHdr, *newIpHdr; | |
151 | CHAR *ethBuf[sizeof(EthHdr)]; | |
152 | CHAR *packetBuf; | |
b34cd611 | 153 | UINT16 ipHdrLen, packetHeader; |
0b5166dc | 154 | POVS_FRAGMENT_LIST head = NULL; |
b34cd611 | 155 | UINT32 packetLen; |
0b5166dc AK |
156 | |
157 | curNb = NET_BUFFER_LIST_FIRST_NB(*curNbl); | |
158 | ASSERT(NET_BUFFER_NEXT_NB(curNb) == NULL); | |
159 | ||
160 | eth = (EthHdr*)NdisGetDataBuffer(curNb, ETH_HEADER_LENGTH, | |
161 | (PVOID)ðBuf, 1, 0); | |
162 | if (eth == NULL) { | |
163 | return NDIS_STATUS_INVALID_PACKET; | |
164 | } | |
165 | ipHdr = (IPHdr *)((PCHAR)eth + ETH_HEADER_LENGTH); | |
166 | if (ipHdr == NULL) { | |
167 | return NDIS_STATUS_INVALID_PACKET; | |
168 | } | |
b34cd611 AK |
169 | ipHdrLen = ipHdr->ihl * 4; |
170 | if (ipHdrLen + entry->totalLen > MAX_IPDATAGRAM_SIZE) { | |
171 | return NDIS_STATUS_INVALID_LENGTH; | |
172 | } | |
0b5166dc AK |
173 | packetLen = ETH_HEADER_LENGTH + ipHdrLen + entry->totalLen; |
174 | packetBuf = (CHAR*)OvsAllocateMemoryWithTag(packetLen, | |
175 | OVS_IPFRAG_POOL_TAG); | |
176 | if (packetBuf == NULL) { | |
177 | OVS_LOG_ERROR("Insufficient resources, failed to allocate packetBuf"); | |
178 | return NDIS_STATUS_RESOURCES; | |
179 | } | |
180 | ||
181 | /* copy Ethernet header */ | |
182 | NdisMoveMemory(packetBuf, eth, ETH_HEADER_LENGTH); | |
183 | /* copy ipv4 header to packet buff */ | |
184 | NdisMoveMemory(packetBuf + ETH_HEADER_LENGTH, ipHdr, ipHdrLen); | |
185 | ||
186 | /* update new ip header */ | |
187 | newIpHdr = (IPHdr *)(packetBuf + ETH_HEADER_LENGTH); | |
188 | newIpHdr->frag_off = 0; | |
189 | newIpHdr->tot_len = htons(packetLen - ETH_HEADER_LENGTH); | |
190 | newIpHdr->check = 0; | |
191 | newIpHdr->check = IPChecksum((UINT8 *)packetBuf + ETH_HEADER_LENGTH, | |
192 | ipHdrLen, 0); | |
193 | packetHeader = ETH_HEADER_LENGTH + ipHdrLen; | |
194 | head = entry->head; | |
195 | while (head) { | |
b34cd611 AK |
196 | if ((UINT32)(packetHeader + head->offset) > packetLen) { |
197 | status = NDIS_STATUS_INVALID_DATA; | |
198 | goto cleanup; | |
199 | } | |
0b5166dc AK |
200 | NdisMoveMemory(packetBuf + packetHeader + head->offset, |
201 | head->pbuff, head->len); | |
202 | head = head->next; | |
203 | } | |
204 | /* Create new nbl from the flat buffer */ | |
205 | *newNbl = OvsAllocateNBLFromBuffer(switchContext, packetBuf, packetLen); | |
206 | if (*newNbl == NULL) { | |
207 | OVS_LOG_ERROR("Insufficient resources, failed to allocate newNbl"); | |
208 | status = NDIS_STATUS_RESOURCES; | |
bf7e459b | 209 | goto cleanup; |
0b5166dc AK |
210 | } |
211 | ||
0b5166dc AK |
212 | /* Complete the fragment NBL */ |
213 | ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(*curNbl); | |
214 | if (ctx->flags & OVS_BUFFER_NEED_COMPLETE) { | |
215 | RtlInitUnicodeString(&filterReason, L"Complete last fragment"); | |
216 | OvsAddPktCompletionList(completionList, TRUE, sourcePort, *curNbl, 1, | |
217 | &filterReason); | |
218 | } else { | |
219 | OvsCompleteNBL(switchContext, *curNbl, TRUE); | |
220 | } | |
221 | /* Store mru in the ovs buffer context. */ | |
39ccaaf7 AK |
222 | ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(*newNbl); |
223 | ctx->mru = entry->mru; | |
0b5166dc | 224 | *curNbl = *newNbl; |
b34cd611 AK |
225 | cleanup: |
226 | OvsFreeMemoryWithTag(packetBuf, OVS_IPFRAG_POOL_TAG); | |
227 | entry->markedForDelete = TRUE; | |
0b5166dc AK |
228 | return status; |
229 | } | |
230 | /* | |
231 | *---------------------------------------------------------------------------- | |
232 | * OvsProcessIpv4Fragment | |
233 | * Reassemble the fragments once all the fragments are recieved and | |
234 | * return NDIS_STATUS_PENDING for the pending fragments | |
235 | * XXX - Instead of copying NBls, Keep the NBLs in limbo state. | |
236 | *---------------------------------------------------------------------------- | |
237 | */ | |
238 | NDIS_STATUS | |
239 | OvsProcessIpv4Fragment(POVS_SWITCH_CONTEXT switchContext, | |
240 | PNET_BUFFER_LIST *curNbl, | |
241 | OvsCompletionList *completionList, | |
242 | NDIS_SWITCH_PORT_ID sourcePort, | |
243 | ovs_be64 tunnelId, | |
244 | PNET_BUFFER_LIST *newNbl) | |
245 | { | |
246 | NDIS_STATUS status = NDIS_STATUS_PENDING; | |
247 | PNET_BUFFER curNb; | |
248 | CHAR *ethBuf[sizeof(EthHdr)]; | |
249 | UINT16 offset, flags; | |
250 | UINT16 payloadLen, ipHdrLen; | |
251 | UINT32 hash; | |
252 | UINT64 currentTime; | |
253 | EthHdr *eth; | |
254 | IPHdr *ipHdr; | |
255 | OVS_IPFRAG_KEY fragKey; | |
256 | POVS_IPFRAG_ENTRY entry; | |
257 | POVS_FRAGMENT_LIST fragStorage; | |
258 | LOCK_STATE_EX htLockState; | |
259 | ||
260 | curNb = NET_BUFFER_LIST_FIRST_NB(*curNbl); | |
261 | ASSERT(NET_BUFFER_NEXT_NB(curNb) == NULL); | |
262 | ||
263 | eth = (EthHdr*)NdisGetDataBuffer(curNb, ETH_HEADER_LENGTH, | |
264 | (PVOID)ðBuf, 1, 0); | |
265 | if (eth == NULL) { | |
266 | return NDIS_STATUS_INVALID_PACKET; | |
267 | } | |
268 | ||
269 | ipHdr = (IPHdr *)((PCHAR)eth + ETH_HEADER_LENGTH); | |
270 | if (ipHdr == NULL) { | |
271 | return NDIS_STATUS_INVALID_PACKET; | |
272 | } | |
b34cd611 | 273 | ipHdrLen = ipHdr->ihl * 4; |
0b5166dc AK |
274 | payloadLen = ntohs(ipHdr->tot_len) - ipHdrLen; |
275 | offset = ntohs(ipHdr->frag_off) & IP_OFFSET; | |
276 | offset <<= 3; | |
277 | flags = ntohs(ipHdr->frag_off) & IP_MF; | |
b34cd611 AK |
278 | /* Only the last fragment can be of smaller size.*/ |
279 | if (flags && ntohs(ipHdr->tot_len) < MIN_FRAGMENT_SIZE) { | |
280 | return NDIS_STATUS_INVALID_LENGTH; | |
281 | } | |
0b5166dc AK |
282 | /*Copy fragment specific fields. */ |
283 | fragKey.protocol = ipHdr->protocol; | |
284 | fragKey.id = ipHdr->id; | |
285 | fragKey.sAddr = ipHdr->saddr; | |
286 | fragKey.dAddr = ipHdr->daddr; | |
287 | fragKey.tunnelId = tunnelId; | |
288 | /* Padding. */ | |
289 | NdisZeroMemory(&fragKey.pad_1, 3); | |
290 | fragKey.pad_2 = 0; | |
291 | ||
292 | fragStorage = (POVS_FRAGMENT_LIST ) | |
293 | OvsAllocateMemoryWithTag(sizeof(OVS_FRAGMENT_LIST), | |
294 | OVS_IPFRAG_POOL_TAG); | |
295 | if (fragStorage == NULL) { | |
296 | OVS_LOG_ERROR("Insufficient resources, fail to allocate fragStorage"); | |
297 | return NDIS_STATUS_RESOURCES; | |
298 | } | |
299 | ||
300 | fragStorage->pbuff = (CHAR *)OvsAllocateMemoryWithTag(payloadLen, | |
301 | OVS_IPFRAG_POOL_TAG); | |
302 | if (fragStorage->pbuff == NULL) { | |
303 | OVS_LOG_ERROR("Insufficient resources, fail to allocate pbuff"); | |
304 | OvsFreeMemoryWithTag(fragStorage, OVS_IPFRAG_POOL_TAG); | |
305 | return NDIS_STATUS_RESOURCES; | |
306 | } | |
307 | ||
308 | /* Copy payload from nbl to fragment storage. */ | |
309 | if (OvsGetPacketBytes(*curNbl, payloadLen, ETH_HEADER_LENGTH + ipHdrLen, | |
310 | fragStorage->pbuff) == NULL) { | |
311 | status = NDIS_STATUS_RESOURCES; | |
312 | goto payload_copy_error; | |
313 | } | |
314 | fragStorage->len = payloadLen; | |
315 | fragStorage->offset = offset; | |
316 | fragStorage->next = NULL; | |
317 | hash = OvsGetIPFragmentHash(&fragKey); | |
318 | entry = OvsLookupIPFrag(&fragKey, hash); | |
319 | if (entry == NULL) { | |
320 | entry = (POVS_IPFRAG_ENTRY) | |
321 | OvsAllocateMemoryWithTag(sizeof(OVS_IPFRAG_ENTRY), | |
322 | OVS_IPFRAG_POOL_TAG); | |
323 | if (entry == NULL) { | |
324 | status = NDIS_STATUS_RESOURCES; | |
325 | goto payload_copy_error; | |
326 | } | |
327 | /* Copy the fragmeny key. */ | |
328 | NdisZeroMemory(entry, sizeof(OVS_IPFRAG_ENTRY)); | |
329 | NdisMoveMemory(&(entry->fragKey), &fragKey, | |
330 | sizeof(OVS_IPFRAG_KEY)); | |
331 | /* Init MRU. */ | |
332 | entry->mru = ETH_HEADER_LENGTH + ipHdrLen + payloadLen; | |
333 | entry->recvdLen += fragStorage->len; | |
334 | entry->head = entry->tail = fragStorage; | |
b34cd611 | 335 | entry->numFragments = 1; |
0b5166dc AK |
336 | if (!flags) { |
337 | entry->totalLen = offset + payloadLen; | |
338 | } | |
339 | NdisGetCurrentSystemTime((LARGE_INTEGER *)¤tTime); | |
340 | entry->expiration = currentTime + IPFRAG_ENTRY_TIMEOUT; | |
341 | ||
342 | /* Init the sync-lock. */ | |
343 | NdisAllocateSpinLock(&(entry->lockObj)); | |
344 | NdisAcquireRWLockWrite(ovsIpFragmentHashLockObj, &htLockState, 0); | |
345 | InsertHeadList(&OvsIpFragTable[hash & IP_FRAG_HASH_TABLE_MASK], | |
346 | &entry->link); | |
347 | ||
348 | ipTotalEntries++; | |
349 | NdisReleaseRWLock(ovsIpFragmentHashLockObj, &htLockState); | |
350 | return NDIS_STATUS_PENDING; | |
351 | } else { | |
352 | /* Acquire the entry lock. */ | |
353 | NdisAcquireSpinLock(&(entry->lockObj)); | |
354 | NdisGetCurrentSystemTime((LARGE_INTEGER *)¤tTime); | |
b34cd611 AK |
355 | if (currentTime > entry->expiration || entry->numFragments == MAX_FRAGMENTS) { |
356 | /* Mark the entry for delete. */ | |
357 | entry->markedForDelete = TRUE; | |
0b5166dc AK |
358 | goto fragment_error; |
359 | } | |
360 | POVS_FRAGMENT_LIST next = entry->head; | |
361 | POVS_FRAGMENT_LIST prev = entry->tail; | |
42409aae | 362 | if (prev != NULL && prev->offset < offset) { |
0b5166dc AK |
363 | next = NULL; |
364 | goto found; | |
365 | } | |
366 | prev = NULL; | |
367 | for (next = entry->head; next != NULL; next = next->next) { | |
368 | if (next->offset > fragStorage->offset) { | |
369 | break; | |
370 | } | |
371 | prev = next; | |
372 | } | |
373 | found: | |
374 | /*Check for overlap. */ | |
375 | if (prev) { | |
376 | /* i bytes overlap. */ | |
377 | int i = (prev->offset + prev->len) - fragStorage->offset; | |
378 | if (i > 0) { | |
379 | goto fragment_error; | |
380 | } | |
381 | } | |
382 | if (next) { | |
383 | /* i bytes overlap. */ | |
384 | int i = (fragStorage->offset + fragStorage->len) - next->offset; | |
385 | if (i > 0) { | |
386 | goto fragment_error; | |
387 | } | |
388 | } | |
389 | ||
390 | if (entry->recvdLen + fragStorage->len > entry->recvdLen) { | |
391 | entry->recvdLen += fragStorage->len; | |
392 | } else { | |
393 | /* Overflow, ignore the fragment.*/ | |
394 | goto fragment_error; | |
395 | } | |
396 | ||
397 | /*Insert. */ | |
398 | if (prev) { | |
399 | prev->next = fragStorage; | |
400 | fragStorage->next = next; | |
401 | } else { | |
402 | fragStorage->next = next; | |
403 | entry->head = fragStorage; | |
404 | } | |
405 | if (!next) { | |
406 | entry->tail = fragStorage; | |
407 | } | |
408 | ||
409 | /*Update Maximum recieved Unit */ | |
410 | entry->mru = entry->mru > (ETH_HEADER_LENGTH + ipHdrLen + payloadLen) ? | |
411 | entry->mru : (ETH_HEADER_LENGTH + ipHdrLen + payloadLen); | |
b34cd611 | 412 | entry->numFragments++; |
0b5166dc AK |
413 | if (!flags) { |
414 | entry->totalLen = offset + payloadLen; | |
415 | } | |
416 | if (entry->recvdLen == entry->totalLen) { | |
417 | status = OvsIpv4Reassemble(switchContext, curNbl, completionList, | |
418 | sourcePort, entry, newNbl); | |
419 | } | |
420 | NdisReleaseSpinLock(&(entry->lockObj)); | |
421 | return status; | |
422 | } | |
423 | fragment_error: | |
b34cd611 | 424 | status = NDIS_STATUS_INVALID_PACKET; |
0b5166dc AK |
425 | /* Release the entry lock. */ |
426 | NdisReleaseSpinLock(&(entry->lockObj)); | |
427 | payload_copy_error: | |
428 | OvsFreeMemoryWithTag(fragStorage->pbuff, OVS_IPFRAG_POOL_TAG); | |
429 | OvsFreeMemoryWithTag(fragStorage, OVS_IPFRAG_POOL_TAG); | |
430 | return status; | |
431 | } | |
432 | ||
433 | ||
434 | /* | |
435 | *---------------------------------------------------------------------------- | |
436 | * OvsIpFragmentEntryCleaner | |
437 | * Runs periodically and cleans up the Ip Fragment table | |
438 | * Interval is selected as twice the entry timeout | |
439 | *---------------------------------------------------------------------------- | |
440 | */ | |
441 | static VOID | |
442 | OvsIpFragmentEntryCleaner(PVOID data) | |
443 | { | |
444 | ||
445 | POVS_IPFRAG_THREAD_CTX context = (POVS_IPFRAG_THREAD_CTX)data; | |
446 | PLIST_ENTRY link, next; | |
447 | POVS_IPFRAG_ENTRY entry; | |
880b52e6 | 448 | LOCK_STATE_EX lockState; |
0b5166dc AK |
449 | BOOLEAN success = TRUE; |
450 | ||
451 | while (success) { | |
880b52e6 SR |
452 | if (ovsIpFragmentHashLockObj == NULL) { |
453 | /* Lock has been freed by 'OvsCleanupIpFragment()' */ | |
454 | break; | |
455 | } | |
0b5166dc AK |
456 | NdisAcquireRWLockWrite(ovsIpFragmentHashLockObj, &lockState, 0); |
457 | if (context->exit) { | |
458 | NdisReleaseRWLock(ovsIpFragmentHashLockObj, &lockState); | |
459 | break; | |
460 | } | |
461 | ||
462 | /* Set the timeout for the thread and cleanup. */ | |
463 | UINT64 currentTime, threadSleepTimeout; | |
464 | NdisGetCurrentSystemTime((LARGE_INTEGER *)¤tTime); | |
465 | threadSleepTimeout = currentTime + IPFRAG_CLEANUP_INTERVAL; | |
466 | for (int i = 0; i < IP_FRAG_HASH_TABLE_SIZE && ipTotalEntries; i++) { | |
467 | LIST_FORALL_SAFE(&OvsIpFragTable[i], link, next) { | |
468 | entry = CONTAINING_RECORD(link, OVS_IPFRAG_ENTRY, link); | |
469 | OvsIpFragmentEntryDelete(entry, TRUE); | |
470 | } | |
471 | } | |
472 | ||
473 | NdisReleaseRWLock(ovsIpFragmentHashLockObj, &lockState); | |
474 | KeWaitForSingleObject(&context->event, Executive, KernelMode, | |
475 | FALSE, (LARGE_INTEGER *)&threadSleepTimeout); | |
476 | } | |
477 | ||
478 | PsTerminateSystemThread(STATUS_SUCCESS); | |
479 | } | |
480 | ||
481 | static VOID | |
482 | OvsIpFragmentEntryDelete(POVS_IPFRAG_ENTRY entry, BOOLEAN checkExpiry) | |
483 | { | |
484 | NdisAcquireSpinLock(&(entry->lockObj)); | |
b34cd611 | 485 | if (!entry->markedForDelete && checkExpiry) { |
0b5166dc AK |
486 | UINT64 currentTime; |
487 | NdisGetCurrentSystemTime((LARGE_INTEGER *)¤tTime); | |
488 | if (entry->expiration > currentTime) { | |
489 | NdisReleaseSpinLock(&(entry->lockObj)); | |
490 | return; | |
491 | } | |
492 | } | |
493 | ||
494 | POVS_FRAGMENT_LIST head = entry->head; | |
495 | POVS_FRAGMENT_LIST temp = NULL; | |
496 | while (head) { | |
497 | temp = head; | |
498 | head = head->next; | |
499 | OvsFreeMemoryWithTag(temp->pbuff, OVS_IPFRAG_POOL_TAG); | |
500 | OvsFreeMemoryWithTag(temp, OVS_IPFRAG_POOL_TAG); | |
501 | } | |
502 | RemoveEntryList(&entry->link); | |
503 | ipTotalEntries--; | |
504 | NdisReleaseSpinLock(&(entry->lockObj)); | |
505 | NdisFreeSpinLock(&(entry->lockObj)); | |
506 | OvsFreeMemoryWithTag(entry, OVS_IPFRAG_POOL_TAG); | |
507 | } | |
508 | ||
509 | VOID | |
510 | OvsCleanupIpFragment(VOID) | |
511 | { | |
512 | PLIST_ENTRY link, next; | |
513 | POVS_IPFRAG_ENTRY entry; | |
514 | LOCK_STATE_EX lockState; | |
515 | ||
516 | ipFragThreadCtx.exit = 1; | |
517 | KeSetEvent(&ipFragThreadCtx.event, 0, FALSE); | |
518 | KeWaitForSingleObject(ipFragThreadCtx.threadObject, Executive, | |
519 | KernelMode, FALSE, NULL); | |
520 | ObDereferenceObject(ipFragThreadCtx.threadObject); | |
521 | NdisAcquireRWLockWrite(ovsIpFragmentHashLockObj, &lockState, 0); | |
522 | if (OvsIpFragTable) { | |
523 | for (int i = 0; i < IP_FRAG_HASH_TABLE_SIZE && ipTotalEntries; i++) { | |
524 | LIST_FORALL_SAFE(&OvsIpFragTable[i], link, next) { | |
525 | entry = CONTAINING_RECORD(link, OVS_IPFRAG_ENTRY, link); | |
526 | OvsIpFragmentEntryDelete(entry, FALSE); | |
527 | } | |
528 | } | |
529 | OvsFreeMemoryWithTag(OvsIpFragTable, OVS_IPFRAG_POOL_TAG); | |
530 | OvsIpFragTable = NULL; | |
531 | } | |
532 | NdisReleaseRWLock(ovsIpFragmentHashLockObj, &lockState); | |
533 | NdisFreeRWLock(ovsIpFragmentHashLockObj); | |
534 | ovsIpFragmentHashLockObj = NULL; | |
535 | } |