/*
 *  Copyright (c) 2016, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 *   This file includes definitions for DHCPv6 Service.
 */

#ifndef OT_CORE_NET_DHCP6_TYPES_HPP_
#define OT_CORE_NET_DHCP6_TYPES_HPP_

#include "openthread-core-config.h"

#include "common/clearable.hpp"
#include "common/debug.hpp"
#include "common/equatable.hpp"
#include "common/message.hpp"
#include "common/random.hpp"
#include "mac/mac_types.hpp"

namespace ot {
namespace Dhcp6 {

/**
 * @addtogroup core-dhcp6
 *
 * @brief
 *   This module includes definitions for DHCPv6.
 *
 * @{
 */

constexpr uint16_t kDhcpClientPort = 546; ///< DHCP Client port number.
constexpr uint16_t kDhcpServerPort = 547; ///< DHCP Server port number.

/**
 * DHCPv6 Message Types.
 */
enum MsgType : uint8_t
{
    kMsgTypeNone               = 0,  ///< Unused message type (reserved).
    kMsgTypeSolicit            = 1,  ///< Solicit message (client sends to locate servers).
    kMsgTypeAdvertise          = 2,  ///< Advertise message (server sends to indicate it is available).
    kMsgTypeRequest            = 3,  ///< Request message (client sends to request config parameters).
    kMsgTypeConfirm            = 4,  ///< Confirm message (client sends to determine if addresses are still valid).
    kMsgTypeRenew              = 5,  ///< Renew message (client sends to extend lifetime).
    kMsgTypeRebind             = 6,  ///< Rebind message (client sends to extend leases or update config).
    kMsgTypeReply              = 7,  ///< Reply message (server sends to reply to client)
    kMsgTypeRelease            = 8,  ///< Release message (client sends to release assigned leases).
    kMsgTypeDecline            = 9,  ///< Decline message (client sends to decline one or more addresses).
    kMsgTypeReconfigure        = 10, ///< Reconfigure message (server sends to inform of new config).
    kMsgTypeInformationRequest = 11, ///< Information-request message (client sends to request without lease).
    kMsgTypeRelayForward       = 12, ///< Relay-forward message (sent by a relay agent).
    kMsgTypeRelayReply         = 13, ///< Relay-reply message (sent by a relay agent).
    kMsgTypeLeaseQuery         = 14, ///< Lease query message (sent to server to obtain info about a client lease).
    kMsgTypeLeaseQueryReply    = 15, ///< Lease query reply message (server sends to reply to lease query).
};

/**
 * Represents a DHCPv6 transaction identifier.
 */
OT_TOOL_PACKED_BEGIN
class TransactionId : public Equatable<TransactionId>, public Clearable<TransactionId>
{
public:
    /**
     * Generates a cryptographically secure random sequence to populate the transaction identifier.
     */
    void GenerateRandom(void) { SuccessOrAssert(Random::Crypto::Fill(m8)); }

private:
    static constexpr uint16_t kSize = 3;

    uint8_t m8[kSize];
} OT_TOOL_PACKED_END;

/**
 * Represents a DHCPv6 header.
 */
OT_TOOL_PACKED_BEGIN
class Header : public Clearable<Header>
{
public:
    /**
     * Returns the DHCPv6 message type.
     *
     * @returns The DHCPv6 message type.
     */
    uint8_t GetMsgType(void) const { return mMsgType; }

    /**
     * Sets the DHCPv6 message type.
     *
     * @param[in]  aType  The DHCPv6 message type.
     */
    void SetMsgType(MsgType aType) { mMsgType = aType; }

    /**
     * Returns the DHCPv6 message transaction identifier.
     *
     * @returns The DHCPv6 message transaction identifier.
     */
    const TransactionId &GetTransactionId(void) const { return mTransactionId; }

    /**
     * Sets the DHCPv6 message transaction identifier.
     *
     * @param[in]  aTransactionId  The DHCPv6 message transaction identifier.
     */
    void SetTransactionId(const TransactionId &aTransactionId) { mTransactionId = aTransactionId; }

private:
    uint8_t       mMsgType;
    TransactionId mTransactionId;
} OT_TOOL_PACKED_END;

/**
 * Represents a DHCPv6 option.
 */
OT_TOOL_PACKED_BEGIN
class Option
{
public:
    /**
     * Represents the DHCPv6 Option Codes.
     */
    enum Code : uint16_t
    {
        kClientId                  = 1,  ///< Client Identifier Option.
        kServerId                  = 2,  ///< Server Identifier Option.
        kIaNa                      = 3,  ///< Identity Association for Non-temporary Addresses Option.
        kIaTa                      = 4,  ///< Identity Association for Temporary Addresses Option.
        kIaAddress                 = 5,  ///< Identity Association Address Option.
        kOptionRequest             = 6,  ///< Option Request Option.
        kPreference                = 7,  ///< Preference Option.
        kElapsedTime               = 8,  ///< Elapsed Time Option.
        kRelayMessage              = 9,  ///< Relay Message Option.
        kAuthentication            = 11, ///< Authentication Option.
        kStatusCode                = 13, ///< Status Code Option.
        kRapidCommit               = 14, ///< Rapid Commit Option.
        kUserClass                 = 15, ///< User Class Option.
        kVendorClass               = 16, ///< Vendor Class Option.
        kVendorSpecificInformation = 17, ///< Vendor-specific Information Option.
        kInterfaceId               = 18, ///< Interface-Id Option.
        kReconfigureMessage        = 19, ///< Reconfigure Message Option.
        kReconfigureAccept         = 20, ///< Reconfigure Accept Option.
        kIaPd                      = 25, ///< Identity Association for Prefix Delegation Option.
        kIaPrefix                  = 26, ///< IA Prefix Option.
        kLeaseQuery                = 44, ///< Lease Query Option.
        kClientData                = 45, ///< Client Data Option.
        kClientLastTransactionTime = 46, ///< Client Last Transaction Time Option.
        kSolMaxRt                  = 82, ///< SOL_MAX_RT Option (Max Solicit timeout value).
    };

    /**
     * Represents an iterator for searching for and iterating over DHCPv6 options with a specific code within a message.
     */
    class Iterator : private Clearable<Iterator>
    {
        friend class Clearable<Iterator>;

    public:
        /**
         * This is an iterator constructor that initializes the iterator to a cleared (invalid) state.
         *
         * An iterator in this state must be initialized using one of the `Init()` methods before use.
         */
        Iterator(void) { Clear(); }

        /**
         * Initializes the iterator and finds the first matching option within an entire message.
         *
         * The search is performed from `aMessage.GetOffset()` to the end of the message.
         *
         * @param[in] aMessage  The message to search in.
         * @param[in] aCode     The option code to search for.
         */
        void Init(const Message &aMessage, Code aCode);

        /**
         * Initializes the iterator and finds the first matching option within a specific range of a message.
         *
         * @param[in] aMessage          The message to search in.
         * @param[in] aMsgOffsetRange   The specific range within @p aMessage to search.
         * @param[in] aCode             The option code to search for.
         */
        void Init(const Message &aMessage, const OffsetRange &aMsgOffsetRange, Code aCode);

        /**
         * Indicates whether the iteration is complete.
         *
         * The iteration is considered done when all matching options have been iterated through, or if an error
         * occurred during iteration. The `GetError()` method can be used to get the error status.
         *
         * Particularly, `IsDone() && GetError() == kErrorNone` indicates a successful end of the iteration (i.e., no
         * more matching options were found).
         *
         * @returns `true` if the iteration is complete, `false` otherwise.
         */
        bool IsDone(void) const { return mIsDone; }

        /**
         * Advances the iterator to the next matching option.
         */
        void Advance(void);

        /**
         * Gets the offset range of the current option matched by the iterator.
         *
         * The returned offset range refers to the matched option in the message when the iterator is not done
         * (`IsDone()` is `false`). Otherwise, an empty offset range is returned.
         *
         * @returns The `OffsetRange` of the current option.
         */
        const OffsetRange &GetOptionOffsetRange(void) const { return mOptionOffsetRange; }

        /**
         * Gets any error that occurred during the iteration.
         *
         * @retval kErrorNone           Successfully iterated over options (so far).
         * @retval kErrorParse          The options in the message were malformed and failed to parse.
         * @retval kErrorInvalidState   The iterator was not initialized.
         */
        Error GetError(void) const { return mError; }

    private:
        const Message *mMessage;
        OffsetRange    mMsgOffsetRange;
        OffsetRange    mOptionOffsetRange;
        Code           mCode;
        Error          mError;
        bool           mIsDone;
    };

    /**
     * Returns the DHCPv6 option code.
     *
     * @returns The DHCPv6 option code.
     */
    uint16_t GetCode(void) const { return BigEndian::HostSwap16(mCode); }

    /**
     * Sets the DHCPv6 option code.
     *
     * @param[in]  aCode  The DHCPv6 option code.
     */
    void SetCode(Code aCode) { mCode = BigEndian::HostSwap16(aCode); }

    /**
     * Returns the length of DHCPv6 option.
     *
     * @returns The length of DHCPv6 option.
     */
    uint16_t GetLength(void) const { return BigEndian::HostSwap16(mLength); }

    /**
     * Sets the length of DHCPv6 option.
     *
     * @param[in]  aLength  The length of DHCPv6 option.
     */
    void SetLength(uint16_t aLength) { mLength = BigEndian::HostSwap16(aLength); }

    /**
     * Returns the total size of DHCPv6 option in bytes.
     *
     * @returns The size of option in bytes (which includes the Code and Length fields).
     */
    uint32_t GetSize(void) const { return GetLength() + sizeof(Option); }

    /**
     * Finds the first DHCPv6 option with a given code in a message.
     *
     * This method searches the message starting from `aMessage.GetOffset()` to the end.
     *
     * @param[in]  aMessage            The message to search.
     * @param[in]  aCode               The option code to find.
     * @param[out] aOptionOffsetRange  On success, is updated to contain the offset range of the found option.
     *
     * @retval kErrorNone      The option was found successfully.
     * @retval kErrorNotFound  The option was not found in the message.
     * @retval kErrorParse     The message is malformed and cannot be parsed.
     */
    static Error FindOption(const Message &aMessage, Code aCode, OffsetRange &aOptionOffsetRange);

    /**
     * Finds the first DHCPv6 option with a given code within a specified range of a message.
     *
     * @param[in]  aMessage            The message to search.
     * @param[in]  aMsgOffsetRange     The specific range within `aMessage` to search.
     * @param[in]  aCode               The option code to find.
     * @param[out] aOptionOffsetRange  On success, is updated to contain the offset range of the found option.
     *
     * @retval kErrorNone      The option was found successfully.
     * @retval kErrorNotFound  The option was not found in the given message range.
     * @retval kErrorParse     The message is malformed and cannot be parsed.
     */
    static Error FindOption(const Message     &aMessage,
                            const OffsetRange &aMsgOffsetRange,
                            Code               aCode,
                            OffsetRange       &aOptionOffsetRange);

    /**
     * Updates the Option length in a message.
     *
     * This method should be called after all option contents are appended to the message. It uses the current
     * message length along with @p aOffset to determine the option length and then updates it within the @p aMessage.
     * The @p aOffset should point to to the start of the option in @p aMessage.
     *
     * @param[in] aMessage   The message to update.
     * @param[in] aOffset    The offset to the start of `Option` in @p aMessage.
     */
    static void UpdateOptionLengthInMessage(Message &aMessage, uint16_t aOffset);

    /**
     * Appends a DHCPv6 Option with a given code and data to a message.
     *
     * @param[in,out]  aMessage            The message to append to.
     * @param[in]      aCode               The option code to append.
     * @param[in]      aData               A pointer to buffer containing the option data to append.
     * @param[in]      aDataLength         The length of @p aData (in bytes).
     *
     * @retval kErrorNone     Successfully appended the Option to the message.
     * @retval kErrorNoBufs   Insufficient available buffers to grow the message.
     */
    static Error AppendOption(Message &aMessage, Code aCode, const void *aData, uint16_t aDataLength);

private:
    uint16_t mCode;
    uint16_t mLength;
} OT_TOOL_PACKED_END;

/**
 * Defines constants and types for DHCPv6 DUID (DHCP Unique Identifier).
 */
class Duid
{
public:
    static constexpr uint16_t kMinSize = sizeof(uint16_t) + 1;   ///< Minimum size of DUID in bytes.
    static constexpr uint16_t kMaxSize = sizeof(uint16_t) + 128; ///< Maximum size of DUID in bytes.

    /**
     * DHCPv6 Unique Identifier (DUID) Type.
     */
    enum Type : uint16_t
    {
        kTypeLinkLayerAddressPlusTime = 1, ///< Link-layer address plus time (DUID-LLT).
        kTypeVendorAssigned           = 2, ///< Vendor-assigned unique ID based on Enterprise Number (DUID-EN).
        kTypeLinkLayerAddress         = 3, ///< Link-layer address (DUID-LL).
        kTypeUniversallyUniqueId      = 4, ///< Universally Unique Identifier (DUID-UUID).
    };

    /**
     * DHCPv6 Unique Identifier (DUID) Hardware Type.
     */
    enum HardwareType : uint16_t
    {
        kHardwareTypeEthernet = 1,  ///< Ethernet HW Type.
        kHardwareTypeEui64    = 27, ///< EUI64 HW Type.
    };

private:
    Duid(void) = delete;
};

/**
 * Represents a DHCPv6 DUID based on EUI64 Link-layer address (DUID-LL).
 */
OT_TOOL_PACKED_BEGIN
class Eui64Duid
{
public:
    /**
     * Initializes the DUID-LL from a given Extended Address.
     *
     * @param[in] aExtAddress   The Extended Address.
     */
    void Init(const Mac::ExtAddress &aExtAddress);

    /**
     * Indicates whether or not the DUID-LL is valid, i.e. using the correct DUID type (`kTypeLinkLayerAddress`) and
     * Hardware Type `kHardwareTypeEui64`.
     *
     * @returns TRUE   The DUID is valid.
     * @returns FALSE  The DUID is not valid.
     */
    bool IsValid(void) const;

    /**
     * Gets the Link-layer address.
     *
     * @returns The Link-layer address.
     */
    const Mac::ExtAddress &GetLinkLayerAddress(void) const { return mLinkLayerAddress; }

private:
    uint16_t GetType(void) const { return BigEndian::HostSwap16(mDuidType); }
    void     SetType(Duid::Type aDuidType) { mDuidType = BigEndian::HostSwap16(aDuidType); }
    uint16_t GetHardwareType(void) const { return BigEndian::HostSwap16(mHardwareType); }
    void     SetHardwareType(uint16_t aHardwareType) { mHardwareType = BigEndian::HostSwap16(aHardwareType); }

    uint16_t        mDuidType;
    uint16_t        mHardwareType;
    Mac::ExtAddress mLinkLayerAddress;
} OT_TOOL_PACKED_END;

/**
 *  Implements parsing and generation of Client/Server Identifier Options.
 */
class IdOption
{
protected:
    IdOption(void) = delete;
    static Error Read(Option::Code aCode, const Message &aMessage, OffsetRange &aDuidOffsetRange);
    static Error ReadEui64(Option::Code aCode, const Message &aMessage, Mac::ExtAddress &aExtAddress);
    static Error MatchesEui64(Option::Code aCode, const Message &aMessage, const Mac::ExtAddress &aExtAddress);
    static Error Append(Option::Code aCode, Message &aMessage, const void *aDuid, uint16_t aDuidLength);
    static Error AppendEui64(Option::Code aCode, Message &aMessage, const Mac::ExtAddress &aExtAddress);
};

/**
 * Implements Client Identifier Option generation and parsing.
 */
class ClientIdOption : private IdOption
{
public:
    /**
     * Searches and reads the Client ID option from a DHCPv6 message, validating that it is a DUID based on an
     * EUI-64 Link-Layer address (DUID-LL).
     *
     * @param[in]  aMessage     The message to search and read the Client ID option from.
     * @param[out] aExtAddress  A reference to populate with the EUI-64 link-layer address on a successful read.
     *
     * @retval kErrorNone      Successfully read the Client ID as a DUID-LL. @p aExtAddress is updated.
     * @retval kErrorNotFound  The Client ID option was not found in the message.
     * @retval kErrorParse     The message is malformed, or the option was found but is not a valid DUID-LL format.
     */
    static Error ReadAsEui64Duid(const Message &aMessage, Mac::ExtAddress &aExtAddress)
    {
        return IdOption::ReadEui64(Option::kClientId, aMessage, aExtAddress);
    }

    /**
     * Appends a Client Identifier option to a DHCPv6 message.
     *
     * The appended option uses the EUI-64 Link-Layer address (DUID-LL) format.
     *
     * @param[in,out] aMessage      The message to which to append the Client ID option.
     * @param[in]     aExtAddress   The EUI-64 address to use for creating the DUID.
     *
     * @retval kErrorNone     Successfully appended the Client ID option.
     * @retval kErrorNoBufs   Insufficient available buffers to grow the message.
     */
    static Error AppendWithEui64Duid(Message &aMessage, const Mac::ExtAddress &aExtAddress)
    {
        return IdOption::AppendEui64(Option::kClientId, aMessage, aExtAddress);
    }

    /**
     * Checks if the Client Identifier option in a DHCPv6 message matches a given EUI-64 address.
     *
     * This method searches for the Client ID option, verifies it is a DUID-LL, and checks if its EUI-64 value
     * matches the given extended address.
     *
     * @param[in] aMessage     The message containing the Client ID option to check.
     * @param[in] aExtAddress  The EUI-64 address to compare against.
     *
     * @retval kErrorNone      The Client ID option was found, is a valid DUID-LL, and matches @p aExtAddress.
     * @retval kErrorNotFound  The Client ID option was not found or did not match the EUI-64 address.
     * @retval kErrorParse     The message is malformed, or the option was found but is not a valid DUID-LL format.
     */
    static Error MatchesEui64Duid(const Message &aMessage, const Mac::ExtAddress &aExtAddress)
    {
        return IdOption::MatchesEui64(Option::kClientId, aMessage, aExtAddress);
    }
};

/**
 * Implements Server Identifier Option generation and parsing.
 */
class ServerIdOption : private IdOption
{
public:
    /**
     * Searches and reads the raw DUID from the Server Identifier option in a DHCPv6 message.
     *
     * This method does not interpret the DUID type. It validates that DUID has the expected minimum DUID size. It
     * returns the `OffsetRange` corresponding to DUID in the message.
     *
     * @param[in]  aMessage          The message to search and read the Server ID option from.
     * @param[out] aDuidOffsetRange  On success, is updated to return the offset range of the Server DUID.
     *
     * @retval kErrorNone      Successfully read the Server ID option. @p aDuidOffsetRange is updated.
     * @retval kErrorNotFound  The Server ID option was not found in the message.
     * @retval kErrorParse     The message is malformed and cannot be parsed.
     */
    static Error ReadDuid(const Message &aMessage, OffsetRange &aDuidOffsetRange)
    {
        return IdOption::Read(Option::kServerId, aMessage, aDuidOffsetRange);
    }

    /**
     * Searches and reads the Server ID option, validating that it is a DUID based on an EUI-64 Link-Layer address
     * (DUID-LL).
     *
     * @param[in]  aMessage     The message to search and read the Server ID option from.
     * @param[out] aExtAddress  A reference to populate with the EUI-64 link-layer address on a successful read.
     *
     * @retval kErrorNone      Successfully read the Server ID as a DUID-LL. @p aExtAddress is updated.
     * @retval kErrorNotFound  The Server ID option was not found in the message.
     * @retval kErrorParse     The message is malformed, or the option was found but is not a valid DUID-LL format.
     */
    static Error ReadAsEui64Duid(const Message &aMessage, Mac::ExtAddress &aExtAddress)
    {
        return IdOption::ReadEui64(Option::kServerId, aMessage, aExtAddress);
    }

    /**
     * Appends a Server Identifier option to a DHCPv6 message with a given raw DUID.
     *
     * @param[in,out] aMessage      The message to which to append the Server ID option.
     * @param[in]     aDuid         A pointer to a buffer containing the DUID bytes.
     * @param[in]     aDuidLength   The DUID length in bytes.
     *
     * @retval kErrorNone     Successfully appended the Server ID option.
     * @retval kErrorNoBufs   Insufficient available buffers to grow the message.
     */
    static Error AppendWithDuid(Message &aMessage, const void *aDuid, uint16_t aDuidLength)
    {
        return IdOption::Append(Option::kServerId, aMessage, aDuid, aDuidLength);
    }

    /**
     * Appends a Server Identifier option to a DHCPv6 message.
     *
     * The appended option uses the DUID based on an EUI-64 Link-Layer address (DUID-LL) format.
     *
     * @param[in,out] aMessage      The message to which to append the Server ID option.
     * @param[in]     aExtAddress   The EUI-64 address to use for creating the DUID.
     *
     * @retval kErrorNone     Successfully appended the Server ID option.
     * @retval kErrorNoBufs   Insufficient available buffers to grow the message.
     */
    static Error AppendWithEui64Duid(Message &aMessage, const Mac::ExtAddress &aExtAddress)
    {
        return IdOption::AppendEui64(Option::kServerId, aMessage, aExtAddress);
    }
};

/**
 * Represents an Identity Association for Non-temporary Address DHCPv6 Option.
 */
OT_TOOL_PACKED_BEGIN
class IaNaOption : public Option
{
public:
    static constexpr uint32_t kDefaultT1 = 0xffffffffU; ///< Default T1 value.
    static constexpr uint32_t kDefaultT2 = 0xffffffffU; ///< Default T2 value.

    /**
     * Initializes the DHCPv6 Option.
     */
    void Init(void) { SetCode(kIaNa), SetLength(sizeof(*this) - sizeof(Option)); }

    /**
     * Returns client IAID.
     *
     * @returns The client IAID.
     */
    uint32_t GetIaid(void) const { return BigEndian::HostSwap32(mIaid); }

    /**
     * Sets the client IAID.
     *
     * @param[in]  aIaid  The client IAID.
     */
    void SetIaid(uint32_t aIaid) { mIaid = BigEndian::HostSwap32(aIaid); }

    /**
     * Returns T1.
     *
     * @returns The value of T1.
     */
    uint32_t GetT1(void) const { return BigEndian::HostSwap32(mT1); }

    /**
     * Sets the value of T1.
     *
     * @param[in]  aT1  The value of T1.
     */
    void SetT1(uint32_t aT1) { mT1 = BigEndian::HostSwap32(aT1); }

    /**
     * Returns T2.
     *
     * @returns The value of T2.
     */
    uint32_t GetT2(void) const { return BigEndian::HostSwap32(mT2); }

    /**
     * Sets the value of T2.
     *
     * @param[in]  aT2  The value of T2.
     */
    void SetT2(uint32_t aT2) { mT2 = BigEndian::HostSwap32(aT2); }

private:
    uint32_t mIaid;
    uint32_t mT1;
    uint32_t mT2;
} OT_TOOL_PACKED_END;

/**
 * Represents an Identity Association Address DHCPv6 Option.
 */
OT_TOOL_PACKED_BEGIN
class IaAddressOption : public Option
{
public:
    static constexpr uint32_t kDefaultPreferredLifetime = 0xffffffffU; ///< Default preferred lifetime.
    static constexpr uint32_t kDefaultValidLifetime     = 0xffffffffU; ///< Default valid lifetime.

    /**
     * Initializes the DHCPv6 Option.
     */
    void Init(void) { SetCode(kIaAddress), SetLength(sizeof(*this) - sizeof(Option)); }

    /**
     * Returns a reference to the IPv6 address.
     *
     * @returns A reference to the IPv6 address.
     */
    Ip6::Address &GetAddress(void) { return mAddress; }

    /**
     * Returns a reference to the IPv6 address.
     *
     * @returns A reference to the IPv6 address.
     */
    const Ip6::Address &GetAddress(void) const { return mAddress; }

    /**
     * Sets the IPv6 address.
     *
     * @param[in]  aAddress  The reference to the IPv6 address to set.
     */
    void SetAddress(const Ip6::Address &aAddress) { mAddress = aAddress; }

    /**
     * Returns the preferred lifetime of the IPv6 address.
     *
     * @returns The preferred lifetime of the IPv6 address.
     */
    uint32_t GetPreferredLifetime(void) const { return BigEndian::HostSwap32(mPreferredLifetime); }

    /**
     * Sets the preferred lifetime of the IPv6 address.
     *
     * @param[in]  aPreferredLifetime  The preferred lifetime of the IPv6 address.
     */
    void SetPreferredLifetime(uint32_t aPreferredLifetime)
    {
        mPreferredLifetime = BigEndian::HostSwap32(aPreferredLifetime);
    }

    /**
     * Returns the valid lifetime of the IPv6 address.
     *
     * @returns The valid lifetime of the IPv6 address.
     */
    uint32_t GetValidLifetime(void) const { return BigEndian::HostSwap32(mValidLifetime); }

    /**
     * Sets the valid lifetime of the IPv6 address.
     *
     * @param[in]  aValidLifetime  The valid lifetime of the IPv6 address.
     */
    void SetValidLifetime(uint32_t aValidLifetime) { mValidLifetime = BigEndian::HostSwap32(aValidLifetime); }

private:
    Ip6::Address mAddress;
    uint32_t     mPreferredLifetime;
    uint32_t     mValidLifetime;
} OT_TOOL_PACKED_END;

/**
 * Represents a Preference DHCPv6 Option.
 */
OT_TOOL_PACKED_BEGIN
class PreferenceOption : public Option
{
public:
    /**
     * Initializes the DHCPv6 Option.
     */
    void Init(void) { SetCode(kPreference), SetLength(sizeof(*this) - sizeof(Option)); }

    /**
     * Returns the preference value.
     *
     * @returns The preference value. Higher value is preferred.
     */
    uint8_t GetPreference(void) const { return mPreference; }

    /**
     * Sets the preference.
     *
     * @param[in] aPreference  The preference value.
     */
    void SetPreference(uint8_t aPreference) { mPreference = aPreference; }

private:
    uint8_t mPreference;
} OT_TOOL_PACKED_END;

/**
 * Represents an Elapsed Time DHCPv6 Option.
 */
OT_TOOL_PACKED_BEGIN
class ElapsedTimeOption : public Option
{
public:
    /**
     * Initializes the DHCPv6 Option.
     */
    void Init(void) { SetCode(kElapsedTime), SetLength(sizeof(*this) - sizeof(Option)); }

    /**
     * Returns the elapsed time.
     *
     * @returns The elapsed time (in unit of hundredths of a second).
     */
    uint16_t GetElapsedTime(void) const { return BigEndian::HostSwap16(mElapsedTime); }

    /**
     * Sets the elapsed time.
     *
     * @param[in] aElapsedTime  The elapsed time (in unit of hundredths of a second).
     */
    void SetElapsedTime(uint16_t aElapsedTime) { mElapsedTime = BigEndian::HostSwap16(aElapsedTime); }

    /**
     * Append an Elapsed Time Option to a message.
     *
     * @param[in,out] aMessage       The message to append to.
     * @param[in]     aElapsedTime   The elapsed time (in unit of hundredths of a second).
     *
     * @retval kErrorNone    Successfully appended the option.
     * @retval kErrorNoBufs  Insufficient available buffers to grow the message.
     */
    static Error AppendTo(Message &aMessage, uint16_t aElapsedTime);

private:
    uint16_t mElapsedTime;
} OT_TOOL_PACKED_END;

/**
 * Represents an Status Code DHCPv6 Option.
 */
OT_TOOL_PACKED_BEGIN
class StatusCodeOption : public Option
{
public:
    /**
     * Status Code.
     */
    enum Status : uint16_t
    {
        kSuccess          = 0,  ///< Success.
        kUnspecFail       = 1,  ///< Failure, reason unspecified.
        kNoAddrsAvail     = 2,  ///< No addresses available.
        kNoBinding        = 3,  ///< Client record (binding) unavailable.
        kNotOnLink        = 4,  ///< The prefix is not appropriate for the link.
        kUseMulticast     = 5,  ///< Force the client to send messages using All-DHCP multicast address.
        kNoPrefixAvail    = 6,  ///< Server has no prefixes available to assign.
        kUnknownQueryType = 7,  ///< The query-type is unknown to or not supported by the server.
        kMalformedQuery   = 8,  ///< The query is not valid.
        kNotConfigured    = 9,  ///< The server does not have the target address or link in its configuration.
        kNotAllowed       = 10, ///< The server does not allow the requestor to issue this LEASEQUERY.
    };

    /**
     * Initializes the DHCPv6 Option.
     */
    void Init(void) { SetCode(kStatusCode), SetLength(sizeof(*this) - sizeof(Option)); }

    /**
     * Returns the status code.
     *
     * @returns The status code.
     */
    uint16_t GetStatusCode(void) const { return BigEndian::HostSwap16(mStatus); }

    /**
     * Sets the status code.
     *
     * @param[in] aStatus The status code.
     */
    void SetStatusCode(Status aStatus) { mStatus = BigEndian::HostSwap16(aStatus); }

    /**
     * Reads the status code from a DHCPv6 message.
     *
     * This method searches the message (starting from `aMessage.GetOffset()` to the end) for a Status Code option. Per
     * RFC 8415, the absence of a Status Code option implies success. Therefore, if no Status Code option is found,
     * this method returns `kSuccess`.
     *
     * @param[in] aMessage The message to read the status code from.
     *
     * @returns The status code from the first found Status Code option, or `kSuccess` if none is found.
     */
    static Status ReadStatusFrom(const Message &aMessage);

    /**
     * Reads the status code from a specified range within a DHCPv-6 message.
     *
     * This method searches for a Status Code option only within the given `aMsgOffsetRange`. If no Status Code
     * option is found within the range, it is considered a success, and `kSuccess` is returned.
     *
     * @param[in] aMessage         The message to read the status code from.
     * @param[in] aMsgOffsetRange  The specific range within `aMessage` to search.
     *
     * @returns The status code from the first found Status Code option within the range, or `kSuccess` if none is
     *          found.
     */
    static Status ReadStatusFrom(const Message &aMessage, const OffsetRange &aMsgOffsetRange);

private:
    uint16_t mStatus;
} OT_TOOL_PACKED_END;

/**
 * Implements Rapid Commit DHCPv6 Option generation and parsing.
 */
class RapidCommitOption
{
public:
    static constexpr uint16_t kCode = Option::kRapidCommit; ///< Rapid Commit Option code.

    /*
     * Searches in a given message for Rapid Commit Option.
     *
     * @param[in] aMessage   The message to search in.
     *
     * @retval kErrorNone       The option was found successfully.
     * @retval kErrorNotFound   Did not find the option in @p aMessage.
     * @retval kErrorParse      Failed to parse the options in @p aMessage (invalid format).
     */
    static Error FindIn(const Message &aMessage);

    /**
     * Append a Rapid Commit Option to a message.
     *
     * The Rapid Commit Option contains no data fields (zero length).
     *
     * @param[in,out] aMessage   The message to append to.
     *
     * @retval kErrorNone    Successfully appended the option.
     * @retval kErrorNoBufs  Insufficient available buffers to grow the message.
     */
    static Error AppendTo(Message &aMessage);
};

/**
 * Represents an Identity Association for Prefix Delegation Option.
 */
OT_TOOL_PACKED_BEGIN
class IaPdOption : public Option
{
public:
    /**
     * Initializes the DHCPv6 Option.
     */
    void Init(void) { SetCode(kIaPd), SetLength(sizeof(*this) - sizeof(Option)); }

    /**
     * Returns IAID.
     *
     * @returns The IAID.
     */
    uint32_t GetIaid(void) const { return BigEndian::HostSwap32(mIaid); }

    /**
     * Sets the IAID.
     *
     * @param[in]  aIaid  The IAID.
     */
    void SetIaid(uint32_t aIaid) { mIaid = BigEndian::HostSwap32(aIaid); }

    /**
     * Returns T1.
     *
     * @returns The value of T1.
     */
    uint32_t GetT1(void) const { return BigEndian::HostSwap32(mT1); }

    /**
     * Sets the value of T1.
     *
     * @param[in]  aT1  The value of T1.
     */
    void SetT1(uint32_t aT1) { mT1 = BigEndian::HostSwap32(aT1); }

    /**
     * Returns T2.
     *
     * @returns The value of T2.
     */
    uint32_t GetT2(void) const { return BigEndian::HostSwap32(mT2); }

    /**
     * Sets the value of T2.
     *
     * @param[in]  aT2  The value of T2.
     */
    void SetT2(uint32_t aT2) { mT2 = BigEndian::HostSwap32(aT2); }

private:
    uint32_t mIaid;
    uint32_t mT1;
    uint32_t mT2;
    // Followed by sub-options
} OT_TOOL_PACKED_END;

/**
 * Represents an Identity Association Prefix Option.
 */
OT_TOOL_PACKED_BEGIN
class IaPrefixOption : public Option
{
public:
    /**
     * Initializes the DHCPv6 Option.
     */
    void Init(void) { SetCode(kIaPrefix), SetLength(sizeof(*this) - sizeof(Option)); }

    /**
     * Returns the preferred lifetime of the IPv6 address.
     *
     * @returns The preferred lifetime of the IPv6 address.
     */
    uint32_t GetPreferredLifetime(void) const { return BigEndian::HostSwap32(mPreferredLifetime); }

    /**
     * Sets the preferred lifetime of the IPv6 address.
     *
     * @param[in]  aPreferredLifetime  The preferred lifetime of the IPv6 address.
     */
    void SetPreferredLifetime(uint32_t aPreferredLifetime)
    {
        mPreferredLifetime = BigEndian::HostSwap32(aPreferredLifetime);
    }

    /**
     * Returns the valid lifetime of the IPv6 address.
     *
     * @returns The valid lifetime of the IPv6 address.
     */
    uint32_t GetValidLifetime(void) const { return BigEndian::HostSwap32(mValidLifetime); }

    /**
     * Sets the valid lifetime of the IPv6 address.
     *
     * @param[in]  aValidLifetime  The valid lifetime of the IPv6 address.
     */
    void SetValidLifetime(uint32_t aValidLifetime) { mValidLifetime = BigEndian::HostSwap32(aValidLifetime); }

    /**
     * Returns the prefix length in bits.
     *
     * @returns The prefix length in bits.
     */
    uint8_t GetPrefixLength(void) const { return mPrefixLength; }

    /**
     * Reads the prefix and its length from the option.
     *
     * @param[out] aPrefix  A reference to an `Ip6::Prefix` to return the prefix and its length.
     */
    void GetPrefix(Ip6::Prefix &aPrefix) const;

    /**
     * Sets the prefix and its length in the option.
     *
     * @param[in] aPrefix  An IPv6 prefix.
     */
    void SetPrefix(const Ip6::Prefix &aPrefix);

private:
    uint32_t     mPreferredLifetime;
    uint32_t     mValidLifetime;
    uint8_t      mPrefixLength;
    Ip6::Address mPrefix;
    // Can be followed by sub-options.
} OT_TOOL_PACKED_END;

/**
 * Represents an SOL_MAX_RT Option (Max Solicit timeout value).
 */
OT_TOOL_PACKED_BEGIN
class SolMaxRtOption : public Option
{
public:
    static constexpr uint32_t kMinSolMaxRt = 60;    ///< Minimum SOL_MAX_RT value.
    static constexpr uint32_t kMaxSolMaxRt = 86400; ///< Maximum SOL_MAX_RT value.

    /**
     * Initializes the DHCPv6 Option.
     */
    void Init(void) { SetCode(kSolMaxRt), SetLength(sizeof(*this) - sizeof(Option)); }

    /**
     * Returns the SOL_MAX_RT value.
     *
     * @returns The SOL_MAX_RT value (in seconds).
     */
    uint32_t GetSolMaxRt(void) const { return BigEndian::HostSwap32(mSolMaxRt); }

    /**
     * Sets the SOL_MAX_RT.
     *
     * @param[in] aSolMaxRt  The SOL_MAX_RT value (in seconds).
     */
    void SetSolMaxRt(uint32_t aSolMaxRt) { mSolMaxRt = BigEndian::HostSwap32(aSolMaxRt); }

private:
    uint32_t mSolMaxRt;
} OT_TOOL_PACKED_END;

/**
 * @}
 */

} // namespace Dhcp6
} // namespace ot

#endif // OT_CORE_NET_DHCP6_TYPES_HPP_
