/*
 * Copyright (C) 2017-20 Freie Universität Berlin
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @defgroup    net_gnrc_netif Network interface API
 * @ingroup     net_gnrc
 * @brief       Abstraction layer for GNRC's network interfaces
 *
 * Network interfaces in the context of GNRC are threads for protocols that are
 * below the network layer.
 *
 * ## Single interface optimizations
 *
 * If you only have one network interface on the board, you can select the
 * `gnrc_netif_single` pseudo-module to enable further optimisations.
 *
 * @{
 *
 * @file
 * @brief   Definition for GNRC's network interfaces
 *
 * @author  Martine Lenders <mlenders@inf.fu-berlin.de>
 */
#ifndef NET_GNRC_NETIF_H
#define NET_GNRC_NETIF_H

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

#include "sched.h"
#include "msg.h"
#ifdef MODULE_GNRC_NETIF_BUS
#include "msg_bus.h"
#endif
#include "event.h"
#include "net/ipv6/addr.h"
#include "net/gnrc/netapi.h"
#include "net/gnrc/pkt.h"
#include "net/gnrc/netif/conf.h"
#if IS_USED(MODULE_GNRC_NETIF_LORAWAN)
#include "net/gnrc/netif/lorawan.h"
#endif
#if IS_USED(MODULE_GNRC_NETIF_6LO)
#include "net/gnrc/netif/6lo.h"
#endif
#if defined(MODULE_GNRC_NETIF_DEDUP) && (GNRC_NETIF_L2ADDR_MAXLEN > 0)
#include "net/gnrc/netif/dedup.h"
#endif
#include "net/gnrc/netif/flags.h"
#if IS_USED(MODULE_GNRC_NETIF_IPV6)
#include "net/gnrc/netif/ipv6.h"
#endif
#if IS_USED(MODULE_GNRC_NETIF_MAC)
#include "net/gnrc/netif/mac.h"
#endif
#if IS_USED(MODULE_GNRC_NETIF_PKTQ)
#include "net/gnrc/netif/pktq/type.h"
#endif
#include "net/l2util.h"
#include "net/ndp.h"
#include "net/netdev.h"
#include "net/netopt.h"
#ifdef MODULE_NETSTATS_L2
#include "net/netstats.h"
#endif
#include "rmutex.h"
#include "net/netif.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief   Per-Interface Event Message Buses
 */
typedef enum {
#ifdef MODULE_GNRC_IPV6
    GNRC_NETIF_BUS_IPV6,                    /**< provides @ref gnrc_ipv6_event_t
                                                 messages to subscribers */
#endif
    GNRC_NETIF_BUS_NUMOF
} gnrc_netif_bus_t;

/**
 * @brief   Event types for GNRC_NETIF_BUS_IPV6 per-interface message bus
 */
typedef enum {
    /**
     * @brief   Address becomes valid
     *
     * The event is generated when an address on the interface becomes valid.
     * The message payload contains a pointer to the respective
     * @ref ipv6_addr_t struct.
     *
     * @note If the address on the interface changed between sending
     * the event and processing it, the pointer will point to the new address
     * which might *not* be valid.
     */
    GNRC_IPV6_EVENT_ADDR_VALID,
} gnrc_ipv6_event_t;

/**
 * @brief   Operations to an interface
 */
typedef struct gnrc_netif_ops gnrc_netif_ops_t;

/**
 * @brief   Representation of a network interface
 */
typedef struct {
    netif_t netif;                          /**< network interface descriptor */
    const gnrc_netif_ops_t *ops;            /**< Operations of the network interface */
    netdev_t *dev;                          /**< Network device of the network interface */
    rmutex_t mutex;                         /**< Mutex of the interface */
#ifdef MODULE_NETSTATS_L2
    netstats_t stats;                       /**< transceiver's statistics */
#endif
#if IS_USED(MODULE_GNRC_NETIF_LORAWAN) || defined(DOXYGEN)
    gnrc_netif_lorawan_t lorawan;           /**< LoRaWAN component */
#endif
#if IS_USED(MODULE_GNRC_NETIF_IPV6) || defined(DOXYGEN)
    gnrc_netif_ipv6_t ipv6;                 /**< IPv6 component */
#endif
#if IS_USED(MODULE_GNRC_NETIF_MAC) || defined(DOXYGEN)
    gnrc_netif_mac_t mac;                   /**< @ref net_gnrc_mac component */
#endif  /* IS_USED(MODULE_GNRC_NETIF_MAC) || defined(DOXYGEN) */
#if IS_USED(MODULE_GNRC_NETIF_BUS) || DOXYGEN
    msg_bus_t bus[GNRC_NETIF_BUS_NUMOF];    /**< Event Message Bus */
#endif
    /**
     * @brief   Flags for the interface
     *
     * @see net_gnrc_netif_flags
     */
    uint32_t flags;
#if IS_USED(MODULE_GNRC_NETIF_EVENTS) || defined(DOXYGEN)
    /**
     * @brief   Event queue for asynchronous events
     */
    event_queue_t evq;
    /**
     * @brief   ISR event for the network device
     */
    event_t event_isr;
#endif /* MODULE_GNRC_NETIF_EVENTS */
#if (GNRC_NETIF_L2ADDR_MAXLEN > 0) || DOXYGEN
    /**
     * @brief   The link-layer address currently used as the source address
     *          on this interface.
     *
     * @note    Only available if @ref GNRC_NETIF_L2ADDR_MAXLEN > 0
     */
    uint8_t l2addr[GNRC_NETIF_L2ADDR_MAXLEN];

    /**
     * @brief   Length in bytes of gnrc_netif_t::l2addr
     *
     * @note    Only available if @ref GNRC_NETIF_L2ADDR_MAXLEN > 0
     */
    uint8_t l2addr_len;
#if defined(MODULE_GNRC_NETIF_DEDUP) || DOXYGEN
    /**
     * @brief   Last received packet information
     *
     * @note    Only available with @ref net_gnrc_netif_dedup.
     */
    gnrc_netif_dedup_t last_pkt;
#endif
#endif
#if IS_USED(MODULE_GNRC_NETIF_6LO) || defined(DOXYGEN)
    gnrc_netif_6lo_t sixlo;                 /**< 6Lo component */
#endif
#if IS_USED(MODULE_GNRC_NETIF_PKTQ) || defined(DOXYGEN)
    /**
     * @brief   Packet queue for sending
     *
     * @note    Only available with @ref net_gnrc_netif_pktq.
     */
    gnrc_netif_pktq_t send_queue;
#endif
    uint8_t cur_hl;                         /**< Current hop-limit for out-going packets */
    uint8_t device_type;                    /**< Device type */
    kernel_pid_t pid;                       /**< PID of the network interface's thread */
} gnrc_netif_t;

/**
 * @see gnrc_netif_ops_t
 */
struct gnrc_netif_ops {
    /**
     * @brief   Initializes network interface beyond the default settings
     *
     * @pre `netif != NULL`
     *
     * @param[in] netif The network interface.
     *
     * This is called after the network device's initial configuration, right
     * before the interface's thread starts receiving messages. It is not
     * necessary to lock the interface's mutex gnrc_netif_t::mutex, since it is
     * already locked. Set to @ref gnrc_netif_default_init() if you do not need
     * any special initialization. If you do need special initialization, it is
     * recommended to call @ref gnrc_netif_default_init() at the start of the
     * custom initialization function set here.
     */
    void (*init)(gnrc_netif_t *netif);

    /**
     * @brief   Send a @ref net_gnrc_pkt "packet" over the network interface
     *
     * @pre `netif != NULL && pkt != NULL`
     *
     * @note The function re-formats the content of @p pkt to a format expected
     *       by the netdev_driver_t::send() method of gnrc_netif_t::dev and
     *       releases the packet before returning (so no additional release
     *       should be required after calling this method).
     *
     * @param[in] netif The network interface.
     * @param[in] pkt   A packet to send.
     *
     * @return  The number of bytes actually sent on success
     * @return  -EBADMSG, if the @ref net_gnrc_netif_hdr in @p pkt is missing
     *          or is in an unexpected format.
     * @return  -ENOTSUP, if sending @p pkt in the given format isn't supported
     *          (e.g. empty payload with Ethernet).
     * @return  Any negative error code reported by gnrc_netif_t::dev.
     */
    int (*send)(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt);

    /**
     * @brief   Receives a @ref net_gnrc_pkt "packet" from the network interface
     *
     * @pre `netif != NULL`
     *
     * @note The function takes the bytes received via netdev_driver_t::recv()
     *       from gnrc_netif_t::dev and re-formats it to a
     *       @ref net_gnrc_pkt "packet" containing a @ref net_gnrc_netif_hdr
     *       and a payload header in receive order.
     *
     * @param[in] netif The network interface.
     *
     * @return  The packet received. Contains the payload (with the type marked
     *          accordingly) and a @ref net_gnrc_netif_hdr in receive order.
     * @return  NULL, if @ref net_gnrc_pktbuf was full.
     */
    gnrc_pktsnip_t *(*recv)(gnrc_netif_t *netif);

    /**
     * @brief   Gets an option from the network interface
     *
     * Use gnrc_netif_get_from_netdev() to just get options from
     * gnrc_netif_t::dev.
     *
     * @param[in] netif     The network interface.
     * @param[in] opt       The option parameters.
     *
     * @return  Number of bytes in @p data.
     * @return  -EOVERFLOW, if @p max_len is lesser than the required space.
     * @return  -ENOTSUP, if @p opt is not supported to be set.
     * @return  Any negative error code reported by gnrc_netif_t::dev.
     */
    int (*get)(gnrc_netif_t *netif, gnrc_netapi_opt_t *opt);

    /**
     * @brief  Sets an option from the network interface
     *
     * Use gnrc_netif_set_from_netdev() to just set options from
     * gnrc_netif_t::dev.
     *
     * @param[in] netif     The network interface.
     * @param[in] opt       The option parameters.
     *
     * @return  Number of bytes written to gnrc_netif_t::dev.
     * @return  -EOVERFLOW, if @p data_len is greater than the allotted space in
     *          gnrc_netif_t::dev or gnrc_netif_t.
     * @return  -ENOTSUP, if @p opt is not supported to be set.
     * @return  Any negative error code reported by gnrc_netif_t::dev.
     */
    int (*set)(gnrc_netif_t *netif, const gnrc_netapi_opt_t *opt);

    /**
     * @brief   Message handler for network interface
     *
     * This message handler is used, when the network interface needs to handle
     * message types beyond the ones defined in @ref net_gnrc_netapi "netapi".
     * Leave NULL if this is not the case.
     *
     * @param[in] netif The network interface.
     * @param[in] msg   Message to be handled.
     */
    void (*msg_handler)(gnrc_netif_t *netif, msg_t *msg);
};

/**
 * @brief   Initialize all available network interfaces.
 *          This function is called automatically if the auto_init_gnrc_netif
 *          module is used.
 *          If only the gnrc_netif_init module is used instead, you can call
 *          this function to manually set up the network interfaces at a later
 *          time.
 */
void gnrc_netif_init_devs(void);

/**
 * @brief   Creates a network interface
 *
 * @param[out] netif    The interface. May not be `NULL`.
 * @param[in] stack     The stack for the network interface's thread.
 * @param[in] stacksize Size of @p stack.
 * @param[in] priority  Priority for the network interface's thread.
 * @param[in] name      Name for the network interface. May be NULL.
 * @param[in] dev       Device for the interface.
 * @param[in] ops       Operations for the network interface.
 *
 * @note If @ref DEVELHELP is defined netif_params_t::name is used as the
 *       name of the network interface's thread.
 *
 * @return  0 on success
 * @return  negative number on error
 */
int gnrc_netif_create(gnrc_netif_t *netif, char *stack, int stacksize,
                      char priority, const char *name, netdev_t *dev,
                      const gnrc_netif_ops_t *ops);

/**
 * @brief   Get number of network interfaces actually allocated
 *
 * @return  Number of network interfaces actually allocated
 */
unsigned gnrc_netif_numof(void);

/**
 * @brief Check if there can only be one @ref gnrc_netif_t interface.
 *
 * > There can only be one!
 *
 * This function is used to allow compile time optimizations for
 * single interface applications
 *
 * @return true, if there can only only one interface
 * @return false, if there can be more than one interface
 */
static inline bool gnrc_netif_highlander(void)
{
    return IS_USED(MODULE_GNRC_NETIF_SINGLE);
}

/**
 * @brief   Iterate over all network interfaces.
 *
 * @param[in] prev  previous interface in iteration. NULL to start iteration.
 *
 * @return  The next network interface after @p prev.
 * @return  NULL, if @p prev was the last network interface.
 */
gnrc_netif_t *gnrc_netif_iter(const gnrc_netif_t *prev);

/**
 * @brief   Get network interface by PID
 *
 * @param[in] pid   A PID of a network interface.
 *
 * @return  The network interface on success.
 * @return  NULL, if no network interface with PID exists.
 */
gnrc_netif_t *gnrc_netif_get_by_pid(kernel_pid_t pid);

/**
 * @brief   Gets the (unicast on anycast) IPv6 address of an interface (if IPv6
 *          is supported)
 *
 * @pre `netif != NULL`
 * @pre `addrs != NULL`
 * @pre `max_len >= sizeof(ipv6_addr_t)`
 *
 * @param[in] netif     The interface. May not be `NULL`.
 * @param[out] addrs    Up to the first `max_len / sizeof(ipv6_addr_t)`
 *                      addresses assigned to @p netif. May not be `NULL`
 * @param[in] max_len   Number of *bytes* available in @p addrs. Must be at
 *                      least `sizeof(ipv6_addr_t)`. It is recommended to use
 *                      @p CONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF `* sizeof(ipv6_addr_t)
 *                      here (and have @p addrs of the according length).
 *
 * @return  Number of addresses in @p addrs times `sizeof(ipv6_addr_t)` on
 *          success (including 0).
 * @return  -ENOTSUP, if @p netif doesn't support IPv6.
 */
static inline int gnrc_netif_ipv6_addrs_get(const gnrc_netif_t *netif,
                                            ipv6_addr_t *addrs,
                                            size_t max_len)
{
    assert(netif != NULL);
    assert(addrs != NULL);
    assert(max_len >= sizeof(ipv6_addr_t));
    return gnrc_netapi_get(netif->pid, NETOPT_IPV6_ADDR, 0, addrs, max_len);
}

/**
 * @brief   Adds an (unicast or anycast) IPv6 address to an interface (if IPv6
 *          is supported)
 *
 * @pre `netif != NULL`
 * @pre `addr != NULL`
 * @pre `(pfx_len > 0) && (pfx_len <= 128)`
 *
 * @param[in] netif     The interface. May not be `NULL`.
 * @param[in] addr      The address to add to @p netif. May not be `NULL`.
 * @param[in] pfx_len   The prefix length of @p addr. Must be greater than 0 and
 *                      lesser than or equal to 128.
 * @param[in] flags     [Flags](@ref net_gnrc_netif_ipv6_addrs_flags) for
 *                      @p addr. Set @ref GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID
 *                      to skip duplicate address detection (when activated).
 *
 * @return  sizeof(ipv6_addr_t) on success.
 * @return  -ENOMEM, if no space is left on @p netif to add @p addr or its
 *          corresponding solicited-nodes multicast address.
 * @return  -ENOTSUP, if @p netif doesn't support IPv6.
 */
static inline int gnrc_netif_ipv6_addr_add(const gnrc_netif_t *netif,
                                           ipv6_addr_t *addr, unsigned pfx_len,
                                           uint8_t flags)
{
    assert(netif != NULL);
    assert(addr != NULL);
    assert((pfx_len > 0) && (pfx_len <= 128));
    return gnrc_netapi_set(netif->pid, NETOPT_IPV6_ADDR,
                           ((pfx_len << 8U) | flags), addr,
                           sizeof(ipv6_addr_t));
}

/**
 * @brief   Removes a (unicast or anycast) IPv6 address from an interface (if
 *          IPv6 is supported)
 *
 * @pre `netif != NULL`
 * @pre `addr != NULL`
 *
 * @param[in] netif     The interface. May not be `NULL`.
 * @param[in] addr      The address to remove from @p netif. May not be `NULL`.
 *
 * @return  sizeof(ipv6_addr_t) on success.
 * @return  -ENOTSUP, if @p netif doesn't support IPv6.
 */
static inline int gnrc_netif_ipv6_addr_remove(const gnrc_netif_t *netif,
                                              ipv6_addr_t *addr)
{
    assert(netif != NULL);
    assert(addr != NULL);
    return gnrc_netapi_set(netif->pid, NETOPT_IPV6_ADDR_REMOVE,
                           0, addr, sizeof(ipv6_addr_t));
}

/**
 * @brief   Gets the IPv6 multicast groups an interface is joined to (if IPv6 is
 *          supported)
 *
 * @pre `netif != NULL`
 * @pre `groups != NULL`
 * @pre `max_len >= sizeof(ipv6_addr_t)`
 *
 * @param[in] netif     The interface. May not be `NULL`.
 * @param[out] groups   Up to the first `max_len / 8` multicast groups @p netif
 *                      is joined to. May not be `NULL`
 * @param[in] max_len   Number of *bytes* available in @p groups. Must be at
 *                      least `sizeof(ipv6_addr_t)`. It is recommended to use
 *                      @p GNRC_NETIF_IPV6_GROUPS_NUMOF `* sizeof(ipv6_addr_t)
 *                      here (and have @p groups of the according length).
 *
 * @return  Number of addresses in @p groups times `sizeof(ipv6_addr_t)` on
 *          success (including 0).
 * @return  -ENOTSUP, if @p netif doesn't support IPv6.
 */
static inline int gnrc_netif_ipv6_groups_get(const gnrc_netif_t *netif,
                                             ipv6_addr_t *groups,
                                             size_t max_len)
{
    assert(netif != NULL);
    assert(groups != NULL);
    assert(max_len >= sizeof(ipv6_addr_t));
    return gnrc_netapi_get(netif->pid, NETOPT_IPV6_GROUP, 0, groups, max_len);
}

/**
 * @brief   Joins an IPv6 multicast group on an interface (if IPv6 is supported)
 *
 * @pre `netif != NULL`
 * @pre `group != NULL`
 *
 * @param[in] netif     The interface.
 * @param[in] group     The address of the multicast group to join on @p netif.
 *                      May not be `NULL`.
 *
 * @return  sizeof(ipv6_addr_t) on success.
 * @return  -ENOMEM, if no space is left on @p netif to add @p group.
 * @return  -ENOTSUP, if @p netif doesn't support IPv6.
 */
static inline int gnrc_netif_ipv6_group_join(const gnrc_netif_t *netif,
                                             ipv6_addr_t *group)
{
    assert(netif != NULL);
    assert(group != NULL);
    return gnrc_netapi_set(netif->pid, NETOPT_IPV6_GROUP, 0, group,
                           sizeof(ipv6_addr_t));
}

/**
 * @brief   Leaves an IPv6 multicast group on an interface (if IPv6 is supported)
 *
 * @pre `netif != NULL`
 * @pre `group != NULL`
 *
 * @param[in] netif     The interface.
 * @param[in] group     The address of the multicast group to leave on @p netif.
 *                      May not be `NULL`.
 *
 * @return  sizeof(ipv6_addr_t) on success.
 * @return  -ENOTSUP, if @p netif doesn't support IPv6.
 */
static inline int gnrc_netif_ipv6_group_leave(const gnrc_netif_t *netif,
                                              ipv6_addr_t *group)
{
    assert(netif != NULL);
    assert(group != NULL);
    return gnrc_netapi_set(netif->pid, NETOPT_IPV6_GROUP_LEAVE, 0, group,
                           sizeof(ipv6_addr_t));
}

/**
 * @brief   Default operation for gnrc_netif_ops_t::init()
 *
 * @note    Can also be used to be called *before* a custom operation.
 *
 * @param[in] netif     The network interface.
 */
void gnrc_netif_default_init(gnrc_netif_t *netif);

/**
 * @brief   Default operation for gnrc_netif_ops_t::get()
 *
 * @note    Can also be used to be called *after* a custom operation.
 *
 * @param[in] netif     The network interface.
 * @param[out] opt      The option parameters.
 *
 * @return  Return value of netdev_driver_t::get() of gnrc_netif_t::dev of
 *          @p netif.
 */
int gnrc_netif_get_from_netdev(gnrc_netif_t *netif, gnrc_netapi_opt_t *opt);

/**
 * @brief   Default operation for gnrc_netif_ops_t::set()
 *
 * @note    Can also be used to be called *after* a custom operation.
 *
 * @param[in] netif     The network interface.
 * @param[in] opt       The option parameters.
 *
 * @return  Return value of netdev_driver_t::set() of gnrc_netif_t::dev of
 *          @p netif.
 */
int gnrc_netif_set_from_netdev(gnrc_netif_t *netif,
                               const gnrc_netapi_opt_t *opt);

/**
 * @brief Gets an interface by the netdev type (and index)
 *
 * @pre The netdev has been registered with @ref netdev_register
 *
 * @param[in]  type         driver type of the netdev, can be @ref NETDEV_ANY
 * @param[in]  index        index of the netdev, can be @ref NETDEV_INDEX_ANY
 *
 * @return  The network interface that has a netdev of matching type and index
 *          NULL if no matching interface could be found
 */
gnrc_netif_t *gnrc_netif_get_by_type(netdev_type_t type, uint8_t index);

/**
 * @brief   Converts a hardware address to a human readable string.
 *
 * @note    Compatibility wrapper for @see l2util_addr_to_str
 *
 * @details The format will be like `xx:xx:xx:xx` where `xx` are the bytes
 *          of @p addr in hexadecimal representation.
 *
 * @pre `(out != NULL) && ((addr != NULL) || (addr_len == 0))`
 * @pre @p out **MUST** have allocated at least 3 * @p addr_len bytes.
 *
 * @param[in] addr      A hardware address.
 * @param[in] addr_len  Length of @p addr.
 * @param[out] out      A string to store the output in. Must at least have
 *                      3 * @p addr_len bytes allocated.
 *
 * @return  @p out.
 */
static inline char *gnrc_netif_addr_to_str(const uint8_t *addr, size_t addr_len, char *out)
{
    return l2util_addr_to_str(addr, addr_len, out);
}

/**
 * @brief   Parses a string of colon-separated hexadecimals to a hardware
 *          address.
 *
 * @note    Compatibility wrapper for @see l2util_addr_from_str
 *
 * @details The input format must be like `xx:xx:xx:xx` where `xx` will be the
 *          bytes of @p addr in hexadecimal representation.
 *
 * @pre `(out != NULL)`
 * @pre @p out **MUST** have allocated at least
 *      @ref GNRC_NETIF_L2ADDR_MAXLEN bytes.
 *
 * @param[in] str       A string of colon-separated hexadecimals.
 * @param[out] out      The resulting hardware address. Must at least have
 *                      @ref GNRC_NETIF_L2ADDR_MAXLEN bytes allocated.
 *
 * @return  Actual length of @p out on success.
 * @return  0, on failure.
 */
static inline size_t gnrc_netif_addr_from_str(const char *str, uint8_t *out)
{
    return l2util_addr_from_str(str, out);
}

/**
 * @brief   Send a GNRC packet via a given @ref gnrc_netif_t interface.
 *
 * @param netif         pointer to the interface
 * @param pkt           packet to be sent.
 *
 * @return              1 if packet was successfully delivered
 * @return              -1 on error
 */
static inline int gnrc_netif_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
{
    return gnrc_netapi_send(netif->pid, pkt);
}

#if defined(MODULE_GNRC_NETIF_BUS) || DOXYGEN
/**
 * @brief   Get a message bus of a given @ref gnrc_netif_t interface.
 *
 * @param netif         pointer to the interface
 * @param type          GNRC message bus [type](@ref gnrc_netif_bus_t)
 *
 * @return              the message bus for the interface
 */
static inline msg_bus_t* gnrc_netif_get_bus(gnrc_netif_t *netif,
                                            gnrc_netif_bus_t type)
{
    assert(type < GNRC_NETIF_BUS_NUMOF);
    return &netif->bus[type];
}
#endif /* MODULE_GNRC_NETIF_BUS */

#ifdef __cplusplus
}
#endif

#endif /* NET_GNRC_NETIF_H */
/** @} */
