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: 656d4f7b0a594028fd32bd57926e6a09e0deba4b $
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: 656d4f7b0a594028fd32bd57926e6a09e0deba4b $")
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);
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
993 if (fr_redis_cluster_pool_by_node_addr(&pool, inst->cluster, &master[i], false) < 0) {
994 ERROR("Failed retrieving pool for node");
995 error:
996 TALLOC_FREE(result);
997 talloc_free(master);
998 return -1;
999 }
1000
1001 conn = fr_pool_connection_get(pool, NULL);
1002 if (!conn) goto error;
1003 do {
1004 /*
1005 * Break up the scan so we don't block any single
1006 * Redis node too long.
1007 */
1008 reply = redisCommand(conn->handle, "SCAN %s MATCH %b COUNT 20", cursor, key, key_p - key);
1009 if (!reply) {
1010 ERROR("Failed reading reply");
1011 fr_pool_connection_release(pool, NULL, conn);
1012 goto error;
1013 }
1014 fr_redis_reply_print(L_DBG_LVL_3, reply, NULL, 0);
1015 if (fr_redis_command_status(conn, reply) != REDIS_RCODE_SUCCESS) {
1016 PERROR("Error retrieving keys %s", cursor);
1017
1018 reply_error:
1019 fr_pool_connection_release(pool, NULL, conn);
1020 fr_redis_reply_free(&reply);
1021 goto error;
1022 }
1023
1024 if (reply->type != REDIS_REPLY_ARRAY) {
1025 ERROR("Failed retrieving result, expected array got %s",
1026 fr_table_str_by_value(redis_reply_types, reply->type, "<UNKNOWN>"));
1027
1028 goto reply_error;
1029 }
1030
1031 if (reply->elements != 2) {
1032 ERROR("Failed retrieving result, expected array with two elements, got %zu elements",
1033 reply->elements);
1034 fr_redis_reply_free(&reply);
1035 goto reply_error;
1036 }
1037
1038 if (reply->element[0]->type != REDIS_REPLY_STRING) {
1039 ERROR("Failed retrieving result, expected string got %s",
1040 fr_table_str_by_value(redis_reply_types, reply->element[0]->type, "<UNKNOWN>"));
1041 goto reply_error;
1042 }
1043
1044 if (reply->element[1]->type != REDIS_REPLY_ARRAY) {
1045 ERROR("Failed retrieving result, expected array got %s",
1046 fr_table_str_by_value(redis_reply_types, reply->element[1]->type, "<UNKNOWN>"));
1047 goto reply_error;
1048 }
1049
1050 if ((talloc_array_length(result) - used) < reply->element[1]->elements) {
1051 MEM(result = talloc_realloc(ctx, result, uint8_t *,
1052 used + reply->element[1]->elements));
1053 if (!result) {
1054 ERROR("Failed expanding array of pool names");
1055 goto reply_error;
1056 }
1057 }
1058 strlcpy(cursor, reply->element[0]->str, sizeof(cursor));
1059
1060 for (k = 0; k < reply->element[1]->elements; k++) {
1061 redisReply *pool_key = reply->element[1]->element[k];
1062
1063 /*
1064 * Skip over things which are not pool names
1065 */
1066 if (pool_key->len < 7) continue; /* { + [<name>] + }:pool */
1067
1068 if ((pool_key->str[0]) != '{') continue;
1069 p = memchr(pool_key->str + 1, '}', pool_key->len - 1);
1070 if (!p) continue;
1071
1072 len = (pool_key->len - ((p + 1) - pool_key->str));
1073 if (len != (sizeof(IPPOOL_POOL_KEY) - 1) + 1) continue;
1074 if (memcmp(p + 1, ":" IPPOOL_POOL_KEY, (sizeof(IPPOOL_POOL_KEY) - 1) + 1) != 0) {
1075 continue;
1076 }
1077
1078 /*
1079 * String between the curly braces is the pool name
1080 */
1081 result[used++] = talloc_memdup(result, pool_key->str + 1, (p - pool_key->str) - 1);
1082 }
1083
1084 fr_redis_reply_free(&reply);
1085 } while (!((cursor[0] == '0') && (cursor[1] == '\0'))); /* Cursor value of 0 means no more results */
1086
1087 fr_pool_connection_release(pool, NULL, conn);
1088 }
1089
1090 if (used == 0) {
1091 *out = NULL;
1092 talloc_free(result);
1093 return 0;
1094 }
1095
1096 /*
1097 * Sort the results
1098 */
1099 if (used > 1) {
1100 uint8_t const **to_sort;
1101
1102 memcpy(&to_sort, &result, sizeof(to_sort));
1103
1104 fr_quick_sort((void const **)to_sort, 0, used - 1, pool_cmp);
1105 }
1106
1107 *out = talloc_array(ctx, uint8_t *, used);
1108 if (!*out) {
1109 ERROR("Failed allocating file pool name array");
1110 talloc_free(result);
1111 return -1;
1112 }
1113
1114 /*
1115 * SCAN can produce duplicates, remove them here
1116 */
1117 i = 0;
1118 k = 0;
1119 do { /* stop before last entry */
1120 (*out)[k++] = talloc_steal(*out, result[i++]);
1121 while ((i < used) && (pool_cmp(result[i - 1], result[i]) == 0)) i++;
1122 } while (i < used);
1123
1124 talloc_free(result);
1125
1126 return used;
1127}
1128
1129static int driver_get_stats(ippool_tool_stats_t *out, void *instance, uint8_t const *key_prefix, size_t key_prefix_len)
1130{
1131 redis_driver_conf_t *inst = talloc_get_type_abort(instance, redis_driver_conf_t);
1133 uint8_t *key_p = key;
1134
1135 fr_redis_conn_t *conn;
1136
1138 fr_redis_rcode_t status;
1139 fr_time_t now;
1140
1141 int s_ret = REDIS_RCODE_SUCCESS;
1142 redisReply **replies = NULL, *reply;
1143 unsigned int pipelined = 0; /* Update if additional commands added */
1144
1145 size_t reply_cnt = 0, i = 0;
1146
1147#define STATS_COMMANDS_TOTAL 14
1148
1149 IPPOOL_BUILD_KEY(key, key_p, key_prefix, key_prefix_len);
1150
1151 MEM(replies = talloc_zero_array(inst, redisReply *, STATS_COMMANDS_TOTAL));
1152
1153 now = fr_time();
1154
1155 for (s_ret = fr_redis_cluster_state_init(&state, &conn, inst->cluster, NULL, key, key_p - key, false);
1156 s_ret == REDIS_RCODE_TRY_AGAIN;
1157 s_ret = fr_redis_cluster_state_next(&state, &conn, inst->cluster, NULL, status, &replies[0])) {
1158 status = REDIS_RCODE_SUCCESS;
1159
1160 redisAppendCommand(conn->handle, "MULTI");
1161 redisAppendCommand(conn->handle, "ZCARD %b", key, key_p - key); /* Total */
1162 redisAppendCommand(conn->handle, "ZCOUNT %b -inf %i",
1163 key, key_p - key, fr_time_to_sec(now)); /* Free */
1164 redisAppendCommand(conn->handle, "ZCOUNT %b -inf %i",
1165 key, key_p - key, fr_time_to_sec(now) + 60); /* Free in next 60s */
1166 redisAppendCommand(conn->handle, "ZCOUNT %b -inf %i",
1167 key, key_p - key, fr_time_to_sec(now) + (60 * 30)); /* Free in next 30 mins */
1168 redisAppendCommand(conn->handle, "ZCOUNT %b -inf %i",
1169 key, key_p - key, fr_time_to_sec(now) + (60 * 60)); /* Free in next 60 mins */
1170 redisAppendCommand(conn->handle, "ZCOUNT %b -inf %i",
1171 key, key_p - key, fr_time_to_sec(now) + (60 * 60 * 24)); /* Free in next day */
1172 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " inf",
1173 key, key_p - key); /* Total static */
1174 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " %"PRIu64,
1175 key, key_p - key, IPPOOL_STATIC_BIT + fr_time_to_sec(now)); /* Static assignments 'free' */
1176 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " %"PRIu64,
1177 key, key_p - key,
1178 IPPOOL_STATIC_BIT + fr_time_to_sec(now) + 60); /* Static renew in 60s */
1179 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " %"PRIu64,
1180 key, key_p - key,
1181 IPPOOL_STATIC_BIT + fr_time_to_sec(now) + (60 * 30)); /* Static renew in 30 mins */
1182 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " %"PRIu64,
1183 key, key_p - key,
1184 IPPOOL_STATIC_BIT + fr_time_to_sec(now) + (60 * 60)); /* Static renew in 60 mins */
1185 redisAppendCommand(conn->handle, "ZCOUNT %b " STRINGIFY(IPPOOL_STATIC_BIT) " %"PRIu64,
1186 key, key_p - key,
1187 IPPOOL_STATIC_BIT + fr_time_to_sec(now) + (60 * 60 * 24)); /* Static renew in 1 day */
1188 redisAppendCommand(conn->handle, "EXEC");
1189 if (!replies) return -1;
1190
1191 pipelined = STATS_COMMANDS_TOTAL;
1192 reply_cnt = fr_redis_pipeline_result(&pipelined, &status, replies,
1193 talloc_array_length(replies), conn);
1194 for (i = 0; (size_t)i < reply_cnt; i++) fr_redis_reply_print(L_DBG_LVL_3,
1195 replies[i], NULL, i);
1196 }
1197 if (s_ret != REDIS_RCODE_SUCCESS) {
1198 error:
1199 fr_redis_pipeline_free(replies, reply_cnt);
1200 talloc_free(replies);
1201 return -1;
1202 }
1203
1204 if (reply_cnt != STATS_COMMANDS_TOTAL) {
1205 ERROR("Failed retrieving pool stats: Expected %i replies, got %zu", pipelined, reply_cnt);
1206 goto error;
1207 }
1208
1209 reply = replies[reply_cnt - 1];
1210
1211 if (reply->type != REDIS_REPLY_ARRAY) {
1212 ERROR("Failed retrieving pool stats: Expected array got %s",
1213 fr_table_str_by_value(redis_reply_types, reply->element[1]->type, "<UNKNOWN>"));
1214 goto error;
1215 }
1216
1217 if (reply->elements != (reply_cnt - 2)) {
1218 ERROR("Failed retrieving pool stats: Expected %zu results, got %zu", reply_cnt - 2, reply->elements);
1219 goto error;
1220 }
1221
1222 if (reply->element[0]->integer == 0) {
1223 ERROR("Pool not found");
1224 goto error;
1225 }
1226
1227 out->total = reply->element[0]->integer;
1228 out->free = reply->element[1]->integer;
1229 out->expiring_1m = reply->element[2]->integer - out->free;
1230 out->expiring_30m = reply->element[3]->integer - out->free;
1231 out->expiring_1h = reply->element[4]->integer - out->free;
1232 out->expiring_1d = reply->element[5]->integer - out->free;
1233 out->static_tot = reply->element[6]->integer;
1234 out->static_free = reply->element[7]->integer;
1235 out->static_1m = reply->element[8]->integer - out->static_free;
1236 out->static_30m = reply->element[9]->integer - out->static_free;
1237 out->static_1h = reply->element[10]->integer - out->static_free;
1238 out->static_1d = reply->element[11]->integer - out->static_free;
1239
1240 fr_redis_pipeline_free(replies, reply_cnt);
1241 talloc_free(replies);
1242
1243 return 0;
1244}
1245
1246/** Driver initialization function
1247 *
1248 */
1249static int driver_init(TALLOC_CTX *ctx, CONF_SECTION *conf, void **instance)
1250{
1251 redis_driver_conf_t *this;
1252 int ret;
1253
1254 *instance = NULL;
1255
1256 if (cf_section_rules_push(conf, redis_config) < 0) return -1;
1257
1258 this = talloc_zero(ctx, redis_driver_conf_t);
1259 if (!this) return -1;
1260
1261 ret = cf_section_parse(this, &this->conf, conf);
1262 if (ret < 0) {
1263 talloc_free(this);
1264 return -1;
1265 }
1266
1267 this->cluster = fr_redis_cluster_alloc(this, conf, &this->conf, false,
1268 "rlm_redis_ippool_tool", NULL, NULL);
1269 if (!this->cluster) {
1270 talloc_free(this);
1271 return -1;
1272 }
1273 *instance = this;
1274
1275 return 0;
1276}
1277
1278/** Convert an IP range or CIDR mask to a start and stop address
1279 *
1280 * @param[out] start_out Where to write the start address.
1281 * @param[out] end_out Where to write the end address.
1282 * @param[in] ip_str Unparsed IP string.
1283 * @param[in] prefix length of prefixes we'll be allocating.
1284 * @return
1285 * - 0 on success.
1286 * - -1 on failure.
1287 */
1288static int parse_ip_range(fr_ipaddr_t *start_out, fr_ipaddr_t *end_out, char const *ip_str, uint8_t prefix)
1289{
1290 fr_ipaddr_t start, end;
1291 bool ex_broadcast;
1292 char const *p;
1293
1294 p = strchr(ip_str, '-');
1295 if (p) {
1296 char start_buff[INET6_ADDRSTRLEN + 4];
1297 char end_buff[INET6_ADDRSTRLEN + 4];
1298 size_t len;
1299
1300 if ((size_t)(p - ip_str) >= sizeof(start_buff)) {
1301 ERROR("Start address too long");
1302 return -1;
1303 }
1304
1305 len = strlcpy(start_buff, ip_str, (p - ip_str) + 1);
1306 if (is_truncated(len, sizeof(start_buff))) {
1307 ERROR("Start address too long");
1308 return -1;
1309 }
1310
1311 len = strlcpy(end_buff, p + 1, sizeof(end_buff));
1312 if (is_truncated(len, sizeof(end_buff))) {
1313 ERROR("End address too long");
1314 return -1;
1315 }
1316
1317 if (fr_inet_pton(&start, start_buff, -1, AF_UNSPEC, false, true) < 0) {
1318 PERROR("Failed parsing \"%s\" as start address", start_buff);
1319 return -1;
1320 }
1321
1322 if (fr_inet_pton(&end, end_buff, -1, AF_UNSPEC, false, true) < 0) {
1323 PERROR("Failed parsing \"%s\" end address", end_buff);
1324 return -1;
1325 }
1326
1327 if (start.af != end.af) {
1328 ERROR("Start and end address must be of the same address family");
1329 return -1;
1330 }
1331
1332 if (!prefix) prefix = IPADDR_LEN(start.af);
1333
1334 /*
1335 * IPv6 addresses
1336 */
1337 if (start.af == AF_INET6) {
1338 uint128_t start_int, end_int;
1339
1340 memcpy(&start_int, start.addr.v6.s6_addr, sizeof(start_int));
1341 memcpy(&end_int, end.addr.v6.s6_addr, sizeof(end_int));
1342 if (uint128_gt(ntohlll(start_int), ntohlll(end_int))) {
1343 ERROR("End address must be greater than or equal to start address");
1344 return -1;
1345 }
1346 /*
1347 * IPv4 addresses
1348 */
1349 } else {
1350 if (ntohl((uint32_t)(start.addr.v4.s_addr)) >
1351 ntohl((uint32_t)(end.addr.v4.s_addr))) {
1352 ERROR("End address must be greater than or equal to start address");
1353 return -1;
1354 }
1355 }
1356
1357 /*
1358 * Mask start and end so we can do prefix ranges too
1359 */
1360 fr_ipaddr_mask(&start, prefix);
1361 fr_ipaddr_mask(&end, prefix);
1362 start.prefix = prefix;
1363 end.prefix = prefix;
1364
1365 *start_out = start;
1366 *end_out = end;
1367
1368 return 0;
1369 }
1370
1371 if (fr_inet_pton(&start, ip_str, -1, AF_UNSPEC, false, false) < 0) {
1372 ERROR("Failed parsing \"%s\" as IPv4/v6 subnet", ip_str);
1373 return -1;
1374 }
1375
1376 if (!prefix) prefix = IPADDR_LEN(start.af);
1377
1378 if (prefix < start.prefix) {
1379 ERROR("-p must be greater than or equal to /<mask> (%u)", start.prefix);
1380 return -1;
1381 }
1382 if (prefix > IPADDR_LEN(start.af)) {
1383 ERROR("-p must be less than or equal to address length (%u)", IPADDR_LEN(start.af));
1384 return -1;
1385 }
1386
1387 if ((prefix - start.prefix) > 64) {
1388 ERROR("-p must be less than or equal to %u", start.prefix + 64);
1389 return -1;
1390 }
1391
1392 /*
1393 * Exclude the broadcast address only if we're dealing with IPv4 addresses
1394 * if we're allocating IPv6 addresses or prefixes we don't need to.
1395 */
1396 ex_broadcast = (start.af == AF_INET) && (IPADDR_LEN(start.af) == prefix);
1397
1398 /*
1399 * Excluding broadcast, 31/32 or 127/128 start/end are the same
1400 */
1401 if (ex_broadcast && (start.prefix >= (IPADDR_LEN(start.af) - 1))) {
1402 *start_out = start;
1403 *end_out = start;
1404 return 0;
1405 }
1406
1407 /*
1408 * Set various fields (we only overwrite the IP later)
1409 */
1410 end = start;
1411
1412 if (start.af == AF_INET6) {
1413 uint128_t ip, p_mask;
1414
1415 /* cond assert to satisfy clang scan */
1416 if (!fr_cond_assert((prefix > 0) && (prefix <= 128))) return -1;
1417
1418 /* Don't be tempted to cast */
1419 memcpy(&ip, start.addr.v6.s6_addr, sizeof(ip));
1420 ip = ntohlll(ip);
1421
1422 /* Generate a mask that covers the prefix bits, and sets them high */
1423 p_mask = uint128_lshift(uint128_gen_mask(prefix - start.prefix), (128 - prefix));
1424 ip = htonlll(uint128_bor(p_mask, ip));
1425
1426 /* Decrement by one */
1427 if (ex_broadcast) ip = uint128_sub(ip, uint128_new(0, 1));
1428 memcpy(&end.addr.v6.s6_addr, &ip, sizeof(end.addr.v6.s6_addr));
1429 } else {
1430 uint32_t ip;
1431
1432 /* cond assert to satisfy clang scan */
1433 if (!fr_cond_assert((prefix > 0) && (prefix <= 32))) return -1;
1434
1435 ip = ntohl(start.addr.v4.s_addr);
1436
1437 /* Generate a mask that covers the prefix bits and sets them high */
1438 ip |= uint32_gen_mask(prefix - start.prefix) << (32 - prefix);
1439
1440 /* Decrement by one */
1441 if (ex_broadcast) ip--;
1442 end.addr.v4.s_addr = htonl(ip);
1443 }
1444
1445 *start_out = start;
1446 *end_out = end;
1447
1448 return 0;
1449}
1450
1451int main(int argc, char *argv[])
1452{
1453 static ippool_tool_operation_t ops[128];
1454 ippool_tool_operation_t *p = ops, *end = ops + (NUM_ELEMENTS(ops));
1455
1456 int c;
1457
1458 uint8_t *range_arg = NULL;
1459 uint8_t *pool_arg = NULL;
1460 bool do_export = false, print_stats = false, list_pools = false;
1461 bool need_pool = false;
1462 char *do_import = NULL;
1463 char const *filename = NULL;
1464 char const *owner = NULL;
1465
1466 CONF_SECTION *pool_cs;
1467 CONF_PAIR *cp;
1469
1470 fr_debug_lvl = 0;
1471 name = argv[0];
1472
1473 conf = talloc_zero(NULL, ippool_tool_t);
1474 conf->cs = cf_section_alloc(conf, NULL, "main", NULL);
1475 if (!conf->cs) fr_exit_now(EXIT_FAILURE);
1476
1477#define ADD_ACTION(_action) \
1478do { \
1479 if (p >= end) { \
1480 ERROR("Too many actions, max is " STRINGIFY(sizeof(ops))); \
1481 usage(64); \
1482 } \
1483 p->action = _action; \
1484 p->name = optarg; \
1485 p++; \
1486 need_pool = true; \
1487} while (0)
1488
1489 while ((c = getopt(argc, argv, "a:d:r:s:Sm:A:U:O:p:ilLhxo:f:")) != -1) switch (c) {
1490 case 'a':
1492 break;
1493
1494 case 'd':
1496 break;
1497
1498 case 'r':
1500 break;
1501
1502 case 's':
1504 break;
1505
1506 case 'm':
1508 break;
1509
1510 case 'A':
1512 break;
1513
1514 case 'U':
1516 break;
1517
1518 case 'O':
1519 owner = optarg;
1520 break;
1521
1522 case 'p':
1523 {
1524 unsigned long tmp;
1525 char *q;
1526
1527 if (p == ops) {
1528 ERROR("Prefix may only be specified after a pool management action");
1529 usage(64);
1530 }
1531
1532 tmp = strtoul(optarg, &q, 10);
1533 if (q != (optarg + strlen(optarg))) {
1534 ERROR("Prefix must be an integer value");
1535
1536 }
1537
1538 (p - 1)->prefix = (uint8_t)tmp & 0xff;
1539 }
1540 break;
1541
1542 case 'i':
1543 do_import = optarg;
1544 break;
1545
1546 case 'I':
1547 do_export = true;
1548 break;
1549
1550 case 'l':
1551 if (list_pools) usage(1); /* Only allowed once */
1552 list_pools = true;
1553 break;
1554
1555 case 'S':
1556 print_stats = true;
1557 break;
1558
1559 case 'h':
1560 usage(0);
1561
1562 case 'x':
1563 fr_debug_lvl++;
1564 break;
1565
1566 case 'o':
1567 break;
1568
1569 case 'f':
1570 filename = optarg;
1571 break;
1572
1573 default:
1574 usage(1);
1575 }
1576 argc -= optind;
1577 argv += optind;
1578
1579 if (argc == 0) {
1580 ERROR("Need server address/port");
1581 usage(64);
1582 }
1583 if ((argc == 1) && need_pool) {
1584 ERROR("Need pool to operate on");
1585 usage(64);
1586 }
1587 if (argc > 3) usage(64);
1588
1589 /*
1590 * Read configuration files if necessary.
1591 */
1592 if (filename && (cf_file_read(conf->cs, filename) < 0 || (cf_section_pass2(conf->cs) < 0))) {
1593 fr_exit_now(EXIT_FAILURE);
1594 }
1595
1596 cp = cf_pair_alloc(conf->cs, "server", argv[0], T_OP_EQ, T_BARE_WORD, T_DOUBLE_QUOTED_STRING);
1597 if (!cp) {
1598 ERROR("Failed creating server pair");
1599 fr_exit_now(EXIT_FAILURE);
1600 }
1601
1602 /*
1603 * Unescape sequences in the pool name
1604 */
1605 if (argv[1] && (argv[1][0] != '\0')) {
1608
1609 MEM(fr_sbuff_init_talloc(conf, &out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1610 (void) fr_value_str_unescape(&out,
1611 &FR_SBUFF_IN(argv[1], strlen(argv[1])), SIZE_MAX, '"');
1612 talloc_realloc(conf, out.buff, uint8_t, fr_sbuff_used(&out));
1613 pool_arg = (uint8_t *)out.buff;
1614 }
1615
1616 if (argc >= 3 && (argv[2][0] != '\0')) {
1619
1620 MEM(fr_sbuff_init_talloc(conf, &out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1621 (void) fr_value_str_unescape(&out,
1622 &FR_SBUFF_IN(argv[2], strlen(argv[2])), SIZE_MAX, '"');
1623 talloc_realloc(conf, out.buff, uint8_t, fr_sbuff_used(&out));
1624 range_arg = (uint8_t *)out.buff;
1625 }
1626
1627 if (!do_import && !do_export && !list_pools && !print_stats && (p == ops)) {
1628 ERROR("Nothing to do!");
1629 fr_exit_now(EXIT_FAILURE);
1630 }
1631
1632 /*
1633 * Set some alternative default pool settings
1634 */
1635 pool_cs = cf_section_find(conf->cs, "pool", NULL);
1636 if (!pool_cs) {
1637 pool_cs = cf_section_alloc(conf->cs, conf->cs, "pool", NULL);
1638 }
1639 cp = cf_pair_find(pool_cs, "start");
1640 if (!cp) {
1641 /*
1642 * Start should always default to 1
1643 * else the cluster code doesn't
1644 * map the cluster.
1645 */
1646 (void) cf_pair_alloc(pool_cs, "start", "1", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1647 }
1648 cp = cf_pair_find(pool_cs, "spare");
1649 if (!cp) {
1650 (void) cf_pair_alloc(pool_cs, "spare", "0", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1651 }
1652 cp = cf_pair_find(pool_cs, "min");
1653 if (!cp) {
1654 (void) cf_pair_alloc(pool_cs, "min", "0", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1655 }
1656 cp = cf_pair_find(pool_cs, "max");
1657 if (!cp) {
1658 /*
1659 * Set a safe default for "max" - as this is a stand alone tool,
1660 * it can't use automatic value from the worker thread count.
1661 */
1662 (void) cf_pair_alloc(pool_cs, "max", "10", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1663 }
1664
1665 if (driver_init(conf, conf->cs, &conf->driver) < 0) {
1666 ERROR("Driver initialisation failed");
1667 fr_exit_now(EXIT_FAILURE);
1668 }
1669
1670 if (do_import) {
1671 ERROR("NOT YET IMPLEMENTED");
1672 }
1673
1674 if (do_export) {
1675 ERROR("NOT YET IMPLEMENTED");
1676 }
1677
1678 if (print_stats) {
1679 ippool_tool_stats_t stats;
1680 uint8_t **pools;
1681 ssize_t slen;
1682 size_t i;
1683
1684 if (pool_arg) {
1685 pools = talloc_zero_array(conf, uint8_t *, 1);
1686 slen = 1;
1687 pools[0] = pool_arg;
1688 } else {
1689 slen = driver_get_pools(conf, &pools, conf->driver);
1690 if (slen < 0) fr_exit_now(EXIT_FAILURE);
1691 }
1692
1693 for (i = 0; i < (size_t)slen; i++) {
1694 if (driver_get_stats(&stats, conf->driver,
1695 pools[i], talloc_array_length(pools[i])) < 0) fr_exit_now(EXIT_FAILURE);
1696
1697 INFO("pool : %pV", fr_box_strvalue_len((char *)pools[i],
1698 talloc_array_length(pools[i])));
1699 INFO("total : %" PRIu64, stats.total);
1700 INFO("dynamic total : %" PRIu64, stats.total - stats.static_tot);
1701 INFO("dynamic free : %" PRIu64, stats.free);
1702 INFO("dynamic used : %" PRIu64, stats.total - stats.free - stats.static_tot);
1703 if ((stats.total - stats.static_tot) > 0) {
1704 INFO("dynamic used (%%) : %.2Lf",
1705 ((long double)(stats.total - stats.free - stats.static_tot) /
1706 (long double)(stats.total - stats.static_tot)) * 100);
1707 } else {
1708 INFO("used (%%) : 0");
1709 }
1710 INFO("expiring 0-1m : %" PRIu64, stats.expiring_1m);
1711 INFO("expiring 1-30m : %" PRIu64, stats.expiring_30m - stats.expiring_1m);
1712 INFO("expiring 30m-1h : %" PRIu64, stats.expiring_1h - stats.expiring_30m);
1713 INFO("expiring 1h-1d : %" PRIu64, stats.expiring_1d - stats.expiring_1h);
1714 INFO("static total : %" PRIu64, stats.static_tot);
1715 INFO("static 'free' : %" PRIu64, stats.static_free);
1716 INFO("static issued : %" PRIu64, stats.static_tot - stats.static_free);
1717 if (stats.static_tot) {
1718 INFO("static issued (%%) : %.2Lf",
1719 ((long double)(stats.static_tot - stats.static_free) /
1720 (long double)(stats.static_tot)) * 100);
1721 } else {
1722 INFO("static issued (%%) : 0");
1723 }
1724 INFO("static renew 0-1m : %" PRIu64, stats.static_1m);
1725 INFO("static renew 1-30m : %" PRIu64, stats.static_30m - stats.static_1m);
1726 INFO("static renew 30m-1h : %" PRIu64, stats.static_1h - stats.static_30m);
1727 INFO("static renew 1h-1d : %" PRIu64, stats.static_1d - stats.static_1h);
1728 INFO("--");
1729 }
1730 }
1731
1732 if (list_pools) {
1733 ssize_t slen;
1734 size_t i;
1735 uint8_t **pools;
1736
1737 slen = driver_get_pools(conf, &pools, conf->driver);
1738 if (slen < 0) fr_exit_now(EXIT_FAILURE);
1739 if (slen > 0) {
1740 for (i = 0; i < (size_t)slen; i++) {
1741 INFO("%pV", fr_box_strvalue_len((char *)pools[i], talloc_array_length(pools[i])));
1742 }
1743 INFO("--");
1744 }
1745
1746 talloc_free(pools);
1747 }
1748
1749 /*
1750 * Fixup the operations without specific pools or ranges
1751 * and parse the IP ranges.
1752 */
1753 end = p;
1754 for (p = ops; p < end; p++) {
1755 if (parse_ip_range(&p->start, &p->end, p->name, p->prefix) < 0) usage(64);
1756 if (!p->prefix) p->prefix = IPADDR_LEN(p->start.af);
1757
1758 if (!p->pool) {
1759 p->pool = pool_arg;
1760 p->pool_len = talloc_array_length(pool_arg);
1761 }
1762 if (!p->range && range_arg) {
1763 p->range = range_arg;
1764 p->range_len = talloc_array_length(range_arg);
1765 }
1766 }
1767
1768 for (p = ops; (p < end) && (p->start.af != AF_UNSPEC); p++) switch (p->action) {
1769 case IPPOOL_TOOL_ADD:
1770 {
1771 uint64_t count = 0;
1772
1773 if (driver_add_lease(&count, conf->driver, p) < 0) {
1774 fr_exit_now(EXIT_FAILURE);
1775 }
1776 INFO("Added %" PRIu64 " address(es)/prefix(es)", count);
1777 }
1778 break;
1779
1780 case IPPOOL_TOOL_REMOVE:
1781 {
1782 uint64_t count = 0;
1783
1784 if (driver_remove_lease(&count, conf->driver, p) < 0) {
1785 fr_exit_now(EXIT_FAILURE);
1786 }
1787 INFO("Removed %" PRIu64 " address(es)/prefix(es)", count);
1788 }
1789 continue;
1790
1792 {
1793 uint64_t count = 0;
1794
1795 if (driver_release_lease(&count, conf->driver, p) < 0) {
1796 fr_exit_now(EXIT_FAILURE);
1797 }
1798 INFO("Released %" PRIu64 " address(es)/prefix(es)", count);
1799 }
1800 continue;
1801
1802 case IPPOOL_TOOL_SHOW:
1803 {
1804 ippool_tool_lease_t **leases = NULL;
1805 size_t len, i;
1806
1807 if (driver_show_lease(&leases, conf->driver, p) < 0) {
1808 fr_exit_now(EXIT_FAILURE);
1809 }
1810 if (!fr_cond_assert(leases)) continue;
1811
1812 len = talloc_array_length(leases);
1813 INFO("Retrieved information for %zu address(es)/prefix(es)", len - 1);
1814 for (i = 0; i < (len - 1); i++) {
1815 char ip_buff[FR_IPADDR_PREFIX_STRLEN];
1816 char time_buff[30];
1817 struct tm tm;
1818 struct timeval now;
1819 char *device = NULL;
1820 char *gateway = NULL;
1821 char *range = NULL;
1822 bool is_active;
1823
1824 leases[i] = talloc_get_type_abort(leases[i], ippool_tool_lease_t);
1825
1826 now = fr_time_to_timeval(fr_time());
1827 is_active = now.tv_sec <= leases[i]->next_event;
1828 if (leases[i]->next_event) {
1829 strftime(time_buff, sizeof(time_buff), "%b %e %Y %H:%M:%S %Z",
1830 localtime_r(&(leases[i]->next_event), &tm));
1831 } else {
1832 time_buff[0] = '\0';
1833 }
1834 IPPOOL_SPRINT_IP(ip_buff, &(leases[i]->ipaddr), leases[i]->ipaddr.prefix);
1835
1836 if (leases[i]->range) {
1837 range = fr_asprint(leases, (char const *)leases[i]->range,
1838 leases[i]->range_len, '\0');
1839 }
1840
1841 INFO("--");
1842 if (range) INFO("range : %s", range);
1843 INFO("address/prefix : %s", ip_buff);
1844 INFO("active : %s", is_active ? "yes" : "no");
1845
1846 if (leases[i]->device) {
1847 device = fr_asprint(leases, (char const *)leases[i]->device,
1848 leases[i]->device_len, '\0');
1849 }
1850 if (leases[i]->gateway) {
1851 gateway = fr_asprint(leases, (char const *)leases[i]->gateway,
1852 leases[i]->gateway_len, '\0');
1853 }
1854 if (is_active) {
1855 if (*time_buff) INFO("lease expires : %s", time_buff);
1856 if (device) INFO("device id : %s", device);
1857 if (gateway) INFO("gateway id : %s", gateway);
1858 } else {
1859 if (*time_buff) INFO("lease expired : %s", time_buff);
1860 if (device) INFO("last device id : %s", device);
1861 if (gateway) INFO("last gateway id : %s", gateway);
1862 }
1863 }
1864 talloc_free(leases);
1865 }
1866 continue;
1867
1868 case IPPOOL_TOOL_MODIFY:
1869 {
1870 uint64_t count = 0;
1871
1872 if (driver_modify_lease(&count, conf->driver, p) < 0) {
1873 fr_exit_now(EXIT_FAILURE);
1874 }
1875 INFO("Modified %" PRIu64 " address(es)/prefix(es)", count);
1876 }
1877 continue;
1878
1879 case IPPOOL_TOOL_ASSIGN:
1880 {
1881 uint64_t count = 0;
1882
1883 if (fr_ipaddr_cmp(&p->start, &p->end) != 0) {
1884 ERROR("Static assignment requires a single IP");
1885 fr_exit_now(EXIT_FAILURE);
1886 }
1887 if (!owner) {
1888 ERROR("Static assignment requires an owner");
1889 fr_exit_now(EXIT_FAILURE);
1890 }
1891
1892 if (driver_assign_lease(&count, conf->driver, p, owner) < 0) {
1893 fr_exit_now(EXIT_FAILURE);
1894 }
1895 INFO("Assigned %" PRIu64 " address(es)/prefix(es)", count);
1896 }
1897 continue;
1898
1900 {
1901 uint64_t count = 0;
1902
1903 if (fr_ipaddr_cmp(&p->start, &p->end) != 0) {
1904 ERROR("Static lease un-assignment requires a single IP");
1905 fr_exit_now(EXIT_FAILURE);
1906 }
1907 if (!owner) {
1908 ERROR("Static lease un-assignment requires an owner");
1909 fr_exit_now(EXIT_FAILURE);
1910 }
1911
1912 if (driver_unassign_lease(&count, conf->driver, p, owner) < 0) {
1913 fr_exit_now(EXIT_FAILURE);
1914 }
1915 INFO("Un-assigned %" PRIu64 " address(es)/prefix(es)", count);
1916 }
1917 continue;
1918
1919 case IPPOOL_TOOL_NOOP:
1920 break;
1921 }
1922
1924
1925 return 0;
1926}
#define RCSID(id)
Definition build.h:483
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:313
#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:315
#define NUM_ELEMENTS(_t)
Definition build.h:337
int cf_file_read(CONF_SECTION *cs, char const *filename)
Definition cf_file.c:3423
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:1124
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
#define cf_section_rules_push(_cs, _rule)
Definition cf_parse.h:674
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:579
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
#define FR_IPADDR_PREFIX_STRLEN
Like FR_IPADDR_STRLEN but with space for a prefix.
Definition inet.h:93
union fr_ipaddr_t::@130 addr
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
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
void fr_redis_reply_print(fr_log_lvl_t lvl, redisReply *reply, request_t *request, int idx)
Print the response data in a useful treelike form.
Definition redis.c:141
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:535
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:286
static size_t char ** out
Definition value.h:997