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
Ip6IcmpReplyEcho(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)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
Ip6ProcessPacketTooBig(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)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
Ip6ProcessIcmpError(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)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
Ip6ProcessIcmpInformation(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)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
Ip6IcmpHandle(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)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
Ip6GetPrefix(IN UINT8 PrefixLength,IN OUT EFI_IPv6_ADDRESS * Prefix)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
Ip6IsAnycast(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * DestinationAddress)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
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)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