diff options
author | SUZUKI, Shinsuke <suz@kame.net> | 2006-01-26 06:21:37 +0000 |
---|---|---|
committer | SUZUKI, Shinsuke <suz@kame.net> | 2006-01-26 06:21:37 +0000 |
commit | d859b0a9117ccd0c5b42e6ba56da47d955516026 (patch) | |
tree | de087126d87a42118b0b0d1f2ec460c9eb6fa133 | |
parent | e137e82e0a14b4b60f165b2621a25fbc8c040c2c (diff) |
supported IA-NA address-pool
ToDo: IA-PD prefix-pool, write a manual
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | Makefile.in | 4 | ||||
-rw-r--r-- | cfparse.y | 135 | ||||
-rw-r--r-- | cftoken.l | 17 | ||||
-rw-r--r-- | config.c | 472 | ||||
-rw-r--r-- | config.h | 42 | ||||
-rw-r--r-- | dhcp6s.c | 175 | ||||
-rw-r--r-- | lease.c | 257 | ||||
-rw-r--r-- | lease.h | 39 |
9 files changed, 1142 insertions, 3 deletions
@@ -1,3 +1,7 @@ +2006-01-26 SUZUKI, Shinsuke <suz@kame.net> + * supported IA-NA address-pool + ToDo: IA-PD prefix-pool, write a manual + 2006-01-19 SUZUKI, Shinsuke <suz@kame.net> * corrected the condition for detecting unwanted incoming messages * missing initialization in dhcp6c diff --git a/Makefile.in b/Makefile.in index 20ddf8a..338f080 100644 --- a/Makefile.in +++ b/Makefile.in @@ -25,7 +25,7 @@ # SUCH DAMAGE. # -# $Id: Makefile.in,v 1.4 2005-12-11 06:33:57 suzsuz Exp $ +# $Id: Makefile.in,v 1.5 2006-01-26 06:21:37 suzsuz Exp $ # $KAME: Makefile.in,v 1.45 2005/10/16 16:25:38 suz Exp $ # @@ -60,7 +60,7 @@ GENSRCS=cfparse.c cftoken.c CLIENTOBJS= dhcp6c.o common.o config.o prefixconf.o dhcp6c_ia.o timer.o \ dhcp6c_script.o if.o base64.o auth.o dhcp6_ctl.o addrconf.o \ $(GENSRCS:%.c=%.o) -SERVOBJS= dhcp6s.o common.o if.o config.o timer.o \ +SERVOBJS= dhcp6s.o common.o if.o config.o timer.o lease.o \ base64.o auth.o dhcp6_ctl.o $(GENSRCS:%.c=%.o) RELAYOBJS = dhcp6relay.o common.o timer.o LITECLIENTOBJS= dhcp6lc.o common.o if.o timer.o dhcp6c_script.o @@ -83,6 +83,7 @@ extern void yyerror __P((char *, ...)) } while (0) static struct cf_namelist *iflist_head, *hostlist_head, *iapdlist_head; +static struct cf_namelist *poollist_head; static struct cf_namelist *authinfolist_head, *keylist_head; static struct cf_namelist *ianalist_head; struct cf_list *cf_dns_list, *cf_dns_name_list, *cf_ntp_list; @@ -109,6 +110,7 @@ static void cleanup_cflist __P((struct cf_list *)); %token AUTHENTICATION PROTOCOL ALGORITHM DELAYED RECONFIG HMACMD5 MONOCOUNTER %token AUTHNAME RDM KEY %token KEYINFO REALM KEYID SECRET KEYNAME EXPIRE +%token POOL POOLNAME RANGE TO ADDRESS_POOL %token NUMBER SLASH EOS BCL ECL STRING QSTRING PREFIX INFINITY %token COMMA @@ -118,9 +120,12 @@ static void cleanup_cflist __P((struct cf_list *)); char* str; struct cf_list *list; struct dhcp6_prefix *prefix; + struct dhcp6_range *range; + struct dhcp6_poolspec *pool; } %type <str> IFNAME HOSTNAME AUTHNAME KEYNAME DUID_ID STRING QSTRING IAID +%type <str> POOLNAME %type <num> NUMBER duration authproto authalg authrdm %type <list> declaration declarations dhcpoption ifparam ifparams %type <list> address_list address_list_ent dhcpoption_list @@ -129,6 +134,8 @@ static void cleanup_cflist __P((struct cf_list *)); %type <list> authparam_list authparam %type <list> keyparam_list keyparam %type <prefix> addressparam prefixparam +%type <range> rangeparam +%type <pool> poolparam %% statements: @@ -143,6 +150,7 @@ statement: | ia_statement | authentication_statement | key_statement + | pool_statement ; interface_statement: @@ -328,6 +336,18 @@ key_statement: } ; +pool_statement: + POOL POOLNAME BCL declarations ECL EOS + { + struct cf_namelist *pool; + + MAKE_NAMELIST(pool, $2, $4); + + if (add_namelist(pool, &poollist_head)) + return (-1); + } + ; + address_list: { $$ = NULL; } | address_list address_list_ent @@ -470,6 +490,22 @@ declaration: $$ = l; } + | RANGE rangeparam EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_RANGE, $2, NULL); + + $$ = l; + } + | ADDRESS_POOL poolparam EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_ADDRESSPOOL, $2, NULL); + + $$ = l; + } ; dhcpoption_list: @@ -568,6 +604,37 @@ dhcpoption: } ; +rangeparam: + STRING TO STRING + { + struct dhcp6_range range0, *range; + + memset(&range0, 0, sizeof(range0)); + if (inet_pton(AF_INET6, $1, &range0.min) != 1) { + yywarn("invalid IPv6 address: %s", $1); + free($1); + free($3); + return (-1); + } + if (inet_pton(AF_INET6, $3, &range0.max) != 1) { + yywarn("invalid IPv6 address: %s", $3); + free($1); + free($3); + return (-1); + } + free($1); + free($3); + + if ((range = malloc(sizeof(*range))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *range = range0; + + $$ = range; + } + ; + addressparam: STRING duration { @@ -686,6 +753,62 @@ prefixparam: $$ = pconf; } +poolparam: + STRING duration + { + struct dhcp6_poolspec* pool; + + if ((pool = malloc(sizeof(*pool))) == NULL) { + yywarn("can't allocate memory"); + free($1); + return (-1); + } + if ((pool->name = strdup($1)) == NULL) { + yywarn("can't allocate memory"); + free($1); + return (-1); + } + free($1); + + /* validate other parameters later */ + if ($2 < 0) + pool->pltime = DHCP6_DURATITION_INFINITE; + else + pool->pltime = (u_int32_t)$2; + pool->vltime = pool->pltime; + + $$ = pool; + } + | STRING duration duration + { + struct dhcp6_poolspec* pool; + + if ((pool = malloc(sizeof(*pool))) == NULL) { + yywarn("can't allocate memory"); + free($1); + return (-1); + } + if ((pool->name = strdup($1)) == NULL) { + yywarn("can't allocate memory"); + free($1); + return (-1); + } + free($1); + + /* validate other parameters later */ + if ($2 < 0) + pool->pltime = DHCP6_DURATITION_INFINITE; + else + pool->pltime = (u_int32_t)$2; + if ($3 < 0) + pool->vltime = DHCP6_DURATITION_INFINITE; + else + pool->vltime = (u_int32_t)$3; + + $$ = pool; + } + ; + duration: INFINITY { @@ -967,6 +1090,8 @@ cleanup() authinfolist_head = NULL; cleanup_namelist(keylist_head); keylist_head = NULL; + cleanup_namelist(poollist_head); + poollist_head = NULL; cleanup_cflist(cf_sip_list); cf_sip_list = NULL; @@ -1004,6 +1129,11 @@ cleanup_cflist(p) return; n = p->next; +#ifdef USE_POOL + if (p->type == DECL_ADDRESSPOOL) { + free(((struct dhcp6_poolspec *)p->ptr)->name); + } +#endif if (p->ptr) free(p->ptr); if (p->list) @@ -1031,6 +1161,11 @@ cf_post_config() if (configure_ia(ianalist_head, IATYPE_NA)) config_fail(); +#ifdef USE_POOL + if (configure_pool(poollist_head)) + config_fail(); +#endif /* USE_POOL */ + if (configure_interface(iflist_head)) config_fail(); @@ -110,6 +110,7 @@ ecl \} %s S_AUTH %s S_KEY %s S_SECRET +%s S_POOL %% %{ @@ -136,6 +137,15 @@ ecl \} return (HOSTNAME); } + /* pool configuration */ +<S_CNF>pool { DECHO; BEGIN S_POOL; return (POOL); } +<S_POOL>{string} { + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (POOLNAME); +} + <S_CNF>duid { DECHO; BEGIN S_DUID; return (DUID); } <S_DUID>{duid} { DECHO; @@ -160,6 +170,13 @@ ecl \} /* send */ <S_CNF>send { DECHO; return (SEND); } + /* range */ +<S_CNF>range { DECHO; return (RANGE); } +<S_CNF>to { DECHO; return (TO); } + + /* address-pool */ +<S_CNF>address-pool { DECHO; return (ADDRESS_POOL); } + /* DHCP options */ <S_CNF>option { DECHO; return (OPTION); } @@ -58,6 +58,9 @@ #include <common.h> #include <auth.h> #include <base64.h> +#ifdef USE_POOL +#include <lease.h> +#endif /* USE_POOL */ extern int errno; @@ -72,6 +75,19 @@ static struct keyinfo *key_list, *key_list0; static struct authinfo *auth_list, *auth_list0; static struct dhcp6_list siplist0, sipnamelist0, dnslist0, dnsnamelist0, ntplist0; static long long optrefreshtime0; +#ifdef USE_POOL +#ifndef DHCP6_DYNAMIC_HOSTCONF_MAX +#define DHCP6_DYNAMIC_HOSTCONF_MAX 1024 +#endif +struct dynamic_hostconf { + TAILQ_ENTRY(dynamic_hostconf) link; + struct host_conf *host; +}; +static TAILQ_HEAD(dynamic_hostconf_listhead, dynamic_hostconf) + dynamic_hostconf_head; +static unsigned int dynamic_hostconf_count; +static struct pool_conf *pool_conflist, *pool_conflist0; +#endif /* USE_POOL */ enum { DHCPOPTCODE_SEND, DHCPOPTCODE_REQUEST, DHCPOPTCODE_ALLOW }; @@ -94,6 +110,9 @@ struct dhcp6_ifconf { struct authinfo *authinfo; /* authentication information * (no need to clear) */ +#ifdef USE_POOL + struct dhcp6_poolspec pool; +#endif }; extern struct cf_list *cf_dns_list, *cf_dns_name_list, *cf_ntp_list; @@ -115,6 +134,13 @@ static void clear_authinfo __P((struct authinfo *)); static int configure_duid __P((char *, struct duid *)); static int get_default_ifid __P((struct prefix_ifconf *)); static char *qstrdup __P((char *)); +#ifdef USE_POOL +static void clear_poolconf __P((struct pool_conf *)); +static struct pool_conf *create_pool __P((char *, struct dhcp6_range *)); +struct host_conf *find_dynamic_hostconf __P((struct duid *)); +static int in6_addr_cmp __P((struct in6_addr *, struct in6_addr *)); +static void in6_addr_inc __P((struct in6_addr *)); +#endif /* USE_POOL */ int configure_interface(iflist) @@ -233,6 +259,45 @@ configure_interface(iflist) cp += strlen(ifc->scriptpath) - 1; *cp = '\0'; /* clear the terminating quote */ break; +#ifdef USE_POOL + case DECL_ADDRESSPOOL: + { + struct dhcp6_poolspec* spec; + struct pool_conf* pool; + + spec = (struct dhcp6_poolspec *)cfl->ptr; + + for (pool = pool_conflist0; pool; pool = pool->next) + if (strcmp(spec->name, pool->name) == 0) + break; + if (pool == NULL) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "pool '%s' not found", + configfilename, cfl->line, + spec->name); + goto bad; + } + if (spec->vltime != DHCP6_DURATITION_INFINITE && + (spec->pltime == DHCP6_DURATITION_INFINITE || + spec->pltime > spec->vltime)) { + dprintf(LOG_ERR, FNAME, "%s:%d ", + configfilename, cfl->line, + "specified a larger preferred lifetime " + "than valid lifetime"); + goto bad; + } + ifc->pool = *spec; + if ((ifc->pool.name = strdup(spec->name)) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation failed"); + goto bad; + } + dprintf(LOG_DEBUG, FNAME, + "pool '%s' is specified to the interface '%s'", + ifc->pool.name, ifc->ifname); + } + break; +#endif /* USE_POOL */ default: dprintf(LOG_ERR, FNAME, "%s:%d " "invalid interface configuration", @@ -534,6 +599,45 @@ configure_host(hostlist) "delayed auth with %s (keyid=%08x)", host->name, hconf->delayedkey->keyid); break; +#ifdef USE_POOL + case DECL_ADDRESSPOOL: + { + struct dhcp6_poolspec* spec; + struct pool_conf *pool; + + spec = (struct dhcp6_poolspec *)cfl->ptr; + + for (pool = pool_conflist0; pool; pool = pool->next) + if (strcmp(spec->name, pool->name) == 0) + break; + if (pool == NULL) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "pool '%s' not found", + configfilename, cfl->line, + spec->name); + goto bad; + } + if (spec->vltime != DHCP6_DURATITION_INFINITE && + (spec->pltime == DHCP6_DURATITION_INFINITE || + spec->pltime > spec->vltime)) { + dprintf(LOG_ERR, FNAME, "%s:%d ", + configfilename, cfl->line, + "specified a larger preferred lifetime " + "than valid lifetime"); + goto bad; + } + hconf->pool = *spec; + if ((hconf->pool.name = strdup(spec->name)) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation failed"); + goto bad; + } + dprintf(LOG_DEBUG, FNAME, + "pool '%s' is specified to the host '%s'", + hconf->pool.name, hconf->name); + } + break; +#endif /* USE_POOL */ default: dprintf(LOG_ERR, FNAME, "%s:%d " "invalid host configuration for %s", @@ -1210,6 +1314,9 @@ configure_cleanup() dhcp6_clear_list(&ntplist0); TAILQ_INIT(&ntplist0); optrefreshtime0 = -1; +#ifdef USE_POOL + clear_poolconf(pool_conflist0); +#endif /* USE_POOL */ } void @@ -1258,6 +1365,10 @@ configure_commit() ifp->authalgorithm = ifc->authinfo->algorithm; ifp->authrdm = ifc->authinfo->rdm; } +#ifdef USE_POOL + ifp->pool = ifc->pool; + ifc->pool.name = NULL; +#endif } clear_ifconf(dhcp6_ifconflist); @@ -1307,6 +1418,12 @@ configure_commit() /* commit information refresh time */ optrefreshtime = optrefreshtime0; +#ifdef USE_POOL + /* commit pool configuration */ + clear_poolconf(pool_conflist); + pool_conflist = pool_conflist0; + pool_conflist0 = NULL; +#endif /* USE_POOL */ } static void @@ -1326,6 +1443,10 @@ clear_ifconf(iflist) if (ifc->scriptpath) free(ifc->scriptpath); +#ifdef USE_POOL + if (ifc->pool.name) + free(ifc->pool.name); +#endif free(ifc); } } @@ -1384,6 +1505,10 @@ clear_hostconf(hlist) dhcp6_clear_list(&host->addr_list); if (host->duid.duid_id) free(host->duid.duid_id); +#ifdef USE_POOL + if (host->pool.name) + free(host->pool.name); +#endif free(host); } } @@ -1699,6 +1824,12 @@ find_hostconf(duid) { struct host_conf *host; +#ifdef USE_POOL + if ((host = find_dynamic_hostconf(duid)) != NULL) { + return (host); + } +#endif /* USE_POOL */ + for (host = host_conflist; host; host = host->next) { if (host->duid.duid_len == duid->duid_len && memcmp(host->duid.duid_id, duid->duid_id, @@ -1779,3 +1910,344 @@ qstrdup(qstr) return (dup); } + +#ifdef USE_POOL +int +configure_pool(poollist) + struct cf_namelist *poollist; +{ + struct cf_namelist *plp; + + dprintf(LOG_DEBUG, FNAME, "called"); + + if (poollist && dhcp6_mode != DHCP6_MODE_SERVER) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "pool statement is server-only", + configfilename, poollist->line); + goto bad; + } + + for (plp = poollist; plp; plp = plp->next) { + struct pool_conf *pool = NULL; + struct dhcp6_range *range = NULL; + struct cf_list *cfl; + + for (cfl = plp->params; cfl; cfl = cfl->next) { + switch(cfl->type) { + case DECL_RANGE: + range = cfl->ptr; + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid pool configuration", + configfilename, cfl->line); + goto bad; + } + } + + if (!range) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "pool '%s' has no range declaration", + configfilename, plp->line, + plp->name); + goto bad; + } + if ((pool = create_pool(plp->name, range)) == NULL) { + dprintf(LOG_ERR, FNAME, + "faled to craete pool '%s'", plp->name); + goto bad; + } + pool->next = pool_conflist0; + pool_conflist0 = pool; + } + + return (0); + + bad: + /* there is currently nothing special to recover the error */ + return (-1); +} + +static void +clear_poolconf(plist) + struct pool_conf *plist; +{ + struct pool_conf *pool, *pool_next; + + dprintf(LOG_DEBUG, FNAME, "called"); + + for (pool = plist; pool; pool = pool_next) { + pool_next = pool->next; + free(pool->name); + free(pool); + } +} + +struct host_conf * +create_dynamic_hostconf(duid, pool) + struct duid *duid; + struct dhcp6_poolspec *pool; +{ + struct dynamic_hostconf *dynconf = NULL; + struct host_conf *host; + char* strid = NULL; + static int init = 1; + + if (init) { + TAILQ_INIT(&dynamic_hostconf_head); + dynamic_hostconf_count = 0; + init = 0; + } + + if (dynamic_hostconf_count >= DHCP6_DYNAMIC_HOSTCONF_MAX) { + struct dynamic_hostconf_listhead *head = &dynamic_hostconf_head; + + dprintf(LOG_DEBUG, FNAME, "reached to the max count (count=%lu)", + dynamic_hostconf_count); + + /* Find the last entry that doesn't need authentication */ + TAILQ_FOREACH_REVERSE(dynconf, head, dynamic_hostconf_listhead, link) + if (dynconf->host->delayedkey == NULL) + break; + if (dynconf == NULL) + dynconf = TAILQ_LAST(head, dynamic_hostconf_listhead); + TAILQ_REMOVE(head, dynconf, link); + dynamic_hostconf_count--; + clear_hostconf(dynconf->host); + } else { + if ((dynconf = malloc(sizeof(*dynconf))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + return (NULL); + } + } + memset(dynconf, 0, sizeof(*dynconf)); + + if ((host = malloc(sizeof(*host))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + return (NULL); + } + memset(host, 0, sizeof(*host)); + TAILQ_INIT(&host->prefix_list); + TAILQ_INIT(&host->addr_list); + + if ((strid = duidstr(duid)) == NULL) + strid = "???"; + if ((host->name = strdup(strid)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + goto bad; + } + if (duidcpy(&host->duid, duid) != 0) { + goto bad; + } + if (pool->name) { + if ((host->pool.name = strdup(pool->name)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + goto bad; + } + } + host->pool.pltime = pool->pltime; + host->pool.vltime = pool->vltime; + + dynconf->host = host; + TAILQ_INSERT_HEAD(&dynamic_hostconf_head, dynconf, link); + dynamic_hostconf_count++; + + dprintf(LOG_DEBUG, FNAME, "created host_conf (name=%s)", host->name); + + return (host); + +bad: + if (host) + clear_hostconf(host); /* host->next must be NULL */ + if (dynconf) + free(dynconf); + + return (NULL); +} + +struct host_conf * +find_dynamic_hostconf(duid) + struct duid *duid; +{ + struct dynamic_hostconf *dynconf = NULL; + + TAILQ_FOREACH(dynconf, &dynamic_hostconf_head, link) { + if (dynconf->host->duid.duid_len == duid->duid_len && + memcmp(dynconf->host->duid.duid_id, duid->duid_id, + duid->duid_len) == 0) + break; + } + + if (dynconf) { + /* relocation */ + TAILQ_REMOVE(&dynamic_hostconf_head, dynconf, link); + TAILQ_INSERT_HEAD(&dynamic_hostconf_head, dynconf, link); + + return (dynconf->host); + } + + return (NULL); +} + +struct pool_conf * +create_pool(name, range) + char *name; + struct dhcp6_range *range; +{ + struct pool_conf *pool = NULL; + + if (!name || !range) { + return (NULL); + } + + dprintf(LOG_DEBUG, FNAME, "name=%s, range=%s->%s", name, + in6addr2str(&range->min, 0), in6addr2str(&range->max, 0)); + + if (in6_addr_cmp(&range->min, &range->max) >= 0) { + dprintf(LOG_ERR, FNAME, "invalid address range %s->%s", + in6addr2str(&range->min, 0), + in6addr2str(&range->max, 0)); + return (NULL); + } + + if ((pool = malloc(sizeof(struct pool_conf))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + return (NULL); + } + if ((pool->name = strdup(name)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + return (NULL); + } + pool->cur = pool->min = range->min; + pool->max = range->max; + + return (pool); +} + +struct pool_conf * +find_pool(name) + const char *name; +{ + struct pool_conf *pool = NULL; + + if (!name) + return (NULL); + + dprintf(LOG_DEBUG, FNAME, "name=%s", name); + + for (pool = pool_conflist; pool; pool = pool->next) { + if (strcmp(name, pool->name) == 0) { + dprintf(LOG_DEBUG, FNAME, "found (name=%s)", name); + return (pool); + } + } + + dprintf(LOG_DEBUG, FNAME, "not found (name=%s)", name); + + return (NULL); +} + +int +get_free_address_from_pool(pool, addr) + struct pool_conf *pool; + struct in6_addr *addr; +{ + int found = 0; + int round = 0; + struct in6_addr first; + + if (!pool || !addr) + return (0); + + dprintf(LOG_DEBUG, FNAME, "called (pool=%s)", pool->name); + + memcpy(&first, &pool->cur, sizeof(first)); + + while (!found) { + if (!is_leased(&pool->cur) && + !IN6_IS_ADDR_MULTICAST(&pool->cur) && + !IN6_IS_ADDR_LINKLOCAL(&pool->cur) && + !IN6_IS_ADDR_SITELOCAL(&pool->cur)) { + memcpy(addr, &pool->cur, sizeof(*addr)); + found = 1; + dprintf(LOG_DEBUG, FNAME, "found %s", + in6addr2str(addr, 0)); + } + + if (in6_addr_cmp(&pool->cur, &pool->max) == 0) { + memcpy(&pool->next, &pool->min, sizeof(pool->next)); + round = 1; + } else { + in6_addr_inc(&pool->cur); + } + + dprintf(LOG_DEBUG, FNAME, "next address %s", + in6addr2str(&pool->cur, 0)); + + if (round && !found && + in6_addr_cmp(&pool->cur, &first) == 0) { + dprintf(LOG_NOTICE, FNAME, "no available address"); + break; + } + } + + return (found); +} + +int +is_available_in_pool(pool, addr) + struct pool_conf *pool; + struct in6_addr *addr; +{ + if (!pool || !addr) + return (0); + + dprintf(LOG_DEBUG, FNAME, "pool=%s, addr=%s", + pool->name, in6addr2str(addr, 0)); + + if (in6_addr_cmp(addr, &pool->min) >= 0 && + in6_addr_cmp(addr, &pool->max) <= 0 && + !is_leased(addr) && + !IN6_IS_ADDR_MULTICAST(addr) && + !IN6_IS_ADDR_LINKLOCAL(addr) && + !IN6_IS_ADDR_SITELOCAL(addr)) { + return (1); + } + + dprintf(LOG_DEBUG, FNAME, "unavailable address (pool=%s, addr=%s)", + pool->name, in6addr2str(addr, 0)); + + return (0); +} + +static int +in6_addr_cmp(addr1, addr2) + struct in6_addr *addr1, *addr2; +{ + int i; + + for (i = 0; i < 16; i++) { + if (addr1->s6_addr[i] != addr2->s6_addr[i]) { + if (addr1->s6_addr[i] > addr2->s6_addr[i]) + return (1); + else + return (-1); + } + } + + return (0); +} + +static void +in6_addr_inc(addr) + struct in6_addr *addr; +{ + int i; + + for (i = 15; i >= 0; i--) { + if (++(addr->s6_addr[i]) != 0x00) + break; + } +} +#endif /* USE_POOL */ + @@ -33,6 +33,29 @@ TAILQ_HEAD(ia_conflist, ia_conf); TAILQ_HEAD(pifc_list, prefix_ifconf); +#ifdef USE_POOL +struct dhcp6_poolspec { + char* name; + u_int32_t pltime; + u_int32_t vltime; +}; + +struct dhcp6_range { + struct in6_addr min; + struct in6_addr max; +}; + +struct pool_conf { + struct pool_conf *next; + + char* name; + + struct in6_addr min; + struct in6_addr max; + struct in6_addr cur; +}; +#endif /* USE_POOL */ + /* per-interface information */ struct dhcp6_if { struct dhcp6_if *next; @@ -55,7 +78,9 @@ struct dhcp6_if { #define DHCIFF_RAPID_COMMIT 0x2 int server_pref; /* server preference (server only) */ - +#ifdef USE_POOL + struct dhcp6_poolspec pool; /* address pool (server only) */ +#endif char *scriptpath; /* path to config script (client only) */ struct dhcp6_list reqopt_list; @@ -197,6 +222,10 @@ struct host_conf { struct dhcp6_list prefix_list; /* address to be assigned for the host */ struct dhcp6_list addr_list; +#ifdef USE_POOL + /* address pool from which addresses are assigned for the host */ + struct dhcp6_poolspec pool; +#endif /* secret key shared with the client for delayed authentication */ struct keyinfo *delayedkey; @@ -241,6 +270,7 @@ struct cf_list { enum { DECL_SEND, DECL_ALLOW, DECL_INFO_ONLY, DECL_REQUEST, DECL_DUID, DECL_PREFIX, DECL_PREFERENCE, DECL_SCRIPT, DECL_DELAYEDKEY, DECL_ADDRESS, + DECL_RANGE, DECL_ADDRESSPOOL, /* USE_POOL */ IFPARAM_SLA_ID, IFPARAM_SLA_LEN, DHCPOPT_RAPID_COMMIT, DHCPOPT_AUTHINFO, DHCPOPT_DNS, DHCPOPT_DNSNAME, @@ -287,3 +317,13 @@ extern struct dhcp6_prefix *find_prefix6 __P((struct dhcp6_list *, struct dhcp6_prefix *)); extern struct ia_conf *find_iaconf __P((struct ia_conflist *, int, u_int32_t)); extern struct keyinfo *find_key __P((char *, size_t, u_int32_t)); +#ifdef USE_POOL +extern int configure_pool __P((struct cf_namelist *)); +extern struct pool_conf *find_pool __P((const char *)); +extern int is_available_in_pool __P((struct pool_conf *, struct in6_addr *)); +extern int get_free_address_from_pool __P((struct pool_conf *, + struct in6_addr *)); +struct host_conf *create_dynamic_hostconf __P((struct duid *, + struct dhcp6_poolspec *)); +#endif /* USE_POOL */ + @@ -74,6 +74,10 @@ #include <base64.h> #include <control.h> #include <dhcp6_ctl.h> +#ifdef USE_POOL +#include <signal.h> +#include <lease.h> +#endif #define DUID_FILE LOCALDBDIR "/dhcp6s_duid" #define DHCP6S_CONF SYSCONFDIR "/dhcp6s.conf" @@ -185,6 +189,10 @@ static int make_ia __P((struct dhcp6_listval *, struct dhcp6_list *, struct dhcp6_list *, struct host_conf *, int)); static int make_match_ia __P((struct dhcp6_listval *, struct dhcp6_list *, struct dhcp6_list *)); +#ifdef USE_POOL +static int make_iana_from_pool __P((struct dhcp6_poolspec *, + struct dhcp6_listval *, struct dhcp6_list *)); +#endif static void calc_ia_timo __P((struct dhcp6_ia *, struct dhcp6_list *, struct host_conf *)); static void update_binding_duration __P((struct dhcp6_binding *)); @@ -332,6 +340,12 @@ server6_init() static struct sockaddr_in6 sa6_any_relay_storage; TAILQ_INIT(&dhcp6_binding_head); +#ifdef USE_POOL + if (lease_init() != 0) { + dprintf(LOG_ERR, FNAME, "failed to initialize the lease table"); + exit(1); + } +#endif ifidx = if_nametoindex(device); if (ifidx == 0) { @@ -1239,6 +1253,14 @@ react_solicit(ifp, dh6, len, optinfo, from, fromlen, relayinfohead) struct dhcp6_list conflist; struct dhcp6_listval *iana; +#ifdef USE_POOL + if (client_conf == NULL && ifp->pool.name) { + if ((client_conf = create_dynamic_hostconf(&optinfo->clientID, + &ifp->pool)) == NULL) + dprintf(LOG_NOTICE, FNAME, + "failed to make host configuration"); + } +#endif TAILQ_INIT(&conflist); /* make a local copy of the configured addresses */ @@ -1448,6 +1470,14 @@ react_request(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead) struct dhcp6_list conflist; struct dhcp6_listval *iana; +#ifdef USE_POOL + if (client_conf == NULL && ifp->pool.name) { + if ((client_conf = create_dynamic_hostconf(&optinfo->clientID, + &ifp->pool)) == NULL) + dprintf(LOG_NOTICE, FNAME, + "failed to make host configuration"); + } +#endif TAILQ_INIT(&conflist); /* make a local copy of the configured prefixes */ @@ -2163,6 +2193,9 @@ release_binding_ia(iap, retlist, optinfo) lvia->val_prefix6.plen); break; case DHCP6_LISTVAL_IANA: +#ifdef USE_POOL + release_address(&lvia->val_prefix6.addr); +#endif dprintf(LOG_DEBUG, FNAME, "bound address %s " "has been released", @@ -2397,8 +2430,17 @@ make_ia(spec, conflist, retlist, client_conf, do_binding) * trivial case: * if the configuration is empty, we cannot make any IA. */ +#ifdef USE_POOL + if (TAILQ_EMPTY(conflist)) { + if (spec->type != DHCP6_LISTVAL_IANA || + client_conf->pool.name == NULL) { + return (0); + } + } +#else if (TAILQ_EMPTY(conflist)) return (0); +#endif /* USE_POOL */ TAILQ_INIT(&ialist); @@ -2406,10 +2448,43 @@ make_ia(spec, conflist, retlist, client_conf, do_binding) for (specia = TAILQ_FIRST(&spec->sublist); specia; specia = TAILQ_NEXT(specia, link)) { /* try to find an IA that matches the spec best. */ +#ifdef USE_POOL + if (!TAILQ_EMPTY(conflist)) { + if (make_match_ia(specia, conflist, &ialist)) + found++; + } else if (spec->type == DHCP6_LISTVAL_IANA && + client_conf->pool.name != NULL) { + if (make_iana_from_pool(&client_conf->pool, specia, &ialist)) + found++; + } +#else if (make_match_ia(specia, conflist, &ialist)) found++; +#endif /* USE_POOL */ } if (found == 0) { +#ifdef USE_POOL + if (!TAILQ_EMPTY(conflist)) { + struct dhcp6_listval *v; + + /* use the first IA in the configuration list */ + for (v = TAILQ_FIRST(conflist); v; v = TAILQ_NEXT(v, link)) { + if (spec->type != DHCP6_LISTVAL_IANA) + break; /* always use the first IA for non-IANA */ + if (!is_leased(&v->val_statefuladdr6.addr)) + break; + } + if (v && dhcp6_add_listval(&ialist, v->type, &v->uv, NULL)) { + found = 1; + TAILQ_REMOVE(conflist, v, link); + dhcp6_clear_listval(v); + } + } else if (spec->type == DHCP6_LISTVAL_IANA && + client_conf->pool.name != NULL) { + if (make_iana_from_pool(&client_conf->pool, NULL, &ialist)) + found = 1; + } +#else struct dhcp6_listval *v; /* use the first IA in the configuration list */ @@ -2419,6 +2494,7 @@ make_ia(spec, conflist, retlist, client_conf, do_binding) TAILQ_REMOVE(conflist, v, link); dhcp6_clear_listval(v); } +#endif /* USE_POOL */ } if (found) { memset(&ia, 0, sizeof(ia)); @@ -2467,6 +2543,10 @@ make_match_ia(spec, conflist, retlist) break; case DHCP6_LISTVAL_STATEFULADDR6: /* No "partial match" for addresses */ +#ifdef USE_POOL + if (is_leased(&spec->val_statefuladdr6.addr)) + match = 0; +#endif break; default: dprintf(LOG_ERR, FNAME, "unsupported IA type"); @@ -2490,6 +2570,52 @@ make_match_ia(spec, conflist, retlist) return (matched); } +#ifdef USE_POOL +/* making sublist of iana */ +static int +make_iana_from_pool(poolspec, spec, retlist) + struct dhcp6_poolspec *poolspec; + struct dhcp6_listval *spec; + struct dhcp6_list *retlist; +{ + struct dhcp6_statefuladdr saddr; + struct pool_conf *pool; + int found = 0; + + dprintf(LOG_DEBUG, FNAME, "called"); + + if ((pool = find_pool(poolspec->name)) == NULL) { + dprintf(LOG_ERR, FNAME, "pool '%s' not found", poolspec->name); + return (0); + } + + if (spec) { + memcpy(&saddr.addr, &spec->val_statefuladdr6.addr, sizeof(saddr.addr)); + if (is_available_in_pool(pool, &saddr.addr)) { + found = 1; + } + } else { + if (get_free_address_from_pool(pool, &saddr.addr)) { + found = 1; + } + } + + if (found) { + saddr.pltime = poolspec->pltime; + saddr.vltime = poolspec->vltime; + + if (!dhcp6_add_listval(retlist, DHCP6_LISTVAL_STATEFULADDR6, + &saddr, NULL)) { + return (0); + } + } + + dprintf(LOG_DEBUG, FNAME, "returns (found=%d)", found); + + return (found); +} +#endif /* USE_POOL */ + static void calc_ia_timo(ia, ialist, client_conf) struct dhcp6_ia *ia; @@ -2629,6 +2755,35 @@ add_binding(clientid, btype, iatype, iaid, val0) "failed to copy binding data"); goto fail; } +#ifdef USE_POOL + /* lease address */ + if (iatype == DHCP6_LISTVAL_IANA) { + struct dhcp6_list *ia_list = &binding->val_list; + struct dhcp6_listval *lv, *lv_next; + + for (lv = TAILQ_FIRST(ia_list); lv; lv = lv_next) { + lv_next = TAILQ_NEXT(lv, link); + + if (lv->type != DHCP6_LISTVAL_STATEFULADDR6) { + dprintf(LOG_ERR, FNAME, + "unexpected binding value type(%d)", lv->type); + continue; + } + + if (!lease_address(&lv->val_statefuladdr6.addr)) { + dprintf(LOG_NOTICE, FNAME, + "cannot lease address %s", + in6addr2str(&lv->val_statefuladdr6.addr, 0)); + TAILQ_REMOVE(ia_list, lv, link); + dhcp6_clear_listval(lv); + } + } + if (TAILQ_EMPTY(ia_list)) { + dprintf(LOG_NOTICE, FNAME, "cannot lease any address"); + goto fail; + } + } +#endif /* USE_POOL */ break; default: dprintf(LOG_ERR, FNAME, "unexpected binding type(%d)", btype); @@ -2734,6 +2889,22 @@ free_binding(binding) /* free configuration info in a type dependent manner. */ switch (binding->type) { case DHCP6_BINDING_IA: +#ifdef USE_POOL + /* releaes address */ + if (binding->iatype == DHCP6_LISTVAL_IANA) { + struct dhcp6_list *ia_list = &binding->val_list; + struct dhcp6_listval *lv; + + for (lv = TAILQ_FIRST(ia_list); lv; lv = TAILQ_NEXT(lv, link)) { + if (lv->type != DHCP6_LISTVAL_STATEFULADDR6) { + dprintf(LOG_ERR, FNAME, + "unexpected binding value type(%d)", lv->type); + continue; + } + release_address(&lv->val_statefuladdr6.addr); + } + } +#endif /* USE_POOL */ dhcp6_clear_list(&binding->val_list); break; default: @@ -2783,6 +2954,10 @@ binding_timo(arg) in6addr2str(&iav->val_prefix6.addr, 0), iav->val_prefix6.plen, bindingstr(binding)); +#ifdef USE_POOL + if (binding->iatype == DHCP6_LISTVAL_IANA) + release_address(&iav->val_prefix6.addr); +#endif TAILQ_REMOVE(ia_list, iav, link); dhcp6_clear_listval(iav); } @@ -0,0 +1,257 @@ +#include <sys/types.h> +#include <unistd.h> +#include <syslog.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <netinet/in.h> +#include "dhcp6.h" +#include "config.h" +#include "common.h" + +#ifndef FALSE +#define FALSE 0 +#define TRUE !FALSE +#endif + +struct hash_entry { + LIST_ENTRY(hash_entry) list; + char val[]; +}; + +LIST_HEAD(hash_head, hash_entry); + +typedef unsigned int (*pfn_hash_t)(void *val) ; +typedef int (*pfh_hash_match_t)(void *val1, void *val2); + +struct hash_table { + struct hash_head *table; + unsigned int size; + pfn_hash_t hash; + pfh_hash_match_t match; +}; + +#ifndef DHCP6_LEASE_TABLE_SIZE +#define DHCP6_LEASE_TABLE_SIZE 256 +#endif + +static struct hash_table dhcp6_lease_table; + +static unsigned int in6_addr_hash __P((void *)); +static int in6_addr_match __P((void *, void *)); + +static int hash_table_init __P((struct hash_table *, unsigned int, + pfn_hash_t, pfh_hash_match_t)); +static void hash_table_cleanup __P((struct hash_table *)); +static int hash_table_add __P((struct hash_table *, void *, unsigned int)); +static int hash_table_remove __P((struct hash_table *, void *)); +static struct hash_entry * hash_table_find __P((struct hash_table *, void *)); + +int +lease_init(void) +{ + dprintf(LOG_DEBUG, FNAME, "called"); + + if (hash_table_init(&dhcp6_lease_table, DHCP6_LEASE_TABLE_SIZE, + in6_addr_hash, in6_addr_match) != 0) { + return (-1); + } + + return (0); +} + +void +lease_cleanup(void) +{ + hash_table_cleanup(&dhcp6_lease_table); +} + +int +lease_address(addr) + struct in6_addr *addr; +{ + if (!addr) + return (FALSE); + + dprintf(LOG_DEBUG, FNAME, "addr=%s", in6addr2str(addr, 0)); + + if (hash_table_find(&dhcp6_lease_table, addr)) { + dprintf(LOG_WARNING, FNAME, "already leased: %s", + in6addr2str(addr, 0)); + return (FALSE); + } + + if (hash_table_add(&dhcp6_lease_table, addr, sizeof(*addr)) != 0) { + return (FALSE); + } + + return (TRUE); +} + +void +release_address(addr) + struct in6_addr *addr; +{ + if (!addr) + return; + + dprintf(LOG_DEBUG, FNAME, "addr=%s", in6addr2str(addr, 0)); + + if (hash_table_remove(&dhcp6_lease_table, addr) != 0) { + dprintf(LOG_WARNING, FNAME, "not found: %s", in6addr2str(addr, 0)); + } +} + +int +is_leased(addr) + struct in6_addr *addr; +{ + return (hash_table_find(&dhcp6_lease_table, addr) != NULL); +} + +static unsigned int +in6_addr_hash(val) + void *val; +{ + u_int8_t *addr = ((struct in6_addr *)val)->s6_addr; + unsigned int hash = 0; + int i; + + for (i = 0; i < 16; i++) { + hash += addr[i]; + } + + return (hash); +} + +static int +in6_addr_match(val1, val2) + void *val1, *val2; +{ + struct in6_addr * addr1 = val1; + struct in6_addr * addr2 = val2; + + return (memcmp(addr1->s6_addr, addr2->s6_addr, 16) == 0); +} + +/* + * hash table + */ +static int +hash_table_init(table, size, hash, match) + struct hash_table *table; + unsigned int size; + pfn_hash_t hash; + pfh_hash_match_t match; +{ + int i; + + if (!table || !hash || !match) { + return (-1); + } + + if ((table->table = malloc(sizeof(*table->table) * size)) == NULL) { + return (-1); + } + + for (i = 0; i < size; i++) + LIST_INIT(&table->table[i]); + + table->size = size; + table->hash = hash; + table->match = match; + + return (0); +} + +static void +hash_table_cleanup(table) + struct hash_table *table; +{ + int i; + + if (!table) { + return; + } + + for (i = 0; i < table->size; i++) { + while (!LIST_EMPTY(&table->table[i])) { + struct hash_entry *entry = LIST_FIRST(&table->table[i]); + LIST_REMOVE(entry, list); + free(entry); + } + } + free(table->table); + memset(table, 0, sizeof(*table)); +} + +static int +hash_table_add(table, val, size) + struct hash_table *table; + void *val; + unsigned int size; +{ + struct hash_entry *entry = NULL; + int i = 0; + + if (!table || !val) { + return (-1); + } + + if ((entry = malloc(sizeof(*entry) + size)) == NULL) { + return (-1); + } + memset(entry, 0, sizeof(*entry)); + memcpy(entry->val, val, size); + + i = table->hash(val) % table->size; + LIST_INSERT_HEAD(&table->table[i], entry, list); + + return (0); +} + +static int +hash_table_remove(table, val) + struct hash_table *table; + void *val; +{ + struct hash_entry *entry; + + if (!table || !val) { + return (-1); + } + + if ((entry = hash_table_find(table, val)) == NULL) { + return (-1); + } + + LIST_REMOVE(entry, list); + free(entry); + + return (0); +} + +static struct hash_entry * +hash_table_find(table, val) + struct hash_table *table; + void *val; +{ + struct hash_entry *entry; + int i; + + if (!table || !val) { + return (NULL); + } + + i = table->hash(val) % table->size; + LIST_FOREACH(entry, &table->table[i], list) + { + if (table->match(val, entry->val)) { + return (entry); + } + } + + return (NULL); +} + @@ -0,0 +1,39 @@ +/* + * Copyright (C) 1998 and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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 __LEASE_H_DEFINED +#define __LEASE_H_DEFINED + +extern int lease_init __P((void)); +extern void lease_cleanup __P((void)); +extern int lease_address __P((struct in6_addr *)); +extern void release_address __P((struct in6_addr *)); +extern int is_addr_leased __P((struct in6_addr *)); + +#endif |