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