The following provides address pools for iked. It's nothing fancy, but it seems to work, at least in the cursory testing done against the Windows 7 client.
Each policy gets it's own pool, configured by "config address <start ip> - <end ip>". There is a hard limit of 65536 addresses (8kb) per pool, which should be plenty. There is NO ipv6 support, partly because I'm not really sure how or why it would be needed. A request for a specific ip that is available in the pool will be honoured. Comments please! --Ryan Slack Index: addr_pool.c =================================================================== RCS file: addr_pool.c diff -N addr_pool.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ addr_pool.c 7 Jun 2013 03:05:46 -0000 @@ -0,0 +1,188 @@ + +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <err.h> + +#include "iked.h" + +int +addr_pool_init_mask(struct addr_pool* pool, + struct sockaddr_storage *address, u_int8_t prefixlengh) +{ + struct sockaddr_in *a4; + /* struct sockaddr_in6 *a6; */ + + if(pool == NULL) return(-1); + + memcpy(&pool->addr, address, sizeof(pool->addr)); + + switch(address->ss_family){ + case AF_INET: + if (!(16 < prefixlengh && prefixlengh < 30)) { + log_warnx("%s: prefixlengh (%d) out of range [16:30]", + __func__,prefixlengh); + return(-1); + } + //pool->mask = prefixlen2mask(prefixlengh); + pool->size = (1<<(32 - prefixlengh)) - 2; + + a4 = (struct sockaddr_in *)&pool->addr; + a4->sin_addr.s_addr = ntohl(a4->sin_addr.s_addr) + 1; + a4->sin_addr.s_addr = htonl(a4->sin_addr.s_addr); + + break; + case AF_INET6: + default: + errno = EAFNOSUPPORT; + log_warn("%s: ",__func__); + return (-1); + } + + return(0); +} + +int +addr_pool_init_range(struct addr_pool* pool, + struct sockaddr_storage *start, + struct sockaddr_storage *end) +{ + struct sockaddr_in *a4, *b4; + //struct sockaddr_in6 *a6, *b6; + uint32_t size_l; + + if(pool == NULL || start == NULL || end == NULL) return(-1); + + if (start->ss_family != end->ss_family) { + log_debug("%s: address family mismatch", __func__); + return (-1); + } + + + switch(start->ss_family){ + case AF_INET: + a4 = (struct sockaddr_in *)start; + b4 = (struct sockaddr_in *)end; + + size_l = ntohl(b4->sin_addr.s_addr) - ntohl(a4->sin_addr.s_addr) + 1; + + if ( size_l > ADDR_POOL_SIZE_MAX ) { + log_warnx("%s: size (%d) out of range (%d)",__func__, size_l, ADDR_POOL_SIZE_MAX); + return(-1); + } + pool->size = size_l; + + break; + case AF_INET6: + default: + errno = EAFNOSUPPORT; + log_warn("%s: ", __func__); + return (-1); + } + + memcpy(&pool->addr, start, sizeof(pool->addr)); + + return(0); +} + + +int +addr_pool_alloc(struct addr_pool* pool) +{ + if((pool->pool = bit_alloc(pool->size))==NULL) + return (-1); + + log_debug("%s: %s+%d", __func__, + print_host(&pool->addr, NULL, 0), pool->size); + + return(0); +} + +int +addr_pool_free(struct addr_pool* pool){ + if(pool->size) + free(pool->pool); + return (0); +} + +int +addr_pool_reqest(struct addr_pool* pool, struct sockaddr_storage *address) +{ + struct sockaddr_in *a4; + //struct sockaddr_in6 *sa6,*pa6; + u_int32_t h_addr; + int32_t i; + if(pool->addr.ss_family != address->ss_family){ + log_debug("%s: address family mismatch", __func__); + return(-1); + } + switch(pool->addr.ss_family){ + case AF_INET: + a4 = ((struct sockaddr_in *)address); + h_addr = ntohl(((struct sockaddr_in*)&pool->addr)->sin_addr.s_addr); + i = ntohl(a4->sin_addr.s_addr) - h_addr; + if (0 <= i && (uint32_t)i < pool->size && !bit_test(pool->pool,i)) { + bit_set(pool->pool,i); + } else { + bit_ffc(pool->pool,pool->size,&i); + bit_set(pool->pool,i); + a4->sin_addr.s_addr = htonl(h_addr + i); + } + log_debug("%s: assigned %s [%d]", __func__, + print_host(address, NULL, 0), i); + + break; + case AF_INET6: + default: + errno = EAFNOSUPPORT; + log_warn("%s: ", __func__); + return (-1); + } + + return (0); +} + +int +addr_pool_release(struct addr_pool* pool, struct sockaddr_storage *address) +{ + struct sockaddr_in *a4; + //struct sockaddr_in6 *sa6,pa6; + u_int32_t h_addr; + int32_t i; + + if (pool->addr.ss_family != address->ss_family) { + log_warnx("%s: address family mismatch", __func__); + return(-1); + } + + switch(pool->addr.ss_family){ + case AF_INET: + a4 = ((struct sockaddr_in *)address); + //if (a4->sin_addr.s_addr == 0x) return (-1); + h_addr = ntohl(((struct sockaddr_in*)&pool->addr)->sin_addr.s_addr); + i = ntohl(a4->sin_addr.s_addr) - h_addr; + if (0 <= i && (uint32_t)i < pool->size && bit_test(pool->pool,i)) { + bit_clear(pool->pool,i); + log_debug("%s: released %s [%d]", __func__, + print_host(address, NULL, 0), i); + + } else { + log_debug("%s: %s is not from pool %s+%d", __func__, + print_host(address, NULL, 0), + print_host(&pool->addr, NULL, 0), pool->size); + return (-1); + } + break; + case AF_INET6: + default: + errno = EAFNOSUPPORT; + log_warn("%s: ", __func__); + return (-1); + } + + return (0); +} Index: addr_pool.h =================================================================== RCS file: addr_pool.h diff -N addr_pool.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ addr_pool.h 7 Jun 2013 03:05:46 -0000 @@ -0,0 +1,41 @@ + + +#include <sys/types.h> +#include <sys/socket.h> +#include <bitstring.h> + +#ifndef _ADDR_POOL_H +#define _ADDR_POOL_H + +/* 8kb bitstring */ +#define ADDR_POOL_SIZE_MAX 65536 + +struct addr_pool{ + struct sockaddr_storage addr; + u_int32_t size; + bitstr_t *pool; +}; + +int +addr_pool_init_mask(struct addr_pool* pool, + struct sockaddr_storage *address, + u_int8_t prefixlengh); + +int +addr_pool_init_range(struct addr_pool *pool, + struct sockaddr_storage *start, + struct sockaddr_storage *end); + +int +addr_pool_alloc(struct addr_pool* pool); + +int +addr_pool_free(struct addr_pool* pool); + +int +addr_pool_reqest(struct addr_pool* pool, struct sockaddr_storage *address); + +int +addr_pool_release(struct addr_pool* pool, struct sockaddr_storage *address); + +#endif /* _ADDR_POOL_H */ Index: config.c =================================================================== RCS file: /cvs/src/sbin/iked/config.c,v retrieving revision 1.20 diff -u -p -r1.20 config.c --- config.c 21 Mar 2013 04:30:14 -0000 1.20 +++ config.c 7 Jun 2013 03:05:46 -0000 @@ -134,6 +134,11 @@ config_free_sa(struct iked *env, struct free(sa->sa_eapid); ibuf_release(sa->sa_eapmsk); + if (sa->sa_policy->pol_addr_pool.size && + sa->sa_cp.cfg_type == IKEV2_CFG_INTERNAL_IP4_ADDRESS) + addr_pool_release(&sa->sa_policy->pol_addr_pool, + &sa->sa_cp.cfg.address.addr); + free(sa); } @@ -173,6 +178,7 @@ config_free_policy(struct iked *env, str remove: config_free_proposals(&pol->pol_proposals, 0); config_free_flows(env, &pol->pol_flows); + addr_pool_free(&pol->pol_addr_pool); free(pol); } @@ -696,6 +702,9 @@ config_getpolicy(struct iked *env, struc RB_INSERT(iked_flows, &pol->pol_flows, flow); } + + addr_pool_alloc(&pol->pol_addr_pool); + TAILQ_INSERT_TAIL(&env->sc_policies, pol, pol_entry); Index: iked.h =================================================================== RCS file: /cvs/src/sbin/iked/iked.h,v retrieving revision 1.56 diff -u -p -r1.56 iked.h --- iked.h 8 Jan 2013 10:38:19 -0000 1.56 +++ iked.h 7 Jun 2013 03:05:46 -0000 @@ -16,13 +16,17 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <sys/param.h> #include <sys/types.h> #include <sys/tree.h> #include <sys/queue.h> #include <imsg.h> +#include <event.h> + #include "types.h" #include "dh.h" +#include "addr_pool.h" #ifndef _IKED_H #define _IKED_H @@ -266,6 +270,8 @@ struct iked_policy { struct iked_cfg pol_cfg[IKED_CFG_MAX]; u_int pol_ncfg; + struct addr_pool pol_addr_pool; + struct iked_lifetime pol_lifetime; struct iked_sapeers pol_sapeers; @@ -356,7 +362,7 @@ struct iked_sa { u_int sa_stateinit; /* SA_INIT */ u_int sa_statevalid; /* IKE_AUTH */ - int sa_cp; /* XXX */ + struct iked_cfg sa_cp; struct iked_policy *sa_policy; struct timeval sa_timecreated; Index: ikev2.c =================================================================== RCS file: /cvs/src/sbin/iked/ikev2.c,v retrieving revision 1.82 diff -u -p -r1.82 ikev2.c --- ikev2.c 21 Mar 2013 04:30:14 -0000 1.82 +++ ikev2.c 7 Jun 2013 03:05:47 -0000 @@ -952,7 +952,7 @@ ikev2_init_ike_auth(struct iked *env, st len = ibuf_size(sa->sa_localauth.id_buf) + sizeof(*auth); /* CP payload */ - if (sa->sa_cp) { + if (sa->sa_cp.cfg_action) { if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CP) == -1) goto done; @@ -1396,12 +1396,13 @@ ikev2_add_cp(struct iked *env, struct ik struct sockaddr_in *in4; struct sockaddr_in6 *in6; u_int8_t prefixlen; + u_int32_t mask; if ((cp = ibuf_advance(buf, sizeof(*cp))) == NULL) return (-1); len = sizeof(*cp); - switch (sa->sa_cp) { + switch (sa->sa_cp.cfg_action) { case IKEV2_CP_REQUEST: cp->cp_type = IKEV2_CP_REPLY; break; @@ -1409,34 +1410,61 @@ ikev2_add_cp(struct iked *env, struct ik case IKEV2_CP_SET: case IKEV2_CP_ACK: /* Not yet supported */ + log_debug("%s: action 0x%04x not yet supported", __func__, + sa->sa_cp.cfg_action); return (-1); } - + log_debug("%s: pol->pol_ncfg = %d", __func__,pol->pol_ncfg); for (i = 0; i < pol->pol_ncfg; i++) { ikecfg = &pol->pol_cfg[i]; if (ikecfg->cfg_action != cp->cp_type) continue; + log_debug("%s: %s", __func__, + print_map(ikecfg->cfg_type, ikev2_cfg_map)); + if ((cfg = ibuf_advance(buf, sizeof(*cfg))) == NULL) return (-1); cfg->cfg_type = htobe16(ikecfg->cfg_type); len += sizeof(*cfg); + in4 = NULL; + switch (ikecfg->cfg_type) { case IKEV2_CFG_INTERNAL_IP4_ADDRESS: + log_debug("%s: INTERNAL_IP4_ADDRESS reply", __func__); + if(pol->pol_addr_pool.size && + sa->sa_cp.cfg.address.addr.ss_family == AF_INET){ + if (addr_pool_reqest(&pol->pol_addr_pool, + &sa->sa_cp.cfg.address.addr) == -1) + return (-1); + in4 = ((struct sockaddr_in*)&sa->sa_cp.cfg.address.addr); + } case IKEV2_CFG_INTERNAL_IP4_NETMASK: case IKEV2_CFG_INTERNAL_IP4_DNS: case IKEV2_CFG_INTERNAL_IP4_NBNS: case IKEV2_CFG_INTERNAL_IP4_DHCP: case IKEV2_CFG_INTERNAL_IP4_SERVER: /* 4 bytes IPv4 address */ - in4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr; + if (in4 == NULL) + in4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr; cfg->cfg_length = htobe16(4); if (ibuf_add(buf, &in4->sin_addr.s_addr, 4) == -1) return (-1); len += 4; break; + case IKEV2_CFG_INTERNAL_IP4_SUBNET: + /* 4 bytes IPv4 address + 4 bytes IPv4 mask + */ + in4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr; + mask = prefixlen2mask(ikecfg->cfg.address.addr_mask); + cfg->cfg_length = htobe16(8); + if (ibuf_add(buf, &in4->sin_addr.s_addr, 4) == -1) + return (-1); + if (ibuf_add(buf, &mask, 4) == -1) + return (-1); + len += 8; + break; case IKEV2_CFG_INTERNAL_IP6_DNS: case IKEV2_CFG_INTERNAL_IP6_NBNS: case IKEV2_CFG_INTERNAL_IP6_DHCP: @@ -1449,6 +1477,7 @@ ikev2_add_cp(struct iked *env, struct ik len += 16; break; case IKEV2_CFG_INTERNAL_IP6_ADDRESS: + 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); @@ -1469,6 +1498,7 @@ ikev2_add_cp(struct iked *env, struct ik } } + log_debug("%s: length %d", __func__, len); return (len); } @@ -1962,7 +1992,7 @@ ikev2_resp_ike_auth(struct iked *env, st len = ibuf_size(sa->sa_localauth.id_buf) + sizeof(*auth); /* CP payload */ - if (sa->sa_cp) { + if (sa->sa_cp.cfg_action) { if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CP) == -1) goto done; Index: ikev2_pld.c =================================================================== RCS file: /cvs/src/sbin/iked/ikev2_pld.c,v retrieving revision 1.31 diff -u -p -r1.31 ikev2_pld.c --- ikev2_pld.c 21 Mar 2013 04:30:14 -0000 1.31 +++ ikev2_pld.c 7 Jun 2013 03:05:47 -0000 @@ -1046,6 +1046,9 @@ ikev2_pld_cp(struct iked *env, struct ik size_t len, i; u_int8_t *msgbuf = ibuf_data(msg->msg_data); struct iked_sa *sa = msg->msg_sa; + struct iked_cfg *ikecfg; + struct sockaddr_in *in4; + struct sockaddr_in6 *in6; memcpy(&cp, msgbuf + offset, sizeof(cp)); offset += sizeof(cp); @@ -1061,9 +1064,45 @@ ikev2_pld_cp(struct iked *env, struct ik cfg = (struct ikev2_cfg *)(buf + i); 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(betoh16(cfg->cfg_type), ikev2_cfg_map), + betoh16(cfg->cfg_type), + betoh16(cfg->cfg_length)); + + if(ikev2_msg_frompeer(msg) && sa && + cp.cp_type == IKEV2_CP_REQUEST){ + + ikecfg = &sa->sa_cp; + + switch(betoh16(cfg->cfg_type)) { + case IKEV2_CFG_INTERNAL_IP4_ADDRESS: + /* 4 bytes IPv4 address */ + in4 = (struct sockaddr_in *) + &ikecfg->cfg.address.addr; + in4->sin_len = sizeof(*in4); + in4->sin_family = AF_INET; + ikecfg->cfg.address.addr_af = AF_INET; + if (cfg->cfg_length >= 4){ + memcpy(&in4->sin_addr.s_addr,buf, 4); + } + ikecfg->cfg_type = IKEV2_CFG_INTERNAL_IP4_ADDRESS; + break; + + case IKEV2_CFG_INTERNAL_IP6_ADDRESS: + /* 16 bytes IPv6 address */ + in6 = (struct sockaddr_in6 *) + &ikecfg->cfg.address.addr; + in6->sin6_len = sizeof(*in6); + in6->sin6_family = AF_INET6; + ikecfg->cfg.address.addr_af = AF_INET6; + + if (cfg->cfg_length >= 16){ + memcpy(&in6->sin6_addr.s6_addr,buf, 16); + }else{ + bzero(&in6->sin6_addr.s6_addr, 16); + } + break; + } + } i += betoh16(cfg->cfg_length) + sizeof(*cfg); } @@ -1072,7 +1111,7 @@ ikev2_pld_cp(struct iked *env, struct ik return (0); if (sa) - sa->sa_cp = cp.cp_type; + sa->sa_cp.cfg_action = cp.cp_type; return (0); } Index: parse.y =================================================================== RCS file: /cvs/src/sbin/iked/parse.y,v retrieving revision 1.30 diff -u -p -r1.30 parse.y --- parse.y 21 Mar 2013 04:30:14 -0000 1.30 +++ parse.y 7 Jun 2013 03:05:48 -0000 @@ -266,7 +266,8 @@ const struct ipsec_xf cpxfs[] = { { "protected-subnet", IKEV2_CFG_INTERNAL_IP4_SUBNET, AF_INET }, { "protected-subnet", IKEV2_CFG_INTERNAL_IP6_SUBNET, AF_INET6 }, { "access-server", IKEV2_CFG_INTERNAL_IP4_SERVER, AF_INET }, - { "access-server", IKEV2_CFG_INTERNAL_IP6_SERVER, AF_INET6 } + { "access-server", IKEV2_CFG_INTERNAL_IP6_SERVER, AF_INET6 }, + { NULL } }; const struct iked_lifetime deflifetime = { @@ -321,7 +322,7 @@ 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_hosts *, struct ipsec_addr_wrap *); int create_user(const char *, const char *); int get_id_type(char *); u_int8_t x2i(unsigned char *); @@ -344,6 +345,7 @@ typedef struct { struct ipsec_hosts peers; struct ipsec_addr_wrap *anyhost; struct ipsec_addr_wrap *host; + struct ipsec_hosts pool; struct ipsec_addr_wrap *cfg; struct { char *srcid; @@ -367,7 +369,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 ADDRESS %token <v.string> STRING %token <v.number> NUMBER %type <v.string> string @@ -392,6 +394,7 @@ typedef struct { %type <v.number> byte_spec time_spec %type <v.string> name %type <v.cfg> cfg ikecfg ikecfgvals +%type <v.pool> cfg_pool pool %% grammar : /* empty */ @@ -437,13 +440,31 @@ user : USER STRING STRING { ; 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 cfg_pool 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, &$14, $15) == -1) YYERROR; } ; + +cfg_pool : CONFIG ADDRESS pool { $$ = $3; } + ; + +pool : ANY { + $$.src = host_any(); + $$.dst = NULL; + } + | host_spec { + $$.src = $1; + $$.dst = NULL; + } + | host_spec '-' host_spec { + $$.src = $1; + $$.dst = $3; + } + ; + ikecfg : /* empty */ { $$ = NULL; } | ikecfgvals { $$ = $1; } ; @@ -1053,6 +1074,7 @@ lookup(char *s) /* this has to be sorted always */ static const struct keywords keywords[] = { { "active", ACTIVE }, + { "address", ADDRESS }, { "ah", AH }, { "any", ANY }, { "auth", AUTHXF }, @@ -2321,7 +2343,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_hosts *pool, struct ipsec_addr_wrap *ikecfg) { struct ipsec_addr_wrap *ipa, *ipb; struct iked_policy pol; @@ -2542,6 +2564,36 @@ create_ike(char *name, int af, u_int8_t pol.pol_nflows++; RB_INSERT(iked_flows, &pol.pol_flows, &flows[j]); + } + + + if (pool && (pol.pol_af != AF_UNSPEC) && + ((pool->src && (pool->src->af != AF_UNSPEC) && + (pool->src->af != pol.pol_af)) || + (pool->dst && (pool->dst->af != AF_UNSPEC) && + (pool->dst->af != pol.pol_af)))) + fatalx("create_ike: policy address family mismatch"); + + if (pool && pool->src){ + if (pool->dst != NULL) { + if (addr_pool_init_range(&pol.pol_addr_pool, + &pool->src->address, + &pool->dst->address) == -1) + return (-1); + + } else if (pool->src->mask) { + if (addr_pool_init_mask(&pol.pol_addr_pool, + &pool->src->address, + pool->src->mask) == -1) + return (-1); + } else { + // XXX build from hosts/flows + return (-1); + } + pool->src->type = IKEV2_CFG_INTERNAL_IP4_ADDRESS; + pool->src->action = IKEV2_CP_REPLY; /* XXX */ + ikecfg->tail->next = pool->src; + ikecfg->tail = pool->src->tail; } for (j = 0, ipa = ikecfg; ipa; ipa = ipa->next, j++) {