]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c
Patch to remove STATIC modifier. This is on longer recommended by EFI Framework codin...
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Ip4Dxe / Ip4Output.c
CommitLineData
772db4bb 1/** @file\r
2\r
3Copyright (c) 2005 - 2006, Intel Corporation\r
4All rights reserved. This program and the accompanying materials\r
5are licensed and made available under the terms and conditions of the BSD License\r
6which accompanies this distribution. The full text of the license may be found at\r
7http://opensource.org/licenses/bsd-license.php\r
8\r
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11\r
12Module Name:\r
13\r
14 Ip4Output.c\r
15\r
16Abstract:\r
17\r
18 Transmit the IP4 packet\r
19\r
20\r
21**/\r
22\r
23#include "Ip4Impl.h"\r
24\r
25UINT16 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
46EFI_STATUS\r
47Ip4PrependHead (\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
687a2e5f 92 PacketHead->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) PacketHead, HeadLen));\r
772db4bb 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
112IP4_INTERFACE *\r
113Ip4SelectInterface (\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
e48e37fc 121 LIST_ENTRY *Entry;\r
772db4bb 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
176VOID\r
177Ip4SysPacketSent (\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
220EFI_STATUS\r
221Ip4Output (\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
84b5c78e 309 HeadLen = sizeof (IP4_HEAD) + ((OptLen + 3) & (~0x03));\r
772db4bb 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
407ON_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
772db4bb 424BOOLEAN\r
425Ip4CancelPacketFragments (\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
448VOID\r
449Ip4CancelPacket (\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