mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-27 16:58:09 +01:00
680 lines
19 KiB
C
680 lines
19 KiB
C
|
/** @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 eror 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 malformated.
|
||
|
@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 malformated.
|
||
|
@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);
|
||
|
}
|
||
|
|