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: b49be5f3f3df9e76fd6c60eb9359dad8460aa79b $
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: b49be5f3f3df9e76fd6c60eb9359dad8460aa79b $")
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 /*
1270 * Triggers won't work from the tool
1271 */
1272 this->conf.triggers = false;
1273 this->cluster = fr_redis_cluster_alloc(this, conf, &this->conf,
1274 "rlm_redis_ippool_tool", NULL, NULL);
1275 if (!this->cluster) {
1276 talloc_free(this);
1277 return -1;
1278 }
1279 *instance = this;
1280
1281 return 0;
1282}
1283
1284/** Convert an IP range or CIDR mask to a start and stop address
1285 *
1286 * @param[out] start_out Where to write the start address.
1287 * @param[out] end_out Where to write the end address.
1288 * @param[in] ip_str Unparsed IP string.
1289 * @param[in] prefix length of prefixes we'll be allocating.
1290 * @return
1291 * - 0 on success.
1292 * - -1 on failure.
1293 */
1294static int parse_ip_range(fr_ipaddr_t *start_out, fr_ipaddr_t *end_out, char const *ip_str, uint8_t prefix)
1295{
1296 fr_ipaddr_t start, end;
1297 bool ex_broadcast;
1298 char const *p;
1299
1300 p = strchr(ip_str, '-');
1301 if (p) {
1302 char start_buff[INET6_ADDRSTRLEN + 4];
1303 char end_buff[INET6_ADDRSTRLEN + 4];
1304 size_t len;
1305
1306 if ((size_t)(p - ip_str) >= sizeof(start_buff)) {
1307 ERROR("Start address too long");
1308 return -1;
1309 }
1310
1311 len = strlcpy(start_buff, ip_str, (p - ip_str) + 1);
1312 if (is_truncated(len, sizeof(start_buff))) {
1313 ERROR("Start address too long");
1314 return -1;
1315 }
1316
1317 len = strlcpy(end_buff, p + 1, sizeof(end_buff));
1318 if (is_truncated(len, sizeof(end_buff))) {
1319 ERROR("End address too long");
1320 return -1;
1321 }
1322
1323 if (fr_inet_pton(&start, start_buff, -1, AF_UNSPEC, false, true) < 0) {
1324 PERROR("Failed parsing \"%s\" as start address", start_buff);
1325 return -1;
1326 }
1327
1328 if (fr_inet_pton(&end, end_buff, -1, AF_UNSPEC, false, true) < 0) {
1329 PERROR("Failed parsing \"%s\" end address", end_buff);
1330 return -1;
1331 }
1332
1333 if (start.af != end.af) {
1334 ERROR("Start and end address must be of the same address family");
1335 return -1;
1336 }
1337
1338 if (!prefix) prefix = IPADDR_LEN(start.af);
1339
1340 /*
1341 * IPv6 addresses
1342 */
1343 if (start.af == AF_INET6) {
1344 uint128_t start_int, end_int;
1345
1346 memcpy(&start_int, start.addr.v6.s6_addr, sizeof(start_int));
1347 memcpy(&end_int, end.addr.v6.s6_addr, sizeof(end_int));
1348 if (uint128_gt(ntohlll(start_int), ntohlll(end_int))) {
1349 ERROR("End address must be greater than or equal to start address");
1350 return -1;
1351 }
1352 /*
1353 * IPv4 addresses
1354 */
1355 } else {
1356 if (ntohl((uint32_t)(start.addr.v4.s_addr)) >
1357 ntohl((uint32_t)(end.addr.v4.s_addr))) {
1358 ERROR("End address must be greater than or equal to start address");
1359 return -1;
1360 }
1361 }
1362
1363 /*
1364 * Mask start and end so we can do prefix ranges too
1365 */
1366 fr_ipaddr_mask(&start, prefix);
1367 fr_ipaddr_mask(&end, prefix);
1368 start.prefix = prefix;
1369 end.prefix = prefix;
1370
1371 *start_out = start;
1372 *end_out = end;
1373
1374 return 0;
1375 }
1376
1377 if (fr_inet_pton(&start, ip_str, -1, AF_UNSPEC, false, false) < 0) {
1378 ERROR("Failed parsing \"%s\" as IPv4/v6 subnet", ip_str);
1379 return -1;
1380 }
1381
1382 if (!prefix) prefix = IPADDR_LEN(start.af);
1383
1384 if (prefix < start.prefix) {
1385 ERROR("-p must be greater than or equal to /<mask> (%u)", start.prefix);
1386 return -1;
1387 }
1388 if (prefix > IPADDR_LEN(start.af)) {
1389 ERROR("-p must be less than or equal to address length (%u)", IPADDR_LEN(start.af));
1390 return -1;
1391 }
1392
1393 if ((prefix - start.prefix) > 64) {
1394 ERROR("-p must be less than or equal to %u", start.prefix + 64);
1395 return -1;
1396 }
1397
1398 /*
1399 * Exclude the broadcast address only if we're dealing with IPv4 addresses
1400 * if we're allocating IPv6 addresses or prefixes we don't need to.
1401 */
1402 ex_broadcast = (start.af == AF_INET) && (IPADDR_LEN(start.af) == prefix);
1403
1404 /*
1405 * Excluding broadcast, 31/32 or 127/128 start/end are the same
1406 */
1407 if (ex_broadcast && (start.prefix >= (IPADDR_LEN(start.af) - 1))) {
1408 *start_out = start;
1409 *end_out = start;
1410 return 0;
1411 }
1412
1413 /*
1414 * Set various fields (we only overwrite the IP later)
1415 */
1416 end = start;
1417
1418 if (start.af == AF_INET6) {
1419 uint128_t ip, p_mask;
1420
1421 /* cond assert to satisfy clang scan */
1422 if (!fr_cond_assert((prefix > 0) && (prefix <= 128))) return -1;
1423
1424 /* Don't be tempted to cast */
1425 memcpy(&ip, start.addr.v6.s6_addr, sizeof(ip));
1426 ip = ntohlll(ip);
1427
1428 /* Generate a mask that covers the prefix bits, and sets them high */
1429 p_mask = uint128_lshift(uint128_gen_mask(prefix - start.prefix), (128 - prefix));
1430 ip = htonlll(uint128_bor(p_mask, ip));
1431
1432 /* Decrement by one */
1433 if (ex_broadcast) ip = uint128_sub(ip, uint128_new(0, 1));
1434 memcpy(&end.addr.v6.s6_addr, &ip, sizeof(end.addr.v6.s6_addr));
1435 } else {
1436 uint32_t ip;
1437
1438 /* cond assert to satisfy clang scan */
1439 if (!fr_cond_assert((prefix > 0) && (prefix <= 32))) return -1;
1440
1441 ip = ntohl(start.addr.v4.s_addr);
1442
1443 /* Generate a mask that covers the prefix bits and sets them high */
1444 ip |= uint32_gen_mask(prefix - start.prefix) << (32 - prefix);
1445
1446 /* Decrement by one */
1447 if (ex_broadcast) ip--;
1448 end.addr.v4.s_addr = htonl(ip);
1449 }
1450
1451 *start_out = start;
1452 *end_out = end;
1453
1454 return 0;
1455}
1456
1457int main(int argc, char *argv[])
1458{
1459 static ippool_tool_operation_t ops[128];
1460 ippool_tool_operation_t *p = ops, *end = ops + (NUM_ELEMENTS(ops));
1461
1462 int c;
1463
1464 uint8_t *range_arg = NULL;
1465 uint8_t *pool_arg = NULL;
1466 bool do_export = false, print_stats = false, list_pools = false;
1467 bool need_pool = false;
1468 char *do_import = NULL;
1469 char const *filename = NULL;
1470 char const *owner = NULL;
1471
1472 CONF_SECTION *pool_cs;
1473 CONF_PAIR *cp;
1475
1476 fr_debug_lvl = 0;
1477 name = argv[0];
1478
1479 conf = talloc_zero(NULL, ippool_tool_t);
1480 conf->cs = cf_section_alloc(conf, NULL, "main", NULL);
1481 if (!conf->cs) fr_exit_now(EXIT_FAILURE);
1482
1483#define ADD_ACTION(_action) \
1484do { \
1485 if (p >= end) { \
1486 ERROR("Too many actions, max is " STRINGIFY(sizeof(ops))); \
1487 usage(64); \
1488 } \
1489 p->action = _action; \
1490 p->name = optarg; \
1491 p++; \
1492 need_pool = true; \
1493} while (0)
1494
1495 while ((c = getopt(argc, argv, "a:d:r:s:Sm:A:U:O:p:ilLhxo:f:")) != -1) switch (c) {
1496 case 'a':
1498 break;
1499
1500 case 'd':
1502 break;
1503
1504 case 'r':
1506 break;
1507
1508 case 's':
1510 break;
1511
1512 case 'm':
1514 break;
1515
1516 case 'A':
1518 break;
1519
1520 case 'U':
1522 break;
1523
1524 case 'O':
1525 owner = optarg;
1526 break;
1527
1528 case 'p':
1529 {
1530 unsigned long tmp;
1531 char *q;
1532
1533 if (p == ops) {
1534 ERROR("Prefix may only be specified after a pool management action");
1535 usage(64);
1536 }
1537
1538 tmp = strtoul(optarg, &q, 10);
1539 if (q != (optarg + strlen(optarg))) {
1540 ERROR("Prefix must be an integer value");
1541
1542 }
1543
1544 (p - 1)->prefix = (uint8_t)tmp & 0xff;
1545 }
1546 break;
1547
1548 case 'i':
1549 do_import = optarg;
1550 break;
1551
1552 case 'I':
1553 do_export = true;
1554 break;
1555
1556 case 'l':
1557 if (list_pools) usage(1); /* Only allowed once */
1558 list_pools = true;
1559 break;
1560
1561 case 'S':
1562 print_stats = true;
1563 break;
1564
1565 case 'h':
1566 usage(0);
1567
1568 case 'x':
1569 fr_debug_lvl++;
1570 break;
1571
1572 case 'o':
1573 break;
1574
1575 case 'f':
1576 filename = optarg;
1577 break;
1578
1579 default:
1580 usage(1);
1581 }
1582 argc -= optind;
1583 argv += optind;
1584
1585 if (argc == 0) {
1586 ERROR("Need server address/port");
1587 usage(64);
1588 }
1589 if ((argc == 1) && need_pool) {
1590 ERROR("Need pool to operate on");
1591 usage(64);
1592 }
1593 if (argc > 3) usage(64);
1594
1595 /*
1596 * Read configuration files if necessary.
1597 */
1598 if (filename && (cf_file_read(conf->cs, filename) < 0 || (cf_section_pass2(conf->cs) < 0))) {
1599 fr_exit_now(EXIT_FAILURE);
1600 }
1601
1602 cp = cf_pair_alloc(conf->cs, "server", argv[0], T_OP_EQ, T_BARE_WORD, T_DOUBLE_QUOTED_STRING);
1603 if (!cp) {
1604 ERROR("Failed creating server pair");
1605 fr_exit_now(EXIT_FAILURE);
1606 }
1607
1608 /*
1609 * Unescape sequences in the pool name
1610 */
1611 if (argv[1] && (argv[1][0] != '\0')) {
1614
1615 MEM(fr_sbuff_init_talloc(conf, &out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1616 (void) fr_value_str_unescape(&out,
1617 &FR_SBUFF_IN_STR(argv[1]), SIZE_MAX, '"');
1618 talloc_realloc(conf, out.buff, uint8_t, fr_sbuff_used(&out));
1619 pool_arg = (uint8_t *)out.buff;
1620 }
1621
1622 if (argc >= 3 && (argv[2][0] != '\0')) {
1625
1626 MEM(fr_sbuff_init_talloc(conf, &out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1627 (void) fr_value_str_unescape(&out,
1628 &FR_SBUFF_IN_STR(argv[2]), SIZE_MAX, '"');
1629 talloc_realloc(conf, out.buff, uint8_t, fr_sbuff_used(&out));
1630 range_arg = (uint8_t *)out.buff;
1631 }
1632
1633 if (!do_import && !do_export && !list_pools && !print_stats && (p == ops)) {
1634 ERROR("Nothing to do!");
1635 fr_exit_now(EXIT_FAILURE);
1636 }
1637
1638 /*
1639 * Set some alternative default pool settings
1640 */
1641 pool_cs = cf_section_find(conf->cs, "pool", NULL);
1642 if (!pool_cs) {
1643 pool_cs = cf_section_alloc(conf->cs, conf->cs, "pool", NULL);
1644 }
1645 cp = cf_pair_find(pool_cs, "start");
1646 if (!cp) {
1647 /*
1648 * Start should always default to 1
1649 * else the cluster code doesn't
1650 * map the cluster.
1651 */
1652 (void) cf_pair_alloc(pool_cs, "start", "1", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1653 }
1654 cp = cf_pair_find(pool_cs, "spare");
1655 if (!cp) {
1656 (void) cf_pair_alloc(pool_cs, "spare", "0", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1657 }
1658 cp = cf_pair_find(pool_cs, "min");
1659 if (!cp) {
1660 (void) cf_pair_alloc(pool_cs, "min", "0", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1661 }
1662 cp = cf_pair_find(pool_cs, "max");
1663 if (!cp) {
1664 /*
1665 * Set a safe default for "max" - as this is a stand alone tool,
1666 * it can't use automatic value from the worker thread count.
1667 */
1668 (void) cf_pair_alloc(pool_cs, "max", "10", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1669 }
1670
1671 if (driver_init(conf, conf->cs, &conf->driver) < 0) {
1672 ERROR("Driver initialisation failed");
1673 fr_exit_now(EXIT_FAILURE);
1674 }
1675
1676 if (do_import) {
1677 ERROR("NOT YET IMPLEMENTED");
1678 }
1679
1680 if (do_export) {
1681 ERROR("NOT YET IMPLEMENTED");
1682 }
1683
1684 if (print_stats) {
1685 ippool_tool_stats_t stats;
1686 uint8_t **pools;
1687 ssize_t slen;
1688 size_t i;
1689
1690 if (pool_arg) {
1691 pools = talloc_zero_array(conf, uint8_t *, 1);
1692 slen = 1;
1693 pools[0] = pool_arg;
1694 } else {
1695 slen = driver_get_pools(conf, &pools, conf->driver);
1696 if (slen < 0) fr_exit_now(EXIT_FAILURE);
1697 }
1698
1699 for (i = 0; i < (size_t)slen; i++) {
1700 if (driver_get_stats(&stats, conf->driver,
1701 pools[i], talloc_array_length(pools[i])) < 0) fr_exit_now(EXIT_FAILURE);
1702
1703 INFO("pool : %pV", fr_box_strvalue_len((char *)pools[i],
1704 talloc_array_length(pools[i])));
1705 INFO("total : %" PRIu64, stats.total);
1706 INFO("dynamic total : %" PRIu64, stats.total - stats.static_tot);
1707 INFO("dynamic free : %" PRIu64, stats.free);
1708 INFO("dynamic used : %" PRIu64, stats.total - stats.free - stats.static_tot);
1709 if ((stats.total - stats.static_tot) > 0) {
1710 INFO("dynamic used (%%) : %.2Lf",
1711 ((long double)(stats.total - stats.free - stats.static_tot) /
1712 (long double)(stats.total - stats.static_tot)) * 100);
1713 } else {
1714 INFO("used (%%) : 0");
1715 }
1716 INFO("expiring 0-1m : %" PRIu64, stats.expiring_1m);
1717 INFO("expiring 1-30m : %" PRIu64, stats.expiring_30m - stats.expiring_1m);
1718 INFO("expiring 30m-1h : %" PRIu64, stats.expiring_1h - stats.expiring_30m);
1719 INFO("expiring 1h-1d : %" PRIu64, stats.expiring_1d - stats.expiring_1h);
1720 INFO("static total : %" PRIu64, stats.static_tot);
1721 INFO("static 'free' : %" PRIu64, stats.static_free);
1722 INFO("static issued : %" PRIu64, stats.static_tot - stats.static_free);
1723 if (stats.static_tot) {
1724 INFO("static issued (%%) : %.2Lf",
1725 ((long double)(stats.static_tot - stats.static_free) /
1726 (long double)(stats.static_tot)) * 100);
1727 } else {
1728 INFO("static issued (%%) : 0");
1729 }
1730 INFO("static renew 0-1m : %" PRIu64, stats.static_1m);
1731 INFO("static renew 1-30m : %" PRIu64, stats.static_30m - stats.static_1m);
1732 INFO("static renew 30m-1h : %" PRIu64, stats.static_1h - stats.static_30m);
1733 INFO("static renew 1h-1d : %" PRIu64, stats.static_1d - stats.static_1h);
1734 INFO("--");
1735 }
1736 }
1737
1738 if (list_pools) {
1739 ssize_t slen;
1740 size_t i;
1741 uint8_t **pools;
1742
1743 slen = driver_get_pools(conf, &pools, conf->driver);
1744 if (slen < 0) fr_exit_now(EXIT_FAILURE);
1745 if (slen > 0) {
1746 for (i = 0; i < (size_t)slen; i++) {
1747 INFO("%pV", fr_box_strvalue_len((char *)pools[i], talloc_array_length(pools[i])));
1748 }
1749 INFO("--");
1750 }
1751
1752 talloc_free(pools);
1753 }
1754
1755 /*
1756 * Fixup the operations without specific pools or ranges
1757 * and parse the IP ranges.
1758 */
1759 end = p;
1760 for (p = ops; p < end; p++) {
1761 if (parse_ip_range(&p->start, &p->end, p->name, p->prefix) < 0) usage(64);
1762 if (!p->prefix) p->prefix = IPADDR_LEN(p->start.af);
1763
1764 if (!p->pool) {
1765 p->pool = pool_arg;
1766 p->pool_len = talloc_array_length(pool_arg);
1767 }
1768 if (!p->range && range_arg) {
1769 p->range = range_arg;
1770 p->range_len = talloc_array_length(range_arg);
1771 }
1772 }
1773
1774 for (p = ops; (p < end) && (p->start.af != AF_UNSPEC); p++) switch (p->action) {
1775 case IPPOOL_TOOL_ADD:
1776 {
1777 uint64_t count = 0;
1778
1779 if (driver_add_lease(&count, conf->driver, p) < 0) {
1780 fr_exit_now(EXIT_FAILURE);
1781 }
1782 INFO("Added %" PRIu64 " address(es)/prefix(es)", count);
1783 }
1784 break;
1785
1786 case IPPOOL_TOOL_REMOVE:
1787 {
1788 uint64_t count = 0;
1789
1790 if (driver_remove_lease(&count, conf->driver, p) < 0) {
1791 fr_exit_now(EXIT_FAILURE);
1792 }
1793 INFO("Removed %" PRIu64 " address(es)/prefix(es)", count);
1794 }
1795 continue;
1796
1798 {
1799 uint64_t count = 0;
1800
1801 if (driver_release_lease(&count, conf->driver, p) < 0) {
1802 fr_exit_now(EXIT_FAILURE);
1803 }
1804 INFO("Released %" PRIu64 " address(es)/prefix(es)", count);
1805 }
1806 continue;
1807
1808 case IPPOOL_TOOL_SHOW:
1809 {
1810 ippool_tool_lease_t **leases = NULL;
1811 size_t len, i;
1812
1813 if (driver_show_lease(&leases, conf->driver, p) < 0) {
1814 fr_exit_now(EXIT_FAILURE);
1815 }
1816 if (!fr_cond_assert(leases)) continue;
1817
1818 len = talloc_array_length(leases);
1819 INFO("Retrieved information for %zu address(es)/prefix(es)", len - 1);
1820 for (i = 0; i < (len - 1); i++) {
1821 char ip_buff[FR_IPADDR_PREFIX_STRLEN];
1822 char time_buff[30];
1823 struct tm tm;
1824 struct timeval now;
1825 char *device = NULL;
1826 char *gateway = NULL;
1827 char *range = NULL;
1828 bool is_active;
1829
1830 leases[i] = talloc_get_type_abort(leases[i], ippool_tool_lease_t);
1831
1832 now = fr_time_to_timeval(fr_time());
1833 is_active = now.tv_sec <= leases[i]->next_event;
1834 if (leases[i]->next_event) {
1835 strftime(time_buff, sizeof(time_buff), "%b %e %Y %H:%M:%S %Z",
1836 localtime_r(&(leases[i]->next_event), &tm));
1837 } else {
1838 time_buff[0] = '\0';
1839 }
1840 IPPOOL_SPRINT_IP(ip_buff, &(leases[i]->ipaddr), leases[i]->ipaddr.prefix);
1841
1842 if (leases[i]->range) {
1843 range = fr_asprint(leases, (char const *)leases[i]->range,
1844 leases[i]->range_len, '\0');
1845 }
1846
1847 INFO("--");
1848 if (range) INFO("range : %s", range);
1849 INFO("address/prefix : %s", ip_buff);
1850 INFO("active : %s", is_active ? "yes" : "no");
1851
1852 if (leases[i]->device) {
1853 device = fr_asprint(leases, (char const *)leases[i]->device,
1854 leases[i]->device_len, '\0');
1855 }
1856 if (leases[i]->gateway) {
1857 gateway = fr_asprint(leases, (char const *)leases[i]->gateway,
1858 leases[i]->gateway_len, '\0');
1859 }
1860 if (is_active) {
1861 if (*time_buff) INFO("lease expires : %s", time_buff);
1862 if (device) INFO("device id : %s", device);
1863 if (gateway) INFO("gateway id : %s", gateway);
1864 } else {
1865 if (*time_buff) INFO("lease expired : %s", time_buff);
1866 if (device) INFO("last device id : %s", device);
1867 if (gateway) INFO("last gateway id : %s", gateway);
1868 }
1869 }
1870 talloc_free(leases);
1871 }
1872 continue;
1873
1874 case IPPOOL_TOOL_MODIFY:
1875 {
1876 uint64_t count = 0;
1877
1878 if (driver_modify_lease(&count, conf->driver, p) < 0) {
1879 fr_exit_now(EXIT_FAILURE);
1880 }
1881 INFO("Modified %" PRIu64 " address(es)/prefix(es)", count);
1882 }
1883 continue;
1884
1885 case IPPOOL_TOOL_ASSIGN:
1886 {
1887 uint64_t count = 0;
1888
1889 if (fr_ipaddr_cmp(&p->start, &p->end) != 0) {
1890 ERROR("Static assignment requires a single IP");
1891 fr_exit_now(EXIT_FAILURE);
1892 }
1893 if (!owner) {
1894 ERROR("Static assignment requires an owner");
1895 fr_exit_now(EXIT_FAILURE);
1896 }
1897
1898 if (driver_assign_lease(&count, conf->driver, p, owner) < 0) {
1899 fr_exit_now(EXIT_FAILURE);
1900 }
1901 INFO("Assigned %" PRIu64 " address(es)/prefix(es)", count);
1902 }
1903 continue;
1904
1906 {
1907 uint64_t count = 0;
1908
1909 if (fr_ipaddr_cmp(&p->start, &p->end) != 0) {
1910 ERROR("Static lease un-assignment requires a single IP");
1911 fr_exit_now(EXIT_FAILURE);
1912 }
1913 if (!owner) {
1914 ERROR("Static lease un-assignment requires an owner");
1915 fr_exit_now(EXIT_FAILURE);
1916 }
1917
1918 if (driver_unassign_lease(&count, conf->driver, p, owner) < 0) {
1919 fr_exit_now(EXIT_FAILURE);
1920 }
1921 INFO("Un-assigned %" PRIu64 " address(es)/prefix(es)", count);
1922 }
1923 continue;
1924
1925 case IPPOOL_TOOL_NOOP:
1926 break;
1927 }
1928
1930
1931 return 0;
1932}
#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:3565
int cf_section_pass2(CONF_SECTION *cs)
Definition cf_file.c:969
int cf_section_parse(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs)
Parse a configuration section into user-supplied variables.
Definition cf_parse.c:1195
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:660
#define cf_section_rules_push(_cs, _rule)
Definition cf_parse.h:692
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:597
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:1266
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:1027
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:1426
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
Definition cf_util.h:140
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
fr_redis_cluster_t * fr_redis_cluster_alloc(TALLOC_CTX *ctx, CONF_SECTION *module, fr_redis_conf_t *conf, char const *log_prefix, char const *trigger_prefix, fr_pair_list_t *trigger_args)
Allocate and initialise a new cluster structure.
Definition cluster.c:2260
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:131
#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:226
#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:779
int8_t fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
Definition inet.c:1347
void fr_ipaddr_mask(fr_ipaddr_t *addr, uint8_t prefix)
Zeroes out the host portion of an fr_ipaddr_t.
Definition inet.c:218
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
#define FR_IPADDR_PREFIX_STRLEN
Like FR_IPADDR_STRLEN but with space for a prefix.
Definition inet.h:93
union fr_ipaddr_t::@136 addr
IPv4/6 prefix.
#define PERROR(_fmt,...)
Definition log.h:228
talloc_free(reap)
int fr_debug_lvl
Definition log.c:40
@ 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:430
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:1405
void * fr_pool_connection_get(fr_pool_t *pool, request_t *request)
Reserve a connection in the connection pool.
Definition pool.c:1390
A connection pool.
Definition pool.c:85
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:135
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_STR(_start)
#define fr_sbuff_used(_sbuff_or_marker)
Talloc sbuff extension structure.
Definition sbuff.h:139
return count
Definition module.c:155
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:1220
#define fr_box_strvalue_len(_val, _len)
Definition value.h:308
static size_t char ** out
Definition value.h:1023