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