The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
rlm_redis_ippool_tool.c
Go to the documentation of this file.
1/*
2 * This program 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: 5f274812609cd0d23caa7c04c604e15047cdcf17 $
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: 5f274812609cd0d23caa7c04c604e15047cdcf17 $")
28#include <freeradius-devel/server/base.h>
29#include <freeradius-devel/server/module_rlm.h>
30#include <freeradius-devel/io/listen.h>
31#include <freeradius-devel/unlang/base.h>
32#include <freeradius-devel/io/thread.h>
33
34#include "base.h"
35#include "redis_ippool.h"
36
37#define EXIT_WITH_FAILURE \
38do { \
39 ret = EXIT_FAILURE; \
40 goto cleanup; \
41} while (0)
42
44
50
76
79 { .out = &attr_ippool, .name = "IP-Pool", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
80 { .out = &attr_ippool_name, .name = "IP-Pool.Name", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
81 { .out = &attr_ippool_lease, .name = "IP-Pool.Lease", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
82 { .out = &attr_ippool_lease_address, .name = "IP-Pool.Lease.Address", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
83 { .out = &attr_ippool_lease_active, .name = "IP-Pool.Lease.Active", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
84 { .out = &attr_ippool_lease_expires, .name = "IP-Pool.Lease.Expires", .type = FR_TYPE_DATE, .dict = &dict_freeradius },
85 { .out = &attr_ippool_lease_device, .name = "IP-Pool.Lease.Device", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
86 { .out = &attr_ippool_lease_gateway, .name = "IP-Pool.Lease.Gateway", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
87 { .out = &attr_ippool_lease_range, .name = "IP-Pool.Lease.Range", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
88 { .out = &attr_ippool_stats, .name = "IP-Pool.Stats", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
89 { .out = &attr_ippool_stats_total, .name = "IP-Pool.Stats.Total", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
90 { .out = &attr_ippool_stats_dynamic, .name = "IP-Pool.Stats.Dynamic", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
91 { .out = &attr_ippool_stats_dynamic_total, .name = "IP-Pool.Stats.Dynamic.Total", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
92 { .out = &attr_ippool_stats_dynamic_free, .name = "IP-Pool.Stats.Dynamic.Free", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
93 { .out = &attr_ippool_stats_dynamic_expire1m, .name = "IP-Pool.Stats.Dynamic.Expire-1m", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
94 { .out = &attr_ippool_stats_dynamic_expire30m, .name = "IP-Pool.Stats.Dynamic.Expire-30m", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
95 { .out = &attr_ippool_stats_dynamic_expire1h, .name = "IP-Pool.Stats.Dynamic.Expire-1h", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
96 { .out = &attr_ippool_stats_dynamic_expire1d, .name = "IP-Pool.Stats.Dynamic.Expire-1d", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
97 { .out = &attr_ippool_stats_static, .name = "IP-Pool.Stats.Static", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
98 { .out = &attr_ippool_stats_static_total, .name = "IP-Pool.Stats.Static.Total", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
99 { .out = &attr_ippool_stats_static_free, .name = "IP-Pool.Stats.Static.Free", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
100 { .out = &attr_ippool_stats_static_renew1m, .name = "IP-Pool.Stats.Static.Renew-1m", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
101 { .out = &attr_ippool_stats_static_renew30m, .name = "IP-Pool.Stats.Static.Renew-30m", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
102 { .out = &attr_ippool_stats_static_renew1h, .name = "IP-Pool.Stats.Static.Renew-1h", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
103 { .out = &attr_ippool_stats_static_renew1d, .name = "IP-Pool.Stats.Static.Renew-1d", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
105};
106
107/** Pool management actions
108 *
109 */
110typedef enum ippool_tool_action {
111 IPPOOL_TOOL_NOOP = 0, //!< Do nothing.
112 IPPOOL_TOOL_ADD, //!< Add one or more IP addresses.
113 IPPOOL_TOOL_REMOVE, //!< Remove one or more IP addresses.
114 IPPOOL_TOOL_RELEASE, //!< Release one or more IP addresses.
115 IPPOOL_TOOL_SHOW, //!< Show one or more IP addresses.
116 IPPOOL_TOOL_MODIFY, //!< Modify attributes of one or more IP addresses.
117 IPPOOL_TOOL_ASSIGN, //!< Assign a static IP address to a device.
118 IPPOOL_TOOL_UNASSIGN //!< Remove static IP address assignment.
120
121/** A single pool operation
122 *
123 */
124typedef struct {
125 char const *name; //!< Original range or CIDR string.
126
127 uint8_t const *pool; //!< Pool identifier.
128 size_t pool_len; //!< Length of the pool identifier.
129
130 uint8_t const *range; //!< Range identifier.
131 size_t range_len; //!< Length of the range identifier.
132
133 fr_ipaddr_t start; //!< Start address.
134 fr_ipaddr_t end; //!< End address.
135 uint8_t prefix; //!< Prefix - The bits between the address mask, and the prefix
136 //!< form the addresses to be modified in the pool.
137 ippool_tool_action_t action; //!< What to do to the leases described by net/prefix.
139
140static char const *name;
141
142#define EOL "\n"
143
144/** Static server config split into two parts
145 *
146 * The command line provided server value is inserted between
147 * the two sections, along with an optional $INCLUDE if the -f option is provided
148 */
149static char server_conf1[] =
150"thread { num_workers = 1}" EOL \
151"trigger { }" EOL \
152"client test { ipaddr = 127.0.0.1; secret = supersecret }" EOL \
153"modules {" EOL \
154" redis_ippool {" EOL \
155" owner = request.IP-Pool.Lease.Device" EOL \
156" pool_name = control.IP-Pool.Name" EOL \
157" requested_address = IP-Pool.Lease.Address[*]" EOL \
158" redis {" EOL \
159" virtual_server = redis" EOL \
160" pool { start = 0 }" EOL;
161
162static char server_conf2[] =
163" }" EOL \
164" }" EOL \
165"}" EOL \
166"server redis { namespace = redis } " EOL \
167"server default {" EOL \
168" namespace = radius" EOL \
169" recv Access-Request { redis_ippool.stats }" EOL \
170" recv Accounting-Request { redis_ippool.show }" EOL \
171"}";
172
173static NEVER_RETURNS void usage(int ret) {
174 INFO("Usage: %s -adrsm range... [-p prefix_len]... [-x]... [-oShf] server[:port] [pool] [range id]", name);
175 INFO("Pool management:");
176 INFO(" -a range Add address(es)/prefix(es) to the pool.");
177 INFO(" -d range Delete address(es)/prefix(es) in this range.");
178 INFO(" -r range Release address(es)/prefix(es) in this range.");
179 INFO(" -s range Show addresses/prefix in this range.");
180 INFO(" -A address/prefix Assign a static lease.");
181 INFO(" -O owner To use when assigning a static lease.");
182 INFO(" -U address/prefix Un-assign a static lease");
183 INFO(" -p prefix_len Length of prefix to allocate (defaults to 32/128)");
184 INFO(" This is used primarily for IPv6 where a prefix is");
185 INFO(" allocated to an intermediary router, which in turn");
186 INFO(" allocates sub-prefixes to the devices it serves.");
187 INFO(" This argument changes the prefix_len for the previous");
188 INFO(" instance of an -adrsm argument, only.");
189 INFO(" -m range Change the range id to the one specified for addresses");
190 INFO(" in this range.");
191 INFO(" -l List available pools.");
192// INFO(" -L List available ranges in pool [NYI]");
193// INFO(" -i file Import entries from ISC lease file [NYI]");
194 INFO(" "); /* -Werror=format-zero-length */
195// INFO("Pool status:");
196// INFO(" -I Output active entries in ISC lease file format [NYI]");
197 INFO(" -S Print pool statistics");
198 INFO(" "); /* -Werror=format-zero-length */
199 INFO("Configuration:");
200 INFO(" -h Print this help message and exit");
201 INFO(" -x Increase the verbosity level");
202// INFO(" -o attr=value Set option, these are specific to the backends [NYI]");
203 INFO(" -D dictdir Set main dictionary directory (defaults to " DICTDIR);
204 INFO(" -f file Load connection options from a FreeRADIUS format config file");
205 INFO(" This file should contain one or more `server = <fqdn>` pairs`");
206 INFO(" and, optionally `port = <port>`, `password = secret`");
207 INFO(" ");
208 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\"");
209 INFO("CIDR host bits set start address, e.g. 127.0.0.200/24 -> 127.0.0.200-127.0.0.254");
210 fr_exit_now(ret);
211}
212
214{
215 if (bits >= 32) return 0xffffffff;
216 return (1U << bits) - 1;
217}
218
219/** Iterate over range of IP addresses
220 *
221 * Mutates the ipaddr passed in, adding one to the prefix bits on each call.
222 *
223 * @param[in,out] ipaddr to increment.
224 * @param[in] end ipaddr to stop at.
225 * @param[in] prefix Length of the prefix.
226 * @return
227 * - true if the prefix bits are not high (continue).
228 * - false if the prefix bits are high (stop).
229 */
230static bool ipaddr_next(fr_ipaddr_t *ipaddr, fr_ipaddr_t const *end, uint8_t prefix)
231{
232 switch (ipaddr->af) {
233 default:
234 case AF_UNSPEC:
235 fr_assert(0);
236 return false;
237
238 case AF_INET6:
239 {
240 uint128_t ip_curr, ip_end;
241
242 if (!fr_cond_assert((prefix > 0) && (prefix <= 128))) return false;
243
244 /* Don't be tempted to cast */
245 memcpy(&ip_curr, ipaddr->addr.v6.s6_addr, sizeof(ip_curr));
246 memcpy(&ip_end, end->addr.v6.s6_addr, sizeof(ip_curr));
247
248 ip_curr = ntohlll(ip_curr);
249 ip_end = ntohlll(ip_end);
250
251 /* We're done */
252 if (uint128_eq(ip_curr, ip_end)) return false;
253
254 /* Increment the prefix */
255 ip_curr = uint128_add(ip_curr, uint128_lshift(uint128_new(0, 1), (128 - prefix)));
256 ip_curr = htonlll(ip_curr);
257 memcpy(&ipaddr->addr.v6.s6_addr, &ip_curr, sizeof(ipaddr->addr.v6.s6_addr));
258 return true;
259 }
260
261 case AF_INET:
262 {
263 uint32_t ip_curr, ip_end;
264
265 if (!fr_cond_assert((prefix > 0) && (prefix <= 32))) return false;
266
267 ip_curr = ntohl(ipaddr->addr.v4.s_addr);
268 ip_end = ntohl(end->addr.v4.s_addr);
269
270 /* We're done */
271 if (ip_curr == ip_end) return false;
272
273 /* Increment the prefix */
274 ip_curr += 1 << (32 - prefix);
275 ipaddr->addr.v4.s_addr = htonl(ip_curr);
276 return true;
277 }
278 }
279}
280
281/** Run the redis_ippool.stats module method to fetch pool statistics
282 */
283static int redis_ippool_get_stats(request_t *request, fr_value_box_t *pool_name,
285{
286 rlm_rcode_t rcode;
287 fr_pair_t *vp;
288
289 /*
290 * Make sure the control list is empty before pools.list populates it.
291 */
292 fr_pair_list_free(&request->control_pairs);
293
294 /*
295 * Add the pool name for to fetch stats for
296 */
297 MEM(vp = fr_pair_afrom_da_nested(request->control_ctx, &request->control_pairs, attr_ippool_name));
298 if (fr_value_box_copy(vp, &vp->data, pool_name) < 0) return -1;
299
300 if (unlang_interpret_push_section(NULL, request, section,
301 FRAME_CONF(RLM_MODULE_OK, UNLANG_TOP_FRAME)) < 0) return -1;
302
303 rcode = unlang_interpret_synchronous(el, request);
304 switch (rcode) {
306 return -1;
307
308 default:
309 return 0;
310 }
311}
312
313/** Run the redis_ippool.show module method to fetch lease details
314 */
315static int redis_ippool_show(request_t *request, ippool_tool_operation_t const *op, CONF_SECTION *section,
317{
318 rlm_rcode_t rcode;
319 fr_pair_t *vp;
320 fr_ipaddr_t ipaddr = op->start;
321 char ip_buff[FR_IPADDR_PREFIX_STRLEN];
322
323 fr_pair_list_free(&request->control_pairs);
324 fr_pair_list_free(&request->request_pairs);
325
326 MEM(vp = fr_pair_afrom_da_nested(request->control_ctx, &request->control_pairs, attr_ippool_name));
327 if (fr_value_box_bstrndup(vp, &vp->data, NULL, (char const *)op->pool, op->pool_len, false) < 0) return -1;
328
329 ipaddr.prefix = (ipaddr.af == AF_INET6) ? 128 : 32;
330
331 do {
332 IPPOOL_SPRINT_IP(ip_buff, &ipaddr, ipaddr.prefix);
333 MEM(vp = fr_pair_afrom_da_nested(request->request_ctx, &request->request_pairs,
335 if (fr_value_box_from_str(vp, &vp->data, vp->da->type, NULL, ip_buff,
336 strlen(ip_buff), NULL) < 0) return -1;
337 } while (ipaddr_next(&ipaddr, &op->end, op->prefix));
338
339 if (unlang_interpret_push_section(NULL, request, section,
340 FRAME_CONF(RLM_MODULE_OK, UNLANG_TOP_FRAME)) < 0) return -1;
341
342 rcode = unlang_interpret_synchronous(el, request);
343 switch (rcode) {
345 return -1;
346
347 default:
348 return 0;
349 }
350}
351
352/** Convert an IP range or CIDR mask to a start and stop address
353 *
354 * @param[out] start_out Where to write the start address.
355 * @param[out] end_out Where to write the end address.
356 * @param[in] ip_str Unparsed IP string.
357 * @param[in] prefix length of prefixes we'll be allocating.
358 * @return
359 * - 0 on success.
360 * - -1 on failure.
361 */
362static int parse_ip_range(fr_ipaddr_t *start_out, fr_ipaddr_t *end_out, char const *ip_str, uint8_t prefix)
363{
364 fr_ipaddr_t start, end;
365 bool ex_broadcast;
366 char const *p;
367
368 p = strchr(ip_str, '-');
369 if (p) {
370 char start_buff[INET6_ADDRSTRLEN + 4];
371 char end_buff[INET6_ADDRSTRLEN + 4];
372 size_t len;
373
374 if ((size_t)(p - ip_str) >= sizeof(start_buff)) {
375 ERROR("Start address too long");
376 return -1;
377 }
378
379 len = strlcpy(start_buff, ip_str, (p - ip_str) + 1);
380 if (is_truncated(len, sizeof(start_buff))) {
381 ERROR("Start address too long");
382 return -1;
383 }
384
385 len = strlcpy(end_buff, p + 1, sizeof(end_buff));
386 if (is_truncated(len, sizeof(end_buff))) {
387 ERROR("End address too long");
388 return -1;
389 }
390
391 if (fr_inet_pton(&start, start_buff, -1, AF_UNSPEC, false, true) < 0) {
392 PERROR("Failed parsing \"%s\" as start address", start_buff);
393 return -1;
394 }
395
396 if (fr_inet_pton(&end, end_buff, -1, AF_UNSPEC, false, true) < 0) {
397 PERROR("Failed parsing \"%s\" end address", end_buff);
398 return -1;
399 }
400
401 if (start.af != end.af) {
402 ERROR("Start and end address must be of the same address family");
403 return -1;
404 }
405
406 if (!prefix) prefix = IPADDR_LEN(start.af);
407
408 /*
409 * IPv6 addresses
410 */
411 if (start.af == AF_INET6) {
412 uint128_t start_int, end_int;
413
414 memcpy(&start_int, start.addr.v6.s6_addr, sizeof(start_int));
415 memcpy(&end_int, end.addr.v6.s6_addr, sizeof(end_int));
416 if (uint128_gt(ntohlll(start_int), ntohlll(end_int))) {
417 ERROR("End address must be greater than or equal to start address");
418 return -1;
419 }
420 /*
421 * IPv4 addresses
422 */
423 } else {
424 if (ntohl((uint32_t)(start.addr.v4.s_addr)) >
425 ntohl((uint32_t)(end.addr.v4.s_addr))) {
426 ERROR("End address must be greater than or equal to start address");
427 return -1;
428 }
429 }
430
431 /*
432 * Mask start and end so we can do prefix ranges too
433 */
434 fr_ipaddr_mask(&start, prefix);
435 fr_ipaddr_mask(&end, prefix);
436 start.prefix = prefix;
437 end.prefix = prefix;
438
439 *start_out = start;
440 *end_out = end;
441
442 return 0;
443 }
444
445 if (fr_inet_pton(&start, ip_str, -1, AF_UNSPEC, false, false) < 0) {
446 ERROR("Failed parsing \"%s\" as IPv4/v6 subnet", ip_str);
447 return -1;
448 }
449
450 if (!prefix) prefix = IPADDR_LEN(start.af);
451
452 if (prefix < start.prefix) {
453 ERROR("-p must be greater than or equal to /<mask> (%u)", start.prefix);
454 return -1;
455 }
456 if (prefix > IPADDR_LEN(start.af)) {
457 ERROR("-p must be less than or equal to address length (%u)", IPADDR_LEN(start.af));
458 return -1;
459 }
460
461 if ((prefix - start.prefix) > 64) {
462 ERROR("-p must be less than or equal to %u", start.prefix + 64);
463 return -1;
464 }
465
466 /*
467 * Exclude the broadcast address only if we're dealing with IPv4 addresses
468 * if we're allocating IPv6 addresses or prefixes we don't need to.
469 */
470 ex_broadcast = (start.af == AF_INET) && (IPADDR_LEN(start.af) == prefix);
471
472 /*
473 * Excluding broadcast, 31/32 or 127/128 start/end are the same
474 */
475 if (ex_broadcast && (start.prefix >= (IPADDR_LEN(start.af) - 1))) {
476 *start_out = start;
477 *end_out = start;
478 return 0;
479 }
480
481 /*
482 * Set various fields (we only overwrite the IP later)
483 */
484 end = start;
485
486 if (start.af == AF_INET6) {
487 uint128_t ip, p_mask;
488
489 /* cond assert to satisfy clang scan */
490 if (!fr_cond_assert((prefix > 0) && (prefix <= 128))) return -1;
491
492 /* Don't be tempted to cast */
493 memcpy(&ip, start.addr.v6.s6_addr, sizeof(ip));
494 ip = ntohlll(ip);
495
496 /* Generate a mask that covers the prefix bits, and sets them high */
497 p_mask = uint128_lshift(uint128_gen_mask(prefix - start.prefix), (128 - prefix));
498 ip = htonlll(uint128_bor(p_mask, ip));
499
500 /* Decrement by one */
501 if (ex_broadcast) ip = uint128_sub(ip, uint128_new(0, 1));
502 memcpy(&end.addr.v6.s6_addr, &ip, sizeof(end.addr.v6.s6_addr));
503 } else {
504 uint32_t ip;
505
506 /* cond assert to satisfy clang scan */
507 if (!fr_cond_assert((prefix > 0) && (prefix <= 32))) return -1;
508
509 ip = ntohl(start.addr.v4.s_addr);
510
511 /* Generate a mask that covers the prefix bits and sets them high */
512 ip |= uint32_gen_mask(prefix - start.prefix) << (32 - prefix);
513
514 /* Decrement by one */
515 if (ex_broadcast) ip--;
516 end.addr.v4.s_addr = htonl(ip);
517 }
518
519 *start_out = start;
520 *end_out = end;
521
522 return 0;
523}
524
525static int run_xlat(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, fr_sbuff_t *xlat, fr_event_list_t *el)
526{
527 xlat_exp_head_t *ex;
528
529 if (xlat_tokenize(ctx, &ex,
530 xlat,
531 &(fr_sbuff_parse_rules_t){
532 .escapes = &(fr_sbuff_unescape_rules_t) {
533 .name = "xlat",
534 .chr = '\\',
535 .subs = {
536 ['%'] = '%',
537 ['\\'] = '\\',
538 },
539 }}, &(tmpl_rules_t) {
540 .attr = {
541 .allow_unknown = false,
542 .dict_def = dict_freeradius
543 },
544 .xlat = {
545 .runtime_el = el
546 },
547 .at_runtime = true
548 }) < 0) return -1;
549
550 if (xlat_needs_resolving(ex) &&
551 (xlat_resolve(ex, &(xlat_res_rules_t){ .allow_unresolved = false }) < 0)) {
552 talloc_free(ex);
553 return -1;
554 }
555
556 if (unlang_xlat_push(ctx, NULL, list, request, ex, UNLANG_TOP_FRAME) < 0) return -1;
558
559 return 0;
560}
561
562static int xlat_add_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list,
564{
565 char xlat[1024];
566 size_t xlat_len;
567
568 op->start.prefix = op->end.prefix = IPADDR_LEN(op->start.af);
569 if (op->range) {
570 xlat_len = fr_sbuff_in_sprintf(&FR_SBUFF_OUT(xlat, sizeof(xlat)),
571 "%%redis_ippool.addresses.add('%s', '%pV', '%pV', %d, '%s')", op->pool,
572 fr_box_ipaddr(op->start), fr_box_ipaddr(op->end), op->prefix, op->range);
573 } else {
574 xlat_len = fr_sbuff_in_sprintf(&FR_SBUFF_OUT(xlat, sizeof(xlat)),
575 "%%redis_ippool.addresses.add('%s', '%pV', '%pV', %d)", op->pool,
576 fr_box_ipaddr(op->start), fr_box_ipaddr(op->end), op->prefix);
577 }
578 if (xlat_len == 0) return -1;
579
580 return run_xlat(ctx, list, request, &FR_SBUFF_IN(xlat, xlat_len), el);
581}
582
583static int xlat_remove_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list,
585{
586 char xlat[1024];
587 size_t xlat_len;
588
589 op->start.prefix = op->end.prefix = IPADDR_LEN(op->start.af);
590 xlat_len = fr_sbuff_in_sprintf(&FR_SBUFF_OUT(xlat, sizeof(xlat)),
591 "%%redis_ippool.addresses.remove('%s', '%pV', '%pV', %d)", op->pool,
592 fr_box_ipaddr(op->start), fr_box_ipaddr(op->end), op->prefix);
593 if (xlat_len == 0) return -1;
594
595 return run_xlat(ctx, list, request, &FR_SBUFF_IN(xlat, xlat_len), el);
596}
597
598static int xlat_release_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request,
600{
601 char xlat[1024];
602 size_t xlat_len;
603
604 op->start.prefix = op->end.prefix = IPADDR_LEN(op->start.af);
605 xlat_len = fr_sbuff_in_sprintf(&FR_SBUFF_OUT(xlat, sizeof(xlat)),
606 "%%redis_ippool.addresses.release('%s', '%pV', '%pV', %d)", op->pool,
607 fr_box_ipaddr(op->start), fr_box_ipaddr(op->end), op->prefix);
608 if (xlat_len == 0) return -1;
609
610 return run_xlat(ctx, list, request, &FR_SBUFF_IN(xlat, xlat_len), el);
611}
612
613static int xlat_modify_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request,
615{
616 char xlat[1024];
617 size_t xlat_len;
618
619 op->start.prefix = op->end.prefix = IPADDR_LEN(op->start.af);
620 xlat_len = fr_sbuff_in_sprintf(&FR_SBUFF_OUT(xlat, sizeof(xlat)),
621 "%%redis_ippool.addresses.modify('%s', '%pV', '%pV', '%s', %d)", op->pool,
622 fr_box_ipaddr(op->start), fr_box_ipaddr(op->end), op->range, op->prefix);
623 if (xlat_len == 0) return -1;
624
625 return run_xlat(ctx, list, request, &FR_SBUFF_IN(xlat, xlat_len), el);
626}
627
628static int xlat_assign_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request,
629 ippool_tool_operation_t *op, char const *owner, fr_event_list_t *el)
630{
631 char xlat[1024];
632 size_t xlat_len;
633
634 if (op->range) {
635 xlat_len = fr_sbuff_in_sprintf(&FR_SBUFF_OUT(xlat, sizeof(xlat)),
636 "%%redis_ippool.address.assign('%s', '%pV', '%s', '%s')", op->pool,
637 fr_box_ipaddr(op->start), owner, op->range);
638 } else {
639 xlat_len = fr_sbuff_in_sprintf(&FR_SBUFF_OUT(xlat, sizeof(xlat)),
640 "%%redis_ippool.address.assign('%s', '%pV', '%s')", op->pool,
641 fr_box_ipaddr(op->start), owner);
642 }
643 if (xlat_len == 0) return -1;
644
645 return run_xlat(ctx, list, request, &FR_SBUFF_IN(xlat, xlat_len), el);
646}
647
648static int xlat_unassign_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request,
649 ippool_tool_operation_t *op, char const *owner, fr_event_list_t *el)
650{
651 char xlat[1024];
652 size_t xlat_len;
653
654 xlat_len = fr_sbuff_in_sprintf(&FR_SBUFF_OUT(xlat, sizeof(xlat)),
655 "%%redis_ippool.address.unassign('%s', '%pV', '%s')", op->pool,
656 fr_box_ipaddr(op->start), owner);
657 if (xlat_len == 0) return -1;
658
659 return run_xlat(ctx, list, request, &FR_SBUFF_IN(xlat, xlat_len), el);
660}
661
662/** Run the redis_ippool.pools.list xlat to fetch the list of pools across the cluster.
663 */
664static int redis_ippool_get_pools(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, fr_event_list_t *el)
665{
666 char xlat[] = "%redis_ippool.pools.list()";
667
668 return run_xlat(ctx, list, request, &FR_SBUFF_IN(xlat, sizeof(xlat) - 1), el);
669}
670
671static request_t *request_from_internal(TALLOC_CTX *ctx)
672{
673 request_t *request;
674
675 /*
676 * Create and initialize the new request.
677 */
678 request = request_local_alloc_internal(ctx, NULL);
679 if (!request->packet) request->packet = fr_packet_alloc(request, false);
680 if (!request->reply) request->reply = fr_packet_alloc(request, false);
681
682 request->packet->socket = (fr_socket_t){
683 .type = SOCK_DGRAM,
684 .inet = {
685 .src_ipaddr = {
686 .af = AF_INET,
687 .prefix = 32,
688 .addr = {
689 .v4 = {
690 .s_addr = htonl(INADDR_LOOPBACK)
691 }
692 }
693 },
694 .src_port = 18120,
695 .dst_ipaddr = {
696 .af = AF_INET,
697 .prefix = 32,
698 .addr = {
699 .v4 = {
700 .s_addr = htonl(INADDR_LOOPBACK)
701 }
702 }
703 },
704 .dst_port = 1812
705 }
706 };
707
708 request->log.dst = talloc_zero(request, log_dst_t);
709 request->log.dst->func = vlog_request;
710 request->log.dst->uctx = &default_log;
711 request->log.dst->lvl = fr_debug_lvl;
712
713 request->master_state = REQUEST_ACTIVE;
714 request->log.lvl = fr_debug_lvl;
715 request->async = talloc_zero(request, fr_async_t);
716
717 if (fr_packet_pairs_from_packet(request->request_ctx, &request->request_pairs, request->packet) < 0) {
718 talloc_free(request);
719 fprintf(stderr, "Failed converting packet IPs to attributes");
720 return NULL;
721 }
722
723 return request;
724}
725
726int main(int argc, char *argv[])
727{
728 static ippool_tool_operation_t ops[128];
729 ippool_tool_operation_t *p = ops, *end = ops + (NUM_ELEMENTS(ops));
730
731 int c, ret = EXIT_SUCCESS;
732
733 uint8_t *range_arg = NULL;
734 uint8_t *pool_arg = NULL;
735 bool do_export = false, print_stats = false, list_pools = false;
736 bool need_pool = false;
737 char *do_import = NULL;
738 char *filename = NULL;
739 char const *owner = NULL;
740
741 request_t *request = NULL;
742 fr_event_list_t *el = NULL;
743 fr_dict_t *dict = NULL;
744
745 TALLOC_CTX *autofree;
746 TALLOC_CTX *thread_ctx;
747
748 char *n;
750 char tmpdir[] = "/tmp/ippool_toolXXXXXX";
751 char filepath[sizeof("/tmp/ippool_toolXXXXXX/rlm_redis_ippool_tool.conf")];
752 FILE *conf_file;
753
754 fr_value_box_list_t vb_list; // To store results of xlat calls.
755
756 virtual_server_t const *vs;
758 CONF_SECTION *pool_stats, *pool_show; // Process sections used to call module methods
759
760 fr_value_box_list_init(&vb_list);
761
762 name = argv[0];
763
765
767 thread_ctx = talloc_new(autofree);
768
770 if (!config) {
771 fr_perror("%s", name);
772 fr_exit_now(EXIT_FAILURE);
773 }
774
775 n = strrchr(name, FR_DIR_SEP);
776 if (!n) {
778 } else {
780 }
781
783
784#ifndef NDEBUG
785 if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), name, PANIC_ACTION_SIGNALS) < 0) {
786 fr_perror("%s", config->name);
787 fr_exit_now(EXIT_FAILURE);
788 }
789#else
791#endif
792
793 fr_debug_lvl = 0;
795
796#define ADD_ACTION(_action) \
797do { \
798 if (p >= end) { \
799 ERROR("Too many actions, max is " STRINGIFY(sizeof(ops))); \
800 usage(64); \
801 } \
802 p->action = _action; \
803 p->name = optarg; \
804 p++; \
805 need_pool = true; \
806} while (0)
807
808 while ((c = getopt(argc, argv, "a:d:D:r:s:Sm:A:U:O:p:ilLhxo:f:")) != -1) switch (c) {
809 case 'a':
811 break;
812
813 case 'd':
815 break;
816
817 case 'D':
819 break;
820
821 case 'r':
823 break;
824
825 case 's':
827 break;
828
829 case 'm':
831 break;
832
833 case 'A':
835 break;
836
837 case 'U':
839 break;
840
841 case 'O':
842 owner = optarg;
843 break;
844
845 case 'p':
846 {
847 unsigned long tmp;
848 char *q;
849
850 if (p == ops) {
851 ERROR("Prefix may only be specified after a pool management action");
852 usage(64);
853 }
854
855 tmp = strtoul(optarg, &q, 10);
856 if (q != (optarg + strlen(optarg))) {
857 ERROR("Prefix must be an integer value");
858 usage(64);
859 }
860
861 (p - 1)->prefix = (uint8_t)tmp & 0xff;
862 }
863 break;
864
865 case 'i':
866 do_import = optarg;
867 break;
868
869 case 'I':
870 do_export = true;
871 break;
872
873 case 'l':
874 if (list_pools) usage(1); /* Only allowed once */
875 list_pools = true;
876 break;
877
878 case 'S':
879 print_stats = true;
880 break;
881
882 case 'h':
883 usage(0);
884
885 case 'x':
886 fr_debug_lvl++;
887 break;
888
889 case 'o':
890 break;
891
892 case 'f':
893 filename = optarg;
894 break;
895
896 default:
897 usage(1);
898 }
899 argc -= optind;
900 argv += optind;
901
902 if (argc == 0) {
903 ERROR("Need server address/port");
904 usage(64);
905 }
906 if ((argc == 1) && need_pool) {
907 ERROR("Need pool to operate on");
908 usage(64);
909 }
910 if (argc > 3) usage(64);
911
913 fr_perror("%s", config->name);
914 ret = EXIT_FAILURE;
915 goto cleanup;
916 }
917
918 modules_init(config->lib_dir);
919
920 if (!fr_dict_global_ctx_init(NULL, true, config->dict_dir)) {
921 fr_perror("%s", config->name);
923 }
924
926 fr_perror("%s", config->name);
928 }
929
931 fr_perror("%s", config->name);
933 }
934
936 fr_perror("%s", config->name);
938 }
939
940 if (request_global_init() < 0) {
941 fr_perror("%s", config->name);
943 }
944
945 if (main_loop_init() < 0) {
946 PERROR("Failed initialising main event loop");
948 }
949
950 if (unlang_global_init() < 0) {
951 fr_perror("%s", config->name);
953 }
954
955 if (modules_rlm_init() < 0) {
956 fr_perror("%s", config->name);
958 }
959 if (virtual_servers_init() < 0) {
960 fr_perror("%s", config->name);
962 }
963
964 /*
965 * Build a temporary configuration file, merging in command
966 * line provided options.
967 */
968 if (!mkdtemp(tmpdir)) EXIT_WITH_FAILURE;
970 snprintf(filepath, sizeof(filepath), "%s/%s.conf", config->confdir, config->name);
971 conf_file = fopen(filepath, "w");
972 if (!conf_file) EXIT_WITH_FAILURE;
973 fputs(server_conf1, conf_file);
974 if (filename) {
975 fprintf(conf_file, "$INCLUDE %s\n", filename);
976 } else {
977 fprintf(conf_file, "server = %s\n", argv[0]);
978 }
979 fputs(server_conf2, conf_file);
980 fclose(conf_file);
981
983 unlink(filepath);
984 rmdir(tmpdir);
985
986 if (ret < 0) EXIT_WITH_FAILURE;
987
988 if (!client_find(NULL, &(fr_ipaddr_t) { .af = AF_INET, .prefix = 32, .addr.v4.s_addr = htonl(INADDR_LOOPBACK) },
989 IPPROTO_IP)) EXIT_WITH_FAILURE;
990
991 if (server_init(config->root_cs, config->confdir, dict) < 0) EXIT_WITH_FAILURE;
992
994 fr_assert(el);
995
997
999 if (fr_thread_instantiate(thread_ctx, el) < 0) {
1000 fr_perror("%s", config->name);
1002 }
1003
1004 if (modules_rlm_coord_attach(el) < 0) {
1005 fr_perror("%s", config->name);
1007 }
1008
1009 if (fr_coord_pre_event_insert(el) < 0) {
1010 fr_strerror_const("Failed adding coordinator pre-check to event list");
1012 }
1013
1014 if (fr_coord_post_event_insert(el) < 0) {
1015 fr_strerror_const("Failed adding coordinator pre-check to event list");
1017 }
1018
1020
1021 vs = virtual_server_find("default");
1022 mi = module_rlm_static_by_name(NULL, "redis_ippool");
1023 if (!mi) EXIT_WITH_FAILURE;
1024
1025 /*
1026 * The module methods for these functions use call_env - which
1027 * preclude them being used with unlang_module_push. Instead they
1028 * need to be called from compiled process sections.
1029 * To avoid inventing another process module, these have been
1030 * wrapped in standard RADIUS process sections.
1031 */
1032 pool_stats = cf_section_find(virtual_server_cs(vs), "recv", "Access-Request");
1033 if (!pool_stats) EXIT_WITH_FAILURE;
1034 pool_show = cf_section_find(virtual_server_cs(vs), "recv", "Accounting-Request");
1035 if (!pool_show) EXIT_WITH_FAILURE;
1036
1037 /*
1038 * Unescape sequences in the pool name
1039 */
1040 if (argv[1] && (argv[1][0] != '\0')) {
1043
1044 MEM(fr_sbuff_init_talloc(autofree, &out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1045 (void) fr_value_str_unescape(&out,
1046 &FR_SBUFF_IN_STR(argv[1]), SIZE_MAX, '"');
1047 talloc_realloc(autofree, out.buff, uint8_t, fr_sbuff_used(&out));
1048 pool_arg = (uint8_t *)out.buff;
1049 }
1050
1051 if (argc >= 3 && (argv[2][0] != '\0')) {
1054
1055 MEM(fr_sbuff_init_talloc(autofree, &out, &tctx, strlen(argv[1]) + 1, SIZE_MAX));
1056 (void) fr_value_str_unescape(&out,
1057 &FR_SBUFF_IN_STR(argv[2]), SIZE_MAX, '"');
1058 talloc_realloc(autofree, out.buff, uint8_t, fr_sbuff_used(&out));
1059 range_arg = (uint8_t *)out.buff;
1060 }
1061
1062 if (!do_import && !do_export && !list_pools && !print_stats && (p == ops)) {
1063 ERROR("Nothing to do!");
1064 fr_exit_now(EXIT_FAILURE);
1065 }
1066
1067 if (do_import) {
1068 ERROR("NOT YET IMPLEMENTED");
1069 }
1070
1071 if (do_export) {
1072 ERROR("NOT YET IMPLEMENTED");
1073 }
1074
1075 if (print_stats) {
1076 fr_value_box_list_t pools;
1077 fr_pair_t *stats, *st, *dyn;
1078 fr_pair_t *total, *static_total, *static_free, *dyn_free;
1079 fr_pair_t *exp1m, *exp30m, *exp1h, *exp1d;
1080
1081 fr_value_box_list_init(&pools);
1082 if (pool_arg) {
1083 fr_value_box_t *vb;
1085 fr_value_box_bstrndup(vb, vb, NULL, (char *)pool_arg, talloc_array_length(pool_arg), false);
1086 fr_value_box_list_insert_tail(&pools, vb);
1087 } else {
1088 if (redis_ippool_get_pools(autofree, &pools, request, el) < 0) EXIT_WITH_FAILURE;
1089 }
1090
1091 fr_value_box_list_foreach(&pools, vb) {
1092 if (redis_ippool_get_stats(request, vb, pool_stats, el) < 0) EXIT_WITH_FAILURE;
1093
1094 INFO("pool : %pV", vb);
1095 stats = fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_ippool_stats);
1096 if (!stats) EXIT_WITH_FAILURE;
1097
1098 st = fr_pair_find_by_da(&stats->vp_group, NULL, attr_ippool_stats_static);
1099 dyn = fr_pair_find_by_da(&stats->vp_group, NULL, attr_ippool_stats_dynamic);
1100 if (!st || !dyn) EXIT_WITH_FAILURE;
1101
1102 total = fr_pair_find_by_da_nested(&stats->vp_group, NULL, attr_ippool_stats_total);
1103 if (!total) EXIT_WITH_FAILURE;
1104 INFO("total : %pV", &total->data);
1105
1106 static_total = fr_pair_find_by_da(&st->vp_group, NULL, attr_ippool_stats_static_total);
1107 if (!static_total) EXIT_WITH_FAILURE;
1108 INFO("dynamic total : %" PRIu64, total->vp_uint64 - static_total->vp_uint64);
1109
1110 dyn_free = fr_pair_find_by_da(&dyn->vp_group, NULL, attr_ippool_stats_dynamic_free);
1111 if (!dyn_free) EXIT_WITH_FAILURE;
1112 INFO("dynamic free : %pV", &dyn_free->data);
1113 INFO("dynamic used : %" PRIu64, total->vp_uint64 - static_total->vp_uint64 - dyn_free->vp_uint64);
1114 if ((total->vp_uint64 - static_total->vp_uint64) > 0) {
1115 INFO("dynamic used (%%) : %.2Lf",
1116 ((long double)(total->vp_uint64 - dyn_free->vp_uint64 - static_total->vp_uint64) /
1117 (long double)(total->vp_uint64 - static_total->vp_uint64)) * 100);
1118 } else {
1119 INFO("used (%%) : 0");
1120 }
1121
1122 exp1m = fr_pair_find_by_da(&dyn->vp_group, NULL, attr_ippool_stats_dynamic_expire1m);
1123 exp30m = fr_pair_find_by_da(&dyn->vp_group, NULL, attr_ippool_stats_dynamic_expire30m);
1124 exp1h = fr_pair_find_by_da(&dyn->vp_group, NULL, attr_ippool_stats_dynamic_expire1h);
1125 exp1d = fr_pair_find_by_da(&dyn->vp_group, NULL, attr_ippool_stats_dynamic_expire1d);
1126 if (!exp1m || !exp30m || !exp1h || !exp1d) EXIT_WITH_FAILURE;
1127
1128 INFO("expiring 0-1m : %" PRIu64, exp1m->vp_uint64);
1129 INFO("expiring 1-30m : %" PRIu64, exp30m->vp_uint64 - exp1m->vp_uint64);
1130 INFO("expiring 30m-1h : %" PRIu64, exp1h->vp_uint64 - exp30m->vp_uint64);
1131 INFO("expiring 1h-1d : %" PRIu64, exp1d->vp_uint64 - exp1h->vp_uint64);
1132 INFO("static total : %pV", &static_total->data);
1133
1134 static_free = fr_pair_find_by_da(&st->vp_group, NULL, attr_ippool_stats_static_free);
1135 if (!static_free) EXIT_WITH_FAILURE;
1136 INFO("static 'free' : %pV", &static_free->data);
1137 INFO("static issued : %" PRIu64, static_total->vp_uint64 - static_free->vp_uint64);
1138 if (static_total->vp_uint64) {
1139 INFO("static issued (%%) : %.2Lf",
1140 ((long double)(static_total->vp_uint64 - static_free->vp_uint64) /
1141 (long double)(static_total->vp_uint64)) * 100);
1142 } else {
1143 INFO("static issued (%%) : 0");
1144 }
1145
1146 exp1m = fr_pair_find_by_da(&st->vp_group, NULL, attr_ippool_stats_static_renew1m);
1147 exp30m = fr_pair_find_by_da(&st->vp_group, NULL, attr_ippool_stats_static_renew30m);
1148 exp1h = fr_pair_find_by_da(&st->vp_group, NULL, attr_ippool_stats_static_renew1h);
1149 exp1d = fr_pair_find_by_da(&st->vp_group, NULL, attr_ippool_stats_static_renew1d);
1150 if (!exp1m || !exp30m || !exp1h || !exp1d) EXIT_WITH_FAILURE;
1151
1152 INFO("static renew 0-1m : %" PRIu64, exp1m->vp_uint64);
1153 INFO("static renew 1-30m : %" PRIu64, exp30m->vp_uint64 - exp1m->vp_uint64);
1154 INFO("static renew 30m-1h : %" PRIu64, exp1h->vp_uint64 - exp30m->vp_uint64);
1155 INFO("static renew 1h-1d : %" PRIu64, exp1d->vp_uint64 - exp1h->vp_uint64);
1156 INFO("--");
1157 }
1158 fr_value_box_list_talloc_free(&pools);
1159 }
1160
1161 if (list_pools) {
1162 fr_value_box_list_t pools;
1163
1164 fr_value_box_list_init(&pools);
1165 if (redis_ippool_get_pools(autofree, &pools, request, el) < 0) EXIT_WITH_FAILURE;
1166 fr_value_box_list_foreach(&pools, vb) INFO("%pV", vb);
1167 INFO("--");
1168 fr_value_box_list_talloc_free(&pools);
1169 }
1170
1171 /*
1172 * Fixup the operations without specific pools or ranges
1173 * and parse the IP ranges.
1174 */
1175 end = p;
1176 for (p = ops; p < end; p++) {
1177 if (parse_ip_range(&p->start, &p->end, p->name, p->prefix) < 0) usage(64);
1178 if (!p->prefix) p->prefix = IPADDR_LEN(p->start.af);
1179
1180 if (!p->pool) {
1181 p->pool = pool_arg;
1182 p->pool_len = talloc_array_length(pool_arg);
1183 }
1184 if (!p->range && range_arg) {
1185 p->range = range_arg;
1186 p->range_len = talloc_array_length(range_arg);
1187 }
1188 }
1189
1190 for (p = ops; (p < end) && (p->start.af != AF_UNSPEC); p++) switch (p->action) {
1191 case IPPOOL_TOOL_ADD:
1192 fr_value_box_list_talloc_free(&vb_list);
1193 if (xlat_add_lease(autofree, &vb_list, request, p, el) < 0) EXIT_WITH_FAILURE;
1194
1195 INFO("Added %pV address(es)/prefix(es)", fr_value_box_list_head(&vb_list));
1196 continue;
1197
1198 case IPPOOL_TOOL_REMOVE:
1199 fr_value_box_list_talloc_free(&vb_list);
1200 if (xlat_remove_lease(autofree, &vb_list, request, p, el) < 0) EXIT_WITH_FAILURE;
1201
1202 INFO("Removed %pV address(es)/prefix(es)", fr_value_box_list_head(&vb_list));
1203 continue;
1204
1206 fr_value_box_list_talloc_free(&vb_list);
1207 if (xlat_release_lease(autofree, &vb_list, request, p, el) < 0) EXIT_WITH_FAILURE;
1208
1209 INFO("Released %pV address(es)/prefix(es)", fr_value_box_list_head(&vb_list));
1210 continue;
1211
1212 case IPPOOL_TOOL_SHOW:
1213 {
1214 fr_pair_t *ippool, *vp, *device, *gateway, *expires;
1215
1216 if (redis_ippool_show(request, p, pool_show, el) < 0) EXIT_WITH_FAILURE;
1217
1218 vp = fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_ippool_lease);
1219 if (vp) {
1220 ippool = fr_pair_parent(vp);
1221 } else {
1222 ippool = fr_pair_find_by_da(&request->control_pairs, NULL, attr_ippool);
1223 }
1224 if (!ippool) EXIT_WITH_FAILURE;
1225
1226 INFO("Retrieved information for %u address(es)/prefix(es)",
1227 fr_pair_count_by_da(&ippool->vp_group, attr_ippool_lease));
1228 fr_pair_list_foreach(&ippool->vp_group, lease) {
1229 if (lease->da != attr_ippool_lease) continue;
1230
1231 INFO("--");
1232 vp = fr_pair_find_by_da(&lease->vp_group, NULL, attr_ippool_lease_range);
1233 if (vp) INFO("range : %pV", &vp->data);
1234
1235 vp = fr_pair_find_by_da(&lease->vp_group, NULL, attr_ippool_lease_address);
1236 if (!vp) EXIT_WITH_FAILURE;
1237 INFO("address/prefix : %pV", &vp->data);
1238
1239 vp = fr_pair_find_by_da(&lease->vp_group, NULL, attr_ippool_lease_active);
1240 if (!vp) EXIT_WITH_FAILURE;
1241 INFO("active : %s", vp->vp_bool ? "yes" : "no");
1242
1243 device = fr_pair_find_by_da(&lease->vp_group, NULL, attr_ippool_lease_device);
1244 gateway = fr_pair_find_by_da(&lease->vp_group, NULL, attr_ippool_lease_gateway);
1245 expires = fr_pair_find_by_da(&lease->vp_group, NULL, attr_ippool_lease_expires);
1246
1247 if (vp->vp_bool) {
1248 if (expires) INFO("lease expires : %pV", &expires->data);
1249 if (device) INFO("device id : %pV", &device->data);
1250 if (gateway) INFO("gateway id : %pV", &gateway->data);
1251 } else {
1252 if (expires) INFO("lease expired : %pV", &expires->data);
1253 if (device) INFO("last device id : %pV", &device->data);
1254 if (gateway) INFO("last gateway id : %pV", &gateway->data);
1255 }
1256 }
1257 }
1258 continue;
1259
1260 case IPPOOL_TOOL_MODIFY:
1261 fr_value_box_list_talloc_free(&vb_list);
1262 if (xlat_modify_lease(autofree, &vb_list, request, p, el) < 0) EXIT_WITH_FAILURE;
1263
1264 INFO("Modified %pV address(es)/prefix(es)", fr_value_box_list_head(&vb_list));
1265 continue;
1266
1267 case IPPOOL_TOOL_ASSIGN:
1268 if (fr_ipaddr_cmp(&p->start, &p->end) != 0) {
1269 ERROR("Static assignment requires a single IP");
1270 fr_exit_now(EXIT_FAILURE);
1271 }
1272 if (!owner) {
1273 ERROR("Static assignment requires an owner");
1274 fr_exit_now(EXIT_FAILURE);
1275 }
1276
1277 fr_value_box_list_talloc_free(&vb_list);
1278 if (xlat_assign_lease(autofree, &vb_list, request, p, owner, el) < 0) EXIT_WITH_FAILURE;
1279 INFO("Assigned %pV address(es)/prefix(es)", fr_value_box_list_head(&vb_list));
1280 continue;
1281
1283 if (fr_ipaddr_cmp(&p->start, &p->end) != 0) {
1284 ERROR("Static lease un-assignment requires a single IP");
1285 fr_exit_now(EXIT_FAILURE);
1286 }
1287 if (!owner) {
1288 ERROR("Static lease un-assignment requires an owner");
1289 fr_exit_now(EXIT_FAILURE);
1290 }
1291
1292 fr_value_box_list_talloc_free(&vb_list);
1293 if (xlat_unassign_lease(autofree, &vb_list, request, p, owner, el) < 0) EXIT_WITH_FAILURE;
1294 INFO("Un-assigned %pV address(es)/prefix(es)", fr_value_box_list_head(&vb_list));
1295 continue;
1296
1297 case IPPOOL_TOOL_NOOP:
1298 break;
1299 }
1300
1301cleanup:
1302
1303 /*
1304 * The same clean up sequence as unit_test_module.c
1305 */
1306 talloc_free(thread_ctx);
1311 server_free();
1316 fr_dict_free(&dict, __FILE__);
1319
1320 return ret;
1321}
int n
Definition acutest.h:577
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition atexit.c:179
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition atexit.c:310
#define fr_atexit_thread_trigger_all(...)
Definition atexit.h:236
#define RCSID(id)
Definition build.h:560
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:382
#define NUM_ELEMENTS(_t)
Definition build.h:406
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
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:1204
TALLOC_CTX * autofree
Definition common.c:29
fr_dict_t * dict
Definition common.c:31
int fr_coords_create(TALLOC_CTX *ctx, fr_event_list_t *el)
Start coordinators in single threaded mode.
Definition coord.c:592
void fr_coords_destroy(void)
Clean up coordinators in single threaded mode.
Definition coord.c:574
int fr_coord_post_event_insert(fr_event_list_t *el)
Insert instance specific post-event callbacks.
Definition coord.c:832
int fr_coord_pre_event_insert(fr_event_list_t *el)
Insert instance specific pre-event callbacks.
Definition coord.c:807
void fr_disable_null_tracking_on_free(TALLOC_CTX *ctx)
Disable the null tracking context when a talloc chunk is freed.
Definition debug.c:1034
void fr_talloc_fault_setup(void)
Register talloc fault handlers.
Definition debug.c:1050
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program, unsigned long fault_signals)
Registers signal handlers to execute panic_action on fatal signal.
Definition debug.c:1074
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:172
#define PANIC_ACTION_SIGNALS
Definition debug.h:128
#define MEM(x)
Definition debug.h:38
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:267
#define ERROR(fmt,...)
Definition dhcpclient.c:40
static NEVER_RETURNS void usage(void)
Definition dhcpclient.c:113
#define fr_dict_autofree(_to_free)
Definition dict.h:915
int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir, char const *dependent))
(Re-)Initialize the special internal dictionary
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
int fr_dict_free(fr_dict_t **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
Definition dict_util.c:4327
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition dict_util.c:4393
#define fr_dict_autoload(_to_load)
Definition dict.h:912
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
fr_dict_gctx_t * fr_dict_global_ctx_init(TALLOC_CTX *ctx, bool free_at_exit, char const *dict_dir))
Initialise the global protocol hashes.
Definition dict_util.c:4711
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
talloc_free(hp)
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:783
void fr_ipaddr_mask(fr_ipaddr_t *addr, uint8_t prefix)
Zeroes out the host portion of an fr_ipaddr_t.
Definition inet.c:218
fr_cmp_ret_t fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
Definition inet.c:1353
union fr_ipaddr_t::@154 addr
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
IPv4/6 prefix.
int unlang_interpret_push_section(unlang_result_t *p_result, request_t *request, CONF_SECTION *cs, unlang_frame_conf_t const *conf)
Push a configuration section onto the request stack for later interpretation.
Definition interpret.c:1535
#define FRAME_CONF(_default_rcode, _top_frame)
Definition interpret.h:158
#define UNLANG_TOP_FRAME
Definition interpret.h:36
rlm_rcode_t unlang_interpret_synchronous(fr_event_list_t *el, request_t *request)
Execute an unlang section synchronously.
int server_init(CONF_SECTION *cs, char const *conf_dir, fr_dict_t *dict)
Initialize src/lib/server/.
Definition base.c:42
void server_free(void)
Free src/lib/server/.
Definition base.c:137
void vlog_request(fr_log_type_t type, fr_log_lvl_t lvl, request_t *request, char const *file, int line, char const *fmt, va_list ap, void *uctx)
Send a log message to its destination, possibly including fields from the request.
Definition log.c:293
#define PERROR(_fmt,...)
Definition log.h:233
Definition log.h:70
int fr_packet_pairs_from_packet(TALLOC_CTX *ctx, fr_pair_list_t *list, fr_packet_t const *packet)
Allocate a "Net." struct with src/dst host and port.
Definition packet.c:91
int unlang_global_init(void)
Definition base.c:158
Stores all information relating to an event list.
Definition event.c:377
int fr_debug_lvl
Definition log.c:41
fr_log_t default_log
Definition log.c:306
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
Definition packet.c:38
Minimal data structure to use the new code.
Definition listen.h:63
int main_config_free(main_config_t **config)
main_config_t * main_config_alloc(TALLOC_CTX *ctx)
Allocate a main_config_t struct, setting defaults.
void main_config_name_set_default(main_config_t *config, char const *name, bool overwrite_config)
Set the server name.
int main_config_init(main_config_t *config)
void main_config_confdir_set(main_config_t *config, char const *name)
Set the global radius config directory.
void main_config_dict_dir_set(main_config_t *config, char const *name)
Set the global dictionary directory.
Main server configuration.
Definition main_config.h:51
fr_event_list_t * main_loop_event_list(void)
Return the main loop event list.
Definition main_loop.c:166
int main_loop_init(void)
Initialise the main event loop, setting up signal handlers.
Definition main_loop.c:255
void main_loop_free(void)
Definition main_loop.c:191
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_BOOL
A truth value.
unsigned int uint32_t
unsigned char uint8_t
module_instance_t * module_rlm_static_by_name(module_instance_t const *parent, char const *asked_name)
Definition module_rlm.c:817
int modules_rlm_coord_attach(fr_event_list_t *el)
Runs the coord_attach method of all registered backend modules.
Definition module_rlm.c:995
int modules_rlm_free(void)
Cleanup all global structures.
int modules_rlm_init(void)
Initialise the module list structure.
unsigned int fr_pair_count_by_da(fr_pair_list_t const *list, fr_dict_attr_t const *da)
Return the number of instances of a given da in the specified list.
Definition pair.c:684
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition pair.c:784
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:707
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
Definition pair.c:956
fr_pair_t * fr_pair_afrom_da_nested(TALLOC_CTX *ctx, fr_pair_list_t *list, fr_dict_attr_t const *da)
Create a pair (and all intermediate parents), and append it to the list.
Definition pair.c:480
#define is_truncated(_ret, _max)
Definition print.h:48
static const conf_parser_t config[]
Definition base.c:162
#define fr_assert(_expr)
Definition rad_assert.h:37
#define INFO(fmt,...)
Definition radict.c:63
static bool cleanup
Definition radsniff.c:59
#define RLM_MODULE_USER_SECTION_REJECT
Rcodes that translate to a user configurable section failing overall.
Definition rcode.h:79
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
#define IPADDR_LEN(_af)
#define IPPOOL_SPRINT_IP(_buff, _ip, _prefix)
If the prefix is as wide as the AF data size then print it without CIDR notation.
int request_global_init(void)
Definition request.c:598
#define request_local_alloc_internal(_ctx, _args)
Allocate a new internal request outside of the request pool.
Definition request.h:344
@ REQUEST_ACTIVE
Request is active (running or runnable)
Definition request.h:89
static fr_dict_attr_t const * attr_ippool_stats_dynamic_expire1d
int main(int argc, char *argv[])
static int xlat_remove_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, ippool_tool_operation_t *op, fr_event_list_t *el)
fr_dict_attr_autoload_t redis_ippool_tool_dict_attr[]
static fr_dict_attr_t const * attr_ippool_stats_static_renew1m
char const * name
Original range or CIDR string.
static fr_dict_attr_t const * attr_ippool_stats_static
static fr_dict_attr_t const * attr_ippool_stats_static_renew1h
static fr_dict_attr_t const * attr_ippool_stats_dynamic_free
static char server_conf2[]
static fr_dict_attr_t const * attr_ippool_stats_static_renew30m
static int xlat_release_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, ippool_tool_operation_t *op, fr_event_list_t *el)
static fr_dict_attr_t const * attr_ippool_stats_dynamic_expire1m
static fr_dict_attr_t const * attr_ippool_lease_device
static fr_dict_attr_t const * attr_ippool_stats_dynamic_expire30m
static fr_dict_attr_t const * attr_ippool_lease_gateway
static fr_dict_t const * dict_freeradius
#define EOL
static fr_dict_attr_t const * attr_ippool_stats_static_total
static fr_dict_attr_t const * attr_ippool_name
fr_dict_autoload_t redis_ippool_tool_dict[]
size_t pool_len
Length of the pool identifier.
static int redis_ippool_get_stats(request_t *request, fr_value_box_t *pool_name, CONF_SECTION *section, fr_event_list_t *el)
Run the redis_ippool.stats module method to fetch pool statistics.
static fr_dict_attr_t const * attr_ippool_stats_static_free
static char const * name
static fr_dict_attr_t const * attr_ippool_lease
static char server_conf1[]
Static server config split into two parts.
static uint32_t uint32_gen_mask(uint8_t bits)
static fr_dict_attr_t const * attr_ippool_lease_active
static int xlat_add_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, ippool_tool_operation_t *op, fr_event_list_t *el)
static fr_dict_attr_t const * attr_ippool
static fr_dict_attr_t const * attr_ippool_stats_dynamic
#define ADD_ACTION(_action)
static int xlat_unassign_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, ippool_tool_operation_t *op, char const *owner, fr_event_list_t *el)
static int redis_ippool_show(request_t *request, ippool_tool_operation_t const *op, CONF_SECTION *section, fr_event_list_t *el)
Run the redis_ippool.show module method to fetch lease details.
uint8_t const * range
Range identifier.
static fr_dict_attr_t const * attr_ippool_lease_address
static fr_dict_attr_t const * attr_ippool_lease_expires
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.
size_t range_len
Length of the range identifier.
fr_ipaddr_t end
End address.
#define EXIT_WITH_FAILURE
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 fr_dict_attr_t const * attr_ippool_lease_range
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 fr_dict_attr_t const * attr_ippool_stats
static fr_dict_attr_t const * attr_ippool_stats_dynamic_expire1h
fr_ipaddr_t start
Start address.
static fr_dict_attr_t const * attr_ippool_stats_static_renew1d
static int xlat_modify_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, ippool_tool_operation_t *op, fr_event_list_t *el)
static int redis_ippool_get_pools(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, fr_event_list_t *el)
Run the redis_ippool.pools.list xlat to fetch the list of pools across the cluster.
static fr_dict_attr_t const * attr_ippool_stats_dynamic_total
static int run_xlat(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, fr_sbuff_t *xlat, fr_event_list_t *el)
static bool ipaddr_next(fr_ipaddr_t *ipaddr, fr_ipaddr_t const *end, uint8_t prefix)
Iterate over range of IP addresses.
static int xlat_assign_lease(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, ippool_tool_operation_t *op, char const *owner, fr_event_list_t *el)
static request_t * request_from_internal(TALLOC_CTX *ctx)
static fr_dict_attr_t const * attr_ippool_stats_total
enum ippool_tool_action ippool_tool_action_t
Pool management actions.
A single pool operation.
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1611
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_IN_STR(_start)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_used(_sbuff_or_marker)
Talloc sbuff extension structure.
Definition sbuff.h:137
Set of parsing rules for *unescape_until functions.
void fr_schedule_worker_id_set(int id)
Explicitly set the worker id for the current thread.
Definition schedule.c:119
Module instance data.
Definition module.h:287
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:233
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_client_t * client_find(fr_client_list_t const *clients, fr_ipaddr_t const *ipaddr, int proto)
Definition client.c:370
void modules_init(char const *lib_dir)
Perform global initialisation for modules.
Definition module.c:1952
fr_pair_t * vp
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:55
int fr_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el)
Instantiate thread-specific data for modules, virtual servers, xlats, unlang, and TLS.
Definition thread.c:165
int fr_time_start(void)
Initialize the local time.
Definition time.c:150
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
static fr_event_list_t * el
int unlang_xlat_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
Push a pre-compiled xlat onto the stack for evaluation.
Definition xlat.c:269
bool xlat_needs_resolving(xlat_exp_head_t const *head)
Check to see if the expansion needs resolving.
fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Tokenize an xlat expansion.
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
Walk over an xlat tree recursively, resolving any unresolved functions or references.
#define FR_DICTIONARY_INTERNAL_DIR
Definition conf.h:7
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
Definition pair.h:279
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Holds information necessary for binding or connecting to a socket.
Definition socket.h:60
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:737
#define fr_strerror_const(_msg)
Definition strerror.h:223
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
Definition version.c:40
#define RADIUSD_MAGIC_NUMBER
Definition version.h:81
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:4416
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
Definition value.c:6094
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:1219
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition value.c:4862
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:644
#define fr_box_ipaddr(_val)
Definition value.h:317
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:224
static size_t char ** out
Definition value.h:1030
virtual_server_t const * virtual_server_find(char const *name)
Return virtual server matching the specified name.
CONF_SECTION * virtual_server_cs(virtual_server_t const *vs)
Return the configuration section for a virtual server.
int virtual_servers_init(void)
Performs global initialisation for the virtual server code.
int virtual_servers_free(void)