Home | History | Annotate | Download | only in Ip6Dxe
      1 /** @file
      2   The ICMPv6 handle routines to process the ICMPv6 control messages.
      3 
      4   Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
      5 
      6   This program and the accompanying materials
      7   are licensed and made available under the terms and conditions of the BSD License
      8   which accompanies this distribution.  The full text of the license may be found at
      9   http://opensource.org/licenses/bsd-license.php.
     10 
     11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include "Ip6Impl.h"
     17 
     18 EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[23] = {
     19 
     20   {
     21     ICMP_V6_DEST_UNREACHABLE,
     22     ICMP_V6_NO_ROUTE_TO_DEST
     23   },
     24   {
     25     ICMP_V6_DEST_UNREACHABLE,
     26     ICMP_V6_COMM_PROHIBITED
     27   },
     28   {
     29     ICMP_V6_DEST_UNREACHABLE,
     30     ICMP_V6_BEYOND_SCOPE
     31   },
     32   {
     33     ICMP_V6_DEST_UNREACHABLE,
     34     ICMP_V6_ADDR_UNREACHABLE
     35   },
     36   {
     37     ICMP_V6_DEST_UNREACHABLE,
     38     ICMP_V6_PORT_UNREACHABLE
     39   },
     40   {
     41     ICMP_V6_DEST_UNREACHABLE,
     42     ICMP_V6_SOURCE_ADDR_FAILED
     43   },
     44   {
     45     ICMP_V6_DEST_UNREACHABLE,
     46     ICMP_V6_ROUTE_REJECTED
     47   },
     48 
     49   {
     50     ICMP_V6_PACKET_TOO_BIG,
     51     ICMP_V6_DEFAULT_CODE
     52   },
     53 
     54   {
     55     ICMP_V6_TIME_EXCEEDED,
     56     ICMP_V6_TIMEOUT_HOP_LIMIT
     57   },
     58   {
     59     ICMP_V6_TIME_EXCEEDED,
     60     ICMP_V6_TIMEOUT_REASSEMBLE
     61   },
     62 
     63   {
     64     ICMP_V6_PARAMETER_PROBLEM,
     65     ICMP_V6_ERRONEOUS_HEADER
     66   },
     67   {
     68     ICMP_V6_PARAMETER_PROBLEM,
     69     ICMP_V6_UNRECOGNIZE_NEXT_HDR
     70   },
     71   {
     72     ICMP_V6_PARAMETER_PROBLEM,
     73     ICMP_V6_UNRECOGNIZE_OPTION
     74   },
     75 
     76   {
     77     ICMP_V6_ECHO_REQUEST,
     78     ICMP_V6_DEFAULT_CODE
     79   },
     80   {
     81     ICMP_V6_ECHO_REPLY,
     82     ICMP_V6_DEFAULT_CODE
     83   },
     84 
     85   {
     86     ICMP_V6_LISTENER_QUERY,
     87     ICMP_V6_DEFAULT_CODE
     88   },
     89   {
     90     ICMP_V6_LISTENER_REPORT,
     91     ICMP_V6_DEFAULT_CODE
     92   },
     93   {
     94     ICMP_V6_LISTENER_REPORT_2,
     95     ICMP_V6_DEFAULT_CODE
     96   },
     97   {
     98     ICMP_V6_LISTENER_DONE,
     99     ICMP_V6_DEFAULT_CODE
    100   },
    101 
    102   {
    103     ICMP_V6_ROUTER_SOLICIT,
    104     ICMP_V6_DEFAULT_CODE
    105   },
    106   {
    107     ICMP_V6_ROUTER_ADVERTISE,
    108     ICMP_V6_DEFAULT_CODE
    109   },
    110   {
    111     ICMP_V6_NEIGHBOR_SOLICIT,
    112     ICMP_V6_DEFAULT_CODE
    113   },
    114   {
    115     ICMP_V6_NEIGHBOR_ADVERTISE,
    116     ICMP_V6_DEFAULT_CODE
    117   },
    118 };
    119 
    120 /**
    121   Reply an ICMPv6 echo request.
    122 
    123   @param[in]  IpSb               The IP service that received the packet.
    124   @param[in]  Head               The IP head of the ICMPv6 informational message.
    125   @param[in]  Packet             The content of the ICMPv6 message with the IP head
    126                                  removed.
    127 
    128   @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.
    129   @retval EFI_SUCCESS            Successfully answered the ICMPv6 Echo request.
    130   @retval Others                 Failed to answer the ICMPv6 Echo request.
    131 
    132 **/
    133 EFI_STATUS
    134 Ip6IcmpReplyEcho (
    135   IN IP6_SERVICE            *IpSb,
    136   IN EFI_IP6_HEADER         *Head,
    137   IN NET_BUF                *Packet
    138   )
    139 {
    140   IP6_ICMP_INFORMATION_HEAD *Icmp;
    141   NET_BUF                   *Data;
    142   EFI_STATUS                Status;
    143   EFI_IP6_HEADER            ReplyHead;
    144 
    145   Status = EFI_OUT_OF_RESOURCES;
    146   //
    147   // make a copy the packet, it is really a bad idea to
    148   // send the MNP's buffer back to MNP.
    149   //
    150   Data = NetbufDuplicate (Packet, NULL, IP6_MAX_HEADLEN);
    151   if (Data == NULL) {
    152     goto Exit;
    153   }
    154 
    155   //
    156   // Change the ICMP type to echo reply, exchange the source
    157   // and destination, then send it. The source is updated to
    158   // use specific destination. See RFC1122. SRR/RR option
    159   // update is omitted.
    160   //
    161   Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Data, 0, NULL);
    162   if (Icmp == NULL) {
    163     NetbufFree (Data);
    164     goto Exit;
    165   }
    166 
    167   Icmp->Head.Type     = ICMP_V6_ECHO_REPLY;
    168   Icmp->Head.Checksum = 0;
    169 
    170   //
    171   // Generate the IPv6 basic header
    172   // If the Echo Reply is a response to a Echo Request sent to one of the node's unicast address,
    173   // the Source address of the Echo Reply must be the same address.
    174   //
    175   ZeroMem (&ReplyHead, sizeof (EFI_IP6_HEADER));
    176 
    177   ReplyHead.PayloadLength  = HTONS ((UINT16) (Packet->TotalSize));
    178   ReplyHead.NextHeader     = IP6_ICMP;
    179   ReplyHead.HopLimit       = IpSb->CurHopLimit;
    180   IP6_COPY_ADDRESS (&ReplyHead.DestinationAddress, &Head->SourceAddress);
    181 
    182   if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {
    183     IP6_COPY_ADDRESS (&ReplyHead.SourceAddress, &Head->DestinationAddress);
    184   }
    185 
    186   //
    187   // If source is unspecified, Ip6Output will select a source for us
    188   //
    189   Status = Ip6Output (
    190              IpSb,
    191              NULL,
    192              NULL,
    193              Data,
    194              &ReplyHead,
    195              NULL,
    196              0,
    197              Ip6SysPacketSent,
    198              NULL
    199              );
    200 
    201 Exit:
    202   NetbufFree (Packet);
    203   return Status;
    204 }
    205 
    206 /**
    207   Process Packet Too Big message sent by a router in response to a packet that
    208   it cannot forward because the packet is larger than the MTU of outgoing link.
    209   Since this driver already uses IPv6 minimum link MTU as the maximum packet size,
    210   if Packet Too Big message is still received, do not reduce the packet size, but
    211   rather include a Fragment header in the subsequent packets.
    212 
    213   @param[in]  IpSb               The IP service that received the packet.
    214   @param[in]  Head               The IP head of the ICMPv6 error packet.
    215   @param[in]  Packet             The content of the ICMPv6 error with the IP head
    216                                  removed.
    217 
    218   @retval EFI_SUCCESS            The ICMPv6 error processed successfully.
    219   @retval EFI_OUT_OF_RESOURCES   Failed to finish the operation due to lack of
    220                                  resource.
    221   @retval EFI_NOT_FOUND          The packet too big message is not sent to us.
    222 
    223 **/
    224 EFI_STATUS
    225 Ip6ProcessPacketTooBig (
    226   IN IP6_SERVICE            *IpSb,
    227   IN EFI_IP6_HEADER         *Head,
    228   IN NET_BUF                *Packet
    229   )
    230 {
    231   IP6_ICMP_ERROR_HEAD       Icmp;
    232   UINT32                    Mtu;
    233   IP6_ROUTE_ENTRY           *RouteEntry;
    234   EFI_IPv6_ADDRESS          *DestAddress;
    235 
    236   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
    237   Mtu         = NTOHL (Icmp.Fourth);
    238   DestAddress = &Icmp.IpHead.DestinationAddress;
    239 
    240   if (Mtu < IP6_MIN_LINK_MTU) {
    241     //
    242     // Normally the multicast address is considered to be on-link and not recorded
    243     // in route table. Here it is added into the table since the MTU information
    244     // need be recorded.
    245     //
    246     if (IP6_IS_MULTICAST (DestAddress)) {
    247       RouteEntry = Ip6CreateRouteEntry (DestAddress, 128, NULL);
    248       if (RouteEntry == NULL) {
    249         NetbufFree (Packet);
    250         return EFI_OUT_OF_RESOURCES;
    251       }
    252 
    253       RouteEntry->Flag = IP6_DIRECT_ROUTE | IP6_PACKET_TOO_BIG;
    254       InsertHeadList (&IpSb->RouteTable->RouteArea[128], &RouteEntry->Link);
    255       IpSb->RouteTable->TotalNum++;
    256     } else {
    257       RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, DestAddress, NULL);
    258       if (RouteEntry == NULL) {
    259         NetbufFree (Packet);
    260         return EFI_NOT_FOUND;
    261       }
    262 
    263       RouteEntry->Flag = RouteEntry->Flag | IP6_PACKET_TOO_BIG;
    264 
    265       Ip6FreeRouteEntry (RouteEntry);
    266     }
    267   }
    268 
    269   NetbufFree (Packet);
    270   return EFI_SUCCESS;
    271 }
    272 
    273 /**
    274   Process the ICMPv6 error packet, and deliver the packet to upper layer.
    275 
    276   @param[in]  IpSb               The IP service that received the packet.
    277   @param[in]  Head               The IP head of the ICMPv6 error packet.
    278   @param[in]  Packet             The content of the ICMPv6 error with the IP head
    279                                  removed.
    280 
    281   @retval EFI_SUCCESS            The ICMPv6 error processed successfully.
    282   @retval EFI_INVALID_PARAMETER  The packet is invalid.
    283   @retval Others                 Failed to process the packet.
    284 
    285 **/
    286 EFI_STATUS
    287 Ip6ProcessIcmpError (
    288   IN IP6_SERVICE            *IpSb,
    289   IN EFI_IP6_HEADER         *Head,
    290   IN NET_BUF                *Packet
    291   )
    292 {
    293   IP6_ICMP_ERROR_HEAD       Icmp;
    294 
    295   //
    296   // Check the validity of the packet
    297   //
    298   if (Packet->TotalSize < sizeof (Icmp)) {
    299     goto DROP;
    300   }
    301 
    302   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
    303   if (Icmp.Head.Type == ICMP_V6_PACKET_TOO_BIG) {
    304     return Ip6ProcessPacketTooBig (IpSb, Head, Packet);
    305   }
    306 
    307   //
    308   // Notify the upper-layer process that an ICMPv6 eror message is received.
    309   //
    310   IP6_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;
    311   return Ip6Demultiplex (IpSb, Head, Packet);
    312 
    313 DROP:
    314   NetbufFree (Packet);
    315   Packet = NULL;
    316   return EFI_INVALID_PARAMETER;
    317 }
    318 
    319 /**
    320   Process the ICMPv6 informational messages. If it is an ICMPv6 echo
    321   request, answer it. If it is a MLD message, trigger MLD routines to
    322   process it. If it is a ND message, trigger ND routines to process it.
    323   Otherwise, deliver it to upper layer.
    324 
    325   @param[in]  IpSb               The IP service that receivd the packet.
    326   @param[in]  Head               The IP head of the ICMPv6 informational packet.
    327   @param[in]  Packet             The content of the ICMPv6 informational packet
    328                                  with IP head removed.
    329 
    330   @retval EFI_INVALID_PARAMETER  The packet is invalid.
    331   @retval EFI_SUCCESS            The ICMPv6 informational message processed.
    332   @retval Others                 Failed to process ICMPv6 informational message.
    333 
    334 **/
    335 EFI_STATUS
    336 Ip6ProcessIcmpInformation (
    337   IN IP6_SERVICE            *IpSb,
    338   IN EFI_IP6_HEADER         *Head,
    339   IN NET_BUF                *Packet
    340   )
    341 {
    342   IP6_ICMP_INFORMATION_HEAD Icmp;
    343   EFI_STATUS                Status;
    344 
    345   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
    346   NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);
    347   ASSERT (Head != NULL);
    348 
    349   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
    350   Status = EFI_INVALID_PARAMETER;
    351 
    352   switch (Icmp.Head.Type) {
    353   case ICMP_V6_ECHO_REQUEST:
    354     //
    355     // If ICMPv6 echo, reply it
    356     //
    357     if (Icmp.Head.Code == 0) {
    358       Status = Ip6IcmpReplyEcho (IpSb, Head, Packet);
    359     }
    360     break;
    361   case ICMP_V6_LISTENER_QUERY:
    362     Status = Ip6ProcessMldQuery (IpSb, Head, Packet);
    363     break;
    364   case ICMP_V6_LISTENER_REPORT:
    365   case ICMP_V6_LISTENER_REPORT_2:
    366     Status = Ip6ProcessMldReport (IpSb, Head, Packet);
    367     break;
    368   case ICMP_V6_NEIGHBOR_SOLICIT:
    369     Status = Ip6ProcessNeighborSolicit (IpSb, Head, Packet);
    370     break;
    371   case ICMP_V6_NEIGHBOR_ADVERTISE:
    372     Status = Ip6ProcessNeighborAdvertise (IpSb, Head, Packet);
    373     break;
    374   case ICMP_V6_ROUTER_ADVERTISE:
    375     Status = Ip6ProcessRouterAdvertise (IpSb, Head, Packet);
    376     break;
    377   case ICMP_V6_REDIRECT:
    378     Status = Ip6ProcessRedirect (IpSb, Head, Packet);
    379     break;
    380   case ICMP_V6_ECHO_REPLY:
    381     Status = Ip6Demultiplex (IpSb, Head, Packet);
    382     break;
    383   default:
    384     Status = EFI_INVALID_PARAMETER;
    385     break;
    386   }
    387 
    388   return Status;
    389 }
    390 
    391 /**
    392   Handle the ICMPv6 packet. First validate the message format,
    393   then, according to the message types, process it as an informational packet or
    394   an error packet.
    395 
    396   @param[in]  IpSb               The IP service that received the packet.
    397   @param[in]  Head               The IP head of the ICMPv6 packet.
    398   @param[in]  Packet             The content of the ICMPv6 packet with IP head
    399                                  removed.
    400 
    401   @retval EFI_INVALID_PARAMETER  The packet is malformated.
    402   @retval EFI_SUCCESS            The ICMPv6 message successfully processed.
    403   @retval Others                 Failed to handle the ICMPv6 packet.
    404 
    405 **/
    406 EFI_STATUS
    407 Ip6IcmpHandle (
    408   IN IP6_SERVICE            *IpSb,
    409   IN EFI_IP6_HEADER         *Head,
    410   IN NET_BUF                *Packet
    411   )
    412 {
    413   IP6_ICMP_HEAD             Icmp;
    414   UINT16                    PseudoCheckSum;
    415   UINT16                    CheckSum;
    416 
    417   //
    418   // Check the validity of the incoming packet.
    419   //
    420   if (Packet->TotalSize < sizeof (Icmp)) {
    421     goto DROP;
    422   }
    423 
    424   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
    425 
    426   //
    427   // Make sure checksum is valid.
    428   //
    429   PseudoCheckSum = NetIp6PseudoHeadChecksum (
    430                      &Head->SourceAddress,
    431                      &Head->DestinationAddress,
    432                      IP6_ICMP,
    433                      Packet->TotalSize
    434                      );
    435   CheckSum = (UINT16) ~NetAddChecksum (PseudoCheckSum, NetbufChecksum (Packet));
    436   if (CheckSum != 0) {
    437     goto DROP;
    438   }
    439 
    440   //
    441   // According to the packet type, call corresponding process
    442   //
    443   if (Icmp.Type <= ICMP_V6_ERROR_MAX) {
    444     return Ip6ProcessIcmpError (IpSb, Head, Packet);
    445   } else {
    446     return Ip6ProcessIcmpInformation (IpSb, Head, Packet);
    447   }
    448 
    449 DROP:
    450   NetbufFree (Packet);
    451   return EFI_INVALID_PARAMETER;
    452 }
    453 
    454 /**
    455   Retrieve the Prefix address according to the PrefixLength by clear the useless
    456   bits.
    457 
    458   @param[in]       PrefixLength  The prefix length of the prefix.
    459   @param[in, out]  Prefix        On input, points to the original prefix address
    460                                  with dirty bits; on output, points to the updated
    461                                  address with useless bit clear.
    462 
    463 **/
    464 VOID
    465 Ip6GetPrefix (
    466   IN     UINT8              PrefixLength,
    467   IN OUT EFI_IPv6_ADDRESS   *Prefix
    468   )
    469 {
    470   UINT8                     Byte;
    471   UINT8                     Bit;
    472   UINT8                     Mask;
    473   UINT8                     Value;
    474 
    475   ASSERT ((Prefix != NULL) && (PrefixLength < IP6_PREFIX_NUM));
    476 
    477   if (PrefixLength == 0) {
    478     ZeroMem (Prefix, sizeof (EFI_IPv6_ADDRESS));
    479     return ;
    480   }
    481 
    482   if (PrefixLength == IP6_PREFIX_NUM - 1) {
    483     return ;
    484   }
    485 
    486   Byte  = (UINT8) (PrefixLength / 8);
    487   Bit   = (UINT8) (PrefixLength % 8);
    488   Value = Prefix->Addr[Byte];
    489 
    490   if ((Byte > 0) && (Byte < 16)) {
    491     ZeroMem (Prefix->Addr + Byte, 16 - Byte);
    492   }
    493 
    494   if (Bit > 0) {
    495     Mask = (UINT8) (0xFF << (8 - Bit));
    496     Prefix->Addr[Byte] = (UINT8) (Value & Mask);
    497   }
    498 
    499 }
    500 
    501 /**
    502   Check whether the DestinationAddress is an anycast address.
    503 
    504   @param[in]  IpSb               The IP service that received the packet.
    505   @param[in]  DestinationAddress Points to the Destination Address of the packet.
    506 
    507   @retval TRUE                   The DestinationAddress is anycast address.
    508   @retval FALSE                  The DestinationAddress is not anycast address.
    509 
    510 **/
    511 BOOLEAN
    512 Ip6IsAnycast (
    513   IN IP6_SERVICE            *IpSb,
    514   IN EFI_IPv6_ADDRESS       *DestinationAddress
    515   )
    516 {
    517   IP6_PREFIX_LIST_ENTRY     *PrefixEntry;
    518   EFI_IPv6_ADDRESS          Prefix;
    519   BOOLEAN                   Flag;
    520 
    521   ZeroMem (&Prefix, sizeof (EFI_IPv6_ADDRESS));
    522 
    523   Flag = FALSE;
    524 
    525   //
    526   // If the address is known as on-link or autonomous prefix, record it as
    527   // anycast address.
    528   //
    529   do {
    530     PrefixEntry = Ip6FindPrefixListEntry (IpSb, Flag, 255, DestinationAddress);
    531     if (PrefixEntry != NULL) {
    532       IP6_COPY_ADDRESS (&Prefix, &PrefixEntry->Prefix);
    533       Ip6GetPrefix (PrefixEntry->PrefixLength, &Prefix);
    534       if (EFI_IP6_EQUAL (&Prefix, DestinationAddress)) {
    535         return TRUE;
    536       }
    537     }
    538 
    539     Flag = (BOOLEAN) !Flag;
    540   } while (Flag);
    541 
    542   return FALSE;
    543 }
    544 
    545 /**
    546   Generate ICMPv6 error message and send it out to DestinationAddress. Currently
    547   Destination Unreachable message, Time Exceeded message and Parameter Problem
    548   message are supported.
    549 
    550   @param[in]  IpSb               The IP service that received the packet.
    551   @param[in]  Packet             The packet which invoking ICMPv6 error.
    552   @param[in]  SourceAddress      If not NULL, points to the SourceAddress.
    553                                  Otherwise, the IP layer will select a source address
    554                                  according to the DestinationAddress.
    555   @param[in]  DestinationAddress Points to the Destination Address of the ICMPv6
    556                                  error message.
    557   @param[in]  Type               The type of the ICMPv6 message.
    558   @param[in]  Code               The additional level of the ICMPv6 message.
    559   @param[in]  Pointer            If not NULL, identifies the octet offset within
    560                                  the invoking packet where the error was detected.
    561 
    562   @retval EFI_INVALID_PARAMETER  The packet is malformated.
    563   @retval EFI_OUT_OF_RESOURCES   There is no sufficient resource to complete the
    564                                  operation.
    565   @retval EFI_SUCCESS            The ICMPv6 message was successfully sent out.
    566   @retval Others                 Failed to generate the ICMPv6 packet.
    567 
    568 **/
    569 EFI_STATUS
    570 Ip6SendIcmpError (
    571   IN IP6_SERVICE            *IpSb,
    572   IN NET_BUF                *Packet,
    573   IN EFI_IPv6_ADDRESS       *SourceAddress       OPTIONAL,
    574   IN EFI_IPv6_ADDRESS       *DestinationAddress,
    575   IN UINT8                  Type,
    576   IN UINT8                  Code,
    577   IN UINT32                 *Pointer             OPTIONAL
    578   )
    579 {
    580   UINT32                    PacketLen;
    581   NET_BUF                   *ErrorMsg;
    582   UINT16                    PayloadLen;
    583   EFI_IP6_HEADER            Head;
    584   IP6_ICMP_INFORMATION_HEAD *IcmpHead;
    585   UINT8                     *ErrorBody;
    586 
    587   if (DestinationAddress == NULL) {
    588     return EFI_INVALID_PARAMETER;
    589   }
    590 
    591   //
    592   // An ICMPv6 error message must not be originated as a result of receiving
    593   // a packet whose source address does not uniquely identify a single node --
    594   // e.g., the IPv6 Unspecified Address, an IPv6 multicast address, or an address
    595   // known by the ICMP message originator to be an IPv6 anycast address.
    596   //
    597   if (NetIp6IsUnspecifiedAddr (DestinationAddress) ||
    598       IP6_IS_MULTICAST (DestinationAddress)        ||
    599       Ip6IsAnycast (IpSb, DestinationAddress)
    600       ) {
    601     return EFI_INVALID_PARAMETER;
    602   }
    603 
    604   switch (Type) {
    605   case ICMP_V6_DEST_UNREACHABLE:
    606   case ICMP_V6_TIME_EXCEEDED:
    607     break;
    608 
    609   case ICMP_V6_PARAMETER_PROBLEM:
    610     if (Pointer == NULL) {
    611       return EFI_INVALID_PARAMETER;
    612     }
    613 
    614     break;
    615 
    616   default:
    617     return EFI_INVALID_PARAMETER;
    618   }
    619 
    620   PacketLen = sizeof (IP6_ICMP_ERROR_HEAD) + Packet->TotalSize;
    621 
    622   if (PacketLen > IpSb->MaxPacketSize) {
    623     PacketLen = IpSb->MaxPacketSize;
    624   }
    625 
    626   ErrorMsg = NetbufAlloc (PacketLen);
    627   if (ErrorMsg == NULL) {
    628     return EFI_OUT_OF_RESOURCES;
    629   }
    630 
    631   PayloadLen = (UINT16) (PacketLen - sizeof (EFI_IP6_HEADER));
    632 
    633   //
    634   // Create the basic IPv6 header.
    635   //
    636   ZeroMem (&Head, sizeof (EFI_IP6_HEADER));
    637 
    638   Head.PayloadLength  = HTONS (PayloadLen);
    639   Head.NextHeader     = IP6_ICMP;
    640   Head.HopLimit       = IpSb->CurHopLimit;
    641 
    642   if (SourceAddress != NULL) {
    643     IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
    644   } else {
    645     ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));
    646   }
    647 
    648   IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
    649 
    650   NetbufReserve (ErrorMsg, sizeof (EFI_IP6_HEADER));
    651 
    652   //
    653   // Fill in the ICMP error message head
    654   //
    655   IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (ErrorMsg, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
    656   if (IcmpHead == NULL) {
    657     NetbufFree (ErrorMsg);
    658     return EFI_OUT_OF_RESOURCES;
    659   }
    660 
    661   ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
    662   IcmpHead->Head.Type = Type;
    663   IcmpHead->Head.Code = Code;
    664 
    665   if (Pointer != NULL) {
    666     IcmpHead->Fourth = HTONL (*Pointer);
    667   }
    668 
    669   //
    670   // Fill in the ICMP error message body
    671   //
    672   PayloadLen -= sizeof (IP6_ICMP_INFORMATION_HEAD);
    673   ErrorBody =  NetbufAllocSpace (ErrorMsg, PayloadLen, FALSE);
    674   if (ErrorBody != NULL) {
    675     ZeroMem (ErrorBody, PayloadLen);
    676     NetbufCopy (Packet, 0, PayloadLen, ErrorBody);
    677   }
    678 
    679   //
    680   // Transmit the packet
    681   //
    682   return Ip6Output (IpSb, NULL, NULL, ErrorMsg, &Head, NULL, 0, Ip6SysPacketSent, NULL);
    683 }
    684 
    685