bc9bc73e2d9a89f8d3f49aaf5c6355865aa108dc
[strongswan.git] / src / libstrongswan / plugins / attr_sql / pool.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE
17 #include <getopt.h>
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <time.h>
21 #include <string.h>
22 #include <errno.h>
23
24 #include <debug.h>
25 #include <library.h>
26 #include <utils/host.h>
27 #include <utils/identification.h>
28 #include <attributes/attributes.h>
29
30 /**
31 * global database handle
32 */
33 database_t *db;
34
35 /**
36 * --start/--end/--server addresses of various subcommands
37 */
38 host_t *start = NULL, *end = NULL, *server = NULL;
39
40 /**
41 * whether --add should --replace an existing pool
42 */
43 bool replace_pool = FALSE;
44
45 /**
46 * forward declaration
47 */
48 static void del(char *name);
49
50 /**
51 * Create or replace a pool by name
52 */
53 static u_int create_pool(char *name, chunk_t start, chunk_t end, int timeout)
54 {
55 enumerator_t *e;
56 int pool;
57
58 e = db->query(db, "SELECT id FROM pools WHERE name = ?",
59 DB_TEXT, name, DB_UINT);
60 if (e && e->enumerate(e, &pool))
61 {
62 if (replace_pool == FALSE)
63 {
64 fprintf(stderr, "pool '%s' exists.\n", name);
65 e->destroy(e);
66 exit(EXIT_FAILURE);
67 }
68 del(name);
69 }
70 DESTROY_IF(e);
71 if (db->execute(db, &pool,
72 "INSERT INTO pools (name, start, end, timeout) VALUES (?, ?, ?, ?)",
73 DB_TEXT, name, DB_BLOB, start, DB_BLOB, end,
74 DB_INT, timeout*3600) != 1)
75 {
76 fprintf(stderr, "creating pool failed.\n");
77 exit(EXIT_FAILURE);
78 }
79
80 return pool;
81 }
82
83 /**
84 * instead of a pool handle a DNS or NBNS attribute
85 */
86 static bool is_attribute(char *name)
87 {
88 return strcaseeq(name, "dns") || strcaseeq(name, "nbns") ||
89 strcaseeq(name, "wins");
90 }
91
92 /**
93 * determine configuration attribute type
94 */
95 static configuration_attribute_type_t get_attribute_type(char *name, host_t* addr)
96 {
97 if (strcaseeq(name, "dns"))
98 {
99 return (addr->get_family(addr) == AF_INET) ? INTERNAL_IP4_DNS :
100 INTERNAL_IP6_DNS;
101 }
102 else
103 {
104 return (addr->get_family(addr) == AF_INET) ? INTERNAL_IP4_NBNS :
105 INTERNAL_IP6_NBNS;
106 }
107 }
108
109 /**
110 * calculate the size of a pool using start and end address chunk
111 */
112 static u_int get_pool_size(chunk_t start, chunk_t end)
113 {
114 u_int *start_ptr, *end_ptr;
115
116 if (start.len < sizeof(u_int) || end.len < sizeof(u_int))
117 {
118 return 0;
119 }
120 start_ptr = (u_int*)(start.ptr + start.len - sizeof(u_int));
121 end_ptr = (u_int*)(end.ptr + end.len - sizeof(u_int));
122 return ntohl(*end_ptr) - ntohl(*start_ptr) + 1;
123 }
124
125 /**
126 * print usage info
127 */
128 static void usage(void)
129 {
130 printf("\
131 Usage:\n\
132 ipsec pool --status|--add|--replace|--del|--resize|--purge [options]\n\
133 \n\
134 ipsec pool --status\n\
135 Show a list of installed pools with statistics.\n\
136 \n\
137 ipsec pool --add <name> --start <start> --end <end> [--timeout <timeout>]\n\
138 ipsec pool --replace <name> --start <start> --end <end> [--timeout <timeout>]\n\
139 Add a new pool to or replace an existing pool in the database.\n\
140 name: Name of the pool, as used in ipsec.conf rightsourceip=%%name\n\
141 start: Start address of the pool\n\
142 end: End address of the pool\n\
143 timeout: Lease time in hours, 0 for static leases\n\
144 \n\
145 ipsec pool --add <name> --addresses <file> [--timeout <timeout>]\n\
146 ipsec pool --replace <name> --addresses <file> [--timeout <timeout>]\n\
147 Add a new pool to or replace an existing pool in the database.\n\
148 name: Name of the pool, as used in ipsec.conf rightsourceip=%%name\n\
149 file: File newline separated addresses for the pool are read from.\n\
150 Optionally each address can be pre-assigned to a roadwarrior\n\
151 identity, e.g. 10.231.14.2=alice@strongswan.org.\n\
152 If a - (hyphen) is given instead of a file name, the addresses\n\
153 are read from STDIN. Reading addresses stops at the end of file\n\
154 or an empty line. Pools created with this command can not be\n\
155 resized.\n\
156 timeout: Lease time in hours, 0 for static leases\n\
157 \n\
158 ipsec pool --add dns|nbns|wins --server <server>\n\
159 Add a new DNS or NBNS server to the database.\n\
160 server: IP address of the name server\n\
161 \n\
162 ipsec pool --del <name>\n\
163 Delete a pool from the database.\n\
164 name: Name of the pool to delete\n\
165 \n\
166 ipsec pool --del dns|nbns|wins [--server <server>]\n\
167 Delete a specific or all DNS or NBNS servers from the database.\n\
168 server: IP address of the name server to delete\n\
169 \n\
170 ipsec pool --resize <name> --end <end>\n\
171 Grow or shrink an existing pool.\n\
172 name: Name of the pool to resize\n\
173 end: New end address for the pool\n\
174 \n\
175 ipsec pool --leases [--filter <filter>] [--utc]\n\
176 Show lease information using filters:\n\
177 filter: Filter string containing comma separated key=value filters,\n\
178 e.g. id=alice@strongswan.org,addr=1.1.1.1\n\
179 pool: name of the pool\n\
180 id: assigned identity of the lease\n\
181 addr: lease IP address\n\
182 tstamp: UNIX timestamp when lease was valid, as integer\n\
183 status: status of the lease: online|valid|expired\n\
184 utc: Show times in UTC instead of local time\n\
185 \n\
186 ipsec pool --purge <name>\n\
187 Delete lease history of a pool:\n\
188 name: Name of the pool to purge\n\
189 \n");
190 }
191
192 /**
193 * ipsec pool --status - show pool overview
194 */
195 static void status(void)
196 {
197 enumerator_t *ns, *pool, *lease;
198 host_t *server;
199 chunk_t value;
200 bool found = FALSE;
201
202 /* enumerate IPv4 DNS servers */
203 ns = db->query(db, "SELECT value FROM attributes WHERE type = ?",
204 DB_INT, INTERNAL_IP4_DNS, DB_BLOB);
205 if (ns)
206 {
207 while (ns->enumerate(ns, &value))
208 {
209 if (!found)
210 {
211 printf("dns servers:");
212 found = TRUE;
213 }
214 server = host_create_from_chunk(AF_INET, value, 0);
215 if (server)
216 {
217 printf(" %H", server);
218 server->destroy(server);
219 }
220 }
221 ns->destroy(ns);
222 }
223
224 /* enumerate IPv6 DNS servers */
225 ns = db->query(db, "SELECT value FROM attributes WHERE type = ?",
226 DB_INT, INTERNAL_IP6_DNS, DB_BLOB);
227 if (ns)
228 {
229 while (ns->enumerate(ns, &value))
230 {
231 if (!found)
232 {
233 printf("dns servers:");
234 found = TRUE;
235 }
236 server = host_create_from_chunk(AF_INET6, value, 0);
237 if (server)
238 {
239 printf(" %H", server);
240 server->destroy(server);
241 }
242 }
243 ns->destroy(ns);
244 }
245 if (found)
246 {
247 printf("\n");
248 }
249 else
250 {
251 printf("no dns servers found.\n");
252 }
253 found = FALSE;
254
255 /* enumerate IPv4 NBNS servers */
256 ns = db->query(db, "SELECT value FROM attributes WHERE type = ?",
257 DB_INT, INTERNAL_IP4_NBNS, DB_BLOB);
258 if (ns)
259 {
260 while (ns->enumerate(ns, &value))
261 {
262 if (!found)
263 {
264 printf("nbns servers:");
265 found = TRUE;
266 }
267 server = host_create_from_chunk(AF_INET, value, 0);
268 if (server)
269 {
270 printf(" %H", server);
271 server->destroy(server);
272 }
273 }
274 ns->destroy(ns);
275 }
276
277 /* enumerate IPv6 NBNS servers */
278 ns = db->query(db, "SELECT value FROM attributes WHERE type = ?",
279 DB_INT, INTERNAL_IP6_NBNS, DB_BLOB);
280 if (ns)
281 {
282 while (ns->enumerate(ns, &value))
283 {
284 if (!found)
285 {
286 printf("nbns servers:");
287 found = TRUE;
288 }
289 server = host_create_from_chunk(AF_INET6, value, 0);
290 if (server)
291 {
292 printf(" %H", server);
293 server->destroy(server);
294 }
295 }
296 ns->destroy(ns);
297 }
298 if (found)
299 {
300 printf("\n");
301 }
302 else
303 {
304 printf("no nbns servers found.\n");
305 }
306 found = FALSE;
307
308 pool = db->query(db, "SELECT id, name, start, end, timeout FROM pools",
309 DB_INT, DB_TEXT, DB_BLOB, DB_BLOB, DB_UINT);
310 if (pool)
311 {
312 char *name;
313 chunk_t start_chunk, end_chunk;
314 host_t *start, *end;
315 u_int id, timeout, online = 0, used = 0, size = 0;
316
317 while (pool->enumerate(pool, &id, &name,
318 &start_chunk, &end_chunk, &timeout))
319 {
320 if (!found)
321 {
322 printf("%8s %15s %15s %8s %6s %11s %11s\n", "name", "start",
323 "end", "timeout", "size", "online", "usage");
324 found = TRUE;
325 }
326
327 start = host_create_from_chunk(AF_UNSPEC, start_chunk, 0);
328 end = host_create_from_chunk(AF_UNSPEC, end_chunk, 0);
329 if (start->is_anyaddr(start) && end->is_anyaddr(end))
330 {
331 printf("%8s %15s %15s ", name, "n/a", "n/a");
332 }
333 else
334 {
335 printf("%8s %15H %15H ", name, start, end);
336 }
337 if (timeout)
338 {
339 printf("%7dh ", timeout/3600);
340 }
341 else
342 {
343 printf("%8s ", "static");
344 }
345 /* get total number of hosts in the pool */
346 lease = db->query(db, "SELECT COUNT(*) FROM addresses "
347 "WHERE pool = ?", DB_UINT, id, DB_INT);
348 if (lease)
349 {
350 lease->enumerate(lease, &size);
351 lease->destroy(lease);
352 }
353 printf("%6d ", size);
354 /* get number of online hosts */
355 lease = db->query(db, "SELECT COUNT(*) FROM addresses "
356 "WHERE pool = ? AND released = 0",
357 DB_UINT, id, DB_INT);
358 if (lease)
359 {
360 lease->enumerate(lease, &online);
361 lease->destroy(lease);
362 }
363 printf("%5d (%2d%%) ", online, online*100/size);
364 /* get number of online or valid lieases */
365 lease = db->query(db, "SELECT COUNT(*) FROM addresses "
366 "WHERE addresses.pool = ? "
367 "AND ((? AND acquired != 0) "
368 " OR released = 0 OR released > ?) ",
369 DB_UINT, id, DB_UINT, !timeout,
370 DB_UINT, time(NULL) - timeout, DB_UINT);
371 if (lease)
372 {
373 lease->enumerate(lease, &used);
374 lease->destroy(lease);
375 }
376 printf("%5d (%2d%%) ", used, used*100/size);
377
378 printf("\n");
379 DESTROY_IF(start);
380 DESTROY_IF(end);
381 }
382 pool->destroy(pool);
383 }
384 if (!found)
385 {
386 printf("no pools found.\n");
387 }
388 }
389
390 /**
391 * ipsec pool --add - add a new pool
392 */
393 static void add(char *name, host_t *start, host_t *end, int timeout)
394 {
395 chunk_t start_addr, end_addr, cur_addr;
396 u_int id, count;
397
398 start_addr = start->get_address(start);
399 end_addr = end->get_address(end);
400 cur_addr = chunk_clonea(start_addr);
401 count = get_pool_size(start_addr, end_addr);
402
403 if (start_addr.len != end_addr.len ||
404 memcmp(start_addr.ptr, end_addr.ptr, start_addr.len) > 0)
405 {
406 fprintf(stderr, "invalid start/end pair specified.\n");
407 exit(EXIT_FAILURE);
408 }
409 id = create_pool(name, start_addr, end_addr, timeout);
410 printf("allocating %d addresses... ", count);
411 fflush(stdout);
412 if (db->get_driver(db) == DB_SQLITE)
413 { /* run population in a transaction for sqlite */
414 db->execute(db, NULL, "BEGIN TRANSACTION");
415 }
416 while (TRUE)
417 {
418 db->execute(db, NULL,
419 "INSERT INTO addresses (pool, address, identity, acquired, released) "
420 "VALUES (?, ?, ?, ?, ?)",
421 DB_UINT, id, DB_BLOB, cur_addr, DB_UINT, 0, DB_UINT, 0, DB_UINT, 1);
422 if (chunk_equals(cur_addr, end_addr))
423 {
424 break;
425 }
426 chunk_increment(cur_addr);
427 }
428 if (db->get_driver(db) == DB_SQLITE)
429 {
430 db->execute(db, NULL, "END TRANSACTION");
431 }
432 printf("done.\n", count);
433 }
434
435 static bool add_address(u_int pool_id, char *address_str, int *family)
436 {
437 host_t *address;
438 int user_id = 0;
439
440 char *pos_eq = strchr(address_str, '=');
441 if (pos_eq != NULL)
442 {
443 enumerator_t *e;
444 identification_t *id = identification_create_from_string(pos_eq + 1);
445
446 /* look for peer identity in the identities table */
447 e = db->query(db,
448 "SELECT id FROM identities WHERE type = ? AND data = ?",
449 DB_INT, id->get_type(id), DB_BLOB, id->get_encoding(id),
450 DB_UINT);
451
452 if (!e || !e->enumerate(e, &user_id))
453 {
454 /* not found, insert new one */
455 if (db->execute(db, &user_id,
456 "INSERT INTO identities (type, data) VALUES (?, ?)",
457 DB_INT, id->get_type(id),
458 DB_BLOB, id->get_encoding(id)) != 1)
459 {
460 fprintf(stderr, "creating id '%s' failed.\n", pos_eq + 1);
461 return FALSE;
462 }
463 }
464 DESTROY_IF(e);
465 id->destroy(id);
466 *pos_eq = '\0';
467 }
468
469 address = host_create_from_string(address_str, 0);
470 if (address == NULL)
471 {
472 fprintf(stderr, "invalid address '%s'.\n", address_str);
473 return FALSE;
474 }
475 if (family && *family && *family != address->get_family(address))
476 {
477 fprintf(stderr, "invalid address family '%s'.\n", address_str);
478 return FALSE;
479 }
480
481 if (db->execute(db, NULL,
482 "INSERT INTO addresses "
483 "(pool, address, identity, acquired, released) "
484 "VALUES (?, ?, ?, ?, ?)",
485 DB_UINT, pool_id, DB_BLOB, address->get_address(address),
486 DB_UINT, user_id, DB_UINT, 0, DB_UINT, 1) != 1)
487 {
488 fprintf(stderr, "inserting address '%s' failed.\n", address_str);
489 return FALSE;
490 }
491 *family = address->get_family(address);
492 address->destroy(address);
493
494 return TRUE;
495 }
496
497 static void add_addresses(char *pool, char *path, int timeout)
498 {
499 u_int pool_id, count = 0;
500 int family = AF_UNSPEC;
501 char address_str[512];
502 host_t *addr;
503 FILE *file;
504
505 if (db->get_driver(db) == DB_SQLITE)
506 { /* run population in a transaction for sqlite */
507 db->execute(db, NULL, "BEGIN TRANSACTION");
508 }
509
510 addr = host_create_from_string("%any", 0);
511 pool_id = create_pool(pool, addr->get_address(addr),
512 addr->get_address(addr), timeout);
513 addr->destroy(addr);
514
515 file = (strcmp(path, "-") == 0 ? stdin : fopen(path, "r"));
516 if (file == NULL)
517 {
518 fprintf(stderr, "opening '%s' failed: %s\n", path, strerror(errno));
519 exit(-1);
520 }
521
522 printf("starting allocation... ");
523 fflush(stdout);
524
525 while (fgets(address_str, sizeof(address_str), file))
526 {
527 size_t addr_len = strlen(address_str);
528 char *last_chr = address_str + addr_len - 1;
529 if (*last_chr == '\n')
530 {
531 if (addr_len == 1)
532 { /* end of input */
533 break;
534 }
535 *last_chr = '\0';
536 }
537 if (add_address(pool_id, address_str, &family) == FALSE)
538 {
539 exit(EXIT_FAILURE);
540 }
541 ++count;
542 }
543
544 if (file != stdin)
545 {
546 fclose(file);
547 }
548
549 if (db->get_driver(db) == DB_SQLITE)
550 {
551 db->execute(db, NULL, "END TRANSACTION");
552 }
553
554 printf("%d addresses done.\n", count);
555 }
556
557 /**
558 * ipsec pool --add dns|nbns|wins - add a DNS or NBNS server entry
559 */
560 static void add_attr(char *name, host_t *server)
561 {
562 configuration_attribute_type_t type;
563 chunk_t value;
564
565 type = get_attribute_type(name, server);
566 value = server->get_address(server);
567 if (db->execute(db, NULL,
568 "INSERT INTO attributes (type, value) VALUES (?, ?)",
569 DB_INT, type, DB_BLOB, value) != 1)
570 {
571 fprintf(stderr, "adding %s server %H failed.\n", name, server);
572 exit(EXIT_FAILURE);
573 }
574 printf("added %s server %H\n", name, server);
575 }
576
577 /**
578 * ipsec pool --del - delete a pool
579 */
580 static void del(char *name)
581 {
582 enumerator_t *query;
583 u_int id;
584 bool found = FALSE;
585
586 query = db->query(db, "SELECT id FROM pools WHERE name = ?",
587 DB_TEXT, name, DB_UINT);
588 if (!query)
589 {
590 fprintf(stderr, "deleting pool failed.\n");
591 exit(EXIT_FAILURE);
592 }
593 while (query->enumerate(query, &id))
594 {
595 found = TRUE;
596 if (db->execute(db, NULL,
597 "DELETE FROM leases WHERE address IN ("
598 " SELECT id FROM addresses WHERE pool = ?)", DB_UINT, id) < 0 ||
599 db->execute(db, NULL,
600 "DELETE FROM addresses WHERE pool = ?", DB_UINT, id) < 0 ||
601 db->execute(db, NULL,
602 "DELETE FROM pools WHERE id = ?", DB_UINT, id) < 0)
603 {
604 fprintf(stderr, "deleting pool failed.\n");
605 query->destroy(query);
606 exit(EXIT_FAILURE);
607 }
608 }
609 query->destroy(query);
610 if (!found)
611 {
612 fprintf(stderr, "pool '%s' not found.\n", name);
613 exit(EXIT_FAILURE);
614 }
615 }
616
617 /**
618 * ipsec pool --del dns|nbns|wins - delete a DNS or NBNS server entry
619 */
620 static void del_attr(char *name, host_t *server)
621 {
622 configuration_attribute_type_t type;
623 chunk_t value;
624 u_int id;
625 enumerator_t *query;
626 bool found = FALSE;
627
628 if (server)
629 {
630 type = get_attribute_type(name, server);
631 value = server->get_address(server);
632 query = db->query(db,
633 "SELECT id, type, value FROM attributes "
634 "WHERE type = ? AND value = ?",
635 DB_INT, type, DB_BLOB, value,
636 DB_UINT, DB_INT, DB_BLOB);
637 }
638 else
639 {
640 configuration_attribute_type_t type_ip4, type_ip6;
641
642 if (strcaseeq(name, "dns"))
643 {
644 type_ip4 = INTERNAL_IP4_DNS;
645 type_ip6 = INTERNAL_IP6_DNS;
646 }
647 else
648 {
649 type_ip4 = INTERNAL_IP4_NBNS;
650 type_ip6 = INTERNAL_IP6_NBNS;
651 }
652
653 query = db->query(db,
654 "SELECT id, type, value FROM attributes "
655 "WHERE type = ? OR type = ?",
656 DB_INT, type_ip4, DB_INT, type_ip6,
657 DB_UINT, DB_INT, DB_BLOB);
658 }
659 if (!query)
660 {
661 fprintf(stderr, "deleting %s servers failed.\n", name);
662 exit(EXIT_FAILURE);
663 }
664
665 while (query->enumerate(query, &id, &type, &value))
666 {
667 int family;
668 host_t *host;
669
670 found = TRUE;
671 family = (type == INTERNAL_IP4_DNS || type == INTERNAL_IP4_NBNS) ?
672 AF_INET : AF_INET6;
673 host = host_create_from_chunk(family, value, 0);
674 if (db->execute(db, NULL,
675 "DELETE FROM attributes WHERE id = ?",
676 DB_UINT, id) != 1)
677 {
678 fprintf(stderr, "deleting %s server %H failed\n", name, host);
679 query->destroy(query);
680 DESTROY_IF(host);
681 exit(EXIT_FAILURE);
682 }
683 printf("deleted %s server %H\n", name, host);
684 DESTROY_IF(host);
685 }
686 query->destroy(query);
687
688 if (!found && server)
689 {
690 printf("%s server %H not found\n", name, server);
691 exit(EXIT_FAILURE);
692 }
693 else if (!found)
694 {
695 printf("no %s servers found\n", name);
696 }
697 }
698
699 /**
700 * ipsec pool --resize - resize a pool if (db->execute(db, NULL,
701 "DELETE FROM attributes WHERE type = ? AND value = ?",
702 DB_INT, type, DB_BLOB, value) != 1)
703 {
704 fprintf(stderr, "deleting %s server %H failed\n", name, server);
705 exit(EXIT_FAILURE);
706 }
707 printf("deleted %s server %H\n", name, server);
708 if (db->execute(db, NULL,
709 "DELETE FROM attributes WHERE type = ? AND value = ?",
710 DB_INT, type, DB_BLOB, value) != 1)
711 {
712 fprintf(stderr, "deleting %s server %H failed\n", name, server);
713 exit(EXIT_FAILURE);
714 }
715 printf("deleted %s server %H\n", name, server);
716
717 */
718 static void resize(char *name, host_t *end)
719 {
720 enumerator_t *query;
721 chunk_t old_addr, new_addr, cur_addr;
722 u_int id, count;
723 host_t *old_end;
724
725 new_addr = end->get_address(end);
726
727 query = db->query(db, "SELECT id, end FROM pools WHERE name = ?",
728 DB_TEXT, name, DB_UINT, DB_BLOB);
729 if (!query || !query->enumerate(query, &id, &old_addr))
730 {
731 DESTROY_IF(query);
732 fprintf(stderr, "resizing pool failed.\n");
733 exit(EXIT_FAILURE);
734 }
735 if (old_addr.len != new_addr.len ||
736 memcmp(new_addr.ptr, old_addr.ptr, old_addr.len) < 0)
737 {
738 fprintf(stderr, "shrinking of pools not supported.\n");
739 query->destroy(query);
740 exit(EXIT_FAILURE);
741 }
742 cur_addr = chunk_clonea(old_addr);
743 count = get_pool_size(old_addr, new_addr) - 1;
744 query->destroy(query);
745
746 /* Check whether pool is resizable */
747 old_end = host_create_from_chunk(AF_UNSPEC, old_addr, 0);
748 if (old_end && old_end->is_anyaddr(old_end))
749 {
750 fprintf(stderr, "pool is not resizable.\n");
751 old_end->destroy(old_end);
752 exit(EXIT_FAILURE);
753 }
754 DESTROY_IF(old_end);
755
756 if (db->execute(db, NULL,
757 "UPDATE pools SET end = ? WHERE name = ?",
758 DB_BLOB, new_addr, DB_TEXT, name) <= 0)
759 {
760 fprintf(stderr, "pool '%s' not found.\n", name);
761 exit(EXIT_FAILURE);
762 }
763
764 printf("allocating %d new addresses... ", count);
765 fflush(stdout);
766 if (db->get_driver(db) == DB_SQLITE)
767 { /* run population in a transaction for sqlite */
768 db->execute(db, NULL, "BEGIN TRANSACTION");
769 }
770 while (count-- > 0)
771 {
772 chunk_increment(cur_addr);
773 db->execute(db, NULL,
774 "INSERT INTO addresses (pool, address, identity, acquired, released) "
775 "VALUES (?, ?, ?, ?, ?)",
776 DB_UINT, id, DB_BLOB, cur_addr, DB_UINT, 0, DB_UINT, 0, DB_UINT, 1);
777 }
778 if (db->get_driver(db) == DB_SQLITE)
779 {
780 db->execute(db, NULL, "END TRANSACTION");
781 }
782 printf("done.\n", count);
783
784 }
785
786 /**
787 * create the lease query using the filter string
788 */
789 static enumerator_t *create_lease_query(char *filter)
790 {
791 enumerator_t *query;
792 identification_t *id = NULL;
793 host_t *addr = NULL;
794 u_int tstamp = 0;
795 bool online = FALSE, valid = FALSE, expired = FALSE;
796 char *value, *pos, *pool = NULL;
797 enum {
798 FIL_POOL = 0,
799 FIL_ID,
800 FIL_ADDR,
801 FIL_TSTAMP,
802 FIL_STATE,
803 };
804 char *const token[] = {
805 [FIL_POOL] = "pool",
806 [FIL_ID] = "id",
807 [FIL_ADDR] = "addr",
808 [FIL_TSTAMP] = "tstamp",
809 [FIL_STATE] = "status",
810 NULL
811 };
812
813 /* if the filter string contains a distinguished name as a ID, we replace
814 * ", " by "/ " in order to not confuse the getsubopt parser */
815 pos = filter;
816 while ((pos = strchr(pos, ',')))
817 {
818 if (pos[1] == ' ')
819 {
820 pos[0] = '/';
821 }
822 pos++;
823 }
824
825 while (filter && *filter != '\0')
826 {
827 switch (getsubopt(&filter, token, &value))
828 {
829 case FIL_POOL:
830 if (value)
831 {
832 pool = value;
833 }
834 break;
835 case FIL_ID:
836 if (value)
837 {
838 id = identification_create_from_string(value);
839 }
840 break;
841 case FIL_ADDR:
842 if (value)
843 {
844 addr = host_create_from_string(value, 0);
845 }
846 if (!addr)
847 {
848 fprintf(stderr, "invalid 'addr' in filter string.\n");
849 exit(EXIT_FAILURE);
850 }
851 break;
852 case FIL_TSTAMP:
853 if (value)
854 {
855 tstamp = atoi(value);
856 }
857 if (tstamp == 0)
858 {
859 online = TRUE;
860 }
861 break;
862 case FIL_STATE:
863 if (value)
864 {
865 if (streq(value, "online"))
866 {
867 online = TRUE;
868 }
869 else if (streq(value, "valid"))
870 {
871 valid = TRUE;
872 }
873 else if (streq(value, "expired"))
874 {
875 expired = TRUE;
876 }
877 else
878 {
879 fprintf(stderr, "invalid 'state' in filter string.\n");
880 exit(EXIT_FAILURE);
881 }
882 }
883 break;
884 default:
885 fprintf(stderr, "invalid filter string.\n");
886 exit(EXIT_FAILURE);
887 break;
888 }
889 }
890 query = db->query(db,
891 "SELECT name, addresses.address, identities.type, "
892 "identities.data, leases.acquired, leases.released, timeout "
893 "FROM leases JOIN addresses ON leases.address = addresses.id "
894 "JOIN pools ON addresses.pool = pools.id "
895 "JOIN identities ON leases.identity = identities.id "
896 "WHERE (? OR name = ?) "
897 "AND (? OR (identities.type = ? AND identities.data = ?)) "
898 "AND (? OR addresses.address = ?) "
899 "AND (? OR (? >= leases.acquired AND (? <= leases.released))) "
900 "AND (? OR leases.released > ? - timeout) "
901 "AND (? OR leases.released < ? - timeout) "
902 "AND ? "
903 "UNION "
904 "SELECT name, address, identities.type, identities.data, "
905 "acquired, released, timeout FROM addresses "
906 "JOIN pools ON addresses.pool = pools.id "
907 "JOIN identities ON addresses.identity = identities.id "
908 "WHERE ? AND released = 0 "
909 "AND (? OR name = ?) "
910 "AND (? OR (identities.type = ? AND identities.data = ?)) "
911 "AND (? OR address = ?)",
912 DB_INT, pool == NULL, DB_TEXT, pool,
913 DB_INT, id == NULL,
914 DB_INT, id ? id->get_type(id) : 0,
915 DB_BLOB, id ? id->get_encoding(id) : chunk_empty,
916 DB_INT, addr == NULL,
917 DB_BLOB, addr ? addr->get_address(addr) : chunk_empty,
918 DB_INT, tstamp == 0, DB_UINT, tstamp, DB_UINT, tstamp,
919 DB_INT, !valid, DB_INT, time(NULL),
920 DB_INT, !expired, DB_INT, time(NULL),
921 DB_INT, !online,
922 /* union */
923 DB_INT, !(valid || expired),
924 DB_INT, pool == NULL, DB_TEXT, pool,
925 DB_INT, id == NULL,
926 DB_INT, id ? id->get_type(id) : 0,
927 DB_BLOB, id ? id->get_encoding(id) : chunk_empty,
928 DB_INT, addr == NULL,
929 DB_BLOB, addr ? addr->get_address(addr) : chunk_empty,
930 /* res */
931 DB_TEXT, DB_BLOB, DB_INT, DB_BLOB, DB_UINT, DB_UINT, DB_UINT);
932 /* id and addr leak but we can't destroy them until query is destroyed. */
933 return query;
934 }
935
936 /**
937 * ipsec pool --leases - show lease information of a pool
938 */
939 static void leases(char *filter, bool utc)
940 {
941 enumerator_t *query;
942 chunk_t address_chunk, identity_chunk;
943 int identity_type;
944 char *name;
945 u_int db_acquired, db_released, db_timeout;
946 time_t acquired, released, timeout;
947 host_t *address;
948 identification_t *identity;
949 bool found = FALSE;
950
951 query = create_lease_query(filter);
952 if (!query)
953 {
954 fprintf(stderr, "querying leases failed.\n");
955 exit(EXIT_FAILURE);
956 }
957 while (query->enumerate(query, &name, &address_chunk, &identity_type,
958 &identity_chunk, &db_acquired, &db_released, &db_timeout))
959 {
960 if (!found)
961 {
962 int len = utc ? 25 : 21;
963
964 found = TRUE;
965 printf("%-8s %-15s %-7s %-*s %-*s %s\n",
966 "name", "address", "status", len, "start", len, "end", "identity");
967 }
968 address = host_create_from_chunk(AF_UNSPEC, address_chunk, 0);
969 identity = identification_create_from_encoding(identity_type, identity_chunk);
970
971 /* u_int is not always equal to time_t */
972 acquired = (time_t)db_acquired;
973 released = (time_t)db_released;
974 timeout = (time_t)db_timeout;
975
976 printf("%-8s %-15H ", name, address);
977 if (released == 0)
978 {
979 printf("%-7s ", "online");
980 }
981 else if (timeout == 0)
982 {
983 printf("%-7s ", "static");
984 }
985 else if (released >= time(NULL) - timeout)
986 {
987 printf("%-7s ", "valid");
988 }
989 else
990 {
991 printf("%-7s ", "expired");
992 }
993
994 printf(" %T ", &acquired, utc);
995 if (released)
996 {
997 printf("%T ", &released, utc);
998 }
999 else
1000 {
1001 printf(" ");
1002 if (utc)
1003 {
1004 printf(" ");
1005 }
1006 }
1007 printf("%Y\n", identity);
1008 DESTROY_IF(address);
1009 identity->destroy(identity);
1010 }
1011 query->destroy(query);
1012 if (!found)
1013 {
1014 fprintf(stderr, "no matching leases found.\n");
1015 exit(EXIT_FAILURE);
1016 }
1017 }
1018
1019 /**
1020 * ipsec pool --purge - delete expired leases
1021 */
1022 static void purge(char *name)
1023 {
1024 int purged = 0;
1025
1026 purged = db->execute(db, NULL,
1027 "DELETE FROM leases WHERE address IN ("
1028 " SELECT id FROM addresses WHERE pool IN ("
1029 " SELECT id FROM pools WHERE name = ?))",
1030 DB_TEXT, name);
1031 if (purged < 0)
1032 {
1033 fprintf(stderr, "purging pool '%s' failed.\n", name);
1034 exit(EXIT_FAILURE);
1035 }
1036 fprintf(stderr, "purged %d leases in pool '%s'.\n", purged, name);
1037 }
1038
1039 /**
1040 * atexit handler to close db on shutdown
1041 */
1042 static void cleanup(void)
1043 {
1044 db->destroy(db);
1045 DESTROY_IF(start);
1046 DESTROY_IF(end);
1047 DESTROY_IF(server);
1048 }
1049
1050 int main(int argc, char *argv[])
1051 {
1052 char *uri, *name = "", *filter = "", *addresses = NULL;
1053 int timeout = 0;
1054 bool utc = FALSE;
1055 enum {
1056 OP_UNDEF,
1057 OP_USAGE,
1058 OP_STATUS,
1059 OP_ADD,
1060 OP_ADD_ATTR,
1061 OP_DEL,
1062 OP_DEL_ATTR,
1063 OP_RESIZE,
1064 OP_LEASES,
1065 OP_PURGE
1066 } operation = OP_UNDEF;
1067
1068 atexit(library_deinit);
1069
1070 /* initialize library */
1071 if (!library_init(NULL))
1072 {
1073 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
1074 }
1075 if (lib->integrity &&
1076 !lib->integrity->check_file(lib->integrity, "pool", argv[0]))
1077 {
1078 fprintf(stderr, "integrity check of pool failed\n");
1079 exit(SS_RC_DAEMON_INTEGRITY);
1080 }
1081 if (!lib->plugins->load(lib->plugins, NULL,
1082 lib->settings->get_str(lib->settings, "pool.load", PLUGINS)))
1083 {
1084 exit(SS_RC_INITIALIZATION_FAILED);
1085 }
1086
1087 uri = lib->settings->get_str(lib->settings, "libstrongswan.plugins.attr-sql.database", NULL);
1088 if (!uri)
1089 {
1090 fprintf(stderr, "database URI libstrongswan.plugins.attr-sql.database not set.\n");
1091 exit(SS_RC_INITIALIZATION_FAILED);
1092 }
1093 db = lib->db->create(lib->db, uri);
1094 if (!db)
1095 {
1096 fprintf(stderr, "opening database failed.\n");
1097 exit(SS_RC_INITIALIZATION_FAILED);
1098 }
1099 atexit(cleanup);
1100
1101 while (TRUE)
1102 {
1103 int c;
1104
1105 struct option long_opts[] = {
1106 { "help", no_argument, NULL, 'h' },
1107
1108 { "utc", no_argument, NULL, 'u' },
1109 { "status", no_argument, NULL, 'w' },
1110 { "add", required_argument, NULL, 'a' },
1111 { "replace", required_argument, NULL, 'c' },
1112 { "del", required_argument, NULL, 'd' },
1113 { "resize", required_argument, NULL, 'r' },
1114 { "leases", no_argument, NULL, 'l' },
1115 { "purge", required_argument, NULL, 'p' },
1116
1117 { "start", required_argument, NULL, 's' },
1118 { "end", required_argument, NULL, 'e' },
1119 { "addresses", required_argument, NULL, 'x' },
1120 { "timeout", required_argument, NULL, 't' },
1121 { "filter", required_argument, NULL, 'f' },
1122 { "server", required_argument, NULL, 'v' },
1123 { 0,0,0,0 }
1124 };
1125
1126 c = getopt_long(argc, argv, "", long_opts, NULL);
1127 switch (c)
1128 {
1129 case EOF:
1130 break;
1131 case 'h':
1132 operation = OP_USAGE;
1133 break;
1134 case 'w':
1135 operation = OP_STATUS;
1136 break;
1137 case 'u':
1138 utc = TRUE;
1139 continue;
1140 case 'c':
1141 replace_pool = TRUE;
1142 /* fallthrough */
1143 case 'a':
1144 name = optarg;
1145 operation = is_attribute(name) ? OP_ADD_ATTR : OP_ADD;
1146 if (replace_pool && operation == OP_ADD_ATTR)
1147 {
1148 fprintf(stderr, "invalid pool name: '%s'.\n", optarg);
1149 usage();
1150 exit(EXIT_FAILURE);
1151 }
1152 continue;
1153 case 'd':
1154 name = optarg;
1155 operation = is_attribute(name) ? OP_DEL_ATTR : OP_DEL;
1156 continue;
1157 case 'r':
1158 name = optarg;
1159 operation = OP_RESIZE;
1160 continue;
1161 case 'l':
1162 operation = OP_LEASES;
1163 continue;
1164 case 'p':
1165 name = optarg;
1166 operation = OP_PURGE;
1167 continue;
1168 case 's':
1169 start = host_create_from_string(optarg, 0);
1170 if (start == NULL)
1171 {
1172 fprintf(stderr, "invalid start address: '%s'.\n", optarg);
1173 usage();
1174 exit(EXIT_FAILURE);
1175 }
1176 continue;
1177 case 'e':
1178 end = host_create_from_string(optarg, 0);
1179 if (end == NULL)
1180 {
1181 fprintf(stderr, "invalid end address: '%s'.\n", optarg);
1182 usage();
1183 exit(EXIT_FAILURE);
1184 }
1185 continue;
1186 case 't':
1187 timeout = atoi(optarg);
1188 if (timeout == 0 && strcmp(optarg, "0") != 0)
1189 {
1190 fprintf(stderr, "invalid timeout '%s'.\n", optarg);
1191 usage();
1192 exit(EXIT_FAILURE);
1193 }
1194 continue;
1195 case 'f':
1196 filter = optarg;
1197 continue;
1198 case 'x':
1199 addresses = optarg;
1200 continue;
1201 case 'v':
1202 server = host_create_from_string(optarg, 0);
1203 if (server == NULL)
1204 {
1205 fprintf(stderr, "invalid server address: '%s'.\n", optarg);
1206 usage();
1207 exit(EXIT_FAILURE);
1208 }
1209 continue;
1210 default:
1211 usage();
1212 exit(EXIT_FAILURE);
1213 break;
1214 }
1215 break;
1216 }
1217
1218 switch (operation)
1219 {
1220 case OP_USAGE:
1221 usage();
1222 break;
1223 case OP_STATUS:
1224 status();
1225 break;
1226 case OP_ADD:
1227 if (addresses != NULL)
1228 {
1229 add_addresses(name, addresses, timeout);
1230 }
1231 else if (start != NULL && end != NULL)
1232 {
1233 add(name, start, end, timeout);
1234 }
1235 else
1236 {
1237 fprintf(stderr, "missing arguments.\n");
1238 usage();
1239 exit(EXIT_FAILURE);
1240 }
1241 break;
1242 case OP_ADD_ATTR:
1243 if (server == NULL)
1244 {
1245 fprintf(stderr, "missing arguments.\n");
1246 usage();
1247 exit(EXIT_FAILURE);
1248 }
1249 add_attr(name, server);
1250 break;
1251 case OP_DEL:
1252 del(name);
1253 break;
1254 case OP_DEL_ATTR:
1255 del_attr(name, server);
1256 break;
1257 case OP_RESIZE:
1258 if (end == NULL)
1259 {
1260 fprintf(stderr, "missing arguments.\n");
1261 usage();
1262 exit(EXIT_FAILURE);
1263 }
1264 resize(name, end);
1265 break;
1266 case OP_LEASES:
1267 leases(filter, utc);
1268 break;
1269 case OP_PURGE:
1270 purge(name);
1271 break;
1272 default:
1273 usage();
1274 exit(EXIT_FAILURE);
1275 }
1276 exit(EXIT_SUCCESS);
1277 }
1278