diff --git a/libs/libks/Makefile.am b/libs/libks/Makefile.am
index 44abb688e3..f49cb238d2 100644
--- a/libs/libks/Makefile.am
+++ b/libs/libks/Makefile.am
@@ -18,6 +18,7 @@ libks_la_SOURCES += src/dht/ks_dht_job.c src/dht/ks_dht_search.c src/dht/ks_dht_
 libks_la_SOURCES += src/dht/ks_dht_bucket.c
 libks_la_SOURCES += crypt/aeskey.c crypt/aestab.c crypt/sha2.c crypt/twofish.c crypt/aes_modes.c crypt/aescrypt.c crypt/twofish_cfb.c 
 #aes.h aescpp.h brg_endian.h aesopt.h aestab.h brg_types.h sha2.h twofish.h
+libks_la_SOURCES += src/ks_acl.c
 
 libks_la_CFLAGS	  = $(AM_CFLAGS)
 libks_la_CPPFLAGS = -DPOSIX
@@ -30,7 +31,7 @@ library_include_HEADERS += src/include/ks_pool.h src/include/simclist.h src/incl
 library_include_HEADERS += src/include/ks_dso.h src/include/ks_platform.h src/include/ks_types.h # src/include/ks_rng.h src/include/ks_dht.h
 library_include_HEADERS += src/include/ks_printf.h src/include/ks_hash.h src/include/ks_ssl.h src/include/kws.h
 library_include_HEADERS += src/utp/utp_internal.h src/utp/utp.h src/utp/utp_types.h src/utp/utp_callbacks.h src/utp/utp_templates.h
-library_include_HEADERS += src/utp/utp_hash.h src/utp/utp_packedsockaddr.h src/utp/utp_utils.h src/include/ks_utp.h
+library_include_HEADERS += src/utp/utp_hash.h src/utp/utp_packedsockaddr.h src/utp/utp_utils.h src/include/ks_utp.h src/include/ks_acl.h
 
 tests: libks.la
 	$(MAKE) -C test tests
diff --git a/libs/libks/src/include/ks.h b/libs/libks/src/include/ks.h
index 382780d407..b19f62e463 100644
--- a/libs/libks/src/include/ks.h
+++ b/libs/libks/src/include/ks.h
@@ -98,7 +98,11 @@ KS_DECLARE(int) ks_toupper(int c);
 KS_DECLARE(int) ks_tolower(int c);
 KS_DECLARE(char *) ks_copy_string(char *from_str, const char *to_str, ks_size_t from_str_len);
 KS_DECLARE(int) ks_snprintf(char *buffer, size_t count, const char *fmt, ...);
-KS_DECLARE(unsigned int) ks_separate_string(char *buf, const char *delim, char **array, unsigned int arraylen);
+KS_DECLARE(unsigned int) ks_separate_string_string(char *buf, const char *delim, char **array, unsigned int arraylen);
+KS_DECLARE(unsigned int) ks_separate_string(char *buf, char delim, char **array, unsigned int arraylen);
+
+#define ks_inet_pton inet_pton
+
 KS_DECLARE(int) ks_cpu_count(void);
 	static __inline__ int ks_safe_strcasecmp(const char *s1, const char *s2) {
 		if (!(s1 && s2)) {
@@ -111,6 +115,8 @@ KS_DECLARE(int) ks_cpu_count(void);
 
 KS_DECLARE(void) ks_random_string(char *buf, uint16_t len, char *set);
 
+#define ks_str_nil(s) (s ? s : "")
+
 #include "ks_pool.h"
 #include "ks_printf.h"
 #include "ks_json.h"
@@ -130,6 +136,7 @@ KS_DECLARE(void) ks_random_string(char *buf, uint16_t len, char *set);
 #include "kws.h"
 #include "ks_bencode.h"
 #include "ks_rng.h"
+#include "ks_acl.h"
 
 KS_END_EXTERN_C
 
diff --git a/libs/libks/src/include/ks_acl.h b/libs/libks/src/include/ks_acl.h
new file mode 100644
index 0000000000..c75612f77c
--- /dev/null
+++ b/libs/libks/src/include/ks_acl.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2007-2014, Anthony Minessale II
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef _KS_ACL_H_
+#define _KS_ACL_H_
+
+KS_BEGIN_EXTERN_C
+
+typedef union{
+	uint32_t v4;
+	struct in6_addr v6;
+} ks_ip_t;
+
+
+#define switch_network_list_validate_ip(_list, _ip) switch_network_list_validate_ip_token(_list, _ip, NULL);
+
+#define switch_test_subnet(_ip, _net, _mask) (_mask ? ((_net & _mask) == (_ip & _mask)) : _net ? _net == _ip : 1)
+
+
+KS_DECLARE(int) ks_parse_cidr(const char *string, ks_ip_t *ip, ks_ip_t *mask, uint32_t *bitp);
+KS_DECLARE(ks_status_t) ks_network_list_create(ks_network_list_t **list, const char *name, ks_bool_t default_type,
+														   ks_pool_t *pool);
+KS_DECLARE(ks_status_t) ks_network_list_add_cidr_token(ks_network_list_t *list, const char *cidr_str, ks_bool_t ok, const char *token);
+#define ks_network_list_add_cidr(_list, _cidr_str, _ok) ks_network_list_add_cidr_token(_list, _cidr_str, _ok, NULL)
+
+KS_DECLARE(char *) ks_network_ipv4_mapped_ipv6_addr(const char* ip_str);
+KS_DECLARE(ks_status_t) ks_network_list_add_host_mask(ks_network_list_t *list, const char *host, const char *mask_str, ks_bool_t ok);
+KS_DECLARE(ks_bool_t) ks_network_list_validate_ip_token(ks_network_list_t *list, uint32_t ip, const char **token);
+KS_DECLARE(ks_bool_t) ks_network_list_validate_ip6_token(ks_network_list_t *list, ks_ip_t ip, const char **token);
+
+#define ks_network_list_validate_ip(_list, _ip) ks_network_list_validate_ip_token(_list, _ip, NULL);
+
+#define ks_test_subnet(_ip, _net, _mask) (_mask ? ((_net & _mask) == (_ip & _mask)) : _net ? _net == _ip : 1)
+
+KS_DECLARE(ks_bool_t) ks_check_network_list_ip_cidr(const char *ip_str, const char *cidr_str);
+KS_DECLARE(ks_bool_t) ks_check_network_list_ip_token(const char *ip_str, ks_network_list_t *list, const char **token);
+#define ks_check_network_list_ip(_i, _l) ks_check_network_list_ip_token(_i, _l, NULL)
+
+KS_END_EXTERN_C
+#endif							/* defined(_KS_ACL_H_) */
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
+ */
+
diff --git a/libs/libks/src/include/ks_pool.h b/libs/libks/src/include/ks_pool.h
index 57dc5a725c..9d0ff0fadf 100644
--- a/libs/libks/src/include/ks_pool.h
+++ b/libs/libks/src/include/ks_pool.h
@@ -462,6 +462,7 @@ KS_DECLARE(char *) ks_pstrndup(ks_pool_t *pool, const char *str, size_t len);
 KS_DECLARE(char *) ks_pstrmemdup(ks_pool_t *pool, const char *str, size_t len);
 KS_DECLARE(void *) ks_pmemdup(ks_pool_t *pool, const void *buf, size_t len);
 KS_DECLARE(char *) ks_pstrcat(ks_pool_t *pool, ...);
+KS_DECLARE(char *) ks_psprintf(ks_pool_t *pool, const char *fmt, ...);
 
 KS_END_EXTERN_C
 
diff --git a/libs/libks/src/include/ks_types.h b/libs/libks/src/include/ks_types.h
index 8c088bdd40..9e1cba61c0 100644
--- a/libs/libks/src/include/ks_types.h
+++ b/libs/libks/src/include/ks_types.h
@@ -212,6 +212,10 @@ typedef void (*ks_flush_fn_t)(ks_q_t *q, void *ptr, void *flush_data);
 
 typedef struct ks_thread_pool_s ks_thread_pool_t;
 
+struct ks_network_list;
+typedef struct ks_network_list ks_network_list_t;
+
+
 KS_END_EXTERN_C
 
 #endif							/* defined(_KS_TYPES_H_) */
diff --git a/libs/libks/src/ks_acl.c b/libs/libks/src/ks_acl.c
new file mode 100644
index 0000000000..2dadec3939
--- /dev/null
+++ b/libs/libks/src/ks_acl.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2007-2014, Anthony Minessale II
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ks.h>
+
+
+struct ks_network_node {
+	ks_ip_t ip;
+	ks_ip_t mask;
+	uint32_t bits;
+	int family;
+	ks_bool_t ok;
+	char *token;
+	char *str;
+	struct ks_network_node *next;
+};
+typedef struct ks_network_node ks_network_node_t;
+
+struct ks_network_list {
+	struct ks_network_node *node_head;
+	ks_bool_t default_type;
+	ks_pool_t *pool;
+	char *name;
+};
+
+
+KS_DECLARE(ks_status_t) ks_network_list_create(ks_network_list_t **list, const char *name, ks_bool_t default_type,
+														   ks_pool_t *pool)
+{
+	ks_network_list_t *new_list;
+
+	if (!pool) {
+		ks_pool_open(&pool);
+	}
+
+	new_list = ks_pool_alloc(pool, sizeof(**list));
+	new_list->pool = pool;
+	new_list->default_type = default_type;
+	new_list->name = ks_pstrdup(new_list->pool, name);
+
+	*list = new_list;
+
+	return KS_STATUS_SUCCESS;
+}
+
+#define IN6_AND_MASK(result, ip, mask) \
+	((uint32_t *) (result))[0] =((const uint32_t *) (ip))[0] & ((const uint32_t *)(mask))[0]; \
+	((uint32_t *) (result))[1] =((const uint32_t *) (ip))[1] & ((const uint32_t *)(mask))[1]; \
+	((uint32_t *) (result))[2] =((const uint32_t *) (ip))[2] & ((const uint32_t *)(mask))[2]; \
+	((uint32_t *) (result))[3] =((const uint32_t *) (ip))[3] & ((const uint32_t *)(mask))[3];
+KS_DECLARE(ks_bool_t) ks_testv6_subnet(ks_ip_t _ip, ks_ip_t _net, ks_ip_t _mask) {
+		if (!IN6_IS_ADDR_UNSPECIFIED(&_mask.v6)) {
+			struct in6_addr a, b;
+			IN6_AND_MASK(&a, &_net, &_mask);
+			IN6_AND_MASK(&b, &_ip, &_mask);
+			return !memcmp(&a,&b, sizeof(struct in6_addr));
+		} else {
+			if (!IN6_IS_ADDR_UNSPECIFIED(&_net.v6)) {
+				return !memcmp(&_net,&_ip,sizeof(struct in6_addr));
+			}
+			else return KS_TRUE;
+		}
+}
+KS_DECLARE(ks_bool_t) ks_network_list_validate_ip6_token(ks_network_list_t *list, ks_ip_t ip, const char **token)
+{
+	ks_network_node_t *node;
+	ks_bool_t ok = list->default_type;
+	uint32_t bits = 0;
+
+	for (node = list->node_head; node; node = node->next) {
+		if (node->family == AF_INET) continue;
+
+		if (node->bits >= bits && ks_testv6_subnet(ip, node->ip, node->mask)) {
+			if (node->ok) {
+				ok = KS_TRUE;
+			} else {
+				ok = KS_FALSE;
+			}
+
+			bits = node->bits;
+
+			if (token) {
+				*token = node->token;
+			}
+		}
+	}
+
+	return ok;
+}
+
+KS_DECLARE(ks_bool_t) ks_network_list_validate_ip_token(ks_network_list_t *list, uint32_t ip, const char **token)
+{
+	ks_network_node_t *node;
+	ks_bool_t ok = list->default_type;
+	uint32_t bits = 0;
+
+	for (node = list->node_head; node; node = node->next) {
+		if (node->family == AF_INET6) continue; /* want AF_INET */
+		if (node->bits >= bits && ks_test_subnet(ip, node->ip.v4, node->mask.v4)) {
+			if (node->ok) {
+				ok = KS_TRUE;
+			} else {
+				ok = KS_FALSE;
+			}
+
+			bits = node->bits;
+
+			if (token) {
+				*token = node->token;
+			}
+		}
+	}
+
+	return ok;
+}
+
+KS_DECLARE(char *) ks_network_ipv4_mapped_ipv6_addr(const char* ip_str)
+{
+	/* ipv4 mapped ipv6 address */
+
+	if (strncasecmp(ip_str, "::ffff:", 7)) {
+		return NULL;
+	}
+
+	return strdup(ip_str + 7);
+}
+
+
+KS_DECLARE(int) ks_parse_cidr(const char *string, ks_ip_t *ip, ks_ip_t *mask, uint32_t *bitp)
+{
+	char host[128];
+	char *bit_str;
+	int32_t bits;
+	const char *ipv6;
+	ks_ip_t *maskv = mask;
+	ks_ip_t *ipv = ip;
+
+	ks_copy_string(host, string, sizeof(host)-1);
+	bit_str = strchr(host, '/');
+
+	if (!bit_str) {
+		return -1;
+	}
+
+	*bit_str++ = '\0';
+	bits = atoi(bit_str);
+	ipv6 = strchr(string, ':');
+	if (ipv6) {
+		int i,n;
+		if (bits < 0 || bits > 128) {
+			return -2;
+		}
+		bits = atoi(bit_str);
+		ks_inet_pton(AF_INET6, host, (unsigned char *)ip);
+		for (n=bits,i=0 ;i < 16; i++){
+			if (n >= 8) {
+				maskv->v6.s6_addr[i] = 0xFF;
+				n -= 8;
+			} else if (n < 8) {
+				maskv->v6.s6_addr[i] = 0xFF & ~(0xFF >> n);
+				n -= n;
+			} else if (n == 0) {
+				maskv->v6.s6_addr[i] = 0x00;
+			}
+		}
+	} else {
+		if (bits < 0 || bits > 32) {
+			return -2;
+		}
+
+		bits = atoi(bit_str);
+		ks_inet_pton(AF_INET, host, (unsigned char *)ip);
+		ipv->v4 = htonl(ipv->v4);
+
+		maskv->v4 = 0xFFFFFFFF & ~(0xFFFFFFFF >> bits);
+	}
+	*bitp = bits;
+
+	return 0;
+}
+
+
+
+
+KS_DECLARE(ks_status_t) ks_network_list_perform_add_cidr_token(ks_network_list_t *list, const char *cidr_str, ks_bool_t ok,
+																		   const char *token)
+{
+	ks_ip_t ip, mask;
+	uint32_t bits;
+	ks_network_node_t *node;
+	char *ipv4 = NULL;
+
+	if ((ipv4 = ks_network_ipv4_mapped_ipv6_addr(cidr_str))) {
+		cidr_str = ipv4;
+	}
+
+	if (ks_parse_cidr(cidr_str, &ip, &mask, &bits)) {
+		ks_log(KS_LOG_ERROR, "Error Adding %s (%s) [%s] to list %s\n",
+						  cidr_str, ok ? "allow" : "deny", ks_str_nil(token), list->name);
+		ks_safe_free(ipv4);
+		return KS_STATUS_GENERR;
+	}
+
+	node = ks_pool_alloc(list->pool, sizeof(*node));
+
+	node->ip = ip;
+	node->mask = mask;
+	node->ok = ok;
+	node->bits = bits;
+	node->str = ks_pstrdup(list->pool, cidr_str);
+
+	if (strchr(cidr_str,':')) {
+		node->family = AF_INET6;
+	} else {
+		node->family = AF_INET;
+	}
+
+	if (!zstr(token)) {
+		node->token = ks_pstrdup(list->pool, token);
+	}
+
+	node->next = list->node_head;
+	list->node_head = node;
+
+	ks_log(KS_LOG_NOTICE, "Adding %s (%s) [%s] to list %s\n",
+					  cidr_str, ok ? "allow" : "deny", ks_str_nil(token), list->name);
+
+	ks_safe_free(ipv4);
+	return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(ks_status_t) ks_network_list_add_cidr_token(ks_network_list_t *list, const char *cidr_str, ks_bool_t ok, const char *token)
+{
+	char *cidr_str_dup = NULL;
+	ks_status_t status = KS_STATUS_SUCCESS;
+
+	if (strchr(cidr_str, ',')) {
+		char *argv[32] = { 0 };
+		int i, argc;
+		cidr_str_dup = strdup(cidr_str);
+
+		ks_assert(cidr_str_dup);
+		if ((argc = ks_separate_string(cidr_str_dup, ',', argv, (sizeof(argv) / sizeof(argv[0]))))) {
+			for (i = 0; i < argc; i++) {
+				ks_status_t this_status;
+				if ((this_status = ks_network_list_perform_add_cidr_token(list, argv[i], ok, token)) != KS_STATUS_SUCCESS) {
+					status = this_status;
+				}
+			}
+		}
+	} else {
+		status = ks_network_list_perform_add_cidr_token(list, cidr_str, ok, token);
+	}
+
+	ks_safe_free(cidr_str_dup);
+	return status;
+}
+
+KS_DECLARE(ks_status_t) ks_network_list_add_host_mask(ks_network_list_t *list, const char *host, const char *mask_str, ks_bool_t ok)
+{
+	ks_ip_t ip, mask;
+	ks_network_node_t *node;
+
+	ks_inet_pton(AF_INET, host, &ip);
+	ks_inet_pton(AF_INET, mask_str, &mask);
+
+	node = ks_pool_alloc(list->pool, sizeof(*node));
+
+	node->ip.v4 = ntohl(ip.v4);
+	node->mask.v4 = ntohl(mask.v4);
+	node->ok = ok;
+
+	/* http://graphics.stanford.edu/~seander/bithacks.html */
+	mask.v4 = mask.v4 - ((mask.v4 >> 1) & 0x55555555);
+	mask.v4 = (mask.v4 & 0x33333333) + ((mask.v4 >> 2) & 0x33333333);
+	node->bits = (((mask.v4 + (mask.v4 >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
+
+	node->str = ks_psprintf(list->pool, "%s:%s", host, mask_str);
+
+	node->next = list->node_head;
+	list->node_head = node;
+
+	return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(ks_bool_t) ks_check_network_list_ip_cidr(const char *ip_str, const char *cidr_str)
+{
+	ks_ip_t  ip, mask, net;
+	uint32_t bits;
+	char *ipv6 = strchr(ip_str,':');
+	ks_bool_t ok = KS_FALSE;
+	char *ipv4 = NULL;
+
+	if ((ipv4 = ks_network_ipv4_mapped_ipv6_addr(ip_str))) {
+		ip_str = ipv4;
+		ipv6 = NULL;
+	}
+
+	if (ipv6) {
+		ks_inet_pton(AF_INET6, ip_str, &ip);
+	} else {
+		ks_inet_pton(AF_INET, ip_str, &ip);
+		ip.v4 = htonl(ip.v4);
+	}
+
+	ks_parse_cidr(cidr_str, &net, &mask, &bits);
+	
+	if (ipv6) {
+		ok = ks_testv6_subnet(ip, net, mask);
+	} else {
+		ok = ks_test_subnet(ip.v4, net.v4, mask.v4);
+	}
+
+	ks_safe_free(ipv4);
+
+	return ok;
+}
+
+KS_DECLARE(ks_bool_t) ks_check_network_list_ip_token(const char *ip_str, ks_network_list_t *list, const char **token)
+{
+	ks_ip_t ip;
+	char *ipv6 = strchr(ip_str,':');
+	ks_bool_t ok = KS_FALSE;
+	char *ipv4 = NULL;
+
+	if ((ipv4 = ks_network_ipv4_mapped_ipv6_addr(ip_str))) {
+		ip_str = ipv4;
+		ipv6 = NULL;
+	}
+
+	if (ipv6) {
+		ks_inet_pton(AF_INET6, ip_str, &ip);
+	} else {
+		ks_inet_pton(AF_INET, ip_str, &ip);
+		ip.v4 = htonl(ip.v4);
+	}
+
+	
+	if (ipv6) {
+		ok = ks_network_list_validate_ip6_token(list, ip, token);
+	} else {
+		ok = ks_network_list_validate_ip_token(list, ip.v4, token);
+	}
+
+	ks_safe_free(ipv4);
+
+	return ok;
+}
+
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
+ */
diff --git a/libs/libks/src/ks_pool.c b/libs/libks/src/ks_pool.c
index a2a9503234..8b18ea0cbb 100644
--- a/libs/libks/src/ks_pool.c
+++ b/libs/libks/src/ks_pool.c
@@ -2182,6 +2182,19 @@ KS_DECLARE(char *) ks_pstrcat(ks_pool_t *pool, ...)
     return result;
 }
 
+KS_DECLARE(char *) ks_psprintf(ks_pool_t *pool, const char *fmt, ...)
+{
+	va_list ap;
+	char *result;
+	va_start(ap, fmt);
+	result = ks_vpprintf(pool, fmt, ap);
+	va_end(ap);
+
+	return result;
+}
+
+
+
 /* For Emacs:
  * Local Variables:
  * mode:c
diff --git a/libs/libks/src/ks_string.c b/libs/libks/src/ks_string.c
index 039398f8fe..8ac3054cc6 100644
--- a/libs/libks/src/ks_string.c
+++ b/libs/libks/src/ks_string.c
@@ -33,6 +33,8 @@
 
 #include <ks.h>
 
+#define ESCAPE_META '\\'
+
 /* Written by Marc Espie, public domain */
 #define KS_CTYPE_NUM_CHARS       256
 
@@ -230,7 +232,87 @@ KS_DECLARE(int) ks_snprintf(char *buffer, size_t count, const char *fmt, ...)
 }
 
 
-KS_DECLARE(unsigned int) ks_separate_string(char *buf, const char *delim, char **array, unsigned int arraylen)
+/* Helper function used when separating strings to unescape a character. The
+   supported characters are:
+
+   \n  linefeed
+   \r  carriage return
+   \t  tab
+   \s  space
+
+   Any other character is returned as it was received. */
+static char unescape_char(char escaped)
+{
+	char unescaped;
+
+	switch (escaped) {
+	case 'n':
+		unescaped = '\n';
+		break;
+	case 'r':
+		unescaped = '\r';
+		break;
+	case 't':
+		unescaped = '\t';
+		break;
+	case 's':
+		unescaped = ' ';
+		break;
+	default:
+		unescaped = escaped;
+	}
+	return unescaped;
+}
+
+
+/* Helper function used when separating strings to remove quotes, leading /
+   trailing spaces, and to convert escaped characters. */
+static char *cleanup_separated_string(char *str, char delim)
+{
+	char *ptr;
+	char *dest;
+	char *start;
+	char *end = NULL;
+	int inside_quotes = 0;
+
+	/* Skip initial whitespace */
+	for (ptr = str; *ptr == ' '; ++ptr) {
+	}
+
+	for (start = dest = ptr; *ptr; ++ptr) {
+		char e;
+		int esc = 0;
+
+		if (*ptr == ESCAPE_META) {
+			e = *(ptr + 1);
+			if (e == '\'' || e == '"' || (delim && e == delim) || e == ESCAPE_META || (e = unescape_char(*(ptr + 1))) != *(ptr + 1)) {
+				++ptr;
+				*dest++ = e;
+				end = dest;
+				esc++;
+			}
+		}
+		if (!esc) {
+			if (*ptr == '\'' && (inside_quotes || ((ptr+1) && strchr(ptr+1, '\'')))) {
+				if ((inside_quotes = (1 - inside_quotes))) {
+					end = dest;
+				}
+			} else {
+				*dest++ = *ptr;
+				if (*ptr != ' ' || inside_quotes) {
+					end = dest;
+				}
+			}
+		}
+	}
+	if (end) {
+		*end = '\0';
+	}
+
+	return start;
+}
+
+KS_DECLARE(unsigned int) ks_separate_string_string(char *buf, const char *delim, char **array, unsigned int arraylen)
 {
 	unsigned int count = 0;
 	char *d;
@@ -273,3 +355,129 @@ KS_DECLARE(char *) ks_copy_string(char *from_str, const char *to_str, ks_size_t
 
 	return p;
 }
+
+
+/* Separate a string using a delimiter that is not a space */
+static unsigned int separate_string_char_delim(char *buf, char delim, char **array, unsigned int arraylen)
+{
+	enum tokenizer_state {
+		START,
+		FIND_DELIM
+	} state = START;
+
+	unsigned int count = 0;
+	char *ptr = buf;
+	int inside_quotes = 0;
+	unsigned int i;
+
+	while (*ptr && count < arraylen) {
+		switch (state) {
+		case START:
+			array[count++] = ptr;
+			state = FIND_DELIM;
+			break;
+
+		case FIND_DELIM:
+			/* escaped characters are copied verbatim to the destination string */
+			if (*ptr == ESCAPE_META) {
+				++ptr;
+			} else if (*ptr == '\'' && (inside_quotes || ((ptr+1) && strchr(ptr+1, '\'')))) {
+				inside_quotes = (1 - inside_quotes);
+			} else if (*ptr == delim && !inside_quotes) {
+				*ptr = '\0';
+				state = START;
+			}
+			++ptr;
+			break;
+		}
+	}
+	/* strip quotes, escaped chars and leading / trailing spaces */
+
+	for (i = 0; i < count; ++i) {
+		array[i] = cleanup_separated_string(array[i], delim);
+	}
+
+	return count;
+}
+
+/* Separate a string using a delimiter that is a space */
+static unsigned int separate_string_blank_delim(char *buf, char **array, unsigned int arraylen)
+{
+	enum tokenizer_state {
+		START,
+		SKIP_INITIAL_SPACE,
+		FIND_DELIM,
+		SKIP_ENDING_SPACE
+	} state = START;
+
+	unsigned int count = 0;
+	char *ptr = buf;
+	int inside_quotes = 0;
+	unsigned int i;
+
+	while (*ptr && count < arraylen) {
+		switch (state) {
+		case START:
+			array[count++] = ptr;
+			state = SKIP_INITIAL_SPACE;
+			break;
+
+		case SKIP_INITIAL_SPACE:
+			if (*ptr == ' ') {
+				++ptr;
+			} else {
+				state = FIND_DELIM;
+			}
+			break;
+
+		case FIND_DELIM:
+			if (*ptr == ESCAPE_META) {
+				++ptr;
+			} else if (*ptr == '\'') {
+				inside_quotes = (1 - inside_quotes);
+			} else if (*ptr == ' ' && !inside_quotes) {
+				*ptr = '\0';
+				state = SKIP_ENDING_SPACE;
+			}
+			++ptr;
+			break;
+
+		case SKIP_ENDING_SPACE:
+			if (*ptr == ' ') {
+				++ptr;
+			} else {
+				state = START;
+			}
+			break;
+		}
+	}
+	/* strip quotes, escaped chars and leading / trailing spaces */
+
+	for (i = 0; i < count; ++i) {
+		array[i] = cleanup_separated_string(array[i], 0);
+	}
+	
+	return count;
+}
+
+KS_DECLARE(unsigned int) ks_separate_string(char *buf, char delim, char **array, unsigned int arraylen)
+{
+	if (!buf || !array || !arraylen) {
+		return 0;
+	}
+
+
+	if (*buf == '^' && *(buf+1) == '^') {
+		char *p = buf + 2;
+		
+		if (p && *p && *(p+1)) {
+			buf = p;
+			delim = *buf++;
+		}
+	}
+
+
+	memset(array, 0, arraylen * sizeof(*array));
+
+	return (delim == ' ' ? separate_string_blank_delim(buf, array, arraylen) : separate_string_char_delim(buf, delim, array, arraylen));
+}
diff --git a/libs/libks/test/Makefile.am b/libs/libks/test/Makefile.am
index bd8ce48325..a974af0062 100644
--- a/libs/libks/test/Makefile.am
+++ b/libs/libks/test/Makefile.am
@@ -14,6 +14,11 @@ testpools_SOURCES = testpools.c tap.c
 testpools_CFLAGS = $(AM_CFLAGS)
 testpools_LDADD = $(TEST_LDADD)
 
+check_PROGRAMS += testacl
+testacl_SOURCES = testacl.c tap.c
+testacl_CFLAGS = $(AM_CFLAGS)
+testacl_LDADD = $(TEST_LDADD)
+
 check_PROGRAMS += test_thread_pools
 test_thread_pools_SOURCES = test_thread_pools.c tap.c
 test_thread_pools_CFLAGS = $(AM_CFLAGS)
diff --git a/libs/libks/test/testacl.c b/libs/libks/test/testacl.c
new file mode 100644
index 0000000000..090b62f0c6
--- /dev/null
+++ b/libs/libks/test/testacl.c
@@ -0,0 +1,70 @@
+#include "ks.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "tap.h"
+
+int main(int argc, char **argv)
+{
+	ks_pool_t *pool;
+	ks_network_list_t *list = NULL;
+	ks_bool_t match;
+
+	ks_init();
+
+	plan(8);
+
+	ks_pool_open(&pool);
+
+	ks_network_list_create(&list, "test", KS_FALSE, pool);
+
+	
+	ks_network_list_add_cidr(list, "10.0.0.0/8", KS_TRUE);
+	ks_network_list_add_cidr(list, "172.16.0.0/12", KS_TRUE);
+	ks_network_list_add_cidr(list, "192.168.0.0/16", KS_TRUE);
+	ks_network_list_add_cidr(list, "fe80::/10", KS_TRUE);
+
+
+	match = ks_check_network_list_ip("192.168.1.1", list);
+	ok(match);
+
+	match = ks_check_network_list_ip("208.34.128.7", list);
+	ok (!match);
+
+	match = ks_check_network_list_ip_cidr("192.168.1.1", "192.168.0.0/16");
+	ok(match);
+
+	match = ks_check_network_list_ip_cidr("208.34.128.7", "192.168.0.0/16");
+	ok (!match);
+
+
+	ks_pool_free(pool, &list);
+
+
+	ks_network_list_create(&list, "test", KS_TRUE, pool);
+
+	ks_network_list_add_cidr(list, "0.0.0.0/0", KS_FALSE);
+	ks_network_list_add_cidr(list, "fe80::/10", KS_FALSE);
+
+	
+	match = ks_check_network_list_ip("2637:f368:1281::10", list);
+	ok(match);
+
+	match = ks_check_network_list_ip("fe80::18b7:53b3:51d8:f5cf", list);
+	ok(!match);
+
+	match = ks_check_network_list_ip_cidr("fe80::18b7:53b3:51d8:f5cf", "fe80::/10");
+	ok(match);
+
+	match = ks_check_network_list_ip_cidr("2637:f368:1281::10", "fe80::/10");
+	ok(!match);
+
+	ks_pool_free(pool, &list);
+
+	ks_pool_close(&pool);
+
+	ks_shutdown();
+
+	done_testing();
+}