/** @file
  Definition of IP6 option process routines.

  Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#pragma once

#define IP6_FRAGMENT_OFFSET_MASK  (~0x3)

//
// For more information see RFC 8200, Section 4.3, 4.4, and 4.6
//
//  This example format is from section 4.6
//  This does not apply to fragment headers
//
//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |  Next Header  |  Hdr Ext Len  |                               |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
//    |                                                               |
//    .                                                               .
//    .                  Header-Specific Data                         .
//    .                                                               .
//    |                                                               |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
//      Next Header           8-bit selector.  Identifies the type of
//                            header immediately following the extension
//                            header.  Uses the same values as the IPv4
//                            Protocol field [IANA-PN].
//
//      Hdr Ext Len           8-bit unsigned integer.  Length of the
//                            Destination Options header in 8-octet units,
//                            not including the first 8 octets.

//
// These defines apply to the following:
//   1. Hop by Hop
//   2. Routing
//   3. Destination
//
typedef struct _IP6_EXT_HDR {
  ///
  /// The Next Header field identifies the type of header immediately
  ///
  UINT8    NextHeader;
  ///
  /// The Hdr Ext Len field specifies the length of the Hop-by-Hop Options
  ///
  UINT8    HdrExtLen;
  ///
  /// Header-Specific Data
  ///
} IP6_EXT_HDR;

STATIC_ASSERT (
  sizeof (IP6_EXT_HDR) == 2,
  "The combined size of Next Header and Len is two 8 bit fields"
  );

//
// IPv6 extension headers contain an 8-bit length field which describes the size of
// the header. However, the length field only includes the size of the extension
// header options, not the size of the first 8 bytes of the header. Therefore, in
// order to calculate the full size of the extension header, we add 1 (to account
// for the first 8 bytes omitted by the length field reporting) and then multiply
// by 8 (since the size is represented in 8-byte units).
//
// a is the length field of the extension header (UINT8)
// The result may be up to 2046 octets (UINT16)
//
#define IP6_HDR_EXT_LEN(a)  (((UINT16)((UINT8)(a)) + 1) * 8)

// This is the maxmimum length permissible by a extension header
// Length is UINT8 of 8 octets not including the first 8 octets
#define IP6_MAX_EXT_DATA_LENGTH  (IP6_HDR_EXT_LEN (MAX_UINT8) - sizeof(IP6_EXT_HDR))
STATIC_ASSERT (
  IP6_MAX_EXT_DATA_LENGTH == 2046,
  "Maximum data length is ((MAX_UINT8 + 1) * 8) - 2"
  );

typedef struct _IP6_FRAGMENT_HEADER {
  UINT8     NextHeader;
  UINT8     Reserved;
  UINT16    FragmentOffset;
  UINT32    Identification;
} IP6_FRAGMENT_HEADER;

typedef struct _IP6_ROUTING_HEADER {
  UINT8    NextHeader;
  UINT8    HeaderLen;
  UINT8    RoutingType;
  UINT8    SegmentsLeft;
} IP6_ROUTING_HEADER;

typedef enum {
  Ip6OptionPad1             = 0,
  Ip6OptionPadN             = 1,
  Ip6OptionRouterAlert      = 5,
  Ip6OptionSkip             = 0,
  Ip6OptionDiscard          = 0x40,
  Ip6OptionParameterProblem = 0x80,
  Ip6OptionMask             = 0xc0,

  Ip6OptionEtherSource = 1,
  Ip6OptionEtherTarget = 2,
  Ip6OptionPrefixInfo  = 3,
  Ip6OptionRedirected  = 4,
  Ip6OptionMtu         = 5
} IP6_OPTION_TYPE;

/**
  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
  );

/**
  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
  );

/**
  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
  );

/**
  Copy the extension headers from the original to buffer. A Fragment header is
  appended to the end.

  @param[in]       NextHeader       The 8-bit selector indicates the type of
                                    the fragment header's next header.
  @param[in]       ExtHdrs          The length of the original extension header.
  @param[in]       LastHeader       The pointer of next header of last extension header.
  @param[in]       FragmentOffset   The fragment offset of the data following the header.
  @param[in]       UnFragmentHdrLen The length of unfragmented length of extension headers.
  @param[in, out]  Buf              The buffer to copy options to.
  @param[in, out]  BufLen           The length of the buffer.

  @retval EFI_SUCCESS           The options are copied over.
  @retval EFI_BUFFER_TOO_SMALL  The buffer caller provided is too small.

**/
EFI_STATUS
Ip6CopyExts (
  IN UINT8       NextHeader,
  IN UINT8       *ExtHdrs,
  IN UINT8       *LastHeader,
  IN UINT16      FragmentOffset,
  IN UINT32      UnFragmentHdrLen,
  IN OUT UINT8   *Buf,
  IN OUT UINT32  *BufLen
  );

/**
  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
  );
