/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef _INET_ILB_IMPL_H
#define	_INET_ILB_IMPL_H

#include <sys/types.h>
#include <sys/kstat.h>
#include <sys/netstack.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Statistics in ILB is stored in several kstat structures.  ilb_g_kstat
 * represents the global statistics.  ilb_rule_kstat represents the statistics
 * of a rule.  ilb_server_kstat represents the statistics of a server.
 */
#define	ILB_KSTAT_MOD_NAME	"ilb"

typedef struct ilb_g_kstat_s {
	kstat_named_t	num_rules;	/* Number of rules */
	kstat_named_t	ip_frag_in;	/* Number of input fragments */
	kstat_named_t	ip_frag_dropped; /* Number of fragments dropped */
} ilb_g_kstat_t;

#define	ILB_KSTAT_UPDATE(ilbs, x, y)				\
{								\
	DTRACE_PROBE1(ilb__g__kstat__##x, ilb_stack_t *,	\
	    (ilbs));						\
	((ilbs)->ilbs_kstat->x.value.ui64 += (y));		\
}

typedef struct ilb_rule_kstat {
	kstat_named_t	num_servers;	/* Number of back end servers */
	kstat_named_t	bytes_not_processed; /* Num of bytes not processed. */
	kstat_named_t	pkt_not_processed; /* Num of packets not processed. */
	kstat_named_t	bytes_dropped;	/* Number of bytes dropped */
	kstat_named_t	pkt_dropped;	/* Number of packets dropped */
	kstat_named_t	nomem_bytes_dropped; /* Bytes dropped due to nomem */
	kstat_named_t	nomem_pkt_dropped; /* Packets dropped due to nomem */
	kstat_named_t	noport_bytes_dropped; /* No NAT sport bytes drop */
	kstat_named_t	noport_pkt_dropped; /* No NAT sport packet drop */
	kstat_named_t	icmp_echo_processed; /* No of ICMP echo processed */
	kstat_named_t	icmp_dropped;	/* No of ICMP packets dropped */
	kstat_named_t	icmp_2big_processed; /* No of ICMP 2big processed */
	kstat_named_t	icmp_2big_dropped; /* No of ICMP 2big dropped */
} ilb_rule_kstat_t;

#define	ILB_R_KSTAT(rule, x)					\
{								\
	DTRACE_PROBE1(ilb__r__kstat__##x, ilb_rule_t *,	\
	    (rule));						\
	((rule)->ir_kstat.x.value.ui64++);			\
}
#define	ILB_R_KSTAT_UPDATE(rule, x, y) 				\
{								\
	DTRACE_PROBE1(ilb__r__kstat__##x, ilb_rule_t *,	\
	    (rule));						\
	((rule)->ir_kstat.x.value.ui64 += (y));			\
}

typedef struct ilb_server_kstat {
	kstat_named_t	bytes_processed; /* Number of bytes processed */
	kstat_named_t	pkt_processed;	/* Number of packets processed */
	kstat_named_t	ip_address;	/* IP address of the server */
} ilb_server_kstat_t;

#define	ILB_S_KSTAT(host, x)					\
{								\
	DTRACE_PROBE1(ilb__s__kstat__##x, ilb_server_t *,	\
	    (host));						\
	((host)->iser_kstat.x.value.ui64++);			\
}
#define	ILB_S_KSTAT_UPDATE(host, x, y)				\
{								\
	DTRACE_PROBE1(ilb__s__kstat__##x, ilb_server_t *,	\
	    (host));						\
	((host)->iser_kstat.x.value.ui64 += (y));		\
}

/* The maximum port range, meaning all ports (65535 - 1). */
#define	ILB_ALL_PORTS_RANGE	65534

struct ilb_nat_src_s;

/*
 * This structure reprensents a server.
 */
typedef struct ilb_server_s {
	in6_addr_t		iser_addr_v6;
	in6_addr_t		iser_prefix_v6;
#define	iser_addr_v4		iser_addr_v6.s6_addr32[3]
#define	iser_prefix_v4		iser_prefix_v6.s6_addr32[3]

	boolean_t		iser_port_range;
	in_port_t		iser_min_port;	/* In host byte order */
	in_port_t		iser_max_port;

	char			iser_name[ILB_SERVER_NAMESZ];
	char			iser_ip_addr[INET6_ADDRSTRLEN];
	netstackid_t		iser_stackid;
	kstat_t			*iser_ksp;
	ilb_server_kstat_t	iser_kstat;
	struct ilb_server_s	*iser_next;

	boolean_t		iser_enabled;
	kmutex_t		iser_lock;
	kcondvar_t		iser_cv;
	uint64_t		iser_refcnt;

	int64_t			iser_die_time;

	struct ilb_nat_src_s	*iser_nat_src;
} ilb_server_t;

#define	ILB_SERVER_REFHOLD(host)	\
{					\
	mutex_enter(&(host)->iser_lock);	\
	(host)->iser_refcnt++;		\
	ASSERT((host)->iser_refcnt != 1);	\
	mutex_exit(&(host)->iser_lock);	\
}

#define	ILB_SERVER_REFRELE(host)		\
{						\
	mutex_enter(&(host)->iser_lock);		\
	(host)->iser_refcnt--;			\
	if ((host)->iser_refcnt == 1)		\
		cv_signal(&(host)->iser_cv);	\
	mutex_exit(&(host)->iser_lock);		\
}

struct ilb_rule_s;
struct ilb_hash_s;

typedef struct ilb_alg_data_s {
	boolean_t	(*ilb_alg_lb)(in6_addr_t *, in_port_t, in6_addr_t *,
			    in_port_t, void *, ilb_server_t **);
	int		(*ilb_alg_server_add)(ilb_server_t *, void *);
	int		(*ilb_alg_server_del)(ilb_server_t *, void *);
	int		(*ilb_alg_server_enable)(ilb_server_t *, void *);
	int		(*ilb_alg_server_disable)(ilb_server_t *, void *);
	void		(*ilb_alg_fini)(struct ilb_alg_data_s **);

	void		*ilb_alg_data;
} ilb_alg_data_t;

/*
 * A load balance rule has
 *
 * 1. a name
 * 2. a network protocol
 * 3. a transport protocol
 * 4. a load balance mechanism (DSR, NAT, ...)
 * 5. a target address (VIP)
 * 6. a target port (or port ranges)
 * 7. a pool of back end servers
 * 8. a load balance algorithm (round robin, hashing, ...)
 */
typedef struct ilb_rule_s {
	char			ir_name[ILB_RULE_NAMESZ];
	uint8_t			ir_ipver;
	uint8_t			ir_proto;
	ilb_topo_impl_t		ir_topo;
	zoneid_t		ir_zoneid;
	uint32_t		ir_flags;

	in6_addr_t		ir_target_v6;
#define	ir_target_v4		ir_target_v6.s6_addr32[3]
	in6_addr_t		ir_prefix_v6;
#define	ir_target_prefix_v4	ir_prefix_v6.s6_addr32[3]

	boolean_t		ir_port_range;
	in_port_t		ir_min_port;	/* In host byte order */
	in_port_t		ir_max_port;

	ilb_server_t		*ir_servers;

	uint32_t		ir_nat_expiry;
	uint32_t		ir_conn_drain_timeout;
	in6_addr_t		ir_nat_src_start;
	in6_addr_t		ir_nat_src_end;

	boolean_t		ir_sticky;
	in6_addr_t		ir_sticky_mask;
	uint32_t		ir_sticky_expiry;

	struct ilb_rule_s	*ir_next;

	struct ilb_rule_s	*ir_hash_next;
	struct ilb_rule_s	*ir_hash_prev;
	struct ilb_hash_s	*ir_hash;

	ilb_algo_impl_t		ir_alg_type;
	ilb_alg_data_t		*ir_alg;

	kstat_t			*ir_ksp;
	ilb_rule_kstat_t	ir_kstat;
	uint_t			ir_ks_instance;

	kmutex_t		ir_lock;
	kcondvar_t		ir_cv;
	uint32_t		ir_refcnt;
} ilb_rule_t;

#define	ILB_RULE_REFHOLD(rule)			\
{						\
	mutex_enter(&(rule)->ir_lock);		\
	(rule)->ir_refcnt++;			\
	ASSERT((rule)->ir_refcnt != 1);		\
	mutex_exit(&(rule)->ir_lock);		\
}

#define	ILB_RULE_REFRELE(rule)			\
{						\
	mutex_enter(&(rule)->ir_lock);		\
	ASSERT((rule)->ir_refcnt >= 2);		\
	if (--(rule)->ir_refcnt <= 2)		\
		cv_signal(&(rule)->ir_cv);	\
	mutex_exit(&(rule)->ir_lock);		\
}


typedef struct ilb_hash_s {
	ilb_rule_t	*ilb_hash_rule;
	kmutex_t	ilb_hash_lock;
#if defined(_LP64) || defined(_I32LPx)
	char		ilb_hash_pad[48];
#else
	char		ilb_hash_pad[56];
#endif
} ilb_hash_t;

struct ilb_nat_src_entry_s;

/*
 * Structure to store NAT info.
 *
 * Half NAT only uses the first 4 fields in the structure.
 */
typedef struct {
	in6_addr_t			vip;
	in6_addr_t			nat_dst;
	in_port_t			dport;
	in_port_t			nat_dport;

	in6_addr_t			src;
	in6_addr_t			nat_src;
	in_port_t			sport;
	in_port_t			nat_sport;

	struct ilb_nat_src_entry_s	*src_ent;
} ilb_nat_info_t;

extern int ilb_kmem_flags;

#ifdef __cplusplus
}
#endif

#endif /* _INET_ILB_IMPL_H */