Added networking code from Casper Hornstrup
[reactos.git] / reactos / drivers / net / tcpip / network / ip.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: network/ip.c
5 * PURPOSE: Internet Protocol module
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISIONS:
8 * CSH 01/08-2000 Created
9 */
10 #include <tcpip.h>
11 #include <ip.h>
12 #include <loopback.h>
13 #include <neighbor.h>
14 #include <receive.h>
15 #include <address.h>
16 #include <route.h>
17 #include <icmp.h>
18 #include <pool.h>
19
20
21 KTIMER IPTimer;
22 KDPC IPTimeoutDpc;
23 LIST_ENTRY InterfaceListHead;
24 KSPIN_LOCK InterfaceListLock;
25 LIST_ENTRY NetTableListHead;
26 KSPIN_LOCK NetTableListLock;
27 LIST_ENTRY PrefixListHead;
28 KSPIN_LOCK PrefixListLock;
29 UINT MaxLLHeaderSize; /* Largest maximum header size */
30 UINT MinLLFrameSize; /* Largest minimum frame size */
31 BOOLEAN IPInitialized = FALSE;
32
33 IP_PROTOCOL_HANDLER ProtocolTable[IP_PROTOCOL_TABLE_SIZE];
34
35
36 PADDRESS_ENTRY CreateADE(
37 PIP_INTERFACE IF,
38 PIP_ADDRESS Address,
39 UCHAR Type,
40 PNET_TABLE_ENTRY NTE)
41 /*
42 * FUNCTION: Creates an address entry and binds it to an interface
43 * ARGUMENTS:
44 * IF = Pointer to interface
45 * Address = Pointer to referenced interface address
46 * Type = Type of address (ADE_*)
47 * NTE = Pointer to net table entry
48 * RETURNS:
49 * Pointer to ADE, NULL if there was not enough free resources
50 * NOTES:
51 * The interface lock must be held when called. The address entry
52 * retains a reference to the provided address and NTE. The caller
53 * is responsible for referencing the these before calling.
54 * As long as you have referenced an ADE you can safely use the
55 * address and NTE as the ADE references both
56 */
57 {
58 PADDRESS_ENTRY ADE;
59
60 TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X) Address (0x%X) Type (0x%X) NTE (0x%X).\n",
61 IF, Address, Type, NTE));
62
63 /* Allocate space for an ADE and set it up */
64 ADE = PoolAllocateBuffer(sizeof(ADDRESS_ENTRY));
65 if (!ADE) {
66 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
67 return NULL;
68 }
69
70 ADE->RefCount = 1;
71 ADE->NTE = NTE;
72 ADE->Type = Type;
73 ADE->Address = Address;
74
75 /* Add ADE to the list on the interface */
76 InsertTailList(&IF->ADEListHead, &ADE->ListEntry);
77
78 return ADE;
79 }
80
81
82 VOID DestroyADE(
83 PIP_INTERFACE IF,
84 PADDRESS_ENTRY ADE)
85 /*
86 * FUNCTION: Destroys an address entry
87 * ARGUMENTS:
88 * IF = Pointer to interface
89 * ADE = Pointer to address entry
90 * NOTES:
91 * The interface lock must be held when called
92 */
93 {
94 TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X) ADE (0x%X).\n", IF, ADE));
95
96 /* Unlink the address entry from the list */
97 RemoveEntryList(&ADE->ListEntry);
98
99 /* Dereference the address */
100 DereferenceObject(ADE->Address);
101
102 /* Dereference the NTE */
103 DereferenceObject(ADE->NTE);
104
105 #ifdef DBG
106 ADE->RefCount--;
107
108 if (ADE->RefCount != 0) {
109 TI_DbgPrint(MIN_TRACE, ("Address entry at (0x%X) has (%d) references (should be 0).\n", ADE, ADE->RefCount));
110 }
111 #endif
112
113 /* And free the ADE */
114 PoolFreeBuffer(ADE);
115 TI_DbgPrint(MIN_TRACE, ("Check.\n"));
116 }
117
118
119 VOID DestroyADEs(
120 PIP_INTERFACE IF)
121 /*
122 * FUNCTION: Destroys all address entries on an interface
123 * ARGUMENTS:
124 * IF = Pointer to interface
125 * NOTES:
126 * The interface lock must be held when called
127 */
128 {
129 PLIST_ENTRY CurrentEntry;
130 PLIST_ENTRY NextEntry;
131 PADDRESS_ENTRY Current;
132
133 TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X).\n", IF));
134
135 /* Search the list and remove every ADE we find */
136 CurrentEntry = IF->ADEListHead.Flink;
137 while (CurrentEntry != &IF->ADEListHead) {
138 NextEntry = CurrentEntry->Flink;
139 Current = CONTAINING_RECORD(CurrentEntry, ADDRESS_ENTRY, ListEntry);
140 /* Destroy the ADE */
141 DestroyADE(IF, Current);
142 CurrentEntry = NextEntry;
143 }
144 }
145
146
147 PPREFIX_LIST_ENTRY CreatePLE(
148 PIP_INTERFACE IF,
149 PIP_ADDRESS Prefix,
150 UINT Length)
151 /*
152 * FUNCTION: Creates a prefix list entry and binds it to an interface
153 * ARGUMENTS:
154 * IF = Pointer to interface
155 * Prefix = Pointer to prefix
156 * Length = Length of prefix
157 * RETURNS:
158 * Pointer to PLE, NULL if there was not enough free resources
159 * NOTES:
160 * The prefix list entry retains a reference to the interface and
161 * the provided address. The caller is responsible for providing
162 * these references
163 */
164 {
165 PPREFIX_LIST_ENTRY PLE;
166
167 TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X) Prefix (0x%X) Length (%d).\n", IF, Prefix, Length));
168
169 /* Allocate space for an PLE and set it up */
170 PLE = PoolAllocateBuffer(sizeof(PREFIX_LIST_ENTRY));
171 if (!PLE) {
172 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
173 return NULL;
174 }
175
176 PLE->RefCount = 1;
177 PLE->Interface = IF;
178 PLE->Prefix = Prefix;
179 PLE->PrefixLength = Length;
180
181 /* Add PLE to the global prefix list */
182 ExInterlockedInsertTailList(&PrefixListHead, &PLE->ListEntry, &PrefixListLock);
183
184 return PLE;
185 }
186
187
188 VOID DestroyPLE(
189 PPREFIX_LIST_ENTRY PLE)
190 /*
191 * FUNCTION: Destroys an prefix list entry
192 * ARGUMENTS:
193 * PLE = Pointer to prefix list entry
194 * NOTES:
195 * The prefix list lock must be held when called
196 */
197 {
198 TI_DbgPrint(DEBUG_IP, ("Called. PLE (0x%X).\n", PLE));
199
200 /* Unlink the prefix list entry from the list */
201 RemoveEntryList(&PLE->ListEntry);
202
203 /* Dereference the address */
204 DereferenceObject(PLE->Prefix);
205
206 /* Dereference the interface */
207 DereferenceObject(PLE->Interface);
208
209 #ifdef DBG
210 PLE->RefCount--;
211
212 if (PLE->RefCount != 0) {
213 TI_DbgPrint(MIN_TRACE, ("Prefix list entry at (0x%X) has (%d) references (should be 0).\n", PLE, PLE->RefCount));
214 }
215 #endif
216
217 /* And free the PLE */
218 PoolFreeBuffer(PLE);
219 }
220
221
222 VOID DestroyPLEs(
223 VOID)
224 /*
225 * FUNCTION: Destroys all prefix list entries
226 */
227 {
228 KIRQL OldIrql;
229 PLIST_ENTRY CurrentEntry;
230 PLIST_ENTRY NextEntry;
231 PPREFIX_LIST_ENTRY Current;
232
233 TI_DbgPrint(DEBUG_IP, ("Called.\n"));
234
235 KeAcquireSpinLock(&PrefixListLock, &OldIrql);
236
237 /* Search the list and remove every PLE we find */
238 CurrentEntry = PrefixListHead.Flink;
239 while (CurrentEntry != &PrefixListHead) {
240 NextEntry = CurrentEntry->Flink;
241 Current = CONTAINING_RECORD(CurrentEntry, PREFIX_LIST_ENTRY, ListEntry);
242 /* Destroy the PLE */
243 DestroyPLE(Current);
244 CurrentEntry = NextEntry;
245 }
246 KeReleaseSpinLock(&PrefixListLock, OldIrql);
247 }
248
249
250 PNET_TABLE_ENTRY IPCreateNTE(
251 PIP_INTERFACE IF,
252 PIP_ADDRESS Address,
253 UINT PrefixLength)
254 /*
255 * FUNCTION: Creates a net table entry and binds it to an interface
256 * ARGUMENTS:
257 * IF = Pointer to interface
258 * Address = Pointer to interface address
259 * PrefixLength = Length of prefix
260 * RETURNS:
261 * Pointer to NTE, NULL if there was not enough free resources
262 * NOTES:
263 * The interface lock must be held when called.
264 * The net table entry retains a reference to the interface and
265 * the provided address. The caller is responsible for providing
266 * these references
267 */
268 {
269 PNET_TABLE_ENTRY NTE;
270 PADDRESS_ENTRY ADE;
271
272 TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X) Address (0x%X) PrefixLength (%d).\n", IF, Address, PrefixLength));
273
274 /* Allocate room for an NTE */
275 NTE = PoolAllocateBuffer(sizeof(NET_TABLE_ENTRY));
276 if (!NTE) {
277 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
278 return NULL;
279 }
280
281 NTE->Interface = IF;
282
283 /* One reference is for beeing alive and one reference is for the ADE */
284 NTE->RefCount = 2;
285
286 NTE->Address = Address;
287 /* One reference is for NTE, one reference is given to the
288 address entry, and one reference is given to the prefix
289 list entry */
290 ReferenceObject(Address);
291 ReferenceObject(Address);
292 ReferenceObject(Address);
293
294 /* Create an address entry and add it to the list */
295 ADE = CreateADE(IF, NTE->Address, ADE_UNICAST, NTE);
296 if (!ADE) {
297 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
298 PoolFreeBuffer(NTE);
299 return NULL;
300 }
301
302 /* Create a prefix list entry for unicast address */
303 NTE->PLE = CreatePLE(IF, NTE->Address, PrefixLength);
304 if (!NTE->PLE) {
305 DestroyADE(IF, ADE);
306 PoolFreeBuffer(NTE);
307 return NULL;
308 }
309
310 /* Reference the interface for the prefix list entry */
311 ReferenceObject(IF);
312
313 /* Add NTE to the list on the interface */
314 InsertTailList(&IF->NTEListHead, &NTE->IFListEntry);
315
316 /* Add NTE to the global net table list */
317 ExInterlockedInsertTailList(&NetTableListHead, &NTE->NTListEntry, &NetTableListLock);
318
319 return NTE;
320 }
321
322
323 VOID DestroyNTE(
324 PIP_INTERFACE IF,
325 PNET_TABLE_ENTRY NTE)
326 /*
327 * FUNCTION: Destroys a net table entry
328 * ARGUMENTS:
329 * IF = Pointer to interface
330 * NTE = Pointer to net table entry
331 * NOTES:
332 * The net table list lock must be held when called
333 * The interface lock must be held when called
334 */
335 {
336 KIRQL OldIrql;
337
338 TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X) NTE (0x%X).\n", IF, NTE));
339
340 /* Invalidate the prefix list entry for this NTE */
341 KeAcquireSpinLock(&PrefixListLock, &OldIrql);
342 DestroyPLE(NTE->PLE);
343 KeReleaseSpinLock(&PrefixListLock, OldIrql);
344
345 /* Remove NTE from the interface list */
346 RemoveEntryList(&NTE->IFListEntry);
347 /* Remove NTE from the net table list */
348 RemoveEntryList(&NTE->NTListEntry);
349 /* Dereference the objects that are referenced */
350 DereferenceObject(NTE->Address);
351 DereferenceObject(NTE->Interface);
352 #ifdef DBG
353 NTE->RefCount--;
354
355 if (NTE->RefCount != 0) {
356 TI_DbgPrint(MIN_TRACE, ("Net table entry at (0x%X) has (%d) references (should be 0).\n", NTE, NTE->RefCount));
357 }
358 #endif
359 /* And free the NTE */
360 PoolFreeBuffer(NTE);
361 }
362
363
364 VOID DestroyNTEs(
365 PIP_INTERFACE IF)
366 /*
367 * FUNCTION: Destroys all net table entries on an interface
368 * ARGUMENTS:
369 * IF = Pointer to interface
370 * NOTES:
371 * The net table list lock must be held when called
372 * The interface lock may be held when called
373 */
374 {
375 PLIST_ENTRY CurrentEntry;
376 PLIST_ENTRY NextEntry;
377 PNET_TABLE_ENTRY Current;
378
379 TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X).\n", IF));
380
381 /* Search the list and remove every NTE we find */
382 CurrentEntry = IF->NTEListHead.Flink;
383 while (CurrentEntry != &IF->NTEListHead) {
384 NextEntry = CurrentEntry->Flink;
385 Current = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, IFListEntry);
386 /* Destroy the NTE */
387 DestroyNTE(IF, Current);
388 CurrentEntry = NextEntry;
389 }
390 }
391
392
393 PNET_TABLE_ENTRY IPLocateNTEOnInterface(
394 PIP_INTERFACE IF,
395 PIP_ADDRESS Address,
396 PUINT AddressType)
397 /*
398 * FUNCTION: Locates an NTE on an interface
399 * ARGUMENTS:
400 * IF = Pointer to interface
401 * Address = Pointer to IP address
402 * AddressType = Address of type of IP address
403 * NOTES:
404 * If found, the NTE is referenced for the caller. The caller is
405 * responsible for dereferencing after use
406 * RETURNS:
407 * Pointer to net table entry, NULL if none was found
408 */
409 {
410 KIRQL OldIrql;
411 PLIST_ENTRY CurrentEntry;
412 PADDRESS_ENTRY Current;
413
414 TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X) Address (0x%X) AddressType (0x%X).\n",
415 IF, Address, AddressType));
416
417 KeAcquireSpinLock(&IF->Lock, &OldIrql);
418
419 /* Search the list and return the NTE if found */
420 CurrentEntry = IF->ADEListHead.Flink;
421 while (CurrentEntry != &IF->ADEListHead) {
422 Current = CONTAINING_RECORD(CurrentEntry, ADDRESS_ENTRY, ListEntry);
423 if (AddrIsEqual(Address, Current->Address)) {
424 ReferenceObject(Current->NTE);
425 *AddressType = Current->Type;
426 KeReleaseSpinLock(&IF->Lock, OldIrql);
427 return Current->NTE;
428 }
429 CurrentEntry = CurrentEntry->Flink;
430 }
431
432 KeReleaseSpinLock(&IF->Lock, OldIrql);
433
434 return NULL;
435 }
436
437
438 PNET_TABLE_ENTRY IPLocateNTE(
439 PIP_ADDRESS Address,
440 PUINT AddressType)
441 /*
442 * FUNCTION: Locates an NTE for the network Address is on
443 * ARGUMENTS:
444 * Address = Pointer to an address to find associated NTE of
445 * AddressType = Address of address type
446 * NOTES:
447 * If found the NTE is referenced for the caller. The caller is
448 * responsible for dereferencing after use
449 * RETURNS:
450 * Pointer to NTE if the address was found, NULL if not.
451 */
452 {
453 KIRQL OldIrql;
454 PLIST_ENTRY CurrentEntry;
455 PNET_TABLE_ENTRY Current;
456 PNET_TABLE_ENTRY NTE;
457
458 TI_DbgPrint(DEBUG_IP, ("Called. Address (0x%X) AddressType (0x%X).\n",
459 Address, AddressType));
460
461 KeAcquireSpinLock(&NetTableListLock, &OldIrql);
462
463 /* Search the list and return the NTE if found */
464 CurrentEntry = NetTableListHead.Flink;
465 while (CurrentEntry != &NetTableListHead) {
466 Current = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, NTListEntry);
467 NTE = IPLocateNTEOnInterface(Current->Interface, Address, AddressType);
468 if (NTE) {
469 ReferenceObject(NTE);
470 KeReleaseSpinLock(&NetTableListLock, OldIrql);
471 return NTE;
472 }
473 CurrentEntry = CurrentEntry->Flink;
474 }
475
476 KeReleaseSpinLock(&NetTableListLock, OldIrql);
477
478 return NULL;
479 }
480
481
482 PADDRESS_ENTRY IPLocateADE(
483 PIP_ADDRESS Address,
484 UINT AddressType)
485 /*
486 * FUNCTION: Locates an ADE for the address
487 * ARGUMENTS:
488 * Address = Pointer to an address to find associated ADE of
489 * AddressType = Type of address
490 * RETURNS:
491 * Pointer to ADE if the address was found, NULL if not.
492 * NOTES:
493 * If found the ADE is referenced for the caller. The caller is
494 * responsible for dereferencing after use
495 */
496 {
497 KIRQL OldIrql;
498 PLIST_ENTRY CurrentIFEntry;
499 PLIST_ENTRY CurrentADEEntry;
500 PIP_INTERFACE CurrentIF;
501 PADDRESS_ENTRY CurrentADE;
502
503 TI_DbgPrint(DEBUG_IP, ("Called. Address (0x%X) AddressType (0x%X).\n",
504 Address, AddressType));
505
506 KeAcquireSpinLock(&InterfaceListLock, &OldIrql);
507
508 /* Search the interface list */
509 CurrentIFEntry = InterfaceListHead.Flink;
510 while (CurrentIFEntry != &InterfaceListHead) {
511 CurrentIF = CONTAINING_RECORD(CurrentIFEntry, IP_INTERFACE, ListEntry);
512
513 /* Search the address entry list and return the ADE if found */
514 CurrentADEEntry = CurrentIF->ADEListHead.Flink;
515 while (CurrentADEEntry != &CurrentIF->ADEListHead) {
516 CurrentADE = CONTAINING_RECORD(CurrentADEEntry, ADDRESS_ENTRY, ListEntry);
517 if ((AddrIsEqual(Address, CurrentADE->Address)) &&
518 (CurrentADE->Type == AddressType)) {
519 ReferenceObject(CurrentADE);
520 KeReleaseSpinLock(&InterfaceListLock, OldIrql);
521 return CurrentADE;
522 }
523 CurrentADEEntry = CurrentADEEntry->Flink;
524 }
525 CurrentIFEntry = CurrentIFEntry->Flink;
526 }
527
528 KeReleaseSpinLock(&InterfaceListLock, OldIrql);
529
530 return NULL;
531 }
532
533
534 PADDRESS_ENTRY IPGetDefaultADE(
535 UINT AddressType)
536 /*
537 * FUNCTION: Returns a default address entry
538 * ARGUMENTS:
539 * AddressType = Type of address
540 * RETURNS:
541 * Pointer to ADE if found, NULL if not.
542 * NOTES:
543 * Loopback interface is only considered if it is the only interface.
544 * If found, the address entry is referenced
545 */
546 {
547 KIRQL OldIrql;
548 PLIST_ENTRY CurrentIFEntry;
549 PLIST_ENTRY CurrentADEEntry;
550 PIP_INTERFACE CurrentIF;
551 PADDRESS_ENTRY CurrentADE;
552 #if 0
553 BOOLEAN LoopbackIsRegistered = FALSE;
554 #endif
555 TI_DbgPrint(DEBUG_IP, ("Called. AddressType (0x%X).\n", AddressType));
556
557 KeAcquireSpinLock(&InterfaceListLock, &OldIrql);
558
559 /* Search the interface list */
560 CurrentIFEntry = InterfaceListHead.Flink;
561 while (CurrentIFEntry != &InterfaceListHead) {
562 CurrentIF = CONTAINING_RECORD(CurrentIFEntry, IP_INTERFACE, ListEntry);
563 #if 0
564 if (CurrentIF != Loopback) {
565 #endif
566 /* Search the address entry list and return the first appropriate ADE found */
567 CurrentADEEntry = CurrentIF->ADEListHead.Flink;
568 while (CurrentADEEntry != &CurrentIF->ADEListHead) {
569 CurrentADE = CONTAINING_RECORD(CurrentADEEntry, ADDRESS_ENTRY, ListEntry);
570 if (CurrentADE->Type == AddressType)
571 ReferenceObject(CurrentADE);
572 KeReleaseSpinLock(&InterfaceListLock, OldIrql);
573 return CurrentADE;
574 }
575 CurrentADEEntry = CurrentADEEntry->Flink;
576 #if 0
577 } else
578 LoopbackIsRegistered = TRUE;
579 #endif
580 CurrentIFEntry = CurrentIFEntry->Flink;
581 }
582 #if 0
583 /* No address was found. Use loopback interface if available */
584 if (LoopbackIsRegistered) {
585 CurrentADEEntry = Loopback->ADEListHead.Flink;
586 while (CurrentADEEntry != &Loopback->ADEListHead) {
587 CurrentADE = CONTAINING_RECORD(CurrentADEEntry, ADDRESS_ENTRY, ListEntry);
588 if (CurrentADE->Type == AddressType) {
589 ReferenceObject(CurrentADE);
590 KeReleaseSpinLock(&InterfaceListLock, OldIrql);
591 return CurrentADE;
592 }
593 CurrentADEEntry = CurrentADEEntry->Flink;
594 }
595 }
596 #endif
597 KeReleaseSpinLock(&InterfaceListLock, OldIrql);
598
599 return NULL;
600 }
601
602
603 VOID IPTimeout(
604 PKDPC Dpc,
605 PVOID DeferredContext,
606 PVOID SystemArgument1,
607 PVOID SystemArgument2)
608 /*
609 * FUNCTION: Timeout DPC
610 * ARGUMENTS:
611 * Dpc = Pointer to our DPC object
612 * DeferredContext = Pointer to context information (unused)
613 * SystemArgument1 = Unused
614 * SystemArgument2 = Unused
615 * NOTES:
616 * This routine is dispatched once in a while to do maintainance jobs
617 */
618 {
619 /* Check if datagram fragments have taken too long to assemble */
620 IPDatagramReassemblyTimeout();
621
622 /* Clean possible outdated cached neighbor addresses */
623 NBTimeout();
624 }
625
626
627 VOID IPDispatchProtocol(
628 PNET_TABLE_ENTRY NTE,
629 PIP_PACKET IPPacket)
630 /*
631 * FUNCTION: IP protocol dispatcher
632 * ARGUMENTS:
633 * NTE = Pointer to net table entry which the packet was received on
634 * IPPacket = Pointer to an IP packet that was received
635 * NOTES:
636 * This routine examines the IP header and passes the packet on to the
637 * right upper level protocol receive handler
638 */
639 {
640 UINT Protocol;
641
642 switch (IPPacket->Type) {
643 case IP_ADDRESS_V4:
644 Protocol = ((PIPv4_HEADER)(IPPacket->Header))->Protocol;
645 break;
646 case IP_ADDRESS_V6:
647 /* FIXME: IPv6 adresses not supported */
648 TI_DbgPrint(MIN_TRACE, ("IPv6 datagram discarded.\n"));
649 return;
650 default:
651 Protocol = 0;
652 }
653
654 /* Call the appropriate protocol handler */
655 (*ProtocolTable[Protocol])(NTE, IPPacket);
656 }
657
658
659 PIP_INTERFACE IPCreateInterface(
660 PLLIP_BIND_INFO BindInfo)
661 /*
662 * FUNCTION: Creates an IP interface
663 * ARGUMENTS:
664 * BindInfo = Pointer to link layer to IP binding information
665 * RETURNS:
666 * Pointer to IP_INTERFACE structure, NULL if there was
667 * not enough free resources
668 */
669 {
670 PIP_INTERFACE IF;
671
672 TI_DbgPrint(DEBUG_IP, ("Called. BindInfo (0x%X).\n", BindInfo));
673
674 IF = PoolAllocateBuffer(sizeof(IP_INTERFACE));
675 if (!IF) {
676 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
677 return NULL;
678 }
679
680 IF->RefCount = 1;
681 IF->Context = BindInfo->Context;
682 IF->HeaderSize = BindInfo->HeaderSize;
683 if (IF->HeaderSize > MaxLLHeaderSize)
684 MaxLLHeaderSize = IF->HeaderSize;
685
686 IF->MinFrameSize = BindInfo->MinFrameSize;
687 if (IF->MinFrameSize > MinLLFrameSize)
688 MinLLFrameSize = IF->MinFrameSize;
689
690 IF->MTU = BindInfo->MTU;
691 IF->Address = BindInfo->Address;
692 IF->AddressLength = BindInfo->AddressLength;
693 IF->Transmit = BindInfo->Transmit;
694
695 InitializeListHead(&IF->ADEListHead);
696 InitializeListHead(&IF->NTEListHead);
697
698 KeInitializeSpinLock(&IF->Lock);
699
700 return IF;
701 }
702
703
704 VOID IPDestroyInterface(
705 PIP_INTERFACE IF)
706 /*
707 * FUNCTION: Destroys an IP interface
708 * ARGUMENTS:
709 * IF = Pointer to interface to destroy
710 */
711 {
712 KIRQL OldIrql1;
713 KIRQL OldIrql2;
714
715 TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X).\n", IF));
716
717 KeAcquireSpinLock(&NetTableListLock, &OldIrql1);
718 KeAcquireSpinLock(&IF->Lock, &OldIrql2);
719 DestroyADEs(IF);
720 DestroyNTEs(IF);
721 KeReleaseSpinLock(&IF->Lock, OldIrql2);
722 KeReleaseSpinLock(&NetTableListLock, OldIrql1);
723
724 #ifdef DBG
725 IF->RefCount--;
726
727 if (IF->RefCount != 0) {
728 TI_DbgPrint(MIN_TRACE, ("Interface at (0x%X) has (%d) references (should be 0).\n", IF, IF->RefCount));
729 }
730 #endif
731 PoolFreeBuffer(IF);
732 }
733
734
735 BOOLEAN IPRegisterInterface(
736 PIP_INTERFACE IF)
737 /*
738 * FUNCTION: Registers an IP interface with IP layer
739 * ARGUMENTS:
740 * IF = Pointer to interface to register
741 * RETURNS;
742 * TRUE if interface was successfully registered, FALSE if not
743 */
744 {
745 KIRQL OldIrql;
746 PLIST_ENTRY CurrentEntry;
747 PNET_TABLE_ENTRY Current;
748 PROUTE_CACHE_NODE RCN;
749 PNEIGHBOR_CACHE_ENTRY NCE;
750
751 TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X).\n", IF));
752
753 KeAcquireSpinLock(&IF->Lock, &OldIrql);
754
755 /* Add routes to all NTEs on this interface */
756 CurrentEntry = IF->NTEListHead.Flink;
757 while (CurrentEntry != &IF->NTEListHead) {
758 Current = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, IFListEntry);
759
760 /* Add a permanent neighbor for this NTE */
761 ReferenceObject(Current->Address);
762 NCE = NBAddNeighbor(IF, Current->Address, IF->Address,
763 IF->AddressLength, NUD_PERMANENT);
764 if (!NCE) {
765 TI_DbgPrint(MIN_TRACE, ("Could not create NCE.\n"));
766 DereferenceObject(Current->Address);
767 KeReleaseSpinLock(&IF->Lock, OldIrql);
768 return FALSE;
769 }
770 RCN = RouteAddRouteToDestination(Current->Address, Current, IF, NCE);
771 if (!RCN) {
772 TI_DbgPrint(MIN_TRACE, ("Could not create RCN.\n"));
773 DereferenceObject(Current->Address);
774 KeReleaseSpinLock(&IF->Lock, OldIrql);
775 return FALSE;
776 }
777 /* Don't need this any more since the route cache references the NCE */
778 DereferenceObject(NCE);
779
780 CurrentEntry = CurrentEntry->Flink;
781 }
782
783 /* Add interface to the global interface list */
784 ExInterlockedInsertTailList(&InterfaceListHead, &IF->ListEntry, &InterfaceListLock);
785
786 KeReleaseSpinLock(&IF->Lock, OldIrql);
787
788 return TRUE;
789 }
790
791
792 VOID IPUnregisterInterface(
793 PIP_INTERFACE IF)
794 /*
795 * FUNCTION: Unregisters an IP interface with IP layer
796 * ARGUMENTS:
797 * IF = Pointer to interface to unregister
798 */
799 {
800 KIRQL OldIrql1;
801 KIRQL OldIrql2;
802 KIRQL OldIrql3;
803 PLIST_ENTRY CurrentEntry;
804 PNET_TABLE_ENTRY Current;
805 PNEIGHBOR_CACHE_ENTRY NCE;
806
807 TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X).\n", IF));
808
809 KeAcquireSpinLock(&NetTableListLock, &OldIrql1);
810 KeAcquireSpinLock(&IF->Lock, &OldIrql2);
811
812 /* Remove routes to all NTEs on this interface */
813 CurrentEntry = IF->NTEListHead.Flink;
814 while (CurrentEntry != &IF->NTEListHead) {
815 Current = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, IFListEntry);
816
817 /* Remove NTE from global net table list */
818 RemoveEntryList(&Current->NTListEntry);
819
820 /* Remove all references from route cache to NTE */
821 RouteInvalidateNTE(Current);
822
823 /* Remove permanent NCE, but first we have to find it */
824 NCE = NBLocateNeighbor(Current->Address);
825 if (NCE) {
826 DereferenceObject(NCE);
827 NBRemoveNeighbor(NCE);
828 }
829
830 CurrentEntry = CurrentEntry->Flink;
831 }
832
833 KeAcquireSpinLock(&InterfaceListLock, &OldIrql3);
834 /* Ouch...three spinlocks acquired! Fortunately
835 we don't unregister interfaces very often */
836 RemoveEntryList(&IF->ListEntry);
837 KeReleaseSpinLock(&InterfaceListLock, OldIrql3);
838
839 KeReleaseSpinLock(&IF->Lock, OldIrql2);
840 KeReleaseSpinLock(&NetTableListLock, OldIrql1);
841 }
842
843
844 VOID IPRegisterProtocol(
845 UINT ProtocolNumber,
846 IP_PROTOCOL_HANDLER Handler)
847 /*
848 * FUNCTION: Registers a handler for an IP protocol number
849 * ARGUMENTS:
850 * ProtocolNumber = Internet Protocol number for which to register handler
851 * Handler = Pointer to handler to be called when a packet is received
852 * NOTES:
853 * To unregister a protocol handler, call this function with Handler = NULL
854 */
855 {
856 #ifdef DBG
857 if (ProtocolNumber >= IP_PROTOCOL_TABLE_SIZE)
858 TI_DbgPrint(MIN_TRACE, ("Protocol number is out of range (%d).\n", ProtocolNumber));
859 #endif
860
861 ProtocolTable[ProtocolNumber] = Handler;
862 }
863
864
865 VOID DefaultProtocolHandler(
866 PNET_TABLE_ENTRY NTE,
867 PIP_PACKET IPPacket)
868 /*
869 * FUNCTION: Default handler for Internet protocols
870 * ARGUMENTS:
871 * NTE = Pointer to net table entry which the packet was received on
872 * IPPacket = Pointer to an IP packet that was received
873 */
874 {
875 TI_DbgPrint(MID_TRACE, ("Packet of unknown Internet protocol discarded.\n"));
876 }
877
878
879 NTSTATUS IPStartup(
880 PDRIVER_OBJECT DriverObject,
881 PUNICODE_STRING RegistryPath)
882 /*
883 * FUNCTION: Initializes the IP subsystem
884 * ARGUMENTS:
885 * DriverObject = Pointer to a driver object for this driver
886 * RegistryPath = Our registry node for configuration parameters
887 * RETURNS:
888 * Status of operation
889 */
890 {
891 UINT i;
892 LARGE_INTEGER DueTime;
893
894 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
895
896 MaxLLHeaderSize = 0;
897 MinLLFrameSize = 0;
898
899 /* Start routing subsystem */
900 RouterStartup();
901
902 /* Start route cache subsystem */
903 RouteStartup();
904
905 /* Start neighbor cache subsystem */
906 NBStartup();
907
908 /* Fill the protocol dispatch table with pointers
909 to the default protocol handler */
910 for (i = 0; i < IP_PROTOCOL_TABLE_SIZE; i++)
911 IPRegisterProtocol(i, DefaultProtocolHandler);
912
913 /* Register network level protocol receive handlers */
914 IPRegisterProtocol(IPPROTO_ICMP, ICMPReceive);
915
916 /* Initialize NTE list and protecting lock */
917 InitializeListHead(&NetTableListHead);
918 KeInitializeSpinLock(&NetTableListLock);
919
920 /* Initialize reassembly list and protecting lock */
921 InitializeListHead(&ReassemblyListHead);
922 KeInitializeSpinLock(&ReassemblyListLock);
923
924 /* Initialize the prefix list and protecting lock */
925 InitializeListHead(&PrefixListHead);
926 KeInitializeSpinLock(&PrefixListLock);
927
928 /* Initialize our periodic timer and its associated DPC object. When the
929 timer expires, the IPTimeout deferred procedure call (DPC) is queued */
930 KeInitializeDpc(&IPTimeoutDpc, IPTimeout, NULL);
931 KeInitializeTimer(&IPTimer);
932
933 /* Start the periodic timer with an initial and periodic
934 relative expiration time of IP_TIMEOUT milliseconds */
935 DueTime.QuadPart = -(LONGLONG)IP_TIMEOUT * 10000;
936 KeSetTimerEx(&IPTimer, DueTime, IP_TIMEOUT, &IPTimeoutDpc);
937
938 IPInitialized = TRUE;
939
940 return STATUS_SUCCESS;
941 }
942
943
944 NTSTATUS IPShutdown(
945 VOID)
946 /*
947 * FUNCTION: Shuts down the IP subsystem
948 * RETURNS:
949 * Status of operation
950 */
951 {
952 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
953
954 if (!IPInitialized)
955 return STATUS_SUCCESS;
956
957 /* Cancel timer */
958 KeCancelTimer(&IPTimer);
959
960 /* Shutdown neighbor cache subsystem */
961 NBShutdown();
962
963 /* Shutdown route cache subsystem */
964 RouteShutdown();
965
966 /* Shutdown routing subsystem */
967 RouterShutdown();
968
969 IPFreeReassemblyList();
970
971 /* Clear prefix list */
972 DestroyPLEs();
973
974 IPInitialized = FALSE;
975
976 return STATUS_SUCCESS;
977 }
978
979 /* EOF */