/** @file
 * IPRT - Network Protocols.
 */

/*
 * Copyright (C) 2008-2024 Oracle and/or its affiliates.
 *
 * This file is part of VirtualBox base platform packages, as
 * available from https://www.virtualbox.org.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, in version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses>.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
 * in the VirtualBox distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 *
 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
 */

#ifndef IPRT_INCLUDED_net_h
#define IPRT_INCLUDED_net_h
#ifndef RT_WITHOUT_PRAGMA_ONCE
# pragma once
#endif

#include <iprt/cdefs.h>
#include <iprt/types.h>
#include <iprt/assert.h>


RT_C_DECLS_BEGIN

/** @defgroup grp_rt_net     RTNet - Network Protocols
 * @ingroup grp_rt
 * @{
 */

/**
 * Converts an stringified Ethernet MAC address into the RTMAC representation.
 *
 * @todo This should be move to some generic part of the runtime.
 *
 * @returns VINF_SUCCESS on success, VERR_GETOPT_INVALID_ARGUMENT_FORMAT on
 *          failure.
 *
 * @param   pszAddr         The address string to convert.
 * @param   pMacAddr        Where to store the result.
 */
RTDECL(int) RTNetStrToMacAddr(const char *pszAddr, PRTMAC pMacAddr);

/**
 * IPv4 address.
 */
typedef RTUINT32U RTNETADDRIPV4;
AssertCompileSize(RTNETADDRIPV4, 4);
/** Pointer to a IPv4 address. */
typedef RTNETADDRIPV4 *PRTNETADDRIPV4;
/** Pointer to a const IPv4 address. */
typedef RTNETADDRIPV4 const *PCRTNETADDRIPV4;

/**
 * Tests if the given string is an IPv4 address.
 *
 * @returns boolean.
 * @param   pcszAddr        String which may be an IPv4 address.
 */
RTDECL(bool) RTNetIsIPv4AddrStr(const char *pcszAddr);

/**
 * Tests if the given string is a wildcard IPv4 address.
 *
 * @returns boolean.
 * @param   pcszAddr        String which may be an IPv4 address.
 */
RTDECL(bool) RTNetStrIsIPv4AddrAny(const char *pcszAddr);

/**
 * Parses dotted-decimal IPv4 address into RTNETADDRIPV4 representation.
 *
 * @returns VINF_SUCCESS on success, VERR_INVALID_PARAMETER on
 *          failure.
 *
 * @param   pcszAddr        The value to convert.
 * @param   ppszNext        Where to store the pointer to the first char
 *                            following the address. (Optional)
 * @param   pAddr           Where to store the result.
 */
RTDECL(int) RTNetStrToIPv4AddrEx(const char *pcszAddr, PRTNETADDRIPV4 pAddr, char **ppszNext);

/**
 * Parses dotted-decimal IPv4 address into RTNETADDRIPV4 representation.
 * Leading and trailing whitespace is ignored.
 *
 * @returns VINF_SUCCESS on success, VERR_INVALID_PARAMETER on
 *          failure.
 *
 * @param   pcszAddr        The value to convert.
 * @param   pAddr           Where to store the result.
 */
RTDECL(int) RTNetStrToIPv4Addr(const char *pcszAddr, PRTNETADDRIPV4 pAddr);

/**
 * Parses dotted-decimal IPv4 CIDR notation into RTNETADDRIPV4
 * representation and prefix length.  Missing prefix specification is
 * treated as exact address specification (prefix length 32).  Leading
 * and trailing whitespace is ignored.
 *
 * @returns VINF_SUCCESS on success, VERR_INVALID_PARAMETER on
 *          failure.
 *
 * @param   pcszAddr        The value to convert.
 * @param   pAddr           Where to store the address.
 * @param   piPrefix        Where to store the prefix length;
 */
RTDECL(int) RTNetStrToIPv4Cidr(const char *pcszAddr, PRTNETADDRIPV4 pAddr, int *piPrefix);

/**
 * Verifies that RTNETADDRIPV4 is a valid contiguous netmask and
 * computes its prefix length.
 *
 * @returns VINF_SUCCESS on success, VERR_INVALID_PARAMETER on
 *          failure.
 *
 * @param   pMask           The netmask to verify and convert.
 * @param   piPrefix        Where to store the prefix length. (Optional)
 */
RTDECL(int) RTNetMaskToPrefixIPv4(PCRTNETADDRIPV4 pMask, int *piPrefix);

/**
 * Computes netmask corresponding to the prefix length.
 *
 * @returns VINF_SUCCESS on success, VERR_INVALID_PARAMETER on
 *          failure.
 *
 * @param   iPrefix         The prefix to convert.
 * @param   pMask           Where to store the netmask.
 */
RTDECL(int) RTNetPrefixToMaskIPv4(int iPrefix, PRTNETADDRIPV4 pMask);

/**
 * Initializes an IPv4 address from four octets.
 *
 * @returns The address (network endian).
 * @param   b3          The 4th and the most significant address octet.
 * @param   b2          The 3rd address octet.
 * @param   b1          The 2nd address octet.
 * @param   b0          The 1st and least significant address octet.
 */
DECLINLINE(RTNETADDRIPV4) RTNetIPv4AddrFromU8(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0)
{
    RTNETADDRIPV4 Ret;
#ifdef RT_LITTLE_ENDIAN
    Ret.u = RT_MAKE_U32_FROM_U8(    b3, b2, b1, b0);
#else
    Ret.u = RT_MAKE_U32_FROM_MSB_U8(b3, b2, b1, b0);
#endif
    return Ret;
}

/**
 * Initializes an IPv4 address from four octets, host endian version.
 *
 * @returns The address (host endian).
 * @param   b3          The 4th and the most significant address octet.
 * @param   b2          The 3rd address octet.
 * @param   b1          The 2nd address octet.
 * @param   b0          The 1st and least significant address octet.
 */
DECLINLINE(RTNETADDRIPV4) RTNetIPv4AddrHEFromU8(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0)
{
    RTNETADDRIPV4 Ret;
    Ret.u = RT_MAKE_U32_FROM_MSB_U8(b3, b2, b1, b0);
    return Ret;
}

#ifdef RTNET_INCL_IN_ADDR

/**
 * Initializes an struct in_addr from four octets
 *
 * @returns The in_addr (network endian).
 * @param   b3          The 4th and the most significant address octet.
 * @param   b2          The 3rd address octet.
 * @param   b1          The 2nd address octet.
 * @param   b0          The 1st and least significant address octet.
 */
DECLINLINE(struct in_addr) RTNetInAddrFromU8(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0)
{
    struct in_addr Ret;
# ifdef RT_LITTLE_ENDIAN
    Ret.s_addr = RT_MAKE_U32_FROM_U8(    b3, b2, b1, b0);
# else
    Ret.s_addr = RT_MAKE_U32_FROM_MSB_U8(b3, b2, b1, b0);
# endif
    return Ret;
}

/**
 * Converts an IPv4 address to the struct in_addr format.
 *
 * @returns Converted address (network endian).
 * @param   pAddr           The IPv4 address to convert (network endian).
 */
DECLINLINE(struct in_addr) RTNetIPv4AddrToInAddr(PCRTNETADDRIPV4 pAddr)
{
    struct in_addr Ret;
    Ret.s_addr = pAddr->u;
    return Ret;
}

# ifdef IPRT_INCLUDED_asm_h /* for ASMByteSwapU32 */
/**
 * Converts an IPv4 address to the struct in_addr format, host endian version.
 *
 * @returns Converted address (network endian).
 * @param   pAddr           The IPv4 address to convert - host endian.
 */
DECLINLINE(struct in_addr) RTNetIPv4AddrHEToInAddr(PCRTNETADDRIPV4 pAddr)
{
    struct in_addr Ret;
    Ret.s_addr = RT_H2N_U32(pAddr->u);
    return Ret;
}
# endif

/**
 * Converts an IPv4 address to the struct in_addr format.
 *
 * @returns Converted address (network endian).
 * @param   pInAddr         The in_addr to convert (network endian).
 */
DECLINLINE(RTNETADDRIPV4) RTNetIPv4AddrFromInAddr(struct in_addr const *pInAddr)
{
    RTNETADDRIPV4 Ret;
    Ret.u = pInAddr->s_addr;
    return Ret;
}

# ifdef IPRT_INCLUDED_asm_h /* for ASMByteSwapU32 */
/**
 * Converts an IPv4 address to the struct in_addr format, host endian version.
 *
 * @returns Converted address (host endian).
 * @param   pInAddr         The in_addr to convert (network endian).
 */
DECLINLINE(RTNETADDRIPV4) RTNetIPv4AddrHEFromInAddr(struct in_addr const *pInAddr)
{
    RTNETADDRIPV4 Ret;
    Ret.u = RT_N2H_U32(pInAddr->s_addr);
    return Ret;
}
# endif

#endif /* RTNET_INCL_IN_ADDR */


/**
 * IPv6 address.
 */
typedef RTUINT128U RTNETADDRIPV6;
AssertCompileSize(RTNETADDRIPV6, 16);
/** Pointer to a IPv6 address. */
typedef RTNETADDRIPV6 *PRTNETADDRIPV6;
/** Pointer to a const IPv6 address. */
typedef RTNETADDRIPV6 const *PCRTNETADDRIPV6;

/**
 * Tests if the given string is a valid IPv6 address.
 *
 * @returns @c true if it is, @c false if not.
 * @param   pszAddress          String which may be an IPv6 address.
 */
RTDECL(bool) RTNetIsIPv6AddrStr(const char *pszAddress);

/**
 * Tests if the given string is a wildcard IPv6 address.
 *
 * @returns @c true if it is, @c false if not.
 * @param   pszAddress          String which may be an IPv6 address.
 */
RTDECL(bool) RTNetStrIsIPv6AddrAny(const char *pszAddress);

/**
 * Parses IPv6 address into RTNETADDRIPV6 representation.
 *
 * @returns VINF_SUCCESS on success, VERR_INVALID_PARAMETER on
 *          failure.
 *
 * @param   pcszAddr        The value to convert.
 * @param   ppszNext        Where to store the pointer to the first char
 *                            following the address. (Optional)
 * @param   pAddr           Where to store the result.
 */
RTDECL(int) RTNetStrToIPv6AddrEx(const char *pcszAddr, PRTNETADDRIPV6 pAddr, char **ppszNext);

/**
 * Parses IPv6 address into RTNETADDRIPV6 representation.
 * Leading and trailing whitespace is ignored.
 *
 * @returns VINF_SUCCESS on success, VERR_INVALID_PARAMETER on
 *          failure.
 *
 * @param   pcszAddr        The value to convert.
 * @param   ppszZone        Where to store the pointer to the first char
 *                            of the zone id.  NULL is stored if there is
 *                            no zone id.
 * @param   pAddr           Where to store the result.
 */
RTDECL(int) RTNetStrToIPv6Addr(const char *pcszAddr, PRTNETADDRIPV6 pAddr, char **ppszZone);

/**
 * Verifies that RTNETADDRIPV6 is a valid contiguous netmask and
 * computes its prefix length.
 *
 * @returns VINF_SUCCESS on success, VERR_INVALID_PARAMETER on
 *          failure.
 *
 * @param   pMask           The netmask to verify and convert.
 * @param   piPrefix        Where to store the prefix length. (Optional)
 */
RTDECL(int) RTNetMaskToPrefixIPv6(PCRTNETADDRIPV6 pMask, int *piPrefix);

/**
 * Computes netmask corresponding to the prefix length.
 *
 * @returns VINF_SUCCESS on success, VERR_INVALID_PARAMETER on
 *          failure.
 *
 * @param   iPrefix         The prefix to convert.
 * @param   pMask           Where to store the netmask.
 */
RTDECL(int) RTNetPrefixToMaskIPv6(int iPrefix, PRTNETADDRIPV6 pMask);

/**
 * Parses IPv6 prefix notation into RTNETADDRIPV6 representation and
 * prefix length.  Missing prefix specification is treated as exact
 * address specification (prefix length 128).  Leading and trailing
 * whitespace is ignored.
 *
 * "CIDR" in the name is a misnomer as IPv6 doesn't have network
 * classes, but is parallel to the IPv4 name (and naming things is
 * hard).
 *
 * @returns VINF_SUCCESS on success, VERR_INVALID_PARAMETER on
 *          failure.
 *
 * @param   pcszAddr        The value to convert.
 * @param   pAddr           Where to store the address.
 * @param   piPrefix        Where to store the prefix length;
 */
RTDECL(int) RTNetStrToIPv6Cidr(const char *pcszAddr, PRTNETADDRIPV6 pAddr, int *piPrefix);


/**
 * IPX address.
 */
#pragma pack(1)
typedef struct RTNETADDRIPX
{
    /** The network ID. */
    uint32_t Network;
    /** The node ID. (Defaults to the MAC address apparently.) */
    RTMAC Node;
} RTNETADDRIPX;
#pragma pack()
AssertCompileSize(RTNETADDRIPX, 4+6);
/** Pointer to an IPX address. */
typedef RTNETADDRIPX *PRTNETADDRIPX;
/** Pointer to a const IPX address. */
typedef RTNETADDRIPX const *PCRTNETADDRIPX;

/**
 * Network address union.
 *
 * @remarks The size of this structure may change in the future.
 */
typedef union RTNETADDRU
{
    /** 64-bit view. */
    uint64_t au64[2];
    /** 32-bit view. */
    uint32_t au32[4];
    /** 16-bit view. */
    uint16_t au16[8];
    /** 8-bit view. */
    uint8_t  au8[16];
    /** IPv4 view. */
    RTNETADDRIPV4 IPv4;
#ifndef IPv6 /* Work around X11 and RDP defining IPv6 to 1. */
    /** IPv6 view. */
    RTNETADDRIPV6 IPv6;
#endif
    /** IPX view. */
    RTNETADDRIPX Ipx;
    /** MAC address view. */
    RTMAC Mac;
} RTNETADDRU;
AssertCompileSize(RTNETADDRU, 16);
/** Pointer to an address union. */
typedef RTNETADDRU *PRTNETADDRU;
/** Pointer to a const address union. */
typedef RTNETADDRU const *PCRTNETADDRU;

/**
 * Network address type.
 *
 * @remarks The value assignments may change in the future.
 */
typedef enum RTNETADDRTYPE
{
    /** The invalid 0 entry. */
    RTNETADDRTYPE_INVALID = 0,
    /** IP version 4. */
    RTNETADDRTYPE_IPV4,
    /** IP version 6. */
    RTNETADDRTYPE_IPV6,
    /** IPX. */
    RTNETADDRTYPE_IPX,
    /** MAC address. */
    RTNETADDRTYPE_MAC,
    /** The end of the valid values. */
    RTNETADDRTYPE_END,
    /** The usual 32-bit hack. */
    RTNETADDRTYPE_32_BIT_HACK = 0x7fffffff
} RTNETADDRTYPE;
/** Pointer to a network address type. */
typedef RTNETADDRTYPE *PRTNETADDRTYPE;
/** Pointer to a const network address type. */
typedef RTNETADDRTYPE const  *PCRTNETADDRTYPE;

/**
 * Network address.
 *
 * @remarks The size and type values may change.
 */
typedef struct RTNETADDR
{
    /** The address union. */
    RTNETADDRU      uAddr;
    /** Indicates which view of @a u that is valid. */
    RTNETADDRTYPE   enmType;
    /** The port number for IPv4 and IPv6 addresses.  This is set to
     *  RTNETADDR_NA_PORT if not applicable. */
    uint32_t        uPort;
} RTNETADDR;
/** Pointer to a network address. */
typedef RTNETADDR *PRTNETADDR;
/** Pointer to a const network address. */
typedef RTNETADDR const *PCRTNETADDR;

/** The not applicable value of  RTNETADDR::uPort value use to inid. */
#define RTNETADDR_PORT_NA       UINT32_MAX

/**
 * Ethernet header.
 */
#pragma pack(1)
typedef struct RTNETETHERHDR
{
    RTMAC       DstMac;
    RTMAC       SrcMac;
    /** Ethernet frame type or frame size, depending on the kind of ethernet.
     * This is big endian on the wire. */
    uint16_t    EtherType;
} RTNETETHERHDR;
#pragma pack()
AssertCompileSize(RTNETETHERHDR, 14);
/** Pointer to an ethernet header. */
typedef RTNETETHERHDR *PRTNETETHERHDR;
/** Pointer to a const ethernet header. */
typedef RTNETETHERHDR const *PCRTNETETHERHDR;

/** @name EtherType (RTNETETHERHDR::EtherType)
 * @{ */
#define RTNET_ETHERTYPE_IPV4    UINT16_C(0x0800)
#define RTNET_ETHERTYPE_ARP     UINT16_C(0x0806)
#define RTNET_ETHERTYPE_IPV6    UINT16_C(0x86dd)
#define RTNET_ETHERTYPE_VLAN    UINT16_C(0x8100)
#define RTNET_ETHERTYPE_IPX_1   UINT16_C(0x8037)
#define RTNET_ETHERTYPE_IPX_2   UINT16_C(0x8137)
#define RTNET_ETHERTYPE_IPX_3   UINT16_C(0x8138)
/** @} */


/**
 * IPv4 header.
 * All is bigendian on the wire.
 */
#pragma pack(1)
typedef struct RTNETIPV4
{
#ifdef RT_BIG_ENDIAN
    unsigned int    ip_v : 4;
    unsigned int    ip_hl : 4;
    unsigned int    ip_tos : 8;
    unsigned int    ip_len : 16;
#else
    /** 00:0 - Header length given as a 32-bit word count. */
    unsigned int    ip_hl : 4;
    /** 00:4 - Header version. */
    unsigned int    ip_v : 4;
    /** 01 - Type of service. */
    unsigned int    ip_tos : 8;
    /** 02 - Total length (header + data). */
    unsigned int    ip_len : 16;
#endif
    /** 04 - Packet idenficiation. */
    uint16_t        ip_id;
    /** 06 - Offset if fragmented. */
    uint16_t        ip_off;
    /** 08 - Time to live. */
    uint8_t         ip_ttl;
    /** 09 - Protocol. */
    uint8_t         ip_p;
    /** 0a - Header check sum. */
    uint16_t        ip_sum;
    /** 0c - Source address. */
    RTNETADDRIPV4   ip_src;
    /** 10 - Destination address. */
    RTNETADDRIPV4   ip_dst;
    /** 14 - Options (optional). */
    uint32_t        ip_options[1];
} RTNETIPV4;
#pragma pack()
AssertCompileSize(RTNETIPV4, 6 * 4);
/** Pointer to a IPv4 header. */
typedef RTNETIPV4 *PRTNETIPV4;
/** Pointer to a const IPv4 header. */
typedef RTNETIPV4 const *PCRTNETIPV4;

/** The minimum IPv4 header length (in bytes).
 * Up to and including RTNETIPV4::ip_dst. */
#define RTNETIPV4_MIN_LEN   (20)


/** @name IPv4 Protocol Numbers
 * @{ */
/** IPv4: ICMP */
#define RTNETIPV4_PROT_ICMP     (1)
/** IPv4: TCP */
#define RTNETIPV4_PROT_TCP      (6)
/** IPv4: UDP */
#define RTNETIPV4_PROT_UDP      (17)
/** @} */

/** @name Common IPv4 Port Assignments
 * @{
 */
/** Boostrap Protocol / DHCP) Server. */
#define RTNETIPV4_PORT_BOOTPS   (67)
/** Boostrap Protocol / DHCP) Client. */
#define RTNETIPV4_PORT_BOOTPC   (68)
/** @} */

/** @name IPv4 Flags
 * @{ */
/** IPv4: Don't fragment */
#define RTNETIPV4_FLAGS_DF      (0x4000)
/** IPv4: More fragments */
#define RTNETIPV4_FLAGS_MF      (0x2000)
/** @} */

RTDECL(uint16_t) RTNetIPv4HdrChecksum(PCRTNETIPV4 pIpHdr);
RTDECL(bool)     RTNetIPv4IsHdrValid(PCRTNETIPV4 pIpHdr, size_t cbHdrMax, size_t cbPktMax, bool fChecksum);
RTDECL(uint32_t) RTNetIPv4PseudoChecksum(PCRTNETIPV4 pIpHdr);
RTDECL(uint32_t) RTNetIPv4PseudoChecksumBits(RTNETADDRIPV4 SrcAddr, RTNETADDRIPV4 DstAddr, uint8_t bProtocol, uint16_t cbPkt);
RTDECL(uint32_t) RTNetIPv4AddDataChecksum(void const *pvData, size_t cbData, uint32_t u32Sum, bool *pfOdd);
RTDECL(uint16_t) RTNetIPv4FinalizeChecksum(uint32_t u32Sum);


/**
 * IPv6 header.
 * All is bigendian on the wire.
 */
#pragma pack(1)
typedef struct RTNETIPV6
{
    /** Version (4 bits), Traffic Class (8 bits) and Flow Lable (20 bits).
     * @todo this is probably mislabeled - ip6_flow vs. ip6_vfc, fix later. */
    uint32_t        ip6_vfc;
    /** 04 - Payload length, including extension headers. */
    uint16_t        ip6_plen;
    /** 06 - Next header type (RTNETIPV4_PROT_XXX). */
    uint8_t         ip6_nxt;
    /** 07 - Hop limit. */
    uint8_t         ip6_hlim;
    /** xx - Source address. */
    RTNETADDRIPV6   ip6_src;
    /** xx - Destination address. */
    RTNETADDRIPV6   ip6_dst;
} RTNETIPV6;
#pragma pack()
AssertCompileSize(RTNETIPV6, 8 + 16 + 16);
/** Pointer to a IPv6 header. */
typedef RTNETIPV6 *PRTNETIPV6;
/** Pointer to a const IPv6 header. */
typedef RTNETIPV6 const *PCRTNETIPV6;

/** The minimum IPv6 header length (in bytes).
 * Up to and including RTNETIPV6::ip6_dst. */
#define RTNETIPV6_MIN_LEN                           (40)
#define RTNETIPV6_ICMPV6_ND_WITH_LLA_OPT_MIN_LEN    (32)

RTDECL(uint32_t) RTNetIPv6PseudoChecksum(PCRTNETIPV6 pIpHdr);
RTDECL(uint32_t) RTNetIPv6PseudoChecksumEx(PCRTNETIPV6 pIpHdr, uint8_t bProtocol, uint16_t cbPkt);
RTDECL(uint32_t) RTNetIPv6PseudoChecksumBits(PCRTNETADDRIPV6 pSrcAddr, PCRTNETADDRIPV6 pDstAddr,
                                             uint8_t bProtocol, uint16_t cbPkt);


/**
 * UDP header.
 */
#pragma pack(1)
typedef struct RTNETUDP
{
    /** The source port. */
    uint16_t    uh_sport;
    /** The destination port. */
    uint16_t    uh_dport;
    /** The length of the UDP header and associated data. */
    uint16_t    uh_ulen;
    /** The checksum of the pseudo header, the UDP header and the data. */
    uint16_t    uh_sum;
} RTNETUDP;
#pragma pack()
AssertCompileSize(RTNETUDP, 8);
/** Pointer to an UDP header. */
typedef RTNETUDP *PRTNETUDP;
/** Pointer to a const UDP header. */
typedef RTNETUDP const *PCRTNETUDP;

/** The minimum UDP packet length (in bytes). (RTNETUDP::uh_ulen) */
#define RTNETUDP_MIN_LEN   (8)

RTDECL(uint16_t) RTNetUDPChecksum(uint32_t u32Sum, PCRTNETUDP pUdpHdr);
RTDECL(uint32_t) RTNetIPv4AddUDPChecksum(PCRTNETUDP pUdpHdr, uint32_t u32Sum);
RTDECL(uint16_t) RTNetIPv4UDPChecksum(PCRTNETIPV4 pIpHdr, PCRTNETUDP pUdpHdr, void const *pvData);
RTDECL(bool)     RTNetIPv4IsUDPSizeValid(PCRTNETIPV4 pIpHdr, PCRTNETUDP pUdpHdr, size_t cbPktMax);
RTDECL(bool)     RTNetIPv4IsUDPValid(PCRTNETIPV4 pIpHdr, PCRTNETUDP pUdpHdr, void const *pvData, size_t cbPktMax, bool fChecksum);


/**
 * IPv4 BOOTP / DHCP packet.
 */
#pragma pack(1)
typedef struct RTNETBOOTP
{
    /** 00 - The packet opcode (RTNETBOOTP_OP_*). */
    uint8_t         bp_op;
    /** 01 - Hardware address type. Same as RTNETARPHDR::ar_htype.  */
    uint8_t         bp_htype;
    /** 02 - Hardware address length. */
    uint8_t         bp_hlen;
    /** 03 - Gateway hops. */
    uint8_t         bp_hops;
    /** 04 - Transaction ID. */
    uint32_t        bp_xid;
    /** 08 - Seconds since boot started. */
    uint16_t        bp_secs;
    /** 0a - Unused (BOOTP) / Flags (DHCP) (RTNET_DHCP_FLAGS_*).  */
    uint16_t        bp_flags;
    /** 0c - Client IPv4 address. */
    RTNETADDRIPV4   bp_ciaddr;
    /** 10 - Your IPv4 address. */
    RTNETADDRIPV4   bp_yiaddr;
    /** 14 - Server IPv4 address. */
    RTNETADDRIPV4   bp_siaddr;
    /** 18 - Gateway IPv4 address. */
    RTNETADDRIPV4   bp_giaddr;
    /** 1c - Client hardware address. */
    union
    {
        uint8_t     au8[16];
        RTMAC       Mac;
    }               bp_chaddr;
    /** 2c - Server name. */
    uint8_t         bp_sname[64];
    /** 6c - File name / more DHCP options. */
    uint8_t         bp_file[128];
    /** ec - Vendor specific area (BOOTP) / Options (DHCP).
     * @remark This is really 312 bytes in the DHCP version. */
    union
    {
        uint8_t         au8[128];
        struct DHCP
        {
            /** ec - The DHCP cookie (RTNET_DHCP_COOKIE). */
            uint32_t    dhcp_cookie;
            /** f0 - The DHCP options. */
            uint8_t     dhcp_opts[124];
        }           Dhcp;
    }               bp_vend;

} RTNETBOOTP;
#pragma pack()
AssertCompileSize(RTNETBOOTP, 0xec + 128);
/** Pointer to a BOOTP / DHCP packet. */
typedef RTNETBOOTP *PRTNETBOOTP;
/** Pointer to a const BOOTP / DHCP packet. */
typedef RTNETBOOTP const *PCRTNETBOOTP;

/** Minimum BOOTP packet length. For quick validation, no standard thing really. */
#define RTNETBOOTP_MIN_LEN          0xec
/** Minimum DHCP packet length. For quick validation, no standard thing really. */
#define RTNETBOOTP_DHCP_MIN_LEN     0xf1

/** The normal size of the a DHCP packet (i.e. a RTNETBOOTP).
 * Same as RTNET_DHCP_OPT_SIZE, just expressed differently.  */
#define RTNET_DHCP_NORMAL_SIZE      (0xec + 4 + RTNET_DHCP_OPT_SIZE)
/** The normal size of RTNETBOOTP::bp_vend::Dhcp::dhcp_opts.  */
#define RTNET_DHCP_OPT_SIZE         (312 - 4)

/** @name BOOTP packet opcode values
 * @{ */
#define RTNETBOOTP_OP_REQUEST       1
#define RTNETBOOTP_OP_REPLY         2
/** @} */

/** @name DHCP flags (RTNETBOOTP::bp_flags)
 * @{ */
#define RTNET_DHCP_FLAGS_NO_BROADCAST   UINT16_C(0x8000) /** @todo check test!!! */
/** @} */

/** The DHCP cookie (network endian). */
#define RTNET_DHCP_COOKIE           UINT32_C(0x63825363)

/**
 * An IPv4 DHCP option header.
 */
typedef struct RTNETDHCPOPT
{
    /** 00 - The DHCP option. */
    uint8_t     dhcp_opt;
    /** 01 - The data length (excluding this header). */
    uint8_t     dhcp_len;
    /*  02 - The option data follows here, optional and of variable length. */
} RTNETDHCPOPT;
AssertCompileSize(RTNETDHCPOPT, 2);
/** Pointer to a DHCP option header. */
typedef RTNETDHCPOPT *PRTNETDHCPOPT;
/** Pointer to a const DHCP option header. */
typedef RTNETDHCPOPT const *PCRTNETDHCPOPT;

/** @name DHCP options
 * @{ */
/** 1 byte padding, this has no dhcp_len field. */
#define RTNET_DHCP_OPT_PAD                  0

/** The subnet mask. */
#define RTNET_DHCP_OPT_SUBNET_MASK          1
/** The time offset. */
#define RTNET_DHCP_OPT_TIME_OFFSET          2
/** The routers for the subnet. */
#define RTNET_DHCP_OPT_ROUTERS              3
/** Domain Name Server. */
#define RTNET_DHCP_OPT_DNS                  6
/** Host name. */
#define RTNET_DHCP_OPT_HOST_NAME            12
/** Domain name. */
#define RTNET_DHCP_OPT_DOMAIN_NAME          15

/** The requested address. */
#define RTNET_DHCP_OPT_REQ_ADDR             50
/** The lease time in seconds. */
#define RTNET_DHCP_OPT_LEASE_TIME           51
/** Option overload.
 *  Indicates that the bp_file and/or bp_sname holds contains DHCP options. */
#define RTNET_DHCP_OPT_OPTION_OVERLOAD      52
/** Have a 8-bit message type value as data, see RTNET_DHCP_MT_*. */
#define RTNET_DHCP_OPT_MSG_TYPE             53
/** Server ID. */
#define RTNET_DHCP_OPT_SERVER_ID            54
/** Parameter request list. */
#define RTNET_DHCP_OPT_PARAM_REQ_LIST       55
/** The maximum DHCP message size a client is willing to accept. */
#define RTNET_DHCP_OPT_MAX_DHCP_MSG_SIZE    57
/** Client ID. */
#define RTNET_DHCP_OPT_CLIENT_ID            61
/** TFTP server name. */
#define RTNET_DHCP_OPT_TFTP_SERVER_NAME     66
/** Bootfile name. */
#define RTNET_DHCP_OPT_BOOTFILE_NAME        67

/** Marks the end of the DHCP options, this has no dhcp_len field. */
#define RTNET_DHCP_OPT_END                  255
/** @} */

/** @name DHCP Option overload flags (option 52)
 * @{ */
#define RTNET_DHCP_OPTION_OVERLOAD_FILE     1
#define RTNET_DHCP_OPTION_OVERLOAD_SNAME    2
#define RTNET_DHCP_OPTION_OVERLOAD_MASK     3
/** @} */

/** @name DHCP Message Types (option 53)
 * @{ */
#define RTNET_DHCP_MT_DISCOVER      1
#define RTNET_DHCP_MT_OFFER         2
#define RTNET_DHCP_MT_REQUEST       3
#define RTNET_DHCP_MT_DECLINE       4
#define RTNET_DHCP_MT_ACK           5
#define RTNET_DHCP_MT_NAC           6
#define RTNET_DHCP_MT_RELEASE       7
#define RTNET_DHCP_MT_INFORM        8
/** @} */

/** @name DHCP Flags
 * @{ */
#define RTNET_DHCP_FLAG_BROADCAST   0x8000
/** @} */

RTDECL(bool) RTNetIPv4IsDHCPValid(PCRTNETUDP pUdpHdr, PCRTNETBOOTP pDhcp, size_t cbDhcp, uint8_t *pMsgType);


/**
 * IPv4 DHCP packet.
 * @deprecated Use RTNETBOOTP.
 */
#pragma pack(1)
typedef struct RTNETDHCP
{
    /** 00 - The packet opcode. */
    uint8_t         Op;
    /** Hardware address type. */
    uint8_t         HType;
    /** Hardware address length. */
    uint8_t         HLen;
    uint8_t         Hops;
    uint32_t        XID;
    uint16_t        Secs;
    uint16_t        Flags;
    /** Client IPv4 address. */
    RTNETADDRIPV4   CIAddr;
    /** Your IPv4 address. */
    RTNETADDRIPV4   YIAddr;
    /** Server IPv4 address. */
    RTNETADDRIPV4   SIAddr;
    /** Gateway IPv4 address. */
    RTNETADDRIPV4   GIAddr;
    /** Client hardware address. */
    uint8_t         CHAddr[16];
    /** Server name. */
    uint8_t         SName[64];
    uint8_t         File[128];
    uint8_t         abMagic[4];
    uint8_t         DhcpOpt;
    uint8_t         DhcpLen; /* 1 */
    uint8_t         DhcpReq;
    uint8_t         abOptions[57];
} RTNETDHCP;
#pragma pack()
/** @todo AssertCompileSize(RTNETDHCP, ); */
/** Pointer to a DHCP packet. */
typedef RTNETDHCP *PRTNETDHCP;
/** Pointer to a const DHCP packet. */
typedef RTNETDHCP const *PCRTNETDHCP;


/**
 * TCP packet.
 */
#pragma pack(1)
typedef struct RTNETTCP
{
    /** 00 - The source port. */
    uint16_t        th_sport;
    /** 02 - The destination port. */
    uint16_t        th_dport;
    /** 04 - The sequence number. */
    uint32_t        th_seq;
    /** 08 - The acknowledgement number. */
    uint32_t        th_ack;
#ifdef RT_BIG_ENDIAN
    unsigned int    th_win : 16;
    unsigned int    th_flags : 8;
    unsigned int    th_off : 4;
    unsigned int    th_x2 : 4;
#else
    /** 0c:0 - Reserved. */
    unsigned int    th_x2 : 4;
    /** 0c:4 - The data offset given as a dword count from the start of this header. */
    unsigned int    th_off : 4;
    /** 0d - flags. */
    unsigned int    th_flags : 8;
    /** 0e - The window. */
    unsigned int    th_win : 16;
#endif
    /** 10 - The checksum of the pseudo header, the TCP header and the data. */
    uint16_t        th_sum;
    /** 12 - The urgent pointer. */
    uint16_t        th_urp;
    /* (options follows here and then the data (aka text).) */
} RTNETTCP;
#pragma pack()
AssertCompileSize(RTNETTCP, 20);
/** Pointer to a TCP packet. */
typedef RTNETTCP *PRTNETTCP;
/** Pointer to a const TCP packet. */
typedef RTNETTCP const *PCRTNETTCP;

/** The minimum TCP header length (in bytes). (RTNETTCP::th_off * 4) */
#define RTNETTCP_MIN_LEN    (20)

/** @name TCP flags (RTNETTCP::th_flags)
 * @{ */
#define RTNETTCP_F_FIN      0x01
#define RTNETTCP_F_SYN      0x02
#define RTNETTCP_F_RST      0x04
#define RTNETTCP_F_PSH      0x08
#define RTNETTCP_F_ACK      0x10
#define RTNETTCP_F_URG      0x20
#define RTNETTCP_F_ECE      0x40
#define RTNETTCP_F_CWR      0x80
/** @} */

RTDECL(uint16_t) RTNetTCPChecksum(uint32_t u32Sum, PCRTNETTCP pTcpHdr, void const *pvData, size_t cbData);
RTDECL(uint32_t) RTNetIPv4AddTCPChecksum(PCRTNETTCP pTcpHdr, uint32_t u32Sum);
RTDECL(uint16_t) RTNetIPv4TCPChecksum(PCRTNETIPV4 pIpHdr, PCRTNETTCP pTcpHdr, void const *pvData);
RTDECL(bool)     RTNetIPv4IsTCPSizeValid(PCRTNETIPV4 pIpHdr, PCRTNETTCP pTcpHdr, size_t cbHdrMax, size_t cbPktMax);
RTDECL(bool)     RTNetIPv4IsTCPValid(PCRTNETIPV4 pIpHdr, PCRTNETTCP pTcpHdr, size_t cbHdrMax, void const *pvData,
                                     size_t cbPktMax, bool fChecksum);


/**
 * IPv4 ICMP packet header.
 */
#pragma pack(1)
typedef struct RTNETICMPV4HDR
{
    /** 00 - The ICMP message type. */
    uint8_t         icmp_type;
    /** 01 - Type specific code that further qualifies the message. */
    uint8_t         icmp_code;
    /** 02 - Checksum of the ICMP message. */
    uint16_t        icmp_cksum;
} RTNETICMPV4HDR;
#pragma pack()
AssertCompileSize(RTNETICMPV4HDR, 4);
/** Pointer to an ICMP packet header. */
typedef RTNETICMPV4HDR *PRTNETICMPV4HDR;
/** Pointer to a const ICMP packet header. */
typedef RTNETICMPV4HDR const *PCRTNETICMPV4HDR;

/** @name ICMP (v4) message types.
 * @{  */
#define RTNETICMPV4_TYPE_ECHO_REPLY     0
#define RTNETICMPV4_TYPE_ECHO_REQUEST   8
#define RTNETICMPV4_TYPE_TRACEROUTE     30
/** @} */

/**
 * IPv4 ICMP ECHO Reply & Request packet.
 */
#pragma pack(1)
typedef struct RTNETICMPV4ECHO
{
    /** 00 - The ICMP header. */
    RTNETICMPV4HDR  Hdr;
    /** 04 - The identifier to help the requestor match up the reply.
     *       Can be 0. Typically fixed value. */
    uint16_t        icmp_id;
    /** 06 - The sequence number to help the requestor match up the reply.
     *       Can be 0. Typically incrementing between requests. */
    uint16_t        icmp_seq;
    /** 08 - Variable length data that is to be returned unmodified in the reply. */
    uint8_t         icmp_data[1];
} RTNETICMPV4ECHO;
#pragma pack()
AssertCompileSize(RTNETICMPV4ECHO, 9);
/** Pointer to an ICMP ECHO packet. */
typedef RTNETICMPV4ECHO *PRTNETICMPV4ECHO;
/** Pointer to a const ICMP ECHO packet. */
typedef RTNETICMPV4ECHO const *PCRTNETICMPV4ECHO;

/**
 * IPv4 ICMP TRACEROUTE packet.
 * This is an reply to an IP packet with the traceroute option set.
 */
#pragma pack(1)
typedef struct RTNETICMPV4TRACEROUTE
{
    /** 00 - The ICMP header. */
    RTNETICMPV4HDR  Hdr;
    /** 04 - Identifier copied from the traceroute option's ID number. */
    uint16_t        icmp_id;
    /** 06 - Unused. (Possibly an icmp_seq?) */
    uint16_t        icmp_void;
    /** 08 - Outbound hop count. From the IP packet causing this message. */
    uint16_t        icmp_ohc;
    /** 0a - Return hop count. From the IP packet causing this message. */
    uint16_t        icmp_rhc;
    /** 0c - Output link speed, 0 if not known. */
    uint32_t        icmp_speed;
    /** 10 - Output link MTU, 0 if not known. */
    uint32_t        icmp_mtu;
} RTNETICMPV4TRACEROUTE;
#pragma pack()
AssertCompileSize(RTNETICMPV4TRACEROUTE, 20);
/** Pointer to an ICMP TRACEROUTE packet. */
typedef RTNETICMPV4TRACEROUTE *PRTNETICMPV4TRACEROUTE;
/** Pointer to a const ICMP TRACEROUTE packet. */
typedef RTNETICMPV4TRACEROUTE const *PCRTNETICMPV4TRACEROUTE;

/** @todo add more ICMPv4 as needed. */

/**
 * IPv4 ICMP union packet.
 */
typedef union RTNETICMPV4
{
    RTNETICMPV4HDR Hdr;
    RTNETICMPV4ECHO Echo;
    RTNETICMPV4TRACEROUTE Traceroute;
} RTNETICMPV4;
/** Pointer to an ICMP union packet. */
typedef RTNETICMPV4 *PRTNETICMPV4;
/** Pointer to a const ICMP union packet. */
typedef RTNETICMPV4 const *PCRTNETICMPV4;


/**
 * IPv6 ICMP packet header.
 */
#pragma pack(1)
typedef struct RTNETICMPV6HDR
{
    /** 00 - The ICMPv6 message type. */
    uint8_t         icmp6_type;
    /** 01 - Type specific code that further qualifies the message. */
    uint8_t         icmp6_code;
    /** 02 - Checksum of the ICMPv6 message. */
    uint16_t        icmp6_cksum;
} RTNETICMPV6HDR;
#pragma pack()
AssertCompileSize(RTNETICMPV6HDR, 4);
/** Pointer to an ICMPv6 packet header. */
typedef RTNETICMPV6HDR *PRTNETICMPV6HDR;
/** Pointer to a const ICMP packet header. */
typedef RTNETICMPV6HDR const *PCRTNETICMPV6HDR;

#define RTNETIPV6_PROT_ICMPV6       (58)

/** @name Internet Control Message Protocol version 6 (ICMPv6) message types.
 * @{ */
#define RTNETIPV6_ICMP_TYPE_RS      133
#define RTNETIPV6_ICMP_TYPE_RA      134
#define RTNETIPV6_ICMP_TYPE_NS      135
#define RTNETIPV6_ICMP_TYPE_NA      136
#define RTNETIPV6_ICMP_TYPE_RDR     137
/** @} */

/** @name Neighbor Discovery option types
 * @{ */
#define RTNETIPV6_ICMP_ND_SLLA_OPT  (1)
#define RTNETIPV6_ICMP_ND_TLLA_OPT  (2)
/** @} */

/** ICMPv6 ND Source/Target Link Layer Address option */
#pragma pack(1)
typedef struct RTNETNDP_LLA_OPT
{
    uint8_t type;
    uint8_t len;
    RTMAC lla;
} RTNETNDP_LLA_OPT;
#pragma pack()

AssertCompileSize(RTNETNDP_LLA_OPT, 1+1+6);

typedef RTNETNDP_LLA_OPT *PRTNETNDP_LLA_OPT;
typedef RTNETNDP_LLA_OPT const *PCRTNETNDP_LLA_OPT;

/** ICMPv6 ND Neighbor Sollicitation */
#pragma pack(1)
typedef struct RTNETNDP
{
    /** 00 - The ICMPv6 header. */
    RTNETICMPV6HDR Hdr;
    /** 04 - reserved */
    uint32_t reserved;
    /** 08 - target address */
    RTNETADDRIPV6 target_address;
} RTNETNDP;
#pragma pack()
AssertCompileSize(RTNETNDP, 4+4+16);
/** Pointer to a NDP ND packet. */
typedef RTNETNDP *PRTNETNDP;
/** Pointer to a const NDP NS packet. */
typedef RTNETNDP const *PCRTNETNDP;


/**
 * Ethernet ARP header.
 */
#pragma pack(1)
typedef struct RTNETARPHDR
{
    /** The hardware type. */
    uint16_t    ar_htype;
    /** The protocol type (ethertype). */
    uint16_t    ar_ptype;
    /** The hardware address length. */
    uint8_t     ar_hlen;
    /** The protocol address length. */
    uint8_t     ar_plen;
    /** The operation. */
    uint16_t    ar_oper;
} RTNETARPHDR;
#pragma pack()
AssertCompileSize(RTNETARPHDR, 8);
/** Pointer to an ethernet ARP header. */
typedef RTNETARPHDR *PRTNETARPHDR;
/** Pointer to a const ethernet ARP header. */
typedef RTNETARPHDR const *PCRTNETARPHDR;

/** ARP hardware type - ethernet. */
#define RTNET_ARP_ETHER            UINT16_C(1)

/** @name ARP operations
 * @{ */
#define RTNET_ARPOP_REQUEST        UINT16_C(1) /**< Request hardware address given a protocol address (ARP). */
#define RTNET_ARPOP_REPLY          UINT16_C(2)
#define RTNET_ARPOP_REVREQUEST     UINT16_C(3) /**< Request protocol address given a hardware address (RARP). */
#define RTNET_ARPOP_REVREPLY       UINT16_C(4)
#define RTNET_ARPOP_INVREQUEST     UINT16_C(8) /**< Inverse ARP.  */
#define RTNET_ARPOP_INVREPLY       UINT16_C(9)
/** Check if an ARP operation is a request or not. */
#define RTNET_ARPOP_IS_REQUEST(Op) ((Op) & 1)
/** Check if an ARP operation is a reply or not. */
#define RTNET_ARPOP_IS_REPLY(Op)   (!RTNET_ARPOP_IS_REQUEST(Op))
/** @} */


/**
 * Ethernet IPv4 + 6-byte MAC ARP request packet.
 */
#pragma pack(1)
typedef struct RTNETARPIPV4
{
    /** ARP header. */
    RTNETARPHDR     Hdr;
    /** The sender hardware address. */
    RTMAC           ar_sha;
    /** The sender protocol address. */
    RTNETADDRIPV4   ar_spa;
    /** The target hardware address. */
    RTMAC           ar_tha;
    /** The target protocol address. */
    RTNETADDRIPV4   ar_tpa;
} RTNETARPIPV4;
#pragma pack()
AssertCompileSize(RTNETARPIPV4, 8+6+4+6+4);
/** Pointer to an ethernet IPv4+MAC ARP request packet. */
typedef RTNETARPIPV4 *PRTNETARPIPV4;
/** Pointer to a const ethernet IPv4+MAC ARP request packet. */
typedef RTNETARPIPV4 const *PCRTNETARPIPV4;


/** @} */

RT_C_DECLS_END

#endif /* !IPRT_INCLUDED_net_h */

