This is a new and improved patch to add dynamic address allocation support
to iked. Includes an update to the man page, and a simpler implementation
then my previous work.

- An address pool is globally defined as "pool <name> <cidr block>", and
referenced from a policy with "pool <name>". (If this patch finally gets
traction, I'd like to make setting up a typical roadwarrior vpn is even
simpler by adding a feature that creates a pool mirroring the policy's
first flow's "to" range.)
- A request for a specific address that is available in the pool will be
honoured (ie Win7 static IP)
- There is a hard limit of 65536 addresses (8kb) per pool, which should be
plenty, and makes the code simpler.
- Configuration changes to live address pools work. Allocated addresses
stay allocated so long as they fall inside the updated range, but not if
they become in range of some other pool.
- "ikectl reset all" will drop all pools. If there is a usecase for it, I
can add a "reset pool" to just drop all pools.

Testing has thus far been limited to Windows 7.

--Ryan Slack
Index: config.c
===================================================================
RCS file: /cvs/src/sbin/iked/config.c,v
retrieving revision 1.22
diff -u -p -r1.22 config.c
--- config.c	28 Nov 2013 20:28:34 -0000	1.22
+++ config.c	30 Apr 2014 02:57:33 -0000
@@ -22,6 +22,8 @@
 #include <sys/socket.h>
 #include <sys/uio.h>
 
+#include <netinet/in.h>
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -89,6 +91,10 @@ config_free_sa(struct iked *env, struct 
 	if (sa->sa_policy) {
 		(void)RB_REMOVE(iked_sapeers, &sa->sa_policy->pol_sapeers, sa);
 		policy_unref(env, sa->sa_policy);
+
+		if(sa->sa_policy->pol_addr_pool[0] != '\0')
+		addr_pool_release(env,sa->sa_policy->pol_addr_pool,
+		    &sa->sa_cfg_peer.cfg.address.addr);
 	}
 
 	ikev2_msg_flushqueue(env, &sa->sa_requests);
@@ -376,6 +382,87 @@ config_new_user(struct iked *env, struct
 	return (usr);
 }
 
+struct iked_addr_pool* 
+config_new_addr_pool(struct iked *env, struct iked_addr_pool *new)
+{
+	struct iked_addr_pool	*pool,*old;
+	struct iked_sa		*sa;
+	int			 i;
+	struct sockaddr_storage	*ss_addr;
+	struct in_addr		*a4;
+	struct in6_addr		*a6;
+
+	if (!(new->pool_size && new->pool_size < IKED_ADDR_POOL_SIZE_MAX
+	    && new->pool_name[0] && new->pool_af)) {
+		log_warnx("%s: required values missing", __func__);
+		return (NULL);
+	}
+
+	if ((pool = calloc(1, sizeof(*pool))) == NULL)
+		return (NULL);
+
+	memcpy(pool, new, sizeof(*pool));
+
+	if ((pool->pool_used = bit_alloc(pool->pool_size)) == NULL)
+		goto fail;
+	bit_set(pool->pool_used, 0);
+
+	if ((old = RB_INSERT(iked_addr_pools, &env->sc_pools, pool)) != NULL) {
+		log_debug("%s: updating address pool '%s'", __func__,
+		    pool->pool_name);
+
+		if(old->pool_size) free(old->pool_used);
+		free(old);
+
+	} else {
+		log_debug("%s: inserting new address pool '%s'", __func__,
+		    pool->pool_name);
+	}
+
+	RB_FOREACH(sa, iked_sas, &env->sc_sas) {
+		ss_addr = &sa->sa_cfg_peer.cfg.address.addr;
+		if (ss_addr->ss_family != pool->pool_af || 
+		    sa->sa_cfg_peer.cfg_action != IKEV2_CP_REPLY ||
+		    strcmp(sa->sa_policy->pol_addr_pool, pool->pool_name))
+			continue;
+
+		switch (pool->pool_af) {
+		case AF_INET:
+			a4 = &((struct sockaddr_in *)ss_addr)->sin_addr;
+			i = betoh32(a4->s_addr) - betoh32(pool->pool_start[3]);
+			break;
+		case AF_INET6:
+			a6 = &((struct sockaddr_in6*)ss_addr)->sin6_addr;
+			if(memcmp(a6->s6_addr, pool->pool_start, 12)) i = -1;
+			else i = betoh32(a6->s6_addr[3]) - 
+				 betoh32(pool->pool_start[3]);
+			break;
+		default:
+			log_warnx("%s: invalid address family", __func__);
+			goto fail;
+		}
+
+		if (0 <= i && (uint32_t)i < pool->pool_size) {
+			bit_set(pool->pool_used, i);
+		}
+	}
+
+	return pool;
+   fail:
+	if(pool->pool_size) free(pool->pool_used);
+	free(pool);
+	return (NULL);
+}
+
+int
+config_free_addr_pool(struct iked *env, struct iked_addr_pool* pool)
+{
+	RB_REMOVE(iked_addr_pools, &env->sc_pools, pool);
+	if(pool->pool_size) free(pool->pool_used);
+	free(pool);
+	return (0);
+}
+
 /*
  * Inter-process communication of configuration items.
  */
@@ -442,6 +529,7 @@ config_getreset(struct iked *env, struct
 	struct iked_policy	*pol, *nextpol;
 	struct iked_sa		*sa, *nextsa;
 	struct iked_user	*usr, *nextusr;
+	struct iked_addr_pool	*pool, *nextpool;
 	u_int			 mode;
 
 	IMSG_SIZE_CHECK(imsg, &mode);
@@ -475,6 +563,16 @@ config_getreset(struct iked *env, struct
 		}
 	}
 
+	if (mode == RESET_ALL || mode == RESET_ADDR_POOL) {
+		log_debug("%s: flushing address pools", __func__);
+		for (pool = RB_MIN(iked_addr_pools, &env->sc_pools);
+		    pool != NULL; pool = nextpool) {
+			nextpool = RB_NEXT(iked_addr_pools, &env->sc_pools,
+			    pool);
+			config_free_addr_pool(env, pool);
+		}
+	}
+
 	return (0);
 }
 
@@ -577,6 +675,35 @@ config_getuser(struct iked *env, struct 
 		return (-1);
 
 	print_user(&usr);
+
+	return (0);
+}
+
+int
+config_setaddrpool(struct iked *env, struct iked_addr_pool *pool, 
+    enum privsep_procid id)
+{
+	if (env->sc_opts & IKED_OPT_NOACTION) {
+		print_addr_pool(pool);
+		return (0);
+	}
+
+	proc_compose_imsg(env, id, IMSG_CFG_ADDR_POOL, -1, pool,sizeof(*pool));
+	return (0);
+}
+
+int
+config_getaddrpool(struct iked *env, struct imsg *imsg)
+{
+	struct iked_addr_pool	*pool;
+
+	IMSG_SIZE_CHECK(imsg, pool);
+	pool = imsg->data;
+
+	if (config_new_addr_pool(env, pool) == NULL)
+		return (-1);
+
+	print_addr_pool(pool);
 
 	return (0);
 }
Index: iked.conf.5
===================================================================
RCS file: /cvs/src/sbin/iked/iked.conf.5,v
retrieving revision 1.26
diff -u -p -r1.26 iked.conf.5
--- iked.conf.5	1 Nov 2013 10:42:38 -0000	1.26
+++ iked.conf.5	30 Apr 2014 02:57:33 -0000
@@ -153,6 +153,8 @@ arguments.
 Note that the password has to be specified in plain text which is
 required to support different challenge-based EAP methods like
 EAP-MD5 or EAP-MSCHAPv2.
+.It Ic pool Ar name Ar address-mask
+Define a pool of addresses available for dynamic allocation.
 .El
 .Sh AUTOMATIC KEYING POLICIES
 This section is used to configure policies that will be used by
@@ -473,6 +475,8 @@ or hex value (starting with 0x) for auth
 Use RSA public key authentication.
 This is the default mode if no option is specified.
 .El
+.It Ic pool Ar name
+Specifies the address pool to use for dynamic allocation.
 .It Ic tag Ar string
 Add a
 .Xr pf 4
Index: iked.h
===================================================================
RCS file: /cvs/src/sbin/iked/iked.h,v
retrieving revision 1.60
diff -u -p -r1.60 iked.h
--- iked.h	28 Nov 2013 20:26:04 -0000	1.60
+++ iked.h	30 Apr 2014 02:57:33 -0000
@@ -20,6 +20,7 @@
 #include <sys/tree.h>
 #include <sys/queue.h>
 #include <imsg.h>
+#include <bitstring.h>
 
 #include "types.h"
 #include "dh.h"
@@ -265,6 +266,7 @@ struct iked_policy {
 	struct iked_flows		 pol_flows;
 	size_t				 pol_nflows;
 
+	char				 pol_addr_pool[IKED_ID_SIZE];
 	struct iked_cfg			 pol_cfg[IKED_CFG_MAX];
 	u_int				 pol_ncfg;
 
@@ -359,6 +361,7 @@ struct iked_sa {
 	u_int				 sa_statevalid;	/* IKE_AUTH */
 
 	int				 sa_cp;		/* XXX */
+	struct iked_cfg			 sa_cfg_peer;
 
 	struct iked_policy		*sa_policy;
 	struct timeval			 sa_timecreated;
@@ -471,6 +474,16 @@ struct iked_user {
 };
 RB_HEAD(iked_users, iked_user);
 
+struct iked_addr_pool {
+	char			 pool_name[IKED_ID_SIZE];
+	int			 pool_af;
+	u_int32_t		 pool_start[4];
+	u_int32_t		 pool_size;
+	bitstr_t		*pool_used;
+	RB_ENTRY(iked_addr_pool) pool_entry;
+};
+RB_HEAD(iked_addr_pools, iked_addr_pool);
+
 struct privsep {
 	int				 ps_pipes[PROC_MAX][PROC_MAX];
 	struct imsgev			 ps_ievs[PROC_MAX];
@@ -520,6 +533,7 @@ struct iked {
 	struct iked_activesas		 sc_activesas;
 	struct iked_flows		 sc_activeflows;
 	struct iked_users		 sc_users;
+	struct iked_addr_pools		 sc_pools;
 
 	void				*sc_priv;	/* per-process */
 
@@ -561,6 +575,9 @@ struct iked_sa *
 	 config_new_sa(struct iked *, int);
 struct iked_user *
 	 config_new_user(struct iked *, struct iked_user *);
+struct iked_addr_pool* 
+	config_new_addr_pool(struct iked *, struct iked_addr_pool *);
+int	config_free_addr_pool(struct iked *, struct iked_addr_pool *);
 u_int64_t
 	 config_getspi(void);
 struct iked_transform *
@@ -592,6 +609,9 @@ int	 config_setpfkey(struct iked *, enum
 int	 config_getpfkey(struct iked *, struct imsg *);
 int	 config_setuser(struct iked *, struct iked_user *, enum privsep_procid);
 int	 config_getuser(struct iked *, struct imsg *);
+int	 config_setaddrpool(struct iked *, struct iked_addr_pool *,
+	    enum privsep_procid);
+int	 config_getaddrpool(struct iked *, struct imsg *);
 int	 config_setcompile(struct iked *, enum privsep_procid);
 int	 config_getcompile(struct iked *, struct imsg *);
 
@@ -623,9 +643,17 @@ struct iked_sa *
 	 sa_peer_lookup(struct iked_policy *, struct sockaddr_storage *);
 struct iked_user *
 	 user_lookup(struct iked *, const char *);
+struct iked_addr_pool *
+	 addr_pool_lookup(struct iked *, const char *);
+int	 addr_pool_request(struct iked *, const char *, 
+	    struct sockaddr_storage *);
+int	 addr_pool_release(struct iked *, const char *,
+	    struct sockaddr_storage *);
+
 RB_PROTOTYPE(iked_sas, iked_sa, sa_entry, sa_cmp);
 RB_PROTOTYPE(iked_sapeers, iked_sa, sa_peer_entry, sa_peer_cmp);
 RB_PROTOTYPE(iked_users, iked_user, user_entry, user_cmp);
+RB_PROTOTYPE(iked_addr_pools, iked_addr_pool, pool_entry, addr_pool_cmp);
 RB_PROTOTYPE(iked_activesas, iked_childsa, csa_node, childsa_cmp);
 RB_PROTOTYPE(iked_flows, iked_flow, flow_node, flow_cmp);
 
@@ -884,6 +912,7 @@ __dead void fatalx(const char *);
 /* parse.y */
 int	 parse_config(const char *, struct iked *);
 void	 print_user(struct iked_user *);
+void	 print_addr_pool(struct iked_addr_pool *);
 void	 print_policy(struct iked_policy *);
 size_t	 keylength_xf(u_int, u_int, u_int);
 size_t	 noncelength_xf(u_int, u_int);
Index: ikev2.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.c,v
retrieving revision 1.86
diff -u -p -r1.86 ikev2.c
--- ikev2.c	28 Nov 2013 20:30:41 -0000	1.86
+++ ikev2.c	30 Apr 2014 02:57:33 -0000
@@ -142,6 +142,8 @@ ikev2_dispatch_parent(int fd, struct pri
 		return (config_getpolicy(env, imsg));
 	case IMSG_CFG_USER:
 		return (config_getuser(env, imsg));
+	case IMSG_CFG_ADDR_POOL:
+		return (config_getaddrpool(env, imsg));
 	case IMSG_COMPILE:
 		return (config_getcompile(env, imsg));
 	default:
@@ -1437,7 +1439,7 @@ ikev2_add_cp(struct iked *env, struct ik
 {
 	struct iked_policy	*pol = sa->sa_policy;
 	struct ikev2_cp		*cp;
-	struct ikev2_cfg	*cfg;
+	struct ikev2_cp_attr	*attr;
 	struct iked_cfg		*ikecfg;
 	u_int			 i;
 	u_int32_t		 mask4;
@@ -1453,24 +1455,63 @@ ikev2_add_cp(struct iked *env, struct ik
 	switch (sa->sa_cp) {
 	case IKEV2_CP_REQUEST:
 		cp->cp_type = IKEV2_CP_REPLY;
+		ikecfg = &sa->sa_cfg_peer;
 		break;
 	case IKEV2_CP_REPLY:
 	case IKEV2_CP_SET:
 	case IKEV2_CP_ACK:
-		/* Not yet supported */
+		log_debug("%s: %s is not yet supported.", __func__, 
+		    print_map(sa->sa_cp,ikev2_cp_map));
+	default:
 		return (-1);
 	}
 
+	if(ikecfg->cfg_action == cp->cp_type)
+	switch (ikecfg->cfg.address.addr.ss_family) {
+	case AF_INET:
+		in4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr;
+
+		if ((attr = ibuf_advance(buf, sizeof(*attr))) == NULL)
+			return (-1);
+
+		attr->attr_type = htobe16(IKEV2_CFG_INTERNAL_IP4_ADDRESS);
+		len += sizeof(*attr);
+
+		/* 4 bytes IPv4 address */
+		attr->attr_length = htobe16(4);
+		if (ibuf_add(buf, &in4->sin_addr.s_addr, 4) == -1)
+			return (-1);
+		len += 4;
+		break;
+	case AF_INET6:
+		in6 = (struct sockaddr_in6 *)&ikecfg->cfg.address.addr;
+		
+		if ((attr = ibuf_advance(buf, sizeof(*attr))) == NULL)
+			return (-1);
+
+		attr->attr_type = htobe16(IKEV2_CFG_INTERNAL_IP6_ADDRESS);
+		len += sizeof(*attr);
+
+		/* 16 bytes IPv6 address + 1 byte prefix length */
+		attr->attr_length = htobe16(16 + 1);
+		if (ibuf_add(buf, &in6->sin6_addr.s6_addr, 16) == -1)
+			return (-1);
+		if (ibuf_add(buf, &ikecfg->cfg.address.addr_mask, 1) == -1)
+			return (-1);
+		len += 16 + 1;
+		break;
+	}
+
 	for (i = 0; i < pol->pol_ncfg; i++) {
 		ikecfg = &pol->pol_cfg[i];
 		if (ikecfg->cfg_action != cp->cp_type)
 			continue;
 
-		if ((cfg = ibuf_advance(buf, sizeof(*cfg))) == NULL)
+		if ((attr = ibuf_advance(buf, sizeof(*attr))) == NULL)
 			return (-1);
 
-		cfg->cfg_type = htobe16(ikecfg->cfg_type);
-		len += sizeof(*cfg);
+		attr->attr_type = htobe16(ikecfg->cfg_type);
+		len += sizeof(*attr);
 
 		switch (ikecfg->cfg_type) {
 		case IKEV2_CFG_INTERNAL_IP4_ADDRESS:
@@ -1481,7 +1522,7 @@ ikev2_add_cp(struct iked *env, struct ik
 		case IKEV2_CFG_INTERNAL_IP4_SERVER:
 			/* 4 bytes IPv4 address */
 			in4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr;
-			cfg->cfg_length = htobe16(4);
+			attr->attr_length = htobe16(4);
 			if (ibuf_add(buf, &in4->sin_addr.s_addr, 4) == -1)
 				return (-1);
 			len += 4;
@@ -1490,7 +1531,7 @@ ikev2_add_cp(struct iked *env, struct ik
 			/* 4 bytes IPv4 address + 4 bytes IPv4 mask + */
 			in4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr;
 			mask4 = prefixlen2mask(ikecfg->cfg.address.addr_mask);
-			cfg->cfg_length = htobe16(8);
+			attr->attr_length = htobe16(8);
 			if (ibuf_add(buf, &in4->sin_addr.s_addr, 4) == -1)
 				return (-1);
 			if (ibuf_add(buf, &mask4, 4) == -1)
@@ -1503,7 +1544,7 @@ ikev2_add_cp(struct iked *env, struct ik
 		case IKEV2_CFG_INTERNAL_IP6_SERVER:
 			/* 16 bytes IPv6 address */
 			in6 = (struct sockaddr_in6 *)&ikecfg->cfg.address;
-			cfg->cfg_length = htobe16(16);
+			attr->attr_length = htobe16(16);
 			if (ibuf_add(buf, &in6->sin6_addr.s6_addr, 16) == -1)
 				return (-1);
 			len += 16;
@@ -1512,7 +1553,7 @@ ikev2_add_cp(struct iked *env, struct ik
 		case IKEV2_CFG_INTERNAL_IP6_SUBNET:
 			/* 16 bytes IPv6 address + 1 byte prefix length */
 			in6 = (struct sockaddr_in6 *)&ikecfg->cfg.address.addr;
-			cfg->cfg_length = htobe16(17);
+			attr->attr_length = htobe16(17);
 			if (ibuf_add(buf, &in6->sin6_addr.s6_addr, 16) == -1)
 				return (-1);
 			if (ikecfg->cfg.address.addr_net)
@@ -1525,7 +1566,7 @@ ikev2_add_cp(struct iked *env, struct ik
 			break;
 		case IKEV2_CFG_APPLICATION_VERSION:
 			/* Reply with an empty string (non-NUL terminated) */
-			cfg->cfg_length = 0;
+			attr->attr_length = 0;
 			break;
 		}
 	}
@@ -2017,6 +2058,32 @@ ikev2_resp_ike_auth(struct iked *env, st
 	if (ibuf_cat(e, sa->sa_localauth.id_buf) != 0)
 		goto done;
 	len = ibuf_size(sa->sa_localauth.id_buf) + sizeof(*auth);
+
+	if (sa->sa_cp && sa->sa_policy->pol_addr_pool[0] != '\0' &&
+	    sa->sa_cfg_peer.cfg_action != IKEV2_CP_REPLY) {
+		if(addr_pool_request(env, sa->sa_policy->pol_addr_pool,
+		    &sa->sa_cfg_peer.cfg.address.addr)){
+			bzero(&sa->sa_cfg_peer,sizeof(sa->sa_cfg_peer));
+			log_debug("%s:  %s", __func__, print_map(
+			    IKEV2_N_INTERNAL_ADDRESS_FAILURE, ikev2_cp_map));
+
+			/* Notify payload */
+			if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NOTIFY)
+			    == -1)
+				goto done;
+			if ((pld = ikev2_add_payload(e)) == NULL)
+				goto done;
+
+			if ((n = ibuf_advance(e, sizeof(*n))) == NULL)
+				goto done;
+			n->n_protoid = IKEV2_SAPROTO_NONE;
+			n->n_spisize = 0;
+			n->n_type = htobe16(IKEV2_N_INTERNAL_ADDRESS_FAILURE);
+			len = sizeof(*n);
+		} else {
+			sa->sa_cfg_peer.cfg_action = IKEV2_CP_REPLY;
+		}
+	}
 
 	/* CP payload */
 	if (sa->sa_cp) {
Index: ikev2.h
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.h,v
retrieving revision 1.12
diff -u -p -r1.12 ikev2.h
--- ikev2.h	30 Mar 2013 16:31:37 -0000	1.12
+++ ikev2.h	30 Apr 2014 02:57:33 -0000
@@ -471,9 +471,9 @@ struct ikev2_cp {
 
 extern struct iked_constmap ikev2_cp_map[];
 
-struct ikev2_cfg {
-	u_int16_t	cfg_type;	/* first bit must be set to zero */
-	u_int16_t	cfg_length;
+struct ikev2_cp_attr {
+	u_int16_t	attr_type;	/* first bit must be set to zero */
+	u_int16_t	attr_length;
 	/* Followed by variable-length data */
 } __packed;
 
Index: ikev2_pld.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2_pld.c,v
retrieving revision 1.32
diff -u -p -r1.32 ikev2_pld.c
--- ikev2_pld.c	28 Nov 2013 20:21:17 -0000	1.32
+++ ikev2_pld.c	30 Apr 2014 02:57:33 -0000
@@ -1049,11 +1049,15 @@ ikev2_pld_cp(struct iked *env, struct ik
     struct iked_message *msg, off_t offset)
 {
 	struct ikev2_cp		 cp;
-	struct ikev2_cfg	*cfg;
+	struct ikev2_cp_attr	*attr;
+	u_int16_t		 attr_type,attr_length;
 	u_int8_t		*buf;
 	size_t			 len, i;
 	u_int8_t		*msgbuf = ibuf_data(msg->msg_data);
 	struct iked_sa		*sa = msg->msg_sa;
+	struct iked_cfg		*sa_cfg;
+	struct sockaddr_in	*in4;
+	struct sockaddr_in6	*in6;
 
 	memcpy(&cp, msgbuf + offset, sizeof(cp));
 	offset += sizeof(cp);
@@ -1061,19 +1065,48 @@ ikev2_pld_cp(struct iked *env, struct ik
 	buf = msgbuf + offset;
 	len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(cp);
 
-	log_debug("%s: type %s length %d",
-	    __func__, print_map(cp.cp_type, ikev2_cp_map), len);
+	log_debug("%s: type %s length %d", __func__,
+	    print_map(cp.cp_type, ikev2_cp_map), len);
 	print_hex(buf, 0, len);
 
 	for (i = 0; i < len;) {
-		cfg = (struct ikev2_cfg *)(buf + i);
+		attr = (struct ikev2_cp_attr *)(buf + i);
+		attr_type = betoh16(attr->attr_type);
+		attr_length = betoh16(attr->attr_length);
 
 		log_debug("%s: %s 0x%04x length %d", __func__,
-		    print_map(betoh16(cfg->cfg_type), ikev2_cfg_map),
-		    betoh16(cfg->cfg_type),
-		    betoh16(cfg->cfg_length));
+		    print_map(attr_type, ikev2_cfg_map), attr_type, attr_length);
+
+		i += sizeof(*attr);
+
+		if (sa && ikev2_msg_frompeer(msg) && 
+		    !sa->sa_cfg_peer.cfg_action ){
+
+			sa_cfg = &sa->sa_cfg_peer;
+			sa_cfg->cfg_action = cp.cp_type;
+			sa_cfg->cfg_type = attr_type;
+
+			in4 = (struct sockaddr_in *)&sa_cfg->cfg.address.addr;
+			in6 = (struct sockaddr_in6 *)&sa_cfg->cfg.address.addr;
+
+			switch(attr_type){
+			case IKEV2_CFG_INTERNAL_IP4_ADDRESS:
+				in4->sin_family = AF_INET;
+				if(attr_length >=4)
+					memcpy(&in4->sin_addr.s_addr,
+					    buf + i, 4);
+				break;
+			case IKEV2_CFG_INTERNAL_IP6_ADDRESS:
+				in6->sin6_family = AF_INET6;
+				if(attr_length>=16)
+					memcpy(&in6->sin6_addr.s6_addr,
+					    buf + i, 16);
+				break;
+			}
+		}
+
+		i += attr_length;
 
-		i += betoh16(cfg->cfg_length) + sizeof(*cfg);
 	}
 
 	if (!ikev2_msg_frompeer(msg))
Index: parse.y
===================================================================
RCS file: /cvs/src/sbin/iked/parse.y,v
retrieving revision 1.33
diff -u -p -r1.33 parse.y
--- parse.y	28 Nov 2013 20:21:17 -0000	1.33
+++ parse.y	30 Apr 2014 02:57:33 -0000
@@ -322,8 +322,10 @@ int			 create_ike(char *, int, u_int8_t,
 			     struct ipsec_mode *, u_int8_t,
 			     u_int8_t, char *, char *, struct iked_lifetime *,
 			     struct iked_auth *, struct ipsec_filters *,
-			     struct ipsec_addr_wrap *);
+			     struct ipsec_addr_wrap *, const char *);
 int			 create_user(const char *, const char *);
+int			 create_addr_pool(const char *,
+			     struct ipsec_addr_wrap *);
 int			 get_id_type(char *);
 u_int8_t		 x2i(unsigned char *);
 int			 parsekey(unsigned char *, size_t, struct iked_auth *);
@@ -368,7 +370,7 @@ typedef struct {
 %token	FILENAME AUTHXF PRFXF ENCXF ERROR IKEV2 IKESA CHILDSA
 %token	PASSIVE ACTIVE ANY TAG TAP PROTO LOCAL GROUP NAME CONFIG EAP USER
 %token	IKEV1 FLOW SA TCPMD5 TUNNEL TRANSPORT COUPLE DECOUPLE SET
-%token	INCLUDE LIFETIME BYTES INET INET6 QUICK SKIP DEFAULT
+%token	INCLUDE LIFETIME BYTES INET INET6 QUICK SKIP DEFAULT POOL
 %token	<v.string>		STRING
 %token	<v.number>		NUMBER
 %type	<v.string>		string
@@ -391,7 +393,7 @@ typedef struct {
 %type	<v.mode>		ike_sa child_sa
 %type	<v.lifetime>		lifetime
 %type	<v.number>		byte_spec time_spec
-%type	<v.string>		name
+%type	<v.string>		name pool_ref
 %type	<v.cfg>			cfg ikecfg ikecfgvals
 %%
 
@@ -400,6 +402,7 @@ grammar		: /* empty */
 		| grammar '\n'
 		| grammar set '\n'
 		| grammar user '\n'
+		| grammar pool '\n'
 		| grammar ikev2rule '\n'
 		| grammar varset '\n'
 		| grammar otherrule skipline '\n'
@@ -437,10 +440,19 @@ user		: USER STRING STRING		{
 		}
 		;
 
+pool		: POOL STRING host_spec 	{
+			if (create_addr_pool($2, $3) == -1) {
+				free($3);
+				YYERROR;
+			}
+			free($3);
+		}
+		;
+
 ikev2rule	: IKEV2 name ikeflags satype af proto hosts_list peers
-		    ike_sa child_sa ids lifetime ikeauth ikecfg filters {
+		    ike_sa child_sa ids lifetime ikeauth pool_ref ikecfg filters {
 			if (create_ike($2, $5, $6, $7, &$8, $9, $10, $4, $3,
-			    $11.srcid, $11.dstid, &$12, &$13, $15, $14) == -1)
+			    $11.srcid, $11.dstid, &$12, &$13, $16, $15, $14) == -1)
 				YYERROR;
 		}
 		;
@@ -463,6 +475,10 @@ ikecfgvals	: cfg				{ $$ = $1; }
 		}
 		;
 
+pool_ref	: /* empty */			{ $$ = NULL; }
+		| POOL STRING			{ $$ = $2; }
+		;
+
 cfg		: CONFIG STRING host_spec	{
 			const struct ipsec_xf	*xf;
 
@@ -1082,6 +1098,7 @@ lookup(char *s)
 		{ "name",		NAME },
 		{ "passive",		PASSIVE },
 		{ "peer",		PEER },
+		{ "pool",		POOL },
 		{ "port",		PORT },
 		{ "prf",		PRFXF },
 		{ "proto",		PROTO },
@@ -2163,6 +2180,30 @@ print_user(struct iked_user *usr)
 }
 
 void
+print_addr_pool(struct iked_addr_pool *pool)
+{
+	uint32_t	 prefix;
+	char		 start[INET6_ADDRSTRLEN];
+	
+	switch (pool->pool_af) {
+	case AF_INET:
+		inet_ntop(AF_INET, &pool->pool_start[3], start, sizeof(start));
+		prefix = 32 - ffs(pool->pool_size +1) + 1;
+		break;
+	case AF_INET6:
+		inet_ntop(AF_INET6, pool->pool_start, start, sizeof(start));
+		prefix = 128 - ffs(pool->pool_size +1) + 1;
+		break;
+	default:
+		fatal("invalid address family");
+		return;
+	}
+
+	print_verbose("pool \"%s\" %s/%u\n", pool->pool_name, start,
+	    prefix);
+}
+
+void
 print_policy(struct iked_policy *pol)
 {
 	struct iked_proposal	*pp;
@@ -2310,6 +2351,10 @@ print_policy(struct iked_policy *pol)
 			    print_xf(pol->pol_auth.auth_method, 0, methodxfs));
 	}
 
+	if (pol->pol_addr_pool[0] != '\0') {
+		print_verbose(" pool \"%s\"", pol->pol_addr_pool);
+	}
+
 	for (i = 0; i < pol->pol_ncfg; i++) {
 		cfg = &pol->pol_cfg[i];
 		print_verbose(" config %s %s", print_xf(cfg->cfg_type,
@@ -2364,7 +2409,7 @@ create_ike(char *name, int af, u_int8_t 
     struct ipsec_mode *ipsec_sa, u_int8_t saproto,
     u_int8_t flags, char *srcid, char *dstid, struct iked_lifetime *lt,
     struct iked_auth *authtype, struct ipsec_filters *filter,
-    struct ipsec_addr_wrap *ikecfg)
+    struct ipsec_addr_wrap *ikecfg, const char *pool_name)
 {
 	char			 idstr[IKED_ID_SIZE];
 	u_int 			 idtype = IKEV2_ID_NONE;
@@ -2590,6 +2635,15 @@ create_ike(char *name, int af, u_int8_t 
 		pol.pol_nflows++;
 		RB_INSERT(iked_flows, &pol.pol_flows, &flows[j]);
 	}
+	
+	if(pool_name != NULL && pool_name[0] != '\0') {
+		if (strlcpy(pol.pol_addr_pool, pool_name, sizeof(pol.pol_addr_pool))
+		    > sizeof(pol.pol_addr_pool)) {
+			yyerror("address pool name is too long");
+			return (-1);
+		}
+		ipa = ipa->next;
+	}
 
 	for (j = 0, ipa = ikecfg; ipa; ipa = ipa->next, j++) {
 		if (j >= IKED_CFG_MAX)
@@ -2657,3 +2711,59 @@ create_user(const char *user, const char
 	rules++;
 	return (0);
 }
+
+int
+create_addr_pool(const char *name, struct ipsec_addr_wrap *start)
+{
+	struct iked_addr_pool	 pool;
+	struct in_addr		*a4;
+	struct in6_addr		*a6;
+	u_int32_t		 pool_bits;
+
+	bzero(&pool, sizeof(pool));
+
+	if (name == NULL || *name == '\0') {
+		yyerror("address pool name is required");
+		return (-1);
+	}
+
+	if (strlcpy(pool.pool_name, name, sizeof(pool.pool_name)) 
+	    > sizeof(pool.pool_name)) {
+		yyerror("address pool name is too long");
+		return (-1);
+	}
+
+	pool.pool_af = start->address.ss_family;
+
+	switch (pool.pool_af) {
+	case AF_INET:
+		a4 = &((struct sockaddr_in *)&start->address)->sin_addr;
+		pool.pool_start[3] = a4->s_addr;
+		pool_bits = 32 - start->mask;
+		break;
+
+	case AF_INET6:
+		a6 = &((struct sockaddr_in6*)&start->address)->sin6_addr;
+		memcpy(pool.pool_start, a6->s6_addr, sizeof(pool.pool_start));
+		pool_bits = 128 - start->mask;		  
+		break;
+	default:
+		yyerror("address type not supported");
+		return (-1);
+	}
+
+	pool.pool_size = (1<<(pool_bits)) - 1;
+
+	if (pool_bits > 30 || pool.pool_size < 2 ||
+	    pool.pool_size > IKED_ADDR_POOL_SIZE_MAX ) {
+		yyerror("address pool size is invalid");
+		return(-1);
+	}
+
+	pool.pool_start[3] &= htonl(0xffffffff << (pool_bits));
+
+	config_setaddrpool(env, &pool, PROC_IKEV2);
+
+	return (0);
+}
+
Index: policy.c
===================================================================
RCS file: /cvs/src/sbin/iked/policy.c,v
retrieving revision 1.26
diff -u -p -r1.26 policy.c
--- policy.c	28 Nov 2013 20:24:48 -0000	1.26
+++ policy.c	30 Apr 2014 02:57:33 -0000
@@ -23,6 +23,9 @@
 #include <sys/uio.h>
 #include <sys/tree.h>
 
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -39,6 +42,8 @@ static __inline int
 static __inline int
 	 user_cmp(struct iked_user *, struct iked_user *);
 static __inline int
+	 addr_pool_cmp(struct iked_addr_pool *, struct iked_addr_pool *);
+static __inline int
 	 childsa_cmp(struct iked_childsa *, struct iked_childsa *);
 static __inline int
 	 flow_cmp(struct iked_flow *, struct iked_flow *);
@@ -49,6 +54,7 @@ policy_init(struct iked *env)
 {
 	TAILQ_INIT(&env->sc_policies);
 	RB_INIT(&env->sc_users);
+	RB_INIT(&env->sc_pools);
 	RB_INIT(&env->sc_sas);
 	RB_INIT(&env->sc_activesas);
 	RB_INIT(&env->sc_activeflows);
@@ -505,6 +511,157 @@ user_cmp(struct iked_user *a, struct ike
 	return (strcmp(a->usr_name, b->usr_name));
 }
 
+struct iked_addr_pool *
+addr_pool_lookup(struct iked *env, const char *name)
+{
+	struct iked_addr_pool	 key;
+
+	if (strlcpy(key.pool_name, name,
+	    sizeof(key.pool_name)) >= sizeof(key.pool_name))
+		return (NULL);
+
+	return (RB_FIND(iked_addr_pools, &env->sc_pools, &key));
+}
+
+static __inline int
+addr_pool_cmp(struct iked_addr_pool *a, struct iked_addr_pool *b)
+{
+	return (strcmp(a->pool_name, b->pool_name));
+}
+
+int
+addr_pool_request(struct iked *env, const char *pool_name, 
+    struct sockaddr_storage *addr)
+{
+	struct iked_addr_pool	*pool;
+	uint32_t 		 i,a,start;
+	struct in_addr		*a4;
+	struct in6_addr		*a6;
+	static char		 p[INET6_ADDRSTRLEN];
+
+	if ((pool = addr_pool_lookup(env, pool_name)) == NULL) {
+		log_warn("address request failed: pool \"%s\" not found",
+		    pool_name);
+		return -1;
+	}
+
+	if (addr->ss_family != AF_UNSPEC &&
+	    addr->ss_family != pool->pool_af) {
+		log_debug("address request failed: address family mismatch");
+		return -1;
+	}
+
+	start = betoh32(pool->pool_start[3]);
+
+	// if the requested address is in range and available, assign it
+	switch (addr->ss_family) {
+	case AF_INET:
+		a4 = &((struct sockaddr_in *)addr)->sin_addr;
+		i = betoh32(a4->s_addr) - start;
+		if (betoh32(a4->s_addr) < start ||
+		    i >= pool->pool_size || 
+		    bit_test(pool->pool_used, i)) break;
+		goto done;
+	case AF_INET6:
+		a6 = &((struct sockaddr_in6*)addr)->sin6_addr;
+		i = betoh32(a6->s6_addr[12]) - start;
+		if(memcmp(a6->s6_addr, pool->pool_start, 12) ||
+		    betoh32(a6->s6_addr[12] < start) ||
+		    i >= pool->pool_size || 
+		    bit_test(pool->pool_used, i)) break;
+		goto done;
+	}
+
+	// get index of next available address
+	bit_ffc(pool->pool_used, pool->pool_size, &i);
+
+	if (i >= pool->pool_size){
+		log_warn("address request failed: pool \"%s\" is empty",
+		    pool_name);
+		return -1;
+	}
+
+	a = start + i;
+
+	switch (pool->pool_af) {
+	case AF_INET:
+		a4 = &((struct sockaddr_in *)addr)->sin_addr;
+		a4->s_addr = htobe32(a);
+		inet_ntop(AF_INET, a4, p, sizeof(p));
+		break;
+	case AF_INET6:
+		a6 = &((struct sockaddr_in6*)addr)->sin6_addr;
+		memcpy(a6->s6_addr, pool->pool_start, sizeof(a6->s6_addr));
+		a6->s6_addr[12] = htobe32(a);
+		inet_ntop(AF_INET6, a6, p, sizeof(p));
+		break;
+	default:
+		log_warnx("%s: invalid address family", __func__);
+		return -1;
+	}
+	addr->ss_family = pool->pool_af;
+   done:
+	bit_set(pool->pool_used, i);
+
+	log_debug("%s: assigned %s from %s (index=%u)", __func__, p,
+	    pool->pool_name, i);
+
+	return 0;
+}
+
+int
+addr_pool_release(struct iked *env, const char *pool_name, 
+    struct sockaddr_storage *addr)
+{
+	struct iked_addr_pool	*pool;
+	int			 i;
+	struct in_addr		*a4;
+	struct in6_addr		*a6;
+	static char		 p[INET6_ADDRSTRLEN];
+
+	if ((pool = addr_pool_lookup(env, pool_name)) == NULL) {
+		log_warn("address release failed: pool \"%s\" not found",
+			pool_name);
+		return -1;
+	}
+
+	if (addr->ss_family != pool->pool_af) {
+		log_warn("address release failed: address family mismatch");
+		return -1;
+	}
+
+	switch (pool->pool_af) {
+	case AF_INET:
+		a4 = &((struct sockaddr_in *)addr)->sin_addr;
+		i = betoh32(a4->s_addr) - betoh32(pool->pool_start[3]);
+		inet_ntop(AF_INET, a4, p, sizeof(p));
+		break;
+	case AF_INET6:
+		a6 = &((struct sockaddr_in6*)addr)->sin6_addr;
+		if(memcmp(a6->s6_addr, pool->pool_start, 12)) i = -1;
+		else i = betoh32(a6->s6_addr[3]) - 
+			 betoh32(pool->pool_start[3]);
+		inet_ntop(AF_INET6, a6, p, sizeof(p));
+		break;
+	default:
+		log_warnx("%s: invalid address family", __func__);
+		return -1;
+	}
+
+	if (!(0 <= i && (uint32_t)i < pool->pool_size)) {
+		log_warn("address release failed, %s is not in \"%s\"",p,
+			pool_name);
+		return -1;
+	}
+
+	bit_clear(pool->pool_used, i);
+
+	log_debug("%s: released %s from \"%s\" (index=%u)", __func__, p,
+	    pool_name, i);
+
+	return 0;
+}
+
 static __inline int
 childsa_cmp(struct iked_childsa *a, struct iked_childsa *b)
 {
@@ -550,5 +707,6 @@ flow_cmp(struct iked_flow *a, struct ike
 RB_GENERATE(iked_sas, iked_sa, sa_entry, sa_cmp);
 RB_GENERATE(iked_sapeers, iked_sa, sa_peer_entry, sa_peer_cmp);
 RB_GENERATE(iked_users, iked_user, usr_entry, user_cmp);
+RB_GENERATE(iked_addr_pools, iked_addr_pool, pool_entry, addr_pool_cmp);
 RB_GENERATE(iked_activesas, iked_childsa, csa_node, childsa_cmp);
 RB_GENERATE(iked_flows, iked_flow, flow_node, flow_cmp);
Index: types.h
===================================================================
RCS file: /cvs/src/sbin/iked/types.h,v
retrieving revision 1.17
diff -u -p -r1.17 types.h
--- types.h	8 Jan 2013 10:38:19 -0000	1.17
+++ types.h	30 Apr 2014 02:57:33 -0000
@@ -58,6 +58,7 @@
 #define IKED_TAG_SIZE		64
 #define IKED_CYCLE_BUFFERS	8	/* # of static buffers for mapping */
 #define IKED_PASSWORD_SIZE	256	/* limited by most EAP types */
+#define IKED_ADDR_POOL_SIZE_MAX 65536   /* 8kb bitstring */
 
 #define IKED_LIFETIME_BYTES	536870912 /* 512 Mb */
 #define IKED_LIFETIME_SECONDS	10800	  /* 3 hours */
@@ -97,6 +98,7 @@ enum imsg_type {
 	IMSG_IKE_MESSAGE,
 	IMSG_CFG_POLICY,
 	IMSG_CFG_USER,
+	IMSG_CFG_ADDR_POOL,
 	IMSG_CERTREQ,
 	IMSG_CERT,
 	IMSG_CERTVALID,
@@ -126,7 +128,8 @@ enum flushmode {
 	RESET_CA,
 	RESET_POLICY,
 	RESET_SA,
-	RESET_USER
+	RESET_USER,
+	RESET_ADDR_POOL
 };
 
 #ifndef nitems

Reply via email to