From: Nick Porter Date: Fri, 2 Feb 2024 14:35:29 +0000 (+0000) Subject: Move the rest of sqlippool alloc to use call_env X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dbca0c3c9d2d75968ecdcd51e5c6f663707bdc94;p=thirdparty%2Ffreeradius-server.git Move the rest of sqlippool alloc to use call_env --- diff --git a/src/modules/rlm_sqlippool/rlm_sqlippool.c b/src/modules/rlm_sqlippool/rlm_sqlippool.c index 6342cac479d..c73d59d40ed 100644 --- a/src/modules/rlm_sqlippool/rlm_sqlippool.c +++ b/src/modules/rlm_sqlippool/rlm_sqlippool.c @@ -30,6 +30,7 @@ RCSID("$Id$") #include #include #include +#include #include @@ -42,16 +43,6 @@ typedef struct { rlm_sql_t const *sql; - /* Alloc sequence */ - char const *alloc_begin; //!< SQL query to begin. - char const *alloc_existing; //!< SQL query to find existing IP. - char const *alloc_requested; //!< SQL query to find requested IP. - char const *alloc_find; //!< SQL query to find an unused IP. - char const *alloc_update; //!< SQL query to mark an IP as used. - char const *alloc_commit; //!< SQL query to commit. - - char const *pool_check; //!< Query to check for the existence of the pool. - /* Update sequence */ char const *update_free; //!< SQL query to clear offered IPs char const *update_update; //!< SQL query to update an IP entry. @@ -83,26 +74,31 @@ typedef struct { fr_value_box_t commit; //!< SQL query to commit transaction. } ippool_alloc_call_env_t; +/** Current step in IP allocation state machine + */ +typedef enum { + IPPOOL_ALLOC_EXISTING, //!< Expanding the "existing" query + IPPOOL_ALLOC_REQUESTED, //!< Expanding the "requested" query + IPPOOL_ALLOC_FIND, //!< Expanding the "find" query + IPPOOL_ALLOC_POOL_CHECK, //!< Expanding the "pool_check" query + IPPOOL_ALLOC_UPDATE //!< Expanding the "update" query +} ippool_alloc_status_t; + +/** Resume context for IP allocation + */ +typedef struct { + request_t *request; //!< Current request. + ippool_alloc_status_t status; //!< Status of the allocation. + ippool_alloc_call_env_t *env; //!< Call environment for the allocation. + rlm_sql_handle_t *handle; //!< SQL handle being used for queries. + rlm_sql_t const *sql; //!< SQL module instance. + fr_value_box_list_t values; //!< Where to put the expanded queries ready for execution. +} ippool_alloc_ctx_t; + static conf_parser_t module_config[] = { { FR_CONF_OFFSET("sql_module_instance", rlm_sqlippool_t, sql_name), .dflt = "sql" }, - { FR_CONF_OFFSET_FLAGS("alloc_begin", CONF_FLAG_XLAT, rlm_sqlippool_t, alloc_begin), .dflt = "START TRANSACTION" }, - - { FR_CONF_OFFSET_FLAGS("alloc_existing", CONF_FLAG_XLAT, rlm_sqlippool_t, alloc_existing) }, - - { FR_CONF_OFFSET_FLAGS("alloc_requested", CONF_FLAG_XLAT, rlm_sqlippool_t, alloc_requested) }, - - { FR_CONF_OFFSET_FLAGS("alloc_find", CONF_FLAG_XLAT | CONF_FLAG_REQUIRED, rlm_sqlippool_t, alloc_find) }, - - { FR_CONF_OFFSET_FLAGS("alloc_update", CONF_FLAG_XLAT, rlm_sqlippool_t, alloc_update) }, - - { FR_CONF_OFFSET_FLAGS("alloc_commit", CONF_FLAG_XLAT, rlm_sqlippool_t, alloc_commit), .dflt = "COMMIT" }, - - - { FR_CONF_OFFSET_FLAGS("pool_check", CONF_FLAG_XLAT, rlm_sqlippool_t, pool_check) }, - - { FR_CONF_OFFSET_FLAGS("update_free", CONF_FLAG_XLAT, rlm_sqlippool_t, update_free) }, { FR_CONF_OFFSET_FLAGS("update_update", CONF_FLAG_XLAT, rlm_sqlippool_t, update_update) }, @@ -185,8 +181,10 @@ static int sqlippool_command(char const *query, rlm_sql_handle_t **handle, /* * Don't repeat yourself */ -#define DO_PART(_x) if(sqlippool_command(inst->_x, &handle, inst, request) <0) goto error #define DO_AFFECTED(_x, _affected) _affected = sqlippool_command(inst->_x, &handle, inst, request); if (_affected < 0) goto error +#define DO_PART(_x) if(env->_x.type == FR_TYPE_STRING) { \ + if(sqlippool_command(env->_x.vb_strvalue, &handle, sql, request) <0) goto error; \ +} #define RESERVE_CONNECTION(_handle, _pool, _request) _handle = fr_pool_connection_get(_pool, _request); \ if (!_handle) { \ REDEBUG("Failed reserving SQL connection"); \ @@ -285,97 +283,159 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) return 0; } -/* - * Allocate an IP address from the pool. +/** Release SQL pool connections when alloc context is freed. */ -static unlang_action_t CC_HINT(nonnull) mod_alloc(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) +static int sqlippool_alloc_ctx_free(ippool_alloc_ctx_t *to_free) { - rlm_sqlippool_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sqlippool_t); - ippool_alloc_call_env_t *env = talloc_get_type_abort(mctx->env_data, ippool_alloc_call_env_t); - char allocation[FR_MAX_STRING_LEN]; - int allocation_len; - rlm_sql_handle_t *handle; - tmpl_t ip_rhs; - map_t ip_map; - - /* - * If the allocated IP attribute already exists, do nothing - */ - if (env->allocated_address.type) { - RDEBUG2("%s already exists (%pV)", env->allocated_address_attr->name, &env->allocated_address); - - RETURN_MODULE_NOOP; - } - - if (env->pool_name.type == FR_TYPE_NULL) { - RDEBUG2("No %s defined", env->pool_name_tmpl->name); + (void) request_data_get(to_free->request, (void *)sql_escape_uctx_alloc, 0); + if (to_free->handle) fr_pool_connection_release(to_free->sql->pool, to_free->request, to_free->handle); + return 0; +} - RETURN_MODULE_NOOP; - } +#define REPEAT_MOD_ALLOC_RESUME if (unlang_function_repeat_set(request, mod_alloc_resume) < 0) RETURN_MODULE_FAIL - RESERVE_CONNECTION(handle, inst->sql->pool, request); +/** Resume function called after each IP allocation query is expanded + * + * Executes the query and, if appropriate, pushes the next tmpl for expansion + * + * Following the final (successful) query, the destination attribute is populated. + * + * @param p_result Result of IP allocation. + * @param priority Unused. + * @param request Current request. + * @param uctx Current allocation context. + * @return One of the UNLANG_ACTION_* values. + */ +static unlang_action_t mod_alloc_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx) +{ + ippool_alloc_ctx_t *alloc_ctx = talloc_get_type_abort(uctx, ippool_alloc_ctx_t); + ippool_alloc_call_env_t *env = alloc_ctx->env; + int allocation_len = 0; + char allocation[FR_MAX_STRING_LEN]; + rlm_sql_handle_t *handle = alloc_ctx->handle; + rlm_sql_t const *sql = alloc_ctx->sql; + fr_value_box_t *query = fr_value_box_list_pop_head(&alloc_ctx->values); + + switch (alloc_ctx->status) { + case IPPOOL_ALLOC_EXISTING: + if (query) { + allocation_len = sqlippool_query1(allocation, sizeof(allocation), query->vb_strvalue, &handle, + alloc_ctx->sql, request); + talloc_free(query); + if (!handle) { + error: + talloc_free(alloc_ctx); + RETURN_MODULE_FAIL; + } + if (allocation_len > 0) goto make_pair; + } - DO_PART(alloc_begin); + /* + * If there's a requested address and associated query, expand that + */ + if (env->requested && (env->requested_address.type != FR_TYPE_NULL)) { + alloc_ctx->status = IPPOOL_ALLOC_REQUESTED; + REPEAT_MOD_ALLOC_RESUME; + if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->requested, NULL) < 0) goto error; + return UNLANG_ACTION_PUSHED_CHILD; + } + goto expand_find; + + case IPPOOL_ALLOC_REQUESTED: + if (query) { + allocation_len = sqlippool_query1(allocation, sizeof(allocation), query->vb_strvalue, &handle, + alloc_ctx->sql, request); + talloc_free(query); + if (!handle) goto error; + if (allocation_len > 0) goto make_pair; + } - /* - * If there is a query for finding the existing IP - * run that first - */ - if (inst->alloc_existing && *inst->alloc_existing) { - allocation_len = sqlippool_query1(allocation, sizeof(allocation), - inst->alloc_existing, &handle, - inst, request); - if (!handle) RETURN_MODULE_FAIL; - } else { - allocation_len = 0; - } + expand_find: + /* + * Neither "existing" nor "requested" found an address, expand "find" query + */ + alloc_ctx->status = IPPOOL_ALLOC_FIND; + REPEAT_MOD_ALLOC_RESUME; + if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->find, NULL) < 0) goto error; + return UNLANG_ACTION_PUSHED_CHILD; + + case IPPOOL_ALLOC_FIND: + { + tmpl_t ip_rhs; + map_t ip_map; + + allocation_len = sqlippool_query1(allocation, sizeof(allocation), query->vb_strvalue, &handle, + alloc_ctx->sql, request); + talloc_free(query); + if (!handle) goto error; + + if (allocation_len == 0) { + /* + * Nothing found + */ + DO_PART(commit); - /* - * If no existing IP was found and we have a requested IP address - * and a query to find whether it is available then try that - */ - if ((allocation_len == 0) && inst->alloc_requested && *inst->alloc_requested && - (env->requested_address.type != FR_TYPE_NULL)) { - allocation_len = sqlippool_query1(allocation, sizeof(allocation), - inst->alloc_requested, &handle, - inst, request); - if (!handle) RETURN_MODULE_FAIL; - } + /* + * Should we perform pool-check? + */ + if (env->pool_check) { + alloc_ctx->status = IPPOOL_ALLOC_POOL_CHECK; + REPEAT_MOD_ALLOC_RESUME; + if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->pool_check, NULL) < 0) goto error; + return UNLANG_ACTION_PUSHED_CHILD; + } + no_address: + RWDEBUG("IP address could not be allocated"); + RETURN_MODULE_NOOP; + } - /* - * If no existing IP was found (or no query was run), - * run the query to find a free IP - */ - if (allocation_len == 0) { - allocation_len = sqlippool_query1(allocation, sizeof(allocation), - inst->alloc_find, &handle, - inst, request); - if (!handle) RETURN_MODULE_FAIL; - } + make_pair: + /* + * See if we can create the VP from the returned data. If not, + * error out. If so, add it to the list. + */ + ip_map = (map_t) { + .lhs = env->allocated_address_attr, + .op = T_OP_SET, + .rhs = &ip_rhs + }; + + tmpl_init_shallow(&ip_rhs, TMPL_TYPE_DATA, T_BARE_WORD, "", 0, NULL); + fr_value_box_bstrndup_shallow(&ip_map.rhs->data.literal, NULL, allocation, allocation_len, false); + if (map_to_request(request, &ip_map, map_to_vp, NULL) < 0) { + DO_PART(commit); + + REDEBUG("Invalid IP address [%s] returned from database query.", allocation); + goto error; + } - /* - * Nothing found... - */ - if (allocation_len == 0) { - DO_PART(alloc_commit); + RDEBUG2("Allocated IP %s", allocation); /* - *Should we perform pool-check ? + * If we have an update query expand it */ - if (inst->pool_check && *inst->pool_check) { + if (env->update) { + alloc_ctx->status = IPPOOL_ALLOC_UPDATE; + REPEAT_MOD_ALLOC_RESUME; + if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->update, NULL) < 0) goto error; + return UNLANG_ACTION_PUSHED_CHILD; + } + goto finish; + } + + case IPPOOL_ALLOC_POOL_CHECK: + if (query) { /* *Ok, so the allocate-find query found nothing ... *Let's check if the pool exists at all */ allocation_len = sqlippool_query1(allocation, sizeof(allocation), - inst->pool_check, &handle, inst, request); + query->vb_strvalue, &handle, sql, request); + talloc_free(query); if (!handle) RETURN_MODULE_FAIL; - fr_pool_connection_release(inst->sql->pool, request, handle); - if (allocation_len) { - /* * Pool exists after all... So, * the failure to allocate the IP @@ -397,51 +457,106 @@ static unlang_action_t CC_HINT(nonnull) mod_alloc(rlm_rcode_t *p_result, module_ RWDEBUG("IP address could not be allocated as no pool exists with the name \"%pV\"", &env->pool_name); RETURN_MODULE_NOOP; + } + goto no_address; + case IPPOOL_ALLOC_UPDATE: + if (query) { + if (sqlippool_command(query->vb_strvalue, &handle, sql, request) < 0) goto error; + talloc_free(query); } - fr_pool_connection_release(inst->sql->pool, request, handle); + finish: + DO_PART(commit); - RDEBUG2("IP address could not be allocated"); - RETURN_MODULE_NOOP; + talloc_free(alloc_ctx); + RETURN_MODULE_UPDATED; } /* - * See if we can create the VP from the returned data. If not, - * error out. If so, add it to the list. + * All return paths are handled within the switch statement. */ - ip_map = (map_t) { - .lhs = env->allocated_address_attr, - .op = T_OP_SET, - .rhs = &ip_rhs - }; + fr_assert(0); + RETURN_MODULE_FAIL; +} + +/** Initiate the allocation of an IP address from the pool. + * + * Based on configured queries and attributes which exist, determines the first + * query tmpl to expand. + * + * @param p_result Result of the allocation (if it fails). + * @param mctx Module context. + * @param request Current request. + * @return One of the UNLANG_ACTION_* values. + */ +static unlang_action_t CC_HINT(nonnull) mod_alloc(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) +{ + rlm_sqlippool_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sqlippool_t); + ippool_alloc_call_env_t *env = talloc_get_type_abort(mctx->env_data, ippool_alloc_call_env_t); + rlm_sql_t const *sql = inst->sql; + rlm_sql_handle_t *handle; + ippool_alloc_ctx_t *alloc_ctx = NULL; - tmpl_init_shallow(&ip_rhs, TMPL_TYPE_DATA, T_BARE_WORD, "", 0, NULL); - fr_value_box_bstrndup_shallow(&ip_map.rhs->data.literal, NULL, allocation, allocation_len, false); - if (map_to_request(request, &ip_map, map_to_vp, NULL) < 0) { - DO_PART(alloc_commit); + /* + * If the allocated IP attribute already exists, do nothing + */ + if (env->allocated_address.type) { + RDEBUG2("%s already exists (%pV)", env->allocated_address_attr->name, &env->allocated_address); + RETURN_MODULE_NOOP; + } - RDEBUG2("Invalid IP address [%s] returned from database query.", allocation); - fr_pool_connection_release(inst->sql->pool, request, handle); + if (env->pool_name.type == FR_TYPE_NULL) { + RDEBUG2("No %s defined", env->pool_name_tmpl->name); RETURN_MODULE_NOOP; } - /* - * UPDATE - */ - if (sqlippool_command(inst->alloc_update, &handle, inst, request) < 0) { + RESERVE_CONNECTION(handle, inst->sql->pool, request); + request_data_add(request, (void *)sql_escape_uctx_alloc, 0, handle, false, false, false); + + DO_PART(begin); + + MEM(alloc_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ippool_alloc_ctx_t)); + *alloc_ctx = (ippool_alloc_ctx_t) { + .env = env, + .handle = handle, + .sql = inst->sql, + .request = request, + }; + talloc_set_destructor(alloc_ctx, sqlippool_alloc_ctx_free); + fr_value_box_list_init(&alloc_ctx->values); + if (unlang_function_push(request, NULL, mod_alloc_resume, NULL, 0, UNLANG_SUB_FRAME, alloc_ctx) < 0 ) { error: - if (handle) fr_pool_connection_release(inst->sql->pool, request, handle); + talloc_free(alloc_ctx); RETURN_MODULE_FAIL; } - DO_PART(alloc_commit); - - RDEBUG2("Allocated IP %s", allocation); + /* + * Establish which tmpl needs expanding first. + * + * If there is a query for finding the existing IP expand that first + */ + if (env->existing) { + alloc_ctx->status = IPPOOL_ALLOC_EXISTING; + if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->existing, NULL) < 0) goto error; + return UNLANG_ACTION_PUSHED_CHILD; + } - if (handle) fr_pool_connection_release(inst->sql->pool, request, handle); + /* + * If have a requested IP address and a query to find whether it is available then try that + */ + if (env->requested && (env->requested_address.type != FR_TYPE_NULL)) { + alloc_ctx->status = IPPOOL_ALLOC_REQUESTED; + if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->requested, NULL) < 0) goto error; + return UNLANG_ACTION_PUSHED_CHILD; + } - RETURN_MODULE_UPDATED; + /* + * If neither of the previous two queries were defined, first expand the "find" query + */ + alloc_ctx->status = IPPOOL_ALLOC_FIND; + if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->find, NULL) < 0) goto error; + return UNLANG_ACTION_PUSHED_CHILD; } /*