| /** @file | |
| Copyright (c) 2005 - 2006, Intel Corporation | |
| All rights reserved. This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| Module Name: | |
| Ip4Output.c | |
| Abstract: | |
| Transmit the IP4 packet | |
| **/ | |
| #include "Ip4Impl.h" | |
| UINT16 mIp4Id; | |
| /** | |
| Prepend an IP4 head to the Packet. It will copy the options and | |
| build the IP4 header fields. Used for IP4 fragmentation. | |
| @param Packet The packet to prepend IP4 header to | |
| @param Head The caller supplied header. The caller should set | |
| the following header fields: Tos, TotalLen, Id, | |
| Fragment, Ttl, Protocol, Src and Dst. All the fields | |
| are in host byte order. This function will fill in | |
| the Ver, HeadLen, and checksum. | |
| @param Option The orginal IP4 option to copy from | |
| @param OptLen The length of the IP4 option | |
| @retval EFI_BAD_BUFFER_SIZE There is no enought room in the head space of | |
| Packet. | |
| @retval EFI_SUCCESS The IP4 header is successfully added to the packet. | |
| **/ | |
| EFI_STATUS | |
| Ip4PrependHead ( | |
| IN NET_BUF *Packet, | |
| IN IP4_HEAD *Head, | |
| IN UINT8 *Option, | |
| IN UINT32 OptLen | |
| ) | |
| { | |
| UINT32 HeadLen; | |
| UINT32 Len; | |
| IP4_HEAD *PacketHead; | |
| BOOLEAN FirstFragment; | |
| // | |
| // Prepend the options: first get the option length, then copy it over. | |
| // | |
| HeadLen = 0; | |
| FirstFragment = IP4_FIRST_FRAGMENT (Head->Fragment); | |
| Ip4CopyOption (Option, OptLen, FirstFragment, NULL, &Len); | |
| HeadLen = IP4_MIN_HEADLEN + Len; | |
| ASSERT (((Len %4) == 0) && (HeadLen <= IP4_MAX_HEADLEN)); | |
| PacketHead = (IP4_HEAD *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD); | |
| if (PacketHead == NULL) { | |
| return EFI_BAD_BUFFER_SIZE; | |
| } | |
| Ip4CopyOption (Option, OptLen, FirstFragment, (UINT8 *) (PacketHead + 1), &Len); | |
| // | |
| // Set the head up, convert the host byte order to network byte order | |
| // | |
| PacketHead->Ver = 4; | |
| PacketHead->HeadLen = (UINT8) (HeadLen >> 2); | |
| PacketHead->Tos = Head->Tos; | |
| PacketHead->TotalLen = HTONS (Packet->TotalSize); | |
| PacketHead->Id = HTONS (Head->Id); | |
| PacketHead->Fragment = HTONS (Head->Fragment); | |
| PacketHead->Checksum = 0; | |
| PacketHead->Ttl = Head->Ttl; | |
| PacketHead->Protocol = Head->Protocol; | |
| PacketHead->Src = HTONL (Head->Src); | |
| PacketHead->Dst = HTONL (Head->Dst); | |
| PacketHead->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) PacketHead, HeadLen)); | |
| Packet->Ip = PacketHead; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Select an interface to send the packet generated in the IP4 driver | |
| itself, that is, not by the requests of IP4 child's consumer. Such | |
| packets include the ICMP echo replies, and other ICMP error packets. | |
| @param IpSb The IP4 service that wants to send the packets. | |
| @param Dst The destination of the packet | |
| @param Src The source of the packet | |
| @return NULL if no proper interface is found, otherwise the interface that | |
| @return can be used to send the system packet from. | |
| **/ | |
| IP4_INTERFACE * | |
| Ip4SelectInterface ( | |
| IN IP4_SERVICE *IpSb, | |
| IN IP4_ADDR Dst, | |
| IN IP4_ADDR Src | |
| ) | |
| { | |
| IP4_INTERFACE *IpIf; | |
| IP4_INTERFACE *Selected; | |
| NET_LIST_ENTRY *Entry; | |
| // | |
| // Select the interface the Dst is on if one of the connected | |
| // network. Some IP instance may be configured with 0.0.0.0/0, | |
| // don't select that interface now. | |
| // | |
| IpIf = Ip4FindNet (IpSb, Dst); | |
| if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) { | |
| return IpIf; | |
| } | |
| // | |
| // If source is one of the interface address, select it. | |
| // | |
| IpIf = Ip4FindInterface (IpSb, Src); | |
| if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) { | |
| return IpIf; | |
| } | |
| // | |
| // Select a configured interface as the fall back. Always prefer | |
| // an interface with non-zero address. | |
| // | |
| Selected = NULL; | |
| NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { | |
| IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); | |
| if (IpIf->Configured && ((Selected == NULL) || (Selected->Ip == 0))) { | |
| Selected = IpIf; | |
| } | |
| } | |
| return Selected; | |
| } | |
| /** | |
| The default callback function for system generated packet. | |
| It will free the packet. | |
| @param Ip4Instance The IP4 child that issued the transmission. It most | |
| like is NULL. | |
| @param Packet The packet that transmitted. | |
| @param IoStatus The result of the transmission, succeeded or failed. | |
| @param LinkFlag Not used when transmission. check IP4_FRAME_CALLBACK | |
| for reference. | |
| @param Context The context provided by us | |
| @return None | |
| **/ | |
| VOID | |
| Ip4SysPacketSent ( | |
| IP4_PROTOCOL *Ip4Instance, | |
| NET_BUF *Packet, | |
| EFI_STATUS IoStatus, | |
| UINT32 LinkFlag, | |
| VOID *Context | |
| ) | |
| { | |
| NetbufFree (Packet); | |
| } | |
| /** | |
| Transmit an IP4 packet. The packet comes either from the IP4 | |
| child's consumer (IpInstance != NULL) or the IP4 driver itself | |
| (IpInstance == NULL). It will route the packet, fragment it, | |
| then transmit all the fragments through some interface. | |
| @param IpSb The IP4 service instance to transmit the packet | |
| @param IpInstance The IP4 child that issues the transmission. It is | |
| NULL if the packet is from the system. | |
| @param Packet The user data to send, excluding the IP header. | |
| @param Head The caller supplied header. The caller should set | |
| the following header fields: Tos, TotalLen, Id, tl, | |
| Fragment, Protocol, Src and Dst. All the fields are | |
| in host byte order. This function will fill in the | |
| Ver, HeadLen, Fragment, and checksum. The Fragment | |
| only need to include the DF flag. Ip4Output will | |
| compute the MF and offset for you. | |
| @param Option The original option to append to the IP headers | |
| @param OptLen The length of the option | |
| @param GateWay The next hop address to transmit packet to. | |
| 255.255.255.255 means broadcast. | |
| @param Callback The callback function to issue when transmission | |
| completed. | |
| @param Context The opaque context for the callback | |
| @retval EFI_NO_MAPPING There is no interface to the destination. | |
| @retval EFI_NOT_FOUND There is no route to the destination | |
| @retval EFI_SUCCESS The packet is successfully transmitted. | |
| @retval Others Failed to transmit the packet. | |
| **/ | |
| EFI_STATUS | |
| Ip4Output ( | |
| IN IP4_SERVICE *IpSb, | |
| IN IP4_PROTOCOL *IpInstance, OPTIONAL | |
| IN NET_BUF *Packet, | |
| IN IP4_HEAD *Head, | |
| IN UINT8 *Option, | |
| IN UINT32 OptLen, | |
| IN IP4_ADDR GateWay, | |
| IN IP4_FRAME_CALLBACK Callback, | |
| IN VOID *Context | |
| ) | |
| { | |
| IP4_INTERFACE *IpIf; | |
| IP4_ROUTE_CACHE_ENTRY *CacheEntry; | |
| IP4_ADDR Dest; | |
| EFI_STATUS Status; | |
| NET_BUF *Fragment; | |
| UINT32 Index; | |
| UINT32 HeadLen; | |
| UINT32 PacketLen; | |
| UINT32 Offset; | |
| UINT32 Mtu; | |
| UINT32 Num; | |
| // | |
| // Select an interface/source for system packet, application | |
| // should select them itself. | |
| // | |
| if (IpInstance == NULL) { | |
| IpIf = Ip4SelectInterface (IpSb, Head->Dst, Head->Src); | |
| } else { | |
| IpIf = IpInstance->Interface; | |
| } | |
| if (IpIf == NULL) { | |
| return EFI_NO_MAPPING; | |
| } | |
| if ((Head->Src == IP4_ALLZERO_ADDRESS) && (IpInstance == NULL)) { | |
| Head->Src = IpIf->Ip; | |
| } | |
| // | |
| // Route the packet unless overrided, that is, GateWay isn't zero. | |
| // | |
| if (GateWay == IP4_ALLZERO_ADDRESS) { | |
| Dest = Head->Dst; | |
| if (IP4_IS_BROADCAST (Ip4GetNetCast (Dest, IpIf)) || (Dest == IP4_ALLONE_ADDRESS)) { | |
| // | |
| // Set the gateway to local broadcast if the Dest is | |
| // is the broadcast address for the connected network | |
| // or it is local broadcast. | |
| // | |
| GateWay = IP4_ALLONE_ADDRESS; | |
| } else if (IP4_IS_MULTICAST (Dest)) { | |
| // | |
| // Set the gateway to the destination if it is an multicast | |
| // address. The IP4_INTERFACE won't consult ARP to send local | |
| // broadcast and multicast. | |
| // | |
| GateWay = Head->Dst; | |
| } else { | |
| // | |
| // Consult the route table to route the packet | |
| // | |
| if (IpInstance == NULL) { | |
| CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src); | |
| } else { | |
| CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src); | |
| } | |
| if (CacheEntry == NULL) { | |
| return EFI_NOT_FOUND; | |
| } | |
| GateWay = CacheEntry->NextHop; | |
| Ip4FreeRouteCacheEntry (CacheEntry); | |
| } | |
| } | |
| // | |
| // OK, selected the source and route, fragment the packet then send | |
| // them. Tag each fragment other than the first one as spawn from it. | |
| // | |
| Mtu = IpSb->SnpMode.MaxPacketSize; | |
| HeadLen = sizeof (IP4_HEAD) + ((OptLen + 3) & (~0x03)); | |
| Head->Id = mIp4Id++; | |
| if (Packet->TotalSize + HeadLen > Mtu) { | |
| // | |
| // Packet is fragmented from the tail to the head, that is, the | |
| // first frame sent is the last fragment of the packet. The first | |
| // fragment is NOT sent in this loop. First compute how many | |
| // fragments there are. | |
| // | |
| Mtu = (Mtu - HeadLen) & (~0x07); | |
| Num = (Packet->TotalSize + Mtu - 1) / Mtu; | |
| // | |
| // Initialize the packet length and Offset. Other than the last | |
| // fragment, the packet length equals to MTU. The offset is always | |
| // aligned to MTU. | |
| // | |
| PacketLen = Packet->TotalSize - (Num - 1) * Mtu; | |
| Offset = Mtu * (Num - 1); | |
| for (Index = 0; Index < Num - 1; Index++, Offset -= Mtu) { | |
| Fragment = NetbufGetFragment (Packet, Offset, PacketLen, IP4_MAX_HEADLEN); | |
| if (Fragment == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ON_ERROR; | |
| } | |
| // | |
| // Update the header's fragment. The caller fills the IP4 header | |
| // fields that are required by Ip4PrependHead except the fragment. | |
| // | |
| Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, (Index != 0), Offset); | |
| Ip4PrependHead (Fragment, Head, Option, OptLen); | |
| // | |
| // Transmit the fragments, pass the Packet address as the context. | |
| // So, we can find all the fragments spawned from the Packet by | |
| // compare the NetBuf and Context to the Packet. | |
| // | |
| Status = Ip4SendFrame ( | |
| IpIf, | |
| IpInstance, | |
| Fragment, | |
| GateWay, | |
| Ip4SysPacketSent, | |
| Packet | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto ON_ERROR; | |
| } | |
| PacketLen = Mtu; | |
| } | |
| // | |
| // Trim the already sent data, then adjust the head's fragment field. | |
| // | |
| NetbufTrim (Packet, Packet->TotalSize - Mtu, FALSE); | |
| Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, TRUE, 0); | |
| } | |
| // | |
| // Send the first fragment, it is either the orginal packet, or the | |
| // first fragment of a fragmented packet. It seems that there is a subtle | |
| // bug here: what if the caller free the packet in Callback and IpIf (or | |
| // MNP child used by that interface) still holds the fragments and try | |
| // to access the data? The caller can free the packet if it recycles the | |
| // consumer's (such as UDP) data in the Callback. But this can't happen. | |
| // The detailed sequence is: | |
| // 1. for the packets generated by IP4 driver itself: | |
| // The Callback is Ip4SysPacketSent, which is the same as the | |
| // fragments' callback. Ip4SysPacketSent simply calls NetbufFree | |
| // to release its reference to the packet. So, no problem for | |
| // system packets. | |
| // | |
| // 2. for the upper layer's packets (use UDP as an example): | |
| // UDP requests the IP layer to transmit some data which is | |
| // wrapped in an asynchronous token, the token is wrapped | |
| // in IP4_TXTOKEN_WRAP by IP4. IP4 also wrap the user's data | |
| // in a net buffer, which is Packet we get here. IP4_TXTOKEN_WRAP | |
| // is bound with the Packet. It will only be freed when all | |
| // the references to Packet have been released. Upon then, the | |
| // Packet's OnFree callback will release the IP4_TXTOKEN_WRAP, | |
| // and singal the user's recycle event. So, also no problem for | |
| // upper layer's packets. | |
| // | |
| Ip4PrependHead (Packet, Head, Option, OptLen); | |
| Status = Ip4SendFrame (IpIf, IpInstance, Packet, GateWay, Callback, Context); | |
| if (EFI_ERROR (Status)) { | |
| goto ON_ERROR; | |
| } | |
| return EFI_SUCCESS; | |
| ON_ERROR: | |
| Ip4CancelPacket (IpIf, Packet, Status); | |
| return Status; | |
| } | |
| /** | |
| The filter function to find a packet and all its fragments. | |
| The packet's fragments have their Context set to the packet. | |
| @param Frame The frames hold by the low level interface | |
| @param Context Context to the function, which is the packet. | |
| @retval TRUE This is the packet to cancel or its fragments. | |
| @retval FALSE This is unrelated packet. | |
| **/ | |
| STATIC | |
| BOOLEAN | |
| Ip4CancelPacketFragments ( | |
| IP4_LINK_TX_TOKEN *Frame, | |
| VOID *Context | |
| ) | |
| { | |
| if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) { | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Cancel the Packet and all its fragments. | |
| @param IpIf The interface from which the Packet is sent | |
| @param Packet The Packet to cancel | |
| @param IoStatus The status returns to the sender. | |
| @return None | |
| **/ | |
| VOID | |
| Ip4CancelPacket ( | |
| IN IP4_INTERFACE *IpIf, | |
| IN NET_BUF *Packet, | |
| IN EFI_STATUS IoStatus | |
| ) | |
| { | |
| Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet); | |
| } |