static int mod_instantiate(void *instance, CONF_SECTION *conf) { rlm_redis_t *inst = instance; inst->cluster = fr_redis_cluster_alloc(inst, conf, &inst->conf, true, NULL, NULL, NULL); if (!inst->cluster) return -1; return 0; }
/** Create a new rlm_cache_redis instance * * @copydetails cache_instantiate_t */ static int mod_instantiate(CONF_SECTION *conf, rlm_cache_config_t const *config, void *driver_inst) { rlm_cache_redis_t *driver = driver_inst; char buffer[256]; buffer[0] = '\0'; fr_redis_version_print(); if (cf_section_parse(conf, driver, driver_config) < 0) return -1; snprintf(buffer, sizeof(buffer), "rlm_cache (%s)", config->name); driver->cluster = fr_redis_cluster_alloc(driver, conf, &driver->conf); if (!driver->cluster) { ERROR("rlm_cache_redis: Cluster failure"); return -1; } /* * These never change, so do it once on instantiation */ if (tmpl_from_attr_str(&driver->created_attr, "&Cache-Created", REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) < 0) { ERROR("rlm_cache_redis: Cache-Created attribute not defined"); return -1; } if (tmpl_from_attr_str(&driver->expires_attr, "&Cache-Expires", REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) < 0) { ERROR("rlm_cache_redis: Cache-Expires attribute not defined"); return -1; } return 0; }
/** Allocate a new IP address from a pool * */ static ippool_rcode_t redis_ippool_allocate(rlm_redis_ippool_t const *inst, REQUEST *request, uint8_t const *key_prefix, size_t key_prefix_len, uint8_t const *device_id, size_t device_id_len, uint8_t const *gateway_id, size_t gateway_id_len, uint32_t expires) { struct timeval now; redisReply *reply = NULL; fr_redis_rcode_t status; ippool_rcode_t ret = IPPOOL_RCODE_SUCCESS; rad_assert(key_prefix); rad_assert(device_id); gettimeofday(&now, NULL); /* * hiredis doesn't deal well with NULL string pointers */ if (!gateway_id) gateway_id = (uint8_t const *)""; status = ippool_script(&reply, request, inst->cluster, key_prefix, key_prefix_len, inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout), lua_alloc_digest, lua_alloc_cmd, "EVALSHA %s 1 %b %u %u %b %b", lua_alloc_digest, key_prefix, key_prefix_len, (unsigned int)now.tv_sec, expires, device_id, device_id_len, gateway_id, gateway_id_len); if (status != REDIS_RCODE_SUCCESS) { ret = IPPOOL_RCODE_FAIL; goto finish; } rad_assert(reply); if (reply->type != REDIS_REPLY_ARRAY) { REDEBUG("Expected result to be array got \"%s\"", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } if (reply->elements == 0) { REDEBUG("Got empty result array"); ret = IPPOOL_RCODE_FAIL; goto finish; } /* * Process return code */ if (reply->element[0]->type != REDIS_REPLY_INTEGER) { REDEBUG("Server returned unexpected type \"%s\" for rcode element (result[0])", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } ret = reply->element[0]->integer; if (ret < 0) goto finish; /* * Process IP address */ if (reply->elements > 1) { vp_tmpl_t ip_rhs = { .type = TMPL_TYPE_DATA, .tmpl_value_type = FR_TYPE_STRING }; vp_map_t ip_map = { .lhs = inst->allocated_address_attr, .op = T_OP_SET, .rhs = &ip_rhs }; switch (reply->element[1]->type) { /* * Destination attribute may not be IPv4, in which case * we want to pre-convert the integer value to an IPv4 * address before casting it once more to the type of * the destination attribute. */ case REDIS_REPLY_INTEGER: { if (ip_map.lhs->tmpl_da->type != FR_TYPE_IPV4_ADDR) { fr_value_box_t tmp; memset(&tmp, 0, sizeof(tmp)); tmp.vb_uint32 = ntohl((uint32_t)reply->element[1]->integer); tmp.type = FR_TYPE_UINT32; if (fr_value_box_cast(NULL, &ip_map.rhs->tmpl_value, FR_TYPE_IPV4_ADDR, NULL, &tmp)) { RPEDEBUG("Failed converting integer to IPv4 address"); ret = IPPOOL_RCODE_FAIL; goto finish; } } else { ip_map.rhs->tmpl_value.vb_uint32 = ntohl((uint32_t)reply->element[1]->integer); ip_map.rhs->tmpl_value_type = FR_TYPE_UINT32; } } goto do_ip_map; case REDIS_REPLY_STRING: ip_map.rhs->tmpl_value.vb_strvalue = reply->element[1]->str; ip_map.rhs->tmpl_value_length = reply->element[1]->len; ip_map.rhs->tmpl_value_type = FR_TYPE_STRING; do_ip_map: if (map_to_request(request, &ip_map, map_to_vp, NULL) < 0) { ret = IPPOOL_RCODE_FAIL; goto finish; } break; default: REDEBUG("Server returned unexpected type \"%s\" for IP element (result[1])", fr_int2str(redis_reply_types, reply->element[1]->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } } /* * Process Range identifier */ if (reply->elements > 2) { switch (reply->element[2]->type) { /* * Add range ID to request */ case REDIS_REPLY_STRING: { vp_tmpl_t range_rhs = { .name = "", .type = TMPL_TYPE_DATA, .tmpl_value_type = FR_TYPE_STRING, .quote = T_DOUBLE_QUOTED_STRING }; vp_map_t range_map = { .lhs = inst->range_attr, .op = T_OP_SET, .rhs = &range_rhs }; range_map.rhs->tmpl_value.vb_strvalue = reply->element[2]->str; range_map.rhs->tmpl_value_length = reply->element[2]->len; range_map.rhs->tmpl_value_type = FR_TYPE_STRING; if (map_to_request(request, &range_map, map_to_vp, NULL) < 0) { ret = IPPOOL_RCODE_FAIL; goto finish; } } break; case REDIS_REPLY_NIL: break; default: REDEBUG("Server returned unexpected type \"%s\" for range element (result[2])", fr_int2str(redis_reply_types, reply->element[2]->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } } /* * Process Expiry time */ if (inst->expiry_attr && (reply->elements > 3)) { vp_tmpl_t expiry_rhs = { .name = "", .type = TMPL_TYPE_DATA, .tmpl_value_type = FR_TYPE_STRING, .quote = T_DOUBLE_QUOTED_STRING }; vp_map_t expiry_map = { .lhs = inst->expiry_attr, .op = T_OP_SET, .rhs = &expiry_rhs }; if (reply->element[3]->type != REDIS_REPLY_INTEGER) { REDEBUG("Server returned unexpected type \"%s\" for expiry element (result[3])", fr_int2str(redis_reply_types, reply->element[3]->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } expiry_map.rhs->tmpl_value.vb_uint32 = reply->element[3]->integer; expiry_map.rhs->tmpl_value_type = FR_TYPE_UINT32; if (map_to_request(request, &expiry_map, map_to_vp, NULL) < 0) { ret = IPPOOL_RCODE_FAIL; goto finish; } } finish: fr_redis_reply_free(&reply); return ret; } /** Update an existing IP address in a pool * */ static ippool_rcode_t redis_ippool_update(rlm_redis_ippool_t const *inst, REQUEST *request, uint8_t const *key_prefix, size_t key_prefix_len, fr_ipaddr_t *ip, uint8_t const *device_id, size_t device_id_len, uint8_t const *gateway_id, size_t gateway_id_len, uint32_t expires) { struct timeval now; redisReply *reply = NULL; fr_redis_rcode_t status; ippool_rcode_t ret = IPPOOL_RCODE_SUCCESS; vp_tmpl_t range_rhs = { .name = "", .type = TMPL_TYPE_DATA, .tmpl_value_type = FR_TYPE_STRING, .quote = T_DOUBLE_QUOTED_STRING }; vp_map_t range_map = { .lhs = inst->range_attr, .op = T_OP_SET, .rhs = &range_rhs }; gettimeofday(&now, NULL); /* * hiredis doesn't deal well with NULL string pointers */ if (!device_id) device_id = (uint8_t const *)""; if (!gateway_id) gateway_id = (uint8_t const *)""; if ((ip->af == AF_INET) && inst->ipv4_integer) { status = ippool_script(&reply, request, inst->cluster, key_prefix, key_prefix_len, inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout), lua_update_digest, lua_update_cmd, "EVALSHA %s 1 %b %u %u %u %b %b", lua_update_digest, key_prefix, key_prefix_len, (unsigned int)now.tv_sec, expires, htonl(ip->addr.v4.s_addr), device_id, device_id_len, gateway_id, gateway_id_len); } else { char ip_buff[FR_IPADDR_PREFIX_STRLEN]; IPPOOL_SPRINT_IP(ip_buff, ip, ip->prefix); status = ippool_script(&reply, request, inst->cluster, key_prefix, key_prefix_len, inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout), lua_update_digest, lua_update_cmd, "EVALSHA %s 1 %b %u %u %s %b %b", lua_update_digest, key_prefix, key_prefix_len, (unsigned int)now.tv_sec, expires, ip_buff, device_id, device_id_len, gateway_id, gateway_id_len); } if (status != REDIS_RCODE_SUCCESS) { ret = IPPOOL_RCODE_FAIL; goto finish; } if (reply->type != REDIS_REPLY_ARRAY) { REDEBUG("Expected result to be array got \"%s\"", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } if (reply->elements == 0) { REDEBUG("Got empty result array"); ret = IPPOOL_RCODE_FAIL; goto finish; } /* * Process return code */ if (reply->element[0]->type != REDIS_REPLY_INTEGER) { REDEBUG("Server returned unexpected type \"%s\" for rcode element (result[0])", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } ret = reply->element[0]->integer; if (ret < 0) goto finish; /* * Process Range identifier */ if (reply->elements > 1) { switch (reply->element[1]->type) { /* * Add range ID to request */ case REDIS_REPLY_STRING: range_map.rhs->tmpl_value.vb_strvalue = reply->element[1]->str; range_map.rhs->tmpl_value_length = reply->element[1]->len; range_map.rhs->tmpl_value_type = FR_TYPE_STRING; if (map_to_request(request, &range_map, map_to_vp, NULL) < 0) { ret = IPPOOL_RCODE_FAIL; goto finish; } break; case REDIS_REPLY_NIL: break; default: REDEBUG("Server returned unexpected type \"%s\" for range element (result[1])", fr_int2str(redis_reply_types, reply->element[0]->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } } /* * Copy expiry time to expires attribute (if set) */ if (inst->expiry_attr) { vp_tmpl_t expiry_rhs = { .name = "", .type = TMPL_TYPE_DATA, .tmpl_value_type = FR_TYPE_STRING, .quote = T_DOUBLE_QUOTED_STRING }; vp_map_t expiry_map = { .lhs = inst->expiry_attr, .op = T_OP_SET, .rhs = &expiry_rhs }; expiry_map.rhs->tmpl_value.vb_uint32 = expires; expiry_map.rhs->tmpl_value_type = FR_TYPE_UINT32; if (map_to_request(request, &expiry_map, map_to_vp, NULL) < 0) { ret = IPPOOL_RCODE_FAIL; goto finish; } } finish: fr_redis_reply_free(&reply); return ret; } /** Release an existing IP address in a pool * */ static ippool_rcode_t redis_ippool_release(rlm_redis_ippool_t const *inst, REQUEST *request, uint8_t const *key_prefix, size_t key_prefix_len, fr_ipaddr_t *ip, uint8_t const *device_id, size_t device_id_len) { struct timeval now; redisReply *reply = NULL; fr_redis_rcode_t status; ippool_rcode_t ret = IPPOOL_RCODE_SUCCESS; gettimeofday(&now, NULL); /* * hiredis doesn't deal well with NULL string pointers */ if (!device_id) device_id = (uint8_t const *)""; if ((ip->af == AF_INET) && inst->ipv4_integer) { status = ippool_script(&reply, request, inst->cluster, key_prefix, key_prefix_len, inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout), lua_release_digest, lua_release_cmd, "EVALSHA %s 1 %b %u %u %b", lua_release_digest, key_prefix, key_prefix_len, (unsigned int)now.tv_sec, htonl(ip->addr.v4.s_addr), device_id, device_id_len); } else { char ip_buff[FR_IPADDR_PREFIX_STRLEN]; IPPOOL_SPRINT_IP(ip_buff, ip, ip->prefix); status = ippool_script(&reply, request, inst->cluster, key_prefix, key_prefix_len, inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout), lua_release_digest, lua_release_cmd, "EVALSHA %s 1 %b %u %s %b", lua_release_digest, key_prefix, key_prefix_len, (unsigned int)now.tv_sec, ip_buff, device_id, device_id_len); } if (status != REDIS_RCODE_SUCCESS) { ret = IPPOOL_RCODE_FAIL; goto finish; } if (reply->type != REDIS_REPLY_ARRAY) { REDEBUG("Expected result to be array got \"%s\"", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } if (reply->elements == 0) { REDEBUG("Got empty result array"); ret = IPPOOL_RCODE_FAIL; goto finish; } /* * Process return code */ if (reply->element[0]->type != REDIS_REPLY_INTEGER) { REDEBUG("Server returned unexpected type \"%s\" for rcode element (result[0])", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } ret = reply->element[0]->integer; if (ret < 0) goto finish; finish: fr_redis_reply_free(&reply); return ret; } /** Find the pool name we'll be allocating from * * @param[out] out Where to write the pool name. * @param[out] buff Where to write the pool name (in the case of an expansion). * @param[in] bufflen Size of the output buffer. * @param[in] inst This instance of the rlm_redis_ippool module. * @param[in] request The current request. * @return * - < 0 on error. * - 0 if no pool attribute exists, or the pool name is a zero length string. * - > 0 on success (length of data written to out). */ static inline ssize_t ippool_pool_name(uint8_t const **out, uint8_t buff[], size_t bufflen, rlm_redis_ippool_t const *inst, REQUEST *request) { ssize_t slen; slen = tmpl_expand(out, (char *)buff, bufflen, request, inst->pool_name, NULL, NULL); if (slen < 0) { if (inst->pool_name->type == TMPL_TYPE_ATTR) { RDEBUG2("Pool attribute not present in request. Doing nothing"); return 0; } REDEBUG("Failed expanding pool name"); return -1; } if (slen == 0) { RDEBUG2("Empty pool name. Doing nothing"); return 0; } if ((*out == buff) && is_truncated((size_t)slen, bufflen)) { REDEBUG("Pool name too long. Expected %zu bytes, got %zu bytes", bufflen, (size_t)slen); return -1; } return slen; } static rlm_rcode_t mod_action(rlm_redis_ippool_t const *inst, REQUEST *request, ippool_action_t action) { uint8_t key_prefix_buff[IPPOOL_MAX_KEY_PREFIX_SIZE], device_id_buff[256], gateway_id_buff[256]; uint8_t const *key_prefix, *device_id = NULL, *gateway_id = NULL; size_t key_prefix_len, device_id_len = 0, gateway_id_len = 0; ssize_t slen; fr_ipaddr_t ip; char expires_buff[20]; char const *expires_str; unsigned long expires = 0; char *q; slen = ippool_pool_name(&key_prefix, (uint8_t *)&key_prefix_buff, sizeof(key_prefix_len), inst, request); if (slen < 0) return RLM_MODULE_FAIL; if (slen == 0) return RLM_MODULE_NOOP; key_prefix_len = (size_t)slen; if (inst->device_id) { slen = tmpl_expand((char const **)&device_id, (char *)&device_id_buff, sizeof(device_id_buff), request, inst->device_id, NULL, NULL); if (slen < 0) { REDEBUG("Failed expanding device (%s)", inst->device_id->name); return RLM_MODULE_FAIL; } device_id_len = (size_t)slen; } if (inst->gateway_id) { slen = tmpl_expand((char const **)&gateway_id, (char *)&gateway_id_buff, sizeof(gateway_id_buff), request, inst->gateway_id, NULL, NULL); if (slen < 0) { REDEBUG("Failed expanding gateway (%s)", inst->gateway_id->name); return RLM_MODULE_FAIL; } gateway_id_len = (size_t)slen; } switch (action) { case POOL_ACTION_ALLOCATE: if (tmpl_expand(&expires_str, expires_buff, sizeof(expires_buff), request, inst->offer_time, NULL, NULL) < 0) { REDEBUG("Failed expanding offer_time (%s)", inst->offer_time->name); return RLM_MODULE_FAIL; } expires = strtoul(expires_str, &q, 10); if (q != (expires_str + strlen(expires_str))) { REDEBUG("Invalid offer_time. Must be an integer value"); return RLM_MODULE_FAIL; } ippool_action_print(request, action, L_DBG_LVL_2, key_prefix, key_prefix_len, NULL, device_id, device_id_len, gateway_id, gateway_id_len, expires); switch (redis_ippool_allocate(inst, request, key_prefix, key_prefix_len, device_id, device_id_len, gateway_id, gateway_id_len, (uint32_t)expires)) { case IPPOOL_RCODE_SUCCESS: RDEBUG2("IP address lease allocated"); return RLM_MODULE_UPDATED; case IPPOOL_RCODE_POOL_EMPTY: RWDEBUG("Pool contains no free addresses"); return RLM_MODULE_NOTFOUND; default: return RLM_MODULE_FAIL; } case POOL_ACTION_UPDATE: { char ip_buff[INET6_ADDRSTRLEN + 4]; char const *ip_str; if (tmpl_expand(&expires_str, expires_buff, sizeof(expires_buff), request, inst->lease_time, NULL, NULL) < 0) { REDEBUG("Failed expanding lease_time (%s)", inst->lease_time->name); return RLM_MODULE_FAIL; } expires = strtoul(expires_str, &q, 10); if (q != (expires_str + strlen(expires_str))) { REDEBUG("Invalid expires. Must be an integer value"); return RLM_MODULE_FAIL; } if (tmpl_expand(&ip_str, ip_buff, sizeof(ip_buff), request, inst->requested_address, NULL, NULL) < 0) { REDEBUG("Failed expanding requested_address (%s)", inst->requested_address->name); return RLM_MODULE_FAIL; } if (fr_inet_pton(&ip, ip_str, -1, AF_UNSPEC, false, true) < 0) { RPEDEBUG("Failed parsing address"); return RLM_MODULE_FAIL; } ippool_action_print(request, action, L_DBG_LVL_2, key_prefix, key_prefix_len, ip_str, device_id, device_id_len, gateway_id, gateway_id_len, expires); switch (redis_ippool_update(inst, request, key_prefix, key_prefix_len, &ip, device_id, device_id_len, gateway_id, gateway_id_len, (uint32_t)expires)) { case IPPOOL_RCODE_SUCCESS: RDEBUG2("Requested IP address' \"%s\" lease updated", ip_str); /* * Copy over the input IP address to the reply attribute */ if (inst->copy_on_update) { vp_tmpl_t ip_rhs = { .name = "", .type = TMPL_TYPE_DATA, .quote = T_BARE_WORD, }; vp_map_t ip_map = { .lhs = inst->allocated_address_attr, .op = T_OP_SET, .rhs = &ip_rhs }; ip_rhs.tmpl_value_length = strlen(ip_str); ip_rhs.tmpl_value.vb_strvalue = ip_str; ip_rhs.tmpl_value_type = FR_TYPE_STRING; if (map_to_request(request, &ip_map, map_to_vp, NULL) < 0) return RLM_MODULE_FAIL; } return RLM_MODULE_UPDATED; /* * It's useful to be able to identify the 'not found' case * as we can relay to a server where the IP address might * be found. This extremely useful for migrations. */ case IPPOOL_RCODE_NOT_FOUND: REDEBUG("Requested IP address \"%s\" is not a member of the specified pool", ip_str); return RLM_MODULE_NOTFOUND; case IPPOOL_RCODE_EXPIRED: REDEBUG("Requested IP address' \"%s\" lease already expired at time of renewal", ip_str); return RLM_MODULE_INVALID; case IPPOOL_RCODE_DEVICE_MISMATCH: REDEBUG("Requested IP address' \"%s\" lease allocated to another device", ip_str); return RLM_MODULE_INVALID; default: return RLM_MODULE_FAIL; } } case POOL_ACTION_RELEASE: { char ip_buff[INET6_ADDRSTRLEN + 4]; char const *ip_str; if (tmpl_expand(&ip_str, ip_buff, sizeof(ip_buff), request, inst->requested_address, NULL, NULL) < 0) { REDEBUG("Failed expanding requested_address (%s)", inst->requested_address->name); return RLM_MODULE_FAIL; } if (fr_inet_pton(&ip, ip_str, -1, AF_UNSPEC, false, true) < 0) { RPEDEBUG("Failed parsing address"); return RLM_MODULE_FAIL; } ippool_action_print(request, action, L_DBG_LVL_2, key_prefix, key_prefix_len, ip_str, device_id, device_id_len, gateway_id, gateway_id_len, 0); switch (redis_ippool_release(inst, request, key_prefix, key_prefix_len, &ip, device_id, device_id_len)) { case IPPOOL_RCODE_SUCCESS: RDEBUG2("IP address \"%s\" released", ip_str); return RLM_MODULE_UPDATED; /* * It's useful to be able to identify the 'not found' case * as we can relay to a server where the IP address might * be found. This extremely useful for migrations. */ case IPPOOL_RCODE_NOT_FOUND: REDEBUG("Requested IP address \"%s\" is not a member of the specified pool", ip_str); return RLM_MODULE_NOTFOUND; case IPPOOL_RCODE_DEVICE_MISMATCH: REDEBUG("Requested IP address' \"%s\" lease allocated to another device", ip_str); return RLM_MODULE_INVALID; default: return RLM_MODULE_FAIL; } } case POOL_ACTION_BULK_RELEASE: RDEBUG2("Bulk release not yet implemented"); return RLM_MODULE_NOOP; default: rad_assert(0); return RLM_MODULE_FAIL; } } static rlm_rcode_t mod_accounting(void *instance, UNUSED void *thread, REQUEST *request) CC_HINT(nonnull); static rlm_rcode_t mod_accounting(void *instance, UNUSED void *thread, REQUEST *request) { rlm_redis_ippool_t const *inst = instance; VALUE_PAIR *vp; /* * Pool-Action override */ vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY); if (vp) return mod_action(inst, request, vp->vp_uint32); /* * Otherwise, guess the action by Acct-Status-Type */ vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY); if (!vp) { RDEBUG2("Couldn't find &request:Acct-Status-Type or &control:Pool-Action, doing nothing..."); return RLM_MODULE_NOOP; } switch (vp->vp_uint32) { case FR_STATUS_START: case FR_STATUS_ALIVE: return mod_action(inst, request, POOL_ACTION_UPDATE); case FR_STATUS_STOP: return mod_action(inst, request, POOL_ACTION_RELEASE); case FR_STATUS_ACCOUNTING_OFF: case FR_STATUS_ACCOUNTING_ON: return mod_action(inst, request, POOL_ACTION_BULK_RELEASE); default: return RLM_MODULE_NOOP; } } static rlm_rcode_t mod_authorize(void *instance, UNUSED void *thread, REQUEST *request) CC_HINT(nonnull); static rlm_rcode_t mod_authorize(void *instance, UNUSED void *thread, REQUEST *request) { rlm_redis_ippool_t const *inst = instance; VALUE_PAIR *vp; /* * Unless it's overridden the default action is to allocate * when called in Post-Auth. */ vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY); return mod_action(inst, request, vp ? vp->vp_uint32 : POOL_ACTION_ALLOCATE); } static rlm_rcode_t mod_post_auth(void *instance, UNUSED void *thread, REQUEST *request) CC_HINT(nonnull); static rlm_rcode_t mod_post_auth(void *instance, UNUSED void *thread, REQUEST *request) { rlm_redis_ippool_t const *inst = instance; VALUE_PAIR *vp; ippool_action_t action = POOL_ACTION_ALLOCATE; /* * Unless it's overridden the default action is to allocate * when called in Post-Auth. */ vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY); if (vp) { if ((vp->vp_uint32 > 0) && (vp->vp_uint32 <= POOL_ACTION_BULK_RELEASE)) { action = vp->vp_uint32; } else { RWDEBUG("Ignoring invalid action %d", vp->vp_uint32); return RLM_MODULE_NOOP; } #ifdef WITH_DHCP } else if (request->dict == dict_dhcpv4) { vp = fr_pair_find_by_da(request->control, attr_message_type, TAG_ANY); if (!vp) goto run; if (vp->vp_uint8 == FR_DHCP_REQUEST) action = POOL_ACTION_UPDATE; #endif } run: return mod_action(inst, request, action); } static int mod_instantiate(void *instance, CONF_SECTION *conf) { static bool done_hash = false; CONF_SECTION *subcs = cf_section_find(conf, "redis", NULL); rlm_redis_ippool_t *inst = instance; rad_assert(inst->allocated_address_attr->type == TMPL_TYPE_ATTR); rad_assert(subcs); inst->cluster = fr_redis_cluster_alloc(inst, subcs, &inst->conf, true, NULL, NULL, NULL); if (!inst->cluster) return -1; if (!fr_redis_cluster_min_version(inst->cluster, "3.0.2")) { PERROR("Cluster error"); return -1; } /* * Pre-Compute the SHA1 hashes of the Lua scripts */ if (!done_hash) { fr_sha1_ctx sha1_ctx; uint8_t digest[SHA1_DIGEST_LENGTH]; fr_sha1_init(&sha1_ctx); fr_sha1_update(&sha1_ctx, (uint8_t const *)lua_alloc_cmd, sizeof(lua_alloc_cmd) - 1); fr_sha1_final(digest, &sha1_ctx); fr_bin2hex(lua_alloc_digest, digest, sizeof(digest)); fr_sha1_init(&sha1_ctx); fr_sha1_update(&sha1_ctx, (uint8_t const *)lua_update_cmd, sizeof(lua_update_cmd) - 1); fr_sha1_final(digest, &sha1_ctx); fr_bin2hex(lua_update_digest, digest, sizeof(digest)); fr_sha1_init(&sha1_ctx); fr_sha1_update(&sha1_ctx, (uint8_t const *)lua_release_cmd, sizeof(lua_release_cmd) - 1); fr_sha1_final(digest, &sha1_ctx); fr_bin2hex(lua_release_digest, digest, sizeof(digest)); } /* * If we don't have a separate time specifically for offers * just use the lease time. */ if (!inst->offer_time) inst->offer_time = inst->lease_time; return 0; } static int mod_load(void) { fr_redis_version_print(); return 0; } extern module_t rlm_redis_ippool; module_t rlm_redis_ippool = { .magic = RLM_MODULE_INIT, .name = "redis", .type = RLM_TYPE_THREAD_SAFE, .inst_size = sizeof(rlm_redis_ippool_t), .config = module_config, .onload = mod_load, .instantiate = mod_instantiate, .methods = { [MOD_ACCOUNTING] = mod_accounting, [MOD_AUTHORIZE] = mod_authorize, [MOD_POST_AUTH] = mod_post_auth, }, };