]> git.proxmox.com Git - mirror_ovs.git/blob - datapath-windows/ovsext/Conntrack.c
datapath-windows: Add a global level RW lock for NAT
[mirror_ovs.git] / datapath-windows / ovsext / Conntrack.c
1 /*
2 * Copyright (c) 2015, 2016 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 "IpFragment.h"
19 #include "Jhash.h"
20 #include "PacketParser.h"
21 #include "Event.h"
22 #include "Conntrack-nat.h"
23
24 #pragma warning(push)
25 #pragma warning(disable:4311)
26
27 #define WINDOWS_TICK 10000000
28 #define SEC_TO_UNIX_EPOCH 11644473600LL
29 #define SEC_TO_NANOSEC 1000000000LL
30
31 KSTART_ROUTINE OvsConntrackEntryCleaner;
32 static PLIST_ENTRY ovsConntrackTable;
33 static OVS_CT_THREAD_CTX ctThreadCtx;
34 static PNDIS_RW_LOCK_EX ovsConntrackLockObj;
35 static PNDIS_RW_LOCK_EX ovsCtNatLockObj;
36 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
37 static LONG ctTotalEntries;
38
39 static __inline OvsCtFlush(UINT16 zone, struct ovs_key_ct_tuple_ipv4 *tuple);
40 static __inline NDIS_STATUS
41 MapNlToCtTuple(POVS_MESSAGE msgIn, PNL_ATTR attr,
42 struct ovs_key_ct_tuple_ipv4 *ct_tuple);
43 /*
44 *----------------------------------------------------------------------------
45 * OvsInitConntrack
46 * Initialize the components used by Connection Tracking
47 *----------------------------------------------------------------------------
48 */
49 NTSTATUS
50 OvsInitConntrack(POVS_SWITCH_CONTEXT context)
51 {
52 NTSTATUS status;
53 HANDLE threadHandle = NULL;
54 ctTotalEntries = 0;
55
56 /* Init the sync-lock */
57 ovsConntrackLockObj = NdisAllocateRWLock(context->NdisFilterHandle);
58 if (ovsConntrackLockObj == NULL) {
59 return STATUS_INSUFFICIENT_RESOURCES;
60 }
61
62 ovsCtNatLockObj = NdisAllocateRWLock(context->NdisFilterHandle);
63 if (ovsCtNatLockObj == NULL) {
64 NdisFreeRWLock(ovsConntrackLockObj);
65 ovsConntrackLockObj = NULL;
66 return STATUS_INSUFFICIENT_RESOURCES;
67 }
68
69 /* Init the Hash Buffer */
70 ovsConntrackTable = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY)
71 * CT_HASH_TABLE_SIZE,
72 OVS_CT_POOL_TAG);
73 if (ovsConntrackTable == NULL) {
74 NdisFreeRWLock(ovsConntrackLockObj);
75 ovsConntrackLockObj = NULL;
76 NdisFreeRWLock(ovsCtNatLockObj);
77 ovsCtNatLockObj = NULL;
78 return STATUS_INSUFFICIENT_RESOURCES;
79 }
80
81 for (int i = 0; i < CT_HASH_TABLE_SIZE; i++) {
82 InitializeListHead(&ovsConntrackTable[i]);
83 }
84
85 /* Init CT Cleaner Thread */
86 KeInitializeEvent(&ctThreadCtx.event, NotificationEvent, FALSE);
87 status = PsCreateSystemThread(&threadHandle, SYNCHRONIZE, NULL, NULL,
88 NULL, OvsConntrackEntryCleaner,
89 &ctThreadCtx);
90
91 if (status != STATUS_SUCCESS) {
92 NdisFreeRWLock(ovsConntrackLockObj);
93 ovsConntrackLockObj = NULL;
94
95 NdisFreeRWLock(ovsCtNatLockObj);
96 ovsCtNatLockObj = NULL;
97
98 OvsFreeMemoryWithTag(ovsConntrackTable, OVS_CT_POOL_TAG);
99 ovsConntrackTable = NULL;
100
101 return status;
102 }
103
104 ObReferenceObjectByHandle(threadHandle, SYNCHRONIZE, NULL, KernelMode,
105 &ctThreadCtx.threadObject, NULL);
106 ZwClose(threadHandle);
107 threadHandle = NULL;
108
109 status = OvsNatInit();
110
111 if (status != STATUS_SUCCESS) {
112 OvsCleanupConntrack();
113 return status;
114 }
115 return STATUS_SUCCESS;
116 }
117
118 /*
119 *----------------------------------------------------------------------------
120 * OvsCleanupConntrack
121 * Cleanup memory and thread that were spawned for Connection tracking
122 *----------------------------------------------------------------------------
123 */
124 VOID
125 OvsCleanupConntrack(VOID)
126 {
127 LOCK_STATE_EX lockState, lockStateNat;
128 NdisAcquireRWLockWrite(ovsConntrackLockObj, &lockState, 0);
129 ctThreadCtx.exit = 1;
130 KeSetEvent(&ctThreadCtx.event, 0, FALSE);
131 NdisReleaseRWLock(ovsConntrackLockObj, &lockState);
132
133 KeWaitForSingleObject(ctThreadCtx.threadObject, Executive,
134 KernelMode, FALSE, NULL);
135 ObDereferenceObject(ctThreadCtx.threadObject);
136
137 /* Force flush all entries before removing */
138 OvsCtFlush(0, NULL);
139
140 if (ovsConntrackTable) {
141 OvsFreeMemoryWithTag(ovsConntrackTable, OVS_CT_POOL_TAG);
142 ovsConntrackTable = NULL;
143 }
144
145 NdisFreeRWLock(ovsConntrackLockObj);
146 ovsConntrackLockObj = NULL;
147 NdisAcquireRWLockWrite(ovsCtNatLockObj, &lockStateNat, 0);
148 OvsNatCleanup();
149 NdisReleaseRWLock(ovsCtNatLockObj, &lockStateNat);
150 NdisFreeRWLock(ovsCtNatLockObj);
151 ovsCtNatLockObj = NULL;
152 }
153
154 static __inline VOID
155 OvsCtKeyReverse(OVS_CT_KEY *key)
156 {
157 struct ct_endpoint tmp;
158 tmp = key->src;
159 key->src = key->dst;
160 key->dst = tmp;
161 }
162
163 static __inline VOID
164 OvsCtUpdateFlowKey(struct OvsFlowKey *key,
165 UINT32 state,
166 UINT16 zone,
167 UINT32 mark,
168 struct ovs_key_ct_labels *labels)
169 {
170 key->ct.state = state | OVS_CS_F_TRACKED;
171 key->ct.zone = zone;
172 key->ct.mark = mark;
173 if (labels) {
174 NdisMoveMemory(&key->ct.labels, labels,
175 sizeof(struct ovs_key_ct_labels));
176 } else {
177 memset(&key->ct.labels, 0,
178 sizeof(struct ovs_key_ct_labels));
179 }
180 }
181
182 static __inline VOID
183 OvsPostCtEventEntry(POVS_CT_ENTRY entry, UINT8 type)
184 {
185 OVS_CT_EVENT_ENTRY ctEventEntry = {0};
186 NdisMoveMemory(&ctEventEntry.entry, entry, sizeof(OVS_CT_ENTRY));
187 ctEventEntry.type = type;
188 OvsPostCtEvent(&ctEventEntry);
189 }
190
191 static __inline VOID
192 OvsCtIncrementCounters(POVS_CT_ENTRY entry, BOOLEAN reply, PNET_BUFFER_LIST nbl)
193 {
194 if (reply) {
195 entry->rev_key.byteCount+= OvsPacketLenNBL(nbl);
196 entry->rev_key.packetCount++;
197 } else {
198 entry->key.byteCount += OvsPacketLenNBL(nbl);
199 entry->key.packetCount++;
200 }
201 }
202
203 static __inline BOOLEAN
204 OvsCtAddEntry(POVS_CT_ENTRY entry, OvsConntrackKeyLookupCtx *ctx,
205 PNAT_ACTION_INFO natInfo, UINT64 now)
206 {
207 NdisMoveMemory(&entry->key, &ctx->key, sizeof(OVS_CT_KEY));
208 NdisMoveMemory(&entry->rev_key, &ctx->key, sizeof(OVS_CT_KEY));
209 OvsCtKeyReverse(&entry->rev_key);
210
211 /* NatInfo is always initialized to be disabled, so that if NAT action
212 * fails, we will not end up deleting an non-existent NAT entry.
213 */
214 if (natInfo == NULL) {
215 entry->natInfo.natAction = NAT_ACTION_NONE;
216 } else {
217 LOCK_STATE_EX lockStateNat;
218 NdisAcquireRWLockWrite(ovsCtNatLockObj, &lockStateNat, 0);
219 if (OvsIsForwardNat(natInfo->natAction)) {
220 entry->natInfo = *natInfo;
221 if (!OvsNatTranslateCtEntry(entry)) {
222 NdisReleaseRWLock(ovsCtNatLockObj, &lockStateNat);
223 return FALSE;
224 }
225 ctx->hash = OvsHashCtKey(&entry->key);
226 } else {
227 entry->natInfo.natAction = natInfo->natAction;
228 }
229 NdisReleaseRWLock(ovsCtNatLockObj, &lockStateNat);
230 }
231
232 entry->timestampStart = now;
233 InsertHeadList(&ovsConntrackTable[ctx->hash & CT_HASH_TABLE_MASK],
234 &entry->link);
235
236 InterlockedIncrement((LONG volatile *)&ctTotalEntries);
237 return TRUE;
238 }
239
240 static __inline POVS_CT_ENTRY
241 OvsCtEntryCreate(OvsForwardingContext *fwdCtx,
242 UINT8 ipProto,
243 UINT32 l4Offset,
244 OvsConntrackKeyLookupCtx *ctx,
245 OvsFlowKey *key,
246 PNAT_ACTION_INFO natInfo,
247 BOOLEAN commit,
248 UINT64 currentTime,
249 BOOLEAN *entryCreated)
250 {
251 POVS_CT_ENTRY entry = NULL;
252 UINT32 state = 0;
253 POVS_CT_ENTRY parentEntry;
254 PNET_BUFFER_LIST curNbl = fwdCtx->curNbl;
255
256 *entryCreated = FALSE;
257 state |= OVS_CS_F_NEW;
258
259 switch (ipProto) {
260 case IPPROTO_TCP:
261 {
262 TCPHdr tcpStorage;
263 const TCPHdr *tcp;
264 tcp = OvsGetTcp(curNbl, l4Offset, &tcpStorage);
265 if (!OvsConntrackValidateTcpPacket(tcp)) {
266 state = OVS_CS_F_INVALID;
267 break;
268 }
269
270 if (commit) {
271 entry = OvsConntrackCreateTcpEntry(tcp, curNbl, currentTime);
272 }
273 break;
274 }
275 case IPPROTO_ICMP:
276 {
277 ICMPHdr storage;
278 const ICMPHdr *icmp;
279 icmp = OvsGetIcmp(curNbl, l4Offset, &storage);
280 if (!OvsConntrackValidateIcmpPacket(icmp)) {
281 state = OVS_CS_F_INVALID;
282 break;
283 }
284
285 if (commit) {
286 entry = OvsConntrackCreateIcmpEntry(currentTime);
287 }
288 break;
289 }
290 case IPPROTO_UDP:
291 {
292 if (commit) {
293 entry = OvsConntrackCreateOtherEntry(currentTime);
294 }
295 break;
296 }
297 default:
298 state = OVS_CS_F_INVALID;
299 break;
300 }
301
302 parentEntry = OvsCtRelatedLookup(ctx->key, currentTime);
303 if (parentEntry != NULL && state != OVS_CS_F_INVALID) {
304 state |= OVS_CS_F_RELATED;
305 }
306
307 if (state != OVS_CS_F_INVALID && commit) {
308 if (entry) {
309 entry->parent = parentEntry;
310 if (OvsCtAddEntry(entry, ctx, natInfo, currentTime)) {
311 *entryCreated = TRUE;
312 } else {
313 /* Unable to add entry to the list */
314 OvsFreeMemoryWithTag(entry, OVS_CT_POOL_TAG);
315 state = OVS_CS_F_INVALID;
316 entry = NULL;
317 }
318 } else {
319 /* OvsAllocateMemoryWithTag returned NULL; treat as invalid */
320 state = OVS_CS_F_INVALID;
321 }
322 }
323
324 OvsCtUpdateFlowKey(key, state, ctx->key.zone, 0, NULL);
325 if (entry) {
326 OvsCtIncrementCounters(entry, ctx->reply, curNbl);
327 }
328 return entry;
329 }
330
331 static enum CT_UPDATE_RES
332 OvsCtUpdateEntry(OVS_CT_ENTRY* entry,
333 PNET_BUFFER_LIST nbl,
334 UINT8 ipProto,
335 UINT32 l4Offset,
336 BOOLEAN reply,
337 UINT64 now)
338 {
339 CT_UPDATE_RES status;
340 switch (ipProto) {
341 case IPPROTO_TCP:
342 {
343 TCPHdr tcpStorage;
344 const TCPHdr *tcp;
345 tcp = OvsGetTcp(nbl, l4Offset, &tcpStorage);
346 if (!tcp) {
347 status = CT_UPDATE_INVALID;
348 break;
349 }
350 status = OvsConntrackUpdateTcpEntry(entry, tcp, nbl, reply, now);
351 break;
352 }
353 case IPPROTO_ICMP:
354 status = OvsConntrackUpdateIcmpEntry(entry, reply, now);
355 break;
356 case IPPROTO_UDP:
357 status = OvsConntrackUpdateOtherEntry(entry, reply, now);
358 break;
359 default:
360 status = CT_UPDATE_INVALID;
361 break;
362 }
363 return status;
364 }
365
366 static __inline BOOLEAN
367 OvsCtEntryExpired(POVS_CT_ENTRY entry)
368 {
369 UINT64 currentTime;
370 NdisGetCurrentSystemTime((LARGE_INTEGER *)&currentTime);
371 return entry->expiration < currentTime;
372 }
373
374 static __inline VOID
375 OvsCtEntryDelete(POVS_CT_ENTRY entry, BOOLEAN forceDelete)
376 {
377 if (entry == NULL) {
378 return;
379 }
380 if (forceDelete || OvsCtEntryExpired(entry)) {
381 if (entry->natInfo.natAction) {
382 LOCK_STATE_EX lockStateNat;
383 NdisAcquireRWLockWrite(ovsCtNatLockObj, &lockStateNat, 0);
384 OvsNatDeleteKey(&entry->key);
385 NdisReleaseRWLock(ovsCtNatLockObj, &lockStateNat);
386 }
387 OvsPostCtEventEntry(entry, OVS_EVENT_CT_DELETE);
388 RemoveEntryList(&entry->link);
389 OvsFreeMemoryWithTag(entry, OVS_CT_POOL_TAG);
390 InterlockedDecrement((LONG volatile*)&ctTotalEntries);
391 return;
392 }
393 }
394
395 static __inline NDIS_STATUS
396 OvsDetectCtPacket(OvsForwardingContext *fwdCtx,
397 OvsFlowKey *key,
398 PNET_BUFFER_LIST *newNbl)
399 {
400 /* Currently we support only Unfragmented TCP packets */
401 switch (ntohs(key->l2.dlType)) {
402 case ETH_TYPE_IPV4:
403 if (key->ipKey.nwFrag != OVS_FRAG_TYPE_NONE) {
404 return OvsProcessIpv4Fragment(fwdCtx->switchContext,
405 &fwdCtx->curNbl,
406 fwdCtx->completionList,
407 fwdCtx->fwdDetail->SourcePortId,
408 key->tunKey.tunnelId,
409 newNbl);
410 }
411 if (key->ipKey.nwProto == IPPROTO_TCP
412 || key->ipKey.nwProto == IPPROTO_UDP
413 || key->ipKey.nwProto == IPPROTO_ICMP) {
414 return NDIS_STATUS_SUCCESS;
415 }
416 return NDIS_STATUS_NOT_SUPPORTED;
417 case ETH_TYPE_IPV6:
418 return NDIS_STATUS_NOT_SUPPORTED;
419 }
420
421 return NDIS_STATUS_NOT_SUPPORTED;
422 }
423
424 BOOLEAN
425 OvsCtKeyAreSame(OVS_CT_KEY ctxKey, OVS_CT_KEY entryKey)
426 {
427 return ((NdisEqualMemory(&ctxKey.src, &entryKey.src,
428 sizeof(struct ct_endpoint))) &&
429 (NdisEqualMemory(&ctxKey.dst, &entryKey.dst,
430 sizeof(struct ct_endpoint))) &&
431 (ctxKey.dl_type == entryKey.dl_type) &&
432 (ctxKey.nw_proto == entryKey.nw_proto) &&
433 (ctxKey.zone == entryKey.zone));
434 }
435
436 POVS_CT_ENTRY
437 OvsCtLookup(OvsConntrackKeyLookupCtx *ctx)
438 {
439 PLIST_ENTRY link;
440 POVS_CT_ENTRY entry;
441 BOOLEAN reply = FALSE;
442 POVS_CT_ENTRY found = NULL;
443
444 /* Reverse NAT must be performed before OvsCtLookup, so here
445 * we simply need to flip the src and dst in key and compare
446 * they are equal. Note that flipped key is not equal to
447 * rev_key due to NAT effect.
448 */
449 OVS_CT_KEY revCtxKey = ctx->key;
450 OvsCtKeyReverse(&revCtxKey);
451
452 if (!ctTotalEntries) {
453 return found;
454 }
455
456 LIST_FORALL(&ovsConntrackTable[ctx->hash & CT_HASH_TABLE_MASK], link) {
457 entry = CONTAINING_RECORD(link, OVS_CT_ENTRY, link);
458
459 if (OvsCtKeyAreSame(ctx->key, entry->key)) {
460 found = entry;
461 reply = FALSE;
462 }
463
464 if (!found && OvsCtKeyAreSame(revCtxKey, entry->key)) {
465 found = entry;
466 reply = TRUE;
467 }
468
469 if (found) {
470 if (OvsCtEntryExpired(found)) {
471 found = NULL;
472 } else {
473 ctx->reply = reply;
474 }
475 break;
476 }
477 }
478
479 ctx->entry = found;
480 return found;
481 }
482
483 UINT32
484 OvsHashCtKey(const OVS_CT_KEY *key)
485 {
486 UINT32 hsrc, hdst, hash;
487 hsrc = OvsJhashBytes((UINT32*) &key->src, sizeof(key->src), 0);
488 hdst = OvsJhashBytes((UINT32*) &key->dst, sizeof(key->dst), 0);
489 hash = hsrc ^ hdst; /* TO identify reverse traffic */
490 hash = OvsJhashBytes((uint32_t *) &key->dst + 1,
491 ((uint32_t *) (key + 1) -
492 (uint32_t *) (&key->dst + 1)),
493 hash);
494 return hash;
495 }
496
497 static UINT8
498 OvsReverseIcmpType(UINT8 type)
499 {
500 switch (type) {
501 case ICMP4_ECHO_REQUEST:
502 return ICMP4_ECHO_REPLY;
503 case ICMP4_ECHO_REPLY:
504 return ICMP4_ECHO_REQUEST;
505 case ICMP4_TIMESTAMP_REQUEST:
506 return ICMP4_TIMESTAMP_REPLY;
507 case ICMP4_TIMESTAMP_REPLY:
508 return ICMP4_TIMESTAMP_REQUEST;
509 case ICMP4_INFO_REQUEST:
510 return ICMP4_INFO_REPLY;
511 case ICMP4_INFO_REPLY:
512 return ICMP4_INFO_REQUEST;
513 default:
514 return 0;
515 }
516 }
517
518 static __inline NDIS_STATUS
519 OvsCtSetupLookupCtx(OvsFlowKey *flowKey,
520 UINT16 zone,
521 OvsConntrackKeyLookupCtx *ctx,
522 PNET_BUFFER_LIST curNbl,
523 UINT32 l4Offset)
524 {
525 const OVS_NAT_ENTRY *natEntry;
526 ctx->key.zone = zone;
527 ctx->key.dl_type = flowKey->l2.dlType;
528 ctx->related = FALSE;
529
530 /* Extract L3 and L4*/
531 if (flowKey->l2.dlType == htons(ETH_TYPE_IPV4)) {
532 ctx->key.src.addr.ipv4 = flowKey->ipKey.nwSrc;
533 ctx->key.dst.addr.ipv4 = flowKey->ipKey.nwDst;
534 ctx->key.nw_proto = flowKey->ipKey.nwProto;
535
536 ctx->key.src.port = flowKey->ipKey.l4.tpSrc;
537 ctx->key.dst.port = flowKey->ipKey.l4.tpDst;
538 if (flowKey->ipKey.nwProto == IPPROTO_ICMP) {
539 ICMPHdr icmpStorage;
540 const ICMPHdr *icmp;
541 icmp = OvsGetIcmp(curNbl, l4Offset, &icmpStorage);
542 ASSERT(icmp);
543
544 /* Related bit is set when ICMP has an error */
545 /* XXX parse out the appropriate src and dst from inner pkt */
546 switch (icmp->type) {
547 case ICMP4_ECHO_REQUEST:
548 case ICMP4_ECHO_REPLY:
549 case ICMP4_TIMESTAMP_REQUEST:
550 case ICMP4_TIMESTAMP_REPLY:
551 case ICMP4_INFO_REQUEST:
552 case ICMP4_INFO_REPLY:
553 if (icmp->code != 0) {
554 return NDIS_STATUS_INVALID_PACKET;
555 }
556 /* Separate ICMP connection: identified using id */
557 ctx->key.dst.icmp_id = icmp->fields.echo.id;
558 ctx->key.src.icmp_id = icmp->fields.echo.id;
559 ctx->key.src.icmp_type = icmp->type;
560 ctx->key.dst.icmp_type = OvsReverseIcmpType(icmp->type);
561 break;
562 case ICMP4_DEST_UNREACH:
563 case ICMP4_TIME_EXCEEDED:
564 case ICMP4_PARAM_PROB:
565 case ICMP4_SOURCE_QUENCH:
566 case ICMP4_REDIRECT: {
567 /* XXX Handle inner packet */
568 ctx->related = TRUE;
569 break;
570 }
571 default:
572 ctx->related = FALSE;
573 }
574 }
575 } else if (flowKey->l2.dlType == htons(ETH_TYPE_IPV6)) {
576 ctx->key.src.addr.ipv6 = flowKey->ipv6Key.ipv6Src;
577 ctx->key.dst.addr.ipv6 = flowKey->ipv6Key.ipv6Dst;
578 ctx->key.nw_proto = flowKey->ipv6Key.nwProto;
579
580 ctx->key.src.port = flowKey->ipv6Key.l4.tpSrc;
581 ctx->key.dst.port = flowKey->ipv6Key.l4.tpDst;
582 /* XXX Handle ICMPv6 errors*/
583 } else {
584 return NDIS_STATUS_INVALID_PACKET;
585 }
586
587 LOCK_STATE_EX lockStateNat;
588 NdisAcquireRWLockRead(ovsCtNatLockObj, &lockStateNat, 0);
589 natEntry = OvsNatLookup(&ctx->key, TRUE);
590 NdisReleaseRWLock(ovsCtNatLockObj, &lockStateNat);
591 if (natEntry) {
592 /* Translate address first for reverse NAT */
593 ctx->key = natEntry->ctEntry->key;
594 OvsCtKeyReverse(&ctx->key);
595 }
596
597 ctx->hash = OvsHashCtKey(&ctx->key);
598 return NDIS_STATUS_SUCCESS;
599 }
600
601 static __inline BOOLEAN
602 OvsDetectFtpPacket(OvsFlowKey *key) {
603 return (key->ipKey.nwProto == IPPROTO_TCP &&
604 (ntohs(key->ipKey.l4.tpDst) == IPPORT_FTP ||
605 ntohs(key->ipKey.l4.tpSrc) == IPPORT_FTP));
606 }
607
608 /*
609 *----------------------------------------------------------------------------
610 * OvsProcessConntrackEntry
611 * Check the TCP flags and set the ct_state of the entry
612 *----------------------------------------------------------------------------
613 */
614 static __inline POVS_CT_ENTRY
615 OvsProcessConntrackEntry(OvsForwardingContext *fwdCtx,
616 UINT32 l4Offset,
617 OvsConntrackKeyLookupCtx *ctx,
618 OvsFlowKey *key,
619 UINT16 zone,
620 NAT_ACTION_INFO *natInfo,
621 BOOLEAN commit,
622 UINT64 currentTime,
623 BOOLEAN *entryCreated)
624 {
625 POVS_CT_ENTRY entry = ctx->entry;
626 UINT32 state = 0;
627 PNET_BUFFER_LIST curNbl = fwdCtx->curNbl;
628 *entryCreated = FALSE;
629
630 /* If an entry was found, update the state based on TCP flags */
631 if (ctx->related) {
632 state |= OVS_CS_F_RELATED;
633 if (ctx->reply) {
634 state |= OVS_CS_F_REPLY_DIR;
635 }
636 } else {
637 CT_UPDATE_RES result;
638 result = OvsCtUpdateEntry(entry, curNbl, key->ipKey.nwProto,
639 l4Offset, ctx->reply, currentTime);
640 switch (result) {
641 case CT_UPDATE_VALID:
642 state |= OVS_CS_F_ESTABLISHED;
643 if (ctx->reply) {
644 state |= OVS_CS_F_REPLY_DIR;
645 }
646 break;
647 case CT_UPDATE_INVALID:
648 state |= OVS_CS_F_INVALID;
649 break;
650 case CT_UPDATE_NEW:
651 //Delete and update the Conntrack
652 OvsCtEntryDelete(ctx->entry, TRUE);
653 ctx->entry = NULL;
654 entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto, l4Offset,
655 ctx, key, natInfo, commit, currentTime,
656 entryCreated);
657 if (!entry) {
658 return NULL;
659 }
660 break;
661 }
662 }
663
664 if (key->ipKey.nwProto == IPPROTO_TCP && entry) {
665 /* Update the related bit if there is a parent */
666 if (entry->parent) {
667 state |= OVS_CS_F_RELATED;
668 } else {
669 POVS_CT_ENTRY parentEntry;
670 parentEntry = OvsCtRelatedLookup(ctx->key, currentTime);
671 entry->parent = parentEntry;
672 if (parentEntry != NULL) {
673 state |= OVS_CS_F_RELATED;
674 }
675 }
676 }
677
678 /* Copy mark and label from entry into flowKey. If actions specify
679 different mark and label, update the flowKey. */
680 if (entry != NULL) {
681 OvsCtUpdateFlowKey(key, state, zone, entry->mark, &entry->labels);
682 } else {
683 OvsCtUpdateFlowKey(key, state, zone, 0, NULL);
684 }
685 return entry;
686 }
687
688 static __inline VOID
689 OvsConntrackSetMark(OvsFlowKey *key,
690 POVS_CT_ENTRY entry,
691 UINT32 value,
692 UINT32 mask,
693 BOOLEAN *markChanged)
694 {
695 UINT32 newMark;
696 newMark = value | (entry->mark & ~(mask));
697 if (entry->mark != newMark) {
698 entry->mark = newMark;
699 key->ct.mark = newMark;
700 *markChanged = TRUE;
701 }
702 }
703
704 static __inline void
705 OvsConntrackSetLabels(OvsFlowKey *key,
706 POVS_CT_ENTRY entry,
707 struct ovs_key_ct_labels *val,
708 struct ovs_key_ct_labels *mask,
709 BOOLEAN *labelChanged)
710 {
711 ovs_u128 v, m, pktMdLabel = {0};
712 memcpy(&v, val, sizeof v);
713 memcpy(&m, mask, sizeof m);
714
715 pktMdLabel.u64.lo = v.u64.lo | (pktMdLabel.u64.lo & ~(m.u64.lo));
716 pktMdLabel.u64.hi = v.u64.hi | (pktMdLabel.u64.hi & ~(m.u64.hi));
717
718 if (!NdisEqualMemory(&entry->labels, &pktMdLabel,
719 sizeof(struct ovs_key_ct_labels))) {
720 *labelChanged = TRUE;
721 }
722 NdisMoveMemory(&entry->labels, &pktMdLabel,
723 sizeof(struct ovs_key_ct_labels));
724 NdisMoveMemory(&key->ct.labels, &pktMdLabel,
725 sizeof(struct ovs_key_ct_labels));
726 }
727
728 static void
729 OvsCtSetMarkLabel(OvsFlowKey *key,
730 POVS_CT_ENTRY entry,
731 MD_MARK *mark,
732 MD_LABELS *labels,
733 BOOLEAN *triggerUpdateEvent)
734 {
735 if (mark) {
736 OvsConntrackSetMark(key, entry, mark->value, mark->mask,
737 triggerUpdateEvent);
738 }
739
740 if (labels) {
741 OvsConntrackSetLabels(key, entry, &labels->value, &labels->mask,
742 triggerUpdateEvent);
743 }
744 }
745
746 static __inline void
747 OvsCtUpdateTuple(OvsFlowKey *key, OVS_CT_KEY *ctKey)
748 {
749 key->ct.tuple_ipv4.ipv4_src = ctKey->src.addr.ipv4_aligned;
750 key->ct.tuple_ipv4.ipv4_dst = ctKey->dst.addr.ipv4_aligned;
751 key->ct.tuple_ipv4.ipv4_proto = ctKey->nw_proto;
752
753 /* Orig tuple Port is overloaded to take in ICMP-Type & Code */
754 /* This mimics the behavior in lib/conntrack.c*/
755 key->ct.tuple_ipv4.src_port = ctKey->nw_proto != IPPROTO_ICMP ?
756 ctKey->src.port :
757 htons(ctKey->src.icmp_type);
758 key->ct.tuple_ipv4.dst_port = ctKey->nw_proto != IPPROTO_ICMP ?
759 ctKey->dst.port :
760 htons(ctKey->src.icmp_code);
761 }
762
763 static __inline NDIS_STATUS
764 OvsCtExecute_(OvsForwardingContext *fwdCtx,
765 OvsFlowKey *key,
766 OVS_PACKET_HDR_INFO *layers,
767 BOOLEAN commit,
768 BOOLEAN force,
769 UINT16 zone,
770 MD_MARK *mark,
771 MD_LABELS *labels,
772 PCHAR helper,
773 PNAT_ACTION_INFO natInfo,
774 BOOLEAN postUpdateEvent)
775 {
776 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
777 BOOLEAN triggerUpdateEvent = FALSE;
778 POVS_CT_ENTRY entry = NULL;
779 PNET_BUFFER_LIST curNbl = fwdCtx->curNbl;
780 OvsConntrackKeyLookupCtx ctx = { 0 };
781 LOCK_STATE_EX lockState;
782 UINT64 currentTime;
783 NdisGetCurrentSystemTime((LARGE_INTEGER *) &currentTime);
784
785
786 /* Retrieve the Conntrack Key related fields from packet */
787 OvsCtSetupLookupCtx(key, zone, &ctx, curNbl, layers->l4Offset);
788
789 NdisAcquireRWLockWrite(ovsConntrackLockObj, &lockState, 0);
790
791 /* Lookup Conntrack entries for a matching entry */
792 entry = OvsCtLookup(&ctx);
793 BOOLEAN entryCreated = FALSE;
794
795 /* Delete entry in reverse direction if 'force' is specified */
796 if (entry && force && ctx.reply) {
797 OvsCtEntryDelete(entry, TRUE);
798 entry = NULL;
799 }
800
801 if (!entry && commit && ctTotalEntries >= CT_MAX_ENTRIES) {
802 /* Don't proceed with processing if the max limit has been hit.
803 * This blocks only new entries from being created and doesn't
804 * affect existing connections.
805 */
806 NdisReleaseRWLock(ovsConntrackLockObj, &lockState);
807 OVS_LOG_ERROR("Conntrack Limit hit: %lu", ctTotalEntries);
808 return NDIS_STATUS_RESOURCES;
809 }
810
811 /* Increment the counters soon after the lookup, since we set ct.state
812 * to OVS_CS_F_TRACKED after processing the ct entry.
813 */
814 if (entry && (!(key->ct.state & OVS_CS_F_TRACKED))) {
815 OvsCtIncrementCounters(entry, ctx.reply, curNbl);
816 }
817
818 if (!entry) {
819 /* If no matching entry was found, create one and add New state */
820 entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto,
821 layers->l4Offset, &ctx,
822 key, natInfo, commit, currentTime,
823 &entryCreated);
824 } else {
825 /* Process the entry and update CT flags */
826 entry = OvsProcessConntrackEntry(fwdCtx, layers->l4Offset, &ctx, key,
827 zone, natInfo, commit, currentTime,
828 &entryCreated);
829 }
830
831 if (entry == NULL) {
832 return status;
833 }
834 /*
835 * Note that natInfo is not the same as entry->natInfo here. natInfo
836 * is decided by action in the openflow rule, entry->natInfo is decided
837 * when the entry is created. In the reverse NAT case, natInfo is
838 * NAT_ACTION_REVERSE, yet entry->natInfo is NAT_ACTION_SRC or
839 * NAT_ACTION_DST without NAT_ACTION_REVERSE
840 */
841 if (natInfo->natAction != NAT_ACTION_NONE)
842 {
843 LOCK_STATE_EX lockStateNat;
844 NdisAcquireRWLockWrite(ovsCtNatLockObj, &lockStateNat, 0);
845 OvsNatPacket(fwdCtx, entry, entry->natInfo.natAction,
846 key, ctx.reply);
847 NdisReleaseRWLock(ovsCtNatLockObj, &lockStateNat);
848 }
849
850 OvsCtSetMarkLabel(key, entry, mark, labels, &triggerUpdateEvent);
851
852 if (OvsDetectFtpPacket(key)) {
853 /* FTP parser will always be loaded */
854 UNREFERENCED_PARAMETER(helper);
855
856 status = OvsCtHandleFtp(curNbl, key, layers, currentTime, entry,
857 (ntohs(key->ipKey.l4.tpDst) == IPPORT_FTP));
858 if (status != NDIS_STATUS_SUCCESS) {
859 OVS_LOG_ERROR("Error while parsing the FTP packet");
860 }
861 }
862
863 /* Add original tuple information to flow Key */
864 if (entry->key.dl_type == ntohs(ETH_TYPE_IPV4)) {
865 if (entry->parent != NULL) {
866 POVS_CT_ENTRY parent = entry->parent;
867 OvsCtUpdateTuple(key, &parent->key);
868 } else {
869 OvsCtUpdateTuple(key, &entry->key);
870 }
871 }
872
873 if (entryCreated) {
874 OvsPostCtEventEntry(entry, OVS_EVENT_CT_NEW);
875 }
876 if (postUpdateEvent && !entryCreated && triggerUpdateEvent) {
877 OvsPostCtEventEntry(entry, OVS_EVENT_CT_UPDATE);
878 }
879
880 NdisReleaseRWLock(ovsConntrackLockObj, &lockState);
881
882 return status;
883 }
884
885 /*
886 *---------------------------------------------------------------------------
887 * OvsExecuteConntrackAction
888 * Executes Conntrack actions XXX - Add more
889 * For the Ipv4 fragments, consume the orginal fragment NBL
890 *---------------------------------------------------------------------------
891 */
892 NDIS_STATUS
893 OvsExecuteConntrackAction(OvsForwardingContext *fwdCtx,
894 OvsFlowKey *key,
895 const PNL_ATTR a)
896 {
897 PNL_ATTR ctAttr;
898 BOOLEAN commit = FALSE;
899 BOOLEAN force = FALSE;
900 BOOLEAN postUpdateEvent = FALSE;
901 UINT16 zone = 0;
902 UINT32 eventmask = 0;
903 MD_MARK *mark = NULL;
904 MD_LABELS *labels = NULL;
905 PCHAR helper = NULL;
906 NAT_ACTION_INFO natActionInfo;
907 OVS_PACKET_HDR_INFO *layers = &fwdCtx->layers;
908 PNET_BUFFER_LIST newNbl = NULL;
909 NDIS_STATUS status;
910
911 memset(&natActionInfo, 0, sizeof natActionInfo);
912 status = OvsDetectCtPacket(fwdCtx, key, &newNbl);
913 if (status != NDIS_STATUS_SUCCESS) {
914 return status;
915 }
916
917 /* XXX Convert this to NL_ATTR_FOR_EACH */
918 ctAttr = NlAttrFindNested(a, OVS_CT_ATTR_ZONE);
919 if (ctAttr) {
920 zone = NlAttrGetU16(ctAttr);
921 }
922 ctAttr = NlAttrFindNested(a, OVS_CT_ATTR_COMMIT);
923 if (ctAttr) {
924 commit = TRUE;
925 }
926 ctAttr = NlAttrFindNested(a, OVS_CT_ATTR_MARK);
927 if (ctAttr) {
928 mark = NlAttrGet(ctAttr);
929 }
930 ctAttr = NlAttrFindNested(a, OVS_CT_ATTR_LABELS);
931 if (ctAttr) {
932 labels = NlAttrGet(ctAttr);
933 }
934 natActionInfo.natAction = NAT_ACTION_NONE;
935 ctAttr = NlAttrFindNested(a, OVS_CT_ATTR_NAT);
936 if (ctAttr) {
937 /* Pares Nested NAT attributes. */
938 PNL_ATTR natAttr;
939 unsigned int left;
940 BOOLEAN hasMinIp = FALSE;
941 BOOLEAN hasMinPort = FALSE;
942 BOOLEAN hasMaxIp = FALSE;
943 BOOLEAN hasMaxPort = FALSE;
944 NL_NESTED_FOR_EACH_UNSAFE (natAttr, left, ctAttr) {
945 enum ovs_nat_attr subtype = NlAttrType(natAttr);
946 switch(subtype) {
947 case OVS_NAT_ATTR_SRC:
948 case OVS_NAT_ATTR_DST:
949 natActionInfo.natAction |=
950 ((subtype == OVS_NAT_ATTR_SRC)
951 ? NAT_ACTION_SRC : NAT_ACTION_DST);
952 break;
953 case OVS_NAT_ATTR_IP_MIN:
954 memcpy(&natActionInfo.minAddr,
955 NlAttrData(natAttr), NlAttrGetSize(natAttr));
956 hasMinIp = TRUE;
957 break;
958 case OVS_NAT_ATTR_IP_MAX:
959 memcpy(&natActionInfo.maxAddr,
960 NlAttrData(natAttr), NlAttrGetSize(natAttr));
961 hasMaxIp = TRUE;
962 break;
963 case OVS_NAT_ATTR_PROTO_MIN:
964 natActionInfo.minPort = NlAttrGetU16(natAttr);
965 hasMinPort = TRUE;
966 break;
967 case OVS_NAT_ATTR_PROTO_MAX:
968 natActionInfo.maxPort = NlAttrGetU16(natAttr);
969 hasMaxPort = TRUE;
970 break;
971 case OVS_NAT_ATTR_PERSISTENT:
972 case OVS_NAT_ATTR_PROTO_HASH:
973 case OVS_NAT_ATTR_PROTO_RANDOM:
974 break;
975 }
976 }
977 if (natActionInfo.natAction == NAT_ACTION_NONE) {
978 natActionInfo.natAction = NAT_ACTION_REVERSE;
979 }
980 if (hasMinIp && !hasMaxIp) {
981 memcpy(&natActionInfo.maxAddr,
982 &natActionInfo.minAddr,
983 sizeof(natActionInfo.maxAddr));
984 }
985 if (hasMinPort && !hasMaxPort) {
986 natActionInfo.maxPort = natActionInfo.minPort;
987 }
988 if (hasMinPort || hasMaxPort) {
989 if (natActionInfo.natAction & NAT_ACTION_SRC) {
990 natActionInfo.natAction |= NAT_ACTION_SRC_PORT;
991 } else if (natActionInfo.natAction & NAT_ACTION_DST) {
992 natActionInfo.natAction |= NAT_ACTION_DST_PORT;
993 }
994 }
995 }
996 ctAttr = NlAttrFindNested(a, OVS_CT_ATTR_HELPER);
997 if (ctAttr) {
998 helper = NlAttrGetString(ctAttr);
999 if (helper == NULL) {
1000 return NDIS_STATUS_INVALID_PARAMETER;
1001 }
1002 if (strcmp("ftp", helper) != 0) {
1003 /* Only support FTP */
1004 return NDIS_STATUS_NOT_SUPPORTED;
1005 }
1006 }
1007 ctAttr = NlAttrFindNested(a, OVS_CT_ATTR_FORCE_COMMIT);
1008 if (ctAttr) {
1009 force = TRUE;
1010 /* Force implicitly means commit */
1011 commit = TRUE;
1012 }
1013 ctAttr = NlAttrFindNested(a, OVS_CT_ATTR_EVENTMASK);
1014 if (ctAttr) {
1015 eventmask = NlAttrGetU32(ctAttr);
1016 /* Only mark and label updates are supported. */
1017 if (eventmask & (1 << IPCT_MARK | 1 << IPCT_LABEL))
1018 postUpdateEvent = TRUE;
1019 }
1020 /* If newNbl is not allocated, use the current Nbl*/
1021 status = OvsCtExecute_(fwdCtx, key, layers,
1022 commit, force, zone, mark, labels, helper, &natActionInfo,
1023 postUpdateEvent);
1024 return status;
1025 }
1026
1027 /*
1028 *----------------------------------------------------------------------------
1029 * OvsConntrackEntryCleaner
1030 * Runs periodically and cleans up the connection tracker
1031 *----------------------------------------------------------------------------
1032 */
1033 VOID
1034 OvsConntrackEntryCleaner(PVOID data)
1035 {
1036
1037 POVS_CT_THREAD_CTX context = (POVS_CT_THREAD_CTX)data;
1038 PLIST_ENTRY link, next;
1039 POVS_CT_ENTRY entry;
1040 LOCK_STATE_EX lockState;
1041 BOOLEAN success = TRUE;
1042
1043 while (success) {
1044 if (ovsConntrackLockObj == NULL) {
1045 /* Lock has been freed by 'OvsCleanupConntrack()' */
1046 break;
1047 }
1048 NdisAcquireRWLockWrite(ovsConntrackLockObj, &lockState, 0);
1049 if (context->exit) {
1050 NdisReleaseRWLock(ovsConntrackLockObj, &lockState);
1051 break;
1052 }
1053
1054 /* Set the timeout for the thread and cleanup */
1055 INT64 threadSleepTimeout = -CT_CLEANUP_INTERVAL;
1056
1057 if (ctTotalEntries) {
1058 for (int i = 0; i < CT_HASH_TABLE_SIZE; i++) {
1059 LIST_FORALL_SAFE(&ovsConntrackTable[i], link, next) {
1060 entry = CONTAINING_RECORD(link, OVS_CT_ENTRY, link);
1061 OvsCtEntryDelete(entry, FALSE);
1062 }
1063 }
1064 }
1065 NdisReleaseRWLock(ovsConntrackLockObj, &lockState);
1066 KeWaitForSingleObject(&context->event, Executive, KernelMode,
1067 FALSE, (LARGE_INTEGER *)&threadSleepTimeout);
1068 }
1069
1070 PsTerminateSystemThread(STATUS_SUCCESS);
1071 }
1072
1073 /*
1074 *----------------------------------------------------------------------------
1075 * OvsCtFlush
1076 * Flushes out all Conntrack Entries that match any of the arguments
1077 *----------------------------------------------------------------------------
1078 */
1079 static __inline NDIS_STATUS
1080 OvsCtFlush(UINT16 zone, struct ovs_key_ct_tuple_ipv4 *tuple)
1081 {
1082 PLIST_ENTRY link, next;
1083 POVS_CT_ENTRY entry;
1084
1085 LOCK_STATE_EX lockState, lockStateNat;
1086 NdisAcquireRWLockWrite(ovsConntrackLockObj, &lockState, 0);
1087
1088 if (ctTotalEntries) {
1089 for (UINT32 i = 0; i < CT_HASH_TABLE_SIZE; i++) {
1090 LIST_FORALL_SAFE(&ovsConntrackTable[i], link, next) {
1091 entry = CONTAINING_RECORD(link, OVS_CT_ENTRY, link);
1092 if (tuple) {
1093 if (tuple->ipv4_proto != IPPROTO_ICMP &&
1094 tuple->ipv4_src == entry->key.src.addr.ipv4_aligned &&
1095 tuple->ipv4_dst == entry->key.dst.addr.ipv4_aligned &&
1096 tuple->ipv4_proto == entry->key.nw_proto &&
1097 tuple->src_port == entry->key.src.port &&
1098 tuple->dst_port == entry->key.dst.port &&
1099 (zone ? entry->key.zone == zone: TRUE)) {
1100 OvsCtEntryDelete(entry, TRUE);
1101 } else if (tuple->ipv4_src == entry->key.src.addr.ipv4_aligned &&
1102 tuple->ipv4_dst == entry->key.dst.addr.ipv4_aligned &&
1103 tuple->ipv4_proto == entry->key.nw_proto &&
1104 tuple->src_port == entry->key.src.icmp_type &&
1105 tuple->dst_port == entry->key.src.icmp_code &&
1106 (zone ? entry->key.zone == zone: TRUE)) {
1107 OvsCtEntryDelete(entry, TRUE);
1108 }
1109 } else if (!zone || zone == entry->key.zone) {
1110 OvsCtEntryDelete(entry, TRUE);
1111 }
1112 }
1113 }
1114 }
1115
1116 NdisAcquireRWLockWrite(ovsCtNatLockObj, &lockStateNat, 0);
1117 OvsNatFlush(zone);
1118 NdisReleaseRWLock(ovsCtNatLockObj, &lockStateNat);
1119 NdisReleaseRWLock(ovsConntrackLockObj, &lockState);
1120 return NDIS_STATUS_SUCCESS;
1121 }
1122
1123 NTSTATUS
1124 OvsCtDeleteCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1125 UINT32 *replyLen)
1126 {
1127 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1128 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1129 PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
1130 PNL_ATTR ctAttrs[__CTA_MAX];
1131 UINT32 attrOffset = NLMSG_HDRLEN + NF_GEN_MSG_HDRLEN + OVS_HDRLEN;
1132 NL_ERROR nlError = NL_ERROR_SUCCESS;
1133 NTSTATUS status;
1134 UINT16 zone = 0;
1135 struct ovs_key_ct_tuple_ipv4 *ct_tuple = NULL;
1136 NL_BUFFER nlBuf;
1137 UINT16 nlmsgType;
1138 PNL_MSG_HDR nlMsg;
1139
1140 static const NL_POLICY ctAttrPolicy[] = {
1141 [CTA_TUPLE_ORIG] = {.type = NL_A_NESTED, .optional = TRUE},
1142 [CTA_ZONE] = {.type = NL_A_BE16, .optional = TRUE },
1143 };
1144
1145 if ((NlAttrParse(nlMsgHdr, attrOffset, NlNfMsgAttrsLen(nlMsgHdr),
1146 ctAttrPolicy, ARRAY_SIZE(ctAttrPolicy),
1147 ctAttrs, ARRAY_SIZE(ctAttrs)))
1148 != TRUE) {
1149 OVS_LOG_ERROR("Ct attr parsing failed for msg: %p", nlMsgHdr);
1150 status = STATUS_INVALID_PARAMETER;
1151 goto done;
1152 }
1153
1154 if (ctAttrs[CTA_ZONE]) {
1155 zone = ntohs(NlAttrGetU16(ctAttrs[CTA_ZONE]));
1156 }
1157
1158 if (ctAttrs[CTA_TUPLE_ORIG]) {
1159 ct_tuple = OvsAllocateMemoryWithTag(sizeof(struct ovs_key_ct_tuple_ipv4),
1160 OVS_CT_POOL_TAG);
1161 if (ct_tuple == NULL) {
1162 status = STATUS_INSUFFICIENT_RESOURCES;
1163 goto done;
1164 }
1165 /* Parse ct tuple. */
1166 status = MapNlToCtTuple(msgIn, ctAttrs[CTA_TUPLE_ORIG], ct_tuple);
1167 if (status != STATUS_SUCCESS) {
1168 goto done;
1169 }
1170 }
1171
1172 status = OvsCtFlush(zone, ct_tuple);
1173 if (status == STATUS_SUCCESS) {
1174 nlmsgType = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_DELETE);
1175 NlBufInit(&nlBuf,
1176 usrParamsCtx->outputBuffer,
1177 usrParamsCtx->outputLength);
1178 if (!NlFillOvsMsgForNfGenMsg(&nlBuf, nlmsgType, NLM_F_CREATE,
1179 msgIn->nlMsg.nlmsgSeq,
1180 msgIn->nlMsg.nlmsgPid,
1181 AF_UNSPEC,
1182 msgIn->nfGenMsg.version,
1183 0)) {
1184 status = STATUS_INVALID_PARAMETER;
1185 }
1186 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuf, 0, 0);
1187 nlMsg->nlmsgLen = NlBufSize(&nlBuf);
1188 *replyLen = msgOut->nlMsg.nlmsgLen;
1189 }
1190
1191 done:
1192 if (ct_tuple) {
1193 OvsFreeMemoryWithTag(ct_tuple, OVS_CT_POOL_TAG);
1194 }
1195
1196 nlError = NlMapStatusToNlErr(status);
1197 if (nlError != NL_ERROR_SUCCESS) {
1198 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1199 usrParamsCtx->outputBuffer;
1200
1201 ASSERT(msgError);
1202 NlBuildErrorMsg(msgIn, msgError, nlError, replyLen);
1203 ASSERT(*replyLen != 0);
1204 status = STATUS_SUCCESS;
1205 }
1206
1207 return status;
1208 }
1209
1210 static __inline NDIS_STATUS
1211 MapNlToCtTuple(POVS_MESSAGE msgIn, PNL_ATTR ctAttr,
1212 struct ovs_key_ct_tuple_ipv4 *ct_tuple) {
1213
1214 PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
1215 PNL_ATTR ctTupleAttrs[__CTA_MAX];
1216 UINT32 attrOffset;
1217 static const NL_POLICY ctTuplePolicy[] = {
1218 [CTA_TUPLE_IP] = {.type = NL_A_NESTED, .optional = FALSE },
1219 [CTA_TUPLE_PROTO] = {.type = NL_A_NESTED, .optional = FALSE},
1220 };
1221
1222 static const NL_POLICY ctTupleIpPolicy[] = {
1223 [CTA_IP_V4_SRC] = { .type = NL_A_BE32, .optional = TRUE },
1224 [CTA_IP_V4_DST] = { .type = NL_A_BE32, .optional = TRUE },
1225 };
1226
1227 static const NL_POLICY ctTupleProtoPolicy[] = {
1228 [CTA_PROTO_NUM] = { .type = NL_A_U8, .optional = FALSE },
1229 [CTA_PROTO_SRC_PORT] = { .type = NL_A_BE16, .optional = TRUE },
1230 [CTA_PROTO_DST_PORT] = { .type = NL_A_BE16, .optional = TRUE },
1231 [CTA_PROTO_ICMP_TYPE] = { .type = NL_A_U8, .optional = TRUE },
1232 [CTA_PROTO_ICMP_CODE] = { .type = NL_A_U8, .optional = TRUE },
1233 };
1234
1235 if (!ctAttr) {
1236 return STATUS_INVALID_PARAMETER;
1237 }
1238
1239 attrOffset = (UINT32)((PCHAR) ctAttr - (PCHAR)nlMsgHdr);
1240 if ((NlAttrParseNested(nlMsgHdr, attrOffset, NlAttrLen(ctAttr),
1241 ctTuplePolicy, ARRAY_SIZE(ctTuplePolicy),
1242 ctTupleAttrs, ARRAY_SIZE(ctTupleAttrs)))
1243 != TRUE) {
1244 OVS_LOG_ERROR("CTA_TUPLE attr parsing failed for msg: %p", nlMsgHdr);
1245 return STATUS_INVALID_PARAMETER;
1246 }
1247
1248 if (ctTupleAttrs[CTA_TUPLE_IP]) {
1249 PNL_ATTR ctTupleIpAttrs[__CTA_MAX];
1250 attrOffset = (UINT32)((PCHAR) ctTupleAttrs[CTA_TUPLE_IP] - (PCHAR)nlMsgHdr);
1251 if ((NlAttrParseNested(nlMsgHdr, attrOffset, NlAttrLen(ctTupleAttrs[CTA_TUPLE_IP]),
1252 ctTupleIpPolicy, ARRAY_SIZE(ctTupleIpPolicy),
1253 ctTupleIpAttrs, ARRAY_SIZE(ctTupleIpAttrs)))
1254 != TRUE) {
1255 OVS_LOG_ERROR("CTA_TUPLE_IP attr parsing failed for msg: %p", nlMsgHdr);
1256 return STATUS_INVALID_PARAMETER;
1257 }
1258
1259 if (ctTupleIpAttrs[CTA_IP_V4_SRC] && ctTupleIpAttrs[CTA_IP_V4_DST]) {
1260 ct_tuple->ipv4_src = NlAttrGetU32(ctTupleIpAttrs[CTA_IP_V4_SRC]);
1261 ct_tuple->ipv4_dst = NlAttrGetU32(ctTupleIpAttrs[CTA_IP_V4_DST]);
1262 }
1263 }
1264
1265 if (ctTupleAttrs[CTA_TUPLE_PROTO]) {
1266 PNL_ATTR ctTupleProtoAttrs[__CTA_MAX];
1267 attrOffset = (UINT32)((PCHAR) ctTupleAttrs[CTA_TUPLE_PROTO] - (PCHAR)nlMsgHdr);
1268 if ((NlAttrParseNested(nlMsgHdr, attrOffset, NlAttrLen(ctTupleAttrs[CTA_TUPLE_PROTO]),
1269 ctTupleProtoPolicy, ARRAY_SIZE(ctTupleProtoPolicy),
1270 ctTupleProtoAttrs, ARRAY_SIZE(ctTupleProtoAttrs)))
1271 != TRUE) {
1272 OVS_LOG_ERROR("CTA_TUPLE_PROTO attr parsing failed for msg: %p", nlMsgHdr);
1273 return STATUS_INVALID_PARAMETER;
1274 }
1275
1276 if (ctTupleProtoAttrs[CTA_PROTO_NUM]) {
1277 ct_tuple->ipv4_proto = NlAttrGetU8 (ctTupleProtoAttrs[CTA_PROTO_NUM]);
1278 if (ctTupleProtoAttrs[CTA_PROTO_SRC_PORT] && ctTupleProtoAttrs[CTA_PROTO_DST_PORT]) {
1279 ct_tuple->src_port = NlAttrGetU16(ctTupleProtoAttrs[CTA_PROTO_SRC_PORT]);
1280 ct_tuple->dst_port = NlAttrGetU16(ctTupleProtoAttrs[CTA_PROTO_DST_PORT]);
1281 } else if (ctTupleProtoAttrs[CTA_PROTO_ICMP_TYPE] &&
1282 ctTupleProtoAttrs[CTA_PROTO_ICMP_CODE] ) {
1283 ct_tuple->src_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMP_TYPE]);
1284 ct_tuple->dst_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMP_CODE]);
1285 }
1286
1287 }
1288 }
1289
1290 return NDIS_STATUS_SUCCESS;
1291 }
1292
1293 static __inline NDIS_STATUS
1294 MapIpTupleToNl(PNL_BUFFER nlBuf, OVS_CT_KEY *key)
1295 {
1296 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1297 UINT32 offset = 0;
1298
1299 offset = NlMsgStartNested(nlBuf, CTA_TUPLE_IP);
1300 if (!offset) {
1301 return NDIS_STATUS_FAILURE;
1302 }
1303
1304 if (key->dl_type == ntohs(ETH_TYPE_IPV4)) {
1305 if (!NlMsgPutTailU32(nlBuf, CTA_IP_V4_SRC, key->src.addr.ipv4)) {
1306 status = NDIS_STATUS_FAILURE;
1307 goto done;
1308 }
1309 if (!NlMsgPutTailU32(nlBuf, CTA_IP_V4_DST, key->dst.addr.ipv4)) {
1310 status = NDIS_STATUS_FAILURE;
1311 goto done;
1312 }
1313 } else if (key->dl_type == ntohs(ETH_TYPE_IPV6)) {
1314 if (!NlMsgPutTailUnspec(nlBuf, CTA_IP_V6_SRC,
1315 (PCHAR)(&key->src.addr.ipv6),
1316 sizeof(key->src.addr.ipv6))) {
1317 status = NDIS_STATUS_FAILURE;
1318 goto done;
1319 }
1320 if (!NlMsgPutTailUnspec(nlBuf, CTA_IP_V6_DST,
1321 (PCHAR)(&key->dst.addr.ipv6),
1322 sizeof(key->dst.addr.ipv6))) {
1323 status = NDIS_STATUS_FAILURE;
1324 goto done;
1325 }
1326 }
1327
1328 done:
1329 NlMsgEndNested(nlBuf, offset);
1330 return status;
1331 }
1332
1333 static __inline NDIS_STATUS
1334 MapProtoTupleToNl(PNL_BUFFER nlBuf, OVS_CT_KEY *key)
1335 {
1336 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1337 UINT32 offset = 0;
1338
1339 offset = NlMsgStartNested(nlBuf, CTA_TUPLE_PROTO);
1340 if (!offset) {
1341 return NDIS_STATUS_FAILURE;
1342 }
1343
1344 if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_NUM, key->nw_proto)) {
1345 status = NDIS_STATUS_FAILURE;
1346 goto done;
1347 }
1348
1349 if (key->dl_type == ntohs(ETH_TYPE_IPV4)
1350 || key->dl_type == ntohs(ETH_TYPE_IPV6)) {
1351 /* ICMP and ICMPv6 Type, Code and ID are currently not tracked */
1352 if (key->nw_proto == IPPROTO_ICMP) {
1353 if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_ICMP_ID,
1354 htons(key->src.icmp_id))) {
1355 status = NDIS_STATUS_FAILURE;
1356 goto done;
1357 }
1358 if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMP_TYPE,
1359 key->src.icmp_type)) {
1360 status = NDIS_STATUS_FAILURE;
1361 goto done;
1362 }
1363 if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMP_CODE,
1364 key->src.icmp_code)) {
1365 status = NDIS_STATUS_FAILURE;
1366 goto done;
1367 }
1368 } else if (key->nw_proto == IPPROTO_ICMPV6) {
1369 if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_ICMPV6_ID, 0)) {
1370 status = NDIS_STATUS_FAILURE;
1371 goto done;
1372 }
1373 if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_TYPE, 0)) {
1374 status = NDIS_STATUS_FAILURE;
1375 goto done;
1376 }
1377 if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_CODE, 0)) {
1378 status = NDIS_STATUS_FAILURE;
1379 goto done;
1380 }
1381 } else if (key->nw_proto == IPPROTO_TCP
1382 || key->nw_proto == IPPROTO_UDP) {
1383 if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_SRC_PORT,
1384 key->src.port)) {
1385 status = NDIS_STATUS_FAILURE;
1386 goto done;
1387 }
1388 if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_DST_PORT,
1389 key->dst.port)) {
1390 status = NDIS_STATUS_FAILURE;
1391 goto done;
1392 }
1393 }
1394 }
1395
1396 done:
1397 NlMsgEndNested(nlBuf, offset);
1398 return status;
1399 }
1400
1401 static __inline NDIS_STATUS
1402 MapCtKeyTupleToNl(PNL_BUFFER nlBuf,
1403 UINT16 tupleType,
1404 OVS_CT_KEY *key)
1405 {
1406 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1407 UINT32 offset = 0;
1408
1409 offset = NlMsgStartNested(nlBuf, tupleType);
1410 if (!offset) {
1411 return NDIS_STATUS_FAILURE;
1412 }
1413
1414 status = MapIpTupleToNl(nlBuf, key);
1415 if (status != NDIS_STATUS_SUCCESS) {
1416 goto done;
1417 }
1418
1419 status = MapProtoTupleToNl(nlBuf, key);
1420 if (status != NDIS_STATUS_SUCCESS) {
1421 goto done;
1422 }
1423
1424 done:
1425 NlMsgEndNested(nlBuf, offset);
1426 return status;
1427 }
1428
1429 static __inline NDIS_STATUS
1430 MapCtCounterToNl(PNL_BUFFER nlBuf,
1431 UINT16 counterType,
1432 OVS_CT_KEY *key)
1433 {
1434 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1435 UINT32 offset = 0;
1436
1437 offset = NlMsgStartNested(nlBuf, counterType);
1438 if (!offset) {
1439 return NDIS_STATUS_FAILURE;
1440 }
1441
1442 if (!NlMsgPutTailU64(nlBuf, CTA_COUNTERS_PACKETS,
1443 htonll(key->packetCount))) {
1444 status = NDIS_STATUS_FAILURE;
1445 goto done;
1446 }
1447
1448 if (!NlMsgPutTailU64(nlBuf, CTA_COUNTERS_BYTES,
1449 htonll(key->byteCount))) {
1450 status = NDIS_STATUS_FAILURE;
1451 goto done;
1452 }
1453
1454 done:
1455 NlMsgEndNested(nlBuf, offset);
1456 return status;
1457 }
1458
1459 /* Userspace expects system time to be Unix timestamp in Nano Seconds */
1460 static __inline unsigned
1461 WindowsTickToUnixSeconds(long long windowsTicks)
1462 {
1463 /*
1464 * Windows epoch starts 1601-01-01T00:00:00Z. It's 11644473600 seconds
1465 * before the UNIX/Linux epoch (1970-01-01T00:00:00Z). Windows ticks are
1466 * in 100 nanoseconds
1467 */
1468 return (unsigned)((windowsTicks / WINDOWS_TICK
1469 - SEC_TO_UNIX_EPOCH));
1470 }
1471
1472 NTSTATUS
1473 OvsCreateNlMsgFromCtEntry(POVS_CT_ENTRY entry,
1474 PVOID outBuffer,
1475 UINT32 outBufLen,
1476 UINT8 eventType,
1477 UINT32 nlmsgSeq,
1478 UINT32 nlmsgPid,
1479 UINT8 nfGenVersion,
1480 UINT32 dpIfIndex)
1481 {
1482 NL_BUFFER nlBuf;
1483 BOOLEAN ok;
1484 PNL_MSG_HDR nlMsg;
1485 UINT32 timeout;
1486 NDIS_STATUS status;
1487 UINT64 currentTime, expiration;
1488 UINT16 nlmsgType;
1489 UINT16 nlmsgFlags = NLM_F_CREATE;
1490 NdisGetCurrentSystemTime((LARGE_INTEGER *)&currentTime);
1491 UINT8 nfgenFamily = 0;
1492
1493 if (entry->key.dl_type == htons(ETH_TYPE_IPV4)) {
1494 nfgenFamily = AF_INET;
1495 } else if (entry->key.dl_type == htons(ETH_TYPE_IPV6)) {
1496 nfgenFamily = AF_INET6;
1497 }
1498
1499 NlBufInit(&nlBuf, outBuffer, outBufLen);
1500 /* Mimic netfilter */
1501 if (eventType == OVS_EVENT_CT_NEW || eventType == OVS_EVENT_CT_UPDATE) {
1502 nlmsgType = (UINT16) (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_NEW);
1503 } else if (eventType == OVS_EVENT_CT_DELETE) {
1504 nlmsgType = (UINT16) (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_DELETE);
1505 } else {
1506 return STATUS_INVALID_PARAMETER;
1507 }
1508
1509 if (eventType == OVS_EVENT_CT_UPDATE) {
1510 /* In netlink-conntrack.c IPCTNL_MSG_CT_NEW msg type is used to
1511 * differentiate between OVS_EVENT_CT_NEW and OVS_EVENT_CT_UPDATE
1512 * events based on nlmsgFlags, unset it to notify an update event.
1513 */
1514 nlmsgFlags = 0;
1515 }
1516 ok = NlFillOvsMsgForNfGenMsg(&nlBuf, nlmsgType, nlmsgFlags,
1517 nlmsgSeq, nlmsgPid, nfgenFamily,
1518 nfGenVersion, dpIfIndex);
1519 if (!ok) {
1520 return STATUS_INVALID_BUFFER_SIZE;
1521 }
1522
1523 status = MapCtKeyTupleToNl(&nlBuf, CTA_TUPLE_ORIG, &entry->key);
1524 if (status != NDIS_STATUS_SUCCESS) {
1525 return STATUS_UNSUCCESSFUL;
1526 }
1527
1528 status = MapCtKeyTupleToNl(&nlBuf, CTA_TUPLE_REPLY, &entry->rev_key);
1529 if (status != NDIS_STATUS_SUCCESS) {
1530 return STATUS_UNSUCCESSFUL;
1531 }
1532
1533 status = MapCtCounterToNl(&nlBuf, CTA_COUNTERS_ORIG, &entry->key);
1534 if (status != NDIS_STATUS_SUCCESS) {
1535 return STATUS_UNSUCCESSFUL;
1536 }
1537
1538 status = MapCtCounterToNl(&nlBuf, CTA_COUNTERS_REPLY, &entry->rev_key);
1539 if (status != NDIS_STATUS_SUCCESS) {
1540 return STATUS_UNSUCCESSFUL;
1541 }
1542
1543 if (entry->key.zone) {
1544 if (!NlMsgPutTailU16(&nlBuf, CTA_ZONE, htons(entry->key.zone))) {
1545 return STATUS_INVALID_BUFFER_SIZE;
1546 }
1547 }
1548
1549 if (entry->mark) {
1550 if (!NlMsgPutTailU32(&nlBuf, CTA_MARK, htonl(entry->mark))) {
1551 return STATUS_INVALID_BUFFER_SIZE;
1552 }
1553 }
1554
1555 if (entry->labels.ct_labels) {
1556 ok = NlMsgPutTailUnspec(&nlBuf, CTA_LABELS,
1557 (PCHAR)(&entry->labels),
1558 sizeof(entry->labels));
1559 if (!ok) {
1560 return STATUS_INVALID_BUFFER_SIZE;
1561 }
1562 }
1563
1564 if (entry->expiration > currentTime) {
1565 expiration = entry->expiration - currentTime;
1566 timeout = (UINT32) (expiration / CT_INTERVAL_SEC);
1567 if (!NlMsgPutTailU32(&nlBuf, CTA_TIMEOUT, htonl(timeout))) {
1568 return STATUS_INVALID_BUFFER_SIZE;
1569 }
1570 }
1571
1572 if (entry->key.nw_proto == IPPROTO_TCP) {
1573 /* Add ProtoInfo for TCP */
1574 UINT32 offset;
1575 offset = NlMsgStartNested(&nlBuf, CTA_PROTOINFO);
1576 if (!offset) {
1577 return NDIS_STATUS_FAILURE;
1578 }
1579
1580 status = OvsCtMapTcpProtoInfoToNl(&nlBuf, entry);
1581 NlMsgEndNested(&nlBuf, offset);
1582 if (status != NDIS_STATUS_SUCCESS) {
1583 return STATUS_UNSUCCESSFUL;
1584 }
1585 }
1586
1587 /* CTA_STATUS is required but not implemented. Default to 0 */
1588 if (!NlMsgPutTailU32(&nlBuf, CTA_STATUS, 0)) {
1589 return STATUS_INVALID_BUFFER_SIZE;
1590 }
1591
1592 /* Mimic netfilter - nf_conntrack_netlink.c:
1593 *
1594 * int ctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct) {
1595 * NLA_PUT_BE32(skb, CTA_ID, htonl((unsigned long)ct));
1596 * return 0;
1597 * }
1598 *
1599 */
1600 if(!NlMsgPutTailU32(&nlBuf, CTA_ID, htonl((UINT32) entry))) {
1601 return STATUS_INVALID_BUFFER_SIZE;
1602 }
1603
1604 if (entry->timestampStart) {
1605 UINT32 offset;
1606 offset = NlMsgStartNested(&nlBuf, CTA_TIMESTAMP);
1607 if (!offset) {
1608 return NDIS_STATUS_FAILURE;
1609 }
1610 UINT64 start;
1611 start = WindowsTickToUnixSeconds(entry->timestampStart);
1612 start = start * SEC_TO_NANOSEC;
1613 if (!NlMsgPutTailU64(&nlBuf, CTA_TIMESTAMP_START, htonll(start))) {
1614 NlMsgEndNested(&nlBuf, offset);
1615 return STATUS_INVALID_BUFFER_SIZE;
1616 }
1617
1618 NlMsgEndNested(&nlBuf, offset);
1619 }
1620
1621 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuf, 0, 0);
1622 nlMsg->nlmsgLen = NlBufSize(&nlBuf);
1623
1624 return STATUS_SUCCESS;
1625 }
1626
1627 /*
1628 *----------------------------------------------------------------------------
1629 * OvsCtDumpCmdHandler --
1630 * Handler for IPCTNL_MSG_CT_GET command.
1631 *
1632 * XXX - Try to consolidate dump handler patterns around dumpState usage
1633 * The following dumpHandler is similar to one vport.c uses
1634 *----------------------------------------------------------------------------
1635 */
1636 NTSTATUS
1637 OvsCtDumpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1638 UINT32 *replyLen)
1639 {
1640 NTSTATUS rc;
1641 /* Setup Dump Start if it's OVS_WRITE_DEV_OP and return */
1642 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1643 *replyLen = 0;
1644 OvsSetupDumpStart(usrParamsCtx);
1645 return STATUS_SUCCESS;
1646 }
1647
1648 POVS_OPEN_INSTANCE instance =
1649 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1650 POVS_MESSAGE msgIn;
1651
1652 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1653 if (instance->dumpState.ovsMsg == NULL) {
1654 ASSERT(FALSE);
1655 return STATUS_INVALID_DEVICE_STATE;
1656 }
1657
1658 /* Output buffer has been validated while validating read dev op. */
1659 ASSERT(usrParamsCtx->outputBuffer != NULL);
1660 msgIn = instance->dumpState.ovsMsg;
1661 UINT32 inBucket = instance->dumpState.index[0];
1662 UINT32 inIndex = instance->dumpState.index[1];
1663 UINT32 i = CT_HASH_TABLE_SIZE;
1664 UINT32 outIndex = 0;
1665
1666 LOCK_STATE_EX lockState;
1667 NdisAcquireRWLockRead(ovsConntrackLockObj, &lockState, 0);
1668
1669 if (ctTotalEntries) {
1670 for (i = inBucket; i < CT_HASH_TABLE_SIZE; i++) {
1671 PLIST_ENTRY head, link;
1672 head = &ovsConntrackTable[i];
1673 POVS_CT_ENTRY entry = NULL;
1674
1675 outIndex = 0;
1676 LIST_FORALL(head, link) {
1677 /*
1678 * if one or more dumps were previously done on this same
1679 * bucket, inIndex will be > 0, so we'll need to reply with
1680 * the inIndex + 1 ct-entry from the bucket.
1681 */
1682 if (outIndex >= inIndex) {
1683 entry = CONTAINING_RECORD(link, OVS_CT_ENTRY, link);
1684
1685 rc = OvsCreateNlMsgFromCtEntry(entry,
1686 usrParamsCtx->outputBuffer,
1687 usrParamsCtx->outputLength,
1688 OVS_EVENT_CT_NEW,
1689 msgIn->nlMsg.nlmsgSeq,
1690 msgIn->nlMsg.nlmsgPid,
1691 msgIn->nfGenMsg.version,
1692 0);
1693
1694 if (rc != NDIS_STATUS_SUCCESS) {
1695 NdisReleaseRWLock(ovsConntrackLockObj, &lockState);
1696 return STATUS_UNSUCCESSFUL;
1697 }
1698
1699 ++outIndex;
1700 break;
1701 }
1702
1703 ++outIndex;
1704 }
1705
1706 if (entry) {
1707 break;
1708 }
1709
1710 /*
1711 * if no ct-entry was found above, check the next bucket, beginning
1712 * with the first (i.e. index 0) elem from within that bucket
1713 */
1714 inIndex = 0;
1715 }
1716 }
1717 instance->dumpState.index[0] = i;
1718 instance->dumpState.index[1] = outIndex;
1719 NdisReleaseRWLock(ovsConntrackLockObj, &lockState);
1720
1721 /* if i < CT_HASH_TABLE_SIZE => entry was found */
1722 if (i < CT_HASH_TABLE_SIZE) {
1723 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1724 *replyLen = msgOut->nlMsg.nlmsgLen;
1725 } else {
1726 /* if i >= CT_HASH_TABLE_SIZE => entry was not found => dump done */
1727 *replyLen = 0;
1728 FreeUserDumpState(instance);
1729 }
1730
1731 return STATUS_SUCCESS;
1732 }
1733
1734 #pragma warning(pop)