All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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: 7a49fb331ce0bb8aeff5bea8e9f2419a3b63668c $
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  */
27 RCSID("$Id: 7a49fb331ce0bb8aeff5bea8e9f2419a3b63668c $")
28 #include <freeradius-devel/libradius.h>
29 #include <freeradius-devel/rad_assert.h>
30 
31 #include "redis.h"
32 #include "cluster.h"
33 #include "redis_ippool.h"
34 
35 #define MAX_PIPELINED 1000
36 
37 #include <sys/wait.h>
38 
39 #undef rad_waitpid
40 pid_t rad_fork(void)
41 {
42  return fork();
43 }
44 
45 pid_t rad_waitpid(pid_t pid, int *status)
46 {
47  return waitpid(pid, status, 0);
48 }
49 
50 /** Pool management actions
51  *
52  */
53 typedef enum ippool_tool_action {
60 
61 /** A single pool operation
62  *
63  */
64 typedef struct ippool_tool_operation {
65  char const *name; //!< Original range or CIDR string.
66 
67  uint8_t const *pool; //!< Pool identifier.
68  size_t pool_len; //!< Length of the pool identifier.
69 
70  uint8_t const *range; //!< Range identifier.
71  size_t range_len; //!< Length of the range identifier.
72 
73  fr_ipaddr_t start; //!< Start address.
74  fr_ipaddr_t end; //!< End address.
75  uint8_t prefix; //!< Prefix - The bits between the address mask, and the prefix
76  //!< form the addresses to be modified in the pool.
77  ippool_tool_action_t action; //!< What to do to the leases described by net/prefix.
79 
80 typedef struct ippool_tool_lease {
81  fr_ipaddr_t ipaddr; //!< Prefix or address.
82  time_t next_event; //!< Last state change.
83  uint8_t const *range; //!< Range the lease belongs to.
84  size_t range_len;
85  uint8_t const *device; //!< Last device id.
86  size_t device_len;
87  uint8_t const *gateway; //!< Last gateway id.
88  size_t gateway_len;
90 
94 };
95 
96 typedef struct redis_driver_conf {
97  fr_redis_conf_t conf; //!< Connection parameters for the Redis server.
100 
101 typedef struct ippool_tool {
102  void *driver;
104 } ippool_tool_t;
105 
107  uint8_t const *key_prefix, size_t key_prefix_len,
108  uint8_t const *range, size_t range_len,
109  fr_ipaddr_t *ipaddr, uint8_t prefix);
110 
111 typedef int (*redis_ippool_process_t)(void *out, fr_ipaddr_t const *ipaddr, redisReply const *reply);
112 
113 #define IPPOOL_BUILD_IP_KEY_FROM_STR(_buff, _p, _key, _key_len, _ip_str) \
114 do { \
115  ssize_t _slen; \
116  *_p++ = '{'; \
117  memcpy(_p, _key, _key_len); \
118  _p += _key_len; \
119  _slen = strlcpy((char *)_p, "}:"IPPOOL_ADDRESS_KEY":", sizeof(_buff) - (_p - _buff)); \
120  if (is_truncated((size_t)_slen, sizeof(_buff) - (_p - _buff))) { \
121  ERROR("IP key too long"); \
122  return 0;\
123  } \
124  _p += (size_t)_slen;\
125  _p += strlcpy((char *)_p, _ip_str, sizeof(_buff) - (_p - _buff)); \
126 } while (0)
127 
128 
129 static char const *name;
130 
131 static void NEVER_RETURNS usage(int ret) {
132  INFO("Usage: %s [[-a|-d|-r] -p] [options] <server[:port]> <pool> [<range>]", name);
133  INFO("Pool management:");
134  INFO(" -a <addr> Add addresses/prefixes to the pool");
135  INFO(" -d <addr> Delete addresses/prefixes in this range");
136  INFO(" -r <addr> Release addresses/prefixes in this range");
137  INFO(" -s <addr> Show addresses/prefix in this range");
138  INFO(" -p <prefix_len> Length of prefix to allocate (defaults to 32/128)");
139 // INFO(" -i <file> Import entries from ISC lease file [NYI]");
140  INFO(" "); /* -Werror=format-zero-length */
141 // INFO("Pool status:");
142 // INFO(" -I Output active entries in ISC lease file format [NYI]");
143 // INFO(" -S Print pool statistics [NYI]");
144  INFO(" "); /* -Werror=format-zero-length */
145  INFO("Configuration:");
146  INFO(" -h Print this help message and exit");
147  INFO(" -x Increase the verbosity level");
148 // INFO(" -o <attr>=<value> Set option, these are specific to the backends [NYI]");
149  INFO(" -f <file> Load options from a FreeRADIUS (radisud) format config file");
150  INFO(" ");
151  INFO("<addr> is range \"127.0.0.1-127.0.0.254\" or CIDR network \"127.0.0.1/24\"");
152  INFO("CIDR host bits set start address, e.g. 127.0.0.200/24 -> 127.0.0.200-127.0.0.254");
153  INFO("CIDR /32 or /128 excludes upper broadcast address");
154  exit(ret);
155 }
156 
157 static uint32_t uint32_gen_mask(uint8_t bits)
158 {
159  if (bits >= 32) return 0xffffffff;
160  return (1 << bits) - 1;
161 }
162 
163 /*
164  * 128bit integers are not standard on many compilers
165  * despite SSE2 instructions for dealing with them
166  * specifically.
167  */
168 #ifndef HAVE_128BIT_INTEGERS
169 /** Create a 128 bit integer value with n bits high
170  *
171  */
172 static uint128_t uint128_gen_mask(uint8_t bits)
173 {
174  uint128_t ret;
175 
176  rad_assert(bits < 128);
177 
178  if (bits > 64) {
179  ret.l = 0xffffffffffffffff;
180  ret.h = (uint64_t)1 << (bits - 64);
181  ret.h ^= (ret.h - 1);
182  return ret;
183  }
184  ret.h = 0;
185  ret.l = (uint64_t)1 << bits;
186  ret.l ^= (ret.l - 1);
187 
188  return ret;
189 }
190 /** Left shift 128 bit integer
191  *
192  * @note shift must be 127 bits or less.
193  */
194 static uint128_t uint128_lshift(uint128_t num, uint8_t bits)
195 {
196  rad_assert(bits < 128);
197 
198  if (bits >= 64) {
199  num.l = 0;
200  num.h = num.l << (bits - 64);
201  return num;
202  }
203  num.h = (num.h << bits) | (num.l >> (64 - bits));
204  num.l <<= bits;
205 
206  return num;
207 }
208 
209 /** Add two 128bit unsigned integers
210  *
211  * @author Jacob F. W
212  * @note copied from http://www.codeproject.com/Tips/617214/UInt-Addition-Subtraction
213  */
214 static uint128_t uint128_add(uint128_t a, uint128_t b)
215 {
216  uint128_t ret;
217  uint64_t tmp = (((a.l & b.l) & 1) + (a.l >> 1) + (b.l >> 1)) >> 63;
218  ret.l = a.l + b.l;
219  ret.h = a.h + b.h + tmp;
220  return ret;
221 }
222 
223 /** Subtract one 128bit integer from another
224  *
225  * @author Jacob F. W
226  * @note copied from http://www.codeproject.com/Tips/617214/UInt-Addition-Subtraction
227  */
228 static uint128_t uint128_sub(uint128_t a, uint128_t b)
229 {
230  uint128_t ret;
231  uint64_t c;
232 
233  ret.l = a.l - b.l;
234  c = (((ret.l & b.l) & 1) + (b.l >> 1) + (ret.l >> 1)) >> 63;
235  ret.h = a.h - (b.h + c);
236 
237  return ret;
238 }
239 
240 /** Perform bitwise & of two 128bit unsigned integers
241  *
242  */
243 static uint128_t uint128_band(uint128_t a, uint128_t b)
244 {
245  uint128_t ret;
246  ret.l = a.l & b.l;
247  ret.h = a.h & b.h;
248  return ret;
249 }
250 
251 /** Perform bitwise | of two 128bit unsigned integers
252  *
253  */
254 static uint128_t uint128_bor(uint128_t a, uint128_t b)
255 {
256  uint128_t ret;
257  ret.l = a.l | b.l;
258  ret.h = a.h + b.h;
259  return ret;
260 }
261 
262 /** Return whether the integers are equal
263  *
264  */
265 static bool uint128_eq(uint128_t a, uint128_t b)
266 {
267  return (a.h == b.h) && (a.l == b.l);
268 }
269 
270 /** Return whether one integer is greater than the other
271  *
272  */
273 static bool uint128_gt(uint128_t a, uint128_t b)
274 {
275  if (a.h < b.h) return false;
276  if (a.h > b.h) return true;
277  return (a.l > b.l);
278 }
279 
280 /** Creates a new uint128_t from an uint64_t
281  *
282  */
283 static uint128_t uint128_new(uint64_t h, uint64_t l) {
284  uint128_t ret;
285  ret.l = l;
286  ret.h = h;
287  return ret;
288 }
289 #else
290 static uint128_t uint128_gen_mask(uint8_t bits)
291 {
292  if (bits >= 128) return ~(uint128_t)0x00;
293  return (((uint128_t)1) << bits) - 1;
294 }
295 #define uint128_lshift(_num, _bits) (_num << _bits)
296 //#define uint128_band(_a, _b) (_a & _b)
297 #define uint128_bor(_a, _b) (_a | _b)
298 #define uint128_eq(_a, _b) (_a == _b)
299 #define uint128_gt(_a, _b) (_a > _b)
300 #define uint128_add(_a, _b) (_a + _b)
301 #define uint128_sub(_a, _b) (_a - _b)
302 #define uint128_new(_a, _b) ((uint128_t)_b | ((uint128_t)_a << 64))
303 #endif
304 
305 /** Iterate over range of IP addresses
306  *
307  * Mutates the ipaddr passed in, adding one to the prefix bits on each call.
308  *
309  * @param[in,out] ipaddr to increment.
310  * @param[in] end ipaddr to stop at.
311  * @param[in] prefix Length of the prefix.
312  * @return
313  * - true if the prefix bits are not high (continue).
314  * - false if the prefix bits are high (stop).
315  */
316 static bool ipaddr_next(fr_ipaddr_t *ipaddr, fr_ipaddr_t const *end, uint8_t prefix)
317 {
318  switch (ipaddr->af) {
319  default:
320  case AF_UNSPEC:
321  rad_assert(0);
322  return false;
323 
324  case AF_INET6:
325  {
326  uint128_t ip_curr, ip_end;
327 
328  rad_assert((prefix > 0) && (prefix <= 128));
329 
330  /* Don't be tempted to cast */
331  memcpy(&ip_curr, ipaddr->ipaddr.ip6addr.s6_addr, sizeof(ip_curr));
332  memcpy(&ip_end, end->ipaddr.ip6addr.s6_addr, sizeof(ip_curr));
333 
334  ip_curr = ntohlll(ip_curr);
335  ip_end = ntohlll(ip_end);
336 
337  /* We're done */
338  if (uint128_eq(ip_curr, ip_end)) return false;
339 
340  /* Increment the prefix */
341  ip_curr = uint128_add(ip_curr, uint128_lshift(uint128_new(0, 1), (128 - prefix)));
342  ip_curr = htonlll(ip_curr);
343  memcpy(&ipaddr->ipaddr.ip6addr.s6_addr, &ip_curr, sizeof(ipaddr->ipaddr.ip6addr.s6_addr));
344  return true;
345  }
346 
347  case AF_INET:
348  {
349  uint32_t ip_curr, ip_end;
350 
351  rad_assert((prefix > 0) && (prefix <= 32));
352 
353  ip_curr = ntohl(ipaddr->ipaddr.ip4addr.s_addr);
354  ip_end = ntohl(end->ipaddr.ip4addr.s_addr);
355 
356  /* We're done */
357  if (ip_curr == ip_end) return false;
358 
359  /* Increment the prefix */
360  ip_curr += 1 << (32 - prefix);
361  ipaddr->ipaddr.ip4addr.s_addr = htonl(ip_curr);
362  return true;
363  }
364  }
365 }
366 
367 /** Add a net to the pool
368  *
369  * @return the number of new addresses added.
370  */
371 static int driver_do_lease(void *out, void *instance, ippool_tool_operation_t const *op,
373 {
374  redis_driver_conf_t *inst = talloc_get_type_abort(instance, redis_driver_conf_t);
375 
376  int i;
377  bool more = true;
378  fr_redis_conn_t *conn;
379 
381  fr_redis_rcode_t status;
382 
383  fr_ipaddr_t ipaddr = op->start, acked;
384  int s_ret = REDIS_RCODE_SUCCESS;
385  REQUEST *request = request_alloc(inst);
386  redisReply **replies = NULL;
387 
388 
389  while (more) {
390  size_t reply_cnt = 0;
391 
392  /* Record our progress */
393  acked = ipaddr;
394  for (s_ret = fr_redis_cluster_state_init(&state, &conn, inst->cluster, request,
395  op->pool, op->pool_len, false);
396  s_ret == REDIS_RCODE_TRY_AGAIN;
397  s_ret = fr_redis_cluster_state_next(&state, &conn, inst->cluster, request, status, &replies[0])) {
398  int pipelined = 0;
399 
400  status = REDIS_RCODE_SUCCESS;
401 
402  /*
403  * If we got a redirect, start back at the beginning of the block.
404  */
405  if (s_ret == REDIS_RCODE_TRY_AGAIN) ipaddr = acked;
406 
407  for (i = 0; (i < MAX_PIPELINED) && more; i++, more = ipaddr_next(&ipaddr, &op->end,
408  op->prefix)) {
409  int enqueued;
410 
411  enqueued = enqueue(inst, conn, op->pool, op->pool_len,
412  op->range, op->range_len, &ipaddr, op->prefix);
413  if (enqueued < 0) break;
414  pipelined += enqueued;
415  }
416 
417  if (!replies) replies = talloc_zero_array(inst, redisReply *, pipelined);
418  if (!replies) return 0;
419 
420  reply_cnt = fr_redis_pipeline_result(&status, replies,
421  talloc_array_length(replies), conn, pipelined);
422  for (i = 0; (size_t)i < reply_cnt; i++) fr_redis_reply_print(L_DBG_LVL_3,
423  replies[i], request, i);
424  }
425  if (s_ret != REDIS_RCODE_SUCCESS) {
426  fr_redis_pipeline_free(replies, reply_cnt);
427  talloc_free(replies);
428  return -1;
429  }
430 
431  if (process) {
432  fr_ipaddr_t to_process = acked;
433 
434  for (i = 0; (size_t)i < reply_cnt; i++) {
435  int ret;
436 
437  ret = process(out, &to_process, replies[i]);
438  if (ret < 0) continue;
439  ipaddr_next(&to_process, &op->end, op->prefix);
440  }
441  }
442  fr_redis_pipeline_free(replies, reply_cnt);
443  TALLOC_FREE(replies);
444  }
445 
446  return 0;
447 }
448 
449 /** Enqueue commands to retrieve lease information
450  *
451  */
452 static int _driver_show_lease_process(void *out, fr_ipaddr_t const *ipaddr, redisReply const *reply)
453 {
454  size_t existing;
455  ippool_tool_lease_t ***modified = out;
456  ippool_tool_lease_t *lease;
457 
458  if (!*modified) *modified = talloc_array(NULL, ippool_tool_lease_t *, 1);
459 
460  /*
461  * The exec command is the only one that produces an array.
462  */
463  if (reply->type != REDIS_REPLY_ARRAY) return -1;
464  if (reply->elements < 4) return -1;
465 
466  if (reply->element[0]->type != REDIS_REPLY_STRING) return -1;
467  lease = talloc_zero(*modified, ippool_tool_lease_t);
468  lease->ipaddr = *ipaddr;
469  lease->next_event = (time_t)strtoull(reply->element[0]->str, NULL, 10);
470 
471  if (reply->element[1]->type == REDIS_REPLY_STRING) {
472  lease->device = talloc_memdup(lease, reply->element[1]->str, reply->element[1]->len);
473  lease->device_len = reply->element[1]->len;
474  }
475  if (reply->element[2]->type == REDIS_REPLY_STRING) {
476  lease->gateway = talloc_memdup(lease, reply->element[2]->str, reply->element[2]->len);
477  lease->gateway_len = reply->element[2]->len;
478  }
479  if (reply->element[3]->type == REDIS_REPLY_STRING) {
480  lease->range = talloc_memdup(lease, reply->element[3]->str, reply->element[3]->len);
481  lease->range_len = reply->element[3]->len;
482  }
483  existing = talloc_array_length(*modified);
484  *modified = talloc_realloc(NULL, *modified, ippool_tool_lease_t *, existing + 1);
485  (*modified)[existing - 1] = lease;
486 
487  return 0;
488 }
489 
490 /** Enqueue commands to retrieve lease information
491  *
492  */
494  uint8_t const *key_prefix, size_t key_prefix_len,
495  UNUSED uint8_t const *range, UNUSED size_t range_len,
496  fr_ipaddr_t *ipaddr, uint8_t prefix)
497 {
498  uint8_t key[IPPOOL_MAX_POOL_KEY_SIZE];
499  uint8_t *key_p = key;
500  char ip_buff[FR_IPADDR_PREFIX_STRLEN];
501 
502  uint8_t ip_key[IPPOOL_MAX_IP_KEY_SIZE];
503  uint8_t *ip_key_p = ip_key;
504 
505  IPPOOL_BUILD_KEY(key, key_p, key_prefix, key_prefix_len);
506  IPPOOL_SPRINT_IP(ip_buff, ipaddr, prefix);
507  IPPOOL_BUILD_IP_KEY_FROM_STR(ip_key, ip_key_p, key_prefix, key_prefix_len, ip_buff);
508 
509  DEBUG("Retrieving lease info for %s from pool %s", ip_buff, key_prefix);
510  redisAppendCommand(conn->handle, "MULTI");
511  redisAppendCommand(conn->handle, "ZSCORE %b %s", key, key_p - key, ip_buff);
512  redisAppendCommand(conn->handle, "HGET %b device", ip_key, ip_key_p - ip_key);
513  redisAppendCommand(conn->handle, "HGET %b gateway", ip_key, ip_key_p - ip_key);
514  redisAppendCommand(conn->handle, "HGET %b range", ip_key, ip_key_p - ip_key);
515  redisAppendCommand(conn->handle, "EXEC");
516  return 6;
517 }
518 
519 /** Show information about leases
520  *
521  */
522 static inline int driver_show_lease(void *out, void *instance, ippool_tool_operation_t const *op)
523 {
525 }
526 
527 /** Count the number of leases we released
528  *
529  */
530 static int _driver_release_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
531 {
532  uint64_t *modified = out;
533  /*
534  * Record the actual number of addresses released.
535  * Leases with a score of zero shouldn't be included,
536  * in this count.
537  */
538  if (reply->type != REDIS_REPLY_INTEGER) return -1;
539 
540  *modified += reply->integer;
541  return 0;
542 }
543 
544 /** Release a lease by setting its score back to zero
545  *
546  */
548  uint8_t const *key_prefix, size_t key_prefix_len,
549  UNUSED uint8_t const *range, UNUSED size_t range_len,
550  fr_ipaddr_t *ipaddr, uint8_t prefix)
551 {
552  uint8_t key[IPPOOL_MAX_POOL_KEY_SIZE];
553  uint8_t *key_p = key;
554 
555  char ip_buff[FR_IPADDR_PREFIX_STRLEN];
556 
557  IPPOOL_BUILD_KEY(key, key_p, key_prefix, key_prefix_len);
558  IPPOOL_SPRINT_IP(ip_buff, ipaddr, prefix);
559 
560  DEBUG("Releasing %s to pool %s", ip_buff, key_prefix);
561  redisAppendCommand(conn->handle, "ZADD %b XX CH 0 %s", key, key_p - key, ip_buff);
562  return 1;
563 }
564 
565 /** Release a range of leases
566  *
567  */
568 static inline int driver_release_lease(void *out, void *instance, ippool_tool_operation_t const *op)
569 {
570  return driver_do_lease(out, instance, op,
572 }
573 
574 /** Count the number of leases we removed
575  *
576  * Because the ZREM and DEL have to occur in a transaction, we need
577  * some fancier processing to just count the number of ZREMs.
578  */
579 static int _driver_remove_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
580 {
581  uint64_t *modified = out;
582  /*
583  * Record the actual number of addresses modified.
584  * Existing addresses won't be included in this
585  * count.
586  */
587  if (reply->type != REDIS_REPLY_ARRAY) return -1;
588 
589  if ((reply->elements > 0) && (reply->element[0]->type == REDIS_REPLY_INTEGER)) {
590  *modified += reply->element[0]->integer;
591  }
592  return 0;
593 }
594 
595 /** Enqueue lease removal commands
596  *
597  * This removes the lease from the expiry heap, and the data associated with
598  * the lease.
599  */
601  uint8_t const *key_prefix, size_t key_prefix_len,
602  UNUSED uint8_t const *range, UNUSED size_t range_len,
603  fr_ipaddr_t *ipaddr, uint8_t prefix)
604 {
605  uint8_t key[IPPOOL_MAX_POOL_KEY_SIZE];
606  uint8_t *key_p = key;
607  char ip_buff[FR_IPADDR_PREFIX_STRLEN];
608 
609  uint8_t ip_key[IPPOOL_MAX_IP_KEY_SIZE];
610  uint8_t *ip_key_p = ip_key;
611 
612  IPPOOL_BUILD_KEY(key, key_p, key_prefix, key_prefix_len);
613  IPPOOL_SPRINT_IP(ip_buff, ipaddr, prefix);
614  IPPOOL_BUILD_IP_KEY_FROM_STR(ip_key, ip_key_p, key_prefix, key_prefix_len, ip_buff);
615 
616  DEBUG("Removing %s from pool %s, and removing hash at %s", ip_buff, key_prefix, ip_key);
617  redisAppendCommand(conn->handle, "MULTI");
618  redisAppendCommand(conn->handle, "ZREM %b %s", key, key_p - key, ip_buff);
619  redisAppendCommand(conn->handle, "DEL %b", ip_key, ip_key_p - ip_key);
620  redisAppendCommand(conn->handle, "EXEC");
621  return 4;
622 }
623 
624 /** Remove a range of leases
625  *
626  */
627 static int driver_remove_lease(void *out, void *instance, ippool_tool_operation_t const *op)
628 {
629  return driver_do_lease(out, instance, op,
631 }
632 
633 /** Count the number of leases we actually added
634  *
635  * This isn't necessarily the same as the number of ZADDs, as leases may
636  * already exist.
637  */
638 static int _driver_add_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
639 {
640  uint64_t *modified = out;
641  /*
642  * Record the actual number of addresses modified.
643  * Existing addresses won't be included in this
644  * count.
645  */
646  if (reply->type != REDIS_REPLY_ARRAY) return -1;
647 
648  if ((reply->elements > 0) && (reply->element[0]->type == REDIS_REPLY_INTEGER)) {
649  *modified += reply->element[0]->integer;
650  }
651  return 0;
652 }
653 
654 /** Enqueue lease addition commands
655  *
656  */
658  uint8_t const *key_prefix, size_t key_prefix_len,
659  uint8_t const *range, size_t range_len,
660  fr_ipaddr_t *ipaddr, uint8_t prefix)
661 {
662  uint8_t key[IPPOOL_MAX_POOL_KEY_SIZE];
663  uint8_t *key_p = key;
664  char ip_buff[FR_IPADDR_PREFIX_STRLEN];
665 
666  uint8_t ip_key[IPPOOL_MAX_IP_KEY_SIZE];
667  uint8_t *ip_key_p = ip_key;
668 
669  IPPOOL_BUILD_KEY(key, key_p, key_prefix, key_prefix_len);
670  IPPOOL_SPRINT_IP(ip_buff, ipaddr, prefix);
671  IPPOOL_BUILD_IP_KEY_FROM_STR(ip_key, ip_key_p, key_prefix, key_prefix_len, ip_buff);
672 
673  DEBUG("Adding %s to pool %.*s (%zu)", ip_buff, (int)(key_p - key), key, key_p - key);
674  redisAppendCommand(conn->handle, "MULTI");
675  redisAppendCommand(conn->handle, "ZADD %b NX %u %s", key, key_p - key, 0, ip_buff);
676  redisAppendCommand(conn->handle, "HSET %b range %b", ip_key, ip_key_p - ip_key, range, range_len);
677  redisAppendCommand(conn->handle, "EXEC");
678  return 4;
679 }
680 
681 /** Add a range of prefixes
682  *
683  */
684 static int driver_add_lease(void *out, void *instance, ippool_tool_operation_t const *op)
685 {
687 }
688 
689 /** Driver initialization function
690  *
691  */
692 static int driver_init(TALLOC_CTX *ctx, CONF_SECTION *conf, void **instance)
693 {
694  redis_driver_conf_t *this;
695  int ret;
696 
697  *instance = NULL;
698 
699  this = talloc_zero(ctx, redis_driver_conf_t);
700  if (!this) return -1;
701 
702  ret = cf_section_parse(conf, &this->conf, redis_config);
703  if (ret < 0) {
704  talloc_free(this);
705  return -1;
706  }
707 
708  this->cluster = fr_redis_cluster_alloc(this, conf, &this->conf);
709  if (!this->cluster) {
710  talloc_free(this);
711  return -1;
712  }
713  *instance = this;
714 
715  return 0;
716 }
717 
718 /** Convert an IP range or CIDR mask to a start and stop address
719  *
720  * @param[out] start_out Where to write the start address.
721  * @param[out] end_out Where to write the end address.
722  * @param[in] ip_str Unparsed IP string.
723  * @param[in] prefix length of prefixes we'll be allocating.
724  * @return
725  * - 0 on success.
726  * - -1 on failure.
727  */
728 static int parse_ip_range(fr_ipaddr_t *start_out, fr_ipaddr_t *end_out, char const *ip_str, uint8_t prefix)
729 {
730  fr_ipaddr_t start, end;
731  bool ex_broadcast;
732  char const *p;
733 
734  p = strchr(ip_str, '-');
735  if (p) {
736  char start_buff[INET6_ADDRSTRLEN + 4];
737  char end_buff[INET6_ADDRSTRLEN + 4];
738  size_t len;
739 
740  if ((size_t)(p - ip_str) >= sizeof(start_buff)) {
741  ERROR("Start address too long");
742  return -1;
743  }
744 
745  len = strlcpy(start_buff, ip_str, (p - ip_str) + 1);
746  rad_assert(!is_truncated(len, sizeof(start_buff)));
747 
748  len = strlcpy(end_buff, p + 1, sizeof(end_buff));
749  if (is_truncated(len, sizeof(end_buff))) {
750  ERROR("End address too long");
751  return -1;
752  }
753 
754  if (fr_inet_pton(&start, start_buff, -1, AF_UNSPEC, false, true) < 0) {
755  ERROR("Failed parsing \"%s\" as start address: %s", start_buff, fr_strerror());
756  return -1;
757  }
758 
759  if (fr_inet_pton(&end, end_buff, -1, AF_UNSPEC, false, true) < 0) {
760  ERROR("Failed parsing \"%s\" end address: %s", end_buff, fr_strerror());
761  return -1;
762  }
763 
764  if (start.af != end.af) {
765  ERROR("Start and end address must be of the same address family");
766  return -1;
767  }
768 
769  if (!prefix) prefix = IPADDR_LEN(start.af);
770 
771  /*
772  * IPv6 addresses
773  */
774  if (start.af == AF_INET6) {
775  uint128_t start_int, end_int;
776 
777  memcpy(&start_int, start.ipaddr.ip6addr.s6_addr, sizeof(start_int));
778  memcpy(&end_int, end.ipaddr.ip6addr.s6_addr, sizeof(end_int));
779  if (uint128_gt(ntohlll(start_int), ntohlll(end_int))) {
780  ERROR("End address must be greater than or equal to start address");
781  return -1;
782  }
783  /*
784  * IPv4 addresses
785  */
786  } else {
787  if (ntohl((uint32_t)(start.ipaddr.ip4addr.s_addr)) >
788  ntohl((uint32_t)(end.ipaddr.ip4addr.s_addr))) {
789  ERROR("End address must be greater than or equal to start address");
790  return -1;
791  }
792  }
793 
794  /*
795  * Mask start and end so we can do prefix ranges too
796  */
797  fr_ipaddr_mask(&start, prefix);
798  fr_ipaddr_mask(&end, prefix);
799  start.prefix = prefix;
800  end.prefix = prefix;
801 
802  *start_out = start;
803  *end_out = end;
804 
805  return 0;
806  }
807 
808  if (fr_inet_pton(&start, ip_str, -1, AF_UNSPEC, false, false) < 0) {
809  ERROR("Failed parsing \"%s\" as IPv4/v6 subnet", ip_str);
810  return -1;
811  }
812 
813  if (!prefix) prefix = IPADDR_LEN(start.af);
814 
815  if (prefix < start.prefix) {
816  ERROR("-p must be greater than or equal to /<mask> (%u)", start.prefix);
817  return -1;
818  }
819  if (prefix > IPADDR_LEN(start.af)) {
820  ERROR("-p must be less than or equal to address length (%u)", IPADDR_LEN(start.af));
821  return -1;
822  }
823 
824  if ((prefix - start.prefix) > 64) {
825  ERROR("-p must be less than or equal to %u", start.prefix + 64);
826  return -1;
827  }
828 
829  /*
830  * Exclude the broadcast address only if we're dealing with IP addresses
831  * if we're allocating prefixes we don't need to.
832  */
833  ex_broadcast = IPADDR_LEN(start.af) == prefix;
834 
835  /*
836  * Excluding broadcast, 31/32 or 127/128 start/end are the same
837  */
838  if (ex_broadcast && (start.prefix >= (IPADDR_LEN(start.af) - 1))) {
839  *start_out = start;
840  *end_out = start;
841  return 0;
842  }
843 
844  /*
845  * Set various fields (we only overwrite the IP later)
846  */
847  end = start;
848 
849  if (start.af == AF_INET6) {
850  uint128_t ip, p_mask;
851 
852  rad_assert((prefix > 0) && (prefix <= 128));
853 
854  /* Don't be tempted to cast */
855  memcpy(&ip, start.ipaddr.ip6addr.s6_addr, sizeof(ip));
856  ip = ntohlll(ip);
857 
858  /* Generate a mask that covers the prefix bits, and sets them high */
859  p_mask = uint128_lshift(uint128_gen_mask(prefix - start.prefix), (128 - prefix));
860  ip = htonlll(uint128_bor(p_mask, ip));
861 
862  /* Decrement by one */
863  if (ex_broadcast) ip = uint128_sub(ip, uint128_new(0, 1));
864  memcpy(&end.ipaddr.ip6addr.s6_addr, &ip, sizeof(end.ipaddr.ip6addr.s6_addr));
865  } else {
866  uint32_t ip;
867 
868  rad_assert((prefix > 0) && (prefix <= 32));
869 
870  ip = ntohl(start.ipaddr.ip4addr.s_addr);
871 
872  /* Generate a mask that covers the prefix bits and sets them high */
873  ip |= uint32_gen_mask(prefix - start.prefix) << (32 - prefix);
874 
875  /* Decrement by one */
876  if (ex_broadcast) ip--;
877  end.ipaddr.ip4addr.s_addr = htonl(ip);
878  }
879 
880  *start_out = start;
881  *end_out = end;
882 
883  return 0;
884 }
885 
886 int main(int argc, char *argv[])
887 {
888  static ippool_tool_operation_t ops[128];
889  ippool_tool_operation_t *p = ops, *end = ops + (sizeof(ops) / sizeof(*ops));
890 
891  int opt;
892 
893  char const *pool_arg, *range_arg = NULL;
894  bool do_export = false, print_stats = false;
895  char *do_import = NULL;
896 
897  CONF_SECTION *pool_cs;
898  CONF_PAIR *cp;
900 
901  fr_debug_lvl = 1;
902  name = argv[0];
903 
904  conf = talloc_zero(NULL, ippool_tool_t);
905  conf->cs = cf_section_alloc(NULL, "main", NULL);
906  if (!conf->cs) exit(1);
907 
908  exec_trigger_set_conf(conf->cs);
909 
910 #define ADD_ACTION(_action) \
911 do { \
912  if ((size_t)(p - ops) >= sizeof(ops)) { \
913  ERROR("Too many actions, max is " STRINGIFY(sizeof(ops))); \
914  usage(64); \
915  } \
916  p->action = _action; \
917  p->name = optarg; \
918  p++; \
919 } while (0);
920 
921  while ((opt = getopt(argc, argv, "a:d:r:s:p:ihxo:f:")) != EOF)
922  switch (opt) {
923  case 'a':
925  break;
926 
927  case 'd':
929  break;
930 
931  case 'r':
933  break;
934 
935  case 's':
937  break;
938 
939  case 'p':
940  {
941  unsigned long tmp;
942  char *q;
943 
944  if (p == ops) {
945  ERROR("Prefix may only be specified after a pool management action");
946  usage(64);
947  }
948 
949  tmp = strtoul(optarg, &q, 10);
950  if (q != (optarg + strlen(optarg))) {
951  ERROR("Prefix must be an integer value");
952 
953  }
954 
955  (p - 1)->prefix = (uint8_t)tmp & 0xff;
956  }
957  break;
958 
959  case 'i':
960  do_import = optarg;
961  break;
962 
963  case 'I':
964  do_export = true;
965  break;
966 
967  case 'S':
968  print_stats = true;
969  break;
970 
971  case 'h':
972  usage(0);
973 
974  case 'x':
975  fr_debug_lvl++;
976  rad_debug_lvl++;
977  break;
978 
979  case 'o':
980  break;
981 
982  case 'f':
983  if (cf_file_read(conf->cs, optarg) < 0) exit(1);
984  break;
985 
986  default:
987  usage(1);
988  }
989  argc -= optind;
990  argv += optind;
991 
992  if (argc < 2) {
993  ERROR("Need server and pool name");
994  usage(64);
995  }
996  if (argc > 3) usage(64);
997 
998  cp = cf_pair_alloc(conf->cs, "server", argv[0], T_OP_EQ, T_BARE_WORD, T_DOUBLE_QUOTED_STRING);
999  if (!cp) {
1000  ERROR("Failed creating server pair");
1001  exit(1);
1002  }
1003  cf_pair_add(conf->cs, cp);
1004  pool_arg = argv[1];
1005  if (argc >= 3) range_arg = argv[2];
1006 
1007  if (p == ops) {
1008  ERROR("Nothing to do!");
1009  exit(1);
1010  }
1011 
1012  /*
1013  * Set some alternative default pool settings
1014  */
1015  pool_cs = cf_section_sub_find(conf->cs, "pool");
1016  if (!pool_cs) {
1017  pool_cs = cf_section_alloc(conf->cs, "pool", NULL);
1018  cf_section_add(conf->cs, pool_cs);
1019  }
1020  cp = cf_pair_find(pool_cs, "start");
1021  if (!cp) {
1022  cp = cf_pair_alloc(pool_cs, "start", "0", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1023  cf_pair_add(pool_cs, cp);
1024  }
1025  cp = cf_pair_find(pool_cs, "spare");
1026  if (!cp) {
1027  cp = cf_pair_alloc(pool_cs, "spare", "0", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1028  cf_pair_add(pool_cs, cp);
1029  }
1030  cp = cf_pair_find(pool_cs, "min");
1031  if (!cp) {
1032  cp = cf_pair_alloc(pool_cs, "min", "0", T_OP_EQ, T_BARE_WORD, T_BARE_WORD);
1033  cf_pair_add(pool_cs, cp);
1034  }
1035 
1036  if (driver_init(conf, conf->cs, &conf->driver) < 0) {
1037  ERROR("Driver initialisation failed");
1038  exit(1);
1039  }
1040 
1041  /*
1042  * Fixup the operations without specific pools or ranges
1043  * and parse the IP ranges.
1044  */
1045  end = p;
1046  for (p = ops; p < end; p++) {
1047  if (parse_ip_range(&p->start, &p->end, p->name, p->prefix) < 0) usage(64);
1048  if (!p->prefix) p->prefix = IPADDR_LEN(p->start.af);
1049 
1050  if (!p->pool) {
1051  p->pool = (uint8_t const *)pool_arg;
1052  p->pool_len = strlen(pool_arg);
1053  }
1054  if (!p->range) {
1055  p->range = (uint8_t const *)range_arg;
1056  p->range_len = strlen(range_arg);
1057  }
1058  }
1059 
1060  for (p = ops; (p < end) && (p->start.af != AF_UNSPEC); p++) switch (p->action) {
1061  case IPPOOL_TOOL_ADD:
1062  {
1063  uint64_t count = 0;
1064 
1065  if (driver_add_lease(&count, conf->driver, p) < 0) {
1066  exit(1);
1067  }
1068  INFO("Added %" PRIu64 " addresses/prefixes", count);
1069  }
1070  break;
1071 
1072  case IPPOOL_TOOL_REMOVE:
1073  {
1074  uint64_t count = 0;
1075 
1076  if (driver_remove_lease(&count, conf->driver, p) < 0) {
1077  exit(1);
1078  }
1079  INFO("Removed %" PRIu64 " addresses/prefixes", count);
1080  }
1081  continue;
1082 
1083  case IPPOOL_TOOL_RELEASE:
1084  {
1085  uint64_t count = 0;
1086 
1087  if (driver_release_lease(&count, conf->driver, p) < 0) {
1088  exit(1);
1089  }
1090  INFO("Released %" PRIu64 " addresses/prefixes", count);
1091  }
1092  continue;
1093 
1094  case IPPOOL_TOOL_SHOW:
1095  {
1096  ippool_tool_lease_t **leases = NULL;
1097  size_t len, i;
1098 
1099  if (driver_show_lease(&leases, conf->driver, p) < 0) {
1100  exit(1);
1101  }
1102 
1103  len = talloc_array_length(leases);
1104  INFO("Retrieved information for %zu addresses/prefixes", len - 1);
1105  for (i = 0; i < (len - 1); i++) {
1106  char ip_buff[FR_IPADDR_PREFIX_STRLEN];
1107  char time_buff[30];
1108  struct tm tm;
1109  struct timeval now;
1110  char *device = NULL;
1111  char *gateway = NULL;
1112  char *range = NULL;
1113  bool is_active;
1114 
1115 #ifndef NDEBUG
1116  leases[i] = talloc_get_type_abort(leases[i], ippool_tool_lease_t);
1117 #endif
1118 
1119  gettimeofday(&now, NULL);
1120  is_active = now.tv_sec <= leases[i]->next_event;
1121  if (leases[i]->next_event) {
1122  strftime(time_buff, sizeof(time_buff), "%b %e %Y %H:%M:%S %Z",
1123  localtime_r(&(leases[i]->next_event), &tm));
1124  } else {
1125  time_buff[0] = '\0';
1126  }
1127  IPPOOL_SPRINT_IP(ip_buff, &(leases[i]->ipaddr), leases[i]->ipaddr.prefix);
1128 
1129  if (leases[i]->range) {
1130  range = fr_asprint(leases, (char const *)leases[i]->range,
1131  leases[i]->range_len, '\0');
1132  }
1133 
1134  INFO("--");
1135  if (range) INFO("range : %s", range);
1136  INFO("address/prefix : %s", ip_buff);
1137  INFO("active : %s", is_active ? "yes" : "no");
1138 
1139  if (leases[i]->device) {
1140  device = fr_asprint(leases, (char const *)leases[i]->device,
1141  leases[i]->device_len, '\0');
1142  }
1143  if (leases[i]->gateway) {
1144  gateway = fr_asprint(leases, (char const *)leases[i]->gateway,
1145  leases[i]->gateway_len, '\0');
1146  }
1147  if (is_active) {
1148  if (*time_buff) INFO("lease expires : %s", time_buff);
1149  if (device) INFO("device id : %s", device);
1150  if (gateway) INFO("gateway id : %s", gateway);
1151  } else {
1152  if (*time_buff) INFO("lease expired : %s", time_buff);
1153  if (device) INFO("last device id : %s", device);
1154  if (gateway) INFO("last gateway id : %s", gateway);
1155  }
1156  }
1157  talloc_free(leases);
1158  }
1159  continue;
1160 
1161  case IPPOOL_TOOL_NOOP:
1162  break;
1163  }
1164 
1165  if (do_import) {
1166  ERROR("NOT YET IMPLEMENTED");
1167  }
1168 
1169  if (do_export) {
1170  ERROR("NOT YET IMPLEMENTED");
1171  }
1172 
1173  if (print_stats) {
1174  ERROR("NOT YET IMPLEMENTED");
1175  }
1176 
1177  talloc_free(conf);
1178 
1179  return 0;
1180 }
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)
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.
static uint128_t uint128_lshift(uint128_t num, uint8_t bits)
Left shift 128 bit integer.
uint128_t ntohlll(uint128_t const num)
Swap byte order of 128 bit integer.
Definition: missing.c:354
Configuration parameters for a redis connection.
Definition: redis.h:88
int void exec_trigger_set_conf(CONF_SECTION *cs)
Set the global trigger section exec_trigger will search in.
Definition: exec.c:670
static int _driver_release_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
Count the number of leases we released.
3rd highest priority debug messages (-xxx | -Xx).
Definition: log.h:53
uint8_t prefix
Prefix length - Between 0-32 for IPv4 and 0-128 for IPv6.
Definition: inet.h:47
#define INFO(fmt,...)
Definition: log.h:143
static char const * name
fr_redis_cluster_t * cluster
uint8_t const * device
Last device id.
void cf_pair_add(CONF_SECTION *parent, CONF_PAIR *cp)
Add a configuration pair to a section.
Definition: conffile.c:612
#define UNUSED
Definition: libradius.h:134
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
int cf_file_read(CONF_SECTION *cs, char const *file)
Definition: conffile.c:3421
static uint128_t uint128_add(uint128_t a, uint128_t b)
Add two 128bit unsigned integers.
#define inst
Definition: token.h:46
struct ippool_tool_operation ippool_tool_operation_t
A single pool operation.
A redis cluster.
Definition: cluster.c:254
#define ADD_ACTION(_action)
void fr_redis_reply_print(log_lvl_t lvl, redisReply *reply, REQUEST *request, int idx)
Print the response data in a useful treelike form.
Definition: redis.c:139
static uint128_t uint128_sub(uint128_t a, uint128_t b)
Subtract one 128bit integer from another.
int fr_debug_lvl
Definition: misc.c:40
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
pid_t rad_waitpid(pid_t pid, int *status)
CONF_PAIR * cf_pair_find(CONF_SECTION const *, char const *name)
Definition: conffile.c:3478
static int driver_release_lease(void *out, void *instance, ippool_tool_operation_t const *op)
Release a range of leases.
#define MAX_PIPELINED
#define is_truncated(_ret, _max)
Definition: libradius.h:204
time_t next_event
Last state change.
static int _driver_remove_lease_process(void *out, UNUSED fr_ipaddr_t const *ipaddr, redisReply const *reply)
Count the number of leases we removed.
int af
Address family.
Definition: inet.h:42
static int _driver_show_lease_process(void *out, fr_ipaddr_t const *ipaddr, redisReply const *reply)
Enqueue commands to retrieve lease information.
#define rad_assert(expr)
Definition: rad_assert.h:38
A single pool operation.
uint8_t prefix
Prefix - The bits between the address mask, and the prefix form the addresses to be modified in the p...
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:564
static bool ipaddr_next(fr_ipaddr_t *ipaddr, fr_ipaddr_t const *end, uint8_t prefix)
Iterate over range of IP addresses.
redisContext * handle
Hiredis context used when issuing commands.
Definition: redis.h:81
#define IPPOOL_BUILD_KEY(_buff, _p, _key, _key_len)
Wrap the prefix in {} and add the pool suffix.
Definition: redis_ippool.h:78
uint8_t const * pool
Pool identifier.
#define DEBUG(fmt,...)
Definition: log.h:175
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)
Release a lease by setting its score back to zero.
uint8_t const * gateway
Last gateway id.
CONF_SECTION * cs
fr_redis_conf_t conf
Connection parameters for the Redis server.
#define REDIS_COMMON_CONFIG
Definition: redis.h:106
int(* redis_ippool_process_t)(void *out, fr_ipaddr_t const *ipaddr, redisReply const *reply)
int cf_section_parse(CONF_SECTION *, void *base, CONF_PARSER const *variables)
Parse a configuration section into user-supplied variables.
Definition: conffile.c:2234
struct ippool_tool_lease ippool_tool_lease_t
union fr_ipaddr_t::@1 ipaddr
pid_t rad_fork(void)
fr_ipaddr_t end
End 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)
Enqueue lease addition commands.
#define IPPOOL_SPRINT_IP(_buff, _ip, _prefix)
If the prefix is as wide as the AF data size then print it without CIDR notation. ...
Definition: redis_ippool.h:117
static int driver_remove_lease(void *out, void *instance, ippool_tool_operation_t const *op)
Remove a range of leases.
Redis connection sequence state.
Definition: cluster.h:43
char const * name
Original range or CIDR string.
uint8_t const * range
Range identifier.
uint8_t const * range
Range the lease belongs to.
Configuration AVP similar to a VALUE_PAIR.
Definition: conffile.c:82
void fr_ipaddr_mask(fr_ipaddr_t *addr, uint8_t prefix)
Zeroes out the host portion of an fr_ipaddr_t.
Definition: inet.c:90
ippool_tool_action_t action
What to do to the leases described by net/prefix.
static rs_t * conf
Definition: radsniff.c:46
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
CONF_SECTION * cf_section_sub_find(CONF_SECTION const *, char const *name)
Find a sub-section in a section.
Definition: conffile.c:3708
static bool uint128_eq(uint128_t a, uint128_t b)
Return whether the integers are equal.
CONF_PAIR * cf_pair_alloc(CONF_SECTION *parent, char const *attr, char const *value, FR_TOKEN op, FR_TOKEN lhs_type, FR_TOKEN rhs_type)
Allocate a CONF_PAIR.
Definition: conffile.c:546
static CONF_PARSER redis_config[]
unsigned int state
Definition: proto_bfd.c:200
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 *request, fr_redis_rcode_t status, redisReply **reply)
Get the next connection to attempt a command against.
Definition: cluster.c:1714
static char const * prefix
Definition: mainconfig.c:81
size_t pool_len
Length of the pool identifier.
log_lvl_t rad_debug_lvl
Global debugging level.
Definition: log.c:49
struct redis_driver_conf redis_driver_conf_t
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)
Enqueue commands to retrieve lease information.
static uint128_t uint128_new(uint64_t h, uint64_t l)
Creates a new uint128_t from an uint64_t.
Common functions for interacting with Redis via hiredis.
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:390
Try the operation again.
Definition: redis.h:70
static uint128_t uint128_bor(uint128_t a, uint128_t b)
Perform bitwise | of two 128bit unsigned integers.
CONF_SECTION * cf_section_alloc(CONF_SECTION *parent, char const *name1, char const *name2)
Allocate a CONF_SECTION.
Definition: conffile.c:626
static void NEVER_RETURNS usage(int ret)
fr_redis_cluster_t * fr_redis_cluster_alloc(TALLOC_CTX *ctx, CONF_SECTION *module, fr_redis_conf_t *conf)
Allocate and initialise a new cluster structure.
Definition: cluster.c:2059
fr_redis_rcode_t fr_redis_pipeline_result(fr_redis_rcode_t *rcode, redisReply *out[], size_t out_len, fr_redis_conn_t *conn, int pipelined)
Simplifies handling of pipelined commands with Redis cluster.
Definition: redis.c:460
Common functions for interacting with Redis cluster via Hiredis.
fr_ipaddr_t start
Start address.
fr_redis_rcode_t
Codes are ordered inversely by priority.
Definition: redis.h:67
static int driver_init(TALLOC_CTX *ctx, CONF_SECTION *conf, void **instance)
Driver initialization function.
size_t range_len
Length of the range identifier.
static int driver_show_lease(void *out, void *instance, ippool_tool_operation_t const *op)
Show information about leases.
#define IPPOOL_BUILD_IP_KEY_FROM_STR(_buff, _p, _key, _key_len, _ip_str)
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 *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:1594
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
IPv4/6 prefix.
Definition: inet.h:41
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)
Add a net to the pool.
static bool uint128_gt(uint128_t a, uint128_t b)
Return whether one integer is greater than the other.
#define IPPOOL_MAX_IP_KEY_SIZE
{prefix}:ipaddr/prefix
Definition: redis_ippool.h:70
#define fr_redis_pipeline_free(_r, _n)
Definition: redis.h:142
#define NEVER_RETURNS
Definition: libradius.h:133
Operation was successfull.
Definition: redis.h:68
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.
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition: missing.c:152
#define RCSID(id)
Definition: build.h:135
void cf_section_add(CONF_SECTION *parent, CONF_SECTION *cs)
Definition: conffile.c:754
static int driver_add_lease(void *out, void *instance, ippool_tool_operation_t const *op)
Add a range of prefixes.
struct ippool_tool ippool_tool_t
#define IPPOOL_MAX_POOL_KEY_SIZE
{prefix}:pool
Definition: redis_ippool.h:66
#define FR_IPADDR_PREFIX_STRLEN
Like FR_IPADDR_STRLEN but with space for a prefix.
Definition: inet.h:71
fr_ipaddr_t ipaddr
Prefix or address.
#define ERROR(fmt,...)
Definition: log.h:145
static uint32_t uint32_gen_mask(uint8_t bits)
REQUEST * request_alloc(TALLOC_CTX *ctx)
Create a new REQUEST data structure.
Definition: request.c:85
int main(int argc, char *argv[])
Connection handle, holding a redis context.
Definition: redis.h:80
static uint128_t uint128_band(uint128_t a, uint128_t b)
Perform bitwise & of two 128bit unsigned integers.
static uint128_t uint128_gen_mask(uint8_t bits)
Create a 128 bit integer value with n bits high.
#define IPADDR_LEN(_af)
Definition: redis_ippool.h:73
enum ippool_tool_action ippool_tool_action_t
Pool management actions.
ippool_tool_action
Pool management actions.
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)
Enqueue lease removal commands.