root/sys/netpfil/ipfw/nat64/nat64lsn.h
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2015-2020 Yandex LLC
 * Copyright (c) 2015 Alexander V. Chernikov <melifaro@FreeBSD.org>
 * Copyright (c) 2015-2020 Andrey V. Elsukov <ae@FreeBSD.org>
 *
 * 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 AUTHOR ``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 AUTHOR 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 _IP_FW_NAT64LSN_H_
#define _IP_FW_NAT64LSN_H_

#include "ip_fw_nat64.h"
#include "nat64_translate.h"

#define NAT64_MIN_PORT          1024
struct nat64lsn_host;
struct nat64lsn_alias;

struct nat64lsn_state {
        /* IPv6 host entry keeps hash table to speedup state lookup */
        CK_SLIST_ENTRY(nat64lsn_state)  entries;
        struct nat64lsn_host    *host;

        struct in6_addr ip6_dst;        /* Destination IPv6 address */

        in_addr_t       ip_src;         /* Alias IPv4 address */
        in_addr_t       ip_dst;         /* Destination IPv4 address */
        uint16_t        dport;          /* Destination port */
        uint16_t        sport;          /* Source port */

        uint32_t        hval;
        uint32_t        flags;          /* Internal flags */
        uint16_t        aport;
        uint16_t        timestamp;      /* last used */
        uint8_t         proto;
        uint8_t         _spare[7];
};

struct nat64lsn_states_chunk {
        struct nat64lsn_state   state[64];
};

#define ISSET64(mask, bit)      ((mask) & ((uint64_t)1 << (bit)))
#define ISSET32(mask, bit)      ((mask) & ((uint32_t)1 << (bit)))
struct nat64lsn_pg {
        uint16_t                base_port;
        uint16_t                timestamp;
        uint8_t                 proto;
        uint8_t                 chunks_count;
        uint16_t                flags;
#define NAT64LSN_DEADPG         1

        union {
                uint64_t        freemask64;
                uint32_t        freemask32[2];
                uint64_t        *freemask64_chunk;
                uint32_t        *freemask32_chunk;
                void            *freemask_chunk;
        };
        union {
                struct nat64lsn_states_chunk *states;
                struct nat64lsn_states_chunk **states_chunk;
        };
        /*
         * An alias object holds chain of all allocated PGs.
         * The chain is used mostly by expiration code.
         */
        CK_SLIST_ENTRY(nat64lsn_pg)     entries;
};

#define CHUNK_BY_FADDR(p, a)    ((a) & ((p)->chunks_count - 1))

#ifdef __LP64__
#define FREEMASK_CHUNK(p, v)    \
    ((p)->chunks_count == 1 ? &(p)->freemask64 : \
        &(p)->freemask64_chunk[CHUNK_BY_FADDR(p, v)])
#define FREEMASK_BITCOUNT(pg, faddr)    \
    bitcount64(*FREEMASK_CHUNK((pg), (faddr)))
#else
#define FREEMASK_CHUNK(p, v)    \
    ((p)->chunks_count == 1 ? &(p)->freemask32[0] : \
        &(p)->freemask32_chunk[CHUNK_BY_FADDR(p, v) * 2])
#define FREEMASK_BITCOUNT(pg, faddr)    \
    bitcount64(*(uint64_t *)FREEMASK_CHUNK((pg), (faddr)))
#endif /* !__LP64__ */

struct nat64lsn_pgchunk {
        struct nat64lsn_pg      *pgptr[32];
};

struct nat64lsn_aliaslink {
        CK_SLIST_ENTRY(nat64lsn_aliaslink)      alias_entries;
        CK_SLIST_ENTRY(nat64lsn_aliaslink)      host_entries;
        struct nat64lsn_alias   *alias;
};

CK_SLIST_HEAD(nat64lsn_aliaslink_slist, nat64lsn_aliaslink);
CK_SLIST_HEAD(nat64lsn_states_slist, nat64lsn_state);
CK_SLIST_HEAD(nat64lsn_hosts_slist, nat64lsn_host);
CK_SLIST_HEAD(nat64lsn_pg_slist, nat64lsn_pg);

struct nat64lsn_alias {
        struct nat64lsn_aliaslink_slist hosts;
        struct nat64lsn_pg_slist        portgroups;

        struct mtx              lock;
        in_addr_t               addr;   /* host byte order */
        uint32_t                hosts_count;

        uint16_t                timestamp;
        uint16_t                tcp_pgcount;
        uint16_t                udp_pgcount;
        uint16_t                icmp_pgcount;
        /*
         * We keep PGs in chunks by 32 PGs in each.
         * Each chunk allocated by demand, and then corresponding bit
         * is set in chunkmask.
         *
         * Also we keep last used PG's index for each protocol.
         *   pgidx / 32 = index of pgchunk;
         *   pgidx % 32 = index of pgptr in pgchunk.
         */
        uint32_t                tcp_chunkmask;
        uint32_t                tcp_pgidx;

        uint32_t                udp_chunkmask;
        uint32_t                udp_pgidx;

        uint32_t                icmp_chunkmask;
        uint32_t                icmp_pgidx;
        /*
         * Each pgchunk keeps 32 pointers to PGs. If pgptr pointer is
         * valid, we have corresponding bit set in the pgmask.
         */
        uint32_t                tcp_pgmask[32];
        uint32_t                udp_pgmask[32];
        uint32_t                icmp_pgmask[32];

        struct nat64lsn_pgchunk *tcp[32];
        struct nat64lsn_pgchunk *udp[32];
        struct nat64lsn_pgchunk *icmp[32];
};
#define ALIAS_LOCK_INIT(p)      \
        mtx_init(&(p)->lock, "alias_lock", NULL, MTX_DEF)
#define ALIAS_LOCK_DESTROY(p)   mtx_destroy(&(p)->lock)
#define ALIAS_LOCK(p)           mtx_lock(&(p)->lock)
#define ALIAS_UNLOCK(p)         mtx_unlock(&(p)->lock)

#define NAT64LSN_HSIZE          256
#define NAT64LSN_MAX_HSIZE      4096
#define NAT64LSN_HOSTS_HSIZE    1024

struct nat64lsn_host {
        struct in6_addr         addr;
        struct nat64lsn_aliaslink_slist aliases;
        struct nat64lsn_states_slist    *states_hash;
        CK_SLIST_ENTRY(nat64lsn_host)   entries;
        uint32_t                states_count;
        uint32_t                hval;
        uint32_t                flags;
#define NAT64LSN_DEADHOST       1
#define NAT64LSN_GROWHASH       2
        uint16_t                states_hashsize;
        uint16_t                timestamp;
        struct mtx              lock;
};

#define HOST_LOCK_INIT(p)       \
        mtx_init(&(p)->lock, "host_lock", NULL, MTX_DEF|MTX_NEW)
#define HOST_LOCK_DESTROY(p)    mtx_destroy(&(p)->lock)
#define HOST_LOCK(p)            mtx_lock(&(p)->lock)
#define HOST_UNLOCK(p)          mtx_unlock(&(p)->lock)

VNET_DECLARE(uint32_t, nat64lsn_eid);
#define V_nat64lsn_eid          VNET(nat64lsn_eid)
#define IPFW_TLV_NAT64LSN_NAME  IPFW_TLV_EACTION_NAME(V_nat64lsn_eid)

/* Timestamp macro */
#define _CT             ((int)time_uptime % 65536)
#define SET_AGE(x)      (x) = _CT
#define GET_AGE(x)      ((_CT >= (x)) ? _CT - (x): (int)65536 + _CT - (x))

STAILQ_HEAD(nat64lsn_job_head, nat64lsn_job_item);

struct nat64lsn_cfg {
        struct nat64lsn_hosts_slist     *hosts_hash;
        struct nat64lsn_alias   *aliases;       /* array of aliases */

        struct mtx      lock;
        uint32_t        hosts_hashsize;
        uint32_t        hash_seed;

        uint32_t        prefix4;        /* IPv4 prefix */
        uint32_t        pmask4;         /* IPv4 prefix mask */
        uint8_t         plen4;
        uint8_t         nomatch_verdict;/* Return value on no-match */

        uint32_t        hosts_count;    /* Number of items in host hash */
        uint32_t        states_chunks;  /* Number of states chunks per PG */
        uint32_t        jmaxlen;        /* Max jobqueue length */
        uint16_t        host_delete_delay;      /* Stale host delete delay */
        uint16_t        pgchunk_delete_delay;
        uint16_t        pg_delete_delay;        /* Stale portgroup del delay */
        uint16_t        st_syn_ttl;     /* TCP syn expire */
        uint16_t        st_close_ttl;   /* TCP fin expire */
        uint16_t        st_estab_ttl;   /* TCP established expire */
        uint16_t        st_udp_ttl;     /* UDP expire */
        uint16_t        st_icmp_ttl;    /* ICMP expire */

        struct nat64_config     base;
#define NAT64LSN_FLAGSMASK      (NAT64_LOG | NAT64_ALLOW_PRIVATE | \
    NAT64LSN_ALLOW_SWAPCONF)
#define NAT64LSN_ANYPREFIX      0x00000100

        struct mtx              periodic_lock;
        struct callout          periodic;
        struct callout          jcallout;
        struct vnet             *vp;
        struct nat64lsn_job_head        jhead;
        int                     jlen;
        char                    name[64];       /* Nat instance name */
};

struct nat64lsn_instance {
        struct named_object     no;
        struct nat64lsn_cfg     *cfg;
        char                    name[64];       /* Nat instance name */
};

/* CFG_LOCK protects cfg->hosts_hash from modification */
#define CFG_LOCK_INIT(p)        \
        mtx_init(&(p)->lock, "cfg_lock", NULL, MTX_DEF)
#define CFG_LOCK_DESTROY(p)     mtx_destroy(&(p)->lock)
#define CFG_LOCK(p)             mtx_lock(&(p)->lock)
#define CFG_UNLOCK(p)           mtx_unlock(&(p)->lock)

#define CALLOUT_LOCK_INIT(p)    \
        mtx_init(&(p)->periodic_lock, "periodic_lock", NULL, MTX_DEF)
#define CALLOUT_LOCK_DESTROY(p) mtx_destroy(&(p)->periodic_lock)
#define CALLOUT_LOCK(p)         mtx_lock(&(p)->periodic_lock)
#define CALLOUT_UNLOCK(p)       mtx_unlock(&(p)->periodic_lock)

MALLOC_DECLARE(M_NAT64LSN);

struct nat64lsn_cfg *nat64lsn_init_config(struct ip_fw_chain *ch,
    in_addr_t prefix, int plen);
void nat64lsn_destroy_config(struct nat64lsn_cfg *cfg);
void nat64lsn_start_instance(struct nat64lsn_cfg *cfg);
void nat64lsn_init_internal(void);
void nat64lsn_uninit_internal(void);
int ipfw_nat64lsn(struct ip_fw_chain *ch, struct ip_fw_args *args,
    ipfw_insn *cmd, int *done);

#endif /* _IP_FW_NAT64LSN_H_ */