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