4007a4a930c286c76553d8585257f9ef14b58618
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Ip4Dxe / Ip4Output.c
1 /** @file\r
2 \r
3 Copyright (c) 2005 - 2006, Intel Corporation\r
4 All rights reserved. This program and the accompanying materials\r
5 are licensed and made available under the terms and conditions of the BSD License\r
6 which accompanies this distribution.  The full text of the license may be found at\r
7 http://opensource.org/licenses/bsd-license.php\r
8 \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11 \r
12 Module Name:\r
13 \r
14   Ip4Output.c\r
15 \r
16 Abstract:\r
17 \r
18   Transmit the IP4 packet\r
19 \r
20 \r
21 **/\r
22 \r
23 #include "Ip4Impl.h"\r
24 \r
25 UINT16  mIp4Id;\r
26 \r
27 \r
28 /**\r
29   Prepend an IP4 head to the Packet. It will copy the options and\r
30   build the IP4 header fields. Used for IP4 fragmentation.\r
31 \r
32   @param  Packet               The packet to prepend IP4 header to\r
33   @param  Head                 The caller supplied header. The caller should set\r
34                                the  following header fields: Tos, TotalLen, Id,\r
35                                Fragment, Ttl, Protocol, Src and Dst. All the fields\r
36                                are in host byte order. This function will fill in\r
37                                the Ver, HeadLen, and checksum.\r
38   @param  Option               The orginal IP4 option to copy from\r
39   @param  OptLen               The length of the IP4 option\r
40 \r
41   @retval EFI_BAD_BUFFER_SIZE  There is no enought room in the head space of\r
42                                Packet.\r
43   @retval EFI_SUCCESS          The IP4 header is successfully added to the packet.\r
44 \r
45 **/\r
46 EFI_STATUS\r
47 Ip4PrependHead (\r
48   IN NET_BUF                *Packet,\r
49   IN IP4_HEAD               *Head,\r
50   IN UINT8                  *Option,\r
51   IN UINT32                 OptLen\r
52   )\r
53 {\r
54   UINT32                    HeadLen;\r
55   UINT32                    Len;\r
56   IP4_HEAD                  *PacketHead;\r
57   BOOLEAN                   FirstFragment;\r
58 \r
59   //\r
60   // Prepend the options: first get the option length, then copy it over.\r
61   //\r
62   HeadLen       = 0;\r
63   FirstFragment = IP4_FIRST_FRAGMENT (Head->Fragment);\r
64 \r
65   Ip4CopyOption (Option, OptLen, FirstFragment, NULL, &Len);\r
66 \r
67   HeadLen = IP4_MIN_HEADLEN + Len;\r
68   ASSERT (((Len %4) == 0) && (HeadLen <= IP4_MAX_HEADLEN));\r
69 \r
70   PacketHead = (IP4_HEAD *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);\r
71 \r
72   if (PacketHead == NULL) {\r
73     return EFI_BAD_BUFFER_SIZE;\r
74   }\r
75 \r
76   Ip4CopyOption (Option, OptLen, FirstFragment, (UINT8 *) (PacketHead + 1), &Len);\r
77 \r
78   //\r
79   // Set the head up, convert the host byte order to network byte order\r
80   //\r
81   PacketHead->Ver       = 4;\r
82   PacketHead->HeadLen   = (UINT8) (HeadLen >> 2);\r
83   PacketHead->Tos       = Head->Tos;\r
84   PacketHead->TotalLen  = HTONS (Packet->TotalSize);\r
85   PacketHead->Id        = HTONS (Head->Id);\r
86   PacketHead->Fragment  = HTONS (Head->Fragment);\r
87   PacketHead->Checksum  = 0;\r
88   PacketHead->Ttl       = Head->Ttl;\r
89   PacketHead->Protocol  = Head->Protocol;\r
90   PacketHead->Src       = HTONL (Head->Src);\r
91   PacketHead->Dst       = HTONL (Head->Dst);\r
92   PacketHead->Checksum  = (UINT16) (~NetblockChecksum ((UINT8 *) PacketHead, HeadLen));\r
93 \r
94   Packet->Ip            = PacketHead;\r
95   return EFI_SUCCESS;\r
96 }\r
97 \r
98 \r
99 /**\r
100   Select an interface to send the packet generated in the IP4 driver\r
101   itself, that is, not by the requests of IP4 child's consumer. Such\r
102   packets include the ICMP echo replies, and other ICMP error packets.\r
103 \r
104   @param  IpSb                 The IP4 service that wants to send the packets.\r
105   @param  Dst                  The destination of the packet\r
106   @param  Src                  The source of the packet\r
107 \r
108   @return NULL if no proper interface is found, otherwise the interface that\r
109   @return can be used to send the system packet from.\r
110 \r
111 **/\r
112 IP4_INTERFACE *\r
113 Ip4SelectInterface (\r
114   IN IP4_SERVICE            *IpSb,\r
115   IN IP4_ADDR               Dst,\r
116   IN IP4_ADDR               Src\r
117   )\r
118 {\r
119   IP4_INTERFACE             *IpIf;\r
120   IP4_INTERFACE             *Selected;\r
121   LIST_ENTRY                *Entry;\r
122 \r
123   //\r
124   // Select the interface the Dst is on if one of the connected\r
125   // network. Some IP instance may be configured with 0.0.0.0/0,\r
126   // don't select that interface now.\r
127   //\r
128   IpIf = Ip4FindNet (IpSb, Dst);\r
129 \r
130   if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {\r
131     return IpIf;\r
132   }\r
133 \r
134   //\r
135   // If source is one of the interface address, select it.\r
136   //\r
137   IpIf = Ip4FindInterface (IpSb, Src);\r
138 \r
139   if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {\r
140     return IpIf;\r
141   }\r
142 \r
143   //\r
144   // Select a configured interface as the fall back. Always prefer\r
145   // an interface with non-zero address.\r
146   //\r
147   Selected = NULL;\r
148 \r
149   NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {\r
150     IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);\r
151 \r
152     if (IpIf->Configured && ((Selected == NULL) || (Selected->Ip == 0))) {\r
153       Selected = IpIf;\r
154     }\r
155   }\r
156 \r
157   return Selected;\r
158 }\r
159 \r
160 \r
161 /**\r
162   The default callback function for system generated packet.\r
163   It will free the packet.\r
164 \r
165   @param  Ip4Instance          The IP4 child that issued the transmission.  It most\r
166                                like is NULL.\r
167   @param  Packet               The packet that transmitted.\r
168   @param  IoStatus             The result of the transmission, succeeded or failed.\r
169   @param  LinkFlag             Not used when transmission. check IP4_FRAME_CALLBACK\r
170                                for reference.\r
171   @param  Context              The context provided by us\r
172 \r
173   @return None\r
174 \r
175 **/\r
176 VOID\r
177 Ip4SysPacketSent (\r
178   IP4_PROTOCOL              *Ip4Instance,\r
179   NET_BUF                   *Packet,\r
180   EFI_STATUS                IoStatus,\r
181   UINT32                    LinkFlag,\r
182   VOID                      *Context\r
183   )\r
184 {\r
185   NetbufFree (Packet);\r
186 }\r
187 \r
188 \r
189 /**\r
190   Transmit an IP4 packet. The packet comes either from the IP4\r
191   child's consumer (IpInstance != NULL) or the IP4 driver itself\r
192   (IpInstance == NULL). It will route the packet, fragment it,\r
193   then transmit all the fragments through some interface.\r
194 \r
195   @param  IpSb                 The IP4 service instance to transmit the packet\r
196   @param  IpInstance           The IP4 child that issues the transmission.  It is\r
197                                NULL if the packet is from the system.\r
198   @param  Packet               The user data to send, excluding the IP header.\r
199   @param  Head                 The caller supplied header. The caller should set\r
200                                the  following header fields: Tos, TotalLen, Id, tl,\r
201                                Fragment, Protocol, Src and Dst. All the fields are\r
202                                in host byte  order. This function will fill in the\r
203                                Ver, HeadLen,  Fragment, and checksum. The Fragment\r
204                                only need to include the DF flag. Ip4Output will\r
205                                compute the MF and offset for  you.\r
206   @param  Option               The original option to append to the IP headers\r
207   @param  OptLen               The length of the option\r
208   @param  GateWay              The next hop address to transmit packet to.\r
209                                255.255.255.255 means broadcast.\r
210   @param  Callback             The callback function to issue when transmission\r
211                                completed.\r
212   @param  Context              The opaque context for the callback\r
213 \r
214   @retval EFI_NO_MAPPING       There is no interface to the destination.\r
215   @retval EFI_NOT_FOUND        There is no route to the destination\r
216   @retval EFI_SUCCESS          The packet is successfully transmitted.\r
217   @retval Others               Failed to transmit the packet.\r
218 \r
219 **/\r
220 EFI_STATUS\r
221 Ip4Output (\r
222   IN IP4_SERVICE            *IpSb,\r
223   IN IP4_PROTOCOL           *IpInstance, OPTIONAL\r
224   IN NET_BUF                *Packet,\r
225   IN IP4_HEAD               *Head,\r
226   IN UINT8                  *Option,\r
227   IN UINT32                 OptLen,\r
228   IN IP4_ADDR               GateWay,\r
229   IN IP4_FRAME_CALLBACK     Callback,\r
230   IN VOID                   *Context\r
231   )\r
232 {\r
233   IP4_INTERFACE             *IpIf;\r
234   IP4_ROUTE_CACHE_ENTRY     *CacheEntry;\r
235   IP4_ADDR                  Dest;\r
236   EFI_STATUS                Status;\r
237   NET_BUF                   *Fragment;\r
238   UINT32                    Index;\r
239   UINT32                    HeadLen;\r
240   UINT32                    PacketLen;\r
241   UINT32                    Offset;\r
242   UINT32                    Mtu;\r
243   UINT32                    Num;\r
244 \r
245   //\r
246   // Select an interface/source for system packet, application\r
247   // should select them itself.\r
248   //\r
249   if (IpInstance == NULL) {\r
250     IpIf = Ip4SelectInterface (IpSb, Head->Dst, Head->Src);\r
251   } else {\r
252     IpIf = IpInstance->Interface;\r
253   }\r
254 \r
255   if (IpIf == NULL) {\r
256     return EFI_NO_MAPPING;\r
257   }\r
258 \r
259   if ((Head->Src == IP4_ALLZERO_ADDRESS) && (IpInstance == NULL)) {\r
260     Head->Src = IpIf->Ip;\r
261   }\r
262 \r
263   //\r
264   // Route the packet unless overrided, that is, GateWay isn't zero.\r
265   //\r
266   if (GateWay == IP4_ALLZERO_ADDRESS) {\r
267     Dest = Head->Dst;\r
268 \r
269     if (IP4_IS_BROADCAST (Ip4GetNetCast (Dest, IpIf)) || (Dest == IP4_ALLONE_ADDRESS)) {\r
270       //\r
271       // Set the gateway to local broadcast if the Dest is\r
272       // is the broadcast address for the connected network\r
273       // or it is local broadcast.\r
274       //\r
275       GateWay = IP4_ALLONE_ADDRESS;\r
276 \r
277     } else if (IP4_IS_MULTICAST (Dest)) {\r
278       //\r
279       // Set the gateway to the destination if it is an multicast\r
280       // address. The IP4_INTERFACE won't consult ARP to send local\r
281       // broadcast and multicast.\r
282       //\r
283       GateWay = Head->Dst;\r
284 \r
285     } else {\r
286       //\r
287       // Consult the route table to route the packet\r
288       //\r
289       if (IpInstance == NULL) {\r
290         CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src);\r
291       } else {\r
292         CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src);\r
293       }\r
294 \r
295       if (CacheEntry == NULL) {\r
296         return EFI_NOT_FOUND;\r
297       }\r
298 \r
299       GateWay = CacheEntry->NextHop;\r
300       Ip4FreeRouteCacheEntry (CacheEntry);\r
301     }\r
302   }\r
303 \r
304   //\r
305   // OK, selected the source and route, fragment the packet then send\r
306   // them. Tag each fragment other than the first one as spawn from it.\r
307   //\r
308   Mtu            = IpSb->SnpMode.MaxPacketSize;\r
309   HeadLen        = sizeof (IP4_HEAD) + ((OptLen + 3) & (~0x03));\r
310   Head->Id       = mIp4Id++;\r
311 \r
312   if (Packet->TotalSize + HeadLen > Mtu) {\r
313     //\r
314     // Packet is fragmented from the tail to the head, that is, the\r
315     // first frame sent is the last fragment of the packet. The first\r
316     // fragment is NOT sent in this loop. First compute how many\r
317     // fragments there are.\r
318     //\r
319     Mtu       = (Mtu - HeadLen) & (~0x07);\r
320     Num       = (Packet->TotalSize + Mtu - 1) / Mtu;\r
321 \r
322     //\r
323     // Initialize the packet length and Offset. Other than the last\r
324     // fragment, the packet length equals to MTU. The offset is always\r
325     // aligned to MTU.\r
326     //\r
327     PacketLen = Packet->TotalSize - (Num - 1) * Mtu;\r
328     Offset    = Mtu * (Num - 1);\r
329 \r
330     for (Index = 0; Index < Num - 1; Index++, Offset -= Mtu) {\r
331       Fragment = NetbufGetFragment (Packet, Offset, PacketLen, IP4_MAX_HEADLEN);\r
332 \r
333       if (Fragment == NULL) {\r
334         Status = EFI_OUT_OF_RESOURCES;\r
335         goto ON_ERROR;\r
336       }\r
337 \r
338       //\r
339       // Update the header's fragment. The caller fills the IP4 header\r
340       // fields that are required by Ip4PrependHead except the fragment.\r
341       //\r
342       Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, (Index != 0), Offset);\r
343       Ip4PrependHead (Fragment, Head, Option, OptLen);\r
344 \r
345       //\r
346       // Transmit the fragments, pass the Packet address as the context.\r
347       // So, we can find all the fragments spawned from the Packet by\r
348       // compare the NetBuf and Context to the Packet.\r
349       //\r
350       Status = Ip4SendFrame (\r
351                  IpIf,\r
352                  IpInstance,\r
353                  Fragment,\r
354                  GateWay,\r
355                  Ip4SysPacketSent,\r
356                  Packet\r
357                  );\r
358 \r
359       if (EFI_ERROR (Status)) {\r
360         goto ON_ERROR;\r
361       }\r
362 \r
363       PacketLen = Mtu;\r
364     }\r
365 \r
366     //\r
367     // Trim the already sent data, then adjust the head's fragment field.\r
368     //\r
369     NetbufTrim (Packet, Packet->TotalSize - Mtu, FALSE);\r
370     Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, TRUE, 0);\r
371   }\r
372 \r
373   //\r
374   // Send the first fragment, it is either the orginal packet, or the\r
375   // first fragment of a fragmented packet. It seems that there is a subtle\r
376   // bug here: what if the caller free the packet in Callback and IpIf (or\r
377   // MNP child used by that interface) still holds the fragments and try\r
378   // to access the data? The caller can free the packet if it recycles the\r
379   // consumer's (such as UDP) data in the Callback. But this can't happen.\r
380   // The detailed sequence is:\r
381   // 1. for the packets generated by IP4 driver itself:\r
382   //    The Callback is Ip4SysPacketSent, which is the same as the\r
383   //    fragments' callback. Ip4SysPacketSent simply calls NetbufFree\r
384   //    to release its reference to the packet. So, no problem for\r
385   //    system packets.\r
386   //\r
387   // 2. for the upper layer's packets (use UDP as an example):\r
388   //    UDP requests the IP layer to transmit some data which is\r
389   //    wrapped in an asynchronous token, the token is wrapped\r
390   //    in IP4_TXTOKEN_WRAP by IP4. IP4 also wrap the user's data\r
391   //    in a net buffer, which is Packet we get here. IP4_TXTOKEN_WRAP\r
392   //    is bound with the Packet. It will only be freed when all\r
393   //    the references to Packet have been released. Upon then, the\r
394   //    Packet's OnFree callback will release the IP4_TXTOKEN_WRAP,\r
395   //    and singal the user's recycle event. So, also no problem for\r
396   //    upper layer's packets.\r
397   //\r
398   Ip4PrependHead (Packet, Head, Option, OptLen);\r
399   Status = Ip4SendFrame (IpIf, IpInstance, Packet, GateWay, Callback, Context);\r
400 \r
401   if (EFI_ERROR (Status)) {\r
402     goto ON_ERROR;\r
403   }\r
404 \r
405   return EFI_SUCCESS;\r
406 \r
407 ON_ERROR:\r
408   Ip4CancelPacket (IpIf, Packet, Status);\r
409   return Status;\r
410 }\r
411 \r
412 \r
413 /**\r
414   The filter function to find a packet and all its fragments.\r
415   The packet's fragments have their Context set to the packet.\r
416 \r
417   @param  Frame                The frames hold by the low level interface\r
418   @param  Context              Context to the function, which is the packet.\r
419 \r
420   @retval TRUE                 This is the packet to cancel or its fragments.\r
421   @retval FALSE                This is unrelated packet.\r
422 \r
423 **/\r
424 BOOLEAN\r
425 Ip4CancelPacketFragments (\r
426   IP4_LINK_TX_TOKEN   *Frame,\r
427   VOID                *Context\r
428   )\r
429 {\r
430   if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {\r
431     return TRUE;\r
432   }\r
433 \r
434   return FALSE;\r
435 }\r
436 \r
437 \r
438 /**\r
439   Cancel the Packet and all its fragments.\r
440 \r
441   @param  IpIf                 The interface from which the Packet is sent\r
442   @param  Packet               The Packet to cancel\r
443   @param  IoStatus             The status returns to the sender.\r
444 \r
445   @return None\r
446 \r
447 **/\r
448 VOID\r
449 Ip4CancelPacket (\r
450   IN IP4_INTERFACE    *IpIf,\r
451   IN NET_BUF          *Packet,\r
452   IN EFI_STATUS       IoStatus\r
453   )\r
454 {\r
455   Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet);\r
456 }\r