]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
v4: Add functionality to rate limit LDAP sync store Cookie (#4784)
authorNick Porter <nick@portercomputing.co.uk>
Mon, 31 Oct 2022 18:11:04 +0000 (18:11 +0000)
committerGitHub <noreply@github.com>
Mon, 31 Oct 2022 18:11:04 +0000 (12:11 -0600)
* Simplify arguments for ldap_sync_cookie_store

* Add config items for cookie store interval

* Add module instance to sync_state_t

And pass it to sync initialisation functions to populate the
sync_state_t

* Define structure for tracking sync packets

* Add list to sync_state for tracking pending packets

* Create sync packet ctx to track progress of each change

* Add counters for batching cookie storing

* Store received cookies in list of pending packets

rather than sending immediately

* Use counters to send cookies based on number of processed changes

* Use timer to send cookies on a periodic basis

raddb/sites-available/ldap_sync
src/listen/ldap_sync/active_directory.c
src/listen/ldap_sync/active_directory.h
src/listen/ldap_sync/persistent_search.c
src/listen/ldap_sync/persistent_search.h
src/listen/ldap_sync/proto_ldap_sync.c
src/listen/ldap_sync/proto_ldap_sync.h
src/listen/ldap_sync/proto_ldap_sync_ldap.c
src/listen/ldap_sync/proto_ldap_sync_ldap.h
src/listen/ldap_sync/rfc4533.c
src/listen/ldap_sync/rfc4533.h

index 6b5f17f22104f95b4005de79cb59f2768b94fa54..8e8b874e9407b4653e94409580659266f22ef9fd 100644 (file)
@@ -151,6 +151,29 @@ server ldap_sync {
                        }
                }
 
+               #
+               #  When directories provide cookies to track progress through
+               #  the list of changes, these can be provided on every update,
+               #  which can be an excessive rate.
+               #
+               #  FreeRADIUS keeps track of pending change and will only call
+               #  store Cookie once the preceding changes have been processed.
+               #
+               #  These options rate limit how often cookies will be stored.
+               #  Provided all preceding changes have been processed, cookie Store
+               #  will be called on a timed interval or after a number of changes
+               #  have been completed, whichever occurs first.
+               #
+               #  How often to store cookies.
+               #
+               cookie_interval = 10
+
+               #
+               #  Number of completed changes which will prompt the storing
+               #  of a cookie
+               #
+               cookie_changes = 100
+
                #
                #  Persistent searches.
                #
index 3c5c7fd02bf41d7046d132d61a3785c8bb1d3cb9..2a303d5ce4663e60aa14f1ddf29a30296e449aab 100644 (file)
@@ -43,13 +43,14 @@ static int active_directory_sync_attr_add(char const *attr, void *uctx)
  * Neither of these controls take values.
  *
  * @param[in] conn             Connection to issue the search request on.
- * @param[in] config           containing callbacks and search parameters.
+ * @param[in] sync_no          number of the sync in the array of configs.
+ * @param[in] inst             instance of ldap_sync this query relates to.
  * @param[in] cookie           unused for Active Directory
  * @return
  *     - 0 on success
  *     - -1 on failure
  */
-int active_directory_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, sync_config_t const *config,
+int active_directory_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst,
                                     UNUSED uint8_t const *cookie)
 {
        static char const       *notify_oid = LDAP_SERVER_NOTIFICATION_OID;
@@ -60,6 +61,7 @@ int active_directory_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no,
        sync_state_t            *sync;
        fr_rb_tree_t            *tree;
        char const              *filter = NULL;
+       sync_config_t const     *config = inst->sync_config[sync_no];
 
        fr_assert(conn);
        fr_assert(config);
@@ -75,7 +77,7 @@ int active_directory_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no,
                tree = talloc_get_type_abort(conn->uctx, fr_rb_tree_t);
        }
 
-       sync = sync_state_alloc(tree, conn, sync_no, config);
+       sync = sync_state_alloc(tree, conn, inst, sync_no, config);
 
        /*
         *      Notification control - marks this as a persistent search.
index 50fb36e6c656d71238f77e8fb977ef82ffbb4e96..c96a8971db09dbbaca8d8d2202180da4480e819a 100644 (file)
@@ -26,7 +26,7 @@
 #include <freeradius-devel/ldap/base.h>
 #include "proto_ldap_sync_ldap.h"
 
-int active_directory_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, sync_config_t const *config,
-                                    UNUSED uint8_t const *cookie);
+int active_directory_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no,
+                                    proto_ldap_sync_t const *inst, UNUSED uint8_t const *cookie);
 
 int active_directory_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, UNUSED LDAPControl **ctrls);
index f18ece800502cb39795d3df561e66ae07d1e3464..ac2964c0a15a9b5c26673dadf2bb0dfe3d496f74 100644 (file)
  * an ldap_abandon message to the server to tell it to cancel the search.
  *
  * @param[in] conn             Connection to issue the search request on.
- * @param[in] config           containing callbacks and search parameters.
+ * @param[in] sync_no          number of the sync in the array of configs.
+ * @param[in] inst             instance of ldap_sync this query relates to.
  * @param[in] cookie           not applicable to persistent search LDAP servers.
  */
-int persistent_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, sync_config_t const *config, UNUSED uint8_t const *cookie)
+int persistent_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst, UNUSED uint8_t const *cookie)
 {
        static char const       *notify_oid = LDAP_CONTROL_PERSIST_REQUEST;
        LDAPControl             ctrl = {0}, *ctrls[2] = { &ctrl, NULL };
@@ -57,6 +58,7 @@ int persistent_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, sync_
        int                     ret;
        sync_state_t            *sync;
        fr_rb_tree_t            *tree;
+       sync_config_t           *config = inst->sync_config[sync_no];
 
        fr_assert(conn);
        fr_assert(config);
@@ -81,7 +83,7 @@ int persistent_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, sync_
                return -1;
        }
 
-       sync = sync_state_alloc(tree, conn, sync_no, config);
+       sync = sync_state_alloc(tree, conn, inst, sync_no, config);
 
        memcpy(&ctrl.ldctl_oid, &notify_oid, sizeof(ctrl.ldctl_oid));
 
@@ -127,6 +129,13 @@ int persistent_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, sync_
 
        DEBUG3("Sync created with msgid %i", sync->msgid);
 
+       /*
+        *      Register event to store cookies at a regular interval
+        *      Whilst persistent search LDAP servers don't provide cookies as such
+        *      we treat change numbers, if provided, as cookies.
+        */
+       fr_event_timer_in(sync, conn->conn->el, &sync->cookie_ev, inst->cookie_interval, ldap_sync_cookie_event, sync);
+
        return 0;
 }
 
@@ -255,7 +264,7 @@ int persistent_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, LDAPContr
                 */
                if (sync->cookie) talloc_free(sync->cookie);
                sync->cookie = (uint8_t *)talloc_asprintf(sync, "%d", change_no);
-               if (ldap_sync_cookie_store(sync, sync->cookie, false) < 0) goto error;
+               if (ldap_sync_cookie_store(sync, false) < 0) goto error;
        }
 
        if (ber_scanf(ber, "}") == LBER_ERROR) {
index affb0ab5d55facd342baa3120a591f3592313b8e..711eb6776bb607ed525928041c5082bce70f7373 100644 (file)
@@ -26,7 +26,7 @@
 #include <freeradius-devel/ldap/base.h>
 #include "proto_ldap_sync_ldap.h"
 
-int persistent_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, sync_config_t const *config,
+int persistent_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst,
                               UNUSED uint8_t const *cookie);
 
 int persistent_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls);
index 4ac2f145f50b69d1564f8aeb0d38c3848e0694e4..17d0b355e7c9c5043a0876a0f33edb83f7f8ea7d 100644 (file)
@@ -53,6 +53,8 @@ static CONF_PARSER const proto_ldap_sync_config[] = {
 
        { FR_CONF_OFFSET("max_packet_size", FR_TYPE_UINT32, proto_ldap_sync_t, max_packet_size) },
        { FR_CONF_OFFSET("num_messages", FR_TYPE_UINT32, proto_ldap_sync_t, num_messages) },
+       { FR_CONF_OFFSET("cookie_interval", FR_TYPE_TIME_DELTA, proto_ldap_sync_t, cookie_interval), .dflt = "10" },
+       { FR_CONF_OFFSET("cookie_changes", FR_TYPE_UINT32, proto_ldap_sync_t, cookie_changes), .dflt = "100" },
 
        /*
         *      Areas of the DIT to listen on
index 8fe3978b0f5cc28c826d07b702a55174c160bcf3..35b2b7a8bfb6a7f438325ebe9037ce7a1e58498e 100644 (file)
@@ -54,6 +54,10 @@ typedef struct {
        uint32_t                num_messages;                   //!< for message ring buffer
        uint32_t                priority;                       //!< for packet processing.
 
+       fr_time_delta_t         cookie_interval;                //!< Interval between storing cookies.
+       uint32_t                cookie_changes;                 //!< Number of LDAP changes to process between
+                                                               //!< each cookie store operation.
+
        fr_schedule_t           *sc;
 
        fr_listen_t             *listen;                        //!< The listener structure which describes
@@ -80,13 +84,14 @@ typedef struct sync_state_s sync_state_t;
  * controls for type of directory in use.
  *
  * @param[in] conn             to initialise the sync on
- * @param[in] config           for the sync
+ * @param[in] sync_no          number of the sync in the array of configs.
+ * @param[in] inst             instance of ldap_sync this query relates to
  * @param[in] cookie           to send with the query (RFC 4533 only)
  * @return
  *     - 0 on success.
  *     - -1 on error.
  */
- typedef int (*sync_init_t)(fr_ldap_connection_t *conn, size_t sync_no, sync_config_t const *config,
+ typedef int (*sync_init_t)(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst,
                            uint8_t const *cookie);
 
 /** Received an LDAP message related to a sync
index 6706d756449af08f36e36e0cd8e65c7c45cb73fe..c4693c49b8234e568477943810a91437ef40df4c 100644 (file)
@@ -158,16 +158,20 @@ static int sync_state_free(sync_state_t *sync)
  * @param[in] config   for the sync.
  * @return new sync state.
  */
-sync_state_t *sync_state_alloc(TALLOC_CTX *ctx, fr_ldap_connection_t *conn, size_t sync_no, sync_config_t const *config)
+sync_state_t *sync_state_alloc(TALLOC_CTX *ctx, fr_ldap_connection_t *conn, proto_ldap_sync_t const *inst,
+                              size_t sync_no, sync_config_t const *config)
 {
        sync_state_t    *sync;
 
        MEM(sync = talloc_zero(ctx, sync_state_t));
        sync->conn = conn;
+       sync->inst = inst;
        sync->config = config;
        sync->sync_no = sync_no;
        sync->phase = SYNC_PHASE_INIT;
 
+       fr_dlist_talloc_init(&sync->pending, sync_packet_ctx_t, entry);
+
        /*
         *      If the connection is freed, all the sync state is also freed
         */
@@ -176,27 +180,86 @@ sync_state_t *sync_state_alloc(TALLOC_CTX *ctx, fr_ldap_connection_t *conn, size
        return sync;
 }
 
-/** Enque a new cookie store packet
+/** Add a new cookie packet ctx to the pending list
  *
- * Create a new internal packet containing the cookie we received from the LDAP server.
- * This allows the administrator to store the cookie and provide it on a future call to
- * load Cookie.
+ * Does not actually send the packet.
  *
  * @param[in] sync     the cookie was received for.
- * @param[in] cookie   received from the LDAP server. Can be NULL to indicate the stored cookie should be cleared.
  * @param[in] refresh  the sync after storing this cookie.
  * @return
  *     - 0 on success.
  *     - -1 on failure
  */
-int ldap_sync_cookie_store(sync_state_t *sync, uint8_t const *cookie, bool refresh)
+int ldap_sync_cookie_store(sync_state_t *sync, bool refresh)
+{
+       sync_packet_ctx_t               *sync_packet_ctx = NULL;
+       uint8_t                         *cookie = sync->cookie;
+
+       MEM(sync_packet_ctx = talloc_zero(sync, sync_packet_ctx_t));
+       sync_packet_ctx->sync = sync;
+
+       sync_packet_ctx->type = SYNC_PACKET_TYPE_COOKIE;
+       if (cookie) sync_packet_ctx->cookie = talloc_memdup(sync_packet_ctx, cookie, talloc_array_length(cookie));
+       sync_packet_ctx->refresh = refresh;
+
+       if (fr_dlist_insert_tail(&sync->pending, sync_packet_ctx) < 0) {
+               talloc_free(sync_packet_ctx);
+               return -1;
+       }
+       sync->pending_cookies++;
+
+       return 0;
+}
+
+/** Event to handle storing of cookies on a timed basis
+ *
+ * Looks at the head of the list of pending sync packets for a cookie.
+ * A cookie at the head says that all the previous changes have been
+ * completed, so the cookie can be sent.
+ */
+void ldap_sync_cookie_event(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
+{
+       sync_state_t            *sync = talloc_get_type_abort(uctx, sync_state_t);
+       sync_packet_ctx_t       *sync_packet_ctx;
+
+       if (sync->pending_cookies == 0) goto finish;
+
+       /*
+        *      Check the head entry in the list - is it a pending cookie
+        */
+       sync_packet_ctx = fr_dlist_head(&sync->pending);
+       if ((sync_packet_ctx->type != SYNC_PACKET_TYPE_COOKIE) ||
+           (sync_packet_ctx->status != SYNC_PACKET_PENDING)) goto finish;
+
+       ldap_sync_cookie_send(sync_packet_ctx);
+
+finish:
+       fr_event_timer_in(sync, el, &sync->cookie_ev, sync->inst->cookie_interval, ldap_sync_cookie_event, sync);
+}
+
+/** Enque a new cookie store packet
+ *
+ * Create a new internal packet containing the cookie we received from the LDAP server.
+ * This allows the administrator to store the cookie and provide it on a future call to
+ * load Cookie.
+ *
+ * @param[in] sync_packet_ctx  packet context containing the cookie to store.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+*/
+int ldap_sync_cookie_send(sync_packet_ctx_t *sync_packet_ctx)
 {
+       sync_state_t                    *sync = sync_packet_ctx->sync;
        proto_ldap_sync_ldap_thread_t   *thread = talloc_get_type_abort(sync->config->user_ctx, proto_ldap_sync_ldap_thread_t);
        fr_dbuff_t                      *dbuff;
-       sync_refresh_packet_t           *refresh_packet = NULL;
        fr_pair_list_t                  pairs;
        fr_pair_t                       *vp;
        TALLOC_CTX                      *local = NULL;
+       uint8_t                         *cookie = sync->cookie;
+
+       if (sync_packet_ctx->status != SYNC_PACKET_PENDING) return 0;
+       sync_packet_ctx->status = SYNC_PACKET_PREPARING;
 
        FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, 4096);
 
@@ -226,23 +289,12 @@ int ldap_sync_cookie_store(sync_state_t *sync, uint8_t const *cookie, bool refre
                if (!vp) goto error;
        }
 
-       /*
-        *      The LDAP server has indicated that the sync needs a refresh.
-        *      Create a tracking structure to trigger the refresh once the cookie is stored.
-        */
-       if (refresh) {
-               MEM(refresh_packet = talloc_zero(thread, sync_refresh_packet_t));
-               if (cookie) {
-                       refresh_packet->refresh_cookie = talloc_memdup(refresh_packet, cookie, talloc_array_length(cookie));
-               }
-               refresh_packet->sync = sync;
-       }
-
        if (fr_internal_encode_list(dbuff, &pairs, NULL) < 0) goto error;
        talloc_free(local);
 
        fr_network_listen_send_packet(thread->nr, thread->li, thread->li, fr_dbuff_buff(dbuff),
-                                     fr_dbuff_used(dbuff), fr_time(), refresh_packet);
+                                     fr_dbuff_used(dbuff), fr_time(), sync_packet_ctx);
+       sync_packet_ctx->status = SYNC_PACKET_PROCESSING;
 
        return 0;
 }
@@ -276,6 +328,7 @@ int ldap_sync_entry_send(sync_state_t *sync, uint8_t const uuid[SYNC_UUID_LENGTH
        fr_pair_list_t                  pairs;
        fr_pair_t                       *vp;
        TALLOC_CTX                      *local = NULL;
+       sync_packet_ctx_t               *sync_packet_ctx = NULL;
 
        FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, 4096);
 
@@ -285,6 +338,7 @@ int ldap_sync_entry_send(sync_state_t *sync, uint8_t const uuid[SYNC_UUID_LENGTH
        error:
                if (msg) ldap_msgfree(msg);
                talloc_free(local);
+               if (sync_packet_ctx) talloc_free(sync_packet_ctx);
                return -1;
        };
 
@@ -360,8 +414,14 @@ int ldap_sync_entry_send(sync_state_t *sync, uint8_t const uuid[SYNC_UUID_LENGTH
 
        if (fr_internal_encode_list(dbuff, &pairs, NULL) < 0) goto error;
 
+       MEM(sync_packet_ctx = talloc_zero(sync, sync_packet_ctx_t));
+       sync_packet_ctx->sync = sync;
+
+       if (fr_dlist_insert_tail(&sync->pending, sync_packet_ctx) < 0) goto error;
+
        fr_network_listen_send_packet(thread->nr, thread->li, thread->li,
-                                     fr_dbuff_buff(dbuff), fr_dbuff_used(dbuff), fr_time(), NULL);
+                                     fr_dbuff_buff(dbuff), fr_dbuff_used(dbuff), fr_time(), sync_packet_ctx);
+       sync_packet_ctx->status = SYNC_PACKET_PROCESSING;
 
        talloc_free(local);
 
@@ -610,10 +670,13 @@ static ssize_t proto_ldap_child_mod_write(fr_listen_t *li, void *packet_ctx, UNU
        fr_pair_t                       *vp = NULL;
        ssize_t                         ret;
        TALLOC_CTX                      *local;
+       sync_packet_ctx_t               *sync_packet_ctx = NULL;
 
        local = talloc_new(NULL);
        fr_dbuff_init(&dbuff, buffer, buffer_len);
 
+       if (packet_ctx) sync_packet_ctx = talloc_get_type_abort(packet_ctx, sync_packet_ctx_t);
+
        /*
         *      Extract returned attributes into a temporary list
         */
@@ -654,7 +717,7 @@ static ssize_t proto_ldap_child_mod_write(fr_listen_t *li, void *packet_ctx, UNU
                vp = fr_pair_find_by_da_nested(&tmp, NULL, attr_ldap_sync_cookie);
                if (vp) cookie = talloc_memdup(inst, vp->vp_octets, vp->vp_length);
 
-               inst->parent->sync_config[packet_id]->init(thread->conn->h, packet_id, inst->parent->sync_config[packet_id], cookie);
+               inst->parent->sync_config[packet_id]->init(thread->conn->h, packet_id, inst->parent, cookie);
        }
                break;
 
@@ -663,26 +726,17 @@ static ssize_t proto_ldap_child_mod_write(fr_listen_t *li, void *packet_ctx, UNU
 
        case FR_LDAP_SYNC_CODE_COOKIE_STORE_RESPONSE:
        {
-               sync_refresh_packet_t   *refresh_packet;
                sync_config_t const     *sync_config;
 
-               if (!packet_ctx) break;
-
-               /*
-                *      If there is a packet_ctx, it will be the tracking structure
-                *      indicating that we need to refresh the sync.
-                */
-               refresh_packet = talloc_get_type_abort(packet_ctx, sync_refresh_packet_t);
+               if (!sync_packet_ctx || !sync_packet_ctx->refresh) break;
 
                /*
                 *      Abandon the old sync and start a new one with the relevant cookie.
                 */
-               sync_config = refresh_packet->sync->config;
+               sync_config = sync_packet_ctx->sync->config;
                DEBUG3("Restarting sync with base %s", sync_config->base_dn);
-               talloc_free(refresh_packet->sync);
-               inst->parent->sync_config[packet_id]->init(thread->conn->h, packet_id, sync_config, refresh_packet->refresh_cookie);
-
-               talloc_free(refresh_packet);
+               talloc_free(sync_packet_ctx->sync);
+               inst->parent->sync_config[packet_id]->init(thread->conn->h, packet_id, inst->parent, sync_packet_ctx->cookie);
        }
                break;
 
@@ -691,6 +745,54 @@ static ssize_t proto_ldap_child_mod_write(fr_listen_t *li, void *packet_ctx, UNU
                break;
        }
 
+       if (sync_packet_ctx) {
+               sync_state_t            *sync = sync_packet_ctx->sync;
+               sync_packet_ctx_t       *pc;
+               proto_ldap_sync_t       *ldap_sync = inst->parent;
+
+               sync_packet_ctx->status = SYNC_PACKET_COMPLETE;
+
+               /*
+                *      A cookie has been stored, reset the counter of changes
+                */
+               if (sync_packet_ctx->type == SYNC_PACKET_TYPE_COOKIE) sync->changes_since_cookie = 0;
+
+               /*
+                *      Pop any processed updates from the head of the list
+                */
+               while ((pc = fr_dlist_head(&sync->pending))) {
+                       /*
+                        *      If the head entry in the list is a pending cookie but we have
+                        *      not processed enough entries and there are more pending
+                        *      cookies, mark this one as processed.
+                        */
+                       if ((pc->type == SYNC_PACKET_TYPE_COOKIE) && (pc->status == SYNC_PACKET_PENDING) &&
+                           (sync->changes_since_cookie < ldap_sync->cookie_changes) &&
+                            (sync->pending_cookies > 1)) pc->status = SYNC_PACKET_COMPLETE;
+
+                       if (pc->status != SYNC_PACKET_COMPLETE) break;
+
+                       /*
+                        *      Update counters depending on entry type
+                        */
+                       if (pc->type == SYNC_PACKET_TYPE_COOKIE) {
+                               sync->pending_cookies--;
+                       } else {
+                               sync->changes_since_cookie++;
+                       }
+                       pc = fr_dlist_pop_head(&sync->pending);
+                       talloc_free(pc);
+               }
+
+               /*
+                *      If the head of the list is a cookie which has not yet
+                *      been processed and sufficient changes have been recorded
+                *      send the cookie.
+                */
+               if (pc && (pc->type == SYNC_PACKET_TYPE_COOKIE) && (pc->status == SYNC_PACKET_PENDING) &&
+                   (sync->changes_since_cookie >= ldap_sync->cookie_changes)) ldap_sync_cookie_send(pc);
+       }
+
        fr_pair_list_free(&tmp);
        talloc_free(local);
 
index 5a6ec6b9e6326f1f001ba64b64ae860b852b4492..7cdd3931419ab544bc1f2a5e4f757e5701c6f8e3 100644 (file)
@@ -59,6 +59,16 @@ struct sync_state_s {
                                                        //!< before passing packets to the worker.
                                                        //!< Predominantly to overcome Active Directory's lack
                                                        //!< of filtering in persistent searches.
+
+       proto_ldap_sync_t const         *inst;          //!< Module instance for this sync.
+
+       fr_dlist_head_t                 pending;        //!< List of pending changes in progress.
+
+       uint32_t                        pending_cookies;        //!< How many cookies are in the pending heap
+       uint32_t                        changes_since_cookie;   //!< How many changes have been added since
+                                                               //!< the last cookie was stored.
+
+       fr_event_timer_t const          *cookie_ev;     //!< Timer event for sending cookies.
 };
 
 typedef struct sync_state_s sync_state_t;
@@ -104,24 +114,46 @@ typedef struct {
        fr_connection_t                 *conn;                  //!< Our connection to the LDAP directory.
 } proto_ldap_sync_ldap_thread_t;
 
-/** Tracking structure for connections requiring refresh
+typedef enum {
+       SYNC_PACKET_PENDING = 0,                                //!< Packet not yet sent.
+       SYNC_PACKET_PREPARING,                                  //!< Packet being prepared.
+       SYNC_PACKET_PROCESSING,                                 //!< Packet sent to worker.
+       SYNC_PACKET_COMPLETE,                                   //!< Packet response received from worker.
+} sync_packet_status_t;
+
+typedef enum {
+       SYNC_PACKET_TYPE_CHANGE = 0,                            //!< Packet is an entry change.
+       SYNC_PACKET_TYPE_COOKIE
+} sync_packet_type_t;
+
+/** Tracking structure for ldap sync packets
  */
-struct sync_refresh_packet_s {
-       sync_state_t                    *sync;                  //!< Sync requiring refresh
+struct sync_packet_ctx_s {
+       sync_packet_type_t              type;                   //!< Type of packet.
+       sync_packet_status_t            status;                 //!< Status of this packet.
+       sync_state_t                    *sync;                  //!< Sync packet relates to.
+
+       uint8_t                         *cookie;                //!< Cookie to store - can be NULL.
+       bool                            refresh;                //!< Does the sync require a refresh.
 
-       uint8_t                         *refresh_cookie;        //!< Cookie provided by the server for the refresh.
+       fr_dlist_t                      entry;                  //!< Entry in list of pending packets.
 };
 
-typedef struct sync_refresh_packet_s sync_refresh_packet_t;
+typedef struct sync_packet_ctx_s sync_packet_ctx_t;
 
 extern fr_table_num_sorted_t const sync_op_table[];
 extern size_t sync_op_table_len;
 
 int8_t sync_state_cmp(void const *one, void const *two);
 
-sync_state_t *sync_state_alloc(TALLOC_CTX *ctx, fr_ldap_connection_t *conn, size_t sync_no, sync_config_t const *config);
+sync_state_t *sync_state_alloc(TALLOC_CTX *ctx, fr_ldap_connection_t *conn, proto_ldap_sync_t const *inst,
+                              size_t sync_no, sync_config_t const *config);
+
+int ldap_sync_cookie_store(sync_state_t *sync, bool refresh);
+
+void ldap_sync_cookie_event(fr_event_list_t *el, fr_time_t now, void *uctx);
 
-int ldap_sync_cookie_store(sync_state_t *sync, uint8_t const *cookie, bool refresh);
+int ldap_sync_cookie_send(sync_packet_ctx_t *sync_packet_ctx);
 
 int ldap_sync_entry_send(sync_state_t *sync, uint8_t const uuid[SYNC_UUID_LENGTH], struct berval *orig_dn,
                        LDAPMessage *msg, sync_op_t op);
index 091c094a5475dfeca7023352a94a84bb7495f67f..ae2a3cdae5d0bc3fa8c6f609e2da2aa110fcfc36 100644 (file)
@@ -71,7 +71,7 @@ static size_t const sync_phase_table_len = NUM_ELEMENTS(sync_phase_table);
  *
  * The Sync Request Control is only applicable to the SearchRequest Message.
  */
-int rfc4533_sync_init(fr_ldap_connection_t *conn, size_t sync_no, sync_config_t const *config, uint8_t const *cookie)
+int rfc4533_sync_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst, uint8_t const *cookie)
 {
        LDAPControl             ctrl = {0}, *ctrls[2] = { &ctrl, NULL };
        BerElement              *ber = NULL;
@@ -79,6 +79,7 @@ int rfc4533_sync_init(fr_ldap_connection_t *conn, size_t sync_no, sync_config_t
        int                     ret;
        fr_rb_tree_t            *tree;
        sync_state_t            *sync;
+       sync_config_t const     *config = inst->sync_config[sync_no];
 
        fr_assert(conn);
        fr_assert(config);
@@ -96,7 +97,7 @@ int rfc4533_sync_init(fr_ldap_connection_t *conn, size_t sync_no, sync_config_t
                return -1;
        }
 
-       sync = sync_state_alloc(tree, conn, sync_no, config);
+       sync = sync_state_alloc(tree, conn, inst, sync_no, config);
 
        /*
         *      Might not necessarily have a cookie
@@ -144,6 +145,11 @@ int rfc4533_sync_init(fr_ldap_connection_t *conn, size_t sync_no, sync_config_t
 
        DEBUG3("Sync created with msgid %i", sync->msgid);
 
+       /*
+        *      Register event to store cookies at a regular interval
+        */
+       fr_event_timer_in(sync, conn->conn->el, &sync->cookie_ev, inst->cookie_interval, ldap_sync_cookie_event, sync);
+
        return 0;
 }
 
@@ -394,7 +400,7 @@ int rfc4533_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, LDAPControl
         *      We have a new cookie - store it
         */
        if ((ret == 0) && new_cookie) {
-               ret = ldap_sync_cookie_store(sync, sync->cookie, false);
+               ret = ldap_sync_cookie_store(sync, false);
        }
        ber_free(ber, 1);
 
@@ -674,7 +680,7 @@ int rfc4533_sync_intermediate(sync_state_t *sync, LDAPMessage *msg, UNUSED LDAPC
        }
 
        if (new_cookie) {
-               ret = ldap_sync_cookie_store(sync, sync->cookie, false);
+               ret = ldap_sync_cookie_store(sync, false);
        }
 
        if (ber) ber_free(ber, 1);
@@ -762,5 +768,5 @@ int rfc4533_sync_refresh_required(sync_state_t *sync, LDAPMessage *msg, LDAPCont
                new_cookie = true;
        }
 
-       return ldap_sync_cookie_store(sync, sync->cookie, true);
+       return ldap_sync_cookie_store(sync, true);
 }
index a9c41dc21fca31b776d69e7871d5bef632334826..a5dae4066ce0da7c1eb23f783eaef905c6df83ea 100644 (file)
@@ -26,7 +26,8 @@
 #include <freeradius-devel/ldap/base.h>
 #include "proto_ldap_sync_ldap.h"
 
-int rfc4533_sync_init(fr_ldap_connection_t *conn, size_t sync_no, sync_config_t const *config, uint8_t const *cookie);
+int rfc4533_sync_init(fr_ldap_connection_t *conn, size_t sync_no,
+                     proto_ldap_sync_t const *inst, uint8_t const *cookie);
 
 int rfc4533_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls);