27RCSID(
"$Id: fd2a5980fbcbcb7e44ab962933bd0d3632833c1c $")
28#include <freeradius-devel/server/cf_parse.h>
29#include <freeradius-devel/util/debug.h>
35#define MAX_PIPELINED 100000
116 uint8_t const *key_prefix,
size_t key_prefix_len,
117 uint8_t const *range,
size_t range_len,
122#define IPPOOL_BUILD_IP_KEY_FROM_STR(_buff, _p, _key, _key_len, _ip_str) \
126 memcpy(_p, _key, _key_len); \
128 _slen = strlcpy((char *)_p, "}:"IPPOOL_ADDRESS_KEY":", sizeof(_buff) - (_p - _buff)); \
129 if (is_truncated((size_t)_slen, sizeof(_buff) - (_p - _buff))) { \
130 ERROR("IP key too long"); \
133 _p += (size_t)_slen;\
134 _p += strlcpy((char *)_p, _ip_str, sizeof(_buff) - (_p - _buff)); \
138#define IPPOOL_BUILD_OWNER_KEY(_buff, _p, _key, _key_len, _owner) \
142 memcpy(_p, _key, _key_len); \
144 _slen = strlcpy((char *)_p, "}:"IPPOOL_OWNER_KEY":", sizeof(_buff) - (_p - _buff)); \
145 if (is_truncated((size_t)_slen, sizeof(_buff) - (_p - _buff))) { \
146 ERROR("Owner key too long"); \
149 _p += (size_t)_slen;\
150 _p += strlcpy((char *)_p, _owner, sizeof(_buff) - (_p - _buff)); \
178 "ret = redis.call('ZADD', '{' .. KEYS[1] .. '}:"IPPOOL_POOL_KEY"', 'XX', 'CH', 0, ARGV[1])" EOL
179 "if ret == 0 then" EOL
183 " .. ARGV[1], 'device')" EOL
184 "if not found then" EOL
220 "local expires = tonumber(redis.call('ZSCORE', pool_key, ARGV[1]))" EOL
228 "local found = redis.call('GET', owner_key)" EOL
229 "if found and found ~= ARGV[1] then" EOL
237 "if expires and expires > tonumber(ARGV[4]) then" EOL
238 " found = redis.call('HGET', ip_key, 'device')"
239 " if found and found ~= ARGV[2] then" EOL
248 "redis.call('ZADD', pool_key, 'CH', expires, ARGV[1])" EOL
249 "redis.call('SET', owner_key, ARGV[1])" EOL
250 "redis.call('HSET', ip_key, 'device', ARGV[2], 'counter', 0)" EOL
251 "if ARGV[3] then" EOL
252 " redis.call('HSET', ip_key, 'range', ARGV[3])" EOL
280 "found = redis.call('GET', owner_key)" EOL
281 "if not found or found ~= ARGV[1] then" EOL
288 "local expires = tonumber(redis.call('ZSCORE', pool_key, ARGV[1]))" EOL
290 "if not static then" EOL
298 "redis.call('ZADD', pool_key, 'XX', expires, ARGV[1])" EOL
304 "if expires > tonumber(ARGV[3]) then" EOL
305 " redis.call('EXPIRE', owner_key, expires - tonumber(ARGV[3]))" EOL
307 " redis.call('DEL', owner_key)" EOL
329 "local address_key" EOL
333 "found = redis.call('HGET', address_key, 'device')" EOL
334 "redis.call('DEL', address_key)" EOL
335 "if not found then" EOL
346 INFO(
"Usage: %s -adrsm range... [-p prefix_len]... [-x]... [-oShf] server[:port] [pool] [range id]",
name);
347 INFO(
"Pool management:");
348 INFO(
" -a range Add address(es)/prefix(es) to the pool.");
349 INFO(
" -d range Delete address(es)/prefix(es) in this range.");
350 INFO(
" -r range Release address(es)/prefix(es) in this range.");
351 INFO(
" -s range Show addresses/prefix in this range.");
352 INFO(
" -A address/prefix Assign a static lease.");
353 INFO(
" -O owner To use when assigning a static lease.");
354 INFO(
" -U address/prefix Un-assign a static lease");
355 INFO(
" -p prefix_len Length of prefix to allocate (defaults to 32/128)");
356 INFO(
" This is used primarily for IPv6 where a prefix is");
357 INFO(
" allocated to an intermediary router, which in turn");
358 INFO(
" allocates sub-prefixes to the devices it serves.");
359 INFO(
" This argument changes the prefix_len for the previous");
360 INFO(
" instance of an -adrsm argument, only.");
361 INFO(
" -m range Change the range id to the one specified for addresses");
362 INFO(
" in this range.");
363 INFO(
" -l List available pools.");
369 INFO(
" -S Print pool statistics");
371 INFO(
"Configuration:");
372 INFO(
" -h Print this help message and exit");
373 INFO(
" -x Increase the verbosity level");
375 INFO(
" -f file Load connection options from a FreeRADIUS format config file");
376 INFO(
" This file should contain a pool { ... } section and one or more");
377 INFO(
" `server = <fqdn>` pairs`");
379 INFO(
"<range> is range \"127.0.0.1-127.0.0.254\" or CIDR network \"127.0.0.1/24\" or host \"127.0.0.1\"");
380 INFO(
"CIDR host bits set start address, e.g. 127.0.0.200/24 -> 127.0.0.200-127.0.0.254");
386 if (bits >= 32)
return 0xffffffff;
387 return (1 << bits) - 1;
403 switch (ipaddr->
af) {
411 uint128_t ip_curr, ip_end;
413 if (!
fr_cond_assert((prefix > 0) && (prefix <= 128)))
return false;
416 memcpy(&ip_curr, ipaddr->
addr.v6.s6_addr,
sizeof(ip_curr));
417 memcpy(&ip_end, end->
addr.v6.s6_addr,
sizeof(ip_curr));
419 ip_curr = ntohlll(ip_curr);
420 ip_end = ntohlll(ip_end);
423 if (
uint128_eq(ip_curr, ip_end))
return false;
427 ip_curr = htonlll(ip_curr);
428 memcpy(&ipaddr->
addr.v6.s6_addr, &ip_curr,
sizeof(ipaddr->
addr.v6.s6_addr));
436 if (!
fr_cond_assert((prefix > 0) && (prefix <= 32)))
return false;
438 ip_curr = ntohl(ipaddr->
addr.v4.s_addr);
439 ip_end = ntohl(end->
addr.v4.s_addr);
442 if (ip_curr == ip_end)
return false;
445 ip_curr += 1 << (32 - prefix);
446 ipaddr->
addr.v4.s_addr = htonl(ip_curr);
470 redisReply **replies = NULL;
472 unsigned int pipelined = 0;
476 size_t reply_cnt = 0;
496 if (enqueued < 0)
break;
497 pipelined += enqueued;
500 if (!replies) replies = talloc_zero_array(
inst, redisReply *, pipelined);
501 if (!replies)
return -1;
504 talloc_array_length(replies), conn);
506 replies[i], NULL, i, status);
517 for (i = 0; (
size_t)i < reply_cnt; i++) {
520 ret = process(
out, &to_process, replies[i]);
521 if (ret < 0)
continue;
526 TALLOC_FREE(replies);
546 if (reply->type != REDIS_REPLY_ARRAY)
return -1;
547 if (reply->elements < 4)
return -1;
549 if (reply->element[0]->type == REDIS_REPLY_NIL)
return 0;
550 if (reply->element[0]->type != REDIS_REPLY_STRING)
return -1;
553 lease->
next_event = (time_t)strtoull(reply->element[0]->str, NULL, 10);
555 if (reply->element[1]->type == REDIS_REPLY_STRING) {
556 lease->
device = talloc_memdup(lease, reply->element[1]->str, reply->element[1]->len);
559 if (reply->element[2]->type == REDIS_REPLY_STRING) {
560 lease->
gateway = talloc_memdup(lease, reply->element[2]->str, reply->element[2]->len);
563 if (reply->element[3]->type == REDIS_REPLY_STRING) {
564 lease->
range = talloc_memdup(lease, reply->element[3]->str, reply->element[3]->len);
565 lease->
range_len = reply->element[3]->len;
571 existing = talloc_array_length(*modified);
573 (*modified)[existing - 1] = lease;
582 uint8_t const *key_prefix,
size_t key_prefix_len,
597 DEBUG(
"Retrieving lease info for %s from pool %pV", ip_buff,
600 redisAppendCommand(conn->
handle,
"MULTI");
601 redisAppendCommand(conn->
handle,
"ZSCORE %b %s", key, key_p - key, ip_buff);
602 redisAppendCommand(conn->
handle,
"HGET %b device", ip_key, ip_key_p - ip_key);
603 redisAppendCommand(conn->
handle,
"HGET %b gateway", ip_key, ip_key_p - ip_key);
604 redisAppendCommand(conn->
handle,
"HGET %b range", ip_key, ip_key_p - ip_key);
605 redisAppendCommand(conn->
handle,
"EXEC");
622 uint64_t *modified =
out;
628 if (reply->type != REDIS_REPLY_INTEGER)
return -1;
630 *modified += reply->integer;
639 uint8_t const *key_prefix,
size_t key_prefix_len,
647 DEBUG(
"Releasing %pV to pool \"%pV\"", ip_buff,
649 redisAppendCommand(conn->
handle,
"EVAL %s 1 %b %s",
lua_release_cmd, key_prefix, key_prefix_len, ip_buff);
669 uint64_t *modified =
out;
675 if (reply->type != REDIS_REPLY_INTEGER)
return -1;
677 *modified += reply->integer;
688 uint8_t const *key_prefix,
size_t key_prefix_len,
696 DEBUG(
"Removing %s from pool \"%pV\"", ip_buff,
698 redisAppendCommand(conn->
handle,
"EVAL %s 1 %b %s",
lua_remove_cmd, key_prefix, key_prefix_len, ip_buff);
718 uint64_t *modified =
out;
724 if (reply->type != REDIS_REPLY_ARRAY)
return -1;
726 if ((reply->elements > 0) && (reply->element[0]->type == REDIS_REPLY_INTEGER)) {
727 *modified += reply->element[0]->integer;
736 uint8_t const *key_prefix,
size_t key_prefix_len,
737 uint8_t const *range,
size_t range_len,
754 redisAppendCommand(conn->
handle,
"MULTI");
756 redisAppendCommand(conn->
handle,
"ZADD %b NX %u %s", key, key_p - key, 0, ip_buff);
765 redisAppendCommand(conn->
handle,
"HSET %b range %b", ip_key, ip_key_p - ip_key, range, range_len);
768 redisAppendCommand(conn->
handle,
"EXEC");
786 uint64_t *modified =
out;
792 if (reply->type != REDIS_REPLY_INTEGER)
return -1;
809 uint8_t const *key_prefix,
size_t key_prefix_len,
810 uint8_t const *range,
size_t range_len,
825 redisAppendCommand(conn->
handle,
"HSET %b range %b", ip_key, ip_key_p - ip_key, range, range_len);
841 uint64_t *modified =
out;
842 if (reply->type != REDIS_REPLY_INTEGER)
return -1;
844 *modified += reply->integer;
852 uint8_t const *key_prefix,
size_t key_prefix_len,
853 uint8_t const *range,
size_t range_len,
863 DEBUG(
"Assigning address %s to owner %s", ip_buff, owner->
owner);
865 redisAppendCommand(conn->
handle,
"EVAL %s 1 %b %s %b %b %i",
lua_assign_cmd, key_prefix, key_prefix_len,
882 uint64_t *modified =
out;
886 if (reply->type != REDIS_REPLY_INTEGER)
return -1;
888 *modified += reply->integer;
897 uint8_t const *key_prefix,
size_t key_prefix_len,
908 DEBUG(
"Un-assigning address %s from owner %s", ip_buff, owner->
owner);
927static int8_t
pool_cmp(
void const *a,
void const *b)
933 len_a = talloc_array_length((
uint8_t const *)a);
934 len_b = talloc_array_length((
uint8_t const *)b);
936 ret =
CMP(len_a, len_b);
937 if (ret != 0)
return ret;
939 ret = memcmp(a, b, len_a);
976 result = talloc_zero_array(ctx,
uint8_t *, 1);
978 ERROR(
"Failed allocating array of pool names");
986 for (i = 0; i < ret; i++) {
991 char cursor[19] =
"0";
995 ERROR(
"Failed retrieving pool for node");
1003 if (!conn)
goto error;
1009 reply = redisCommand(conn->
handle,
"SCAN %s MATCH %b COUNT 20", cursor, key, key_p - key);
1011 ERROR(
"Failed reading reply");
1018 PERROR(
"Error retrieving keys %s", cursor);
1026 if (reply->type != REDIS_REPLY_ARRAY) {
1027 ERROR(
"Failed retrieving result, expected array got %s",
1033 if (reply->elements != 2) {
1034 ERROR(
"Failed retrieving result, expected array with two elements, got %zu elements",
1040 if (reply->element[0]->type != REDIS_REPLY_STRING) {
1041 ERROR(
"Failed retrieving result, expected string got %s",
1046 if (reply->element[1]->type != REDIS_REPLY_ARRAY) {
1047 ERROR(
"Failed retrieving result, expected array got %s",
1052 if ((talloc_array_length(result) -
used) < reply->element[1]->elements) {
1053 MEM(result = talloc_realloc(ctx, result,
uint8_t *,
1054 used + reply->element[1]->elements));
1056 ERROR(
"Failed expanding array of pool names");
1060 strlcpy(cursor, reply->element[0]->str,
sizeof(cursor));
1062 for (k = 0; k < reply->element[1]->elements; k++) {
1063 redisReply *pool_key = reply->element[1]->element[k];
1068 if (pool_key->len < 7)
continue;
1070 if ((pool_key->str[0]) !=
'{')
continue;
1071 p = memchr(pool_key->str + 1,
'}', pool_key->len - 1);
1074 len = (pool_key->len - ((p + 1) - pool_key->str));
1083 result[
used++] = talloc_memdup(result, pool_key->str + 1, (p - pool_key->str) - 1);
1087 }
while (!((cursor[0] ==
'0') && (cursor[1] ==
'\0')));
1104 memcpy(&to_sort, &result,
sizeof(to_sort));
1111 ERROR(
"Failed allocating file pool name array");
1122 (*out)[k++] = talloc_steal(*
out, result[i++]);
1123 while ((i <
used) && (
pool_cmp(result[i - 1], result[i]) == 0)) i++;
1144 redisReply **replies = NULL, *reply;
1145 unsigned int pipelined = 0;
1147 size_t reply_cnt = 0, i = 0;
1149#define STATS_COMMANDS_TOTAL 14
1162 redisAppendCommand(conn->
handle,
"MULTI");
1163 redisAppendCommand(conn->
handle,
"ZCARD %b", key, key_p - key);
1164 redisAppendCommand(conn->
handle,
"ZCOUNT %b -inf %i",
1166 redisAppendCommand(conn->
handle,
"ZCOUNT %b -inf %i",
1168 redisAppendCommand(conn->
handle,
"ZCOUNT %b -inf %i",
1170 redisAppendCommand(conn->
handle,
"ZCOUNT %b -inf %i",
1172 redisAppendCommand(conn->
handle,
"ZCOUNT %b -inf %i",
1190 redisAppendCommand(conn->
handle,
"EXEC");
1191 if (!replies)
return -1;
1195 talloc_array_length(replies), conn);
1197 replies[i], NULL, i, status);
1207 ERROR(
"Failed retrieving pool stats: Expected %i replies, got %zu", pipelined, reply_cnt);
1211 reply = replies[reply_cnt - 1];
1213 if (reply->type != REDIS_REPLY_ARRAY) {
1214 ERROR(
"Failed retrieving pool stats: Expected array got %s",
1219 if (reply->elements != (reply_cnt - 2)) {
1220 ERROR(
"Failed retrieving pool stats: Expected %zu results, got %zu", reply_cnt - 2, reply->elements);
1224 if (reply->element[0]->integer == 0) {
1225 ERROR(
"Pool not found");
1229 out->total = reply->element[0]->integer;
1230 out->free = reply->element[1]->integer;
1231 out->expiring_1m = reply->element[2]->integer -
out->free;
1232 out->expiring_30m = reply->element[3]->integer -
out->free;
1233 out->expiring_1h = reply->element[4]->integer -
out->free;
1234 out->expiring_1d = reply->element[5]->integer -
out->free;
1235 out->static_tot = reply->element[6]->integer;
1236 out->static_free = reply->element[7]->integer;
1237 out->static_1m = reply->element[8]->integer -
out->static_free;
1238 out->static_30m = reply->element[9]->integer -
out->static_free;
1239 out->static_1h = reply->element[10]->integer -
out->static_free;
1240 out->static_1d = reply->element[11]->integer -
out->static_free;
1261 if (!
this)
return -1;
1270 "rlm_redis_ippool_tool", NULL, NULL);
1271 if (!this->cluster) {
1296 p = strchr(ip_str,
'-');
1298 char start_buff[INET6_ADDRSTRLEN + 4];
1299 char end_buff[INET6_ADDRSTRLEN + 4];
1302 if ((
size_t)(p - ip_str) >=
sizeof(start_buff)) {
1303 ERROR(
"Start address too long");
1307 len =
strlcpy(start_buff, ip_str, (p - ip_str) + 1);
1309 ERROR(
"Start address too long");
1313 len =
strlcpy(end_buff, p + 1,
sizeof(end_buff));
1315 ERROR(
"End address too long");
1319 if (
fr_inet_pton(&start, start_buff, -1, AF_UNSPEC,
false,
true) < 0) {
1320 PERROR(
"Failed parsing \"%s\" as start address", start_buff);
1324 if (
fr_inet_pton(&end, end_buff, -1, AF_UNSPEC,
false,
true) < 0) {
1325 PERROR(
"Failed parsing \"%s\" end address", end_buff);
1329 if (start.
af != end.
af) {
1330 ERROR(
"Start and end address must be of the same address family");
1339 if (start.
af == AF_INET6) {
1340 uint128_t start_int, end_int;
1342 memcpy(&start_int, start.
addr.v6.s6_addr,
sizeof(start_int));
1343 memcpy(&end_int, end.
addr.v6.s6_addr,
sizeof(end_int));
1344 if (
uint128_gt(ntohlll(start_int), ntohlll(end_int))) {
1345 ERROR(
"End address must be greater than or equal to start address");
1354 ERROR(
"End address must be greater than or equal to start address");
1373 if (
fr_inet_pton(&start, ip_str, -1, AF_UNSPEC,
false,
false) < 0) {
1374 ERROR(
"Failed parsing \"%s\" as IPv4/v6 subnet", ip_str);
1380 if (prefix < start.
prefix) {
1381 ERROR(
"-p must be greater than or equal to /<mask> (%u)", start.
prefix);
1385 ERROR(
"-p must be less than or equal to address length (%u)",
IPADDR_LEN(start.
af));
1389 if ((prefix - start.
prefix) > 64) {
1390 ERROR(
"-p must be less than or equal to %u", start.
prefix + 64);
1398 ex_broadcast = (start.
af == AF_INET) && (
IPADDR_LEN(start.
af) == prefix);
1414 if (start.
af == AF_INET6) {
1415 uint128_t ip, p_mask;
1421 memcpy(&ip, start.
addr.v6.s6_addr,
sizeof(ip));
1430 memcpy(&end.
addr.v6.s6_addr, &ip,
sizeof(end.
addr.v6.s6_addr));
1437 ip = ntohl(start.
addr.v4.s_addr);
1443 if (ex_broadcast) ip--;
1444 end.
addr.v4.s_addr = htonl(ip);
1462 bool do_export =
false, print_stats =
false, list_pools =
false;
1463 bool need_pool =
false;
1464 char *do_import = NULL;
1465 char const *filename = NULL;
1466 char const *owner = NULL;
1479#define ADD_ACTION(_action) \
1482 ERROR("Too many actions, max is " STRINGIFY(sizeof(ops))); \
1485 p->action = _action; \
1491 while ((c = getopt(argc, argv,
"a:d:r:s:Sm:A:U:O:p:ilLhxo:f:")) != -1)
switch (c) {
1530 ERROR(
"Prefix may only be specified after a pool management action");
1534 tmp = strtoul(optarg, &q, 10);
1535 if (q != (optarg + strlen(optarg))) {
1536 ERROR(
"Prefix must be an integer value");
1540 (p - 1)->prefix = (
uint8_t)tmp & 0xff;
1553 if (list_pools)
usage(1);
1582 ERROR(
"Need server address/port");
1585 if ((argc == 1) && need_pool) {
1586 ERROR(
"Need pool to operate on");
1589 if (argc > 3)
usage(64);
1600 ERROR(
"Failed creating server pair");
1607 if (argv[1] && (argv[1][0] !=
'\0')) {
1611 MEM(fr_sbuff_init_talloc(
conf, &
out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1613 &
FR_SBUFF_IN(argv[1], strlen(argv[1])), SIZE_MAX,
'"');
1618 if (argc >= 3 && (argv[2][0] !=
'\0')) {
1622 MEM(fr_sbuff_init_talloc(
conf, &
out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1624 &
FR_SBUFF_IN(argv[2], strlen(argv[2])), SIZE_MAX,
'"');
1629 if (!do_import && !do_export && !list_pools && !print_stats && (p == ops)) {
1630 ERROR(
"Nothing to do!");
1668 ERROR(
"Driver initialisation failed");
1673 ERROR(
"NOT YET IMPLEMENTED");
1677 ERROR(
"NOT YET IMPLEMENTED");
1689 pools[0] = pool_arg;
1695 for (i = 0; i < (
size_t)slen; i++) {
1697 pools[i], talloc_array_length(pools[i])) < 0)
fr_exit_now(EXIT_FAILURE);
1700 talloc_array_length(pools[i])));
1703 INFO(
"dynamic free : %" PRIu64, stats.
free);
1706 INFO(
"dynamic used (%%) : %.2Lf",
1710 INFO(
"used (%%) : 0");
1720 INFO(
"static issued (%%) : %.2Lf",
1724 INFO(
"static issued (%%) : 0");
1742 for (i = 0; i < (
size_t)slen; i++) {
1756 for (p = ops; p < end; p++) {
1762 p->
pool_len = talloc_array_length(pool_arg);
1764 if (!p->
range && range_arg) {
1765 p->
range = range_arg;
1766 p->
range_len = talloc_array_length(range_arg);
1770 for (p = ops; (p < end) && (p->
start.
af != AF_UNSPEC); p++)
switch (p->
action) {
1778 INFO(
"Added %" PRIu64
" address(es)/prefix(es)",
count);
1789 INFO(
"Removed %" PRIu64
" address(es)/prefix(es)",
count);
1800 INFO(
"Released %" PRIu64
" address(es)/prefix(es)",
count);
1814 len = talloc_array_length(leases);
1815 INFO(
"Retrieved information for %zu address(es)/prefix(es)", len - 1);
1816 for (i = 0; i < (len - 1); i++) {
1821 char *device = NULL;
1822 char *gateway = NULL;
1829 is_active = now.tv_sec <= leases[i]->
next_event;
1830 if (leases[i]->next_event) {
1831 strftime(time_buff,
sizeof(time_buff),
"%b %e %Y %H:%M:%S %Z",
1834 time_buff[0] =
'\0';
1838 if (leases[i]->range) {
1839 range =
fr_asprint(leases, (
char const *)leases[i]->range,
1840 leases[i]->range_len,
'\0');
1844 if (range)
INFO(
"range : %s", range);
1845 INFO(
"address/prefix : %s", ip_buff);
1846 INFO(
"active : %s", is_active ?
"yes" :
"no");
1848 if (leases[i]->device) {
1849 device =
fr_asprint(leases, (
char const *)leases[i]->device,
1850 leases[i]->device_len,
'\0');
1852 if (leases[i]->gateway) {
1853 gateway =
fr_asprint(leases, (
char const *)leases[i]->gateway,
1854 leases[i]->gateway_len,
'\0');
1857 if (*time_buff)
INFO(
"lease expires : %s", time_buff);
1858 if (device)
INFO(
"device id : %s", device);
1859 if (gateway)
INFO(
"gateway id : %s", gateway);
1861 if (*time_buff)
INFO(
"lease expired : %s", time_buff);
1862 if (device)
INFO(
"last device id : %s", device);
1863 if (gateway)
INFO(
"last gateway id : %s", gateway);
1877 INFO(
"Modified %" PRIu64
" address(es)/prefix(es)",
count);
1886 ERROR(
"Static assignment requires a single IP");
1890 ERROR(
"Static assignment requires an owner");
1897 INFO(
"Assigned %" PRIu64
" address(es)/prefix(es)",
count);
1906 ERROR(
"Static lease un-assignment requires a single IP");
1910 ERROR(
"Static lease un-assignment requires an owner");
1917 INFO(
"Un-assigned %" PRIu64
" address(es)/prefix(es)",
count);
#define NEVER_RETURNS
Should be placed before the function return type.
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
int cf_file_read(CONF_SECTION *cs, char const *filename)
int cf_section_pass2(CONF_SECTION *cs)
int cf_section_parse(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs)
Parse a configuration section into user-supplied variables.
#define CONF_PARSER_TERMINATOR
#define cf_section_rules_push(_cs, _rule)
Defines a CONF_PAIR to C data type mapping.
Configuration AVP similar to a fr_pair_t.
A section grouping multiple CONF_PAIR.
CONF_PAIR * cf_pair_alloc(CONF_SECTION *parent, char const *attr, char const *value, fr_token_t op, fr_token_t lhs_quote, fr_token_t rhs_quote)
Allocate a CONF_PAIR.
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
fr_redis_cluster_t * fr_redis_cluster_alloc(TALLOC_CTX *ctx, CONF_SECTION *module, fr_redis_conf_t *conf, bool triggers_enabled, char const *log_prefix, char const *trigger_prefix, fr_pair_list_t *trigger_args)
Allocate and initialise a new cluster structure.
fr_redis_rcode_t fr_redis_cluster_state_next(fr_redis_cluster_state_t *state, fr_redis_conn_t **conn, fr_redis_cluster_t *cluster, request_t *request, fr_redis_rcode_t status, redisReply **reply)
Get the next connection to attempt a command against.
int fr_redis_cluster_pool_by_node_addr(fr_pool_t **pool, fr_redis_cluster_t *cluster, fr_socket_t *node_addr, bool create)
Get the pool associated with a node in the cluster.
ssize_t fr_redis_cluster_node_addr_by_role(TALLOC_CTX *ctx, fr_socket_t *out[], fr_redis_cluster_t *cluster, bool is_master, bool is_slave)
Return an array of IP addresses belonging to masters or slaves.
fr_redis_rcode_t fr_redis_cluster_state_init(fr_redis_cluster_state_t *state, fr_redis_conn_t **conn, fr_redis_cluster_t *cluster, request_t *request, uint8_t const *key, size_t key_len, bool read_only)
Resolve a key to a pool, and reserve a connection in that pool.
Common functions for interacting with Redis cluster via Hiredis.
Redis connection sequence state.
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
static NEVER_RETURNS void usage(void)
int fr_inet_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, int af, bool resolve, bool mask)
Simple wrapper to decide whether an IP value is v4 or v6 and call the appropriate parser.
int8_t fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
void fr_ipaddr_mask(fr_ipaddr_t *addr, uint8_t prefix)
Zeroes out the host portion of an fr_ipaddr_t.
uint8_t prefix
Prefix length - Between 0-32 for IPv4 and 0-128 for IPv6.
union fr_ipaddr_t::@131 addr
#define FR_IPADDR_PREFIX_STRLEN
Like FR_IPADDR_STRLEN but with space for a prefix.
@ L_DBG_LVL_3
3rd highest priority debug messages (-xxx | -Xx).
void fr_quick_sort(void const *to_sort[], int start, int end, fr_cmp_t cmp)
Quick sort an array of pointers using a comparator.
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
void fr_pool_connection_release(fr_pool_t *pool, request_t *request, void *conn)
Release a connection.
void * fr_pool_connection_get(fr_pool_t *pool, request_t *request)
Reserve a connection in the connection pool.
char * fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
Escape string that may contain binary data, and write it to a new buffer.
#define is_truncated(_ret, _max)
void fr_redis_reply_print(fr_log_lvl_t lvl, redisReply *reply, request_t *request, int idx, fr_redis_rcode_t status)
Print the response data in a useful treelike form.
static void fr_redis_pipeline_free(redisReply *reply[], size_t num)
redisContext * handle
Hiredis context used when issuing commands.
#define REDIS_COMMON_CONFIG
static void fr_redis_reply_free(redisReply **reply)
Wrap freeReplyObject so we consistently check for NULL pointers.
fr_redis_rcode_t fr_redis_command_status(fr_redis_conn_t *conn, redisReply *reply)
Check the reply for errors.
fr_table_num_sorted_t const redis_reply_types[]
fr_redis_rcode_t fr_redis_pipeline_result(unsigned int *pipelined, fr_redis_rcode_t *rcode, redisReply *out[], size_t out_len, fr_redis_conn_t *conn)
Simplifies handling of pipelined commands with Redis cluster.
fr_redis_rcode_t
Codes are ordered inversely by priority.
@ REDIS_RCODE_SUCCESS
Operation was successful.
@ REDIS_RCODE_TRY_AGAIN
Try the operation again.
Configuration parameters for a redis connection.
Connection handle, holding a redis context.
#define IPPOOL_STATIC_BIT
#define IPPOOL_MAX_POOL_KEY_SIZE
{prefix}:pool
#define IPPOOL_SPRINT_IP(_buff, _ip, _prefix)
If the prefix is as wide as the AF data size then print it without CIDR notation.
#define IPPOOL_ADDRESS_KEY
#define IPPOOL_MAX_IP_KEY_SIZE
{prefix}:ipaddr/prefix
#define IPPOOL_BUILD_KEY(_buff, _p, _key, _key_len)
Wrap the prefix in {} and add the pool suffix.
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_used(_sbuff_or_marker)
Talloc sbuff extension structure.
eap_aka_sim_process_conf_t * inst
#define fr_time()
Allow us to arbitrarily manipulate time.
size_t strlcpy(char *dst, char const *src, size_t siz)
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
static int64_t fr_time_to_sec(fr_time_t when)
Convert an fr_time_t (internal time) to number of sec since the unix epoch (wallclock time)
#define fr_time_to_timeval(_when)
Convert server epoch time to unix epoch time.
static uint128_t uint128_new(uint64_t h, uint64_t l)
Creates a new uint128_t from a uint64_t.
static uint128_t uint128_gen_mask(uint8_t bits)
Create a 128 bit integer value with n bits high.
static uint128_t uint128_bor(uint128_t a, uint128_t b)
Perform bitwise | of two 128bit unsigned integers.
static uint128_t uint128_lshift(uint128_t num, uint8_t bits)
Left shift 128 bit integer.
static bool uint128_gt(uint128_t a, uint128_t b)
Return whether one integer is greater than the other.
static bool uint128_eq(uint128_t a, uint128_t b)
Return whether the integers are equal.
static uint128_t uint128_sub(uint128_t a, uint128_t b)
Subtract one 128bit integer from another.
static uint128_t uint128_add(uint128_t a, uint128_t b)
Add two 128bit unsigned integers.
Holds information necessary for binding or connecting to a socket.
size_t fr_value_str_unescape(fr_sbuff_t *out, fr_sbuff_t *in, size_t inlen, char quote)
Convert a string value with escape sequences into its binary form.
#define fr_box_strvalue_len(_val, _len)
static size_t char ** out