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