2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
26 #include <utils/host.h>
29 * global database handle
34 * --start/--end addresses of various subcommands
36 host_t
*start
= NULL
, *end
= NULL
;
39 * create a host from a blob
41 static host_t
*host_create_from_blob(chunk_t blob
)
43 return host_create_from_chunk(blob
.len
== 4 ? AF_INET
: AF_INET6
, blob
, 0);
47 * calculate the size of a pool using start and end address chunk
49 static u_int
get_pool_size(chunk_t start
, chunk_t end
)
51 u_int
*start_ptr
, *end_ptr
;
53 if (start
.len
< sizeof(u_int
) || end
.len
< sizeof(u_int
))
57 start_ptr
= (u_int
*)(start
.ptr
+ start
.len
- sizeof(u_int
));
58 end_ptr
= (u_int
*)(end
.ptr
+ end
.len
- sizeof(u_int
));
59 return ntohl(*end_ptr
) - ntohl(*start_ptr
) + 1;
65 static void usage(void)
69 ipsec pool --status|--add|--del|--resize|--purge [options]\n\
71 ipsec pool --status\n\
72 Show a list of installed pools with statistics.\n\
74 ipsec pool --add <name> --start <start> --end <end> [--timeout <timeout>]\n\
75 Add a new pool to the database.\n\
76 name: Name of the pool, as used in ipsec.conf rightsourceip=%%name\n\
77 start: Start address of the pool\n\
78 end: End address of the pool\n\
79 timeout: Lease time in hours, 0 for static leases\n\
81 ipsec pool --del <name>\n\
82 Delete a pool from the database.\n\
83 name: Name of the pool to delete\n\
85 ipsec pool --resize <name> --end <end>\n\
86 Grow or shrink an existing pool.\n\
87 name: Name of the pool to resize\n\
88 end: New end address for the pool\n\
90 ipsec pool --leases <name> [--filter <filter>] [--utc]\n\
91 Show lease information using filters:\n\
92 name: Name of the pool to show leases from\n\
93 filter: Filter string containing comma separated key=value filters,\n\
94 e.g. id=alice@strongswan.org,addr=1.1.1.1\n\
95 pool: name of the pool\n\
96 id: assigned identity of the lease\n\
97 addr: lease IP address\n\
98 tstamp: UNIX timestamp when lease was valid, as integer\n\
99 status: status of the lease: online|valid|expired\n\
100 utc: Show times in UTC instead of local time\n\
102 ipsec pool --purge <name>\n\
103 Delete lease history of a pool:\n\
104 name: Name of the pool to purge\n\
110 * ipsec pool --status - show pool overview
112 static void status(void)
114 enumerator_t
*pool
, *lease
;
117 pool
= db
->query(db
, "SELECT id, name, start, end, timeout FROM pools",
118 DB_INT
, DB_TEXT
, DB_BLOB
, DB_BLOB
, DB_UINT
);
122 chunk_t start_chunk
, end_chunk
;
124 u_int id
, timeout
, online
= 0, used
= 0, size
= 0;
126 while (pool
->enumerate(pool
, &id
, &name
,
127 &start_chunk
, &end_chunk
, &timeout
))
131 printf("%8s %15s %15s %8s %6s %11s %11s\n",
132 "name", "start", "end", "timeout", "size", "online", "leases");
136 start
= host_create_from_blob(start_chunk
);
137 end
= host_create_from_blob(end_chunk
);
138 size
= get_pool_size(start_chunk
, end_chunk
);
139 printf("%8s %15H %15H ", name
, start
, end
);
142 printf("%7dh ", timeout
/3600);
146 printf("%8s ", "static");
148 printf("%6d ", size
);
149 /* get number of online hosts */
150 lease
= db
->query(db
, "SELECT COUNT(*) FROM addresses "
151 "WHERE pool = ? AND acquired != 0 AND released = 0",
152 DB_UINT
, id
, DB_INT
);
155 lease
->enumerate(lease
, &online
);
156 lease
->destroy(lease
);
158 printf("%5d (%2d%%) ", online
, online
*100/size
);
159 /* get number of online or valid lieases */
160 lease
= db
->query(db
, "SELECT COUNT(*) FROM addresses JOIN pools "
161 "ON addresses.pool = pools.id "
162 "WHERE pools.id = ? AND acquired != 0 "
163 "AND (released = 0 OR released > ? - timeout) ",
164 DB_UINT
, id
, DB_UINT
, time(NULL
), DB_UINT
);
167 lease
->enumerate(lease
, &used
);
168 lease
->destroy(lease
);
170 printf("%5d (%2d%%) ", used
, used
*100/size
);
180 printf("no pools found.\n");
186 * increment a chunk, as it would reprensent a network order integer
188 static void increment_chunk(chunk_t chunk
)
192 for (i
= chunk
.len
- 1; i
>= 0; i
--)
194 if (++chunk
.ptr
[i
] != 0)
202 * ipsec pool --add - add a new pool
204 static void add(char *name
, host_t
*start
, host_t
*end
, int timeout
)
206 chunk_t start_addr
, end_addr
, cur_addr
;
209 start_addr
= start
->get_address(start
);
210 end_addr
= end
->get_address(end
);
211 cur_addr
= chunk_clonea(start_addr
);
212 count
= get_pool_size(start_addr
, end_addr
);
214 if (start_addr
.len
!= end_addr
.len
||
215 memcmp(start_addr
.ptr
, end_addr
.ptr
, start_addr
.len
) > 0)
217 fprintf(stderr
, "invalid start/end pair specified.\n");
220 if (db
->execute(db
, &id
,
221 "INSERT INTO pools (name, start, end, timeout) "
222 "VALUES (?, ?, ?, ?)",
223 DB_TEXT
, name
, DB_BLOB
, start_addr
,
224 DB_BLOB
, end_addr
, DB_INT
, timeout
*3600) != 1)
226 fprintf(stderr
, "creating pool failed.\n");
229 printf("allocating %d addresses... ", count
);
231 if (db
->get_driver(db
) == DB_SQLITE
)
232 { /* run population in a transaction for sqlite */
233 db
->execute(db
, NULL
, "BEGIN TRANSACTION");
237 db
->execute(db
, NULL
,
238 "INSERT INTO addresses (pool, address, identity, acquired, released) "
239 "VALUES (?, ?, ?, ?, ?)",
240 DB_UINT
, id
, DB_BLOB
, cur_addr
, DB_UINT
, 0, DB_UINT
, 0, DB_UINT
, 1);
241 increment_chunk(cur_addr
);
243 while (!chunk_equals(cur_addr
, end_addr
));
244 if (db
->get_driver(db
) == DB_SQLITE
)
246 db
->execute(db
, NULL
, "END TRANSACTION");
248 printf("done.\n", count
);
254 * ipsec pool --del - delete a pool
256 static void del(char *name
)
262 query
= db
->query(db
, "SELECT id FROM pools WHERE name = ?",
263 DB_TEXT
, name
, DB_UINT
);
266 fprintf(stderr
, "deleting pool failed.\n");
269 while (query
->enumerate(query
, &id
))
272 if (db
->execute(db
, NULL
,
273 "DELETE FROM leases WHERE address IN ("
274 " SELECT id FROM addresses WHERE pool = ?)", DB_UINT
, id
) < 0 ||
275 db
->execute(db
, NULL
,
276 "DELETE FROM addresses WHERE pool = ?", DB_UINT
, id
) < 0 ||
277 db
->execute(db
, NULL
,
278 "DELETE FROM pools WHERE id = ?", DB_UINT
, id
) < 0)
280 fprintf(stderr
, "deleting pool failed.\n");
281 query
->destroy(query
);
285 query
->destroy(query
);
288 fprintf(stderr
, "pool '%s' not found.\n", name
);
295 * ipsec pool --resize - resize a pool
297 static void resize(char *name
, host_t
*end
)
300 chunk_t old_addr
, new_addr
, cur_addr
;
303 new_addr
= end
->get_address(end
);
305 query
= db
->query(db
, "SELECT id, end FROM pools WHERE name = ?",
306 DB_TEXT
, name
, DB_UINT
, DB_BLOB
);
307 if (!query
|| !query
->enumerate(query
, &id
, &old_addr
))
310 fprintf(stderr
, "resizing pool failed.\n");
313 if (old_addr
.len
!= new_addr
.len
||
314 memcmp(new_addr
.ptr
, old_addr
.ptr
, old_addr
.len
) < 0)
316 fprintf(stderr
, "shrinking of pools not supported.\n");
317 query
->destroy(query
);
320 cur_addr
= chunk_clonea(old_addr
);
321 count
= get_pool_size(old_addr
, new_addr
) - 1;
322 query
->destroy(query
);
324 if (db
->execute(db
, NULL
,
325 "UPDATE pools SET end = ? WHERE name = ?",
326 DB_BLOB
, new_addr
, DB_TEXT
, name
) <= 0)
328 fprintf(stderr
, "pool '%s' not found.\n", name
);
332 printf("allocating %d new addresses... ", count
);
334 if (db
->get_driver(db
) == DB_SQLITE
)
335 { /* run population in a transaction for sqlite */
336 db
->execute(db
, NULL
, "BEGIN TRANSACTION");
340 increment_chunk(cur_addr
);
341 db
->execute(db
, NULL
,
342 "INSERT INTO addresses (pool, address, identity, acquired, released) "
343 "VALUES (?, ?, ?, ?, ?)",
344 DB_UINT
, id
, DB_BLOB
, cur_addr
, DB_UINT
, 0, DB_UINT
, 0, DB_UINT
, 1);
346 if (db
->get_driver(db
) == DB_SQLITE
)
348 db
->execute(db
, NULL
, "END TRANSACTION");
350 printf("done.\n", count
);
356 * create the lease query using the filter string
358 static enumerator_t
*create_lease_query(char *filter
)
361 identification_t
*id
= NULL
;
364 bool online
= FALSE
, valid
= FALSE
, expired
= FALSE
;
365 char *value
, *pos
, *pool
= NULL
;
373 char *const token
[] = {
377 [FIL_TSTAMP
] = "tstamp",
378 [FIL_STATE
] = "status",
382 /* if the filter string contains a distinguished name as a ID, we replace
383 * ", " by "/ " in order to not confuse the getsubopt parser */
385 while ((pos
= strchr(pos
, ',')))
394 while (filter
&& *filter
!= '\0')
396 switch (getsubopt(&filter
, token
, &value
))
407 id
= identification_create_from_string(value
);
411 fprintf(stderr
, "invalid 'id' in filter string.\n");
418 addr
= host_create_from_string(value
, 0);
422 fprintf(stderr
, "invalid 'addr' in filter string.\n");
429 tstamp
= atoi(value
);
439 if (streq(value
, "online"))
443 else if (streq(value
, "valid"))
447 else if (streq(value
, "expired"))
453 fprintf(stderr
, "invalid 'state' in filter string.\n");
459 fprintf(stderr
, "invalid filter string.\n");
464 query
= db
->query(db
,
465 "SELECT name, addresses.address, identities.type, "
466 "identities.data, leases.acquired, leases.released, timeout "
467 "FROM leases JOIN addresses ON leases.address = addresses.id "
468 "JOIN pools ON addresses.pool = pools.id "
469 "JOIN identities ON leases.identity = identities.id "
470 "WHERE (? OR name = ?) "
471 "AND (? OR (identities.type = ? AND identities.data = ?)) "
472 "AND (? OR addresses.address = ?) "
473 "AND (? OR (? >= leases.acquired AND (? <= leases.released))) "
474 "AND (? OR leases.released > ? - timeout) "
475 "AND (? OR leases.released < ? - timeout) "
478 "SELECT name, address, identities.type, identities.data, "
479 "acquired, released, timeout FROM addresses "
480 "JOIN pools ON addresses.pool = pools.id "
481 "JOIN identities ON addresses.identity = identities.id "
482 "WHERE ? AND released = 0 "
483 "AND (? OR name = ?) "
484 "AND (? OR (identities.type = ? AND identities.data = ?)) "
485 "AND (? OR address = ?)",
486 DB_INT
, pool
== NULL
, DB_TEXT
, pool
,
488 DB_INT
, id ? id
->get_type(id
) : 0,
489 DB_BLOB
, id ? id
->get_encoding(id
) : chunk_empty
,
490 DB_INT
, addr
== NULL
,
491 DB_BLOB
, addr ? addr
->get_address(addr
) : chunk_empty
,
492 DB_INT
, tstamp
== 0, DB_UINT
, tstamp
, DB_UINT
, tstamp
,
493 DB_INT
, !valid
, DB_INT
, time(NULL
),
494 DB_INT
, !expired
, DB_INT
, time(NULL
),
497 DB_INT
, !(valid
|| expired
),
498 DB_INT
, pool
== NULL
, DB_TEXT
, pool
,
500 DB_INT
, id ? id
->get_type(id
) : 0,
501 DB_BLOB
, id ? id
->get_encoding(id
) : chunk_empty
,
502 DB_INT
, addr
== NULL
,
503 DB_BLOB
, addr ? addr
->get_address(addr
) : chunk_empty
,
505 DB_TEXT
, DB_BLOB
, DB_INT
, DB_BLOB
, DB_UINT
, DB_UINT
, DB_UINT
);
506 /* id and addr leak but we can't destroy them until query is destroyed. */
511 * ipsec pool --leases - show lease information of a pool
513 static void leases(char *filter
, bool utc
)
516 chunk_t address_chunk
, identity_chunk
;
519 u_int acquired
, released
, timeout
;
521 identification_t
*identity
;
524 query
= create_lease_query(filter
);
527 fprintf(stderr
, "querying leases failed.\n");
530 while (query
->enumerate(query
, &name
, &address_chunk
, &identity_type
,
531 &identity_chunk
, &acquired
, &released
, &timeout
))
535 int len
= utc ?
25 : 21;
538 printf("%-8s %-15s %-7s %-*s %-*s %s\n",
539 "name", "address", "status", len
, "start", len
, "end", "identity");
541 address
= host_create_from_blob(address_chunk
);
542 identity
= identification_create_from_encoding(identity_type
, identity_chunk
);
544 printf("%-8s %-15H ", name
, address
);
547 printf("%-7s ", "online");
549 else if (timeout
== 0)
551 printf("%-7s ", "static");
553 else if (released
>= time(NULL
) - timeout
)
555 printf("%-7s ", "valid");
559 printf("%-7s ", "expired");
562 printf(" %#T ", &acquired
, utc
);
565 printf("%#T ", &released
, utc
);
575 printf("%D\n", identity
);
577 identity
->destroy(identity
);
579 query
->destroy(query
);
582 fprintf(stderr
, "no matching leases found.\n");
589 * ipsec pool --purge - delete expired leases
591 static void purge(char *name
)
595 purged
= db
->execute(db
, NULL
,
596 "DELETE FROM leases WHERE address IN ("
597 " SELECT id FROM addresses WHERE pool IN ("
598 " SELECT id FROM pools WHERE name = ?))",
602 fprintf(stderr
, "purging pool '%s' failed.\n", name
);
605 fprintf(stderr
, "purged %d leases in pool '%s'.\n", purged
, name
);
610 * atexit handler to close db on shutdown
612 static void cleanup(void)
620 * Logging hook for library logs, using stderr output
622 static void dbg_stderr(int level
, char *fmt
, ...)
629 vfprintf(stderr
, fmt
, args
);
630 fprintf(stderr
, "\n");
635 int main(int argc
, char *argv
[])
637 char *uri
, *name
= "", *filter
= "";
648 } operation
= OP_USAGE
;
651 library_init(STRONGSWAN_CONF
);
652 atexit(library_deinit
);
653 lib
->plugins
->load(lib
->plugins
, IPSEC_PLUGINDIR
,
654 lib
->settings
->get_str(lib
->settings
, "pool.load", PLUGINS
));
656 uri
= lib
->settings
->get_str(lib
->settings
, "charon.plugins.sql.database", NULL
);
659 fprintf(stderr
, "database URI charon.plugins.sql.database not set.\n");
662 db
= lib
->db
->create(lib
->db
, uri
);
665 fprintf(stderr
, "opening database failed.\n");
674 struct option long_opts
[] = {
675 { "help", no_argument
, NULL
, 'h' },
677 { "utc", no_argument
, NULL
, 'u' },
678 { "status", no_argument
, NULL
, 'w' },
679 { "add", required_argument
, NULL
, 'a' },
680 { "del", required_argument
, NULL
, 'd' },
681 { "resize", required_argument
, NULL
, 'r' },
682 { "leases", no_argument
, NULL
, 'l' },
683 { "purge", required_argument
, NULL
, 'p' },
685 { "start", required_argument
, NULL
, 's' },
686 { "end", required_argument
, NULL
, 'e' },
687 { "timeout", required_argument
, NULL
, 't' },
688 { "filter", required_argument
, NULL
, 'f' },
692 c
= getopt_long(argc
, argv
, "", long_opts
, NULL
);
700 operation
= OP_STATUS
;
714 operation
= OP_RESIZE
;
718 operation
= OP_LEASES
;
721 operation
= OP_PURGE
;
725 start
= host_create_from_string(optarg
, 0);
728 fprintf(stderr
, "invalid start address: '%s'.\n", optarg
);
729 operation
= OP_USAGE
;
734 end
= host_create_from_string(optarg
, 0);
737 fprintf(stderr
, "invalid end address: '%s'.\n", optarg
);
738 operation
= OP_USAGE
;
743 timeout
= atoi(optarg
);
744 if (timeout
== 0 && strcmp(optarg
, "0") != 0)
746 fprintf(stderr
, "invalid timeout '%s'.\n", optarg
);
747 operation
= OP_USAGE
;
755 operation
= OP_USAGE
;
770 if (start
== NULL
|| end
== NULL
)
772 fprintf(stderr
, "missing arguments.\n");
775 add(name
, start
, end
, timeout
);
783 fprintf(stderr
, "missing arguments.\n");