Merge aicom-network-branch (without NDIS changes for now)
[reactos.git] / reactos / lib / drivers / ip / network / transmit.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: network/transmit.c
5 * PURPOSE: Internet Protocol transmit routines
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 BOOLEAN PrepareNextFragment(PIPFRAGMENT_CONTEXT IFC);
14 NTSTATUS IPSendFragment(PNDIS_PACKET NdisPacket,
15 PNEIGHBOR_CACHE_ENTRY NCE,
16 PIPFRAGMENT_CONTEXT IFC);
17
18 VOID IPSendComplete
19 (PVOID Context, PNDIS_PACKET NdisPacket, NDIS_STATUS NdisStatus)
20 /*
21 * FUNCTION: IP datagram fragment send completion handler
22 * ARGUMENTS:
23 * Context = Pointer to context information (IP_INTERFACE)
24 * Packet = Pointer to NDIS packet that was sent
25 * NdisStatus = NDIS status of operation
26 * NOTES:
27 * This routine is called when an IP datagram fragment has been sent
28 */
29 {
30 PIPFRAGMENT_CONTEXT IFC = (PIPFRAGMENT_CONTEXT)Context;
31 NTSTATUS Status;
32
33 TI_DbgPrint
34 (MAX_TRACE,
35 ("Called. Context (0x%X) NdisPacket (0x%X) NdisStatus (0x%X)\n",
36 Context, NdisPacket, NdisStatus));
37
38 if (NT_SUCCESS(NdisStatus) && PrepareNextFragment(IFC)) {
39 /* A fragment was prepared for transmission, so send it */
40 Status = IPSendFragment(IFC->NdisPacket, IFC->NCE, IFC);
41 if (!NT_SUCCESS(Status))
42 {
43 FreeNdisPacket(IFC->NdisPacket);
44 IFC->Complete(IFC->Context, IFC->Datagram, Status);
45 ExFreePoolWithTag(IFC, IFC_TAG);
46 }
47 } else {
48 TI_DbgPrint(MAX_TRACE, ("Calling completion handler.\n"));
49
50 /* There are no more fragments to transmit, so call completion handler */
51 FreeNdisPacket(IFC->NdisPacket);
52 IFC->Complete(IFC->Context, IFC->Datagram, NdisStatus);
53 ExFreePoolWithTag(IFC, IFC_TAG);
54 }
55 }
56
57 NTSTATUS IPSendFragment(
58 PNDIS_PACKET NdisPacket,
59 PNEIGHBOR_CACHE_ENTRY NCE,
60 PIPFRAGMENT_CONTEXT IFC)
61 /*
62 * FUNCTION: Sends an IP datagram fragment to a neighbor
63 * ARGUMENTS:
64 * NdisPacket = Pointer to an NDIS packet containing fragment
65 * NCE = Pointer to NCE for first hop to destination
66 * RETURNS:
67 * Status of operation
68 * NOTES:
69 * Lowest level IP send routine
70 */
71 {
72 TI_DbgPrint(MAX_TRACE, ("Called. NdisPacket (0x%X) NCE (0x%X).\n", NdisPacket, NCE));
73
74 TI_DbgPrint(MAX_TRACE, ("NCE->State = %d.\n", NCE->State));
75 return NBQueuePacket(NCE, NdisPacket, IPSendComplete, IFC);
76 }
77
78 BOOLEAN PrepareNextFragment(
79 PIPFRAGMENT_CONTEXT IFC)
80 /*
81 * FUNCTION: Prepares the next fragment of an IP datagram for transmission
82 * ARGUMENTS:
83 * IFC = Pointer to IP fragment context
84 * RETURNS:
85 * TRUE if a fragment was prepared for transmission, FALSE if
86 * there are no more fragments to send
87 */
88 {
89 UINT MaxData;
90 UINT DataSize;
91 PIPv4_HEADER Header;
92 BOOLEAN MoreFragments;
93 USHORT FragOfs;
94
95 TI_DbgPrint(MAX_TRACE, ("Called. IFC (0x%X)\n", IFC));
96
97 if (IFC->BytesLeft > 0) {
98
99 TI_DbgPrint(MAX_TRACE, ("Preparing 1 fragment.\n"));
100
101 MaxData = IFC->PathMTU - IFC->HeaderSize;
102 /* Make fragment a multiplum of 64bit */
103 MaxData -= MaxData % 8;
104 if (IFC->BytesLeft > MaxData) {
105 DataSize = MaxData;
106 MoreFragments = TRUE;
107 } else {
108 DataSize = IFC->BytesLeft;
109 MoreFragments = FALSE;
110 }
111
112 TI_DbgPrint(MID_TRACE,("Copying data from %x to %x (%d)\n",
113 IFC->DatagramData, IFC->Data, DataSize));
114
115 RtlCopyMemory(IFC->Data, IFC->DatagramData, DataSize); // SAFE
116
117 /* Fragment offset is in 8 byte blocks */
118 FragOfs = (USHORT)(IFC->Position / 8);
119
120 if (MoreFragments)
121 FragOfs |= IPv4_MF_MASK;
122 else
123 FragOfs &= ~IPv4_MF_MASK;
124
125 Header = IFC->Header;
126 Header->FlagsFragOfs = WH2N(FragOfs);
127 Header->TotalLength = WH2N((USHORT)(DataSize + IFC->HeaderSize));
128
129 /* FIXME: Handle options */
130
131 /* Calculate checksum of IP header */
132 Header->Checksum = 0;
133 Header->Checksum = (USHORT)IPv4Checksum(Header, IFC->HeaderSize, 0);
134 TI_DbgPrint(MID_TRACE,("IP Check: %x\n", Header->Checksum));
135
136 /* Update pointers */
137 IFC->DatagramData = (PVOID)((ULONG_PTR)IFC->DatagramData + DataSize);
138 IFC->Position += DataSize;
139 IFC->BytesLeft -= DataSize;
140
141 return TRUE;
142 } else {
143 TI_DbgPrint(MAX_TRACE, ("No more fragments.\n"));
144 return FALSE;
145 }
146 }
147
148 NTSTATUS SendFragments(
149 PIP_PACKET IPPacket,
150 PNEIGHBOR_CACHE_ENTRY NCE,
151 UINT PathMTU,
152 PIP_TRANSMIT_COMPLETE Complete,
153 PVOID Context)
154 /*
155 * FUNCTION: Fragments and sends the first fragment of an IP datagram
156 * ARGUMENTS:
157 * IPPacket = Pointer to an IP packet
158 * NCE = Pointer to NCE for first hop to destination
159 * PathMTU = Size of Maximum Transmission Unit of path
160 * RETURNS:
161 * Status of operation
162 * NOTES:
163 * IP datagram is larger than PathMTU when this is called
164 */
165 {
166 PIPFRAGMENT_CONTEXT IFC;
167 NDIS_STATUS NdisStatus;
168 PVOID Data;
169 UINT BufferSize = PathMTU, InSize;
170 PCHAR InData;
171
172 TI_DbgPrint(MAX_TRACE, ("Called. IPPacket (0x%X) NCE (0x%X) PathMTU (%d).\n",
173 IPPacket, NCE, PathMTU));
174
175 /* Make a smaller buffer if we will only send one fragment */
176 GetDataPtr( IPPacket->NdisPacket, 0, &InData, &InSize );
177 if( InSize < BufferSize ) BufferSize = InSize;
178
179 TI_DbgPrint(MAX_TRACE, ("Fragment buffer is %d bytes\n", BufferSize));
180
181 IFC = ExAllocatePoolWithTag(NonPagedPool, sizeof(IPFRAGMENT_CONTEXT), IFC_TAG);
182 if (IFC == NULL)
183 return STATUS_INSUFFICIENT_RESOURCES;
184
185 /* Allocate NDIS packet */
186 NdisStatus = AllocatePacketWithBuffer
187 ( &IFC->NdisPacket, NULL, BufferSize );
188
189 if( !NT_SUCCESS(NdisStatus) ) {
190 ExFreePoolWithTag( IFC, IFC_TAG );
191 return NdisStatus;
192 }
193
194 GetDataPtr( IFC->NdisPacket, 0, (PCHAR *)&Data, &InSize );
195
196 IFC->Header = ((PCHAR)Data);
197 IFC->Datagram = IPPacket->NdisPacket;
198 IFC->DatagramData = ((PCHAR)IPPacket->Header) + IPPacket->HeaderSize;
199 IFC->HeaderSize = IPPacket->HeaderSize;
200 IFC->PathMTU = PathMTU;
201 IFC->NCE = NCE;
202 IFC->Position = 0;
203 IFC->BytesLeft = IPPacket->TotalSize - IPPacket->HeaderSize;
204 IFC->Data = (PVOID)((ULONG_PTR)IFC->Header + IPPacket->HeaderSize);
205 IFC->Complete = Complete;
206 IFC->Context = Context;
207
208 TI_DbgPrint(MID_TRACE,("Copying header from %x to %x (%d)\n",
209 IPPacket->Header, IFC->Header,
210 IPPacket->HeaderSize));
211
212 RtlCopyMemory( IFC->Header, IPPacket->Header, IPPacket->HeaderSize );
213
214 /* Prepare next fragment for transmission and send it */
215
216 if (!PrepareNextFragment(IFC)) {
217 FreeNdisPacket(IFC->NdisPacket);
218 ExFreePoolWithTag(IFC, IFC_TAG);
219 return NDIS_STATUS_FAILURE;
220 }
221
222 if (!NT_SUCCESS((NdisStatus = IPSendFragment(IFC->NdisPacket, NCE, IFC))))
223 {
224 FreeNdisPacket(IFC->NdisPacket);
225 ExFreePoolWithTag(IFC, IFC_TAG);
226 }
227
228 return NdisStatus;
229 }
230
231 NTSTATUS IPSendDatagram(PIP_PACKET IPPacket, PNEIGHBOR_CACHE_ENTRY NCE,
232 PIP_TRANSMIT_COMPLETE Complete, PVOID Context)
233 /*
234 * FUNCTION: Sends an IP datagram to a remote address
235 * ARGUMENTS:
236 * IPPacket = Pointer to an IP packet
237 * RCN = Pointer to route cache node
238 * RETURNS:
239 * Status of operation
240 * NOTES:
241 * This is the highest level IP send routine. It possibly breaks the packet
242 * into two or more fragments before passing it on to the next lower level
243 * send routine (IPSendFragment)
244 */
245 {
246 UINT PacketSize;
247
248 TI_DbgPrint(MAX_TRACE, ("Called. IPPacket (0x%X) NCE (0x%X)\n", IPPacket, NCE));
249
250 DISPLAY_IP_PACKET(IPPacket);
251 /*OskitDumpBuffer( IPPacket->Header, IPPacket->TotalSize );*/
252
253 /* Fetch path MTU now, because it may change */
254 TI_DbgPrint(MID_TRACE,("PathMTU: %d\n", NCE->Interface->MTU));
255
256 NdisQueryPacket(IPPacket->NdisPacket,
257 NULL,
258 NULL,
259 NULL,
260 &PacketSize);
261
262 NCE->Interface->Stats.OutBytes += PacketSize;
263
264 return SendFragments(IPPacket, NCE, NCE->Interface->MTU,
265 Complete, Context);
266 }
267
268 /* EOF */