ipsec pool --batch command
[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 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 if (db->execute(db, NULL,
739 "DELETE FROM attributes WHERE type = ? AND value = ?",
740 DB_INT, type, DB_BLOB, value) != 1)
741 {
742 fprintf(stderr, "deleting %s server %H failed\n", name, server);
743 exit(EXIT_FAILURE);
744 }
745 printf("deleted %s server %H\n", name, server);
746 if (db->execute(db, NULL,
747 "DELETE FROM attributes WHERE type = ? AND value = ?",
748 DB_INT, type, DB_BLOB, value) != 1)
749 {
750 fprintf(stderr, "deleting %s server %H failed\n", name, server);
751 exit(EXIT_FAILURE);
752 }
753 printf("deleted %s server %H\n", name, server);
754
755 */
756 static void resize(char *name, host_t *end)
757 {
758 enumerator_t *query;
759 chunk_t old_addr, new_addr, cur_addr;
760 u_int id, count;
761 host_t *old_end;
762
763 new_addr = end->get_address(end);
764
765 query = db->query(db, "SELECT id, end FROM pools WHERE name = ?",
766 DB_TEXT, name, DB_UINT, DB_BLOB);
767 if (!query || !query->enumerate(query, &id, &old_addr))
768 {
769 DESTROY_IF(query);
770 fprintf(stderr, "resizing pool failed.\n");
771 exit(EXIT_FAILURE);
772 }
773 if (old_addr.len != new_addr.len ||
774 memcmp(new_addr.ptr, old_addr.ptr, old_addr.len) < 0)
775 {
776 fprintf(stderr, "shrinking of pools not supported.\n");
777 query->destroy(query);
778 exit(EXIT_FAILURE);
779 }
780 cur_addr = chunk_clonea(old_addr);
781 count = get_pool_size(old_addr, new_addr) - 1;
782 query->destroy(query);
783
784 /* Check whether pool is resizable */
785 old_end = host_create_from_chunk(AF_UNSPEC, old_addr, 0);
786 if (old_end && old_end->is_anyaddr(old_end))
787 {
788 fprintf(stderr, "pool is not resizable.\n");
789 old_end->destroy(old_end);
790 exit(EXIT_FAILURE);
791 }
792 DESTROY_IF(old_end);
793
794 if (db->execute(db, NULL,
795 "UPDATE pools SET end = ? WHERE name = ?",
796 DB_BLOB, new_addr, DB_TEXT, name) <= 0)
797 {
798 fprintf(stderr, "pool '%s' not found.\n", name);
799 exit(EXIT_FAILURE);
800 }
801
802 printf("allocating %d new addresses... ", count);
803 fflush(stdout);
804 /* run population in a transaction for sqlite */
805 begin_transaction();
806 while (count-- > 0)
807 {
808 chunk_increment(cur_addr);
809 db->execute(db, NULL,
810 "INSERT INTO addresses (pool, address, identity, acquired, released) "
811 "VALUES (?, ?, ?, ?, ?)",
812 DB_UINT, id, DB_BLOB, cur_addr, DB_UINT, 0, DB_UINT, 0, DB_UINT, 1);
813 }
814 commit_transaction();
815 printf("done.\n", count);
816
817 }
818
819 /**
820 * create the lease query using the filter string
821 */
822 static enumerator_t *create_lease_query(char *filter)
823 {
824 enumerator_t *query;
825 identification_t *id = NULL;
826 host_t *addr = NULL;
827 u_int tstamp = 0;
828 bool online = FALSE, valid = FALSE, expired = FALSE;
829 char *value, *pos, *pool = NULL;
830 enum {
831 FIL_POOL = 0,
832 FIL_ID,
833 FIL_ADDR,
834 FIL_TSTAMP,
835 FIL_STATE,
836 };
837 char *const token[] = {
838 [FIL_POOL] = "pool",
839 [FIL_ID] = "id",
840 [FIL_ADDR] = "addr",
841 [FIL_TSTAMP] = "tstamp",
842 [FIL_STATE] = "status",
843 NULL
844 };
845
846 /* if the filter string contains a distinguished name as a ID, we replace
847 * ", " by "/ " in order to not confuse the getsubopt parser */
848 pos = filter;
849 while ((pos = strchr(pos, ',')))
850 {
851 if (pos[1] == ' ')
852 {
853 pos[0] = '/';
854 }
855 pos++;
856 }
857
858 while (filter && *filter != '\0')
859 {
860 switch (getsubopt(&filter, token, &value))
861 {
862 case FIL_POOL:
863 if (value)
864 {
865 pool = value;
866 }
867 break;
868 case FIL_ID:
869 if (value)
870 {
871 id = identification_create_from_string(value);
872 }
873 break;
874 case FIL_ADDR:
875 if (value)
876 {
877 addr = host_create_from_string(value, 0);
878 }
879 if (!addr)
880 {
881 fprintf(stderr, "invalid 'addr' in filter string.\n");
882 exit(EXIT_FAILURE);
883 }
884 break;
885 case FIL_TSTAMP:
886 if (value)
887 {
888 tstamp = atoi(value);
889 }
890 if (tstamp == 0)
891 {
892 online = TRUE;
893 }
894 break;
895 case FIL_STATE:
896 if (value)
897 {
898 if (streq(value, "online"))
899 {
900 online = TRUE;
901 }
902 else if (streq(value, "valid"))
903 {
904 valid = TRUE;
905 }
906 else if (streq(value, "expired"))
907 {
908 expired = TRUE;
909 }
910 else
911 {
912 fprintf(stderr, "invalid 'state' in filter string.\n");
913 exit(EXIT_FAILURE);
914 }
915 }
916 break;
917 default:
918 fprintf(stderr, "invalid filter string.\n");
919 exit(EXIT_FAILURE);
920 break;
921 }
922 }
923 query = db->query(db,
924 "SELECT name, addresses.address, identities.type, "
925 "identities.data, leases.acquired, leases.released, timeout "
926 "FROM leases JOIN addresses ON leases.address = addresses.id "
927 "JOIN pools ON addresses.pool = pools.id "
928 "JOIN identities ON leases.identity = identities.id "
929 "WHERE (? OR name = ?) "
930 "AND (? OR (identities.type = ? AND identities.data = ?)) "
931 "AND (? OR addresses.address = ?) "
932 "AND (? OR (? >= leases.acquired AND (? <= leases.released))) "
933 "AND (? OR leases.released > ? - timeout) "
934 "AND (? OR leases.released < ? - timeout) "
935 "AND ? "
936 "UNION "
937 "SELECT name, address, identities.type, identities.data, "
938 "acquired, released, timeout FROM addresses "
939 "JOIN pools ON addresses.pool = pools.id "
940 "JOIN identities ON addresses.identity = identities.id "
941 "WHERE ? AND released = 0 "
942 "AND (? OR name = ?) "
943 "AND (? OR (identities.type = ? AND identities.data = ?)) "
944 "AND (? OR address = ?)",
945 DB_INT, pool == NULL, DB_TEXT, pool,
946 DB_INT, id == NULL,
947 DB_INT, id ? id->get_type(id) : 0,
948 DB_BLOB, id ? id->get_encoding(id) : chunk_empty,
949 DB_INT, addr == NULL,
950 DB_BLOB, addr ? addr->get_address(addr) : chunk_empty,
951 DB_INT, tstamp == 0, DB_UINT, tstamp, DB_UINT, tstamp,
952 DB_INT, !valid, DB_INT, time(NULL),
953 DB_INT, !expired, DB_INT, time(NULL),
954 DB_INT, !online,
955 /* union */
956 DB_INT, !(valid || expired),
957 DB_INT, pool == NULL, DB_TEXT, pool,
958 DB_INT, id == NULL,
959 DB_INT, id ? id->get_type(id) : 0,
960 DB_BLOB, id ? id->get_encoding(id) : chunk_empty,
961 DB_INT, addr == NULL,
962 DB_BLOB, addr ? addr->get_address(addr) : chunk_empty,
963 /* res */
964 DB_TEXT, DB_BLOB, DB_INT, DB_BLOB, DB_UINT, DB_UINT, DB_UINT);
965 /* id and addr leak but we can't destroy them until query is destroyed. */
966 return query;
967 }
968
969 /**
970 * ipsec pool --leases - show lease information of a pool
971 */
972 static void leases(char *filter, bool utc)
973 {
974 enumerator_t *query;
975 chunk_t address_chunk, identity_chunk;
976 int identity_type;
977 char *name;
978 u_int db_acquired, db_released, db_timeout;
979 time_t acquired, released, timeout;
980 host_t *address;
981 identification_t *identity;
982 bool found = FALSE;
983
984 query = create_lease_query(filter);
985 if (!query)
986 {
987 fprintf(stderr, "querying leases failed.\n");
988 exit(EXIT_FAILURE);
989 }
990 while (query->enumerate(query, &name, &address_chunk, &identity_type,
991 &identity_chunk, &db_acquired, &db_released, &db_timeout))
992 {
993 if (!found)
994 {
995 int len = utc ? 25 : 21;
996
997 found = TRUE;
998 printf("%-8s %-15s %-7s %-*s %-*s %s\n",
999 "name", "address", "status", len, "start", len, "end", "identity");
1000 }
1001 address = host_create_from_chunk(AF_UNSPEC, address_chunk, 0);
1002 identity = identification_create_from_encoding(identity_type, identity_chunk);
1003
1004 /* u_int is not always equal to time_t */
1005 acquired = (time_t)db_acquired;
1006 released = (time_t)db_released;
1007 timeout = (time_t)db_timeout;
1008
1009 printf("%-8s %-15H ", name, address);
1010 if (released == 0)
1011 {
1012 printf("%-7s ", "online");
1013 }
1014 else if (timeout == 0)
1015 {
1016 printf("%-7s ", "static");
1017 }
1018 else if (released >= time(NULL) - timeout)
1019 {
1020 printf("%-7s ", "valid");
1021 }
1022 else
1023 {
1024 printf("%-7s ", "expired");
1025 }
1026
1027 printf(" %T ", &acquired, utc);
1028 if (released)
1029 {
1030 printf("%T ", &released, utc);
1031 }
1032 else
1033 {
1034 printf(" ");
1035 if (utc)
1036 {
1037 printf(" ");
1038 }
1039 }
1040 printf("%Y\n", identity);
1041 DESTROY_IF(address);
1042 identity->destroy(identity);
1043 }
1044 query->destroy(query);
1045 if (!found)
1046 {
1047 fprintf(stderr, "no matching leases found.\n");
1048 exit(EXIT_FAILURE);
1049 }
1050 }
1051
1052 /**
1053 * ipsec pool --purge - delete expired leases
1054 */
1055 static void purge(char *name)
1056 {
1057 int purged = 0;
1058
1059 purged = db->execute(db, NULL,
1060 "DELETE FROM leases WHERE address IN ("
1061 " SELECT id FROM addresses WHERE pool IN ("
1062 " SELECT id FROM pools WHERE name = ?))",
1063 DB_TEXT, name);
1064 if (purged < 0)
1065 {
1066 fprintf(stderr, "purging pool '%s' failed.\n", name);
1067 exit(EXIT_FAILURE);
1068 }
1069 fprintf(stderr, "purged %d leases in pool '%s'.\n", purged, name);
1070 }
1071
1072 #define ARGV_SIZE 32
1073
1074 static void argv_add(char **argv, int argc, char *value)
1075 {
1076 if (argc >= ARGV_SIZE)
1077 {
1078 fprintf(stderr, "too many arguments: %s\n", value);
1079 exit(EXIT_FAILURE);
1080 }
1081 argv[argc] = value;
1082 }
1083
1084 /**
1085 * ipsec pool --batch - read commands from a file
1086 */
1087 static void batch(char *argv0, char *name)
1088 {
1089 char command[512];
1090
1091 FILE *file = strncmp(name, "-", 1) == 0 ? stdin : fopen(name, "r");
1092 if (file == NULL)
1093 {
1094 fprintf(stderr, "opening '%s' failed: %s\n", name, strerror(errno));
1095 exit(EXIT_FAILURE);
1096 }
1097
1098 begin_transaction();
1099 while (fgets(command, sizeof(command), file))
1100 {
1101 char *argv[ARGV_SIZE], *start;
1102 int i, argc = 0;
1103 size_t cmd_len = strlen(command);
1104
1105 /* ignore empty lines */
1106 if (cmd_len == 1 && *(command + cmd_len - 1) == '\n')
1107 {
1108 continue;
1109 }
1110
1111 /* parse command into argv */
1112 start = command;
1113 argv_add(argv, argc++, argv0);
1114 for (i = 0; i < cmd_len; ++i)
1115 {
1116 if (command[i] == ' ' || command[i] == '\n')
1117 {
1118 if (command + i == start)
1119 {
1120 /* ignore leading whitespace */
1121 ++start;
1122 continue;
1123 }
1124 command[i] = '\0';
1125 argv_add(argv, argc++, start);
1126 start = command + i + 1;
1127 }
1128 }
1129 if (strlen(start) > 0)
1130 {
1131 argv_add(argv, argc++, start);
1132 }
1133 argv_add(argv, argc, NULL);
1134
1135 do_args(argc, argv);
1136 }
1137 commit_transaction();
1138
1139 if (file != stdin)
1140 {
1141 fclose(file);
1142 }
1143 }
1144
1145 /**
1146 * atexit handler to close db on shutdown
1147 */
1148 static void cleanup(void)
1149 {
1150 db->destroy(db);
1151 DESTROY_IF(start);
1152 DESTROY_IF(end);
1153 DESTROY_IF(server);
1154 }
1155
1156 static void do_args(int argc, char *argv[])
1157 {
1158 char *name = "", *filter = "", *addresses = NULL;
1159 int timeout = 0;
1160 bool utc = FALSE;
1161 enum {
1162 OP_UNDEF,
1163 OP_USAGE,
1164 OP_STATUS,
1165 OP_ADD,
1166 OP_ADD_ATTR,
1167 OP_DEL,
1168 OP_DEL_ATTR,
1169 OP_RESIZE,
1170 OP_LEASES,
1171 OP_PURGE,
1172 OP_BATCH
1173 } operation = OP_UNDEF;
1174
1175 /* set option index to first argument */
1176 optind = 1;
1177
1178 while (TRUE)
1179 {
1180 int c;
1181
1182 struct option long_opts[] = {
1183 { "help", no_argument, NULL, 'h' },
1184
1185 { "utc", no_argument, NULL, 'u' },
1186 { "status", no_argument, NULL, 'w' },
1187 { "add", required_argument, NULL, 'a' },
1188 { "replace", required_argument, NULL, 'c' },
1189 { "del", required_argument, NULL, 'd' },
1190 { "resize", required_argument, NULL, 'r' },
1191 { "leases", no_argument, NULL, 'l' },
1192 { "purge", required_argument, NULL, 'p' },
1193 { "batch", required_argument, NULL, 'b' },
1194
1195 { "start", required_argument, NULL, 's' },
1196 { "end", required_argument, NULL, 'e' },
1197 { "addresses", required_argument, NULL, 'x' },
1198 { "timeout", required_argument, NULL, 't' },
1199 { "filter", required_argument, NULL, 'f' },
1200 { "server", required_argument, NULL, 'v' },
1201 { 0,0,0,0 }
1202 };
1203
1204 c = getopt_long(argc, argv, "", long_opts, NULL);
1205 switch (c)
1206 {
1207 case EOF:
1208 break;
1209 case 'h':
1210 operation = OP_USAGE;
1211 break;
1212 case 'w':
1213 operation = OP_STATUS;
1214 break;
1215 case 'u':
1216 utc = TRUE;
1217 continue;
1218 case 'c':
1219 replace_pool = TRUE;
1220 /* fallthrough */
1221 case 'a':
1222 name = optarg;
1223 operation = is_attribute(name) ? OP_ADD_ATTR : OP_ADD;
1224 if (replace_pool && operation == OP_ADD_ATTR)
1225 {
1226 fprintf(stderr, "invalid pool name: '%s'.\n", optarg);
1227 usage();
1228 exit(EXIT_FAILURE);
1229 }
1230 continue;
1231 case 'd':
1232 name = optarg;
1233 operation = is_attribute(name) ? OP_DEL_ATTR : OP_DEL;
1234 continue;
1235 case 'r':
1236 name = optarg;
1237 operation = OP_RESIZE;
1238 continue;
1239 case 'l':
1240 operation = OP_LEASES;
1241 continue;
1242 case 'p':
1243 name = optarg;
1244 operation = OP_PURGE;
1245 continue;
1246 case 'b':
1247 name = optarg;
1248 if (operation == OP_BATCH)
1249 {
1250 fprintf(stderr, "--batch commands can not be nested\n");
1251 exit(EXIT_FAILURE);
1252 }
1253 operation = OP_BATCH;
1254 continue;
1255 case 's':
1256 DESTROY_IF(start);
1257 start = host_create_from_string(optarg, 0);
1258 if (start == NULL)
1259 {
1260 fprintf(stderr, "invalid start address: '%s'.\n", optarg);
1261 usage();
1262 exit(EXIT_FAILURE);
1263 }
1264 continue;
1265 case 'e':
1266 DESTROY_IF(end);
1267 end = host_create_from_string(optarg, 0);
1268 if (end == NULL)
1269 {
1270 fprintf(stderr, "invalid end address: '%s'.\n", optarg);
1271 usage();
1272 exit(EXIT_FAILURE);
1273 }
1274 continue;
1275 case 't':
1276 timeout = atoi(optarg);
1277 if (timeout == 0 && strcmp(optarg, "0") != 0)
1278 {
1279 fprintf(stderr, "invalid timeout '%s'.\n", optarg);
1280 usage();
1281 exit(EXIT_FAILURE);
1282 }
1283 continue;
1284 case 'f':
1285 filter = optarg;
1286 continue;
1287 case 'x':
1288 addresses = optarg;
1289 continue;
1290 case 'v':
1291 DESTROY_IF(server);
1292 server = host_create_from_string(optarg, 0);
1293 if (server == NULL)
1294 {
1295 fprintf(stderr, "invalid server address: '%s'.\n", optarg);
1296 usage();
1297 exit(EXIT_FAILURE);
1298 }
1299 continue;
1300 default:
1301 usage();
1302 exit(EXIT_FAILURE);
1303 break;
1304 }
1305 break;
1306 }
1307
1308 switch (operation)
1309 {
1310 case OP_USAGE:
1311 usage();
1312 break;
1313 case OP_STATUS:
1314 status();
1315 break;
1316 case OP_ADD:
1317 if (addresses != NULL)
1318 {
1319 add_addresses(name, addresses, timeout);
1320 }
1321 else if (start != NULL && end != NULL)
1322 {
1323 add(name, start, end, timeout);
1324 }
1325 else
1326 {
1327 fprintf(stderr, "missing arguments.\n");
1328 usage();
1329 exit(EXIT_FAILURE);
1330 }
1331 break;
1332 case OP_ADD_ATTR:
1333 if (server == NULL)
1334 {
1335 fprintf(stderr, "missing arguments.\n");
1336 usage();
1337 exit(EXIT_FAILURE);
1338 }
1339 add_attr(name, server);
1340 break;
1341 case OP_DEL:
1342 del(name);
1343 break;
1344 case OP_DEL_ATTR:
1345 del_attr(name, server);
1346 break;
1347 case OP_RESIZE:
1348 if (end == NULL)
1349 {
1350 fprintf(stderr, "missing arguments.\n");
1351 usage();
1352 exit(EXIT_FAILURE);
1353 }
1354 resize(name, end);
1355 break;
1356 case OP_LEASES:
1357 leases(filter, utc);
1358 break;
1359 case OP_PURGE:
1360 purge(name);
1361 break;
1362 case OP_BATCH:
1363 if (name == NULL)
1364 {
1365 fprintf(stderr, "missing arguments.\n");
1366 usage();
1367 exit(EXIT_FAILURE);
1368 }
1369 batch(argv[0], name);
1370 break;
1371 default:
1372 usage();
1373 exit(EXIT_FAILURE);
1374 }
1375 }
1376
1377 int main(int argc, char *argv[])
1378 {
1379 char *uri;
1380
1381 atexit(library_deinit);
1382
1383 /* initialize library */
1384 if (!library_init(NULL))
1385 {
1386 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
1387 }
1388 if (lib->integrity &&
1389 !lib->integrity->check_file(lib->integrity, "pool", argv[0]))
1390 {
1391 fprintf(stderr, "integrity check of pool failed\n");
1392 exit(SS_RC_DAEMON_INTEGRITY);
1393 }
1394 if (!lib->plugins->load(lib->plugins, NULL,
1395 lib->settings->get_str(lib->settings, "pool.load", PLUGINS)))
1396 {
1397 exit(SS_RC_INITIALIZATION_FAILED);
1398 }
1399
1400 uri = lib->settings->get_str(lib->settings, "libstrongswan.plugins.attr-sql.database", NULL);
1401 if (!uri)
1402 {
1403 fprintf(stderr, "database URI libstrongswan.plugins.attr-sql.database not set.\n");
1404 exit(SS_RC_INITIALIZATION_FAILED);
1405 }
1406 db = lib->db->create(lib->db, uri);
1407 if (!db)
1408 {
1409 fprintf(stderr, "opening database failed.\n");
1410 exit(SS_RC_INITIALIZATION_FAILED);
1411 }
1412 atexit(cleanup);
1413
1414 do_args(argc, argv);
1415
1416 exit(EXIT_SUCCESS);
1417 }
1418