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