:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / 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); // RobD - why are we trying to free this here?, not allocated yet!
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 */