root/sys/net/lagg/if_laggproto.h
/*      $NetBSD: if_laggproto.h,v 1.20 2023/11/28 05:28:37 yamaguchi Exp $      */

/*
 * Copyright (c) 2021 Internet Initiative Japan Inc.
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */

#ifndef _NET_LAGG_IF_LAGGPROTO_H_
#define _NET_LAGG_IF_LAGGPROTO_H_

struct lagg_softc;
struct lagg_proto_softc;

#define LAGG_MAX_PORTS  32
#define LAGG_PORT_PRIO  0x8000U

enum lagg_work_state {
        LAGG_WORK_IDLE,
        LAGG_WORK_ENQUEUED,
        LAGG_WORK_STOPPING
};
struct lagg_work {
        struct work      lw_cookie;
        void            (*lw_func)(struct lagg_work *, void *);
        void            *lw_arg;
        int              lw_state;
};

static inline void
lagg_work_set(struct lagg_work *w,
    void (*func)(struct lagg_work *, void *), void *arg)
{

        w->lw_func = func;
        w->lw_arg = arg;
}

struct workqueue *
                lagg_workq_create(const char *, pri_t, int, int);
void            lagg_workq_destroy(struct workqueue *);
void            lagg_workq_add(struct workqueue *, struct lagg_work *);
void            lagg_workq_wait(struct workqueue *, struct lagg_work *);

struct lagg_port {
        struct psref_target      lp_psref;
        struct ifnet            *lp_ifp;        /* physical interface */
        struct lagg_softc       *lp_softc;      /* parent lagg */
        void                    *lp_proto_ctx;
        bool                     lp_ifdetaching;
        bool                     lp_promisc;
        void                    *lp_linkstate_hook;
        void                    *lp_ifdetach_hook;

        uint32_t                 lp_prio;       /* port priority */
        uint32_t                 lp_flags;      /* port flags */

        u_char                   lp_iftype;
        uint8_t                  lp_lladdr[ETHER_ADDR_LEN];
        int                      lp_eccapenable;
        uint64_t                 lp_ifcapenable;
        uint64_t                 lp_mtu;

        int                     (*lp_ioctl)(struct ifnet *, u_long, void *);
        void                    (*lp_input)(struct ifnet *, struct mbuf *);
        int                     (*lp_output)(struct ifnet *, struct mbuf *,
                                    const struct sockaddr *,
                                    const struct rtentry *);

        SIMPLEQ_ENTRY(lagg_port)
                                 lp_entry;
};

struct lagg_proto {
        lagg_proto       pr_num;
        void            (*pr_init)(void);
        void            (*pr_fini)(void);
        int             (*pr_attach)(struct lagg_softc *,
                            struct lagg_proto_softc **);
        void            (*pr_detach)(struct lagg_proto_softc *);
        int             (*pr_up)(struct lagg_proto_softc *);
        void            (*pr_down)(struct lagg_proto_softc *);
        int             (*pr_transmit)(struct lagg_proto_softc *,
                            struct mbuf *);
        struct mbuf *   (*pr_input)(struct lagg_proto_softc *,
                            struct lagg_port *, struct mbuf *);
        int             (*pr_allocport)(struct lagg_proto_softc *,
                            struct lagg_port *);
        void            (*pr_freeport)(struct lagg_proto_softc *,
                            struct lagg_port *);
        void            (*pr_startport)(struct lagg_proto_softc *,
                            struct lagg_port *);
        void            (*pr_stopport)(struct lagg_proto_softc *,
                            struct lagg_port *);
        void            (*pr_protostat)(struct lagg_proto_softc *,
                            struct laggreqproto *);
        void            (*pr_portstat)(struct lagg_proto_softc *,
                            struct lagg_port *, struct laggreqport *);
        void            (*pr_linkstate)(struct lagg_proto_softc *,
                            struct lagg_port *);
        int             (*pr_ioctl)(struct lagg_proto_softc *,
                            struct laggreqproto *);
};

struct lagg_variant {
        lagg_proto       lv_proto;
        struct lagg_proto_softc
                        *lv_psc;

        struct psref_target     lv_psref;
};

struct lagg_mc_entry {
        LIST_ENTRY(lagg_mc_entry)
                                 mc_entry;
        struct ether_multi      *mc_enm;
        struct sockaddr_storage  mc_addr;
};

struct lagg_vlantag {
        uint16_t         lvt_vtag;
        TAILQ_ENTRY(lagg_vlantag)
                         lvt_entry;
};

struct lagg_softc {
        kmutex_t                 sc_lock;
        struct ifmedia           sc_media;
        uint64_t                 sc_media_active;
        u_char                   sc_iftype;

        /* interface link-layer address */
        uint8_t                  sc_lladdr[ETHER_ADDR_LEN];
        /* generated random lladdr */
        uint8_t                  sc_lladdr_rand[ETHER_ADDR_LEN];

        LIST_HEAD(, lagg_mc_entry)
                                 sc_mclist;
        TAILQ_HEAD(, lagg_vlantag)
                                 sc_vtags;
        pserialize_t             sc_psz;
        struct lagg_variant     *sc_var;
        SIMPLEQ_HEAD(, lagg_port)
                                 sc_ports;
        size_t                   sc_nports;
        char                     sc_evgroup[16];
        struct evcnt             sc_novar;

        struct sysctllog        *sc_sysctllog;
        const struct sysctlnode *sc_sysctlnode;
        bool                     sc_hash_mac;
        bool                     sc_hash_ipaddr;
        bool                     sc_hash_ip6addr;
        bool                     sc_hash_tcp;
        bool                     sc_hash_udp;

        /*
         * storage size of sc_if is a variable-length,
         * should be the last
         */
        struct ifnet             sc_if;
};

/*
 * Locking notes:
 * - sc_lock(LAGG_LOCK()) is an adaptive mutex and protects items
 *   of struct lagg_softc
 * - a lock in struct lagg_proto_softc, for example LACP_LOCK(), is
 *   an adaptive mutex and protects member contained in the struct
 * - sc_var is protected by both pselialize (sc_psz) and psref (lv_psref)
 *    - Updates of sc_var is serialized by sc_lock
 * - Items in sc_ports is protected by both psref (lp_psref) and
 *   pserialize contained in struct lagg_proto_softc
 *   - details are described in if_laggport.c and if_lagg_lacp.c
 *   - Updates of items in sc_ports are serialized by sc_lock
 * - an instance referenced by lp_proto_ctx in struct lagg_port is
 *   protected by a lock in struct lagg_proto_softc
 *
 * Locking order:
 * - IFNET_LOCK(sc_if) -> LAGG_LOCK -> ETHER_LOCK(sc_if) -> a lock in
 *   struct lagg_port_softc
 * - IFNET_LOCK(sc_if) -> LAGG_LOCK -> IFNET_LOCK(lp_ifp)
 * - IFNET_LOCK(lp_ifp) -> a lock in struct lagg_proto_softc
 * - Currently, there is no combination of following locks
 *   - IFNET_LOCK(lp_ifp) and ETHER_LOCK(sc_if)
 */
#define LAGG_LOCK(_sc)          mutex_enter(&(_sc)->sc_lock)
#define LAGG_UNLOCK(_sc)        mutex_exit(&(_sc)->sc_lock)
#define LAGG_LOCKED(_sc)        mutex_owned(&(_sc)->sc_lock)
#define LAGG_CLLADDR(_sc)       CLLADDR((_sc)->sc_if.if_sadl)

#define LAGG_PORTS_FOREACH(_sc, _lp)    \
    SIMPLEQ_FOREACH((_lp), &(_sc)->sc_ports, lp_entry)
#define LAGG_PORTS_FIRST(_sc)   SIMPLEQ_FIRST(&(_sc)->sc_ports)
#define LAGG_PORTS_EMPTY(_sc)   SIMPLEQ_EMPTY(&(_sc)->sc_ports)
#define LAGG_PORT_IOCTL(_lp, _cmd, _data)       \
        (_lp)->lp_ioctl == NULL ? ENOTTY :      \
        (_lp)->lp_ioctl((_lp)->lp_ifp, (_cmd), (_data))

static inline const void *
lagg_m_extract(struct mbuf *m, size_t off, size_t reqlen, size_t align,
    void *buf)
{
        ssize_t len;
        const void *rv;

        KASSERT(ISSET(m->m_flags, M_PKTHDR));
        len = off + reqlen;

        if (m->m_pkthdr.len < len) {
                return NULL;
        }

        if (m->m_len >= len &&
            ((uintptr_t)(mtod(m, uint8_t *) + off) % align) == 0) {
                rv = mtod(m, uint8_t *) + off;
        } else {
                m_copydata(m, off, reqlen, buf);
                rv = buf;
        }

        return rv;
}

static inline int
lagg_port_xmit(struct lagg_port *lp, struct mbuf *m)
{

        return if_transmit_lock(lp->lp_ifp, m);
}

static inline bool
lagg_portactive(struct lagg_port *lp)
{
        struct ifnet *ifp;

        ifp = lp->lp_ifp;

        if (ifp->if_link_state != LINK_STATE_DOWN &&
            ISSET(ifp->if_flags, IFF_UP)) {
                return true;
        }

        return false;
}

static inline bool
lagg_debug_enable(struct lagg_softc *sc)
{
        if (__predict_false(ISSET(sc->sc_if.if_flags, IFF_DEBUG)))
                return true;

        return false;
}

#define LAGG_LOG(_sc, _lvl, _fmt, _arg...) do {         \
        if ((_lvl) == LOG_DEBUG &&                      \
            !lagg_debug_enable(_sc))                    \
                break;                                  \
                                                        \
        log((_lvl), "%s: ", (_sc)->sc_if.if_xname);     \
        addlog((_fmt), ##_arg);                         \
} while(0)

void            lagg_port_getref(struct lagg_port *, struct psref *);
void            lagg_port_putref(struct lagg_port *, struct psref *);
void            lagg_output(struct lagg_softc *,
                    struct lagg_port *, struct mbuf *);
uint32_t        lagg_hashmbuf(struct lagg_softc *, struct mbuf *);
void            lagg_set_linkspeed(struct lagg_softc *, uint64_t);

void            lagg_common_detach(struct lagg_proto_softc *);
int             lagg_common_allocport(struct lagg_proto_softc *,
                    struct lagg_port *);
void            lagg_common_freeport(struct lagg_proto_softc *,
                    struct lagg_port *);
void            lagg_common_startport(struct lagg_proto_softc *,
                    struct lagg_port *);
void            lagg_common_stopport(struct lagg_proto_softc *,
                    struct lagg_port *);
void            lagg_common_linkstate_ifnet_locked(struct lagg_proto_softc *,
                    struct lagg_port *);

int             lagg_none_attach(struct lagg_softc *,
                    struct lagg_proto_softc **);

int             lagg_fail_attach(struct lagg_softc *,
                    struct lagg_proto_softc **);
int             lagg_fail_transmit(struct lagg_proto_softc *, struct mbuf *);
struct mbuf *   lagg_fail_input(struct lagg_proto_softc *, struct lagg_port *,
                   struct mbuf *);
void            lagg_fail_portstat(struct lagg_proto_softc *,
                    struct lagg_port *, struct laggreqport *);
int             lagg_fail_ioctl(struct lagg_proto_softc *,
                    struct laggreqproto *);

int             lagg_lb_attach(struct lagg_softc *, struct lagg_proto_softc **);
void            lagg_lb_startport(struct lagg_proto_softc *,
                    struct lagg_port *);
void            lagg_lb_stopport(struct lagg_proto_softc *, struct lagg_port *);
int             lagg_lb_transmit(struct lagg_proto_softc *, struct mbuf *);
struct mbuf *   lagg_lb_input(struct lagg_proto_softc *, struct lagg_port *,
                    struct mbuf *);
void            lagg_lb_portstat(struct lagg_proto_softc *,
                    struct lagg_port *, struct laggreqport *);

int             lacp_attach(struct lagg_softc *, struct lagg_proto_softc **);
void            lacp_detach(struct lagg_proto_softc *);
int             lacp_up(struct lagg_proto_softc *);
void            lacp_down(struct lagg_proto_softc *);
int             lacp_transmit(struct lagg_proto_softc *, struct mbuf *);
struct mbuf *   lacp_input(struct lagg_proto_softc *, struct lagg_port *,
                    struct mbuf *);
int             lacp_allocport(struct lagg_proto_softc *, struct lagg_port *);
void            lacp_freeport(struct lagg_proto_softc *, struct lagg_port *);
void            lacp_startport(struct lagg_proto_softc *, struct lagg_port *);
void            lacp_stopport(struct lagg_proto_softc *, struct lagg_port *);
void            lacp_protostat(struct lagg_proto_softc *,
                    struct laggreqproto *);
void            lacp_portstat(struct lagg_proto_softc *, struct lagg_port *,
                    struct laggreqport *);
void            lacp_linkstate_ifnet_locked(struct lagg_proto_softc *, struct lagg_port *);
int             lacp_ioctl(struct lagg_proto_softc *, struct laggreqproto *);
#endif