bd0d2942c0f4818cb65947950fcf5fe4fa228be7
[reactos.git] / reactos / drivers / net / tcpip / 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 #include <tcpip.h>
11 #include <transmit.h>
12 #include <routines.h>
13 #include <checksum.h>
14 #include <pool.h>
15 #include <arp.h>
16 #include <lan.h>
17
18
19 BOOLEAN PrepareNextFragment(
20 PIPFRAGMENT_CONTEXT IFC)
21 /*
22 * FUNCTION: Prepares the next fragment of an IP datagram for transmission
23 * ARGUMENTS:
24 * IFC = Pointer to IP fragment context
25 * RETURNS:
26 * TRUE if a fragment was prepared for transmission, FALSE if
27 * there are no more fragments to send
28 */
29 {
30 UINT MaxData;
31 UINT DataSize;
32 PIPv4_HEADER Header;
33 BOOLEAN MoreFragments;
34 USHORT FragOfs;
35
36 TI_DbgPrint(MAX_TRACE, ("Called. IFC (0x%X)\n", IFC));
37
38 if (IFC->BytesLeft != 0) {
39
40 TI_DbgPrint(MAX_TRACE, ("Preparing 1 fragment.\n"));
41
42 MaxData = IFC->PathMTU - IFC->HeaderSize;
43 /* Make fragment a multiplum of 64bit */
44 MaxData -= MaxData % 8;
45 if (IFC->BytesLeft > MaxData) {
46 DataSize = MaxData;
47 MoreFragments = TRUE;
48 } else {
49 DataSize = IFC->BytesLeft;
50 MoreFragments = FALSE;
51 }
52
53 RtlCopyMemory(IFC->Data, IFC->DatagramData, DataSize);
54
55 FragOfs = (USHORT)IFC->Position; // Swap?
56 if (MoreFragments)
57 FragOfs |= IPv4_MF_MASK;
58 else
59 FragOfs &= ~IPv4_MF_MASK;
60
61 Header = IFC->Header;
62 Header->FlagsFragOfs = FragOfs;
63
64 /* FIXME: Handle options */
65
66 /* Calculate checksum of IP header */
67 Header->Checksum = 0;
68 Header->Checksum = (USHORT)IPv4Checksum(Header, IFC->HeaderSize, 0);
69
70 /* Update pointers */
71 (ULONG_PTR)IFC->DatagramData += DataSize;
72 IFC->Position += DataSize;
73 IFC->BytesLeft -= DataSize;
74
75 return TRUE;
76 } else {
77 TI_DbgPrint(MAX_TRACE, ("No more fragments.\n"));
78 return FALSE;
79 }
80 }
81
82
83 NTSTATUS SendFragments(
84 PIP_PACKET IPPacket,
85 PNEIGHBOR_CACHE_ENTRY NCE,
86 UINT PathMTU)
87 /*
88 * FUNCTION: Fragments and sends the first fragment of an IP datagram
89 * ARGUMENTS:
90 * IPPacket = Pointer to an IP packet
91 * NCE = Pointer to NCE for first hop to destination
92 * PathMTU = Size of Maximum Transmission Unit of path
93 * RETURNS:
94 * Status of operation
95 * NOTES:
96 * IP datagram is larger than PathMTU when this is called
97 */
98 {
99 PIPFRAGMENT_CONTEXT IFC;
100 NDIS_STATUS NdisStatus;
101 PVOID Data;
102
103 TI_DbgPrint(MAX_TRACE, ("Called. IPPacket (0x%X) NCE (0x%X) PathMTU (%d).\n",
104 IPPacket, NCE, PathMTU));
105
106 IFC = ExAllocatePool(NonPagedPool, sizeof(IPFRAGMENT_CONTEXT));
107 if (!IFC)
108 return STATUS_INSUFFICIENT_RESOURCES;
109
110 /* We allocate a buffer for a PathMTU sized packet and reuse
111 it for all fragments */
112 Data = ExAllocatePool(NonPagedPool, MaxLLHeaderSize + PathMTU);
113 if (!IFC->Header) {
114 ExFreePool(IFC);
115 return STATUS_INSUFFICIENT_RESOURCES;
116 }
117
118 /* Allocate NDIS packet */
119 NdisAllocatePacket(&NdisStatus, &IFC->NdisPacket, GlobalPacketPool);
120 if (NdisStatus != NDIS_STATUS_SUCCESS) {
121 ExFreePool(Data);
122 ExFreePool(IFC);
123 return STATUS_INSUFFICIENT_RESOURCES;
124 }
125
126 /* Allocate NDIS buffer */
127 NdisAllocateBuffer(&NdisStatus, &IFC->NdisBuffer,
128 GlobalBufferPool, Data, MaxLLHeaderSize + PathMTU);
129 if (NdisStatus != NDIS_STATUS_SUCCESS) {
130 NdisFreePacket(IFC->NdisPacket);
131 ExFreePool(Data);
132 ExFreePool(IFC);
133 return STATUS_INSUFFICIENT_RESOURCES;
134 }
135
136 /* Link NDIS buffer into packet */
137 NdisChainBufferAtFront(IFC->NdisPacket, IFC->NdisBuffer);
138
139 IFC->Header = (PVOID)((ULONG_PTR)Data + MaxLLHeaderSize);
140 IFC->Datagram = IPPacket->NdisPacket;
141 IFC->DatagramData = IPPacket->Header;
142 IFC->HeaderSize = IPPacket->HeaderSize;
143 IFC->PathMTU = PathMTU;
144 IFC->NCE = NCE;
145 IFC->Position = 0;
146 IFC->BytesLeft = IPPacket->TotalSize - IPPacket->HeaderSize;
147 IFC->Data = (PVOID)((ULONG_PTR)IFC->Header + IPPacket->HeaderSize);
148
149 PC(IFC->NdisPacket)->DLComplete = IPSendComplete;
150 /* Set upper layer completion function to NULL to indicate that
151 this packet is an IP datagram fragment and thus we should
152 check for more fragments to send. If this is NULL the
153 Context field is a pointer to an IPFRAGMENT_CONTEXT structure */
154 PC(IFC->NdisPacket)->Complete = NULL;
155 PC(IFC->NdisPacket)->Context = IFC;
156
157 /* Copy IP datagram header to fragment buffer */
158 RtlCopyMemory(IFC->Header, IPPacket->Header, IPPacket->HeaderSize);
159
160 /* Prepare next fragment for transmission and send it */
161
162 PrepareNextFragment(IFC);
163
164 IPSendFragment(IFC->NdisPacket, NCE);
165
166 return STATUS_SUCCESS;
167 }
168
169
170 VOID IPSendComplete(
171 PVOID Context,
172 PNDIS_PACKET NdisPacket,
173 NDIS_STATUS NdisStatus)
174 /*
175 * FUNCTION: IP datagram fragment send completion handler
176 * ARGUMENTS:
177 * Context = Pointer to context information (IP_INTERFACE)
178 * Packet = Pointer to NDIS packet that was sent
179 * NdisStatus = NDIS status of operation
180 * NOTES:
181 * This routine is called when an IP datagram fragment has been sent
182 */
183 {
184 TI_DbgPrint(MAX_TRACE, ("Called. Context (0x%X) NdisPacket (0x%X) NdisStatus (0x%X)\n",
185 Context, NdisPacket, NdisStatus));
186
187 /* FIXME: Stop sending fragments and cleanup datagram buffers if
188 there was an error */
189
190 if (PC(NdisPacket)->Complete)
191 /* This datagram was only one fragment long so call completion handler now */
192 (*PC(NdisPacket)->Complete)(PC(NdisPacket)->Context, NdisPacket, NdisStatus);
193 else {
194 /* This was one of many fragments of an IP datagram. Prepare
195 next fragment and send it or if there are no more fragments,
196 call upper layer completion routine */
197
198 PIPFRAGMENT_CONTEXT IFC = (PIPFRAGMENT_CONTEXT)PC(NdisPacket)->Context;
199
200 if (PrepareNextFragment(IFC)) {
201 /* A fragment was prepared for transmission, so send it */
202 IPSendFragment(IFC->NdisPacket, IFC->NCE);
203 } else {
204 TI_DbgPrint(MAX_TRACE, ("Calling completion handler.\n"));
205
206 /* There are no more fragments to transmit, so call completion handler */
207 NdisPacket = IFC->Datagram;
208 FreeNdisPacket(IFC->NdisPacket);
209 ExFreePool(IFC);
210 (*PC(NdisPacket)->Complete)(PC(NdisPacket)->Context, NdisPacket, NdisStatus);
211 }
212 }
213 }
214
215
216 NTSTATUS IPSendFragment(
217 PNDIS_PACKET NdisPacket,
218 PNEIGHBOR_CACHE_ENTRY NCE)
219 /*
220 * FUNCTION: Sends an IP datagram fragment to a neighbor
221 * ARGUMENTS:
222 * NdisPacket = Pointer to an NDIS packet containing fragment
223 * NCE = Pointer to NCE for first hop to destination
224 * RETURNS:
225 * Status of operation
226 * NOTES:
227 * Lowest level IP send routine
228 */
229 {
230 TI_DbgPrint(MAX_TRACE, ("Called. NdisPacket (0x%X) NCE (0x%X).\n", NdisPacket, NCE));
231
232 TI_DbgPrint(MAX_TRACE, ("NCE->State = %d.\n", NCE->State));
233
234 switch (NCE->State) {
235 case NUD_PERMANENT:
236 /* Neighbor is always valid */
237 break;
238
239 case NUD_REACHABLE:
240 /* Neighbor is reachable */
241
242 /* FIXME: Set reachable timer */
243
244 break;
245
246 case NUD_STALE:
247 /* Enter delay state and send packet */
248
249 /* FIXME: Enter delay state */
250
251 break;
252
253 case NUD_DELAY:
254 case NUD_PROBE:
255 /* In these states we send the packet and hope the neighbor
256 hasn't changed hardware address */
257 break;
258
259 case NUD_INCOMPLETE:
260 TI_DbgPrint(MAX_TRACE, ("Queueing packet.\n"));
261
262 /* We don't know the hardware address of the first hop to
263 the destination. Queue the packet on the NCE and return */
264 NBQueuePacket(NCE, NdisPacket);
265
266 return STATUS_SUCCESS;
267 default:
268 /* Should not happen */
269 TI_DbgPrint(MIN_TRACE, ("Unknown NCE state.\n"));
270
271 return STATUS_SUCCESS;
272 }
273
274 PC(NdisPacket)->DLComplete = IPSendComplete;
275 (*NCE->Interface->Transmit)(NCE->Interface->Context,
276 NdisPacket,
277 MaxLLHeaderSize,
278 NCE->LinkAddress,
279 LAN_PROTO_IPv4);
280
281 return STATUS_SUCCESS;
282 }
283
284
285 NTSTATUS IPSendDatagram(
286 PIP_PACKET IPPacket,
287 PROUTE_CACHE_NODE RCN)
288 /*
289 * FUNCTION: Sends an IP datagram to a remote address
290 * ARGUMENTS:
291 * IPPacket = Pointer to an IP packet
292 * RCN = Pointer to route cache node
293 * RETURNS:
294 * Status of operation
295 * NOTES:
296 * This is the highest level IP send routine. It possibly breaks the packet
297 * into two or more fragments before passing it on to the next lower level
298 * send routine (IPSendFragment)
299 */
300 {
301 PNEIGHBOR_CACHE_ENTRY NCE;
302 UINT PathMTU;
303
304 TI_DbgPrint(MAX_TRACE, ("Called. IPPacket (0x%X) RCN (0x%X)\n", IPPacket, RCN));
305
306 DISPLAY_IP_PACKET(IPPacket);
307
308 NCE = RCN->NCE;
309
310 #if DBG
311 if (!NCE) {
312 TI_DbgPrint(MIN_TRACE, ("No NCE to use.\n"));
313 FreeNdisPacket(IPPacket->NdisPacket);
314 return STATUS_SUCCESS;
315 }
316 #endif
317
318 /* Fetch path MTU now, because it may change */
319 PathMTU = RCN->PathMTU;
320
321 if (IPPacket->TotalSize > PathMTU) {
322 return SendFragments(IPPacket, NCE, PathMTU);
323 } else {
324 if ((IPPacket->Flags & IP_PACKET_FLAG_RAW) == 0) {
325 /* Calculate checksum of IP header */
326 ((PIPv4_HEADER)IPPacket->Header)->Checksum = 0;
327
328 ((PIPv4_HEADER)IPPacket->Header)->Checksum = (USHORT)
329 IPv4Checksum(IPPacket->Header, IPPacket->HeaderSize, 0);
330
331 TI_DbgPrint(MAX_TRACE, ("Sending packet (length is %d).\n",
332 WN2H(((PIPv4_HEADER)IPPacket->Header)->TotalLength)));
333 } else {
334 TI_DbgPrint(MAX_TRACE, ("Sending raw packet (flags are 0x%X).\n",
335 IPPacket->Flags));
336 }
337
338 return IPSendFragment(IPPacket->NdisPacket, NCE);
339 }
340 }
341
342 /* EOF */