[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)
[reactos.git] / sdk / lib / drivers / ip / network / neighbor.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: network/neighbor.c
5 * PURPOSE: Neighbor address cache
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISIONS:
8 * CSH 01/08-2000 Created
9 */
10
11 #include "precomp.h"
12
13 NEIGHBOR_CACHE_TABLE NeighborCache[NB_HASHMASK + 1];
14
15 VOID NBCompleteSend( PVOID Context,
16 PNDIS_PACKET NdisPacket,
17 NDIS_STATUS Status ) {
18 PNEIGHBOR_PACKET Packet = (PNEIGHBOR_PACKET)Context;
19 TI_DbgPrint(MID_TRACE, ("Called\n"));
20 ASSERT_KM_POINTER(Packet);
21 ASSERT_KM_POINTER(Packet->Complete);
22 Packet->Complete( Packet->Context, Packet->Packet, Status );
23 TI_DbgPrint(MID_TRACE, ("Completed\n"));
24 ExFreePoolWithTag( Packet, NEIGHBOR_PACKET_TAG );
25 TI_DbgPrint(MID_TRACE, ("Freed\n"));
26 }
27
28 VOID NBSendPackets( PNEIGHBOR_CACHE_ENTRY NCE ) {
29 PLIST_ENTRY PacketEntry;
30 PNEIGHBOR_PACKET Packet;
31 UINT HashValue;
32
33 ASSERT(!(NCE->State & NUD_INCOMPLETE));
34
35 HashValue = *(PULONG)(&NCE->Address.Address);
36 HashValue ^= HashValue >> 16;
37 HashValue ^= HashValue >> 8;
38 HashValue ^= HashValue >> 4;
39 HashValue &= NB_HASHMASK;
40
41 /* Send any waiting packets */
42 while ((PacketEntry = ExInterlockedRemoveHeadList(&NCE->PacketQueue,
43 &NeighborCache[HashValue].Lock)) != NULL)
44 {
45 Packet = CONTAINING_RECORD( PacketEntry, NEIGHBOR_PACKET, Next );
46
47 TI_DbgPrint
48 (MID_TRACE,
49 ("PacketEntry: %x, NdisPacket %x\n",
50 PacketEntry, Packet->Packet));
51
52 PC(Packet->Packet)->DLComplete = NBCompleteSend;
53 PC(Packet->Packet)->Context = Packet;
54
55 NCE->Interface->Transmit
56 ( NCE->Interface->Context,
57 Packet->Packet,
58 0,
59 NCE->LinkAddress,
60 LAN_PROTO_IPv4 );
61 }
62 }
63
64 /* Must be called with table lock acquired */
65 VOID NBFlushPacketQueue( PNEIGHBOR_CACHE_ENTRY NCE,
66 NTSTATUS ErrorCode ) {
67 PLIST_ENTRY PacketEntry;
68 PNEIGHBOR_PACKET Packet;
69
70 while( !IsListEmpty(&NCE->PacketQueue) ) {
71 PacketEntry = RemoveHeadList(&NCE->PacketQueue);
72 Packet = CONTAINING_RECORD
73 ( PacketEntry, NEIGHBOR_PACKET, Next );
74
75 ASSERT_KM_POINTER(Packet);
76
77 TI_DbgPrint
78 (MID_TRACE,
79 ("PacketEntry: %x, NdisPacket %x\n",
80 PacketEntry, Packet->Packet));
81
82 ASSERT_KM_POINTER(Packet->Complete);
83 Packet->Complete( Packet->Context,
84 Packet->Packet,
85 ErrorCode );
86
87 ExFreePoolWithTag( Packet, NEIGHBOR_PACKET_TAG );
88 }
89 }
90
91 VOID NBTimeout(VOID)
92 /*
93 * FUNCTION: Neighbor address cache timeout handler
94 * NOTES:
95 * This routine is called by IPTimeout to remove outdated cache
96 * entries.
97 */
98 {
99 UINT i;
100 PNEIGHBOR_CACHE_ENTRY *PrevNCE;
101 PNEIGHBOR_CACHE_ENTRY NCE;
102 NDIS_STATUS Status;
103
104 for (i = 0; i <= NB_HASHMASK; i++) {
105 TcpipAcquireSpinLockAtDpcLevel(&NeighborCache[i].Lock);
106
107 for (PrevNCE = &NeighborCache[i].Cache;
108 (NCE = *PrevNCE) != NULL;) {
109 if (NCE->State & NUD_INCOMPLETE)
110 {
111 /* Solicit for an address */
112 NBSendSolicit(NCE);
113 if (NCE->EventTimer == 0)
114 {
115 NCE->EventCount++;
116 if (NCE->EventCount == ARP_INCOMPLETE_TIMEOUT)
117 {
118 NBFlushPacketQueue(NCE, NDIS_STATUS_NETWORK_UNREACHABLE);
119 NCE->EventCount = 0;
120 }
121 }
122 }
123
124 /* Check if event timer is running */
125 if (NCE->EventTimer > 0) {
126 ASSERT(!(NCE->State & NUD_PERMANENT));
127 NCE->EventCount++;
128
129 if ((NCE->EventCount > ARP_RATE &&
130 NCE->EventCount % ARP_TIMEOUT_RETRANSMISSION == 0) ||
131 (NCE->EventCount == ARP_RATE))
132 {
133 /* We haven't gotten a packet from them in
134 * EventCount seconds so we mark them as stale
135 * and solicit now */
136 NCE->State |= NUD_STALE;
137 NBSendSolicit(NCE);
138 }
139 if (NCE->EventTimer - NCE->EventCount == 0) {
140 /* Unlink and destroy the NCE */
141 *PrevNCE = NCE->Next;
142
143 /* Choose the proper failure status */
144 if (NCE->State & NUD_INCOMPLETE)
145 {
146 /* We couldn't get an address to this IP at all */
147 Status = NDIS_STATUS_HOST_UNREACHABLE;
148 }
149 else
150 {
151 /* This guy was stale for way too long */
152 Status = NDIS_STATUS_REQUEST_ABORTED;
153 }
154
155 NBFlushPacketQueue(NCE, Status);
156
157 ExFreePoolWithTag(NCE, NCE_TAG);
158
159 continue;
160 }
161 }
162 PrevNCE = &NCE->Next;
163 }
164
165 TcpipReleaseSpinLockFromDpcLevel(&NeighborCache[i].Lock);
166 }
167 }
168
169 VOID NBStartup(VOID)
170 /*
171 * FUNCTION: Starts the neighbor cache
172 */
173 {
174 UINT i;
175
176 TI_DbgPrint(DEBUG_NCACHE, ("Called.\n"));
177
178 for (i = 0; i <= NB_HASHMASK; i++) {
179 NeighborCache[i].Cache = NULL;
180 TcpipInitializeSpinLock(&NeighborCache[i].Lock);
181 }
182 }
183
184 VOID NBShutdown(VOID)
185 /*
186 * FUNCTION: Shuts down the neighbor cache
187 */
188 {
189 PNEIGHBOR_CACHE_ENTRY NextNCE;
190 PNEIGHBOR_CACHE_ENTRY CurNCE;
191 KIRQL OldIrql;
192 UINT i;
193
194 TI_DbgPrint(DEBUG_NCACHE, ("Called.\n"));
195
196 /* Remove possible entries from the cache */
197 for (i = 0; i <= NB_HASHMASK; i++)
198 {
199 TcpipAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql);
200
201 CurNCE = NeighborCache[i].Cache;
202 while (CurNCE) {
203 NextNCE = CurNCE->Next;
204
205 /* Flush wait queue */
206 NBFlushPacketQueue( CurNCE, NDIS_STATUS_NOT_ACCEPTED );
207
208 ExFreePoolWithTag(CurNCE, NCE_TAG);
209
210 CurNCE = NextNCE;
211 }
212
213 NeighborCache[i].Cache = NULL;
214
215 TcpipReleaseSpinLock(&NeighborCache[i].Lock, OldIrql);
216 }
217
218 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
219 }
220
221 VOID NBSendSolicit(PNEIGHBOR_CACHE_ENTRY NCE)
222 /*
223 * FUNCTION: Sends a neighbor solicitation message
224 * ARGUMENTS:
225 * NCE = Pointer to NCE of neighbor to solicit
226 * NOTES:
227 * May be called with lock held on NCE's table
228 */
229 {
230 TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
231
232 ARPTransmit(&NCE->Address,
233 (NCE->State & NUD_INCOMPLETE) ? NULL : NCE->LinkAddress,
234 NCE->Interface);
235 }
236
237 VOID NBDestroyNeighborsForInterface(PIP_INTERFACE Interface)
238 {
239 KIRQL OldIrql;
240 PNEIGHBOR_CACHE_ENTRY *PrevNCE;
241 PNEIGHBOR_CACHE_ENTRY NCE;
242 ULONG i;
243
244 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
245 for (i = 0; i <= NB_HASHMASK; i++)
246 {
247 TcpipAcquireSpinLockAtDpcLevel(&NeighborCache[i].Lock);
248
249 for (PrevNCE = &NeighborCache[i].Cache;
250 (NCE = *PrevNCE) != NULL;)
251 {
252 if (NCE->Interface == Interface)
253 {
254 /* Unlink and destroy the NCE */
255 *PrevNCE = NCE->Next;
256
257 NBFlushPacketQueue(NCE, NDIS_STATUS_REQUEST_ABORTED);
258 ExFreePoolWithTag(NCE, NCE_TAG);
259
260 continue;
261 }
262 else
263 {
264 PrevNCE = &NCE->Next;
265 }
266 }
267
268 TcpipReleaseSpinLockFromDpcLevel(&NeighborCache[i].Lock);
269 }
270 KeLowerIrql(OldIrql);
271 }
272
273 PNEIGHBOR_CACHE_ENTRY NBAddNeighbor(
274 PIP_INTERFACE Interface,
275 PIP_ADDRESS Address,
276 PVOID LinkAddress,
277 UINT LinkAddressLength,
278 UCHAR State,
279 UINT EventTimer)
280 /*
281 * FUNCTION: Adds a neighbor to the neighbor cache
282 * ARGUMENTS:
283 * Interface = Pointer to interface
284 * Address = Pointer to IP address
285 * LinkAddress = Pointer to link address (may be NULL)
286 * LinkAddressLength = Length of link address
287 * State = State of NCE
288 * RETURNS:
289 * Pointer to NCE, NULL there is not enough free resources
290 * NOTES:
291 * The NCE if referenced for the caller if created. The NCE retains
292 * a reference to the IP address if it is created, the caller is
293 * responsible for providing this reference
294 */
295 {
296 PNEIGHBOR_CACHE_ENTRY NCE;
297 ULONG HashValue;
298 KIRQL OldIrql;
299
300 TI_DbgPrint
301 (DEBUG_NCACHE,
302 ("Called. Interface (0x%X) Address (0x%X) "
303 "LinkAddress (0x%X) LinkAddressLength (%d) State (0x%X)\n",
304 Interface, Address, LinkAddress, LinkAddressLength, State));
305
306 NCE = ExAllocatePoolWithTag
307 (NonPagedPool, sizeof(NEIGHBOR_CACHE_ENTRY) + LinkAddressLength,
308 NCE_TAG);
309 if (NCE == NULL)
310 {
311 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
312 return NULL;
313 }
314
315 NCE->Interface = Interface;
316 NCE->Address = *Address;
317 NCE->LinkAddressLength = LinkAddressLength;
318 NCE->LinkAddress = (PVOID)&NCE[1];
319 if( LinkAddress )
320 RtlCopyMemory(NCE->LinkAddress, LinkAddress, LinkAddressLength);
321 else
322 memset(NCE->LinkAddress, 0xff, LinkAddressLength);
323 NCE->State = State;
324 NCE->EventTimer = EventTimer;
325 NCE->EventCount = 0;
326 InitializeListHead( &NCE->PacketQueue );
327
328 TI_DbgPrint(MID_TRACE,("NCE: %x\n", NCE));
329
330 HashValue = *(PULONG)&Address->Address;
331 HashValue ^= HashValue >> 16;
332 HashValue ^= HashValue >> 8;
333 HashValue ^= HashValue >> 4;
334 HashValue &= NB_HASHMASK;
335
336 TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
337
338 NCE->Next = NeighborCache[HashValue].Cache;
339 NeighborCache[HashValue].Cache = NCE;
340
341 TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
342
343 return NCE;
344 }
345
346 VOID NBUpdateNeighbor(
347 PNEIGHBOR_CACHE_ENTRY NCE,
348 PVOID LinkAddress,
349 UCHAR State)
350 /*
351 * FUNCTION: Update link address information in NCE
352 * ARGUMENTS:
353 * NCE = Pointer to NCE to update
354 * LinkAddress = Pointer to link address
355 * State = State of NCE
356 * NOTES:
357 * The link address and state is updated. Any waiting packets are sent
358 */
359 {
360 KIRQL OldIrql;
361 UINT HashValue;
362
363 TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X) LinkAddress (0x%X) State (0x%X).\n", NCE, LinkAddress, State));
364
365 HashValue = *(PULONG)(&NCE->Address.Address);
366 HashValue ^= HashValue >> 16;
367 HashValue ^= HashValue >> 8;
368 HashValue ^= HashValue >> 4;
369 HashValue &= NB_HASHMASK;
370
371 TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
372
373 RtlCopyMemory(NCE->LinkAddress, LinkAddress, NCE->LinkAddressLength);
374 NCE->State = State;
375 NCE->EventCount = 0;
376
377 TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
378
379 if( !(NCE->State & NUD_INCOMPLETE) )
380 {
381 if (NCE->EventTimer) NCE->EventTimer = ARP_COMPLETE_TIMEOUT;
382 NBSendPackets( NCE );
383 }
384 }
385
386 VOID
387 NBResetNeighborTimeout(PIP_ADDRESS Address)
388 {
389 KIRQL OldIrql;
390 UINT HashValue;
391 PNEIGHBOR_CACHE_ENTRY NCE;
392
393 TI_DbgPrint(DEBUG_NCACHE, ("Resetting NCE timout for 0x%s\n", A2S(Address)));
394
395 HashValue = *(PULONG)(&Address->Address);
396 HashValue ^= HashValue >> 16;
397 HashValue ^= HashValue >> 8;
398 HashValue ^= HashValue >> 4;
399 HashValue &= NB_HASHMASK;
400
401 TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
402
403 for (NCE = NeighborCache[HashValue].Cache;
404 NCE != NULL;
405 NCE = NCE->Next)
406 {
407 if (AddrIsEqual(Address, &NCE->Address))
408 {
409 NCE->EventCount = 0;
410 break;
411 }
412 }
413
414 TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
415 }
416
417 PNEIGHBOR_CACHE_ENTRY NBLocateNeighbor(
418 PIP_ADDRESS Address,
419 PIP_INTERFACE Interface)
420 /*
421 * FUNCTION: Locates a neighbor in the neighbor cache
422 * ARGUMENTS:
423 * Address = Pointer to IP address
424 * Interface = Pointer to IP interface
425 * RETURNS:
426 * Pointer to NCE, NULL if not found
427 * NOTES:
428 * If the NCE is found, it is referenced. The caller is
429 * responsible for dereferencing it again after use
430 */
431 {
432 PNEIGHBOR_CACHE_ENTRY NCE;
433 UINT HashValue;
434 KIRQL OldIrql;
435 PIP_INTERFACE FirstInterface;
436
437 TI_DbgPrint(DEBUG_NCACHE, ("Called. Address (0x%X).\n", Address));
438
439 HashValue = *(PULONG)&Address->Address;
440 HashValue ^= HashValue >> 16;
441 HashValue ^= HashValue >> 8;
442 HashValue ^= HashValue >> 4;
443 HashValue &= NB_HASHMASK;
444
445 TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
446
447 /* If there's no adapter specified, we'll look for a match on
448 * each one. */
449 if (Interface == NULL)
450 {
451 FirstInterface = GetDefaultInterface();
452 Interface = FirstInterface;
453 }
454 else
455 {
456 FirstInterface = NULL;
457 }
458
459 do
460 {
461 NCE = NeighborCache[HashValue].Cache;
462 while (NCE != NULL)
463 {
464 if (NCE->Interface == Interface &&
465 AddrIsEqual(Address, &NCE->Address))
466 {
467 break;
468 }
469
470 NCE = NCE->Next;
471 }
472
473 if (NCE != NULL)
474 break;
475 }
476 while ((FirstInterface != NULL) &&
477 ((Interface = GetDefaultInterface()) != FirstInterface));
478
479 if ((NCE == NULL) && (FirstInterface != NULL))
480 {
481 /* This time we'll even match loopback NCEs */
482 NCE = NeighborCache[HashValue].Cache;
483 while (NCE != NULL)
484 {
485 if (AddrIsEqual(Address, &NCE->Address))
486 {
487 break;
488 }
489
490 NCE = NCE->Next;
491 }
492 }
493
494 TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
495
496 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
497
498 return NCE;
499 }
500
501 PNEIGHBOR_CACHE_ENTRY NBFindOrCreateNeighbor(
502 PIP_INTERFACE Interface,
503 PIP_ADDRESS Address,
504 BOOLEAN NoTimeout)
505 /*
506 * FUNCTION: Tries to find a neighbor and if unsuccesful, creates a new NCE
507 * ARGUMENTS:
508 * Interface = Pointer to interface to use (in case NCE is not found)
509 * Address = Pointer to IP address
510 * RETURNS:
511 * Pointer to NCE, NULL if there is not enough free resources
512 * NOTES:
513 * The NCE is referenced if found or created. The caller is
514 * responsible for dereferencing it again after use
515 */
516 {
517 PNEIGHBOR_CACHE_ENTRY NCE;
518
519 TI_DbgPrint(DEBUG_NCACHE, ("Called. Interface (0x%X) Address (0x%X).\n", Interface, Address));
520
521 NCE = NBLocateNeighbor(Address, Interface);
522 if (NCE == NULL)
523 {
524 TI_DbgPrint(MID_TRACE,("BCAST: %s\n", A2S(&Interface->Broadcast)));
525 if( AddrIsEqual(Address, &Interface->Broadcast) ||
526 AddrIsUnspecified(Address) ) {
527 TI_DbgPrint(MID_TRACE,("Packet targeted at broadcast addr\n"));
528 NCE = NBAddNeighbor(Interface, Address, NULL,
529 Interface->AddressLength, NUD_PERMANENT, 0);
530 } else {
531 NCE = NBAddNeighbor(Interface, Address, NULL,
532 Interface->AddressLength, NUD_INCOMPLETE, NoTimeout ? 0 : ARP_INCOMPLETE_TIMEOUT);
533 if (!NCE) return NULL;
534 NBSendSolicit(NCE);
535 }
536 }
537
538 return NCE;
539 }
540
541 BOOLEAN NBQueuePacket(
542 PNEIGHBOR_CACHE_ENTRY NCE,
543 PNDIS_PACKET NdisPacket,
544 PNEIGHBOR_PACKET_COMPLETE PacketComplete,
545 PVOID PacketContext)
546 /*
547 * FUNCTION: Queues a packet on an NCE for later transmission
548 * ARGUMENTS:
549 * NCE = Pointer to NCE to queue packet on
550 * NdisPacket = Pointer to NDIS packet to queue
551 * RETURNS:
552 * TRUE if the packet was successfully queued, FALSE if not
553 */
554 {
555 KIRQL OldIrql;
556 PNEIGHBOR_PACKET Packet;
557 UINT HashValue;
558
559 TI_DbgPrint
560 (DEBUG_NCACHE,
561 ("Called. NCE (0x%X) NdisPacket (0x%X).\n", NCE, NdisPacket));
562
563 Packet = ExAllocatePoolWithTag( NonPagedPool, sizeof(NEIGHBOR_PACKET),
564 NEIGHBOR_PACKET_TAG );
565 if( !Packet ) return FALSE;
566
567 /* FIXME: Should we limit the number of queued packets? */
568
569 HashValue = *(PULONG)(&NCE->Address.Address);
570 HashValue ^= HashValue >> 16;
571 HashValue ^= HashValue >> 8;
572 HashValue ^= HashValue >> 4;
573 HashValue &= NB_HASHMASK;
574
575 TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
576
577 Packet->Complete = PacketComplete;
578 Packet->Context = PacketContext;
579 Packet->Packet = NdisPacket;
580 InsertTailList( &NCE->PacketQueue, &Packet->Next );
581
582 TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
583
584 if( !(NCE->State & NUD_INCOMPLETE) )
585 NBSendPackets( NCE );
586
587 return TRUE;
588 }
589
590 VOID NBRemoveNeighbor(
591 PNEIGHBOR_CACHE_ENTRY NCE)
592 /*
593 * FUNCTION: Removes a neighbor from the neighbor cache
594 * ARGUMENTS:
595 * NCE = Pointer to NCE to remove from cache
596 * NOTES:
597 * The NCE must be in a safe state
598 */
599 {
600 PNEIGHBOR_CACHE_ENTRY *PrevNCE;
601 PNEIGHBOR_CACHE_ENTRY CurNCE;
602 ULONG HashValue;
603 KIRQL OldIrql;
604
605 TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
606
607 HashValue = *(PULONG)(&NCE->Address.Address);
608 HashValue ^= HashValue >> 16;
609 HashValue ^= HashValue >> 8;
610 HashValue ^= HashValue >> 4;
611 HashValue &= NB_HASHMASK;
612
613 TcpipAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
614
615 /* Search the list and remove the NCE from the list if found */
616 for (PrevNCE = &NeighborCache[HashValue].Cache;
617 (CurNCE = *PrevNCE) != NULL;
618 PrevNCE = &CurNCE->Next)
619 {
620 if (CurNCE == NCE)
621 {
622 /* Found it, now unlink it from the list */
623 *PrevNCE = CurNCE->Next;
624
625 NBFlushPacketQueue( CurNCE, NDIS_STATUS_REQUEST_ABORTED );
626 ExFreePoolWithTag(CurNCE, NCE_TAG);
627
628 break;
629 }
630 }
631
632 TcpipReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
633 }
634
635 ULONG NBCopyNeighbors
636 (PIP_INTERFACE Interface,
637 PIPARP_ENTRY ArpTable)
638 {
639 PNEIGHBOR_CACHE_ENTRY CurNCE;
640 KIRQL OldIrql;
641 UINT Size = 0, i;
642
643 for (i = 0; i <= NB_HASHMASK; i++) {
644 TcpipAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql);
645 for( CurNCE = NeighborCache[i].Cache;
646 CurNCE;
647 CurNCE = CurNCE->Next ) {
648 if( CurNCE->Interface == Interface &&
649 !AddrIsEqual( &CurNCE->Address, &CurNCE->Interface->Unicast ) ) {
650 if( ArpTable ) {
651 ArpTable[Size].Index = Interface->Index;
652 ArpTable[Size].AddrSize = CurNCE->LinkAddressLength;
653 RtlCopyMemory
654 (ArpTable[Size].PhysAddr,
655 CurNCE->LinkAddress,
656 CurNCE->LinkAddressLength);
657 ArpTable[Size].LogAddr = CurNCE->Address.Address.IPv4Address;
658 if( CurNCE->State & NUD_PERMANENT )
659 ArpTable[Size].Type = ARP_ENTRY_STATIC;
660 else if( CurNCE->State & NUD_INCOMPLETE )
661 ArpTable[Size].Type = ARP_ENTRY_INVALID;
662 else
663 ArpTable[Size].Type = ARP_ENTRY_DYNAMIC;
664 }
665 Size++;
666 }
667 }
668 TcpipReleaseSpinLock(&NeighborCache[i].Lock, OldIrql);
669 }
670
671 return Size;
672 }