| /** @file | |
| The internal functions and routines to transmit the IP6 packet. | |
| Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR> | |
| 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. | |
| **/ | |
| #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 UnFragnmentLen 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); | |
| } | |