:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / drivers / net / tcpip / transport / tcp / tcp.c
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS TCP/IP protocol driver
4  * FILE:        transport/tcp/tcp.c
5  * PURPOSE:     Transmission Control Protocol
6  * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7  * REVISIONS:
8  *   CSH 01/08-2000 Created
9  */
10 #include <tcpip.h>
11 #include <tcp.h>
12 #include <pool.h>
13 #include <address.h>
14 #include <datagram.h>
15
16
17 BOOLEAN TCPInitialized = FALSE;
18
19
20 NTSTATUS TCPiAddHeaderIPv4(
21   PDATAGRAM_SEND_REQUEST SendRequest,
22   PIP_ADDRESS LocalAddress,
23   USHORT LocalPort,
24   PIP_PACKET IPPacket)
25 /*
26  * FUNCTION: Adds an IPv4 and TCP header to an IP packet
27  * ARGUMENTS:
28  *     SendRequest  = Pointer to send request
29  *     LocalAddress = Pointer to our local address
30  *     LocalPort    = The port we send this segment from
31  *     IPPacket     = Pointer to IP packet
32  * RETURNS:
33  *     Status of operation
34  */
35 {
36   PIPv4_HEADER IPHeader;
37   PTCP_HEADER TCPHeader;
38   PVOID Header;
39         ULONG BufferSize;
40   NDIS_STATUS NdisStatus;
41   PNDIS_BUFFER HeaderBuffer;
42
43         BufferSize = MaxLLHeaderSize + sizeof(IPv4_HEADER) + sizeof(TCP_HEADER);
44   Header     = ExAllocatePool(NonPagedPool, BufferSize);
45   if (!Header)
46     return STATUS_INSUFFICIENT_RESOURCES;
47
48   TI_DbgPrint(MAX_TRACE, ("Allocated %d bytes for headers at 0x%X.\n", BufferSize, Header));
49
50   /* Allocate NDIS buffer for maximum Link level, IP and TCP header */
51   NdisAllocateBuffer(&NdisStatus,
52                      &HeaderBuffer,
53                      GlobalBufferPool,
54                      Header,
55                      BufferSize);
56   if (NdisStatus != NDIS_STATUS_SUCCESS) {
57     ExFreePool(Header);
58     return STATUS_INSUFFICIENT_RESOURCES;
59   }
60
61   /* Chain header at front of NDIS packet */
62   NdisChainBufferAtFront(IPPacket->NdisPacket, HeaderBuffer);
63   
64   IPPacket->Header     = (PVOID)((ULONG_PTR)Header + MaxLLHeaderSize);
65   IPPacket->HeaderSize = 20;
66
67   /* Build IPv4 header */
68   IPHeader = (PIPv4_HEADER)IPPacket->Header;
69   /* Version = 4, Length = 5 DWORDs */
70   IPHeader->VerIHL = 0x45;
71   /* Normal Type-of-Service */
72   IPHeader->Tos = 0;
73   /* Length of header and data */
74   IPHeader->TotalLength = WH2N((USHORT)IPPacket->TotalSize);
75   /* Identification */
76   IPHeader->Id = 0;
77   /* One fragment at offset 0 */
78   IPHeader->FlagsFragOfs = 0;
79   /* Time-to-Live is 128 */
80   IPHeader->Ttl = 128;
81   /* Transmission Control Protocol */
82   IPHeader->Protocol = IPPROTO_TCP;
83   /* Checksum is 0 (for later calculation of this) */
84   IPHeader->Checksum = 0;
85   /* Source address */
86   IPHeader->SrcAddr = LocalAddress->Address.IPv4Address;
87   /* Destination address. FIXME: IPv4 only */
88   IPHeader->DstAddr = SendRequest->RemoteAddress->Address.IPv4Address;
89
90   /* Build TCP header */
91   TCPHeader = (PTCP_HEADER)((ULONG_PTR)IPHeader + sizeof(IPv4_HEADER));
92   /* Port values are already big-endian values */
93   TCPHeader->SourcePort = LocalPort;
94   TCPHeader->DestPort   = SendRequest->RemotePort;
95   TCPHeader->SeqNum     = 0;
96   TCPHeader->AckNum     = 0;
97   TCPHeader->DataOfs    = 0;
98   TCPHeader->Flags      = SendRequest->Flags;
99   TCPHeader->Window     = 0;
100   /* FIXME: Calculate TCP checksum and put it in TCP header */
101   TCPHeader->Checksum   = 0;
102   TCPHeader->Urgent     = 0;
103
104   return STATUS_SUCCESS;
105 }
106
107
108 NTSTATUS TCPiBuildPacket(
109   PVOID Context,
110   PIP_ADDRESS LocalAddress,
111   USHORT LocalPort,
112   PIP_PACKET *IPPacket)
113 /*
114  * FUNCTION: Builds a TCP packet
115  * ARGUMENTS:
116  *     Context      = Pointer to context information (DATAGRAM_SEND_REQUEST)
117  *     LocalAddress = Pointer to our local address
118  *     LocalPort    = The port we send this segment from
119  *     IPPacket     = Address of pointer to IP packet
120  * RETURNS:
121  *     Status of operation
122  * NOTES:
123  *     The ProcotolContext field in the send request structure (pointed to
124  *     by the Context field) contains a pointer to the CONNECTION_ENDPOINT
125  *     structure for the connection
126  */
127 {
128   NTSTATUS Status;
129   PIP_PACKET Packet;
130   NDIS_STATUS NdisStatus;
131   PDATAGRAM_SEND_REQUEST SendRequest = (PDATAGRAM_SEND_REQUEST)Context;
132
133   TI_DbgPrint(MAX_TRACE, ("Called.\n"));
134
135   /* Prepare packet */
136
137   /* FIXME: Assumes IPv4*/
138   Packet = IPCreatePacket(IP_ADDRESS_V4);
139   if (!Packet)
140     return STATUS_INSUFFICIENT_RESOURCES;
141
142   Packet->TotalSize = sizeof(IPv4_HEADER) +
143                       sizeof(TCP_HEADER)  +
144                       SendRequest->BufferSize;
145
146   /* Allocate NDIS packet */
147   NdisAllocatePacket(&NdisStatus, &Packet->NdisPacket, GlobalPacketPool);
148   if (NdisStatus != NDIS_STATUS_SUCCESS) {
149     (*Packet->Free)(Packet);
150     return STATUS_INSUFFICIENT_RESOURCES;
151   }
152
153   switch (SendRequest->RemoteAddress->Type) {
154   case IP_ADDRESS_V4:
155     Status = TCPiAddHeaderIPv4(SendRequest, LocalAddress, LocalPort, Packet);
156     break;
157   case IP_ADDRESS_V6:
158     /* FIXME: Support IPv6 */
159     TI_DbgPrint(MIN_TRACE, ("IPv6 TCP segments are not supported.\n"));
160   default:
161     Status = STATUS_UNSUCCESSFUL;
162     break;
163   }
164   if (!NT_SUCCESS(Status)) {
165     TI_DbgPrint(MIN_TRACE, ("Cannot add TCP header. Status (0x%X)\n", Status));
166     NdisFreePacket(Packet->NdisPacket);
167     (*Packet->Free)(Packet);
168     return Status;
169   }
170
171   /* Chain data after header */
172   NdisChainBufferAtBack(Packet->NdisPacket, SendRequest->Buffer);
173
174   //DISPLAY_IP_PACKET(Packet);
175
176   *IPPacket = Packet;
177
178   return STATUS_SUCCESS;
179 }
180
181
182 VOID TCPiSendRequestComplete(
183   PVOID Context,
184   NTSTATUS Status,
185   ULONG Count)
186 /*
187  * FUNCTION: Completion routine for datagram send requests
188  * ARGUMENTS:
189  *     Context = Pointer to context information (TCP_SEND_REQUEST)
190  *     Status  = Status of the request
191  *     Count   = Number of bytes sent or received
192  */
193 {
194   DATAGRAM_COMPLETION_ROUTINE Complete;
195   PVOID CompleteContext;
196   PTCP_SEND_REQUEST SendRequest = (PTCP_SEND_REQUEST)Context;
197
198   Complete        = SendRequest->Complete;
199   CompleteContext = SendRequest->Context;
200   ExFreePool(SendRequest);
201
202   TI_DbgPrint(MAX_TRACE, ("Calling completion routine.\n"));
203
204   /* Call upper level completion routine */
205   (*Complete)(CompleteContext, Status, Count);
206
207   TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
208 }
209
210
211 VOID TCPTimeout(VOID)
212 /*
213  * FUNCTION: Transmission Control Protocol timeout handler
214  * NOTES:
215  *     This routine is called by IPTimeout to perform several
216  *     maintainance tasks
217  */
218 {
219 }
220
221
222 inline NTSTATUS TCPBuildSendRequest(
223     PTCP_SEND_REQUEST *SendRequest,
224     PDATAGRAM_SEND_REQUEST *DGSendRequest,
225     PCONNECTION_ENDPOINT Connection,
226     DATAGRAM_COMPLETION_ROUTINE Complete,
227     PVOID Context,
228     PNDIS_BUFFER Buffer,
229     DWORD BufferSize,
230     ULONG Flags)
231 /*
232  * FUNCTION: Allocates and intializes a TCP send request
233  * ARGUMENTS:
234  *     SendRequest   = TCP send request
235  *     DGSendRequest = Datagram send request (optional)
236  *     Connection    = Connection endpoint
237  *     Complete      = Completion routine
238  *     Context       = Pointer to context information
239  *     Buffer        = Pointer to NDIS buffer to send
240  *     BufferSize    = Size of Buffer
241  *     Flags         = Protocol specific flags
242  * RETURNS:
243  *     Status of operation
244  */
245 {
246   PDATAGRAM_SEND_REQUEST DGSendReq;
247   NTSTATUS Status;
248
249   Status = BuildTCPSendRequest(
250     SendRequest,
251     Complete,
252     Context,
253     NULL);
254   if (!NT_SUCCESS(Status))
255     return Status;
256
257   Status = BuildDatagramSendRequest(
258     &DGSendReq,                   /* Datagram send request */
259     Connection->RemoteAddress,    /* Address of remote peer */
260     Connection->RemotePort,       /* Port of remote peer */
261     Buffer,                       /* Buffer */
262     BufferSize,                   /* Size of buffer */
263     TCPiSendRequestComplete,      /* Completion function */
264     *SendRequest,                 /* Context for completion function */
265     TCPiBuildPacket,              /* Packet build function */
266     Flags);                       /* Protocol specific flags */
267   if (!NT_SUCCESS(Status)) {
268     ExFreePool(*SendRequest);
269     return Status;
270   }
271
272   if (DGSendRequest)
273     *DGSendRequest = DGSendReq;
274
275   return STATUS_SUCCESS;
276 }
277
278
279 inline NTSTATUS TCPBuildAndTransmitSendRequest(
280     PCONNECTION_ENDPOINT Connection,
281     DATAGRAM_COMPLETION_ROUTINE Complete,
282     PVOID Context,
283     PNDIS_BUFFER Buffer,
284     DWORD BufferSize,
285     ULONG Flags)
286 /*
287  * FUNCTION: Allocates and intializes a TCP send request
288  * ARGUMENTS:
289  *     Connection    = Connection endpoint
290  *     Complete      = Completion routine
291  *     Context       = Pointer to context information
292  *     Buffer        = Pointer to NDIS buffer to send
293  *     BufferSize    = Size of Buffer
294  *     Flags         = Protocol specific flags
295  * RETURNS:
296  *     Status of operation
297  */
298 {
299   PDATAGRAM_SEND_REQUEST DGSendRequest;
300   PTCP_SEND_REQUEST TCPSendRequest;
301   NTSTATUS Status;
302
303   Status = TCPBuildSendRequest(
304     &TCPSendRequest,
305     &DGSendRequest,
306     Connection,                   /* Connection endpoint */
307     Complete,                     /* Completion routine */
308     Context,                      /* Completion routine context */
309     Buffer,                       /* Buffer */
310     BufferSize,                   /* Size of buffer */
311     Flags);                       /* Protocol specific flags */
312   if (!NT_SUCCESS(Status))
313     return Status;
314
315   Status = DGTransmit(
316     Connection->AddressFile,
317     DGSendRequest);
318   if (!NT_SUCCESS(Status)) {
319     ExFreePool(DGSendRequest);
320     ExFreePool(TCPSendRequest);
321     return Status;
322   }
323
324   return STATUS_SUCCESS;
325 }
326
327
328 NTSTATUS TCPConnect(
329   PTDI_REQUEST Request,
330   PTDI_CONNECTION_INFORMATION ConnInfo,
331   PTDI_CONNECTION_INFORMATION ReturnInfo)
332 /*
333  * FUNCTION: Attempts to connect to a remote peer
334  * ARGUMENTS:
335  *     Request    = Pointer to TDI request
336  *     ConnInfo   = Pointer to connection information
337  *     ReturnInfo = Pointer to structure for return information
338  * RETURNS:
339  *     Status of operation
340  * NOTES:
341  *     This is the high level interface for connecting to remote peers
342  */
343 {
344   PDATAGRAM_SEND_REQUEST DGSendRequest;
345   PTCP_SEND_REQUEST TCPSendRequest;
346   PCONNECTION_ENDPOINT Connection;
347   LARGE_INTEGER DueTime;
348   NTSTATUS Status;
349   KIRQL OldIrql;
350
351   TI_DbgPrint(MID_TRACE, ("Called.\n"));
352
353   Connection = Request->Handle.ConnectionContext;
354
355   KeAcquireSpinLock(&Connection->Lock, &OldIrql);
356
357   if (Connection->State != ctClosed) {
358     /* The connection has already been opened so return success */
359     KeReleaseSpinLock(&Connection->Lock, OldIrql);
360     return STATUS_SUCCESS;
361   }
362
363   Connection->LocalAddress = Connection->AddressFile->ADE->Address;
364   Connection->LocalPort    = Connection->AddressFile->Port;
365
366   Status = AddrBuildAddress(
367     (PTA_ADDRESS)ConnInfo->RemoteAddress,
368     &Connection->RemoteAddress,
369     &Connection->RemotePort);
370   if (!NT_SUCCESS(Status)) {
371     KeReleaseSpinLock(&Connection->Lock, OldIrql);
372     return Status;
373   }
374
375   /* Issue SYN segment */
376
377   Status = TCPBuildAndTransmitSendRequest(
378     Connection,                   /* Connection endpoint */
379     Request->RequestNotifyObject, /* Completion routine */
380     Request->RequestContext,      /* Completion routine context */
381     NULL,                         /* Buffer */
382     0,                            /* Size of buffer */
383     SRF_SYN);                     /* Protocol specific flags */
384   if (!NT_SUCCESS(Status)) {
385     KeReleaseSpinLock(&Connection->Lock, OldIrql);
386     ExFreePool(Connection->RemoteAddress);
387     return Status;
388   }
389
390   KeReleaseSpinLock(&Connection->Lock, OldIrql);
391
392   TI_DbgPrint(MAX_TRACE, ("Leaving. Status (0x%X)\n", Status));
393
394   return Status;
395 }
396
397
398 NTSTATUS TCPSendDatagram(
399   PTDI_REQUEST Request,
400   PTDI_CONNECTION_INFORMATION ConnInfo,
401   PNDIS_BUFFER Buffer,
402   ULONG DataSize)
403 /*
404  * FUNCTION: Sends TCP data to a remote address
405  * ARGUMENTS:
406  *     Request   = Pointer to TDI request
407  *     ConnInfo  = Pointer to connection information
408  *     Buffer    = Pointer to NDIS buffer with data
409  *     DataSize  = Size in bytes of data to be sent
410  * RETURNS:
411  *     Status of operation
412  */
413 {
414   return STATUS_SUCCESS;
415 }
416
417
418 VOID TCPReceive(
419     PNET_TABLE_ENTRY NTE,
420     PIP_PACKET IPPacket)
421 /*
422  * FUNCTION: Receives and queues TCP data
423  * ARGUMENTS:
424  *     NTE      = Pointer to net table entry which the packet was received on
425  *     IPPacket = Pointer to an IP packet that was received
426  * NOTES:
427  *     This is the low level interface for receiving TCP data
428  */
429 {
430   PIPv4_HEADER IPv4Header;
431   PADDRESS_FILE AddrFile;
432   PTCP_HEADER TCPHeader;
433   PIP_ADDRESS DstAddress;
434
435   TI_DbgPrint(MAX_TRACE, ("Called.\n"));
436
437   switch (IPPacket->Type) {
438   /* IPv4 packet */
439   case IP_ADDRESS_V4:
440     IPv4Header = IPPacket->Header;
441     DstAddress = &IPPacket->DstAddr;
442     break;
443
444   /* IPv6 packet */
445   case IP_ADDRESS_V6:
446     TI_DbgPrint(MIN_TRACE, ("Discarded IPv6 TCP data (%i bytes).\n",
447       IPPacket->TotalSize));
448
449     /* FIXME: IPv6 is not supported */
450     return;
451
452   default:
453     return;
454   }
455
456   TCPHeader = (PTCP_HEADER)IPPacket->Data;
457
458   TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
459 }
460
461
462 NTSTATUS TCPStartup(
463   VOID)
464 /*
465  * FUNCTION: Initializes the TCP subsystem
466  * RETURNS:
467  *     Status of operation
468  */
469 {
470   /* Register this protocol with IP layer */
471   IPRegisterProtocol(IPPROTO_TCP, TCPReceive);
472
473   TCPInitialized = TRUE;
474
475   return STATUS_SUCCESS;
476 }
477
478
479 NTSTATUS TCPShutdown(
480   VOID)
481 /*
482  * FUNCTION: Shuts down the TCP subsystem
483  * RETURNS:
484  *     Status of operation
485  */
486 {
487   if (!TCPInitialized)
488     return STATUS_SUCCESS;
489
490   /* Deregister this protocol with IP layer */
491   IPRegisterProtocol(IPPROTO_TCP, NULL);
492
493   TCPInitialized = FALSE;
494
495   return STATUS_SUCCESS;
496 }
497
498 /* EOF */