ee55ce9fd2930f2385f2d61e71aac39c2f5f670a
[reactos.git] / reactos / lib / drivers / ip / transport / tcp / event.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: transport/tcp/event.c
5 * PURPOSE: Transmission Control Protocol -- Events from oskittcp
6 * PROGRAMMERS: Art Yerkes
7 * REVISIONS:
8 * CSH 01/08-2000 Created
9 */
10
11 #include "precomp.h"
12
13 int TCPSocketState(void *ClientData,
14 void *WhichSocket,
15 void *WhichConnection,
16 OSK_UINT NewState ) {
17 PCONNECTION_ENDPOINT Connection = WhichConnection;
18 ULONG OldState;
19 KIRQL OldIrql;
20
21 ASSERT_LOCKED(&TCPLock);
22
23 TI_DbgPrint(MID_TRACE,("Flags: %c%c%c%c\n",
24 NewState & SEL_CONNECT ? 'C' : 'c',
25 NewState & SEL_READ ? 'R' : 'r',
26 NewState & SEL_FIN ? 'F' : 'f',
27 NewState & SEL_ACCEPT ? 'A' : 'a'));
28
29 TI_DbgPrint(DEBUG_TCP,("Called: NewState %x (Conn %x) (Change %x)\n",
30 NewState, Connection,
31 Connection ? Connection->SignalState ^ NewState :
32 NewState));
33
34 if( !Connection ) {
35 TI_DbgPrint(DEBUG_TCP,("Socket closing.\n"));
36 Connection = FileFindConnectionByContext( WhichSocket );
37 if( !Connection )
38 return 0;
39 else
40 TI_DbgPrint(DEBUG_TCP,("Found socket %x\n", Connection));
41 }
42
43 OldState = Connection->SignalState;
44
45 Connection->SignalState |= NewState;
46
47 NewState = HandleSignalledConnection(Connection);
48
49 KeAcquireSpinLock(&SignalledConnectionsLock, &OldIrql);
50 if ((NewState == 0 || NewState == SEL_FIN) &&
51 (OldState != 0 && OldState != SEL_FIN))
52 {
53 RemoveEntryList(&Connection->SignalList);
54 }
55 else if (NewState != 0 && NewState != SEL_FIN)
56 {
57 InsertTailList(&SignalledConnectionsList, &Connection->SignalList);
58 }
59 KeReleaseSpinLock(&SignalledConnectionsLock, OldIrql);
60
61 return 0;
62 }
63
64 void TCPPacketSendComplete( PVOID Context,
65 PNDIS_PACKET NdisPacket,
66 NDIS_STATUS NdisStatus ) {
67 TI_DbgPrint(DEBUG_TCP,("called %x\n", NdisPacket));
68 FreeNdisPacket(NdisPacket);
69 TI_DbgPrint(DEBUG_TCP,("done\n"));
70 }
71
72 #define STRINGIFY(x) #x
73
74 int TCPPacketSend(void *ClientData, OSK_PCHAR data, OSK_UINT len ) {
75 NDIS_STATUS NdisStatus;
76 PNEIGHBOR_CACHE_ENTRY NCE;
77 IP_PACKET Packet = { 0 };
78 IP_ADDRESS RemoteAddress, LocalAddress;
79 PIPv4_HEADER Header;
80
81 ASSERT_LOCKED(&TCPLock);
82
83 if( *data == 0x45 ) { /* IPv4 */
84 Header = (PIPv4_HEADER)data;
85 LocalAddress.Type = IP_ADDRESS_V4;
86 LocalAddress.Address.IPv4Address = Header->SrcAddr;
87 RemoteAddress.Type = IP_ADDRESS_V4;
88 RemoteAddress.Address.IPv4Address = Header->DstAddr;
89 } else {
90 TI_DbgPrint(MIN_TRACE,("Outgoing packet is not IPv4\n"));
91 OskitDumpBuffer( data, len );
92 return OSK_EINVAL;
93 }
94
95 if(!(NCE = RouteGetRouteToDestination( &RemoteAddress ))) {
96 TI_DbgPrint(MIN_TRACE,("No route to %s\n", A2S(&RemoteAddress)));
97 return OSK_EADDRNOTAVAIL;
98 }
99
100 NdisStatus = AllocatePacketWithBuffer( &Packet.NdisPacket, NULL, len );
101
102 if (NdisStatus != NDIS_STATUS_SUCCESS) {
103 TI_DbgPrint(DEBUG_TCP, ("Error from NDIS: %08x\n", NdisStatus));
104 return OSK_ENOBUFS;
105 }
106
107 GetDataPtr( Packet.NdisPacket, 0,
108 (PCHAR *)&Packet.Header, &Packet.ContigSize );
109
110 RtlCopyMemory( Packet.Header, data, len );
111
112 Packet.HeaderSize = sizeof(IPv4_HEADER);
113 Packet.TotalSize = len;
114 Packet.SrcAddr = LocalAddress;
115 Packet.DstAddr = RemoteAddress;
116
117 if (!NT_SUCCESS(IPSendDatagram( &Packet, NCE, TCPPacketSendComplete, NULL )))
118 {
119 FreeNdisPacket(Packet.NdisPacket);
120 return OSK_EINVAL;
121 }
122
123 return 0;
124 }
125
126 int TCPSleep( void *ClientData, void *token, int priority, char *msg,
127 int tmio ) {
128 PSLEEPING_THREAD SleepingThread;
129 LARGE_INTEGER Timeout;
130
131 ASSERT_LOCKED(&TCPLock);
132
133 TI_DbgPrint(DEBUG_TCP,
134 ("Called TSLEEP: tok = %x, pri = %d, wmesg = %s, tmio = %x\n",
135 token, priority, msg, tmio));
136
137 SleepingThread = exAllocatePool( NonPagedPool, sizeof( *SleepingThread ) );
138 if( SleepingThread ) {
139 KeInitializeEvent( &SleepingThread->Event, NotificationEvent, FALSE );
140 SleepingThread->SleepToken = token;
141
142 /* We're going to sleep and need to release the lock, otherwise
143 it's impossible to re-enter oskittcp to deliver the event that's
144 going to wake us */
145 TcpipRecursiveMutexLeave( &TCPLock );
146
147 TcpipAcquireFastMutex( &SleepingThreadsLock );
148 InsertTailList( &SleepingThreadsList, &SleepingThread->Entry );
149 TcpipReleaseFastMutex( &SleepingThreadsLock );
150
151 Timeout.QuadPart = Int32x32To64(tmio, -10000);
152
153 TI_DbgPrint(DEBUG_TCP,("Waiting on %x\n", token));
154 KeWaitForSingleObject( &SleepingThread->Event,
155 Executive,
156 KernelMode,
157 TRUE,
158 (tmio != 0) ? &Timeout : NULL );
159
160 TcpipAcquireFastMutex( &SleepingThreadsLock );
161 RemoveEntryList( &SleepingThread->Entry );
162 TcpipReleaseFastMutex( &SleepingThreadsLock );
163
164 TcpipRecursiveMutexEnter( &TCPLock, TRUE );
165
166 exFreePool( SleepingThread );
167 } else
168 return OSK_ENOBUFS;
169
170 TI_DbgPrint(DEBUG_TCP,("Waiting finished: %x\n", token));
171 return 0;
172 }
173
174 void TCPWakeup( void *ClientData, void *token ) {
175 PLIST_ENTRY Entry;
176 PSLEEPING_THREAD SleepingThread;
177
178 ASSERT_LOCKED(&TCPLock);
179
180 TcpipAcquireFastMutex( &SleepingThreadsLock );
181 Entry = SleepingThreadsList.Flink;
182 while( Entry != &SleepingThreadsList ) {
183 SleepingThread = CONTAINING_RECORD(Entry, SLEEPING_THREAD, Entry);
184 TI_DbgPrint(DEBUG_TCP,("Sleeper @ %x\n", SleepingThread));
185 if( SleepingThread->SleepToken == token ) {
186 TI_DbgPrint(DEBUG_TCP,("Setting event to wake %x\n", token));
187 KeSetEvent( &SleepingThread->Event, IO_NETWORK_INCREMENT, FALSE );
188 }
189 Entry = Entry->Flink;
190 }
191 TcpipReleaseFastMutex( &SleepingThreadsLock );
192 }
193
194 /* Memory management routines
195 *
196 * By far the most requests for memory are either for 128 or 2048 byte blocks,
197 * so we want to satisfy those from lookaside lists. Unfortunately, the
198 * TCPFree() function doesn't pass the size of the block to be freed, so we
199 * need to keep track of it ourselves. We do it by prepending each block with
200 * 4 bytes, indicating if this is a 'L'arge (2048), 'S'mall (128) or 'O'ther
201 * block.
202 */
203
204 /* Set to some non-zero value to get a profile of memory allocation sizes */
205 #define MEM_PROFILE 0
206
207 #define SMALL_SIZE 128
208 #define LARGE_SIZE 2048
209
210 #define SIGNATURE_LARGE 'LLLL'
211 #define SIGNATURE_SMALL 'SSSS'
212 #define SIGNATURE_OTHER 'OOOO'
213 #define TCP_TAG ' PCT'
214
215 static NPAGED_LOOKASIDE_LIST LargeLookasideList;
216 static NPAGED_LOOKASIDE_LIST SmallLookasideList;
217
218 NTSTATUS
219 TCPMemStartup( void )
220 {
221 ExInitializeNPagedLookasideList( &LargeLookasideList,
222 NULL,
223 NULL,
224 0,
225 LARGE_SIZE + sizeof( ULONG ),
226 TCP_TAG,
227 0 );
228 ExInitializeNPagedLookasideList( &SmallLookasideList,
229 NULL,
230 NULL,
231 0,
232 SMALL_SIZE + sizeof( ULONG ),
233 TCP_TAG,
234 0 );
235
236 return STATUS_SUCCESS;
237 }
238
239 void *TCPMalloc( void *ClientData,
240 OSK_UINT Bytes, OSK_PCHAR File, OSK_UINT Line ) {
241 void *v;
242 ULONG Signature;
243
244 ASSERT_LOCKED(&TCPLock);
245
246 #if 0 != MEM_PROFILE
247 static OSK_UINT *Sizes = NULL, *Counts = NULL, ArrayAllocated = 0;
248 static OSK_UINT ArrayUsed = 0, AllocationCount = 0;
249 OSK_UINT i, NewSize, *NewArray;
250 int Found;
251
252 Found = 0;
253 for ( i = 0; i < ArrayUsed && ! Found; i++ ) {
254 Found = ( Sizes[i] == Bytes );
255 if ( Found ) {
256 Counts[i]++;
257 }
258 }
259 if ( ! Found ) {
260 if ( ArrayAllocated <= ArrayUsed ) {
261 NewSize = ( 0 == ArrayAllocated ? 16 : 2 * ArrayAllocated );
262 NewArray = exAllocatePool( NonPagedPool, 2 * NewSize * sizeof( OSK_UINT ) );
263 if ( NULL != NewArray ) {
264 if ( 0 != ArrayAllocated ) {
265 memcpy( NewArray, Sizes,
266 ArrayAllocated * sizeof( OSK_UINT ) );
267 exFreePool( Sizes );
268 memcpy( NewArray + NewSize, Counts,
269 ArrayAllocated * sizeof( OSK_UINT ) );
270 exFreePool( Counts );
271 }
272 Sizes = NewArray;
273 Counts = NewArray + NewSize;
274 ArrayAllocated = NewSize;
275 } else if ( 0 != ArrayAllocated ) {
276 exFreePool( Sizes );
277 exFreePool( Counts );
278 ArrayAllocated = 0;
279 }
280 }
281 if ( ArrayUsed < ArrayAllocated ) {
282 Sizes[ArrayUsed] = Bytes;
283 Counts[ArrayUsed] = 1;
284 ArrayUsed++;
285 }
286 }
287
288 if ( 0 == (++AllocationCount % MEM_PROFILE) ) {
289 TI_DbgPrint(DEBUG_TCP, ("Memory allocation size profile:\n"));
290 for ( i = 0; i < ArrayUsed; i++ ) {
291 TI_DbgPrint(DEBUG_TCP,
292 ("Size %4u Count %5u\n", Sizes[i], Counts[i]));
293 }
294 TI_DbgPrint(DEBUG_TCP, ("End of memory allocation size profile\n"));
295 }
296 #endif /* MEM_PROFILE */
297
298 if ( SMALL_SIZE == Bytes ) {
299 v = ExAllocateFromNPagedLookasideList( &SmallLookasideList );
300 Signature = SIGNATURE_SMALL;
301 } else if ( LARGE_SIZE == Bytes ) {
302 v = ExAllocateFromNPagedLookasideList( &LargeLookasideList );
303 Signature = SIGNATURE_LARGE;
304 } else {
305 v = ExAllocatePool( NonPagedPool, Bytes + sizeof(ULONG) );
306 Signature = SIGNATURE_OTHER;
307 }
308 if( v ) {
309 *((ULONG *) v) = Signature;
310 v = (void *)((char *) v + sizeof(ULONG));
311 TrackWithTag( FOURCC('f','b','s','d'), v, (PCHAR)File, Line );
312 }
313
314 return v;
315 }
316
317 void TCPFree( void *ClientData,
318 void *data, OSK_PCHAR File, OSK_UINT Line ) {
319 ULONG Signature;
320
321 ASSERT_LOCKED(&TCPLock);
322
323 UntrackFL( (PCHAR)File, Line, data, FOURCC('f','b','s','d') );
324 data = (void *)((char *) data - sizeof(ULONG));
325 Signature = *((ULONG *) data);
326 if ( SIGNATURE_SMALL == Signature ) {
327 ExFreeToNPagedLookasideList( &SmallLookasideList, data );
328 } else if ( SIGNATURE_LARGE == Signature ) {
329 ExFreeToNPagedLookasideList( &LargeLookasideList, data );
330 } else if ( SIGNATURE_OTHER == Signature ) {
331 ExFreePool( data );
332 } else {
333 ASSERT( FALSE );
334 }
335 }
336
337 void
338 TCPMemShutdown( void )
339 {
340 ExDeleteNPagedLookasideList( &SmallLookasideList );
341 ExDeleteNPagedLookasideList( &LargeLookasideList );
342 }