123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678 |
- /** @file
- The ICMPv6 handle routines to process the ICMPv6 control messages.
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include "Ip6Impl.h"
- EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[23] = {
- {
- ICMP_V6_DEST_UNREACHABLE,
- ICMP_V6_NO_ROUTE_TO_DEST
- },
- {
- ICMP_V6_DEST_UNREACHABLE,
- ICMP_V6_COMM_PROHIBITED
- },
- {
- ICMP_V6_DEST_UNREACHABLE,
- ICMP_V6_BEYOND_SCOPE
- },
- {
- ICMP_V6_DEST_UNREACHABLE,
- ICMP_V6_ADDR_UNREACHABLE
- },
- {
- ICMP_V6_DEST_UNREACHABLE,
- ICMP_V6_PORT_UNREACHABLE
- },
- {
- ICMP_V6_DEST_UNREACHABLE,
- ICMP_V6_SOURCE_ADDR_FAILED
- },
- {
- ICMP_V6_DEST_UNREACHABLE,
- ICMP_V6_ROUTE_REJECTED
- },
- {
- ICMP_V6_PACKET_TOO_BIG,
- ICMP_V6_DEFAULT_CODE
- },
- {
- ICMP_V6_TIME_EXCEEDED,
- ICMP_V6_TIMEOUT_HOP_LIMIT
- },
- {
- ICMP_V6_TIME_EXCEEDED,
- ICMP_V6_TIMEOUT_REASSEMBLE
- },
- {
- ICMP_V6_PARAMETER_PROBLEM,
- ICMP_V6_ERRONEOUS_HEADER
- },
- {
- ICMP_V6_PARAMETER_PROBLEM,
- ICMP_V6_UNRECOGNIZE_NEXT_HDR
- },
- {
- ICMP_V6_PARAMETER_PROBLEM,
- ICMP_V6_UNRECOGNIZE_OPTION
- },
- {
- ICMP_V6_ECHO_REQUEST,
- ICMP_V6_DEFAULT_CODE
- },
- {
- ICMP_V6_ECHO_REPLY,
- ICMP_V6_DEFAULT_CODE
- },
- {
- ICMP_V6_LISTENER_QUERY,
- ICMP_V6_DEFAULT_CODE
- },
- {
- ICMP_V6_LISTENER_REPORT,
- ICMP_V6_DEFAULT_CODE
- },
- {
- ICMP_V6_LISTENER_REPORT_2,
- ICMP_V6_DEFAULT_CODE
- },
- {
- ICMP_V6_LISTENER_DONE,
- ICMP_V6_DEFAULT_CODE
- },
- {
- ICMP_V6_ROUTER_SOLICIT,
- ICMP_V6_DEFAULT_CODE
- },
- {
- ICMP_V6_ROUTER_ADVERTISE,
- ICMP_V6_DEFAULT_CODE
- },
- {
- ICMP_V6_NEIGHBOR_SOLICIT,
- ICMP_V6_DEFAULT_CODE
- },
- {
- ICMP_V6_NEIGHBOR_ADVERTISE,
- ICMP_V6_DEFAULT_CODE
- },
- };
- /**
- Reply an ICMPv6 echo request.
- @param[in] IpSb The IP service that received the packet.
- @param[in] Head The IP head of the ICMPv6 informational message.
- @param[in] Packet The content of the ICMPv6 message with the IP head
- removed.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
- @retval EFI_SUCCESS Successfully answered the ICMPv6 Echo request.
- @retval Others Failed to answer the ICMPv6 Echo request.
- **/
- EFI_STATUS
- Ip6IcmpReplyEcho (
- IN IP6_SERVICE *IpSb,
- IN EFI_IP6_HEADER *Head,
- IN NET_BUF *Packet
- )
- {
- IP6_ICMP_INFORMATION_HEAD *Icmp;
- NET_BUF *Data;
- EFI_STATUS Status;
- EFI_IP6_HEADER ReplyHead;
- Status = EFI_OUT_OF_RESOURCES;
- //
- // make a copy the packet, it is really a bad idea to
- // send the MNP's buffer back to MNP.
- //
- Data = NetbufDuplicate (Packet, NULL, IP6_MAX_HEADLEN);
- if (Data == NULL) {
- goto Exit;
- }
- //
- // Change the ICMP type to echo reply, exchange the source
- // and destination, then send it. The source is updated to
- // use specific destination. See RFC1122. SRR/RR option
- // update is omitted.
- //
- Icmp = (IP6_ICMP_INFORMATION_HEAD *)NetbufGetByte (Data, 0, NULL);
- if (Icmp == NULL) {
- NetbufFree (Data);
- goto Exit;
- }
- Icmp->Head.Type = ICMP_V6_ECHO_REPLY;
- Icmp->Head.Checksum = 0;
- //
- // Generate the IPv6 basic header
- // If the Echo Reply is a response to a Echo Request sent to one of the node's unicast address,
- // the Source address of the Echo Reply must be the same address.
- //
- ZeroMem (&ReplyHead, sizeof (EFI_IP6_HEADER));
- ReplyHead.PayloadLength = HTONS ((UINT16)(Packet->TotalSize));
- ReplyHead.NextHeader = IP6_ICMP;
- ReplyHead.HopLimit = IpSb->CurHopLimit;
- IP6_COPY_ADDRESS (&ReplyHead.DestinationAddress, &Head->SourceAddress);
- if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {
- IP6_COPY_ADDRESS (&ReplyHead.SourceAddress, &Head->DestinationAddress);
- }
- //
- // If source is unspecified, Ip6Output will select a source for us
- //
- Status = Ip6Output (
- IpSb,
- NULL,
- NULL,
- Data,
- &ReplyHead,
- NULL,
- 0,
- Ip6SysPacketSent,
- NULL
- );
- Exit:
- NetbufFree (Packet);
- return Status;
- }
- /**
- Process Packet Too Big message sent by a router in response to a packet that
- it cannot forward because the packet is larger than the MTU of outgoing link.
- Since this driver already uses IPv6 minimum link MTU as the maximum packet size,
- if Packet Too Big message is still received, do not reduce the packet size, but
- rather include a Fragment header in the subsequent packets.
- @param[in] IpSb The IP service that received the packet.
- @param[in] Head The IP head of the ICMPv6 error packet.
- @param[in] Packet The content of the ICMPv6 error with the IP head
- removed.
- @retval EFI_SUCCESS The ICMPv6 error processed successfully.
- @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of
- resource.
- @retval EFI_NOT_FOUND The packet too big message is not sent to us.
- **/
- EFI_STATUS
- Ip6ProcessPacketTooBig (
- IN IP6_SERVICE *IpSb,
- IN EFI_IP6_HEADER *Head,
- IN NET_BUF *Packet
- )
- {
- IP6_ICMP_ERROR_HEAD Icmp;
- UINT32 Mtu;
- IP6_ROUTE_ENTRY *RouteEntry;
- EFI_IPv6_ADDRESS *DestAddress;
- NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *)&Icmp);
- Mtu = NTOHL (Icmp.Fourth);
- DestAddress = &Icmp.IpHead.DestinationAddress;
- if (Mtu < IP6_MIN_LINK_MTU) {
- //
- // Normally the multicast address is considered to be on-link and not recorded
- // in route table. Here it is added into the table since the MTU information
- // need be recorded.
- //
- if (IP6_IS_MULTICAST (DestAddress)) {
- RouteEntry = Ip6CreateRouteEntry (DestAddress, 128, NULL);
- if (RouteEntry == NULL) {
- NetbufFree (Packet);
- return EFI_OUT_OF_RESOURCES;
- }
- RouteEntry->Flag = IP6_DIRECT_ROUTE | IP6_PACKET_TOO_BIG;
- InsertHeadList (&IpSb->RouteTable->RouteArea[128], &RouteEntry->Link);
- IpSb->RouteTable->TotalNum++;
- } else {
- RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, DestAddress, NULL);
- if (RouteEntry == NULL) {
- NetbufFree (Packet);
- return EFI_NOT_FOUND;
- }
- RouteEntry->Flag = RouteEntry->Flag | IP6_PACKET_TOO_BIG;
- Ip6FreeRouteEntry (RouteEntry);
- }
- }
- NetbufFree (Packet);
- return EFI_SUCCESS;
- }
- /**
- Process the ICMPv6 error packet, and deliver the packet to upper layer.
- @param[in] IpSb The IP service that received the packet.
- @param[in] Head The IP head of the ICMPv6 error packet.
- @param[in] Packet The content of the ICMPv6 error with the IP head
- removed.
- @retval EFI_SUCCESS The ICMPv6 error processed successfully.
- @retval EFI_INVALID_PARAMETER The packet is invalid.
- @retval Others Failed to process the packet.
- **/
- EFI_STATUS
- Ip6ProcessIcmpError (
- IN IP6_SERVICE *IpSb,
- IN EFI_IP6_HEADER *Head,
- IN NET_BUF *Packet
- )
- {
- IP6_ICMP_ERROR_HEAD Icmp;
- //
- // Check the validity of the packet
- //
- if (Packet->TotalSize < sizeof (Icmp)) {
- goto DROP;
- }
- NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *)&Icmp);
- if (Icmp.Head.Type == ICMP_V6_PACKET_TOO_BIG) {
- return Ip6ProcessPacketTooBig (IpSb, Head, Packet);
- }
- //
- // Notify the upper-layer process that an ICMPv6 error message is received.
- //
- IP6_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;
- return Ip6Demultiplex (IpSb, Head, Packet);
- DROP:
- NetbufFree (Packet);
- Packet = NULL;
- return EFI_INVALID_PARAMETER;
- }
- /**
- Process the ICMPv6 informational messages. If it is an ICMPv6 echo
- request, answer it. If it is a MLD message, trigger MLD routines to
- process it. If it is a ND message, trigger ND routines to process it.
- Otherwise, deliver it to upper layer.
- @param[in] IpSb The IP service that receivd the packet.
- @param[in] Head The IP head of the ICMPv6 informational packet.
- @param[in] Packet The content of the ICMPv6 informational packet
- with IP head removed.
- @retval EFI_INVALID_PARAMETER The packet is invalid.
- @retval EFI_SUCCESS The ICMPv6 informational message processed.
- @retval Others Failed to process ICMPv6 informational message.
- **/
- EFI_STATUS
- Ip6ProcessIcmpInformation (
- IN IP6_SERVICE *IpSb,
- IN EFI_IP6_HEADER *Head,
- IN NET_BUF *Packet
- )
- {
- IP6_ICMP_INFORMATION_HEAD Icmp;
- EFI_STATUS Status;
- NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
- NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);
- ASSERT (Head != NULL);
- NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *)&Icmp);
- Status = EFI_INVALID_PARAMETER;
- switch (Icmp.Head.Type) {
- case ICMP_V6_ECHO_REQUEST:
- //
- // If ICMPv6 echo, reply it
- //
- if (Icmp.Head.Code == 0) {
- Status = Ip6IcmpReplyEcho (IpSb, Head, Packet);
- }
- break;
- case ICMP_V6_LISTENER_QUERY:
- Status = Ip6ProcessMldQuery (IpSb, Head, Packet);
- break;
- case ICMP_V6_LISTENER_REPORT:
- case ICMP_V6_LISTENER_REPORT_2:
- Status = Ip6ProcessMldReport (IpSb, Head, Packet);
- break;
- case ICMP_V6_NEIGHBOR_SOLICIT:
- Status = Ip6ProcessNeighborSolicit (IpSb, Head, Packet);
- break;
- case ICMP_V6_NEIGHBOR_ADVERTISE:
- Status = Ip6ProcessNeighborAdvertise (IpSb, Head, Packet);
- break;
- case ICMP_V6_ROUTER_ADVERTISE:
- Status = Ip6ProcessRouterAdvertise (IpSb, Head, Packet);
- break;
- case ICMP_V6_REDIRECT:
- Status = Ip6ProcessRedirect (IpSb, Head, Packet);
- break;
- case ICMP_V6_ECHO_REPLY:
- Status = Ip6Demultiplex (IpSb, Head, Packet);
- break;
- default:
- Status = EFI_INVALID_PARAMETER;
- break;
- }
- return Status;
- }
- /**
- Handle the ICMPv6 packet. First validate the message format,
- then, according to the message types, process it as an informational packet or
- an error packet.
- @param[in] IpSb The IP service that received the packet.
- @param[in] Head The IP head of the ICMPv6 packet.
- @param[in] Packet The content of the ICMPv6 packet with IP head
- removed.
- @retval EFI_INVALID_PARAMETER The packet is malformatted.
- @retval EFI_SUCCESS The ICMPv6 message successfully processed.
- @retval Others Failed to handle the ICMPv6 packet.
- **/
- EFI_STATUS
- Ip6IcmpHandle (
- IN IP6_SERVICE *IpSb,
- IN EFI_IP6_HEADER *Head,
- IN NET_BUF *Packet
- )
- {
- IP6_ICMP_HEAD Icmp;
- UINT16 PseudoCheckSum;
- UINT16 CheckSum;
- //
- // Check the validity of the incoming packet.
- //
- if (Packet->TotalSize < sizeof (Icmp)) {
- goto DROP;
- }
- NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *)&Icmp);
- //
- // Make sure checksum is valid.
- //
- PseudoCheckSum = NetIp6PseudoHeadChecksum (
- &Head->SourceAddress,
- &Head->DestinationAddress,
- IP6_ICMP,
- Packet->TotalSize
- );
- CheckSum = (UINT16) ~NetAddChecksum (PseudoCheckSum, NetbufChecksum (Packet));
- if (CheckSum != 0) {
- goto DROP;
- }
- //
- // According to the packet type, call corresponding process
- //
- if (Icmp.Type <= ICMP_V6_ERROR_MAX) {
- return Ip6ProcessIcmpError (IpSb, Head, Packet);
- } else {
- return Ip6ProcessIcmpInformation (IpSb, Head, Packet);
- }
- DROP:
- NetbufFree (Packet);
- return EFI_INVALID_PARAMETER;
- }
- /**
- Retrieve the Prefix address according to the PrefixLength by clear the useless
- bits.
- @param[in] PrefixLength The prefix length of the prefix.
- @param[in, out] Prefix On input, points to the original prefix address
- with dirty bits; on output, points to the updated
- address with useless bit clear.
- **/
- VOID
- Ip6GetPrefix (
- IN UINT8 PrefixLength,
- IN OUT EFI_IPv6_ADDRESS *Prefix
- )
- {
- UINT8 Byte;
- UINT8 Bit;
- UINT8 Mask;
- UINT8 Value;
- ASSERT ((Prefix != NULL) && (PrefixLength < IP6_PREFIX_MAX));
- if (PrefixLength == 0) {
- ZeroMem (Prefix, sizeof (EFI_IPv6_ADDRESS));
- return;
- }
- if (PrefixLength >= IP6_PREFIX_MAX) {
- return;
- }
- Byte = (UINT8)(PrefixLength / 8);
- Bit = (UINT8)(PrefixLength % 8);
- Value = Prefix->Addr[Byte];
- if (Byte > 0) {
- ZeroMem (Prefix->Addr + Byte, 16 - Byte);
- }
- if (Bit > 0) {
- Mask = (UINT8)(0xFF << (8 - Bit));
- Prefix->Addr[Byte] = (UINT8)(Value & Mask);
- }
- }
- /**
- Check whether the DestinationAddress is an anycast address.
- @param[in] IpSb The IP service that received the packet.
- @param[in] DestinationAddress Points to the Destination Address of the packet.
- @retval TRUE The DestinationAddress is anycast address.
- @retval FALSE The DestinationAddress is not anycast address.
- **/
- BOOLEAN
- Ip6IsAnycast (
- IN IP6_SERVICE *IpSb,
- IN EFI_IPv6_ADDRESS *DestinationAddress
- )
- {
- IP6_PREFIX_LIST_ENTRY *PrefixEntry;
- EFI_IPv6_ADDRESS Prefix;
- BOOLEAN Flag;
- ZeroMem (&Prefix, sizeof (EFI_IPv6_ADDRESS));
- Flag = FALSE;
- //
- // If the address is known as on-link or autonomous prefix, record it as
- // anycast address.
- //
- do {
- PrefixEntry = Ip6FindPrefixListEntry (IpSb, Flag, 255, DestinationAddress);
- if (PrefixEntry != NULL) {
- IP6_COPY_ADDRESS (&Prefix, &PrefixEntry->Prefix);
- Ip6GetPrefix (PrefixEntry->PrefixLength, &Prefix);
- if (EFI_IP6_EQUAL (&Prefix, DestinationAddress)) {
- return TRUE;
- }
- }
- Flag = (BOOLEAN) !Flag;
- } while (Flag);
- return FALSE;
- }
- /**
- Generate ICMPv6 error message and send it out to DestinationAddress. Currently
- Destination Unreachable message, Time Exceeded message and Parameter Problem
- message are supported.
- @param[in] IpSb The IP service that received the packet.
- @param[in] Packet The packet which invoking ICMPv6 error.
- @param[in] SourceAddress If not NULL, points to the SourceAddress.
- Otherwise, the IP layer will select a source address
- according to the DestinationAddress.
- @param[in] DestinationAddress Points to the Destination Address of the ICMPv6
- error message.
- @param[in] Type The type of the ICMPv6 message.
- @param[in] Code The additional level of the ICMPv6 message.
- @param[in] Pointer If not NULL, identifies the octet offset within
- the invoking packet where the error was detected.
- @retval EFI_INVALID_PARAMETER The packet is malformatted.
- @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the
- operation.
- @retval EFI_SUCCESS The ICMPv6 message was successfully sent out.
- @retval Others Failed to generate the ICMPv6 packet.
- **/
- EFI_STATUS
- Ip6SendIcmpError (
- IN IP6_SERVICE *IpSb,
- IN NET_BUF *Packet,
- IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL,
- IN EFI_IPv6_ADDRESS *DestinationAddress,
- IN UINT8 Type,
- IN UINT8 Code,
- IN UINT32 *Pointer OPTIONAL
- )
- {
- UINT32 PacketLen;
- NET_BUF *ErrorMsg;
- UINT16 PayloadLen;
- EFI_IP6_HEADER Head;
- IP6_ICMP_INFORMATION_HEAD *IcmpHead;
- UINT8 *ErrorBody;
- if (DestinationAddress == NULL) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // An ICMPv6 error message must not be originated as a result of receiving
- // a packet whose source address does not uniquely identify a single node --
- // e.g., the IPv6 Unspecified Address, an IPv6 multicast address, or an address
- // known by the ICMP message originator to be an IPv6 anycast address.
- //
- if (NetIp6IsUnspecifiedAddr (DestinationAddress) ||
- IP6_IS_MULTICAST (DestinationAddress) ||
- Ip6IsAnycast (IpSb, DestinationAddress)
- )
- {
- return EFI_INVALID_PARAMETER;
- }
- switch (Type) {
- case ICMP_V6_DEST_UNREACHABLE:
- case ICMP_V6_TIME_EXCEEDED:
- break;
- case ICMP_V6_PARAMETER_PROBLEM:
- if (Pointer == NULL) {
- return EFI_INVALID_PARAMETER;
- }
- break;
- default:
- return EFI_INVALID_PARAMETER;
- }
- PacketLen = sizeof (IP6_ICMP_ERROR_HEAD) + Packet->TotalSize;
- if (PacketLen > IpSb->MaxPacketSize) {
- PacketLen = IpSb->MaxPacketSize;
- }
- ErrorMsg = NetbufAlloc (PacketLen);
- if (ErrorMsg == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- PayloadLen = (UINT16)(PacketLen - sizeof (EFI_IP6_HEADER));
- //
- // Create the basic IPv6 header.
- //
- ZeroMem (&Head, sizeof (EFI_IP6_HEADER));
- Head.PayloadLength = HTONS (PayloadLen);
- Head.NextHeader = IP6_ICMP;
- Head.HopLimit = IpSb->CurHopLimit;
- if (SourceAddress != NULL) {
- IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
- } else {
- ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));
- }
- IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
- NetbufReserve (ErrorMsg, sizeof (EFI_IP6_HEADER));
- //
- // Fill in the ICMP error message head
- //
- IcmpHead = (IP6_ICMP_INFORMATION_HEAD *)NetbufAllocSpace (ErrorMsg, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
- if (IcmpHead == NULL) {
- NetbufFree (ErrorMsg);
- return EFI_OUT_OF_RESOURCES;
- }
- ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
- IcmpHead->Head.Type = Type;
- IcmpHead->Head.Code = Code;
- if (Pointer != NULL) {
- IcmpHead->Fourth = HTONL (*Pointer);
- }
- //
- // Fill in the ICMP error message body
- //
- PayloadLen -= sizeof (IP6_ICMP_INFORMATION_HEAD);
- ErrorBody = NetbufAllocSpace (ErrorMsg, PayloadLen, FALSE);
- if (ErrorBody != NULL) {
- ZeroMem (ErrorBody, PayloadLen);
- NetbufCopy (Packet, 0, PayloadLen, ErrorBody);
- }
- //
- // Transmit the packet
- //
- return Ip6Output (IpSb, NULL, NULL, ErrorMsg, &Head, NULL, 0, Ip6SysPacketSent, NULL);
- }
|