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