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