27 RCSID(
"$Id: 656d4f7b0a594028fd32bd57926e6a09e0deba4b $")
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);
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);
927 static 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";
994 ERROR(
"Failed retrieving pool for node");
1002 if (!conn)
goto error;
1008 reply = redisCommand(conn->
handle,
"SCAN %s MATCH %b COUNT 20", cursor, key, key_p - key);
1010 ERROR(
"Failed reading reply");
1016 PERROR(
"Error retrieving keys %s", cursor);
1024 if (reply->type != REDIS_REPLY_ARRAY) {
1025 ERROR(
"Failed retrieving result, expected array got %s",
1031 if (reply->elements != 2) {
1032 ERROR(
"Failed retrieving result, expected array with two elements, got %zu elements",
1038 if (reply->element[0]->type != REDIS_REPLY_STRING) {
1039 ERROR(
"Failed retrieving result, expected string got %s",
1044 if (reply->element[1]->type != REDIS_REPLY_ARRAY) {
1045 ERROR(
"Failed retrieving result, expected array got %s",
1050 if ((talloc_array_length(result) -
used) < reply->element[1]->elements) {
1051 MEM(result = talloc_realloc(ctx, result,
uint8_t *,
1052 used + reply->element[1]->elements));
1054 ERROR(
"Failed expanding array of pool names");
1058 strlcpy(cursor, reply->element[0]->str,
sizeof(cursor));
1060 for (k = 0; k < reply->element[1]->elements; k++) {
1061 redisReply *pool_key = reply->element[1]->element[k];
1066 if (pool_key->len < 7)
continue;
1068 if ((pool_key->str[0]) !=
'{')
continue;
1069 p = memchr(pool_key->str + 1,
'}', pool_key->len - 1);
1072 len = (pool_key->len - ((p + 1) - pool_key->str));
1081 result[
used++] = talloc_memdup(result, pool_key->str + 1, (p - pool_key->str) - 1);
1085 }
while (!((cursor[0] ==
'0') && (cursor[1] ==
'\0')));
1102 memcpy(&to_sort, &result,
sizeof(to_sort));
1109 ERROR(
"Failed allocating file pool name array");
1120 (*out)[k++] = talloc_steal(*
out, result[i++]);
1121 while ((i <
used) && (
pool_cmp(result[i - 1], result[i]) == 0)) i++;
1142 redisReply **replies = NULL, *reply;
1143 unsigned int pipelined = 0;
1145 size_t reply_cnt = 0, i = 0;
1147 #define STATS_COMMANDS_TOTAL 14
1160 redisAppendCommand(conn->
handle,
"MULTI");
1161 redisAppendCommand(conn->
handle,
"ZCARD %b", key, key_p - key);
1162 redisAppendCommand(conn->
handle,
"ZCOUNT %b -inf %i",
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",
1188 redisAppendCommand(conn->
handle,
"EXEC");
1189 if (!replies)
return -1;
1193 talloc_array_length(replies), conn);
1195 replies[i], NULL, i);
1205 ERROR(
"Failed retrieving pool stats: Expected %i replies, got %zu", pipelined, reply_cnt);
1209 reply = replies[reply_cnt - 1];
1211 if (reply->type != REDIS_REPLY_ARRAY) {
1212 ERROR(
"Failed retrieving pool stats: Expected array got %s",
1217 if (reply->elements != (reply_cnt - 2)) {
1218 ERROR(
"Failed retrieving pool stats: Expected %zu results, got %zu", reply_cnt - 2, reply->elements);
1222 if (reply->element[0]->integer == 0) {
1223 ERROR(
"Pool not found");
1227 out->total = reply->element[0]->integer;
1228 out->free = reply->element[1]->integer;
1229 out->expiring_1m = reply->element[2]->integer -
out->free;
1230 out->expiring_30m = reply->element[3]->integer -
out->free;
1231 out->expiring_1h = reply->element[4]->integer -
out->free;
1232 out->expiring_1d = reply->element[5]->integer -
out->free;
1233 out->static_tot = reply->element[6]->integer;
1234 out->static_free = reply->element[7]->integer;
1235 out->static_1m = reply->element[8]->integer -
out->static_free;
1236 out->static_30m = reply->element[9]->integer -
out->static_free;
1237 out->static_1h = reply->element[10]->integer -
out->static_free;
1238 out->static_1d = reply->element[11]->integer -
out->static_free;
1259 if (!
this)
return -1;
1268 "rlm_redis_ippool_tool", NULL, NULL);
1269 if (!this->cluster) {
1294 p = strchr(ip_str,
'-');
1296 char start_buff[INET6_ADDRSTRLEN + 4];
1297 char end_buff[INET6_ADDRSTRLEN + 4];
1300 if ((
size_t)(p - ip_str) >=
sizeof(start_buff)) {
1301 ERROR(
"Start address too long");
1305 len =
strlcpy(start_buff, ip_str, (p - ip_str) + 1);
1307 ERROR(
"Start address too long");
1311 len =
strlcpy(end_buff, p + 1,
sizeof(end_buff));
1313 ERROR(
"End address too long");
1317 if (
fr_inet_pton(&start, start_buff, -1, AF_UNSPEC,
false,
true) < 0) {
1318 PERROR(
"Failed parsing \"%s\" as start address", start_buff);
1322 if (
fr_inet_pton(&end, end_buff, -1, AF_UNSPEC,
false,
true) < 0) {
1323 PERROR(
"Failed parsing \"%s\" end address", end_buff);
1327 if (start.
af != end.
af) {
1328 ERROR(
"Start and end address must be of the same address family");
1337 if (start.
af == AF_INET6) {
1338 uint128_t start_int, end_int;
1340 memcpy(&start_int, start.
addr.v6.s6_addr,
sizeof(start_int));
1341 memcpy(&end_int, end.
addr.v6.s6_addr,
sizeof(end_int));
1342 if (
uint128_gt(ntohlll(start_int), ntohlll(end_int))) {
1343 ERROR(
"End address must be greater than or equal to start address");
1352 ERROR(
"End address must be greater than or equal to start address");
1371 if (
fr_inet_pton(&start, ip_str, -1, AF_UNSPEC,
false,
false) < 0) {
1372 ERROR(
"Failed parsing \"%s\" as IPv4/v6 subnet", ip_str);
1378 if (prefix < start.
prefix) {
1379 ERROR(
"-p must be greater than or equal to /<mask> (%u)", start.
prefix);
1383 ERROR(
"-p must be less than or equal to address length (%u)",
IPADDR_LEN(start.
af));
1387 if ((prefix - start.
prefix) > 64) {
1388 ERROR(
"-p must be less than or equal to %u", start.
prefix + 64);
1396 ex_broadcast = (start.
af == AF_INET) && (
IPADDR_LEN(start.
af) == prefix);
1412 if (start.
af == AF_INET6) {
1413 uint128_t ip, p_mask;
1419 memcpy(&ip, start.
addr.v6.s6_addr,
sizeof(ip));
1428 memcpy(&end.
addr.v6.s6_addr, &ip,
sizeof(end.
addr.v6.s6_addr));
1435 ip = ntohl(start.
addr.v4.s_addr);
1441 if (ex_broadcast) ip--;
1442 end.
addr.v4.s_addr = htonl(ip);
1460 bool do_export =
false, print_stats =
false, list_pools =
false;
1461 bool need_pool =
false;
1462 char *do_import = NULL;
1463 char const *filename = NULL;
1464 char const *owner = NULL;
1477 #define ADD_ACTION(_action) \
1480 ERROR("Too many actions, max is " STRINGIFY(sizeof(ops))); \
1483 p->action = _action; \
1489 while ((c = getopt(argc, argv,
"a:d:r:s:Sm:A:U:O:p:ilLhxo:f:")) != -1)
switch (c) {
1528 ERROR(
"Prefix may only be specified after a pool management action");
1532 tmp = strtoul(optarg, &q, 10);
1533 if (q != (optarg + strlen(optarg))) {
1534 ERROR(
"Prefix must be an integer value");
1538 (p - 1)->prefix = (
uint8_t)tmp & 0xff;
1551 if (list_pools)
usage(1);
1580 ERROR(
"Need server address/port");
1583 if ((argc == 1) && need_pool) {
1584 ERROR(
"Need pool to operate on");
1587 if (argc > 3)
usage(64);
1598 ERROR(
"Failed creating server pair");
1605 if (argv[1] && (argv[1][0] !=
'\0')) {
1609 MEM(fr_sbuff_init_talloc(
conf, &
out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1611 &
FR_SBUFF_IN(argv[1], strlen(argv[1])), SIZE_MAX,
'"');
1616 if (argc >= 3 && (argv[2][0] !=
'\0')) {
1620 MEM(fr_sbuff_init_talloc(
conf, &
out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1622 &
FR_SBUFF_IN(argv[2], strlen(argv[2])), SIZE_MAX,
'"');
1627 if (!do_import && !do_export && !list_pools && !print_stats && (p == ops)) {
1628 ERROR(
"Nothing to do!");
1666 ERROR(
"Driver initialisation failed");
1671 ERROR(
"NOT YET IMPLEMENTED");
1675 ERROR(
"NOT YET IMPLEMENTED");
1687 pools[0] = pool_arg;
1693 for (i = 0; i < (
size_t)slen; i++) {
1695 pools[i], talloc_array_length(pools[i])) < 0)
fr_exit_now(EXIT_FAILURE);
1698 talloc_array_length(pools[i])));
1700 INFO(
"dynamic total : %" PRIu64,
stats.total -
stats.static_tot);
1701 INFO(
"dynamic free : %" PRIu64,
stats.free);
1704 INFO(
"dynamic used (%%) : %.2Lf",
1706 (
long double)(
stats.total -
stats.static_tot)) * 100);
1708 INFO(
"used (%%) : 0");
1710 INFO(
"expiring 0-1m : %" PRIu64,
stats.expiring_1m);
1711 INFO(
"expiring 1-30m : %" PRIu64,
stats.expiring_30m -
stats.expiring_1m);
1712 INFO(
"expiring 30m-1h : %" PRIu64,
stats.expiring_1h -
stats.expiring_30m);
1713 INFO(
"expiring 1h-1d : %" PRIu64,
stats.expiring_1d -
stats.expiring_1h);
1714 INFO(
"static total : %" PRIu64,
stats.static_tot);
1715 INFO(
"static 'free' : %" PRIu64,
stats.static_free);
1716 INFO(
"static issued : %" PRIu64,
stats.static_tot -
stats.static_free);
1717 if (
stats.static_tot) {
1718 INFO(
"static issued (%%) : %.2Lf",
1719 ((
long double)(
stats.static_tot -
stats.static_free) /
1720 (
long double)(
stats.static_tot)) * 100);
1722 INFO(
"static issued (%%) : 0");
1724 INFO(
"static renew 0-1m : %" PRIu64,
stats.static_1m);
1725 INFO(
"static renew 1-30m : %" PRIu64,
stats.static_30m -
stats.static_1m);
1726 INFO(
"static renew 30m-1h : %" PRIu64,
stats.static_1h -
stats.static_30m);
1727 INFO(
"static renew 1h-1d : %" PRIu64,
stats.static_1d -
stats.static_1h);
1740 for (i = 0; i < (
size_t)slen; i++) {
1754 for (p = ops; p < end; p++) {
1760 p->
pool_len = talloc_array_length(pool_arg);
1762 if (!p->
range && range_arg) {
1763 p->
range = range_arg;
1764 p->
range_len = talloc_array_length(range_arg);
1768 for (p = ops; (p < end) && (p->
start.
af != AF_UNSPEC); p++)
switch (p->
action) {
1776 INFO(
"Added %" PRIu64
" address(es)/prefix(es)",
count);
1787 INFO(
"Removed %" PRIu64
" address(es)/prefix(es)",
count);
1798 INFO(
"Released %" PRIu64
" address(es)/prefix(es)",
count);
1812 len = talloc_array_length(leases);
1813 INFO(
"Retrieved information for %zu address(es)/prefix(es)", len - 1);
1814 for (i = 0; i < (len - 1); i++) {
1819 char *device = NULL;
1820 char *gateway = NULL;
1827 is_active = now.tv_sec <= leases[i]->
next_event;
1828 if (leases[i]->next_event) {
1829 strftime(time_buff,
sizeof(time_buff),
"%b %e %Y %H:%M:%S %Z",
1832 time_buff[0] =
'\0';
1836 if (leases[i]->range) {
1837 range =
fr_asprint(leases, (
char const *)leases[i]->range,
1838 leases[i]->range_len,
'\0');
1842 if (range)
INFO(
"range : %s", range);
1843 INFO(
"address/prefix : %s", ip_buff);
1844 INFO(
"active : %s", is_active ?
"yes" :
"no");
1846 if (leases[i]->device) {
1847 device =
fr_asprint(leases, (
char const *)leases[i]->device,
1848 leases[i]->device_len,
'\0');
1850 if (leases[i]->gateway) {
1851 gateway =
fr_asprint(leases, (
char const *)leases[i]->gateway,
1852 leases[i]->gateway_len,
'\0');
1855 if (*time_buff)
INFO(
"lease expires : %s", time_buff);
1856 if (device)
INFO(
"device id : %s", device);
1857 if (gateway)
INFO(
"gateway id : %s", gateway);
1859 if (*time_buff)
INFO(
"lease expired : %s", time_buff);
1860 if (device)
INFO(
"last device id : %s", device);
1861 if (gateway)
INFO(
"last gateway id : %s", gateway);
1875 INFO(
"Modified %" PRIu64
" address(es)/prefix(es)",
count);
1884 ERROR(
"Static assignment requires a single IP");
1888 ERROR(
"Static assignment requires an owner");
1895 INFO(
"Assigned %" PRIu64
" address(es)/prefix(es)",
count);
1904 ERROR(
"Static lease un-assignment requires a single IP");
1908 ERROR(
"Static lease un-assignment requires an owner");
1915 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_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.
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.
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
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.
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.
Common functions for interacting with Redis cluster via Hiredis.
Redis connection sequence state.
fr_dcursor_eval_t void const * uctx
#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.
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.
#define FR_IPADDR_PREFIX_STRLEN
Like FR_IPADDR_STRLEN but with space for a prefix.
union fr_ipaddr_t::@130 addr
@ 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)
static void fr_redis_pipeline_free(redisReply *reply[], size_t num)
redisContext * handle
Hiredis context used when issuing commands.
#define REDIS_COMMON_CONFIG
void fr_redis_reply_print(fr_log_lvl_t lvl, redisReply *reply, request_t *request, int idx)
Print the response data in a useful treelike form.
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.
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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