[PATCH 2 of 6] SSL: object caching

Mini Hawthorne mini at nginx.com
Tue Jul 23 19:30:25 UTC 2024


# HG changeset patch
# User Mini Hawthorne <mini at f5.com>
# Date 1721762842 0
#      Tue Jul 23 19:27:22 2024 +0000
# Node ID 8eee61e223bb7cb7475e50b866fd6b9a83fa5fa0
# Parent  59ac183dfee8e9641563e043eb19480d91dd7cc0
SSL: object caching.

Added ngx_openssl_cache_module, which indexes a type-aware object cache.
It maps an id to a unique instance, and provides references to it, which
are dropped when the cycle's pool is destroyed.  Also, for those objects
that can be cached, valid references may be pulled from cycle->old_cycle.

The cache will be used in subsequent patches.

diff --git a/auto/modules b/auto/modules
--- a/auto/modules
+++ b/auto/modules
@@ -1307,10 +1307,11 @@ fi
 
 if [ $USE_OPENSSL = YES ]; then
     ngx_module_type=CORE
-    ngx_module_name=ngx_openssl_module
+    ngx_module_name="ngx_openssl_module ngx_openssl_cache_module"
     ngx_module_incs=
     ngx_module_deps=src/event/ngx_event_openssl.h
     ngx_module_srcs="src/event/ngx_event_openssl.c
+                     src/event/ngx_event_openssl_cache.c
                      src/event/ngx_event_openssl_stapling.c"
     ngx_module_libs=
     ngx_module_link=YES
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -83,7 +83,8 @@
 #endif
 
 
-typedef struct ngx_ssl_ocsp_s  ngx_ssl_ocsp_t;
+typedef struct ngx_ssl_ocsp_s        ngx_ssl_ocsp_t;
+typedef struct ngx_ssl_cache_type_s  ngx_ssl_cache_type_t;
 
 
 typedef struct {
@@ -233,6 +234,9 @@ ngx_int_t ngx_ssl_ocsp_get_status(ngx_co
 void ngx_ssl_ocsp_cleanup(ngx_connection_t *c);
 ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data);
 
+void *ngx_ssl_cache_fetch(ngx_cycle_t *cycle, ngx_pool_t *pool,
+    ngx_ssl_cache_type_t *type, char **err, ngx_str_t *id, void *data);
+
 ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);
 ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf,
     ngx_array_t *passwords);
diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_openssl_cache.c
@@ -0,0 +1,277 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+typedef struct {
+    ngx_rbtree_node_t           node;
+    ngx_str_t                   id;
+
+    ngx_ssl_cache_type_t       *type;
+    void                       *value;
+} ngx_ssl_cache_node_t;
+
+
+typedef void *(*ngx_ssl_cache_create_pt)(ngx_str_t *id, char **err, void *data);
+typedef void (*ngx_ssl_cache_free_pt)(void *data);
+typedef void *(*ngx_ssl_cache_ref_pt)(char **err, void *data);
+
+
+struct ngx_ssl_cache_type_s {
+    const char                 *name;
+
+    ngx_ssl_cache_create_pt     create;
+    ngx_ssl_cache_free_pt       free;
+    ngx_ssl_cache_ref_pt        ref;
+};
+
+
+typedef struct {
+    ngx_rbtree_t                rbtree;
+    ngx_rbtree_node_t           sentinel;
+} ngx_ssl_cache_t;
+
+
+static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle);
+static void ngx_ssl_cache_cleanup(void *data);
+static void ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+
+static ngx_ssl_cache_node_t *ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache,
+    ngx_ssl_cache_type_t *type, ngx_str_t *id, uint32_t hash);
+
+
+static ngx_core_module_t  ngx_openssl_cache_module_ctx = {
+    ngx_string("openssl_cache"),
+    ngx_openssl_cache_create_conf,
+    NULL
+};
+
+
+ngx_module_t  ngx_openssl_cache_module = {
+    NGX_MODULE_V1,
+    &ngx_openssl_cache_module_ctx,         /* module context */
+    NULL,                                  /* module directives */
+    NGX_CORE_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_openssl_cache_create_conf(ngx_cycle_t *cycle)
+{
+    ngx_ssl_cache_t     *cache;
+    ngx_pool_cleanup_t  *cln;
+
+    cache = ngx_pcalloc(cycle->pool, sizeof(ngx_ssl_cache_t));
+    if (cache == NULL) {
+        return NULL;
+    }
+
+    cln = ngx_pool_cleanup_add(cycle->pool, 0);
+    if (cln == NULL) {
+        return NULL;
+    }
+
+    cln->handler = ngx_ssl_cache_cleanup;
+    cln->data = cache;
+
+    ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
+                    ngx_ssl_cache_node_insert);
+
+    return cache;
+}
+
+
+static void
+ngx_ssl_cache_cleanup(void *data)
+{
+    ngx_ssl_cache_t  *cache = data;
+
+    ngx_rbtree_t          *tree;
+    ngx_rbtree_node_t     *node;
+    ngx_ssl_cache_node_t  *cn;
+
+    tree = &cache->rbtree;
+
+    if (tree->root == tree->sentinel) {
+        return;
+    }
+
+    for (node = ngx_rbtree_min(tree->root, tree->sentinel);
+         node;
+         node = ngx_rbtree_next(tree, node))
+    {
+        cn = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node);
+
+        if (cn->type != NULL && cn->value != NULL) {
+            cn->type->free(cn->value);
+        }
+    }
+}
+
+
+static void
+ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_rbtree_node_t     **p;
+    ngx_ssl_cache_node_t   *n, *t;
+
+    for ( ;; ) {
+
+        n = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node);
+        t = ngx_rbtree_data(temp, ngx_ssl_cache_node_t, node);
+
+        if (node->key != temp->key) {
+
+            p = (node->key < temp->key) ? &temp->left : &temp->right;
+
+        } else if (n->type != t->type) {
+
+            p = (n->type < t->type) ? &temp->left : &temp->right;
+
+        } else {
+
+            p = (ngx_memn2cmp(n->id.data, t->id.data, n->id.len, t->id.len)
+                 < 0) ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}
+
+
+void *
+ngx_ssl_cache_fetch(ngx_cycle_t *cycle, ngx_pool_t *pool,
+    ngx_ssl_cache_type_t *type, char **err, ngx_str_t *id, void *data)
+{
+    void                  *value;
+    uint32_t               hash;
+    ngx_ssl_cache_t       *cache;
+    ngx_ssl_cache_node_t  *cn;
+
+    value = NULL;
+
+    hash = ngx_murmur_hash2(id->data, id->len);
+
+    cache = (ngx_ssl_cache_t *) ngx_get_conf(cycle->conf_ctx,
+                                             ngx_openssl_cache_module);
+
+    if (ngx_process == NGX_PROCESS_WORKER
+        || ngx_process == NGX_PROCESS_SINGLE)
+    {
+        return type->create(id, err, data);
+    }
+
+    cn = ngx_ssl_cache_lookup(cache, type, id, hash);
+
+    if (cn == NULL) {
+        cn = ngx_palloc(pool, sizeof(ngx_ssl_cache_node_t) + id->len + 1);
+        if (cn == NULL) {
+            return NULL;
+        }
+
+        cn->node.key = hash;
+        cn->id.data = (u_char *)(cn + 1);
+        cn->id.len = id->len;
+        cn->type = type;
+        cn->value = NULL;
+
+        ngx_cpystrn(cn->id.data, id->data, id->len + 1);
+
+        ngx_rbtree_insert(&cache->rbtree, &cn->node);
+    }
+
+    /* try to use a reference from the cache */
+
+    if (cn->value != NULL) {
+        value = type->ref(err, cn->value);
+    }
+
+    if (value == NULL) {
+        value = type->create(id, err, data);
+    }
+
+    if (value != NULL && cn->value == NULL) {
+        /* we have a value and the node needs one; try to reference it */
+        cn->value = type->ref(err, value);
+    }
+
+    return value;
+}
+
+
+static ngx_ssl_cache_node_t *
+ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type,
+    ngx_str_t *id, uint32_t hash)
+{
+    ngx_int_t              rc;
+    ngx_rbtree_node_t     *node, *sentinel;
+    ngx_ssl_cache_node_t  *cn;
+
+    node = cache->rbtree.root;
+    sentinel = cache->rbtree.sentinel;
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        /* hash == node->key */
+
+        cn = (ngx_ssl_cache_node_t *) node;
+
+        if ((ngx_uint_t) type < (ngx_uint_t) cn->type) {
+            node = node->left;
+            continue;
+        }
+
+        if ((ngx_uint_t) type > (ngx_uint_t) cn->type) {
+            node = node->right;
+            continue;
+        }
+
+        /* type == cn->type */
+
+        rc = ngx_memn2cmp(id->data, cn->id.data, id->len, cn->id.len);
+
+        if (rc == 0) {
+            return cn;
+        }
+
+        node = (rc < 0) ? node->left : node->right;
+    }
+
+    return NULL;
+}


More information about the nginx-devel mailing list