/** @file The internal functions and routines to transmit the IP6 packet. Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Ip6Impl.h" UINT32 mIp6Id; /** Output all the available source addresses to a list entry head SourceList. The number of source addresses are also returned. @param[in] IpSb Points to an IP6 service binding instance. @param[out] SourceList The list entry head of all source addresses. It is the caller's responsibility to free the resources. @param[out] SourceCount The number of source addresses. @retval EFI_SUCCESS The source addresses were copied to a list entry head SourceList. @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. **/ EFI_STATUS Ip6CandidateSource ( IN IP6_SERVICE *IpSb, OUT LIST_ENTRY *SourceList, OUT UINT32 *SourceCount ) { IP6_INTERFACE *IpIf; LIST_ENTRY *Entry; LIST_ENTRY *Entry2; IP6_ADDRESS_INFO *AddrInfo; IP6_ADDRESS_INFO *Copy; *SourceCount = 0; if (IpSb->LinkLocalOk) { Copy = AllocatePool (sizeof (IP6_ADDRESS_INFO)); if (Copy == NULL) { return EFI_OUT_OF_RESOURCES; } Copy->Signature = IP6_ADDR_INFO_SIGNATURE; IP6_COPY_ADDRESS (&Copy->Address, &IpSb->LinkLocalAddr); Copy->IsAnycast = FALSE; Copy->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; Copy->ValidLifetime = (UINT32) IP6_INFINIT_LIFETIME; Copy->PreferredLifetime = (UINT32) IP6_INFINIT_LIFETIME; InsertTailList (SourceList, &Copy->Link); (*SourceCount)++; } NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) { AddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); if (AddrInfo->IsAnycast) { // // Never use an anycast address. // continue; } Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), AddrInfo); if (Copy == NULL) { return EFI_OUT_OF_RESOURCES; } InsertTailList (SourceList, &Copy->Link); (*SourceCount)++; } } return EFI_SUCCESS; } /** Calculate how many bits are the same between two IPv6 addresses. @param[in] AddressA Points to an IPv6 address. @param[in] AddressB Points to another IPv6 address. @return The common bits of the AddressA and AddressB. **/ UINT8 Ip6CommonPrefixLen ( IN EFI_IPv6_ADDRESS *AddressA, IN EFI_IPv6_ADDRESS *AddressB ) { UINT8 Count; UINT8 Index; UINT8 ByteA; UINT8 ByteB; UINT8 NumBits; Count = 0; Index = 0; while (Index < 16) { ByteA = AddressA->Addr[Index]; ByteB = AddressB->Addr[Index]; if (ByteA == ByteB) { Count += 8; Index++; continue; } // // Check how many bits are common between the two bytes. // NumBits = 8; ByteA = (UINT8) (ByteA ^ ByteB); while (ByteA != 0) { NumBits--; ByteA = (UINT8) (ByteA >> 1); } return (UINT8) (Count + NumBits); } return Count; } /** Output all the available source addresses to a list entry head SourceList. The number of source addresses are also returned. @param[in] IpSb Points to a IP6 service binding instance. @param[in] Destination The IPv6 destination address. @param[out] Source The selected IPv6 source address according to the Destination. @retval EFI_SUCCESS The source addresses were copied to a list entry head SourceList. @retval EFI_NO_MAPPING The IPv6 stack is not auto configured. **/ EFI_STATUS Ip6SelectSourceAddress ( IN IP6_SERVICE *IpSb, IN EFI_IPv6_ADDRESS *Destination, OUT EFI_IPv6_ADDRESS *Source ) { EFI_STATUS Status; LIST_ENTRY SourceList; UINT32 SourceCount; UINT8 ScopeD; LIST_ENTRY *Entry; IP6_ADDRESS_INFO *AddrInfo; IP6_PREFIX_LIST_ENTRY *Prefix; UINT8 LastCommonLength; UINT8 CurrentCommonLength; EFI_IPv6_ADDRESS *TmpAddress; NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); Status = EFI_SUCCESS; InitializeListHead (&SourceList); if (!IpSb->LinkLocalOk) { return EFI_NO_MAPPING; } // // Rule 1: Prefer same address. // if (Ip6IsOneOfSetAddress (IpSb, Destination, NULL, NULL)) { IP6_COPY_ADDRESS (Source, Destination); goto Exit; } // // Rule 2: Prefer appropriate scope. // if (IP6_IS_MULTICAST (Destination)) { ScopeD = (UINT8) (Destination->Addr[1] >> 4); } else if (NetIp6IsLinkLocalAddr (Destination)) { ScopeD = 0x2; } else { ScopeD = 0xE; } if (ScopeD <= 0x2) { // // Return the link-local address if it exists // One IP6_SERVICE only has one link-local address. // IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr); goto Exit; } // // All candidate source addresses are global unicast address. // Ip6CandidateSource (IpSb, &SourceList, &SourceCount); if (SourceCount == 0) { Status = EFI_NO_MAPPING; goto Exit; } IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr); if (SourceCount == 1) { goto Exit; } // // Rule 3: Avoid deprecated addresses. // TODO: check the "deprecated" state of the stateful configured address // NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) { Prefix = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); if (Prefix->PreferredLifetime == 0) { Ip6RemoveAddr (NULL, &SourceList, &SourceCount, &Prefix->Prefix, Prefix->PrefixLength); if (SourceCount == 1) { goto Exit; } } } // // TODO: Rule 4: Prefer home addresses. // TODO: Rule 5: Prefer outgoing interface. // TODO: Rule 6: Prefer matching label. // TODO: Rule 7: Prefer public addresses. // // // Rule 8: Use longest matching prefix. // LastCommonLength = Ip6CommonPrefixLen (Source, Destination); TmpAddress = NULL; for (Entry = SourceList.ForwardLink; Entry != &SourceList; Entry = Entry->ForwardLink) { AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); CurrentCommonLength = Ip6CommonPrefixLen (&AddrInfo->Address, Destination); if (CurrentCommonLength > LastCommonLength) { LastCommonLength = CurrentCommonLength; TmpAddress = &AddrInfo->Address; } } if (TmpAddress != NULL) { IP6_COPY_ADDRESS (Source, TmpAddress); } Exit: Ip6RemoveAddr (NULL, &SourceList, &SourceCount, NULL, 0); return Status; } /** Select an interface to send the packet generated in the IP6 driver itself: that is, not by the requests of the IP6 child's consumer. Such packets include the ICMPv6 echo replies and other ICMPv6 error packets. @param[in] IpSb The IP4 service that wants to send the packets. @param[in] Destination The destination of the packet. @param[in, out] Source 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. **/ IP6_INTERFACE * Ip6SelectInterface ( IN IP6_SERVICE *IpSb, IN EFI_IPv6_ADDRESS *Destination, IN OUT EFI_IPv6_ADDRESS *Source ) { EFI_STATUS Status; EFI_IPv6_ADDRESS SelectedSource; IP6_INTERFACE *IpIf; BOOLEAN Exist; NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); ASSERT (Destination != NULL && Source != NULL); if (NetIp6IsUnspecifiedAddr (Destination)) { return NULL; } if (!NetIp6IsUnspecifiedAddr (Source)) { Exist = Ip6IsOneOfSetAddress (IpSb, Source, &IpIf, NULL); ASSERT (Exist); return IpIf; } // // If source is unspecified, select a source according to the destination. // Status = Ip6SelectSourceAddress (IpSb, Destination, &SelectedSource); if (EFI_ERROR (Status)) { return IpSb->DefaultInterface; } Ip6IsOneOfSetAddress (IpSb, &SelectedSource, &IpIf, NULL); IP6_COPY_ADDRESS (Source, &SelectedSource); return IpIf; } /** The default callback function for the system generated packet. It will free the packet. @param[in] Packet The packet that transmitted. @param[in] IoStatus The result of the transmission, succeeded or failed. @param[in] LinkFlag Not used when transmitted. Check IP6_FRAME_CALLBACK for reference. @param[in] Context The context provided by us. **/ VOID Ip6SysPacketSent ( NET_BUF *Packet, EFI_STATUS IoStatus, UINT32 LinkFlag, VOID *Context ) { NetbufFree (Packet); Packet = NULL; } /** Prefix an IP6 basic head and unfragmentable extension headers and a fragment header to the Packet. Used for IP6 fragmentation. @param[in] IpSb The IP6 service instance to transmit the packet. @param[in] Packet The packet to prefix the IP6 header to. @param[in] Head The caller supplied header. @param[in] FragmentOffset The fragment offset of the data following the header. @param[in] ExtHdrs The length of the original extension header. @param[in] ExtHdrsLen The length of the extension headers. @param[in] LastHeader The pointer of next header of last extension header. @param[in] HeadLen The length of the unfragmented part of the IP6 header. @retval EFI_BAD_BUFFER_SIZE There is no enough room in the head space of Packet. @retval EFI_SUCCESS The operation performed successfully. **/ EFI_STATUS Ip6PrependHead ( IN IP6_SERVICE *IpSb, IN NET_BUF *Packet, IN EFI_IP6_HEADER *Head, IN UINT16 FragmentOffset, IN UINT8 *ExtHdrs, IN UINT32 ExtHdrsLen, IN UINT8 LastHeader, IN UINT32 HeadLen ) { UINT32 Len; UINT32 UnFragExtHdrsLen; EFI_IP6_HEADER *PacketHead; UINT8 *UpdatedExtHdrs; EFI_STATUS Status; UINT8 NextHeader; UpdatedExtHdrs = NULL; // // HeadLen is the length of the fixed part of the sequences of fragments, i.e. // the unfragment part. // PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD); if (PacketHead == NULL) { return EFI_BAD_BUFFER_SIZE; } // // Set the head up, convert the host byte order to network byte order // CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER)); PacketHead->PayloadLength = HTONS ((UINT16) (Packet->TotalSize - sizeof (EFI_IP6_HEADER))); Packet->Ip.Ip6 = PacketHead; Len = HeadLen - sizeof (EFI_IP6_HEADER); UnFragExtHdrsLen = Len - sizeof (IP6_FRAGMENT_HEADER); if (UnFragExtHdrsLen == 0) { PacketHead->NextHeader = IP6_FRAGMENT; } // // Append the extension headers: firstly copy the unfragmentable headers, then append // fragmentation header. // if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) { NextHeader = Head->NextHeader; } else { NextHeader = PacketHead->NextHeader; } Status = Ip6FillFragmentHeader ( IpSb, NextHeader, LastHeader, ExtHdrs, ExtHdrsLen, FragmentOffset, &UpdatedExtHdrs ); if (EFI_ERROR (Status)) { return Status; } CopyMem ( (UINT8 *) (PacketHead + 1), UpdatedExtHdrs, UnFragExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER) ); FreePool (UpdatedExtHdrs); return EFI_SUCCESS; } /** Transmit an IP6 packet. The packet comes either from the IP6 child's consumer (IpInstance != NULL) or the IP6 driver itself (IpInstance == NULL). It will route the packet, fragment it, then transmit all the fragments through an interface. @param[in] IpSb The IP6 service instance to transmit the packet. @param[in] Interface The IP6 interface to transmit the packet. Ignored if NULL. @param[in] IpInstance The IP6 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: NextHeader, HopLimit, Src, Dest, FlowLabel, PayloadLength. This function will fill in the Ver, TrafficClass. @param[in] ExtHdrs The extension headers to append to the IPv6 basic header. @param[in] ExtHdrsLen The length of the extension headers. @param[in] Callback The callback function to issue when transmission completed. @param[in] Context The opaque context for the callback. @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid. @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 successfully transmitted. @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of resources. @retval Others Failed to transmit the packet. **/ EFI_STATUS Ip6Output ( IN IP6_SERVICE *IpSb, IN IP6_INTERFACE *Interface OPTIONAL, IN IP6_PROTOCOL *IpInstance OPTIONAL, IN NET_BUF *Packet, IN EFI_IP6_HEADER *Head, IN UINT8 *ExtHdrs, IN UINT32 ExtHdrsLen, IN IP6_FRAME_CALLBACK Callback, IN VOID *Context ) { IP6_INTERFACE *IpIf; EFI_IPv6_ADDRESS NextHop; IP6_NEIGHBOR_ENTRY *NeighborCache; IP6_ROUTE_CACHE_ENTRY *RouteCache; EFI_STATUS Status; UINT32 Mtu; UINT32 HeadLen; UINT16 FragmentOffset; UINT8 *LastHeader; UINT32 UnFragmentLen; UINT32 UnFragmentHdrsLen; UINT32 FragmentHdrsLen; UINT16 *Checksum; UINT16 PacketChecksum; UINT16 PseudoChecksum; UINT32 Index; UINT32 PacketLen; UINT32 RealExtLen; UINT32 Offset; NET_BUF *TmpPacket; NET_BUF *Fragment; UINT32 Num; UINT8 *Buf; EFI_IP6_HEADER *PacketHead; IP6_ICMP_HEAD *IcmpHead; IP6_TXTOKEN_WRAP *Wrap; IP6_ROUTE_ENTRY *RouteEntry; UINT8 *UpdatedExtHdrs; UINT8 NextHeader; UINT8 LastHeaderBackup; BOOLEAN FragmentHeadInserted; UINT8 *ExtHdrsBackup; UINT8 NextHeaderBackup; EFI_IPv6_ADDRESS Source; EFI_IPv6_ADDRESS Destination; NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); // // RFC2460: Each extension header is an integer multiple of 8 octets long, // in order to retain 8-octet alignment for subsequent headers. // if ((ExtHdrsLen & 0x7) != 0) { return EFI_INVALID_PARAMETER; } LastHeader = NULL; Ip6IsExtsValid ( NULL, NULL, &Head->NextHeader, ExtHdrs, ExtHdrsLen, FALSE, NULL, &LastHeader, NULL, NULL, NULL ); // // Select an interface/source for system packet, application // should select them itself. // IpIf = Interface; if (IpIf == NULL) { // // IpInstance->Interface is NULL when IpInstance is configured with both stationaddress // and destinationaddress is unspecified. // if (IpInstance == NULL || IpInstance->Interface == NULL) { IpIf = Ip6SelectInterface (IpSb, &Head->DestinationAddress, &Head->SourceAddress); if (IpInstance != NULL) { IpInstance->Interface = IpIf; } } else { IpIf = IpInstance->Interface; } } if (IpIf == NULL) { return EFI_NO_MAPPING; } // // Update the common field in Head here. // Head->Version = 6; Head->TrafficClassL = 0; Head->TrafficClassH = 0; Checksum = NULL; NextHeader = *LastHeader; switch (NextHeader) { case EFI_IP_PROTO_UDP: Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); ASSERT (Packet->Udp != NULL); if (Packet->Udp->Checksum == 0) { Checksum = &Packet->Udp->Checksum; } break; case EFI_IP_PROTO_TCP: Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, NULL); ASSERT (Packet->Tcp != NULL); if (Packet->Tcp->Checksum == 0) { Checksum = &Packet->Tcp->Checksum; } break; case IP6_ICMP: // // Don't send ICMP packet to an IPv6 anycast address. // if (Ip6IsAnycast (IpSb, &Head->DestinationAddress)) { return EFI_INVALID_PARAMETER; } IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL); ASSERT (IcmpHead != NULL); if (IcmpHead->Checksum == 0) { Checksum = &IcmpHead->Checksum; } break; default: break; } if (Checksum != NULL) { // // Calculate the checksum for upper layer protocol if it is not calculated due to lack of // IPv6 source address. // PacketChecksum = NetbufChecksum (Packet); PseudoChecksum = NetIp6PseudoHeadChecksum ( &Head->SourceAddress, &Head->DestinationAddress, NextHeader, Packet->TotalSize ); *Checksum = (UINT16) ~NetAddChecksum (PacketChecksum, PseudoChecksum); } Status = Ip6IpSecProcessPacket ( IpSb, &Head, LastHeader, // no need get the lasthead value for output &Packet, &ExtHdrs, &ExtHdrsLen, EfiIPsecOutBound, Context ); if (EFI_ERROR(Status)) { return Status; } LastHeader = NULL; // // Check incoming parameters. // if (!Ip6IsExtsValid ( IpSb, Packet, &Head->NextHeader, ExtHdrs, ExtHdrsLen, FALSE, NULL, &LastHeader, &RealExtLen, &UnFragmentHdrsLen, NULL )) { return EFI_INVALID_PARAMETER; } if ((RealExtLen & 0x7) != 0) { return EFI_INVALID_PARAMETER; } LastHeaderBackup = *LastHeader; // // Perform next hop determination: // For multicast packets, the next-hop is always the destination address and // is considered to be on-link. // if (IP6_IS_MULTICAST (&Head->DestinationAddress)) { IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress); } else { // // For unicast packets, use a combination of the Destination Cache, the Prefix List // and the Default Router List to determine the IP address of the appropriate next hop. // NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->DestinationAddress); if (NeighborCache != NULL) { // // Hit Neighbor Cache. // IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress); } else { // // Not in Neighbor Cache, check Router cache // RouteCache = Ip6Route (IpSb, &Head->DestinationAddress, &Head->SourceAddress); if (RouteCache == NULL) { return EFI_NOT_FOUND; } IP6_COPY_ADDRESS (&NextHop, &RouteCache->NextHop); Ip6FreeRouteCacheEntry (RouteCache); } } // // Examines the Neighbor Cache for link-layer information about that neighbor. // DO NOT create neighbor cache if neighbor is itself - when reporting ICMP error. // if (!IP6_IS_MULTICAST (&NextHop) && !EFI_IP6_EQUAL (&Head->DestinationAddress, &Head->SourceAddress)) { NeighborCache = Ip6FindNeighborEntry (IpSb, &NextHop); if (NeighborCache == NULL) { NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &NextHop, NULL); if (NeighborCache == NULL) { return EFI_OUT_OF_RESOURCES; } // // Send out multicast neighbor solicitation for address resolution immediately. // Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination); Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); if (EFI_ERROR (Status)) { return Status; } Status = Ip6SendNeighborSolicit ( IpSb, &Source, &Destination, &NeighborCache->Neighbor, &IpSb->SnpMode.CurrentAddress ); if (EFI_ERROR (Status)) { return Status; } --NeighborCache->Transmit; NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer) + 1; } NeighborCache->Interface = IpIf; } UpdatedExtHdrs = NULL; ExtHdrsBackup = NULL; NextHeaderBackup = 0; FragmentHeadInserted = FALSE; // // Check whether we received Packet Too Big message for the packet sent to the // Destination. If yes include a Fragment Header in the subsequent packets. // RouteEntry = Ip6FindRouteEntry ( IpSb->RouteTable, &Head->DestinationAddress, NULL ); if (RouteEntry != NULL) { if ((RouteEntry->Flag & IP6_PACKET_TOO_BIG) == IP6_PACKET_TOO_BIG) { // // FragmentHead is inserted after Hop-by-Hop Options header, Destination // Options header (first occur), Routing header, and before Fragment header, // Authentication header, Encapsulating Security Payload header, and // Destination Options header (last occur), and upper-layer header. // Status = Ip6FillFragmentHeader ( IpSb, Head->NextHeader, LastHeaderBackup, ExtHdrs, ExtHdrsLen, 0, &UpdatedExtHdrs ); if (EFI_ERROR (Status)) { return Status; } if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) { NextHeaderBackup = Head->NextHeader; Head->NextHeader = IP6_FRAGMENT; } ExtHdrsBackup = ExtHdrs; ExtHdrs = UpdatedExtHdrs; ExtHdrsLen = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER); RealExtLen = RealExtLen + sizeof (IP6_FRAGMENT_HEADER); mIp6Id++; FragmentHeadInserted = TRUE; } Ip6FreeRouteEntry (RouteEntry); } // // OK, selected the source and route, fragment the packet then send // them. Tag each fragment other than the first one as spawn from it. // Each extension header is an integer multiple of 8 octets long, in // order to retain 8-octet alignment for subsequent headers. // Mtu = IpSb->MaxPacketSize + sizeof (EFI_IP6_HEADER); HeadLen = sizeof (EFI_IP6_HEADER) + RealExtLen; if (Packet->TotalSize + HeadLen > Mtu) { // // Remove the inserted Fragment Header since we need fragment the packet. // if (FragmentHeadInserted) { ExtHdrs = ExtHdrsBackup; ExtHdrsLen = ExtHdrsLen - sizeof (IP6_FRAGMENT_HEADER); if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) { Head->NextHeader = NextHeaderBackup; } } FragmentHdrsLen = ExtHdrsLen - UnFragmentHdrsLen; // // The packet is beyond the maximum which can be described through the // fragment offset field in Fragment header. // if ((((Packet->TotalSize + FragmentHdrsLen) >> 3) & (~0x1fff)) != 0) { Status = EFI_BAD_BUFFER_SIZE; goto Error; } if (FragmentHdrsLen != 0) { // // Append the fragmentable extension hdrs before the upper layer payload // to form a new NET_BUF. This NET_BUF contains all the buffer which will // be fragmented below. // TmpPacket = NetbufGetFragment (Packet, 0, Packet->TotalSize, FragmentHdrsLen); ASSERT (TmpPacket != NULL); // // Allocate the space to contain the fragmentable hdrs and copy the data. // Buf = NetbufAllocSpace (TmpPacket, FragmentHdrsLen, TRUE); ASSERT (Buf != NULL); CopyMem (Buf, ExtHdrs + UnFragmentHdrsLen, FragmentHdrsLen); // // Free the old Packet. // NetbufFree (Packet); Packet = TmpPacket; } // // The unfragment part which appears in every fragmented IPv6 packet includes // the IPv6 header, the unfragmentable extension hdrs and the fragment header. // UnFragmentLen = sizeof (EFI_IP6_HEADER) + UnFragmentHdrsLen + sizeof (IP6_FRAGMENT_HEADER); // // Mtu now is the length of the fragment part in a full-length fragment. // Mtu = (Mtu - UnFragmentLen) & (~0x07); Num = (Packet->TotalSize + Mtu - 1) / Mtu; for (Index = 0, Offset = 0, PacketLen = Mtu; Index < Num; Index++) { // // Get fragment from the Packet, append UnFragmentLen spare buffer // before the fragmented data, the corresponding data is filled in later. // Fragment = NetbufGetFragment (Packet, Offset, PacketLen, UnFragmentLen); if (Fragment == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Error; } FragmentOffset = (UINT16) ((UINT16) Offset | 0x1); if (Index == Num - 1){ // // The last fragment, clear the M flag. // FragmentOffset &= (~0x1); } Status = Ip6PrependHead ( IpSb, Fragment, Head, FragmentOffset, ExtHdrs, ExtHdrsLen, LastHeaderBackup, UnFragmentLen ); ASSERT (Status == EFI_SUCCESS); Status = Ip6SendFrame ( IpIf, IpInstance, Fragment, &NextHop, Ip6SysPacketSent, Packet ); if (EFI_ERROR (Status)) { goto Error; } // // The last fragment of upper layer packet, update the IP6 token status. // if ((Index == Num -1) && (Context != NULL)) { Wrap = (IP6_TXTOKEN_WRAP *) Context; Wrap->Token->Status = Status; } Offset += PacketLen; PacketLen = Packet->TotalSize - Offset; if (PacketLen > Mtu) { PacketLen = Mtu; } } NetbufFree (Packet); mIp6Id++; if (UpdatedExtHdrs != NULL) { FreePool (UpdatedExtHdrs); } return EFI_SUCCESS; } // // Need not fragment the packet, send it in one frame. // PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD); if (PacketHead == NULL) { Status = EFI_BAD_BUFFER_SIZE; goto Error; } CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER)); Packet->Ip.Ip6 = PacketHead; if (ExtHdrs != NULL) { Buf = (UINT8 *) (PacketHead + 1); CopyMem (Buf, ExtHdrs, ExtHdrsLen); } if (UpdatedExtHdrs != NULL) { // // A Fragment Header is inserted to the packet, update the payload length. // PacketHead->PayloadLength = (UINT16) (NTOHS (PacketHead->PayloadLength) + sizeof (IP6_FRAGMENT_HEADER)); PacketHead->PayloadLength = HTONS (PacketHead->PayloadLength); FreePool (UpdatedExtHdrs); } return Ip6SendFrame ( IpIf, IpInstance, Packet, &NextHop, Callback, Context ); Error: if (UpdatedExtHdrs != NULL) { FreePool (UpdatedExtHdrs); } Ip6CancelPacket (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 an unrelated packet. **/ BOOLEAN Ip6CancelPacketFragments ( IN IP6_LINK_TX_TOKEN *Frame, IN VOID *Context ) { if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) { return TRUE; } return FALSE; } /** Remove all the frames on the interface that pass the FrameToCancel, either queued on ARP queues or that have already been delivered to MNP and not yet recycled. @param[in] Interface Interface to remove the frames from. @param[in] IoStatus The transmit status returned to the frames' callback. @param[in] FrameToCancel Function to select the frame to cancel; NULL to select all. @param[in] Context Opaque parameters passed to FrameToCancel. Ignored if FrameToCancel is NULL. **/ VOID Ip6CancelFrames ( IN IP6_INTERFACE *Interface, IN EFI_STATUS IoStatus, IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, IN VOID *Context OPTIONAL ) { LIST_ENTRY *Entry; LIST_ENTRY *Next; IP6_LINK_TX_TOKEN *Token; IP6_SERVICE *IpSb; IP6_NEIGHBOR_ENTRY *ArpQue; EFI_STATUS Status; IpSb = Interface->Service; NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); // // Cancel all the pending frames on ARP requests // NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) { ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList); Status = Ip6FreeNeighborEntry ( IpSb, ArpQue, FALSE, FALSE, IoStatus, FrameToCancel, Context ); ASSERT_EFI_ERROR (Status); } // // Cancel all the frames that have been delivered to MNP // but not yet recycled. // NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) { Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link); if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) { IpSb->Mnp->Cancel (IpSb->Mnp, &Token->MnpToken); } } } /** Cancel the Packet and all its fragments. @param[in] IpIf The interface from which the Packet is sent. @param[in] Packet The Packet to cancel. @param[in] IoStatus The status returns to the sender. **/ VOID Ip6CancelPacket ( IN IP6_INTERFACE *IpIf, IN NET_BUF *Packet, IN EFI_STATUS IoStatus ) { Ip6CancelFrames (IpIf, IoStatus, Ip6CancelPacketFragments, Packet); }