| /** @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); | |
| } |