git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			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 malformated.
 | 
						|
 | 
						|
  @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 malformated.
 | 
						|
 | 
						|
**/
 | 
						|
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 malformated.
 | 
						|
 | 
						|
**/
 | 
						|
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-formated.
 | 
						|
 | 
						|
  @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 formated.
 | 
						|
  @retval     FALSE         The option is malformated.
 | 
						|
 | 
						|
**/
 | 
						|
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 octects 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 responsiblity 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;
 | 
						|
}
 | 
						|
 |