The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_redis_ippool_tool.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: fd2a5980fbcbcb7e44ab962933bd0d3632833c1c $
19 * @file rlm_redis_ippool_tool.c
20 * @brief IP population tool.
21 *
22 * @author Arran Cudbard-Bell
23 *
24 * @copyright 2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25 * @copyright 2015 The FreeRADIUS server project
26 */
27RCSID("$Id: fd2a5980fbcbcb7e44ab962933bd0d3632833c1c $")
28#include <freeradius-devel/server/cf_parse.h>
29#include <freeradius-devel/util/debug.h>
30
31#include "base.h"
32#include "cluster.h"
33#include "redis_ippool.h"
34
35#define MAX_PIPELINED 100000
36
37/** Pool management actions
38 *
39 */
40typedef enum ippool_tool_action {
41 IPPOOL_TOOL_NOOP = 0, //!< Do nothing.
42 IPPOOL_TOOL_ADD, //!< Add one or more IP addresses.
43 IPPOOL_TOOL_REMOVE, //!< Remove one or more IP addresses.
44 IPPOOL_TOOL_RELEASE, //!< Release one or more IP addresses.
45 IPPOOL_TOOL_SHOW, //!< Show one or more IP addresses.
46 IPPOOL_TOOL_MODIFY, //!< Modify attributes of one or more IP addresses.
47 IPPOOL_TOOL_ASSIGN, //!< Assign a static IP address to a device.
48 IPPOOL_TOOL_UNASSIGN //!< Remove static IP address assignment.
50
51/** A single pool operation
52 *
53 */
54typedef struct {
55 char const *name; //!< Original range or CIDR string.
56
57 uint8_t const *pool; //!< Pool identifier.
58 size_t pool_len; //!< Length of the pool identifier.
59
60 uint8_t const *range; //!< Range identifier.
61 size_t range_len; //!< Length of the range identifier.
62
63 fr_ipaddr_t start; //!< Start address.
64 fr_ipaddr_t end; //!< End address.
65 uint8_t prefix; //!< Prefix - The bits between the address mask, and the prefix
66 //!< form the addresses to be modified in the pool.
67 ippool_tool_action_t action; //!< What to do to the leases described by net/prefix.
69
70typedef struct {
71 fr_ipaddr_t ipaddr; //!< Prefix or address.
72 time_t next_event; //!< Last state change.
73 uint8_t const *range; //!< Range the lease belongs to.
74 size_t range_len;
75 uint8_t const *device; //!< Last device id.
76 size_t device_len;
77 uint8_t const *gateway; //!< Last gateway id.
80
81typedef struct {
82 uint64_t total; //!< Addresses available.
83 uint64_t free; //!< Addresses in use.
84 uint64_t expiring_1m; //!< Addresses that expire in the next minute.
85 uint64_t expiring_30m; //!< Addresses that expire in the next 30 minutes.
86 uint64_t expiring_1h; //!< Addresses that expire in the next hour.
87 uint64_t expiring_1d; //!< Addresses that expire in the next day.
88 uint64_t static_tot; //!< Static assignments configured.
89 uint64_t static_free; //!< Static leases that have not been requested.
90 uint64_t static_1m; //!< Static leases that should renew in the next minute.
91 uint64_t static_30m; //!< Static leases that should renew in the next 30 minutes.
92 uint64_t static_1h; //!< Static leases that should renew in the next hour.
93 uint64_t static_1d; //!< Static leases that should renew in the next day.
95
100
101typedef struct {
102 fr_redis_conf_t conf; //!< Connection parameters for the Redis server.
105
106typedef struct {
107 void *driver;
110
111typedef struct {
112 char const *owner;
114
116 uint8_t const *key_prefix, size_t key_prefix_len,
117 uint8_t const *range, size_t range_len,
118 fr_ipaddr_t *ipaddr, uint8_t prefix, void *uctx);
119
120typedef int (*redis_ippool_process_t)(void *out, fr_ipaddr_t const *ipaddr, redisReply const *reply);
121
122#define IPPOOL_BUILD_IP_KEY_FROM_STR(_buff, _p, _key, _key_len, _ip_str) \
123do { \
124 ssize_t _slen; \
125 *_p++ = '{'; \
126 memcpy(_p, _key, _key_len); \
127 _p += _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"); \
131 return 0;\
132 } \
133 _p += (size_t)_slen;\
134 _p += strlcpy((char *)_p, _ip_str, sizeof(_buff) - (_p - _buff)); \
135} while (0)
136
137#if 0
138#define IPPOOL_BUILD_OWNER_KEY(_buff, _p, _key, _key_len, _owner) \
139do { \
140 ssize_t _slen; \
141 *_p++ = '{'; \
142 memcpy(_p, _key, _key_len); \
143 _p += _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"); \
147 return 0;\
148 } \
149 _p += (size_t)_slen;\
150 _p += strlcpy((char *)_p, _owner, sizeof(_buff) - (_p - _buff)); \
151} while (0)
152#endif
153
154#define EOL "\n"
155
156static char const *name;
157/** Lua script for releasing a lease
158 *
159 * - KEYS[1] The pool name.
160 * - ARGV[1] IP address to release.
161 *
162 * Removes the IP entry in the ZSET, then removes the address hash, and the device key
163 * if one exists.
164 *
165 * Will do nothing if the lease is not found in the ZSET.
166 *
167 * Returns
168 * - 0 if no ip addresses were removed.
169 * - 1 if an ip address was removed.
170 */
171static char lua_release_cmd[] =
172 "local found" EOL /* 1 */
173 "local ret" EOL /* 2 */
174
175 /*
176 * Set expiry time to 0
177 */
178 "ret = redis.call('ZADD', '{' .. KEYS[1] .. '}:"IPPOOL_POOL_KEY"', 'XX', 'CH', 0, ARGV[1])" EOL /* 3 */
179 "if ret == 0 then" EOL /* 4 */
180 " return 0" EOL /* 5 */
181 "end" EOL /* 6 */
182 "found = redis.call('HGET', '{' .. KEYS[1] .. '}:"IPPOOL_ADDRESS_KEY":'"
183 " .. ARGV[1], 'device')" EOL /* 7 */
184 "if not found then" EOL /* 8 */
185 " return ret" EOL /* 9 */
186 "end" EOL /* 10 */
187
188 /*
189 * Remove the association between the device and a lease
190 */
191 "redis.call('DEL', '{' .. KEYS[1] .. '}:"IPPOOL_OWNER_KEY":' .. found)" EOL /* 11 */
192 "return 1"; /* 12 */
193
194/** Lua script for assigning a static lease
195 *
196 * - KEYS[1] The pool name.
197 * - ARGV[1] THE ip address to create a static assignment for.
198 * - ARGV[2] The owner to assign the static lease to.
199 * - ARGV[3] The range identifier.
200 * - ARGV[4] Wall time (seconds since epoch)
201 *
202 * Checks whether the IP already has a static assignment, and
203 * whether the owner is already associated with a different IP.
204 *
205 * If check pass, sets the static flag on the IP entry in the ZSET and
206 * creates the association between the IP and the owner.
207 *
208 * Returns
209 * - 0 if no assignment is made.
210 * - 1 if the IP assignment is made.
211 */
212static char lua_assign_cmd[] =
213 "local pool_key = '{' .. KEYS[1] .. '}:"IPPOOL_POOL_KEY"'" EOL /* 1 */
214 "local owner_key = '{' .. KEYS[1] .. '}:"IPPOOL_OWNER_KEY":' .. ARGV[2]" EOL /* 2 */
215 "local ip_key = '{' .. KEYS[1]..'}:"IPPOOL_ADDRESS_KEY":' .. ARGV[1]" EOL /* 3 */
216
217 /*
218 * Check the address doesn't already have a static assignment.
219 */
220 "local expires = tonumber(redis.call('ZSCORE', pool_key, ARGV[1]))" EOL /* 4 */
221 "if expires and expires >= " STRINGIFY(IPPOOL_STATIC_BIT) " then" EOL /* 5 */
222 " return 0" EOL /* 6 */
223 "end" EOL /* 7 */
224
225 /*
226 * Check current assignment for device.
227 */
228 "local found = redis.call('GET', owner_key)" EOL /* 8 */
229 "if found and found ~= ARGV[1] then" EOL /* 9 */
230 " return 0" EOL /* 10 */
231 "end" EOL /* 11 */
232
233 /*
234 * If expires is in the future, check it is not
235 * another owner.
236 */
237 "if expires and expires > tonumber(ARGV[4]) then" EOL /* 12 */
238 " found = redis.call('HGET', ip_key, 'device')" /* 13 */
239 " if found and found ~= ARGV[2] then" EOL /* 14 */
240 " return 0" EOL /* 15 */
241 " end" EOL /* 16 */
242 "end" EOL /* 17 */
243
244 /*
245 * All checks passed - set the assignment.
246 */
247 "expires = (expires or 0) + " STRINGIFY(IPPOOL_STATIC_BIT) EOL /* 18 */
248 "redis.call('ZADD', pool_key, 'CH', expires, ARGV[1])" EOL /* 19 */
249 "redis.call('SET', owner_key, ARGV[1])" EOL /* 20 */
250 "redis.call('HSET', ip_key, 'device', ARGV[2], 'counter', 0)" EOL /* 21 */
251 "if ARGV[3] then" EOL /* 22 */
252 " redis.call('HSET', ip_key, 'range', ARGV[3])" EOL /* 23 */
253 "end" EOL /* 24 */
254 "return 1"; /* 25 */
255
256/** Lua script for un-assigning a static lease
257 *
258 * - KEYS[1] The pool name.
259 * - ARGV[1] IP address to remove static lease from.
260 * - ARGV[2] The owner the static lease should be removed from.
261 * - ARGV[3] Wall time (seconds since epoch).
262 *
263 * Removes the static flag from the IP entry in the ZSET, then, depending on the remaining time
264 * determined by the ZSCORE removes the address hash, and the device key.
265 *
266 * Will do nothing if the static assignment does not exist or the IP and device do not match.
267 *
268 * Returns
269 * - 0 if no ip addresses were unassigned.
270 * - 1 if an ip address was unassigned.
271 */
272static char lua_unassign_cmd[] =
273 "local found" EOL /* 1 */
274 "local pool_key = '{' .. KEYS[1] .. '}:"IPPOOL_POOL_KEY"'" EOL /* 2 */
275 "local owner_key = '{' .. KEYS[1] .. '}:"IPPOOL_OWNER_KEY":' .. ARGV[2]" EOL /* 3 */
276
277 /*
278 * Check that the device hash exists and points at the correct IP
279 */
280 "found = redis.call('GET', owner_key)" EOL /* 4 */
281 "if not found or found ~= ARGV[1] then" EOL /* 5 */
282 " return 0" EOL /* 6 */
283 "end" EOL /* 7 */
284
285 /*
286 * Check the assignment is actually static
287 */
288 "local expires = tonumber(redis.call('ZSCORE', pool_key, ARGV[1]))" EOL /* 8 */
289 "local static = expires >= " STRINGIFY(IPPOOL_STATIC_BIT) EOL /* 9 */
290 "if not static then" EOL /* 10 */
291 " return 0" EOL /* 11 */
292 "end" EOL /* 12 */
293
294 /*
295 * Remove static bit from ZSCORE
296 */
297 "expires = expires - " STRINGIFY(IPPOOL_STATIC_BIT) EOL /* 13 */
298 "redis.call('ZADD', pool_key, 'XX', expires, ARGV[1])" EOL /* 14 */
299
300 /*
301 * If the lease still has time left, set an expiry on the device key.
302 * otherwise delete it.
303 */
304 "if expires > tonumber(ARGV[3]) then" EOL /* 15 */
305 " redis.call('EXPIRE', owner_key, expires - tonumber(ARGV[3]))" EOL /* 16 */
306 "else" EOL /* 17 */
307 " redis.call('DEL', owner_key)" EOL /* 18 */
308 "end" EOL /* 19 */
309 "return 1"; /* 20 */
310
311/** Lua script for removing a lease
312 *
313 * - KEYS[1] The pool name.
314 * - ARGV[1] IP address to remove.
315 *
316 * Removes the IP entry in the ZSET, then removes the address hash, and the device key
317 * if one exists.
318 *
319 * Will work with partially removed IP addresses (where the ZSET entry is absent but other
320 * elements weren't cleaned up).
321 *
322 * Returns
323 * - 0 if no ip addresses were removed.
324 * - 1 if an ip address was removed.
325 */
326static char lua_remove_cmd[] =
327 "local found" EOL /* 1 */
328 "local ret" EOL /* 2 */
329 "local address_key" EOL /* 3 */
330
331 "ret = redis.call('ZREM', '{' .. KEYS[1] .. '}:"IPPOOL_POOL_KEY"', ARGV[1])" EOL /* 4 */
332 "address_key = '{' .. KEYS[1] .. '}:"IPPOOL_ADDRESS_KEY":' .. ARGV[1]" EOL /* 5 */
333 "found = redis.call('HGET', address_key, 'device')" EOL /* 6 */
334 "redis.call('DEL', address_key)" EOL /* 7 */
335 "if not found then" EOL /* 8 */
336 " return ret" EOL /* 9 */
337 "end" EOL /* 10 */
338
339 /*
340 * Remove the association between the device and a lease
341 */
342 "redis.call('DEL', '{' .. KEYS[1] .. '}:"IPPOOL_OWNER_KEY":' .. found)" EOL /* 11 */
343 "return 1" EOL; /* 12 */
344
345static NEVER_RETURNS void usage(int ret) {
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.");
364// INFO(" -L List available ranges in pool [NYI]");
365// INFO(" -i file Import entries from ISC lease file [NYI]");
366 INFO(" "); /* -Werror=format-zero-length */
367// INFO("Pool status:");
368// INFO(" -I Output active entries in ISC lease file format [NYI]");
369 INFO(" -S Print pool statistics");
370 INFO(" "); /* -Werror=format-zero-length */
371 INFO("Configuration:");
372 INFO(" -h Print this help message and exit");
373 INFO(" -x Increase the verbosity level");
374// INFO(" -o attr=value Set option, these are specific to the backends [NYI]");
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`");
378 INFO(" ");
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");
381 fr_exit_now(ret);
382}
383
385{
386 if (bits >= 32) return 0xffffffff;
387 return (1 << bits) - 1;
388}
389
390/** Iterate over range of IP addresses
391 *
392 * Mutates the ipaddr passed in, adding one to the prefix bits on each call.
393 *
394 * @param[in,out] ipaddr to increment.
395 * @param[in] end ipaddr to stop at.
396 * @param[in] prefix Length of the prefix.
397 * @return
398 * - true if the prefix bits are not high (continue).
399 * - false if the prefix bits are high (stop).
400 */
401static bool ipaddr_next(fr_ipaddr_t *ipaddr, fr_ipaddr_t const *end, uint8_t prefix)
402{
403 switch (ipaddr->af) {
404 default:
405 case AF_UNSPEC:
406 fr_assert(0);
407 return false;
408
409 case AF_INET6:
410 {
411 uint128_t ip_curr, ip_end;
412
413 if (!fr_cond_assert((prefix > 0) && (prefix <= 128))) return false;
414
415 /* Don't be tempted to cast */
416 memcpy(&ip_curr, ipaddr->addr.v6.s6_addr, sizeof(ip_curr));
417 memcpy(&ip_end, end->addr.v6.s6_addr, sizeof(ip_curr));
418
419 ip_curr = ntohlll(ip_curr);
420 ip_end = ntohlll(ip_end);
421
422 /* We're done */
423 if (uint128_eq(ip_curr, ip_end)) return false;
424
425 /* Increment the prefix */
426 ip_curr = uint128_add(ip_curr, uint128_lshift(uint128_new(0, 1), (128 - prefix)));
427 ip_curr = htonlll(ip_curr);
428 memcpy(&ipaddr->addr.v6.s6_addr, &ip_curr, sizeof(ipaddr->addr.v6.s6_addr));
429 return true;
430 }
431
432 case AF_INET:
433 {
434 uint32_t ip_curr, ip_end;
435
436 if (!fr_cond_assert((prefix > 0) && (prefix <= 32))) return false;
437
438 ip_curr = ntohl(ipaddr->addr.v4.s_addr);
439 ip_end = ntohl(end->addr.v4.s_addr);
440
441 /* We're done */
442 if (ip_curr == ip_end) return false;
443
444 /* Increment the prefix */
445 ip_curr += 1 << (32 - prefix);
446 ipaddr->addr.v4.s_addr = htonl(ip_curr);
447 return true;
448 }
449 }
450}
451
452/** Add a net to the pool
453 *
454 * @return the number of new addresses added.
455 */
456static int driver_do_lease(void *out, void *instance, ippool_tool_operation_t const *op,
457 redis_ippool_queue_t enqueue, redis_ippool_process_t process, void *uctx)
458{
459 redis_driver_conf_t *inst = talloc_get_type_abort(instance, redis_driver_conf_t);
460
461 int i;
462 bool more = true;
463 fr_redis_conn_t *conn;
464
466 fr_redis_rcode_t status;
467
468 fr_ipaddr_t ipaddr = op->start;
470 redisReply **replies = NULL;
471
472 unsigned int pipelined = 0;
473
474 while (more) {
475 fr_ipaddr_t acked = ipaddr; /* Record our progress */
476 size_t reply_cnt = 0;
477
478 for (s_ret = fr_redis_cluster_state_init(&state, &conn, inst->cluster, NULL,
479 op->pool, op->pool_len, false);
480 s_ret == REDIS_RCODE_TRY_AGAIN;
481 s_ret = fr_redis_cluster_state_next(&state, &conn, inst->cluster, NULL, status, &replies[0])) {
482 more = true; /* Reset to true, may have errored last loop */
483 status = REDIS_RCODE_SUCCESS;
484
485 /*
486 * If we got a redirect, start back at the beginning of the block.
487 */
488 ipaddr = acked;
489
490 for (i = 0; (i < MAX_PIPELINED) && more; i++, more = ipaddr_next(&ipaddr, &op->end,
491 op->prefix)) {
492 int enqueued;
493
494 enqueued = enqueue(inst, conn, op->pool, op->pool_len,
495 op->range, op->range_len, &ipaddr, op->prefix, uctx);
496 if (enqueued < 0) break;
497 pipelined += enqueued;
498 }
499
500 if (!replies) replies = talloc_zero_array(inst, redisReply *, pipelined);
501 if (!replies) return -1;
502
503 reply_cnt = fr_redis_pipeline_result(&pipelined, &status, replies,
504 talloc_array_length(replies), conn);
505 for (i = 0; (size_t)i < reply_cnt; i++) fr_redis_reply_print(L_DBG_LVL_3,
506 replies[i], NULL, i, status);
507 }
508 if (s_ret != REDIS_RCODE_SUCCESS) {
509 fr_redis_pipeline_free(replies, reply_cnt);
510 talloc_free(replies);
511 return -1;
512 }
513
514 if (process) {
515 fr_ipaddr_t to_process = acked;
516
517 for (i = 0; (size_t)i < reply_cnt; i++) {
518 int ret;
519
520 ret = process(out, &to_process, replies[i]);
521 if (ret < 0) continue;
522 ipaddr_next(&to_process, &op->end, op->prefix);
523 }
524 }
525 fr_redis_pipeline_free(replies, reply_cnt);
526 TALLOC_FREE(replies);
527 }
528
529 return 0;
530}
531
532/** Enqueue commands to retrieve lease information
533 *
534 */
535static int _driver_show_lease_process(void *out, fr_ipaddr_t const *ipaddr, redisReply const *reply)
536{
537 size_t existing;
538 ippool_tool_lease_t ***modified = out;
539 ippool_tool_lease_t *lease;
540
541 if (!*modified) *modified = talloc_array(NULL, ippool_tool_lease_t *, 1);
542
543 /*
544 * The exec command is the only one that produces an array.
545 */
546 if (reply->type != REDIS_REPLY_ARRAY) return -1;
547 if (reply->elements < 4) return -1;
548
549 if (reply->element[0]->type == REDIS_REPLY_NIL) return 0; /* A nil result (IP didn't exist) */
550 if (reply->element[0]->type != REDIS_REPLY_STRING) return -1; /* Something bad */
551 lease = talloc_zero(*modified, ippool_tool_lease_t);
552 lease->ipaddr = *ipaddr;
553 lease->next_event = (time_t)strtoull(reply->element[0]->str, NULL, 10);
554
555 if (reply->element[1]->type == REDIS_REPLY_STRING) {
556 lease->device = talloc_memdup(lease, reply->element[1]->str, reply->element[1]->len);
557 lease->device_len = reply->element[1]->len;
558 }
559 if (reply->element[2]->type == REDIS_REPLY_STRING) {
560 lease->gateway = talloc_memdup(lease, reply->element[2]->str, reply->element[2]->len);
561 lease->gateway_len = reply->element[2]->len;
562 }
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;
566 }
567
568 /*
569 * Grow the result array...
570 */
571 existing = talloc_array_length(*modified);
572 MEM(*modified = talloc_realloc(NULL, *modified, ippool_tool_lease_t *, existing + 1));
573 (*modified)[existing - 1] = lease;
574
575 return 0;
576}
577
578/** Enqueue commands to retrieve lease information
579 *
580 */
582 uint8_t const *key_prefix, size_t key_prefix_len,
583 UNUSED uint8_t const *range, UNUSED size_t range_len,
584 fr_ipaddr_t *ipaddr, uint8_t prefix, UNUSED void *uctx)
585{
587 uint8_t *key_p = key;
588 char ip_buff[FR_IPADDR_PREFIX_STRLEN];
589
591 uint8_t *ip_key_p = ip_key;
592
593 IPPOOL_BUILD_KEY(key, key_p, key_prefix, key_prefix_len);
594 IPPOOL_SPRINT_IP(ip_buff, ipaddr, prefix);
595 IPPOOL_BUILD_IP_KEY_FROM_STR(ip_key, ip_key_p, key_prefix, key_prefix_len, ip_buff);
596
597 DEBUG("Retrieving lease info for %s from pool %pV", ip_buff,
598 fr_box_strvalue_len((char const *)key_prefix, key_prefix_len));
599
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");
606 return 6;
607}
608
609/** Show information about leases
610 *
611 */
612static inline int driver_show_lease(void *out, void *instance, ippool_tool_operation_t const *op)
613{
615}
616
617/** Count the number of leases we released
618 *
619 */
620static int _driver_release_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
621{
622 uint64_t *modified = out;
623 /*
624 * Record the actual number of addresses released.
625 * Leases with a score of zero shouldn't be included,
626 * in this count.
627 */
628 if (reply->type != REDIS_REPLY_INTEGER) return -1;
629
630 *modified += reply->integer;
631
632 return 0;
633}
634
635/** Release a lease by setting its score back to zero
636 *
637 */
639 uint8_t const *key_prefix, size_t key_prefix_len,
640 UNUSED uint8_t const *range, UNUSED size_t range_len,
641 fr_ipaddr_t *ipaddr, uint8_t prefix, UNUSED void *uctx)
642{
643 char ip_buff[FR_IPADDR_PREFIX_STRLEN];
644
645 IPPOOL_SPRINT_IP(ip_buff, ipaddr, prefix);
646
647 DEBUG("Releasing %pV to pool \"%pV\"", ip_buff,
648 fr_box_strvalue_len((char const *)key_prefix, key_prefix_len));
649 redisAppendCommand(conn->handle, "EVAL %s 1 %b %s", lua_release_cmd, key_prefix, key_prefix_len, ip_buff);
650 return 1;
651}
652
653/** Release a range of leases
654 *
655 */
656static inline int driver_release_lease(void *out, void *instance, ippool_tool_operation_t const *op)
657{
658 return driver_do_lease(out, instance, op,
660}
661
662/** Count the number of leases we removed
663 *
664 * Because the ZREM and DEL have to occur in a transaction, we need
665 * some fancier processing to just count the number of ZREMs.
666 */
667static int _driver_remove_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
668{
669 uint64_t *modified = out;
670 /*
671 * Record the actual number of addresses released.
672 * Leases with a score of zero shouldn't be included,
673 * in this count.
674 */
675 if (reply->type != REDIS_REPLY_INTEGER) return -1;
676
677 *modified += reply->integer;
678
679 return 0;
680}
681
682/** Enqueue lease removal commands
683 *
684 * This removes the lease from the expiry heap, and the data associated with
685 * the lease.
686 */
688 uint8_t const *key_prefix, size_t key_prefix_len,
689 UNUSED uint8_t const *range, UNUSED size_t range_len,
690 fr_ipaddr_t *ipaddr, uint8_t prefix, UNUSED void *uctx)
691{
692 char ip_buff[FR_IPADDR_PREFIX_STRLEN];
693
694 IPPOOL_SPRINT_IP(ip_buff, ipaddr, prefix);
695
696 DEBUG("Removing %s from pool \"%pV\"", ip_buff,
697 fr_box_strvalue_len((char const *)key_prefix, key_prefix_len));
698 redisAppendCommand(conn->handle, "EVAL %s 1 %b %s", lua_remove_cmd, key_prefix, key_prefix_len, ip_buff);
699 return 1;
700}
701
702/** Remove a range of leases
703 *
704 */
705static int driver_remove_lease(void *out, void *instance, ippool_tool_operation_t const *op)
706{
707 return driver_do_lease(out, instance, op,
709}
710
711/** Count the number of leases we actually added
712 *
713 * This isn't necessarily the same as the number of ZADDs, as leases may
714 * already exist.
715 */
716static int _driver_add_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
717{
718 uint64_t *modified = out;
719 /*
720 * Record the actual number of addresses modified.
721 * Existing addresses won't be included in this
722 * count.
723 */
724 if (reply->type != REDIS_REPLY_ARRAY) return -1;
725
726 if ((reply->elements > 0) && (reply->element[0]->type == REDIS_REPLY_INTEGER)) {
727 *modified += reply->element[0]->integer;
728 }
729 return 0;
730}
731
732/** Enqueue lease addition commands
733 *
734 */
736 uint8_t const *key_prefix, size_t key_prefix_len,
737 uint8_t const *range, size_t range_len,
738 fr_ipaddr_t *ipaddr, uint8_t prefix, UNUSED void *uctx)
739{
741 uint8_t *key_p = key;
742 char ip_buff[FR_IPADDR_PREFIX_STRLEN];
743
745 uint8_t *ip_key_p = ip_key;
746
747 int enqueued = 0;
748
749 IPPOOL_BUILD_KEY(key, key_p, key_prefix, key_prefix_len);
750 IPPOOL_SPRINT_IP(ip_buff, ipaddr, prefix);
751 IPPOOL_BUILD_IP_KEY_FROM_STR(ip_key, ip_key_p, key_prefix, key_prefix_len, ip_buff);
752
753 DEBUG("Adding %s to pool \"%pV\" (%zu)", ip_buff, fr_box_strvalue_len((char *)key, (key_p - key)), key_p - key);
754 redisAppendCommand(conn->handle, "MULTI");
755 enqueued++;
756 redisAppendCommand(conn->handle, "ZADD %b NX %u %s", key, key_p - key, 0, ip_buff);
757 enqueued++;
758
759 /*
760 * Only add range if it's not NULL.
761 *
762 * Zero length ranges are allowed, and should be preserved.
763 */
764 if (range) {
765 redisAppendCommand(conn->handle, "HSET %b range %b", ip_key, ip_key_p - ip_key, range, range_len);
766 enqueued++;
767 }
768 redisAppendCommand(conn->handle, "EXEC");
769 enqueued++;
770
771 return enqueued;
772}
773
774/** Add a range of prefixes
775 *
776 */
777static int driver_add_lease(void *out, void *instance, ippool_tool_operation_t const *op)
778{
780}
781
782/** Count the number of leases we modified
783 */
784static int _driver_modify_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
785{
786 uint64_t *modified = out;
787 /*
788 * Record the actual number of addresses released.
789 * Leases with a score of zero shouldn't be included,
790 * in this count.
791 */
792 if (reply->type != REDIS_REPLY_INTEGER) return -1;
793
794 /*
795 * return code is 0 or 1 depending on if its a new
796 * field, neither are useful
797 */
798 *modified += 1;
799
800 return 0;
801}
802
803/** Enqueue lease removal commands
804 *
805 * This modifys the lease from the expiry heap, and the data associated with
806 * the lease.
807 */
809 uint8_t const *key_prefix, size_t key_prefix_len,
810 uint8_t const *range, size_t range_len,
811 fr_ipaddr_t *ipaddr, uint8_t prefix, UNUSED void *uctx)
812{
814 uint8_t *key_p = key;
815 char ip_buff[FR_IPADDR_PREFIX_STRLEN];
816
818 uint8_t *ip_key_p = ip_key;
819
820 IPPOOL_BUILD_KEY(key, key_p, key_prefix, key_prefix_len);
821 IPPOOL_SPRINT_IP(ip_buff, ipaddr, prefix);
822 IPPOOL_BUILD_IP_KEY_FROM_STR(ip_key, ip_key_p, key_prefix, key_prefix_len, ip_buff);
823
824 DEBUG("Modifying %s in pool \"%pV\"", ip_buff, fr_box_strvalue_len((char const *)key_prefix, key_prefix_len));
825 redisAppendCommand(conn->handle, "HSET %b range %b", ip_key, ip_key_p - ip_key, range, range_len);
826
827 return 1;
828}
829
830/** Remove a range of leases
831 *
832 */
833static int driver_modify_lease(void *out, void *instance, ippool_tool_operation_t const *op)
834{
835 return driver_do_lease(out, instance, op,
837}
838
839static int _driver_assign_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
840{
841 uint64_t *modified = out;
842 if (reply->type != REDIS_REPLY_INTEGER) return -1;
843
844 *modified += reply->integer;
845 return 0;
846}
847
848/** Enqueue static lease assignment commands
849 *
850 */
852 uint8_t const *key_prefix, size_t key_prefix_len,
853 uint8_t const *range, size_t range_len,
854 fr_ipaddr_t *ipaddr, uint8_t prefix, void *uctx)
855{
856 ippool_tool_owner_t *owner = uctx;
857 char ip_buff[FR_IPADDR_PREFIX_STRLEN];
858 fr_time_t now;
859
860 IPPOOL_SPRINT_IP(ip_buff, ipaddr, prefix);
861 now = fr_time();
862
863 DEBUG("Assigning address %s to owner %s", ip_buff, owner->owner);
864
865 redisAppendCommand(conn->handle, "EVAL %s 1 %b %s %b %b %i", lua_assign_cmd, key_prefix, key_prefix_len,
866 ip_buff, owner->owner, strlen(owner->owner), range, range_len, fr_time_to_sec(now));
867 return 1;
868}
869
870/** Add static lease assignments
871 *
872 */
873static int driver_assign_lease(void *out, void *instance, ippool_tool_operation_t const *op, char const *owner)
874{
875 return driver_do_lease(out, instance, op,
877 &(ippool_tool_owner_t){ .owner = owner });
878}
879
880static int _driver_unassign_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
881{
882 uint64_t *modified = out;
883 /*
884 * Record the actual number of addresses unassigned.
885 */
886 if (reply->type != REDIS_REPLY_INTEGER) return -1;
887
888 *modified += reply->integer;
889
890 return 0;
891}
892
893/** Enqueue static lease un-assignment commands
894 *
895 */
897 uint8_t const *key_prefix, size_t key_prefix_len,
898 UNUSED uint8_t const *range, UNUSED size_t range_len,
899 fr_ipaddr_t *ipaddr, uint8_t prefix, void *uctx)
900{
901 ippool_tool_owner_t *owner = uctx;
902 char ip_buff[FR_IPADDR_PREFIX_STRLEN];
903 fr_time_t now;
904
905 IPPOOL_SPRINT_IP(ip_buff, ipaddr, prefix);
906 now = fr_time();
907
908 DEBUG("Un-assigning address %s from owner %s", ip_buff, owner->owner);
909 redisAppendCommand(conn->handle, "EVAL %s 1 %b %s %b %i", lua_unassign_cmd, key_prefix, key_prefix_len,
910 ip_buff, owner->owner, strlen(owner->owner), fr_time_to_sec(now));
911 return 1;
912}
913
914/** Unassign static lease assignments
915 *
916 */
917static int driver_unassign_lease(void *out, void *instance, ippool_tool_operation_t const *op, char const *owner)
918{
919 return driver_do_lease(out, instance, op,
921 &(ippool_tool_owner_t){ .owner = owner });
922}
923
924/** Compare two pool names
925 *
926 */
927static int8_t pool_cmp(void const *a, void const *b)
928{
929 size_t len_a;
930 size_t len_b;
931 int ret;
932
933 len_a = talloc_array_length((uint8_t const *)a);
934 len_b = talloc_array_length((uint8_t const *)b);
935
936 ret = CMP(len_a, len_b);
937 if (ret != 0) return ret;
938
939 ret = memcmp(a, b, len_a);
940 return CMP(ret, 0);
941}
942
943/** Return the pools available across the cluster
944 *
945 * @param[in] ctx to allocate range names in.
946 * @param[out] out Array of pool names.
947 * @param[in] instance Driver specific instance data.
948 * @return
949 * - < 0 on failure.
950 * - >= 0 the number of ranges in the array we allocated.
951 */
952static ssize_t driver_get_pools(TALLOC_CTX *ctx, uint8_t **out[], void *instance)
953{
954 fr_socket_t *master;
955 size_t k;
956 ssize_t ret, i, used = 0;
957 fr_redis_conn_t *conn = NULL;
958 redis_driver_conf_t *inst = talloc_get_type_abort(instance, redis_driver_conf_t);
960 uint8_t *key_p = key;
961 uint8_t **result;
962
963 IPPOOL_BUILD_KEY(key, key_p, "*}:"IPPOOL_POOL_KEY, 1);
964
965 *out = NULL; /* Initialise output pointer */
966
967 /*
968 * Get the addresses of all masters in the pool
969 */
970 ret = fr_redis_cluster_node_addr_by_role(ctx, &master, inst->cluster, true, false);
971 if (ret <= 0) {
972 result = NULL;
973 return ret;
974 }
975
976 result = talloc_zero_array(ctx, uint8_t *, 1);
977 if (!result) {
978 ERROR("Failed allocating array of pool names");
979 talloc_free(master);
980 return -1;
981 }
982
983 /*
984 * Iterate over the masters, getting the pools on each
985 */
986 for (i = 0; i < ret; i++) {
987 fr_pool_t *pool;
988 redisReply *reply;
989 char const *p;
990 size_t len;
991 char cursor[19] = "0";
992 fr_redis_rcode_t status;
993
994 if (fr_redis_cluster_pool_by_node_addr(&pool, inst->cluster, &master[i], false) < 0) {
995 ERROR("Failed retrieving pool for node");
996 error:
997 TALLOC_FREE(result);
998 talloc_free(master);
999 return -1;
1000 }
1001
1002 conn = fr_pool_connection_get(pool, NULL);
1003 if (!conn) goto error;
1004 do {
1005 /*
1006 * Break up the scan so we don't block any single
1007 * Redis node too long.
1008 */
1009 reply = redisCommand(conn->handle, "SCAN %s MATCH %b COUNT 20", cursor, key, key_p - key);
1010 if (!reply) {
1011 ERROR("Failed reading reply");
1012 fr_pool_connection_release(pool, NULL, conn);
1013 goto error;
1014 }
1015 status = fr_redis_command_status(conn, reply);
1016 fr_redis_reply_print(L_DBG_LVL_3, reply, NULL, 0, status);
1017 if (status != REDIS_RCODE_SUCCESS) {
1018 PERROR("Error retrieving keys %s", cursor);
1019
1020 reply_error:
1021 fr_pool_connection_release(pool, NULL, conn);
1022 fr_redis_reply_free(&reply);
1023 goto error;
1024 }
1025
1026 if (reply->type != REDIS_REPLY_ARRAY) {
1027 ERROR("Failed retrieving result, expected array got %s",
1028 fr_table_str_by_value(redis_reply_types, reply->type, "<UNKNOWN>"));
1029
1030 goto reply_error;
1031 }
1032
1033 if (reply->elements != 2) {
1034 ERROR("Failed retrieving result, expected array with two elements, got %zu elements",
1035 reply->elements);
1036 fr_redis_reply_free(&reply);
1037 goto reply_error;
1038 }
1039
1040 if (reply->element[0]->type != REDIS_REPLY_STRING) {
1041 ERROR("Failed retrieving result, expected string got %s",
1042 fr_table_str_by_value(redis_reply_types, reply->element[0]->type, "<UNKNOWN>"));
1043 goto reply_error;
1044 }
1045
1046 if (reply->element[1]->type != REDIS_REPLY_ARRAY) {
1047 ERROR("Failed retrieving result, expected array got %s",
1048 fr_table_str_by_value(redis_reply_types, reply->element[1]->type, "<UNKNOWN>"));
1049 goto reply_error;
1050 }
1051
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));
1055 if (!result) {
1056 ERROR("Failed expanding array of pool names");
1057 goto reply_error;
1058 }
1059 }
1060 strlcpy(cursor, reply->element[0]->str, sizeof(cursor));
1061
1062 for (k = 0; k < reply->element[1]->elements; k++) {
1063 redisReply *pool_key = reply->element[1]->element[k];
1064
1065 /*
1066 * Skip over things which are not pool names
1067 */
1068 if (pool_key->len < 7) continue; /* { + [<name>] + }:pool */
1069
1070 if ((pool_key->str[0]) != '{') continue;
1071 p = memchr(pool_key->str + 1, '}', pool_key->len - 1);
1072 if (!p) continue;
1073
1074 len = (pool_key->len - ((p + 1) - pool_key->str));
1075 if (len != (sizeof(IPPOOL_POOL_KEY) - 1) + 1) continue;
1076 if (memcmp(p + 1, ":" IPPOOL_POOL_KEY, (sizeof(IPPOOL_POOL_KEY) - 1) + 1) != 0) {
1077 continue;
1078 }
1079
1080 /*
1081 * String between the curly braces is the pool name
1082 */
1083 result[used++] = talloc_memdup(result, pool_key->str + 1, (p - pool_key->str) - 1);
1084 }
1085
1086 fr_redis_reply_free(&reply);
1087 } while (!((cursor[0] == '0') && (cursor[1] == '\0'))); /* Cursor value of 0 means no more results */
1088
1089 fr_pool_connection_release(pool, NULL, conn);
1090 }
1091
1092 if (used == 0) {
1093 *out = NULL;
1094 talloc_free(result);
1095 return 0;
1096 }
1097
1098 /*
1099 * Sort the results
1100 */
1101 if (used > 1) {
1102 uint8_t const **to_sort;
1103
1104 memcpy(&to_sort, &result, sizeof(to_sort));
1105
1106 fr_quick_sort((void const **)to_sort, 0, used - 1, pool_cmp);
1107 }
1108
1109 *out = talloc_array(ctx, uint8_t *, used);
1110 if (!*out) {
1111 ERROR("Failed allocating file pool name array");
1112 talloc_free(result);
1113 return -1;
1114 }
1115
1116 /*
1117 * SCAN can produce duplicates, remove them here
1118 */
1119 i = 0;
1120 k = 0;
1121 do { /* stop before last entry */
1122 (*out)[k++] = talloc_steal(*out, result[i++]);
1123 while ((i < used) && (pool_cmp(result[i - 1], result[i]) == 0)) i++;
1124 } while (i < used);
1125
1126 talloc_free(result);
1127
1128 return used;
1129}
1130
1131static int driver_get_stats(ippool_tool_stats_t *out, void *instance, uint8_t const *key_prefix, size_t key_prefix_len)
1132{
1133 redis_driver_conf_t *inst = talloc_get_type_abort(instance, redis_driver_conf_t);
1135 uint8_t *key_p = key;
1136
1137 fr_redis_conn_t *conn;
1138
1140 fr_redis_rcode_t status;
1141 fr_time_t now;
1142
1143 int s_ret = REDIS_RCODE_SUCCESS;
1144 redisReply **replies = NULL, *reply;
1145 unsigned int pipelined = 0; /* Update if additional commands added */
1146
1147 size_t reply_cnt = 0, i = 0;
1148
1149#define STATS_COMMANDS_TOTAL 14
1150
1151 IPPOOL_BUILD_KEY(key, key_p, key_prefix, key_prefix_len);
1152
1153 MEM(replies = talloc_zero_array(inst, redisReply *, STATS_COMMANDS_TOTAL));
1154
1155 now = fr_time();
1156
1157 for (s_ret = fr_redis_cluster_state_init(&state, &conn, inst->cluster, NULL, key, key_p - key, false);
1158 s_ret == REDIS_RCODE_TRY_AGAIN;
1159 s_ret = fr_redis_cluster_state_next(&state, &conn, inst->cluster, NULL, status, &replies[0])) {
1160 status = REDIS_RCODE_SUCCESS;
1161
1162 redisAppendCommand(conn->handle, "MULTI");
1163 redisAppendCommand(conn->handle, "ZCARD %b", key, key_p - key); /* Total */
1164 redisAppendCommand(conn->handle, "ZCOUNT %b -inf %i",
1165 key, key_p - key, fr_time_to_sec(now)); /* Free */
1166 redisAppendCommand(conn->handle, "ZCOUNT %b -inf %i",
1167 key, key_p - key, fr_time_to_sec(now) + 60); /* Free in next 60s */
1168 redisAppendCommand(conn->handle, "ZCOUNT %b -inf %i",
1169 key, key_p - key, fr_time_to_sec(now) + (60 * 30)); /* Free in next 30 mins */
1170 redisAppendCommand(conn->handle, "ZCOUNT %b -inf %i",
1171 key, key_p - key, fr_time_to_sec(now) + (60 * 60)); /* Free in next 60 mins */
1172 redisAppendCommand(conn->handle, "ZCOUNT %b -inf %i",
1173 key, key_p - key, fr_time_to_sec(now) + (60 * 60 * 24)); /* Free in next day */
1174 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " inf",
1175 key, key_p - key); /* Total static */
1176 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " %"PRIu64,
1177 key, key_p - key, IPPOOL_STATIC_BIT + fr_time_to_sec(now)); /* Static assignments 'free' */
1178 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " %"PRIu64,
1179 key, key_p - key,
1180 IPPOOL_STATIC_BIT + fr_time_to_sec(now) + 60); /* Static renew in 60s */
1181 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " %"PRIu64,
1182 key, key_p - key,
1183 IPPOOL_STATIC_BIT + fr_time_to_sec(now) + (60 * 30)); /* Static renew in 30 mins */
1184 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " %"PRIu64,
1185 key, key_p - key,
1186 IPPOOL_STATIC_BIT + fr_time_to_sec(now) + (60 * 60)); /* Static renew in 60 mins */
1187 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " %"PRIu64,
1188 key, key_p - key,
1189 IPPOOL_STATIC_BIT + fr_time_to_sec(now) + (60 * 60 * 24)); /* Static renew in 1 day */
1190 redisAppendCommand(conn->handle, "EXEC");
1191 if (!replies) return -1;
1192
1193 pipelined = STATS_COMMANDS_TOTAL;
1194 reply_cnt = fr_redis_pipeline_result(&pipelined, &status, replies,
1195 talloc_array_length(replies), conn);
1196 for (i = 0; (size_t)i < reply_cnt; i++) fr_redis_reply_print(L_DBG_LVL_3,
1197 replies[i], NULL, i, status);
1198 }
1199 if (s_ret != REDIS_RCODE_SUCCESS) {
1200 error:
1201 fr_redis_pipeline_free(replies, reply_cnt);
1202 talloc_free(replies);
1203 return -1;
1204 }
1205
1206 if (reply_cnt != STATS_COMMANDS_TOTAL) {
1207 ERROR("Failed retrieving pool stats: Expected %i replies, got %zu", pipelined, reply_cnt);
1208 goto error;
1209 }
1210
1211 reply = replies[reply_cnt - 1];
1212
1213 if (reply->type != REDIS_REPLY_ARRAY) {
1214 ERROR("Failed retrieving pool stats: Expected array got %s",
1215 fr_table_str_by_value(redis_reply_types, reply->element[1]->type, "<UNKNOWN>"));
1216 goto error;
1217 }
1218
1219 if (reply->elements != (reply_cnt - 2)) {
1220 ERROR("Failed retrieving pool stats: Expected %zu results, got %zu", reply_cnt - 2, reply->elements);
1221 goto error;
1222 }
1223
1224 if (reply->element[0]->integer == 0) {
1225 ERROR("Pool not found");
1226 goto error;
1227 }
1228
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;
1241
1242 fr_redis_pipeline_free(replies, reply_cnt);
1243 talloc_free(replies);
1244
1245 return 0;
1246}
1247
1248/** Driver initialization function
1249 *
1250 */
1251static int driver_init(TALLOC_CTX *ctx, CONF_SECTION *conf, void **instance)
1252{
1253 redis_driver_conf_t *this;
1254 int ret;
1255
1256 *instance = NULL;
1257
1258 if (cf_section_rules_push(conf, redis_config) < 0) return -1;
1259
1260 this = talloc_zero(ctx, redis_driver_conf_t);
1261 if (!this) return -1;
1262
1263 ret = cf_section_parse(this, &this->conf, conf);
1264 if (ret < 0) {
1265 talloc_free(this);
1266 return -1;
1267 }
1268
1269 this->cluster = fr_redis_cluster_alloc(this, conf, &this->conf, false,
1270 "rlm_redis_ippool_tool", NULL, NULL);
1271 if (!this->cluster) {
1272 talloc_free(this);
1273 return -1;
1274 }
1275 *instance = this;
1276
1277 return 0;
1278}
1279
1280/** Convert an IP range or CIDR mask to a start and stop address
1281 *
1282 * @param[out] start_out Where to write the start address.
1283 * @param[out] end_out Where to write the end address.
1284 * @param[in] ip_str Unparsed IP string.
1285 * @param[in] prefix length of prefixes we'll be allocating.
1286 * @return
1287 * - 0 on success.
1288 * - -1 on failure.
1289 */
1290static int parse_ip_range(fr_ipaddr_t *start_out, fr_ipaddr_t *end_out, char const *ip_str, uint8_t prefix)
1291{
1292 fr_ipaddr_t start, end;
1293 bool ex_broadcast;
1294 char const *p;
1295
1296 p = strchr(ip_str, '-');
1297 if (p) {
1298 char start_buff[INET6_ADDRSTRLEN + 4];
1299 char end_buff[INET6_ADDRSTRLEN + 4];
1300 size_t len;
1301
1302 if ((size_t)(p - ip_str) >= sizeof(start_buff)) {
1303 ERROR("Start address too long");
1304 return -1;
1305 }
1306
1307 len = strlcpy(start_buff, ip_str, (p - ip_str) + 1);
1308 if (is_truncated(len, sizeof(start_buff))) {
1309 ERROR("Start address too long");
1310 return -1;
1311 }
1312
1313 len = strlcpy(end_buff, p + 1, sizeof(end_buff));
1314 if (is_truncated(len, sizeof(end_buff))) {
1315 ERROR("End address too long");
1316 return -1;
1317 }
1318
1319 if (fr_inet_pton(&start, start_buff, -1, AF_UNSPEC, false, true) < 0) {
1320 PERROR("Failed parsing \"%s\" as start address", start_buff);
1321 return -1;
1322 }
1323
1324 if (fr_inet_pton(&end, end_buff, -1, AF_UNSPEC, false, true) < 0) {
1325 PERROR("Failed parsing \"%s\" end address", end_buff);
1326 return -1;
1327 }
1328
1329 if (start.af != end.af) {
1330 ERROR("Start and end address must be of the same address family");
1331 return -1;
1332 }
1333
1334 if (!prefix) prefix = IPADDR_LEN(start.af);
1335
1336 /*
1337 * IPv6 addresses
1338 */
1339 if (start.af == AF_INET6) {
1340 uint128_t start_int, end_int;
1341
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");
1346 return -1;
1347 }
1348 /*
1349 * IPv4 addresses
1350 */
1351 } else {
1352 if (ntohl((uint32_t)(start.addr.v4.s_addr)) >
1353 ntohl((uint32_t)(end.addr.v4.s_addr))) {
1354 ERROR("End address must be greater than or equal to start address");
1355 return -1;
1356 }
1357 }
1358
1359 /*
1360 * Mask start and end so we can do prefix ranges too
1361 */
1362 fr_ipaddr_mask(&start, prefix);
1363 fr_ipaddr_mask(&end, prefix);
1364 start.prefix = prefix;
1365 end.prefix = prefix;
1366
1367 *start_out = start;
1368 *end_out = end;
1369
1370 return 0;
1371 }
1372
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);
1375 return -1;
1376 }
1377
1378 if (!prefix) prefix = IPADDR_LEN(start.af);
1379
1380 if (prefix < start.prefix) {
1381 ERROR("-p must be greater than or equal to /<mask> (%u)", start.prefix);
1382 return -1;
1383 }
1384 if (prefix > IPADDR_LEN(start.af)) {
1385 ERROR("-p must be less than or equal to address length (%u)", IPADDR_LEN(start.af));
1386 return -1;
1387 }
1388
1389 if ((prefix - start.prefix) > 64) {
1390 ERROR("-p must be less than or equal to %u", start.prefix + 64);
1391 return -1;
1392 }
1393
1394 /*
1395 * Exclude the broadcast address only if we're dealing with IPv4 addresses
1396 * if we're allocating IPv6 addresses or prefixes we don't need to.
1397 */
1398 ex_broadcast = (start.af == AF_INET) && (IPADDR_LEN(start.af) == prefix);
1399
1400 /*
1401 * Excluding broadcast, 31/32 or 127/128 start/end are the same
1402 */
1403 if (ex_broadcast && (start.prefix >= (IPADDR_LEN(start.af) - 1))) {
1404 *start_out = start;
1405 *end_out = start;
1406 return 0;
1407 }
1408
1409 /*
1410 * Set various fields (we only overwrite the IP later)
1411 */
1412 end = start;
1413
1414 if (start.af == AF_INET6) {
1415 uint128_t ip, p_mask;
1416
1417 /* cond assert to satisfy clang scan */
1418 if (!fr_cond_assert((prefix > 0) && (prefix <= 128))) return -1;
1419
1420 /* Don't be tempted to cast */
1421 memcpy(&ip, start.addr.v6.s6_addr, sizeof(ip));
1422 ip = ntohlll(ip);
1423
1424 /* Generate a mask that covers the prefix bits, and sets them high */
1425 p_mask = uint128_lshift(uint128_gen_mask(prefix - start.prefix), (128 - prefix));
1426 ip = htonlll(uint128_bor(p_mask, ip));
1427
1428 /* Decrement by one */
1429 if (ex_broadcast) ip = uint128_sub(ip, uint128_new(0, 1));
1430 memcpy(&end.addr.v6.s6_addr, &ip, sizeof(end.addr.v6.s6_addr));
1431 } else {
1432 uint32_t ip;
1433
1434 /* cond assert to satisfy clang scan */
1435 if (!fr_cond_assert((prefix > 0) && (prefix <= 32))) return -1;
1436
1437 ip = ntohl(start.addr.v4.s_addr);
1438
1439 /* Generate a mask that covers the prefix bits and sets them high */
1440 ip |= uint32_gen_mask(prefix - start.prefix) << (32 - prefix);
1441
1442 /* Decrement by one */
1443 if (ex_broadcast) ip--;
1444 end.addr.v4.s_addr = htonl(ip);
1445 }
1446
1447 *start_out = start;
1448 *end_out = end;
1449
1450 return 0;
1451}
1452
1453int main(int argc, char *argv[])
1454{
1455 static ippool_tool_operation_t ops[128];
1456 ippool_tool_operation_t *p = ops, *end = ops + (NUM_ELEMENTS(ops));
1457
1458 int c;
1459
1460 uint8_t *range_arg = NULL;
1461 uint8_t *pool_arg = NULL;
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;
1467
1468 CONF_SECTION *pool_cs;
1469 CONF_PAIR *cp;
1471
1472 fr_debug_lvl = 0;
1473 name = argv[0];
1474
1475 conf = talloc_zero(NULL, ippool_tool_t);
1476 conf->cs = cf_section_alloc(conf, NULL, "main", NULL);
1477 if (!conf->cs) fr_exit_now(EXIT_FAILURE);
1478
1479#define ADD_ACTION(_action) \
1480do { \
1481 if (p >= end) { \
1482 ERROR("Too many actions, max is " STRINGIFY(sizeof(ops))); \
1483 usage(64); \
1484 } \
1485 p->action = _action; \
1486 p->name = optarg; \
1487 p++; \
1488 need_pool = true; \
1489} while (0)
1490
1491 while ((c = getopt(argc, argv, "a:d:r:s:Sm:A:U:O:p:ilLhxo:f:")) != -1) switch (c) {
1492 case 'a':
1494 break;
1495
1496 case 'd':
1498 break;
1499
1500 case 'r':
1502 break;
1503
1504 case 's':
1506 break;
1507
1508 case 'm':
1510 break;
1511
1512 case 'A':
1514 break;
1515
1516 case 'U':
1518 break;
1519
1520 case 'O':
1521 owner = optarg;
1522 break;
1523
1524 case 'p':
1525 {
1526 unsigned long tmp;
1527 char *q;
1528
1529 if (p == ops) {
1530 ERROR("Prefix may only be specified after a pool management action");
1531 usage(64);
1532 }
1533
1534 tmp = strtoul(optarg, &q, 10);
1535 if (q != (optarg + strlen(optarg))) {
1536 ERROR("Prefix must be an integer value");
1537
1538 }
1539
1540 (p - 1)->prefix = (uint8_t)tmp & 0xff;
1541 }
1542 break;
1543
1544 case 'i':
1545 do_import = optarg;
1546 break;
1547
1548 case 'I':
1549 do_export = true;
1550 break;
1551
1552 case 'l':
1553 if (list_pools) usage(1); /* Only allowed once */
1554 list_pools = true;
1555 break;
1556
1557 case 'S':
1558 print_stats = true;
1559 break;
1560
1561 case 'h':
1562 usage(0);
1563
1564 case 'x':
1565 fr_debug_lvl++;
1566 break;
1567
1568 case 'o':
1569 break;
1570
1571 case 'f':
1572 filename = optarg;
1573 break;
1574
1575 default:
1576 usage(1);
1577 }
1578 argc -= optind;
1579 argv += optind;
1580
1581 if (argc == 0) {
1582 ERROR("Need server address/port");
1583 usage(64);
1584 }
1585 if ((argc == 1) && need_pool) {
1586 ERROR("Need pool to operate on");
1587 usage(64);
1588 }
1589 if (argc > 3) usage(64);
1590
1591 /*
1592 * Read configuration files if necessary.
1593 */
1594 if (filename && (cf_file_read(conf->cs, filename) < 0 || (cf_section_pass2(conf->cs) < 0))) {
1595 fr_exit_now(EXIT_FAILURE);
1596 }
1597
1598 cp = cf_pair_alloc(conf->cs, "server", argv[0], T_OP_EQ, T_BARE_WORD, T_DOUBLE_QUOTED_STRING);
1599 if (!cp) {
1600 ERROR("Failed creating server pair");
1601 fr_exit_now(EXIT_FAILURE);
1602 }
1603
1604 /*
1605 * Unescape sequences in the pool name
1606 */
1607 if (argv[1] && (argv[1][0] != '\0')) {
1610
1611 MEM(fr_sbuff_init_talloc(conf, &out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1612 (void) fr_value_str_unescape(&out,
1613 &FR_SBUFF_IN(argv[1], strlen(argv[1])), SIZE_MAX, '"');
1614 talloc_realloc(conf, out.buff, uint8_t, fr_sbuff_used(&out));
1615 pool_arg = (uint8_t *)out.buff;
1616 }
1617
1618 if (argc >= 3 && (argv[2][0] != '\0')) {
1621
1622 MEM(fr_sbuff_init_talloc(conf, &out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1623 (void) fr_value_str_unescape(&out,
1624 &FR_SBUFF_IN(argv[2], strlen(argv[2])), SIZE_MAX, '"');
1625 talloc_realloc(conf, out.buff, uint8_t, fr_sbuff_used(&out));
1626 range_arg = (uint8_t *)out.buff;
1627 }
1628
1629 if (!do_import && !do_export && !list_pools && !print_stats && (p == ops)) {
1630 ERROR("Nothing to do!");
1631 fr_exit_now(EXIT_FAILURE);
1632 }
1633
1634 /*
1635 * Set some alternative default pool settings
1636 */
1637 pool_cs = cf_section_find(conf->cs, "pool", NULL);
1638 if (!pool_cs) {
1639 pool_cs = cf_section_alloc(conf->cs, conf->cs, "pool", NULL);
1640 }
1641 cp = cf_pair_find(pool_cs, "start");
1642 if (!cp) {
1643 /*
1644 * Start should always default to 1
1645 * else the cluster code doesn't
1646 * map the cluster.
1647 */
1648 (void) cf_pair_alloc(pool_cs, "start", "1", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1649 }
1650 cp = cf_pair_find(pool_cs, "spare");
1651 if (!cp) {
1652 (void) cf_pair_alloc(pool_cs, "spare", "0", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1653 }
1654 cp = cf_pair_find(pool_cs, "min");
1655 if (!cp) {
1656 (void) cf_pair_alloc(pool_cs, "min", "0", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1657 }
1658 cp = cf_pair_find(pool_cs, "max");
1659 if (!cp) {
1660 /*
1661 * Set a safe default for "max" - as this is a stand alone tool,
1662 * it can't use automatic value from the worker thread count.
1663 */
1664 (void) cf_pair_alloc(pool_cs, "max", "10", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1665 }
1666
1667 if (driver_init(conf, conf->cs, &conf->driver) < 0) {
1668 ERROR("Driver initialisation failed");
1669 fr_exit_now(EXIT_FAILURE);
1670 }
1671
1672 if (do_import) {
1673 ERROR("NOT YET IMPLEMENTED");
1674 }
1675
1676 if (do_export) {
1677 ERROR("NOT YET IMPLEMENTED");
1678 }
1679
1680 if (print_stats) {
1681 ippool_tool_stats_t stats;
1682 uint8_t **pools;
1683 ssize_t slen;
1684 size_t i;
1685
1686 if (pool_arg) {
1687 pools = talloc_zero_array(conf, uint8_t *, 1);
1688 slen = 1;
1689 pools[0] = pool_arg;
1690 } else {
1691 slen = driver_get_pools(conf, &pools, conf->driver);
1692 if (slen < 0) fr_exit_now(EXIT_FAILURE);
1693 }
1694
1695 for (i = 0; i < (size_t)slen; i++) {
1696 if (driver_get_stats(&stats, conf->driver,
1697 pools[i], talloc_array_length(pools[i])) < 0) fr_exit_now(EXIT_FAILURE);
1698
1699 INFO("pool : %pV", fr_box_strvalue_len((char *)pools[i],
1700 talloc_array_length(pools[i])));
1701 INFO("total : %" PRIu64, stats.total);
1702 INFO("dynamic total : %" PRIu64, stats.total - stats.static_tot);
1703 INFO("dynamic free : %" PRIu64, stats.free);
1704 INFO("dynamic used : %" PRIu64, stats.total - stats.free - stats.static_tot);
1705 if ((stats.total - stats.static_tot) > 0) {
1706 INFO("dynamic used (%%) : %.2Lf",
1707 ((long double)(stats.total - stats.free - stats.static_tot) /
1708 (long double)(stats.total - stats.static_tot)) * 100);
1709 } else {
1710 INFO("used (%%) : 0");
1711 }
1712 INFO("expiring 0-1m : %" PRIu64, stats.expiring_1m);
1713 INFO("expiring 1-30m : %" PRIu64, stats.expiring_30m - stats.expiring_1m);
1714 INFO("expiring 30m-1h : %" PRIu64, stats.expiring_1h - stats.expiring_30m);
1715 INFO("expiring 1h-1d : %" PRIu64, stats.expiring_1d - stats.expiring_1h);
1716 INFO("static total : %" PRIu64, stats.static_tot);
1717 INFO("static 'free' : %" PRIu64, stats.static_free);
1718 INFO("static issued : %" PRIu64, stats.static_tot - stats.static_free);
1719 if (stats.static_tot) {
1720 INFO("static issued (%%) : %.2Lf",
1721 ((long double)(stats.static_tot - stats.static_free) /
1722 (long double)(stats.static_tot)) * 100);
1723 } else {
1724 INFO("static issued (%%) : 0");
1725 }
1726 INFO("static renew 0-1m : %" PRIu64, stats.static_1m);
1727 INFO("static renew 1-30m : %" PRIu64, stats.static_30m - stats.static_1m);
1728 INFO("static renew 30m-1h : %" PRIu64, stats.static_1h - stats.static_30m);
1729 INFO("static renew 1h-1d : %" PRIu64, stats.static_1d - stats.static_1h);
1730 INFO("--");
1731 }
1732 }
1733
1734 if (list_pools) {
1735 ssize_t slen;
1736 size_t i;
1737 uint8_t **pools;
1738
1739 slen = driver_get_pools(conf, &pools, conf->driver);
1740 if (slen < 0) fr_exit_now(EXIT_FAILURE);
1741 if (slen > 0) {
1742 for (i = 0; i < (size_t)slen; i++) {
1743 INFO("%pV", fr_box_strvalue_len((char *)pools[i], talloc_array_length(pools[i])));
1744 }
1745 INFO("--");
1746 }
1747
1748 talloc_free(pools);
1749 }
1750
1751 /*
1752 * Fixup the operations without specific pools or ranges
1753 * and parse the IP ranges.
1754 */
1755 end = p;
1756 for (p = ops; p < end; p++) {
1757 if (parse_ip_range(&p->start, &p->end, p->name, p->prefix) < 0) usage(64);
1758 if (!p->prefix) p->prefix = IPADDR_LEN(p->start.af);
1759
1760 if (!p->pool) {
1761 p->pool = pool_arg;
1762 p->pool_len = talloc_array_length(pool_arg);
1763 }
1764 if (!p->range && range_arg) {
1765 p->range = range_arg;
1766 p->range_len = talloc_array_length(range_arg);
1767 }
1768 }
1769
1770 for (p = ops; (p < end) && (p->start.af != AF_UNSPEC); p++) switch (p->action) {
1771 case IPPOOL_TOOL_ADD:
1772 {
1773 uint64_t count = 0;
1774
1775 if (driver_add_lease(&count, conf->driver, p) < 0) {
1776 fr_exit_now(EXIT_FAILURE);
1777 }
1778 INFO("Added %" PRIu64 " address(es)/prefix(es)", count);
1779 }
1780 break;
1781
1782 case IPPOOL_TOOL_REMOVE:
1783 {
1784 uint64_t count = 0;
1785
1786 if (driver_remove_lease(&count, conf->driver, p) < 0) {
1787 fr_exit_now(EXIT_FAILURE);
1788 }
1789 INFO("Removed %" PRIu64 " address(es)/prefix(es)", count);
1790 }
1791 continue;
1792
1794 {
1795 uint64_t count = 0;
1796
1797 if (driver_release_lease(&count, conf->driver, p) < 0) {
1798 fr_exit_now(EXIT_FAILURE);
1799 }
1800 INFO("Released %" PRIu64 " address(es)/prefix(es)", count);
1801 }
1802 continue;
1803
1804 case IPPOOL_TOOL_SHOW:
1805 {
1806 ippool_tool_lease_t **leases = NULL;
1807 size_t len, i;
1808
1809 if (driver_show_lease(&leases, conf->driver, p) < 0) {
1810 fr_exit_now(EXIT_FAILURE);
1811 }
1812 if (!fr_cond_assert(leases)) continue;
1813
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++) {
1817 char ip_buff[FR_IPADDR_PREFIX_STRLEN];
1818 char time_buff[30];
1819 struct tm tm;
1820 struct timeval now;
1821 char *device = NULL;
1822 char *gateway = NULL;
1823 char *range = NULL;
1824 bool is_active;
1825
1826 leases[i] = talloc_get_type_abort(leases[i], ippool_tool_lease_t);
1827
1828 now = fr_time_to_timeval(fr_time());
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",
1832 localtime_r(&(leases[i]->next_event), &tm));
1833 } else {
1834 time_buff[0] = '\0';
1835 }
1836 IPPOOL_SPRINT_IP(ip_buff, &(leases[i]->ipaddr), leases[i]->ipaddr.prefix);
1837
1838 if (leases[i]->range) {
1839 range = fr_asprint(leases, (char const *)leases[i]->range,
1840 leases[i]->range_len, '\0');
1841 }
1842
1843 INFO("--");
1844 if (range) INFO("range : %s", range);
1845 INFO("address/prefix : %s", ip_buff);
1846 INFO("active : %s", is_active ? "yes" : "no");
1847
1848 if (leases[i]->device) {
1849 device = fr_asprint(leases, (char const *)leases[i]->device,
1850 leases[i]->device_len, '\0');
1851 }
1852 if (leases[i]->gateway) {
1853 gateway = fr_asprint(leases, (char const *)leases[i]->gateway,
1854 leases[i]->gateway_len, '\0');
1855 }
1856 if (is_active) {
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);
1860 } else {
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);
1864 }
1865 }
1866 talloc_free(leases);
1867 }
1868 continue;
1869
1870 case IPPOOL_TOOL_MODIFY:
1871 {
1872 uint64_t count = 0;
1873
1874 if (driver_modify_lease(&count, conf->driver, p) < 0) {
1875 fr_exit_now(EXIT_FAILURE);
1876 }
1877 INFO("Modified %" PRIu64 " address(es)/prefix(es)", count);
1878 }
1879 continue;
1880
1881 case IPPOOL_TOOL_ASSIGN:
1882 {
1883 uint64_t count = 0;
1884
1885 if (fr_ipaddr_cmp(&p->start, &p->end) != 0) {
1886 ERROR("Static assignment requires a single IP");
1887 fr_exit_now(EXIT_FAILURE);
1888 }
1889 if (!owner) {
1890 ERROR("Static assignment requires an owner");
1891 fr_exit_now(EXIT_FAILURE);
1892 }
1893
1894 if (driver_assign_lease(&count, conf->driver, p, owner) < 0) {
1895 fr_exit_now(EXIT_FAILURE);
1896 }
1897 INFO("Assigned %" PRIu64 " address(es)/prefix(es)", count);
1898 }
1899 continue;
1900
1902 {
1903 uint64_t count = 0;
1904
1905 if (fr_ipaddr_cmp(&p->start, &p->end) != 0) {
1906 ERROR("Static lease un-assignment requires a single IP");
1907 fr_exit_now(EXIT_FAILURE);
1908 }
1909 if (!owner) {
1910 ERROR("Static lease un-assignment requires an owner");
1911 fr_exit_now(EXIT_FAILURE);
1912 }
1913
1914 if (driver_unassign_lease(&count, conf->driver, p, owner) < 0) {
1915 fr_exit_now(EXIT_FAILURE);
1916 }
1917 INFO("Un-assigned %" PRIu64 " address(es)/prefix(es)", count);
1918 }
1919 continue;
1920
1921 case IPPOOL_TOOL_NOOP:
1922 break;
1923 }
1924
1926
1927 return 0;
1928}
#define RCSID(id)
Definition build.h:485
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:315
#define STRINGIFY(x)
Definition build.h:197
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:112
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
int cf_file_read(CONF_SECTION *cs, char const *filename)
Definition cf_file.c:3519
int cf_section_pass2(CONF_SECTION *cs)
Definition cf_file.c:755
int cf_section_parse(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs)
Parse a configuration section into user-supplied variables.
Definition cf_parse.c:1155
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:658
#define cf_section_rules_push(_cs, _rule)
Definition cf_parse.h:690
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:595
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
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.
Definition cf_util.c:1279
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1028
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition cf_util.c:1439
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
Definition cf_util.h:140
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.
Definition cluster.c:2261
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.
Definition cluster.c:1863
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.
Definition cluster.c:2070
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.
Definition cluster.c:2138
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.
Definition cluster.c:1741
A redis cluster.
Definition cluster.c:251
Common functions for interacting with Redis cluster via Hiredis.
Redis connection sequence state.
Definition cluster.h:49
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define MEM(x)
Definition debug.h:36
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:234
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
static NEVER_RETURNS void usage(void)
Definition dhcpclient.c:114
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.
Definition inet.c:778
int8_t fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
Definition inet.c:1346
void fr_ipaddr_mask(fr_ipaddr_t *addr, uint8_t prefix)
Zeroes out the host portion of an fr_ipaddr_t.
Definition inet.c:217
uint8_t prefix
Prefix length - Between 0-32 for IPv4 and 0-128 for IPv6.
Definition inet.h:69
int af
Address family.
Definition inet.h:64
union fr_ipaddr_t::@131 addr
#define FR_IPADDR_PREFIX_STRLEN
Like FR_IPADDR_STRLEN but with space for a prefix.
Definition inet.h:93
IPv4/6 prefix.
#define PERROR(_fmt,...)
Definition log.h:228
talloc_free(reap)
int fr_debug_lvl
Definition log.c:43
@ L_DBG_LVL_3
3rd highest priority debug messages (-xxx | -Xx).
Definition log.h:72
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
unsigned long int size_t
static size_t used
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.
Definition misc.c:429
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition missing.c:163
void fr_pool_connection_release(fr_pool_t *pool, request_t *request, void *conn)
Release a connection.
Definition pool.c:1407
void * fr_pool_connection_get(fr_pool_t *pool, request_t *request)
Reserve a connection in the connection pool.
Definition pool.c:1392
A connection pool.
Definition pool.c:87
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.
Definition print.c:428
#define is_truncated(_ret, _max)
Definition print.h:48
#define fr_assert(_expr)
Definition rad_assert.h:38
#define INFO(fmt,...)
Definition radict.c:54
static rs_t * conf
Definition radsniff.c:53
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.
Definition redis.c:142
static void fr_redis_pipeline_free(redisReply *reply[], size_t num)
Definition base.h:70
redisContext * handle
Hiredis context used when issuing commands.
Definition base.h:101
#define REDIS_COMMON_CONFIG
Definition base.h:133
static void fr_redis_reply_free(redisReply **reply)
Wrap freeReplyObject so we consistently check for NULL pointers.
Definition base.h:64
fr_redis_rcode_t fr_redis_command_status(fr_redis_conn_t *conn, redisReply *reply)
Check the reply for errors.
Definition redis.c:71
fr_table_num_sorted_t const redis_reply_types[]
Definition redis.c:30
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.
Definition redis.c:539
fr_redis_rcode_t
Codes are ordered inversely by priority.
Definition base.h:87
@ REDIS_RCODE_SUCCESS
Operation was successful.
Definition base.h:88
@ REDIS_RCODE_TRY_AGAIN
Try the operation again.
Definition base.h:90
Configuration parameters for a redis connection.
Definition base.h:109
Connection handle, holding a redis context.
Definition base.h:100
#define IPPOOL_STATIC_BIT
#define IPADDR_LEN(_af)
#define IPPOOL_MAX_POOL_KEY_SIZE
{prefix}:pool
#define IPPOOL_OWNER_KEY
#define IPPOOL_POOL_KEY
#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.
uint64_t static_tot
Static assignments configured.
static char lua_remove_cmd[]
Lua script for removing a lease.
int main(int argc, char *argv[])
time_t next_event
Last state change.
static int _driver_show_lease_process(void *out, fr_ipaddr_t const *ipaddr, redisReply const *reply)
Enqueue commands to retrieve lease information.
char const * name
Original range or CIDR string.
fr_redis_conf_t conf
Connection parameters for the Redis server.
uint8_t const * gateway
Last gateway id.
static int _driver_add_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
Count the number of leases we actually added.
fr_ipaddr_t ipaddr
Prefix or address.
static int _driver_add_lease_enqueue(UNUSED redis_driver_conf_t *inst, fr_redis_conn_t *conn, uint8_t const *key_prefix, size_t key_prefix_len, uint8_t const *range, size_t range_len, fr_ipaddr_t *ipaddr, uint8_t prefix, UNUSED void *uctx)
Enqueue lease addition commands.
static int _driver_unassign_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
static char lua_assign_cmd[]
Lua script for assigning a static lease.
static int driver_remove_lease(void *out, void *instance, ippool_tool_operation_t const *op)
Remove a range of leases.
#define MAX_PIPELINED
static int _driver_modify_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
Count the number of leases we modified.
static int _driver_assign_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
uint8_t const * range
Range the lease belongs to.
#define STATS_COMMANDS_TOTAL
static int driver_get_stats(ippool_tool_stats_t *out, void *instance, uint8_t const *key_prefix, size_t key_prefix_len)
static int driver_unassign_lease(void *out, void *instance, ippool_tool_operation_t const *op, char const *owner)
Unassign static lease assignments.
static int driver_do_lease(void *out, void *instance, ippool_tool_operation_t const *op, redis_ippool_queue_t enqueue, redis_ippool_process_t process, void *uctx)
Add a net to the pool.
#define EOL
static int _driver_show_lease_enqueue(UNUSED redis_driver_conf_t *inst, fr_redis_conn_t *conn, uint8_t const *key_prefix, size_t key_prefix_len, UNUSED uint8_t const *range, UNUSED size_t range_len, fr_ipaddr_t *ipaddr, uint8_t prefix, UNUSED void *uctx)
Enqueue commands to retrieve lease information.
size_t pool_len
Length of the pool identifier.
uint64_t static_30m
Static leases that should renew in the next 30 minutes.
static char const * name
uint64_t expiring_1m
Addresses that expire in the next minute.
static int driver_assign_lease(void *out, void *instance, ippool_tool_operation_t const *op, char const *owner)
Add static lease assignments.
int(* redis_ippool_process_t)(void *out, fr_ipaddr_t const *ipaddr, redisReply const *reply)
static uint32_t uint32_gen_mask(uint8_t bits)
uint64_t expiring_30m
Addresses that expire in the next 30 minutes.
uint64_t expiring_1h
Addresses that expire in the next hour.
static ssize_t driver_get_pools(TALLOC_CTX *ctx, uint8_t **out[], void *instance)
Return the pools available across the cluster.
static char lua_unassign_cmd[]
Lua script for un-assigning a static lease.
static conf_parser_t redis_config[]
uint64_t total
Addresses available.
static int _driver_release_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
Count the number of leases we released.
static int8_t pool_cmp(void const *a, void const *b)
Compare two pool names.
uint64_t static_1h
Static leases that should renew in the next hour.
static int _driver_remove_lease_enqueue(UNUSED redis_driver_conf_t *inst, fr_redis_conn_t *conn, uint8_t const *key_prefix, size_t key_prefix_len, UNUSED uint8_t const *range, UNUSED size_t range_len, fr_ipaddr_t *ipaddr, uint8_t prefix, UNUSED void *uctx)
Enqueue lease removal commands.
#define ADD_ACTION(_action)
static char lua_release_cmd[]
Lua script for releasing a lease.
static int driver_add_lease(void *out, void *instance, ippool_tool_operation_t const *op)
Add a range of prefixes.
fr_redis_cluster_t * cluster
static int _driver_remove_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
Count the number of leases we removed.
static int driver_release_lease(void *out, void *instance, ippool_tool_operation_t const *op)
Release a range of leases.
static int driver_init(TALLOC_CTX *ctx, CONF_SECTION *conf, void **instance)
Driver initialization function.
uint8_t const * range
Range identifier.
uint8_t prefix
Prefix - The bits between the address mask, and the prefix form the addresses to be modified in the p...
uint8_t const * pool
Pool identifier.
ippool_tool_action_t action
What to do to the leases described by net/prefix.
static int _driver_modify_lease_enqueue(UNUSED redis_driver_conf_t *inst, fr_redis_conn_t *conn, uint8_t const *key_prefix, size_t key_prefix_len, uint8_t const *range, size_t range_len, fr_ipaddr_t *ipaddr, uint8_t prefix, UNUSED void *uctx)
Enqueue lease removal commands.
uint64_t free
Addresses in use.
static int _driver_assign_lease_enqueue(UNUSED redis_driver_conf_t *inst, fr_redis_conn_t *conn, uint8_t const *key_prefix, size_t key_prefix_len, uint8_t const *range, size_t range_len, fr_ipaddr_t *ipaddr, uint8_t prefix, void *uctx)
Enqueue static lease assignment commands.
uint64_t expiring_1d
Addresses that expire in the next day.
static int driver_modify_lease(void *out, void *instance, ippool_tool_operation_t const *op)
Remove a range of leases.
uint64_t static_free
Static leases that have not been requested.
size_t range_len
Length of the range identifier.
fr_ipaddr_t end
End address.
uint64_t static_1m
Static leases that should renew in the next minute.
ippool_tool_action
Pool management actions.
@ IPPOOL_TOOL_ADD
Add one or more IP addresses.
@ IPPOOL_TOOL_REMOVE
Remove one or more IP addresses.
@ IPPOOL_TOOL_NOOP
Do nothing.
@ IPPOOL_TOOL_MODIFY
Modify attributes of one or more IP addresses.
@ IPPOOL_TOOL_SHOW
Show one or more IP addresses.
@ IPPOOL_TOOL_ASSIGN
Assign a static IP address to a device.
@ IPPOOL_TOOL_RELEASE
Release one or more IP addresses.
@ IPPOOL_TOOL_UNASSIGN
Remove static IP address assignment.
static int parse_ip_range(fr_ipaddr_t *start_out, fr_ipaddr_t *end_out, char const *ip_str, uint8_t prefix)
Convert an IP range or CIDR mask to a start and stop address.
fr_ipaddr_t start
Start address.
#define IPPOOL_BUILD_IP_KEY_FROM_STR(_buff, _p, _key, _key_len, _ip_str)
static int _driver_unassign_lease_enqueue(UNUSED redis_driver_conf_t *inst, fr_redis_conn_t *conn, uint8_t const *key_prefix, size_t key_prefix_len, UNUSED uint8_t const *range, UNUSED size_t range_len, fr_ipaddr_t *ipaddr, uint8_t prefix, void *uctx)
Enqueue static lease un-assignment commands.
int(* redis_ippool_queue_t)(redis_driver_conf_t *inst, fr_redis_conn_t *conn, uint8_t const *key_prefix, size_t key_prefix_len, uint8_t const *range, size_t range_len, fr_ipaddr_t *ipaddr, uint8_t prefix, void *uctx)
static bool ipaddr_next(fr_ipaddr_t *ipaddr, fr_ipaddr_t const *end, uint8_t prefix)
Iterate over range of IP addresses.
uint64_t static_1d
Static leases that should renew in the next day.
static int _driver_release_lease_enqueue(UNUSED redis_driver_conf_t *inst, fr_redis_conn_t *conn, uint8_t const *key_prefix, size_t key_prefix_len, UNUSED uint8_t const *range, UNUSED size_t range_len, fr_ipaddr_t *ipaddr, uint8_t prefix, UNUSED void *uctx)
Release a lease by setting its score back to zero.
static int driver_show_lease(void *out, void *instance, ippool_tool_operation_t const *op)
Show information about leases.
uint8_t const * device
Last device id.
enum ippool_tool_action ippool_tool_action_t
Pool management actions.
A single pool operation.
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_used(_sbuff_or_marker)
Talloc sbuff extension structure.
Definition sbuff.h:139
return count
Definition module.c:163
eap_aka_sim_process_conf_t * inst
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
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)
Definition time.h:731
#define fr_time_to_timeval(_when)
Convert server epoch time to unix epoch time.
Definition time.h:742
"server local" time.
Definition time.h:69
@ T_BARE_WORD
Definition token.h:120
@ T_OP_EQ
Definition token.h:83
@ T_DOUBLE_QUOTED_STRING
Definition token.h:121
static uint128_t uint128_new(uint64_t h, uint64_t l)
Creates a new uint128_t from a uint64_t.
Definition uint128.h:268
static uint128_t uint128_gen_mask(uint8_t bits)
Create a 128 bit integer value with n bits high.
Definition uint128.h:65
static uint128_t uint128_bor(uint128_t a, uint128_t b)
Perform bitwise | of two 128bit unsigned integers.
Definition uint128.h:239
static uint128_t uint128_lshift(uint128_t num, uint8_t bits)
Left shift 128 bit integer.
Definition uint128.h:191
static bool uint128_gt(uint128_t a, uint128_t b)
Return whether one integer is greater than the other.
Definition uint128.h:258
static bool uint128_eq(uint128_t a, uint128_t b)
Return whether the integers are equal.
Definition uint128.h:250
static uint128_t uint128_sub(uint128_t a, uint128_t b)
Subtract one 128bit integer from another.
Definition uint128.h:128
static uint128_t uint128_add(uint128_t a, uint128_t b)
Add two 128bit unsigned integers.
Definition uint128.h:115
Holds information necessary for binding or connecting to a socket.
Definition socket.h:63
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.
Definition value.c:1128
#define fr_box_strvalue_len(_val, _len)
Definition value.h:297
static size_t char ** out
Definition value.h:1012