- assocated -> associated - malformated -> malformatted - mal-formated -> mal-formatted - formated -> formatted - octects -> octets - responsiblity -> responsibility - enought -> enough - immediatly -> immediately - integar -> integer - Alogrithem -> Algorithm - Initializeion -> Initialization - primelenght -> primelength - Vlaue -> Value - perfoms -> performs - randome -> random - verifed -> verified - finallization -> finalization - Intializes -> Initializes - specifed -> specified - if -> If - Decrption -> Decryption - Autahentication -> Authentication - informatino -> information - alogrithm -> algorithm - Authenticaion -> Authentication - Alogrithem -> Algorithm - containning -> containing - paramter -> parameter Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Gary Lin <glin@suse.com> Reviewed-By: Siyuan Fu <siyuan.fu@intel.com> Reviewed-By: Jiaxin Wu <jiaxin.wu@intel.com>
		
			
				
	
	
		
			759 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			759 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   IP6 option support functions and routines.
 | |
| 
 | |
|   Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
 | |
| 
 | |
|   This program and the accompanying materials
 | |
|   are licensed and made available under the terms and conditions of the BSD License
 | |
|   which accompanies this distribution.  The full text of the license may be found at
 | |
|   http://opensource.org/licenses/bsd-license.php.
 | |
| 
 | |
|   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
|   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "Ip6Impl.h"
 | |
| 
 | |
| /**
 | |
|   Validate the IP6 option format for both the packets we received
 | |
|   and that we will transmit. It will compute the ICMPv6 error message fields
 | |
|   if the option is malformatted.
 | |
| 
 | |
|   @param[in]  IpSb              The IP6 service data.
 | |
|   @param[in]  Packet            The to be validated packet.
 | |
|   @param[in]  Option            The first byte of the option.
 | |
|   @param[in]  OptionLen         The length of the whole option.
 | |
|   @param[in]  Pointer           Identifies the octet offset within
 | |
|                                 the invoking packet where the error was detected.
 | |
| 
 | |
| 
 | |
|   @retval TRUE     The option is properly formatted.
 | |
|   @retval FALSE    The option is malformatted.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| Ip6IsOptionValid (
 | |
|   IN IP6_SERVICE            *IpSb,
 | |
|   IN NET_BUF                *Packet,
 | |
|   IN UINT8                  *Option,
 | |
|   IN UINT8                  OptionLen,
 | |
|   IN UINT32                 Pointer
 | |
|   )
 | |
| {
 | |
|   UINT8                      Offset;
 | |
|   UINT8                      OptionType;
 | |
| 
 | |
|   Offset = 0;
 | |
| 
 | |
|   while (Offset < OptionLen) {
 | |
|     OptionType = *(Option + Offset);
 | |
| 
 | |
|     switch (OptionType) {
 | |
|     case Ip6OptionPad1:
 | |
|       //
 | |
|       // It is a Pad1 option
 | |
|       //
 | |
|       Offset++;
 | |
|       break;
 | |
|     case Ip6OptionPadN:
 | |
|       //
 | |
|       // It is a PadN option
 | |
|       //
 | |
|       Offset = (UINT8) (Offset + *(Option + Offset + 1) + 2);
 | |
|       break;
 | |
|     case Ip6OptionRouterAlert:
 | |
|       //
 | |
|       // It is a Router Alert Option
 | |
|       //
 | |
|       Offset += 4;
 | |
|       break;
 | |
|     default:
 | |
|       //
 | |
|       // The highest-order two bits specify the action must be taken if
 | |
|       // the processing IPv6 node does not recognize the option type.
 | |
|       //
 | |
|       switch (OptionType & Ip6OptionMask) {
 | |
|       case Ip6OptionSkip:
 | |
|         Offset = (UINT8) (Offset + *(Option + Offset + 1));
 | |
|         break;
 | |
|       case Ip6OptionDiscard:
 | |
|         return FALSE;
 | |
|       case Ip6OptionParameterProblem:
 | |
|         Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);
 | |
|         Ip6SendIcmpError (
 | |
|           IpSb,
 | |
|           Packet,
 | |
|           NULL,
 | |
|           &Packet->Ip.Ip6->SourceAddress,
 | |
|           ICMP_V6_PARAMETER_PROBLEM,
 | |
|           2,
 | |
|           &Pointer
 | |
|           );
 | |
|         return FALSE;
 | |
|       case Ip6OptionMask:
 | |
|         if (!IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
 | |
|           Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER);
 | |
|           Ip6SendIcmpError (
 | |
|             IpSb,
 | |
|             Packet,
 | |
|             NULL,
 | |
|             &Packet->Ip.Ip6->SourceAddress,
 | |
|             ICMP_V6_PARAMETER_PROBLEM,
 | |
|             2,
 | |
|             &Pointer
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         return FALSE;
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   }
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Validate the IP6 option format for both the packets we received
 | |
|   and that we will transmit. It supports the defined options in Neighbor
 | |
|   Discovery messages.
 | |
| 
 | |
|   @param[in]  Option            The first byte of the option.
 | |
|   @param[in]  OptionLen         The length of the whole option.
 | |
| 
 | |
|   @retval TRUE     The option is properly formatted.
 | |
|   @retval FALSE    The option is malformatted.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| Ip6IsNDOptionValid (
 | |
|   IN UINT8                  *Option,
 | |
|   IN UINT16                 OptionLen
 | |
|   )
 | |
| {
 | |
|   UINT16                    Offset;
 | |
|   UINT8                     OptionType;
 | |
|   UINT16                    Length;
 | |
| 
 | |
|   Offset = 0;
 | |
| 
 | |
|   while (Offset < OptionLen) {
 | |
|     OptionType = *(Option + Offset);
 | |
|      Length    = (UINT16) (*(Option + Offset + 1) * 8);
 | |
| 
 | |
|     switch (OptionType) {
 | |
|     case Ip6OptionPrefixInfo:
 | |
|       if (Length != 32) {
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case Ip6OptionMtu:
 | |
|       if (Length != 8) {
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       //
 | |
|       // Check the length of Ip6OptionEtherSource, Ip6OptionEtherTarget, and
 | |
|       // Ip6OptionRedirected here. For unrecognized options, silently ignore
 | |
|       // and continue processsing the message.
 | |
|       //
 | |
|       if (Length == 0) {
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     Offset = (UINT16) (Offset + Length);
 | |
|   }
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Validate whether the NextHeader is a known valid protocol or one of the user configured
 | |
|   protocols from the upper layer.
 | |
| 
 | |
|   @param[in]  IpSb          The IP6 service instance.
 | |
|   @param[in]  NextHeader    The next header field.
 | |
| 
 | |
|   @retval TRUE              The NextHeader is a known valid protocol or user configured.
 | |
|   @retval FALSE             The NextHeader is not a known valid protocol.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| Ip6IsValidProtocol (
 | |
|   IN IP6_SERVICE            *IpSb,
 | |
|   IN UINT8                  NextHeader
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY                *Entry;
 | |
|   IP6_PROTOCOL              *IpInstance;
 | |
| 
 | |
|   if (NextHeader == EFI_IP_PROTO_TCP ||
 | |
|       NextHeader == EFI_IP_PROTO_UDP ||
 | |
|       NextHeader == IP6_ICMP ||
 | |
|       NextHeader == IP6_ESP
 | |
|       ) {
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   if (IpSb == NULL) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   if (IpSb->Signature != IP6_SERVICE_SIGNATURE) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Entry, &IpSb->Children) {
 | |
|     IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);
 | |
|     if (IpInstance->State == IP6_STATE_CONFIGED) {
 | |
|       if (IpInstance->ConfigData.DefaultProtocol == NextHeader) {
 | |
|         return TRUE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Validate the IP6 extension header format for both the packets we received
 | |
|   and that we will transmit. It will compute the ICMPv6 error message fields
 | |
|   if the option is mal-formatted.
 | |
| 
 | |
|   @param[in]  IpSb          The IP6 service instance. This is an optional parameter.
 | |
|   @param[in]  Packet        The data of the packet. Ignored if NULL.
 | |
|   @param[in]  NextHeader    The next header field in IPv6 basic header.
 | |
|   @param[in]  ExtHdrs       The first byte of the option.
 | |
|   @param[in]  ExtHdrsLen    The length of the whole option.
 | |
|   @param[in]  Rcvd          The option is from the packet we received if TRUE,
 | |
|                             otherwise, the option we want to transmit.
 | |
|   @param[out] FormerHeader  The offset of NextHeader which points to Fragment
 | |
|                             Header when we received, of the ExtHdrs.
 | |
|                             Ignored if we transmit.
 | |
|   @param[out] LastHeader    The pointer of NextHeader of the last extension
 | |
|                             header processed by IP6.
 | |
|   @param[out] RealExtsLen   The length of extension headers processed by IP6 layer.
 | |
|                             This is an optional parameter that may be NULL.
 | |
|   @param[out] UnFragmentLen The length of unfragmented length of extension headers.
 | |
|                             This is an optional parameter that may be NULL.
 | |
|   @param[out] Fragmented    Indicate whether the packet is fragmented.
 | |
|                             This is an optional parameter that may be NULL.
 | |
| 
 | |
|   @retval     TRUE          The option is properly formatted.
 | |
|   @retval     FALSE         The option is malformatted.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| Ip6IsExtsValid (
 | |
|   IN IP6_SERVICE            *IpSb           OPTIONAL,
 | |
|   IN NET_BUF                *Packet         OPTIONAL,
 | |
|   IN UINT8                  *NextHeader,
 | |
|   IN UINT8                  *ExtHdrs,
 | |
|   IN UINT32                 ExtHdrsLen,
 | |
|   IN BOOLEAN                Rcvd,
 | |
|   OUT UINT32                *FormerHeader   OPTIONAL,
 | |
|   OUT UINT8                 **LastHeader,
 | |
|   OUT UINT32                *RealExtsLen    OPTIONAL,
 | |
|   OUT UINT32                *UnFragmentLen  OPTIONAL,
 | |
|   OUT BOOLEAN               *Fragmented     OPTIONAL
 | |
|   )
 | |
| {
 | |
|   UINT32                     Pointer;
 | |
|   UINT32                     Offset;
 | |
|   UINT8                      *Option;
 | |
|   UINT8                      OptionLen;
 | |
|   BOOLEAN                    Flag;
 | |
|   UINT8                      CountD;
 | |
|   UINT8                      CountA;
 | |
|   IP6_FRAGMENT_HEADER        *FragmentHead;
 | |
|   UINT16                     FragmentOffset;
 | |
|   IP6_ROUTING_HEADER         *RoutingHead;
 | |
| 
 | |
|   if (RealExtsLen != NULL) {
 | |
|     *RealExtsLen = 0;
 | |
|   }
 | |
| 
 | |
|   if (UnFragmentLen != NULL) {
 | |
|     *UnFragmentLen = 0;
 | |
|   }
 | |
| 
 | |
|   if (Fragmented != NULL) {
 | |
|     *Fragmented = FALSE;
 | |
|   }
 | |
| 
 | |
|   *LastHeader = NextHeader;
 | |
| 
 | |
|   if (ExtHdrs == NULL && ExtHdrsLen == 0) {
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   Pointer = 0;
 | |
|   Offset  = 0;
 | |
|   Flag    = FALSE;
 | |
|   CountD  = 0;
 | |
|   CountA  = 0;
 | |
| 
 | |
|   while (Offset <= ExtHdrsLen) {
 | |
| 
 | |
|     switch (*NextHeader) {
 | |
|     case IP6_HOP_BY_HOP:
 | |
|       if (Offset != 0) {
 | |
|         if (!Rcvd) {
 | |
|           return FALSE;
 | |
|         }
 | |
|         //
 | |
|         // Hop-by-Hop Options header is restricted to appear immediately after an IPv6 header only.
 | |
|         // If not, generate a ICMP parameter problem message with code value of 1.
 | |
|         //
 | |
|         if (Pointer == 0) {
 | |
|           Pointer = sizeof (EFI_IP6_HEADER);
 | |
|         } else {
 | |
|           Pointer = Offset + sizeof (EFI_IP6_HEADER);
 | |
|         }
 | |
| 
 | |
|         if ((IpSb != NULL) && (Packet != NULL) &&
 | |
|             !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
 | |
|           Ip6SendIcmpError (
 | |
|             IpSb,
 | |
|             Packet,
 | |
|             NULL,
 | |
|             &Packet->Ip.Ip6->SourceAddress,
 | |
|             ICMP_V6_PARAMETER_PROBLEM,
 | |
|             1,
 | |
|             &Pointer
 | |
|             );
 | |
|         }
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|       Flag = TRUE;
 | |
| 
 | |
|     //
 | |
|     // Fall through
 | |
|     //
 | |
|     case IP6_DESTINATION:
 | |
|       if (*NextHeader == IP6_DESTINATION) {
 | |
|         CountD++;
 | |
|       }
 | |
| 
 | |
|       if (CountD > 2) {
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|       NextHeader = ExtHdrs + Offset;
 | |
|       Pointer    = Offset;
 | |
| 
 | |
|       Offset++;
 | |
|       Option     = ExtHdrs + Offset;
 | |
|       OptionLen  = (UINT8) ((*Option + 1) * 8 - 2);
 | |
|       Option++;
 | |
|       Offset++;
 | |
| 
 | |
|       if (IpSb != NULL && Packet != NULL && !Ip6IsOptionValid (IpSb, Packet, Option, OptionLen, Offset)) {
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|       Offset = Offset + OptionLen;
 | |
| 
 | |
|       if (Flag) {
 | |
|         if (UnFragmentLen != NULL) {
 | |
|           *UnFragmentLen = Offset;
 | |
|         }
 | |
| 
 | |
|         Flag = FALSE;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case IP6_ROUTING:
 | |
|       NextHeader = ExtHdrs + Offset;
 | |
|       RoutingHead = (IP6_ROUTING_HEADER *) NextHeader;
 | |
| 
 | |
|       //
 | |
|       // Type 0 routing header is defined in RFC2460 and deprecated in RFC5095.
 | |
|       // Thus all routing types are processed as unrecognized.
 | |
|       //
 | |
|       if (RoutingHead->SegmentsLeft == 0) {
 | |
|         //
 | |
|         // Ignore the routing header and proceed to process the next header.
 | |
|         //
 | |
|         Offset = Offset + (RoutingHead->HeaderLen + 1) * 8;
 | |
| 
 | |
|         if (UnFragmentLen != NULL) {
 | |
|           *UnFragmentLen = Offset;
 | |
|         }
 | |
| 
 | |
|       } else {
 | |
|         //
 | |
|         // Discard the packet and send an ICMP Parameter Problem, Code 0, message
 | |
|         // to the packet's source address, pointing to the unrecognized routing
 | |
|         // type.
 | |
|         //
 | |
|         Pointer = Offset + 2 + sizeof (EFI_IP6_HEADER);
 | |
|         if ((IpSb != NULL) && (Packet != NULL) &&
 | |
|             !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
 | |
|           Ip6SendIcmpError (
 | |
|             IpSb,
 | |
|             Packet,
 | |
|             NULL,
 | |
|             &Packet->Ip.Ip6->SourceAddress,
 | |
|             ICMP_V6_PARAMETER_PROBLEM,
 | |
|             0,
 | |
|             &Pointer
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case IP6_FRAGMENT:
 | |
| 
 | |
|       //
 | |
|       // RFC2402, AH header should after fragment header.
 | |
|       //
 | |
|       if (CountA > 1) {
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // RFC2460, ICMP Parameter Problem message with code 0 should be sent
 | |
|       // if the length of a fragment is not a multiple of 8 octets and the M
 | |
|       // flag of that fragment is 1, pointing to the Payload length field of the
 | |
|       // fragment packet.
 | |
|       //
 | |
|       if (IpSb != NULL && Packet != NULL && (ExtHdrsLen % 8) != 0) {
 | |
|         //
 | |
|         // Check whether it is the last fragment.
 | |
|         //
 | |
|         FragmentHead = (IP6_FRAGMENT_HEADER *) (ExtHdrs + Offset);
 | |
|         if (FragmentHead == NULL) {
 | |
|           return FALSE;
 | |
|         }
 | |
| 
 | |
|         FragmentOffset = NTOHS (FragmentHead->FragmentOffset);
 | |
| 
 | |
|         if (((FragmentOffset & 0x1) == 0x1) &&
 | |
|             !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
 | |
|           Pointer = sizeof (UINT32);
 | |
|           Ip6SendIcmpError (
 | |
|             IpSb,
 | |
|             Packet,
 | |
|             NULL,
 | |
|             &Packet->Ip.Ip6->SourceAddress,
 | |
|             ICMP_V6_PARAMETER_PROBLEM,
 | |
|             0,
 | |
|             &Pointer
 | |
|             );
 | |
|           return FALSE;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (Fragmented != NULL) {
 | |
|         *Fragmented = TRUE;
 | |
|       }
 | |
| 
 | |
|       if (Rcvd && FormerHeader != NULL) {
 | |
|         *FormerHeader = (UINT32) (NextHeader - ExtHdrs);
 | |
|       }
 | |
| 
 | |
|       NextHeader = ExtHdrs + Offset;
 | |
|       Offset     = Offset + 8;
 | |
|       break;
 | |
| 
 | |
|     case IP6_AH:
 | |
|       if (++CountA > 1) {
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|       Option     = ExtHdrs + Offset;
 | |
|       NextHeader = Option;
 | |
|       Option++;
 | |
|       //
 | |
|       // RFC2402, Payload length is specified in 32-bit words, minus "2".
 | |
|       //
 | |
|       OptionLen  = (UINT8) ((*Option + 2) * 4);
 | |
|       Offset     = Offset + OptionLen;
 | |
|       break;
 | |
| 
 | |
|     case IP6_NO_NEXT_HEADER:
 | |
|       *LastHeader = NextHeader;
 | |
|       return FALSE;
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       if (Ip6IsValidProtocol (IpSb, *NextHeader)) {
 | |
| 
 | |
|         *LastHeader = NextHeader;
 | |
| 
 | |
|         if (RealExtsLen != NULL) {
 | |
|           *RealExtsLen = Offset;
 | |
|         }
 | |
| 
 | |
|         return TRUE;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // The Next Header value is unrecognized by the node, discard the packet and
 | |
|       // send an ICMP parameter problem message with code value of 1.
 | |
|       //
 | |
|       if (Offset == 0) {
 | |
|         //
 | |
|         // The Next Header directly follows IPv6 basic header.
 | |
|         //
 | |
|         Pointer = 6;
 | |
|       } else {
 | |
|         if (Pointer == 0) {
 | |
|           Pointer = sizeof (EFI_IP6_HEADER);
 | |
|         } else {
 | |
|           Pointer = Offset + sizeof (EFI_IP6_HEADER);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if ((IpSb != NULL) && (Packet != NULL) &&
 | |
|           !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) {
 | |
|         Ip6SendIcmpError (
 | |
|           IpSb,
 | |
|           Packet,
 | |
|           NULL,
 | |
|           &Packet->Ip.Ip6->SourceAddress,
 | |
|           ICMP_V6_PARAMETER_PROBLEM,
 | |
|           1,
 | |
|           &Pointer
 | |
|           );
 | |
|       }
 | |
|       return FALSE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   *LastHeader = NextHeader;
 | |
| 
 | |
|   if (RealExtsLen != NULL) {
 | |
|     *RealExtsLen = Offset;
 | |
|   }
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Generate an IPv6 router alert option in network order and output it through Buffer.
 | |
| 
 | |
|   @param[out]     Buffer         Points to a buffer to record the generated option.
 | |
|   @param[in, out] BufferLen      The length of Buffer, in bytes.
 | |
|   @param[in]      NextHeader     The 8-bit selector indicates the type of header
 | |
|                                  immediately following the Hop-by-Hop Options header.
 | |
| 
 | |
|   @retval EFI_BUFFER_TOO_SMALL   The Buffer is too small to contain the generated
 | |
|                                  option. BufferLen is updated for the required size.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The option is generated and filled in to Buffer.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip6FillHopByHop (
 | |
|   OUT UINT8                  *Buffer,
 | |
|   IN OUT UINTN               *BufferLen,
 | |
|   IN UINT8                   NextHeader
 | |
|   )
 | |
| {
 | |
|   UINT8                      BufferArray[8];
 | |
| 
 | |
|   if (*BufferLen < 8) {
 | |
|     *BufferLen = 8;
 | |
|     return EFI_BUFFER_TOO_SMALL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Form the Hop-By-Hop option in network order.
 | |
|   // NextHeader (1 octet) + HdrExtLen (1 octet) + RouterAlertOption(4 octets) + PadN
 | |
|   // The Hdr Ext Len is the length in 8-octet units, and does not including the first 8 octets.
 | |
|   //
 | |
|   ZeroMem (BufferArray, sizeof (BufferArray));
 | |
|   BufferArray[0] = NextHeader;
 | |
|   BufferArray[2] = 0x5;
 | |
|   BufferArray[3] = 0x2;
 | |
|   BufferArray[6] = 1;
 | |
| 
 | |
|   CopyMem (Buffer, BufferArray, sizeof (BufferArray));
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs.
 | |
| 
 | |
|   @param[in]  IpSb             The IP6 service instance to transmit the packet.
 | |
|   @param[in]  NextHeader       The extension header type of first extension header.
 | |
|   @param[in]  LastHeader       The extension header type of last extension header.
 | |
|   @param[in]  ExtHdrs          The length of the original extension header.
 | |
|   @param[in]  ExtHdrsLen       The length of the extension headers.
 | |
|   @param[in]  FragmentOffset   The fragment offset of the data following the header.
 | |
|   @param[out] UpdatedExtHdrs   The updated ExtHdrs with Fragment header inserted.
 | |
|                                It's caller's responsibility to free this buffer.
 | |
| 
 | |
|   @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of
 | |
|                                resource.
 | |
|   @retval EFI_UNSUPPORTED      The extension header specified in ExtHdrs is not
 | |
|                                supported currently.
 | |
|   @retval EFI_SUCCESS          The operation performed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip6FillFragmentHeader (
 | |
|   IN  IP6_SERVICE           *IpSb,
 | |
|   IN  UINT8                 NextHeader,
 | |
|   IN  UINT8                 LastHeader,
 | |
|   IN  UINT8                 *ExtHdrs,
 | |
|   IN  UINT32                ExtHdrsLen,
 | |
|   IN  UINT16                FragmentOffset,
 | |
|   OUT UINT8                 **UpdatedExtHdrs
 | |
|   )
 | |
| {
 | |
|   UINT32                    Length;
 | |
|   UINT8                     *Buffer;
 | |
|   UINT32                    FormerHeader;
 | |
|   UINT32                    Offset;
 | |
|   UINT32                    Part1Len;
 | |
|   UINT32                    HeaderLen;
 | |
|   UINT8                     Current;
 | |
|   IP6_FRAGMENT_HEADER       FragmentHead;
 | |
| 
 | |
|   if (UpdatedExtHdrs == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Length = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);
 | |
|   Buffer = AllocatePool (Length);
 | |
|   if (Buffer == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   Offset         = 0;
 | |
|   Part1Len       = 0;
 | |
|   FormerHeader   = 0;
 | |
|   Current        = NextHeader;
 | |
| 
 | |
|   while ((ExtHdrs != NULL) && (Offset <= ExtHdrsLen)) {
 | |
|     switch (NextHeader) {
 | |
|     case IP6_ROUTING:
 | |
|     case IP6_HOP_BY_HOP:
 | |
|     case IP6_DESTINATION:
 | |
|       Current      = NextHeader;
 | |
|       NextHeader   = *(ExtHdrs + Offset);
 | |
| 
 | |
|       if ((Current == IP6_DESTINATION) && (NextHeader != IP6_ROUTING)) {
 | |
|         //
 | |
|         // Destination Options header should occur at most twice, once before
 | |
|         // a Routing header and once before the upper-layer header. Here we
 | |
|         // find the one before the upper-layer header. Insert the Fragment
 | |
|         // Header before it.
 | |
|         //
 | |
|         CopyMem (Buffer, ExtHdrs, Part1Len);
 | |
|         *(Buffer + FormerHeader) = IP6_FRAGMENT;
 | |
|         //
 | |
|         // Exit the loop.
 | |
|         //
 | |
|         Offset = ExtHdrsLen + 1;
 | |
|         break;
 | |
|       }
 | |
| 
 | |
| 
 | |
|       FormerHeader = Offset;
 | |
|       HeaderLen    = (*(ExtHdrs + Offset + 1) + 1) * 8;
 | |
|       Part1Len     = Part1Len + HeaderLen;
 | |
|       Offset       = Offset + HeaderLen;
 | |
|       break;
 | |
| 
 | |
|     case IP6_FRAGMENT:
 | |
|       Current    = NextHeader;
 | |
| 
 | |
|       if (Part1Len != 0) {
 | |
|         CopyMem (Buffer, ExtHdrs, Part1Len);
 | |
|       }
 | |
| 
 | |
|       *(Buffer + FormerHeader) = IP6_FRAGMENT;
 | |
| 
 | |
|       //
 | |
|       // Exit the loop.
 | |
|       //
 | |
|       Offset = ExtHdrsLen + 1;
 | |
|       break;
 | |
| 
 | |
|     case IP6_AH:
 | |
|       Current    = NextHeader;
 | |
|       NextHeader = *(ExtHdrs + Offset);
 | |
|       //
 | |
|       // RFC2402, Payload length is specified in 32-bit words, minus "2".
 | |
|       //
 | |
|       HeaderLen  = (*(ExtHdrs + Offset + 1) + 2) * 4;
 | |
|       Part1Len   = Part1Len + HeaderLen;
 | |
|       Offset     = Offset + HeaderLen;
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       if (Ip6IsValidProtocol (IpSb, NextHeader)) {
 | |
|         Current = NextHeader;
 | |
|         CopyMem (Buffer, ExtHdrs, Part1Len);
 | |
|         *(Buffer + FormerHeader) = IP6_FRAGMENT;
 | |
|         //
 | |
|         // Exit the loop.
 | |
|         //
 | |
|         Offset = ExtHdrsLen + 1;
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       FreePool (Buffer);
 | |
|       return EFI_UNSUPPORTED;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Append the Fragment header. If the fragment offset indicates the fragment
 | |
|   // is the first fragment.
 | |
|   //
 | |
|   if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {
 | |
|     FragmentHead.NextHeader = Current;
 | |
|   } else {
 | |
|     FragmentHead.NextHeader = LastHeader;
 | |
|   }
 | |
| 
 | |
|   FragmentHead.Reserved       = 0;
 | |
|   FragmentHead.FragmentOffset = HTONS (FragmentOffset);
 | |
|   FragmentHead.Identification = mIp6Id;
 | |
| 
 | |
|   CopyMem (Buffer + Part1Len, &FragmentHead, sizeof (IP6_FRAGMENT_HEADER));
 | |
| 
 | |
|   if ((ExtHdrs != NULL) && (Part1Len < ExtHdrsLen)) {
 | |
|     //
 | |
|     // Append the part2 (fragmentable part) of Extension headers
 | |
|     //
 | |
|     CopyMem (
 | |
|       Buffer + Part1Len + sizeof (IP6_FRAGMENT_HEADER),
 | |
|       ExtHdrs + Part1Len,
 | |
|       ExtHdrsLen - Part1Len
 | |
|       );
 | |
|   }
 | |
| 
 | |
|   *UpdatedExtHdrs = Buffer;
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 |