File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / pool / pool.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 09:46:45 2020 UTC (4 years, 2 months ago) by misho
Branches: strongswan, MAIN
CVS tags: v5_9_2p0, v5_8_4p7, HEAD
Strongswan

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>