123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- /** @file
- Transmit the IP4 packet.
- Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #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 original IP4 option to copy from
- @param OptLen The length of the IP4 option
- @retval EFI_BAD_BUFFER_SIZE There is no enough room in the head space of
- Packet.
- @retval EFI_SUCCESS The IP4 header is successfully added to the packet.
- **/
- EFI_STATUS
- Ip4PrependHead (
- IN OUT 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 ((UINT16)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.Ip4 = 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[in] IpSb The IP4 service that wants to send the packets.
- @param[in] Dst The destination of the packet
- @param[in] Src The source of the packet
- @return NULL if no proper interface is found, otherwise the interface that
- 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;
- 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
- **/
- 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[in] IpSb The IP4 service instance to transmit the packet
- @param[in] IpInstance The IP4 child that issues the transmission. It is
- NULL if the packet is from the system.
- @param[in] Packet The user data to send, excluding the IP header.
- @param[in] 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[in] Option The original option to append to the IP headers
- @param[in] OptLen The length of the option
- @param[in] GateWay The next hop address to transmit packet to.
- 255.255.255.255 means broadcast.
- @param[in] Callback The callback function to issue when transmission
- completed.
- @param[in] 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 EFI_BAD_BUFFER_SIZE The length of the IPv4 header + option length +
- total data length is greater than MTU (or greater
- than the maximum packet size if Token.Packet.TxData.
- OverrideData.DoNotFragment is TRUE.)
- @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;
- BOOLEAN RawData;
- //
- // 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;
- }
- //
- // Before IPsec process, prepared the IP head.
- // If Ip4Output is transmitting RawData, don't update IPv4 header.
- //
- HeadLen = sizeof (IP4_HEAD) + ((OptLen + 3) & (~0x03));
- if ((IpInstance != NULL) && IpInstance->ConfigData.RawData) {
- RawData = TRUE;
- } else {
- Head->HeadLen = (UINT8)(HeadLen >> 2);
- Head->Id = mIp4Id++;
- Head->Ver = 4;
- RawData = FALSE;
- }
- //
- // Call IPsec process.
- //
- Status = Ip4IpSecProcessPacket (
- IpSb,
- &Head,
- &Packet,
- &Option,
- &OptLen,
- EfiIPsecOutBound,
- Context
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Dest = Head->Dst;
- if (IP4_IS_BROADCAST (Ip4GetNetCast (Dest, IpIf)) || (Dest == IP4_ALLONE_ADDRESS)) {
- //
- // Set the gateway to local broadcast if the Dest 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 if (GateWay == IP4_ALLZERO_ADDRESS) {
- //
- // Route the packet unless overridden, that is, GateWay isn't zero.
- //
- if (IpInstance == NULL) {
- CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src, IpIf->SubnetMask, TRUE);
- } else {
- CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src, IpIf->SubnetMask, FALSE);
- //
- // If failed to route the packet by using the instance's route table,
- // try to use the default route table.
- //
- if (CacheEntry == NULL) {
- CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src, IpIf->SubnetMask, TRUE);
- }
- }
- 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->MaxPacketSize + sizeof (IP4_HEAD);
- if (Packet->TotalSize + HeadLen > Mtu) {
- //
- // Fragmentation is disabled for RawData mode.
- //
- if (RawData) {
- return EFI_BAD_BUFFER_SIZE;
- }
- //
- // 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,
- IpSb
- );
- 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 original 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 signal 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, IpSb);
- 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[in] Frame The frames hold by the low level interface
- @param[in] 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.
- **/
- BOOLEAN
- Ip4CancelPacketFragments (
- IN IP4_LINK_TX_TOKEN *Frame,
- IN 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.
- **/
- VOID
- Ip4CancelPacket (
- IN IP4_INTERFACE *IpIf,
- IN NET_BUF *Packet,
- IN EFI_STATUS IoStatus
- )
- {
- Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet);
- }
|