]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/Ip6Dxe/Ip6Icmp.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / NetworkPkg / Ip6Dxe / Ip6Icmp.c
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
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
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
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
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
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
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
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
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