summaryrefslogtreecommitdiffstats
path: root/servers/lloadd/lload.h
diff options
context:
space:
mode:
Diffstat (limited to 'servers/lloadd/lload.h')
-rw-r--r--servers/lloadd/lload.h601
1 files changed, 601 insertions, 0 deletions
diff --git a/servers/lloadd/lload.h b/servers/lloadd/lload.h
new file mode 100644
index 0000000..f9144a5
--- /dev/null
+++ b/servers/lloadd/lload.h
@@ -0,0 +1,601 @@
+/* lload.h - load balancer include file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _LLOAD_H_
+#define _LLOAD_H_
+
+#include "ldap_defaults.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <sys/types.h>
+#include <ac/syslog.h>
+#include <ac/regex.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+#include <ac/param.h>
+
+#include "ldap_avl.h"
+
+#include "../servers/slapd/slap.h"
+#include "../slapd/back-monitor/back-monitor.h"
+
+#ifndef ldap_debug
+#define ldap_debug slap_debug
+#endif
+
+#include "ldap_log.h"
+
+#include <ldap.h>
+#include <ldap_schema.h>
+
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
+#include "ldap_pvt_thread.h"
+#include "ldap_queue.h"
+
+#include <event2/event.h>
+
+#ifdef HAVE_CYRUS_SASL
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+#endif /* HAVE_CYRUS_SASL */
+
+LDAP_BEGIN_DECL
+
+#ifdef SERVICE_NAME
+#undef SERVICE_NAME
+#endif
+
+#define SERVICE_NAME OPENLDAP_PACKAGE "-lloadd"
+
+#define LLOAD_SB_MAX_INCOMING_CLIENT ( ( 1 << 24 ) - 1 )
+#define LLOAD_SB_MAX_INCOMING_UPSTREAM ( ( 1 << 24 ) - 1 )
+
+#define LLOAD_CONN_MAX_PDUS_PER_CYCLE_DEFAULT 10
+
+#define BER_BV_OPTIONAL( bv ) ( BER_BVISNULL( bv ) ? NULL : ( bv ) )
+
+#include <epoch.h>
+
+#define checked_lock( mutex ) \
+ if ( ldap_pvt_thread_mutex_lock( mutex ) != 0 ) assert(0)
+#define checked_unlock( mutex ) \
+ if ( ldap_pvt_thread_mutex_unlock( mutex ) != 0 ) assert(0)
+
+#ifdef LDAP_THREAD_DEBUG
+#define assert_locked( mutex ) \
+ if ( ldap_pvt_thread_mutex_trylock( mutex ) == 0 ) assert(0)
+#else
+#define assert_locked( mutex ) ( (void)0 )
+#endif
+
+typedef struct LloadTier LloadTier;
+typedef struct LloadBackend LloadBackend;
+typedef struct LloadPendingConnection LloadPendingConnection;
+typedef struct LloadConnection LloadConnection;
+typedef struct LloadOperation LloadOperation;
+typedef struct LloadChange LloadChange;
+/* end of forward declarations */
+
+typedef LDAP_STAILQ_HEAD(TierSt, LloadTier) lload_t_head;
+typedef LDAP_CIRCLEQ_HEAD(BeSt, LloadBackend) lload_b_head;
+typedef LDAP_CIRCLEQ_HEAD(ConnSt, LloadConnection) lload_c_head;
+
+LDAP_SLAPD_V (lload_t_head) tiers;
+LDAP_SLAPD_V (lload_c_head) clients;
+LDAP_SLAPD_V (struct slap_bindconf) bindconf;
+LDAP_SLAPD_V (struct berval) lloadd_identity;
+
+/* Used to coordinate server (un)pause, shutdown */
+LDAP_SLAPD_V (ldap_pvt_thread_mutex_t) lload_wait_mutex;
+LDAP_SLAPD_V (ldap_pvt_thread_cond_t) lload_pause_cond;
+LDAP_SLAPD_V (ldap_pvt_thread_cond_t) lload_wait_cond;
+
+typedef int lload_cf_aux_table_parse_x( struct berval *val,
+ void *bc,
+ slap_cf_aux_table *tab0,
+ const char *tabmsg,
+ int unparse );
+
+typedef struct LloadListener LloadListener;
+
+enum lc_type {
+ LLOAD_CHANGE_UNDEFINED = 0,
+ LLOAD_CHANGE_MODIFY,
+ LLOAD_CHANGE_ADD,
+ LLOAD_CHANGE_DEL,
+};
+
+enum lc_object {
+ LLOAD_UNDEFINED = 0,
+ LLOAD_DAEMON,
+ /*
+ LLOAD_BINDCONF,
+ */
+ LLOAD_TIER,
+ LLOAD_BACKEND,
+};
+
+enum lcf_daemon {
+ LLOAD_DAEMON_MOD_THREADS = 1 << 0,
+ LLOAD_DAEMON_MOD_FEATURES = 1 << 1,
+ LLOAD_DAEMON_MOD_TLS = 1 << 2,
+ LLOAD_DAEMON_MOD_LISTENER_ADD = 1 << 3,
+ LLOAD_DAEMON_MOD_LISTENER_REPLACE = 1 << 4,
+ LLOAD_DAEMON_MOD_BINDCONF = 1 << 5,
+};
+
+enum lcf_tier {
+ LLOAD_TIER_MOD_TYPE = 1 << 0,
+};
+
+enum lcf_backend {
+ LLOAD_BACKEND_MOD_OTHER = 1 << 0,
+ LLOAD_BACKEND_MOD_CONNS = 1 << 1,
+};
+
+struct LloadChange {
+ enum lc_type type;
+ enum lc_object object;
+ union {
+ int generic;
+ enum lcf_daemon daemon;
+ enum lcf_tier tier;
+ enum lcf_backend backend;
+ } flags;
+ void *target;
+};
+
+typedef enum {
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+ LLOAD_FEATURE_VC = 1 << 0,
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+ LLOAD_FEATURE_PROXYAUTHZ = 1 << 1,
+ LLOAD_FEATURE_PAUSE = 1 << 2,
+} lload_features_t;
+
+#define LLOAD_FEATURE_SUPPORTED_MASK ( \
+ LLOAD_FEATURE_PROXYAUTHZ | \
+ 0 )
+
+#ifdef BALANCER_MODULE
+#define LLOAD_TLS_CTX ( lload_use_slap_tls_ctx ? slap_tls_ctx : lload_tls_ctx )
+#else
+#define LLOAD_TLS_CTX ( lload_tls_ctx )
+#endif
+
+enum lload_tls_type {
+ LLOAD_CLEARTEXT = 0,
+ LLOAD_LDAPS,
+ LLOAD_STARTTLS_OPTIONAL,
+ LLOAD_STARTTLS,
+ LLOAD_TLS_ESTABLISHED,
+};
+
+struct LloadPendingConnection {
+ LloadBackend *backend;
+
+ struct event *event;
+ ber_socket_t fd;
+
+ LDAP_LIST_ENTRY(LloadPendingConnection) next;
+};
+
+typedef struct lload_counters_t {
+ ldap_pvt_mp_t lc_ops_completed;
+ ldap_pvt_mp_t lc_ops_received;
+ ldap_pvt_mp_t lc_ops_forwarded;
+ ldap_pvt_mp_t lc_ops_rejected;
+ ldap_pvt_mp_t lc_ops_failed;
+} lload_counters_t;
+
+enum {
+ LLOAD_STATS_OPS_BIND = 0,
+ LLOAD_STATS_OPS_OTHER,
+ LLOAD_STATS_OPS_LAST
+};
+
+typedef struct lload_global_stats_t {
+ ldap_pvt_mp_t global_incoming;
+ ldap_pvt_mp_t global_outgoing;
+ lload_counters_t counters[LLOAD_STATS_OPS_LAST];
+} lload_global_stats_t;
+
+typedef LloadTier *(LloadTierInit)( void );
+typedef int (LloadTierConfigCb)( LloadTier *tier, char *arg );
+typedef int (LloadTierBackendConfigCb)( LloadTier *tier, LloadBackend *b, char *arg );
+typedef int (LloadTierCb)( LloadTier *tier );
+typedef int (LloadTierResetCb)( LloadTier *tier, int shutdown );
+typedef int (LloadTierBackendCb)( LloadTier *tier, LloadBackend *b );
+typedef void (LloadTierChange)( LloadTier *tier, LloadChange *change );
+typedef int (LloadTierSelect)( LloadTier *tier,
+ LloadOperation *op,
+ LloadConnection **cp,
+ int *res,
+ char **message );
+
+struct lload_tier_type {
+ char *tier_name;
+
+ struct berval tier_oc, tier_backend_oc;
+
+ LloadTierInit *tier_init;
+ LloadTierConfigCb *tier_config;
+ LloadTierBackendConfigCb *tier_backend_config;
+ LloadTierCb *tier_startup;
+ LloadTierCb *tier_update;
+ LloadTierResetCb *tier_reset;
+ LloadTierCb *tier_destroy;
+
+ LloadTierBackendCb *tier_add_backend;
+ LloadTierBackendCb *tier_remove_backend;
+ LloadTierChange *tier_change;
+
+ LloadTierSelect *tier_select;
+};
+
+struct LloadTier {
+ struct lload_tier_type t_type;
+ ldap_pvt_thread_mutex_t t_mutex;
+
+ lload_b_head t_backends;
+ int t_nbackends;
+
+ enum {
+ LLOAD_TIER_EXCLUSIVE = 1 << 0, /* Reject if busy */
+ } t_flags;
+
+ struct berval t_name;
+#ifdef BALANCER_MODULE
+ monitor_subsys_t *t_monitor;
+#endif /* BALANCER_MODULE */
+
+ void *t_private;
+ LDAP_STAILQ_ENTRY(LloadTier) t_next;
+};
+
+/* Can hold mutex when locking a linked connection */
+struct LloadBackend {
+ ldap_pvt_thread_mutex_t b_mutex;
+
+ struct berval b_name, b_uri;
+ int b_proto, b_port;
+ enum lload_tls_type b_tls, b_tls_conf;
+ char *b_host;
+
+ int b_retry_timeout, b_failed;
+ struct event *b_retry_event;
+ struct timeval b_retry_tv;
+
+ int b_numconns, b_numbindconns;
+ int b_bindavail, b_active, b_opening;
+ lload_c_head b_conns, b_bindconns, b_preparing;
+ LDAP_LIST_HEAD(ConnectingSt, LloadPendingConnection) b_connecting;
+ LloadConnection *b_last_conn, *b_last_bindconn;
+
+ long b_max_pending, b_max_conn_pending;
+ long b_n_ops_executing;
+
+ lload_counters_t b_counters[LLOAD_STATS_OPS_LAST];
+
+ LloadTier *b_tier;
+
+ time_t b_last_update;
+ uintptr_t b_fitness;
+ int b_weight;
+
+ uintptr_t b_operation_count;
+ uintptr_t b_operation_time;
+
+#ifdef BALANCER_MODULE
+ monitor_subsys_t *b_monitor;
+#endif /* BALANCER_MODULE */
+
+ struct evdns_getaddrinfo_request *b_dns_req;
+ void *b_cookie;
+
+ LDAP_CIRCLEQ_ENTRY(LloadBackend) b_next;
+};
+
+typedef int (*LloadOperationHandler)( LloadConnection *client,
+ LloadOperation *op,
+ BerElement *ber );
+typedef int (*RequestHandler)( LloadConnection *c, LloadOperation *op );
+typedef struct lload_exop_handlers_t {
+ struct berval oid;
+ RequestHandler func;
+} ExopHandler;
+
+typedef int (*CONNECTION_PDU_CB)( LloadConnection *c );
+typedef void (*CONNECTION_DESTROY_CB)( LloadConnection *c );
+
+/* connection state (protected by c_mutex) */
+enum sc_state {
+ LLOAD_C_INVALID = 0, /* MUST BE ZERO (0) */
+ LLOAD_C_READY, /* ready */
+ LLOAD_C_CLOSING, /* closing */
+ LLOAD_C_ACTIVE, /* exclusive operation (tls setup, ...) in progress */
+ LLOAD_C_BINDING, /* binding */
+ LLOAD_C_DYING, /* part-processed dead waiting to be freed, someone
+ * might still be observing it */
+};
+enum sc_type {
+ LLOAD_C_OPEN = 0, /* regular connection */
+ LLOAD_C_PREPARING, /* upstream connection not assigned yet */
+ LLOAD_C_BIND, /* connection used to handle bind client requests if VC not enabled */
+ LLOAD_C_PRIVILEGED, /* connection can override proxyauthz control */
+};
+enum sc_io_state {
+ LLOAD_C_OPERATIONAL = 0, /* all is good */
+ LLOAD_C_READ_HANDOVER = 1 << 0, /* A task to process PDUs is scheduled or
+ * running, do not re-enable c_read_event */
+ LLOAD_C_READ_PAUSE = 1 << 1, /* We want to pause reading until the client
+ * has sufficiently caught up with what we
+ * sent */
+};
+
+/* Tracking whether an operation might cause a client to restrict which
+ * upstreams are eligible */
+enum op_restriction {
+ LLOAD_OP_NOT_RESTRICTED, /* no restrictions in place */
+ LLOAD_OP_RESTRICTED_WRITE, /* client is restricted to a certain backend with
+ * a timeout attached */
+ LLOAD_OP_RESTRICTED_BACKEND, /* client is restricted to a certain backend,
+ * without a timeout */
+ LLOAD_OP_RESTRICTED_UPSTREAM, /* client is restricted to a certain upstream */
+ LLOAD_OP_RESTRICTED_ISOLATE, /* TODO: client is restricted to a certain
+ * upstream and removes the upstream from the
+ * pool */
+ LLOAD_OP_RESTRICTED_REJECT, /* operation should not be forwarded to any
+ * backend, either it is processed internally
+ * or rejected */
+};
+
+/*
+ * represents a connection from an ldap client/to ldap server
+ */
+struct LloadConnection {
+ enum sc_state c_state; /* connection state */
+ enum sc_type c_type;
+ enum sc_io_state c_io_state;
+ ber_socket_t c_fd;
+
+/*
+ * LloadConnection reference counting:
+ * - connection has a reference counter in c_refcnt
+ * - also a liveness/validity token is added to c_refcnt during
+ * lload_connection_init, its existence is tracked in c_live and is usually the
+ * only one that prevents it from being destroyed
+ * - anyone who needs to be able to relock the connection after unlocking it has
+ * to use acquire_ref(), they need to make sure a matching
+ * RELEASE_REF( c, c_refcnt, c->c_destroy ); is run eventually
+ * - when a connection is considered dead, use CONNECTION_DESTROY on a locked
+ * connection, it will be made unreachable from normal places and either
+ * scheduled for reclamation when safe to do so or if anyone still holds a
+ * reference, it just gets unlocked and reclaimed after the last ref is
+ * released
+ * - CONNECTION_LOCK_DESTROY is a shorthand for locking and CONNECTION_DESTROY
+ */
+ ldap_pvt_thread_mutex_t c_mutex; /* protect the connection */
+ uintptr_t c_refcnt, c_live;
+ CONNECTION_DESTROY_CB c_unlink;
+ CONNECTION_DESTROY_CB c_destroy;
+ CONNECTION_PDU_CB c_pdu_cb;
+#define CONNECTION_ASSERT_LOCKED(c) assert_locked( &(c)->c_mutex )
+#define CONNECTION_LOCK(c) \
+ do { \
+ checked_lock( &(c)->c_mutex ); \
+ } while (0)
+#define CONNECTION_UNLOCK(c) \
+ do { \
+ checked_unlock( &(c)->c_mutex ); \
+ } while (0)
+#define CONNECTION_UNLINK_(c) \
+ do { \
+ if ( __atomic_exchange_n( &(c)->c_live, 0, __ATOMIC_ACQ_REL ) ) { \
+ (c)->c_unlink( (c) ); \
+ RELEASE_REF( (c), c_refcnt, c->c_destroy ); \
+ } \
+ } while (0)
+#define CONNECTION_DESTROY(c) \
+ do { \
+ CONNECTION_UNLINK_(c); \
+ CONNECTION_UNLOCK(c); \
+ } while (0)
+#define CONNECTION_LOCK_DESTROY(c) \
+ do { \
+ CONNECTION_LOCK(c); \
+ CONNECTION_DESTROY(c); \
+ } while (0);
+
+ Sockbuf *c_sb; /* ber connection stuff */
+
+ /* set by connection_init */
+ unsigned long c_connid; /* unique id of this connection */
+ struct berval c_peer_name; /* peer name (trans=addr:port) */
+ time_t c_starttime; /* when the connection was opened */
+
+ time_t c_activitytime; /* when the connection was last used */
+ ber_int_t c_next_msgid; /* msgid of the next message */
+
+ /* must not be used while holding either mutex */
+ struct event *c_read_event, *c_write_event;
+ struct timeval *c_read_timeout;
+
+ /* can only be changed by binding thread */
+ struct berval c_sasl_bind_mech; /* mech in progress */
+ struct berval c_auth; /* authcDN (possibly in progress) */
+
+ unsigned long c_pin_id;
+
+#ifdef HAVE_CYRUS_SASL
+ sasl_conn_t *c_sasl_authctx;
+ void *c_sasl_defaults;
+#ifdef SASL_CHANNEL_BINDING /* 2.1.25+ */
+ sasl_channel_binding_t *c_sasl_cbinding; /* Else cyrus-sasl would happily
+ * leak it on sasl_dispose */
+#endif /* SASL_CHANNEL_BINDING */
+#endif /* HAVE_CYRUS_SASL */
+
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+ struct berval c_vc_cookie;
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+
+ /* Can be held while acquiring c_mutex to inject things into c_ops or
+ * destroy the connection */
+ ldap_pvt_thread_mutex_t c_io_mutex; /* only one pdu written at a time */
+
+ BerElement *c_currentber; /* ber we're attempting to read */
+ BerElement *c_pendingber; /* ber we're attempting to write */
+
+ TAvlnode *c_ops; /* Operations pending on the connection */
+
+#ifdef HAVE_TLS
+ enum lload_tls_type c_is_tls; /* true if this LDAP over raw TLS */
+#endif
+
+ long c_n_ops_executing; /* num of ops currently executing */
+ long c_n_ops_completed; /* num of ops completed */
+ lload_counters_t c_counters; /* per connection operation counters */
+
+ enum op_restriction c_restricted;
+ uintptr_t c_restricted_inflight;
+ time_t c_restricted_at;
+ LloadBackend *c_backend;
+ LloadConnection *c_linked_upstream;
+
+ TAvlnode *c_linked;
+
+#ifdef BALANCER_MODULE
+ struct berval c_monitor_dn;
+#endif /* BALANCER_MODULE */
+
+ /*
+ * Protected by the CIRCLEQ mutex:
+ * - Client: clients_mutex
+ * - Upstream: b->b_mutex
+ */
+ LDAP_CIRCLEQ_ENTRY(LloadConnection) c_next;
+};
+
+enum op_state {
+ LLOAD_OP_NOT_FREEING = 0,
+ LLOAD_OP_DETACHING_CLIENT = 1 << 1,
+ LLOAD_OP_DETACHING_UPSTREAM = 1 << 0,
+};
+
+#define LLOAD_OP_DETACHING_MASK \
+ ( LLOAD_OP_DETACHING_UPSTREAM | LLOAD_OP_DETACHING_CLIENT )
+
+/* operation result for monitoring purposes */
+enum op_result {
+ LLOAD_OP_REJECTED, /* operation was not forwarded */
+ LLOAD_OP_COMPLETED, /* operation sent and response received */
+ LLOAD_OP_FAILED, /* operation was forwarded, but no response was received */
+};
+
+/*
+ * Operation reference tracking:
+ * - o_refcnt is set to 1, never incremented
+ * - OPERATION_UNLINK sets it to 0 and on transition from 1 clears both
+ * connection links (o_client, o_upstream)
+ */
+struct LloadOperation {
+ uintptr_t o_refcnt;
+#define OPERATION_UNLINK(op) \
+ try_release_ref( &(op)->o_refcnt, (op), \
+ (dispose_cb *)operation_unlink, \
+ (dispose_cb *)operation_destroy )
+
+ LloadConnection *o_client;
+ unsigned long o_client_connid;
+ ber_int_t o_client_msgid;
+ ber_int_t o_saved_msgid;
+ enum op_restriction o_restricted;
+
+ LloadConnection *o_upstream;
+ unsigned long o_upstream_connid;
+ ber_int_t o_upstream_msgid;
+ struct timeval o_last_response;
+
+ /* Protects o_client, o_upstream links */
+ ldap_pvt_thread_mutex_t o_link_mutex;
+
+ ber_tag_t o_tag;
+ struct timeval o_start;
+ unsigned long o_pin_id;
+
+ enum op_result o_res;
+ BerElement *o_ber;
+ BerValue o_request, o_ctrls;
+};
+
+struct restriction_entry {
+ struct berval oid;
+ enum op_restriction action;
+};
+
+/*
+ * listener; need to access it from monitor backend
+ */
+struct LloadListener {
+ struct berval sl_url;
+ struct berval sl_name;
+ mode_t sl_perms;
+#ifdef HAVE_TLS
+ int sl_is_tls;
+#endif
+ int sl_is_proxied;
+ struct event_base *base;
+ struct evconnlistener *listener;
+ int sl_mute; /* Listener is temporarily disabled due to emfile */
+ int sl_busy; /* Listener is busy (accept thread activated) */
+ ber_socket_t sl_sd;
+ Sockaddr sl_sa;
+#define sl_addr sl_sa.sa_in_addr
+#define LDAP_TCP_BUFFER
+#ifdef LDAP_TCP_BUFFER
+ int sl_tcp_rmem; /* custom TCP read buffer size */
+ int sl_tcp_wmem; /* custom TCP write buffer size */
+#endif
+};
+
+typedef int (*CONNCB)( LloadConnection *c, void *arg );
+
+/* config requires a bi_private with configuration data - dummy for now */
+struct lload_conf_info {
+ int dummy;
+};
+LDAP_END_DECL
+
+#include "proto-lload.h"
+#endif /* _LLOAD_H_ */