diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:43:02 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:43:02 +0000 |
commit | f2c49875c1ce6e1d3c51968f7b058b1b0c55428c (patch) | |
tree | 233b80410596436a63c870ab8e22e4e828867515 /config.c | |
parent | Initial commit. (diff) | |
download | wide-dhcpv6-upstream/20080615.tar.xz wide-dhcpv6-upstream/20080615.zip |
Adding upstream version 20080615.upstream/20080615upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'config.c')
-rw-r--r-- | config.c | 2233 |
1 files changed, 2233 insertions, 0 deletions
diff --git a/config.c b/config.c new file mode 100644 index 0000000..383c12a --- /dev/null +++ b/config.c @@ -0,0 +1,2233 @@ +/* $KAME: config.c,v 1.53 2005/09/16 11:30:14 suz Exp $ */ + +/* + * Copyright (C) 2002 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. + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <netinet/in.h> +#ifdef __KAME__ +#include <net/if_dl.h> +#endif +#ifdef __linux__ +#include <linux/if_packet.h> +#endif + +#include <syslog.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ifaddrs.h> +#include <errno.h> +#ifdef __linux__ +#define __USE_XOPEN +#include <time.h> +#endif + +#include <dhcp6.h> +#include <config.h> +#include <common.h> +#include <auth.h> +#include <base64.h> +#include <lease.h> + +extern int errno; + +struct prefix_ifconf *prefix_ifconflist; +struct dhcp6_list siplist, sipnamelist, dnslist, dnsnamelist, ntplist; +struct dhcp6_list nislist, nisnamelist; +struct dhcp6_list nisplist, nispnamelist; +struct dhcp6_list bcmcslist, bcmcsnamelist; +long long optrefreshtime; + +static struct dhcp6_ifconf *dhcp6_ifconflist; +struct ia_conflist ia_conflist0; +static struct host_conf *host_conflist0, *host_conflist; +static struct keyinfo *key_list, *key_list0; +static struct authinfo *auth_list, *auth_list0; +static struct dhcp6_list siplist0, sipnamelist0, dnslist0, dnsnamelist0, ntplist0; +static struct dhcp6_list nislist0, nisnamelist0; +static struct dhcp6_list nisplist0, nispnamelist0; +static struct dhcp6_list bcmcslist0, bcmcsnamelist0; +static long long optrefreshtime0 = -1; +#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; + +enum { DHCPOPTCODE_SEND, DHCPOPTCODE_REQUEST, DHCPOPTCODE_ALLOW }; + +/* temporary configuration structure for DHCP interface */ +struct dhcp6_ifconf { + struct dhcp6_ifconf *next; + + char *ifname; + + /* configuration flags */ + u_long send_flags; + u_long allow_flags; + + int server_pref; /* server preference (server only) */ + + char *scriptpath; /* path to config script (client only) */ + + struct dhcp6_list reqopt_list; + struct ia_conflist iaconf_list; + + struct authinfo *authinfo; /* authentication information + * (no need to clear) */ + struct dhcp6_poolspec pool; +}; + +extern struct cf_list *cf_dns_list, *cf_dns_name_list, *cf_ntp_list; +extern struct cf_list *cf_sip_list, *cf_sip_name_list; +extern struct cf_list *cf_nis_list, *cf_nis_name_list; +extern struct cf_list *cf_nisp_list, *cf_nisp_name_list; +extern struct cf_list *cf_bcmcs_list, *cf_bcmcs_name_list; +extern long long cf_refreshtime; +extern char *configfilename; + +static struct keyinfo *find_keybyname __P((struct keyinfo *, char *)); +static int add_pd_pif __P((struct iapd_conf *, struct cf_list *)); +static int add_options __P((int, struct dhcp6_ifconf *, struct cf_list *)); +static int add_prefix __P((struct dhcp6_list *, char *, int, + struct dhcp6_prefix *)); +static void clear_pd_pif __P((struct iapd_conf *)); +static void clear_ifconf __P((struct dhcp6_ifconf *)); +static void clear_iaconf __P((struct ia_conflist *)); +static void clear_hostconf __P((struct host_conf *)); +static void clear_keys __P((struct keyinfo *)); +static void clear_authinfo __P((struct authinfo *)); +static int configure_duid __P((char *, struct duid *)); +static int configure_addr __P((struct cf_list *, struct dhcp6_list *, char *)); +static int configure_domain __P((struct cf_list *, struct dhcp6_list *, char *)); +static int get_default_ifid __P((struct prefix_ifconf *)); +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 *)); + +int +configure_interface(iflist) + struct cf_namelist *iflist; +{ + struct cf_namelist *ifp; + struct dhcp6_ifconf *ifc; + char *cp; + + for (ifp = iflist; ifp; ifp = ifp->next) { + struct cf_list *cfl; + + if (if_nametoindex(ifp->name) == 0) { + dprintf(LOG_ERR, FNAME, "invalid interface(%s): %s", + ifp->name, strerror(errno)); + goto bad; + } + + if ((ifc = malloc(sizeof(*ifc))) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation for %s failed", ifp->name); + goto bad; + } + memset(ifc, 0, sizeof(*ifc)); + ifc->next = dhcp6_ifconflist; + dhcp6_ifconflist = ifc; + + if ((ifc->ifname = strdup(ifp->name)) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to copy ifname"); + goto bad; + } + + ifc->server_pref = DH6OPT_PREF_UNDEF; + TAILQ_INIT(&ifc->reqopt_list); + TAILQ_INIT(&ifc->iaconf_list); + + for (cfl = ifp->params; cfl; cfl = cfl->next) { + switch(cfl->type) { + case DECL_REQUEST: + if (dhcp6_mode != DHCP6_MODE_CLIENT) { + dprintf(LOG_INFO, FNAME, "%s:%d " + "client-only configuration", + configfilename, + cfl->line); + goto bad; + } + if (add_options(DHCPOPTCODE_REQUEST, + ifc, cfl->list)) { + goto bad; + } + break; + case DECL_SEND: + if (add_options(DHCPOPTCODE_SEND, + ifc, cfl->list)) { + goto bad; + } + break; + case DECL_ALLOW: + if (add_options(DHCPOPTCODE_ALLOW, + ifc, cfl->list)) { + goto bad; + } + break; + case DECL_INFO_ONLY: + if (dhcp6_mode != DHCP6_MODE_CLIENT) { + dprintf(LOG_INFO, FNAME, "%s:%d " + "client-only configuration", + configfilename, cfl->line); + goto bad; + } + ifc->send_flags |= DHCIFF_INFO_ONLY; + break; + case DECL_PREFERENCE: + if (dhcp6_mode != DHCP6_MODE_SERVER) { + dprintf(LOG_INFO, FNAME, "%s:%d " + "server-only configuration", + configfilename, cfl->line); + goto bad; + } + ifc->server_pref = (int)cfl->num; + if (ifc->server_pref < 0 || + ifc->server_pref > 255) { + dprintf(LOG_INFO, FNAME, "%s:%d " + "bad value: %d", + configfilename, cfl->line, + ifc->server_pref); + goto bad; + } + break; + case DECL_SCRIPT: + if (dhcp6_mode != DHCP6_MODE_CLIENT) { + dprintf(LOG_INFO, FNAME, "%s:%d " + "client-only configuration", + configfilename, cfl->line); + goto bad; + } + if (ifc->scriptpath) { + dprintf(LOG_INFO, FNAME, + "%s:%d duplicated configuration", + configfilename, cfl->line); + goto bad; + } + cp = cfl->ptr; + ifc->scriptpath = strdup(cp + 1); + if (ifc->scriptpath == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to copy script path"); + goto bad; + } + cp = ifc->scriptpath; + if (*cp != '/') { + dprintf(LOG_INFO, FNAME, + "script must be an absolute path"); + goto bad; + } + cp += strlen(ifc->scriptpath) - 1; + *cp = '\0'; /* clear the terminating quote */ + break; + 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_DURATION_INFINITE && + (spec->pltime == DHCP6_DURATION_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; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid interface configuration", + configfilename, cfl->line); + goto bad; + } + } + } + + return (0); + + bad: + clear_ifconf(dhcp6_ifconflist); + dhcp6_ifconflist = NULL; + return (-1); +} + +int +configure_ia(ialist, iatype) + struct cf_namelist *ialist; + iatype_t iatype; +{ + struct cf_namelist *iap; + struct ia_conf *iac = NULL; + size_t confsize; + static int init = 1; + + if (init) { + TAILQ_INIT(&ia_conflist0); + init = 0; + } + + switch(iatype) { + case IATYPE_PD: + confsize = sizeof(struct iapd_conf); + break; + case IATYPE_NA: + confsize = sizeof(struct iana_conf); + break; + default: + dprintf(LOG_ERR, FNAME, "internal error"); + goto bad; + } + + for (iap = ialist; iap; iap = iap->next) { + struct cf_list *cfl; + + if ((iac = malloc(confsize)) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation for IA %s failed", + iap->name); + goto bad; + } + memset(iac, 0, confsize); + + /* common initialization */ + iac->type = iatype; + iac->iaid = (u_int32_t)atoi(iap->name); + TAILQ_INIT(&iac->iadata); + TAILQ_INSERT_TAIL(&ia_conflist0, iac, link); + + /* IA-type specific initialization */ + switch(iatype) { + case IATYPE_PD: + TAILQ_INIT(&((struct iapd_conf *)iac)->iapd_prefix_list); + TAILQ_INIT(&((struct iapd_conf *)iac)->iapd_pif_list); + break; + case IATYPE_NA: + TAILQ_INIT(&((struct iana_conf *)iac)->iana_address_list); + break; + } + + /* set up parameters for the IA */ + for (cfl = iap->params; cfl; cfl = cfl->next) { + struct iapd_conf *pdp = (struct iapd_conf *) iac; + struct iana_conf *nap = (struct iana_conf *) iac; + + switch (iatype) { + case IATYPE_PD: + switch(cfl->type) { + case IACONF_PIF: + if (add_pd_pif(pdp, cfl)) + goto bad; + break; + case IACONF_PREFIX: + if (add_prefix(&pdp->iapd_prefix_list, + "IAPD", DHCP6_LISTVAL_PREFIX6, + cfl->ptr)) { + dprintf(LOG_NOTICE, FNAME, "failed " + "to configure prefix"); + goto bad; + } + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid configuration", + configfilename, cfl->line); + goto bad; + } + break; + case IATYPE_NA: + switch(cfl->type) { + case IACONF_ADDR: + if (add_prefix(&nap->iana_address_list, + "IANA", DHCP6_LISTVAL_STATEFULADDR6, + cfl->ptr)) { + dprintf(LOG_NOTICE, FNAME, "failed " + "to configure address"); + goto bad; + } + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid configuration", + configfilename, cfl->line); + goto bad; + } + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid iatype %d", + configfilename, cfl->line, iatype); + goto bad; + } + } + } + + return (0); + + bad: + return (-1); +} + +static int +add_pd_pif(iapdc, cfl0) + struct iapd_conf *iapdc; + struct cf_list *cfl0; +{ + struct cf_list *cfl; + struct prefix_ifconf *pif; + + /* duplication check */ + for (pif = TAILQ_FIRST(&iapdc->iapd_pif_list); pif; + pif = TAILQ_NEXT(pif, link)) { + if (strcmp(pif->ifname, cfl0->ptr) == 0) { + dprintf(LOG_NOTICE, FNAME, "%s:%d " + "duplicated prefix interface: %s", + configfilename, cfl0->line, cfl0->ptr); + return (0); /* ignore it */ + } + } + + if ((pif = malloc(sizeof(*pif))) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation for %s failed", cfl0->ptr); + goto bad; + } + memset(pif, 0, sizeof(*pif)); + + /* validate and copy ifname */ + if (if_nametoindex(cfl0->ptr) == 0) { + dprintf(LOG_ERR, FNAME, "%s:%d invalid interface (%s): %s", + configfilename, cfl0->line, + cfl0->ptr, strerror(errno)); + goto bad; + } + if ((pif->ifname = strdup(cfl0->ptr)) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to copy ifname"); + goto bad; + } + + pif->ifid_len = IFID_LEN_DEFAULT; + pif->sla_len = SLA_LEN_DEFAULT; + if (get_default_ifid(pif)) { + dprintf(LOG_NOTICE, FNAME, + "failed to get default IF ID for %s", pif->ifname); + goto bad; + } + + for (cfl = cfl0->list; cfl; cfl = cfl->next) { + switch(cfl->type) { + case IFPARAM_SLA_ID: + pif->sla_id = (u_int32_t)cfl->num; + break; + case IFPARAM_SLA_LEN: + pif->sla_len = (int)cfl->num; + if (pif->sla_len < 0 || pif->sla_len > 128) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid SLA length: %d", + configfilename, cfl->line, pif->sla_len); + goto bad; + } + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d internal error: " + "invalid configuration", + configfilename, cfl->line); + goto bad; + } + } + + TAILQ_INSERT_TAIL(&iapdc->iapd_pif_list, pif, link); + return (0); + + bad: + if (pif->ifname) + free(pif->ifname); + free(pif); + return (-1); +} + +int +configure_host(hostlist) + struct cf_namelist *hostlist; +{ + struct cf_namelist *host; + struct host_conf *hconf; + + for (host = hostlist; host; host = host->next) { + struct cf_list *cfl; + + if ((hconf = malloc(sizeof(*hconf))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed " + "for host %s", host->name); + goto bad; + } + memset(hconf, 0, sizeof(*hconf)); + TAILQ_INIT(&hconf->prefix_list); + TAILQ_INIT(&hconf->addr_list); + hconf->next = host_conflist0; + host_conflist0 = hconf; + + if ((hconf->name = strdup(host->name)) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to copy host name: %s", + host->name); + goto bad; + } + + for (cfl = host->params; cfl; cfl = cfl->next) { + switch(cfl->type) { + case DECL_DUID: + if (hconf->duid.duid_id) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "duplicated DUID for %s", + configfilename, + cfl->line, host->name); + goto bad; + } + if ((configure_duid((char *)cfl->ptr, + &hconf->duid)) != 0) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "failed to configure " + "DUID for %s", + configfilename, cfl->line, + host->name); + goto bad; + } + dprintf(LOG_DEBUG, FNAME, + "configure DUID for %s: %s", + host->name, duidstr(&hconf->duid)); + break; + case DECL_PREFIX: + if (add_prefix(&hconf->prefix_list, + hconf->name, DHCP6_LISTVAL_PREFIX6, + cfl->ptr)) { + dprintf(LOG_ERR, FNAME, "failed " + "to configure prefix for %s", + host->name); + goto bad; + } + break; + case DECL_ADDRESS: + if (add_prefix(&hconf->addr_list, + hconf->name, DHCP6_LISTVAL_STATEFULADDR6, + cfl->ptr)) { + dprintf(LOG_ERR, FNAME, "failed " + "to configure address for %s", + host->name); + goto bad; + } + break; + case DECL_DELAYEDKEY: + if (hconf->delayedkey != NULL) { + dprintf(LOG_WARNING, FNAME, + "%s:%d: duplicate key %s for %s" + " (ignored)", configfilename, + cfl->line, cfl->ptr, host->name); + continue; + } + if ((hconf->delayedkey = + find_keybyname(key_list0, cfl->ptr)) + == NULL) { + dprintf(LOG_ERR, FNAME, "failed to " + "find key information for %s", + cfl->ptr); + goto bad; + } + dprintf(LOG_DEBUG, FNAME, "configure key for " + "delayed auth with %s (keyid=%08x)", + host->name, hconf->delayedkey->keyid); + break; + 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_DURATION_INFINITE && + (spec->pltime == DHCP6_DURATION_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; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid host configuration for %s", + configfilename, cfl->line, + host->name); + goto bad; + } + } + } + + return (0); + + bad: + /* there is currently nothing special to recover the error */ + return (-1); +} + +int +configure_keys(keylist) + struct cf_namelist *keylist; +{ + struct cf_namelist *key; + char *secretstr; + char secret[1024]; + int secretlen; + struct keyinfo *kinfo; + long long keyid; + char *expire = NULL; + + for (key = keylist; key; key = key->next) { + struct cf_list *cfl; + + if ((kinfo = malloc(sizeof(*kinfo))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed " + "for key %s", key->name); + goto bad; + } + memset(kinfo, 0, sizeof(*kinfo)); + kinfo->next = key_list0; + key_list0 = kinfo; + + if ((kinfo->name = strdup(key->name)) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to copy key name: %s", + key->name); + goto bad; + } + + keyid = -1; + expire = NULL; + for (cfl = key->params; cfl; cfl = cfl->next) { + switch (cfl->type) { + case KEYPARAM_REALM: + if (kinfo->realm != NULL) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate realm for key %s " + "(ignored)", configfilename, + cfl->line, key->name); + continue; + } + kinfo->realm = qstrdup(cfl->ptr); + if (kinfo->realm == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to allocate memory for " + "realm"); + goto bad; + } + kinfo->realmlen = strlen(kinfo->realm); + break; + case KEYPARAM_KEYID: + if (keyid != -1) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate realm for key %s " + "(ignored)", + configfilename, cfl->line); + continue; + } + keyid = cfl->num; + if (keyid < 0 || keyid > 0xffffffff) { + dprintf(LOG_WARNING, FNAME, + "%s:%d key ID overflow", + configfilename, cfl->line); + goto bad; + } + break; + case KEYPARAM_SECRET: + /* duplicate check */ + if (kinfo->secret != NULL) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate secret " + "for key %s (ignored)", + configfilename, cfl->line, + key->name); + continue; /* ignored */ + } + + /* convert base64 string to binary secret */ + if ((secretstr = qstrdup(cfl->ptr)) == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to make a copy of secret"); + goto bad; + } + memset(secret, 0, sizeof(secret)); + secretlen = base64_decodestring(secretstr, + secret, sizeof(secret)); + if (secretlen < 0) { + dprintf(LOG_ERR, FNAME, + "%s:%d failed to parse base64 key", + configfilename, cfl->line); + free(secretstr); + goto bad; + } + free(secretstr); + + /* set the binary secret */ + kinfo->secret = malloc(secretlen); + if (kinfo->secret == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to allocate memory " + "for secret"); + goto bad; + } + memcpy(kinfo->secret, secret, secretlen); + kinfo->secretlen = secretlen; + break; + case KEYPARAM_EXPIRE: + if (expire != NULL) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate expire for key " + "%s (ignored)", configfilename, + cfl->line, key->name); + continue; + } + expire = qstrdup(cfl->ptr); + break; + default: + dprintf(LOG_ERR, FNAME, + "%s:%d invalid key parameter for %s", + configfilename, cfl->line, key->name); + goto bad; + } + } + + /* check for mandatory parameters or use default */ + if (kinfo->realm == NULL) { + dprintf(LOG_ERR, FNAME, + "realm not specified for key %s", key->name); + goto bad; + } + if (keyid == -1) { + dprintf(LOG_ERR, FNAME, + "key ID not specified for key %s", key->name); + goto bad; + } + kinfo->keyid = keyid; + if (kinfo->secret == NULL) { + dprintf(LOG_ERR, FNAME, + "secret not specified for key %s", key->name); + goto bad; + } + kinfo->expire = 0; + if (expire != NULL) { + if (strcmp(expire, "forever") != 0) { + time_t now, expire_time; + struct tm *lt; + + if (time(&now) == -1) { + dprintf(LOG_ERR, FNAME, "cannot get " + "current time: %s", + strerror(errno)); + goto bad; + } + lt = localtime(&now); + lt->tm_sec = 0; + + if (strptime(expire, "%Y-%m-%d %H:%M", lt) + == NULL && + strptime(expire, "%m-%d %H:%M", lt) + == NULL && + strptime(expire, "%H:%M", lt) == NULL) { + dprintf(LOG_ERR, FNAME, "invalid " + "expiration time: %s"); + goto bad; + } + + expire_time = mktime(lt); + if (expire_time < now) { + dprintf(LOG_ERR, FNAME, "past " + "expiration time specified: %s", + expire); + goto bad; + } + + kinfo->expire = expire_time; + } + } + } + + return (0); + + bad: + if (expire != NULL) + free(expire); + return (-1); +} + +static struct keyinfo * +find_keybyname(head, kname) + struct keyinfo *head; + char *kname; +{ + struct keyinfo *kinfo; + + for (kinfo = head; kinfo != NULL; kinfo = kinfo->next) { + if (strcmp(kname, kinfo->name) == 0) + return (kinfo); + } + + return (NULL); +} + +int +configure_authinfo(authlist) + struct cf_namelist *authlist; +{ + struct cf_namelist *auth; + struct authinfo *ainfo; + + for (auth = authlist; auth; auth = auth->next) { + struct cf_list *cfl; + + if ((ainfo = malloc(sizeof(*ainfo))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed " + "for auth info %s", auth->name); + goto bad; + } + memset(ainfo, 0, sizeof(*ainfo)); + ainfo->next = auth_list0; + auth_list0 = ainfo; + ainfo->protocol = DHCP6_AUTHPROTO_UNDEF; + ainfo->algorithm = DHCP6_AUTHALG_UNDEF; + ainfo->rdm = DHCP6_AUTHRDM_UNDEF; + + if ((ainfo->name = strdup(auth->name)) == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to copy auth info name: %s", auth->name); + goto bad; + } + + for (cfl = auth->params; cfl; cfl = cfl->next) { + switch (cfl->type) { + case AUTHPARAM_PROTO: + if (ainfo->protocol != DHCP6_AUTHPROTO_UNDEF) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate protocol " + "for auth info %s " + "(ignored)", + configfilename, cfl->line, + auth->name); + continue; /* ignored */ + } + ainfo->protocol = (int)cfl->num; + break; + case AUTHPARAM_ALG: + if (ainfo->algorithm != DHCP6_AUTHALG_UNDEF) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate algorithm " + "for auth info %s " + "(ignored)", + configfilename, cfl->line, + auth->name); + continue; /* ignored */ + } + ainfo->algorithm = (int)cfl->num; + break; + case AUTHPARAM_RDM: + if (ainfo->rdm != DHCP6_AUTHRDM_UNDEF) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate RDM " + "for auth info %s " + "(ignored)", + configfilename, cfl->line, + auth->name); + continue; /* ignored */ + } + ainfo->rdm = (int)cfl->num; + break; + case AUTHPARAM_KEY: + dprintf(LOG_WARNING, FNAME, + "%s:%d auth info specific keys " + "are not supported", + configfilename, cfl->line); + break; + default: + dprintf(LOG_ERR, FNAME, + "%s:%d invalid auth info parameter for %s", + configfilename, cfl->line, auth->name); + goto bad; + } + } + + /* check for mandatory parameters and consistency */ + switch (ainfo->protocol) { + case DHCP6_AUTHPROTO_UNDEF: + dprintf(LOG_ERR, FNAME, + "auth protocol is not specified for %s", + auth->name); + goto bad; + case DHCP6_AUTHPROTO_DELAYED: + if (dhcp6_mode != DHCP6_MODE_CLIENT) { + dprintf(LOG_ERR, FNAME, + "client-only auth protocol is specified"); + goto bad; + } + break; + case DHCP6_AUTHPROTO_RECONFIG: + if (dhcp6_mode != DHCP6_MODE_SERVER) { + dprintf(LOG_ERR, FNAME, + "server-only auth protocol is specified"); + goto bad; + } + break; + } + if (ainfo->algorithm == DHCP6_AUTHALG_UNDEF) + ainfo->algorithm = DHCP6_AUTHALG_HMACMD5; + if (ainfo->rdm == DHCP6_AUTHRDM_UNDEF) + ainfo->rdm = DHCP6_AUTHRDM_MONOCOUNTER; + } + + return (0); + + bad: + /* there is currently nothing special to recover the error */ + return (-1); +} + +int +configure_global_option() +{ + /* SIP Server address */ + if (configure_addr(cf_sip_list, &siplist0, "SIP") < 0) + goto bad; + + /* SIP domain name */ + if (configure_domain(cf_sip_name_list, &sipnamelist0, "SIP") < 0) + goto bad; + + /* DNS servers */ + if (configure_addr(cf_dns_list, &dnslist0, "DNS") < 0) + goto bad; + + /* DNS name */ + if (configure_domain(cf_dns_name_list, &dnsnamelist0, "DNS") < 0) + goto bad; + + /* NTP servers */ + if (configure_addr(cf_ntp_list, &ntplist0, "NTP") < 0) + goto bad; + + /* NIS Server address */ + if (configure_addr(cf_nis_list, &nislist0, "NIS") < 0) + goto bad; + + /* NIS domain name */ + if (configure_domain(cf_nis_name_list, &nisnamelist0, "NIS") < 0) + goto bad; + + /* NIS+ Server address */ + if (configure_addr(cf_nisp_list, &nisplist0, "NISP") < 0) + goto bad; + + /* NIS+ domain name */ + if (configure_domain(cf_nisp_name_list, &nispnamelist0, "NISP") < 0) + goto bad; + + /* BCMCS Server address */ + if (configure_addr(cf_bcmcs_list, &bcmcslist0, "BCMCS") < 0) + goto bad; + + /* BCMCS domain name */ + if (configure_domain(cf_bcmcs_name_list, &bcmcsnamelist0, "BCMCS") < 0) + goto bad; + + /* Lifetime for stateless options */ + if (cf_refreshtime >= 0) { + optrefreshtime0 = cf_refreshtime; + } + + return (0); + + bad: + return (-1); /* no need to free intermediate list */ +} + +static int +configure_addr(cf_addr_list, list0, optname) + struct cf_list *cf_addr_list; + struct dhcp6_list *list0; + char *optname; +{ + struct cf_list *cl; + + /* check against configuration restriction */ + if (cf_addr_list != NULL && dhcp6_mode != DHCP6_MODE_SERVER) { + dprintf(LOG_INFO, FNAME, "%s:%d server-only configuration", + configfilename, cf_addr_list->line); + return -1; + } + + TAILQ_INIT(list0); + for (cl = cf_addr_list; cl; cl = cl->next) { + /* duplication check */ + if (dhcp6_find_listval(list0, DHCP6_LISTVAL_ADDR6, + cl->ptr, 0)) { + dprintf(LOG_INFO, FNAME, + "%s:%d duplicated %s server: %s", + configfilename, cl->line, + optname, + in6addr2str((struct in6_addr *)cl->ptr, 0)); + return -1; + } + if (dhcp6_add_listval(list0, DHCP6_LISTVAL_ADDR6, + cl->ptr, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to add a %s server", + optname); + return -1; + } + } + + return 0; +} + +static int +configure_domain(cf_name_list, list0, optname) + struct cf_list *cf_name_list; + struct dhcp6_list *list0; + char *optname; +{ + struct cf_list *cl; + + /* check against configuration restriction */ + if (cf_name_list != NULL && dhcp6_mode != DHCP6_MODE_SERVER) { + dprintf(LOG_INFO, FNAME, "%s:%d server-only configuration", + configfilename, cf_name_list->line); + return -1; + } + + TAILQ_INIT(list0); + for (cl = cf_name_list; cl; cl = cl->next) { + char *name, *cp; + struct dhcp6_vbuf name_vbuf; + + name = strdup(cl->ptr + 1); + if (name == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to copy a %s domain name", + optname); + return -1; + } + cp = name + strlen(name) - 1; + *cp = '\0'; /* clear the terminating quote */ + + name_vbuf.dv_buf = name; + name_vbuf.dv_len = strlen(name) + 1; + + /* duplication check */ + if (dhcp6_find_listval(list0, DHCP6_LISTVAL_VBUF, + &name_vbuf, 0)) { + dprintf(LOG_INFO, FNAME, + "%s:%d duplicated %s name: %s", + configfilename, cl->line, optname, + name_vbuf.dv_buf); + dhcp6_vbuf_free(&name_vbuf); + return -1; + } + + /* add the name */ + if (dhcp6_add_listval(list0, DHCP6_LISTVAL_VBUF, + &name_vbuf, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to add a %s name", + optname); + dhcp6_vbuf_free(&name_vbuf); + return -1; + } + dhcp6_vbuf_free(&name_vbuf); + } + + return 0; +} + +static int +configure_duid(str, duid) + char *str; /* this is a valid DUID string */ + struct duid *duid; +{ + char *cp, *bp; + char *idbuf = NULL; + int duidlen, slen; + unsigned int x; + + /* calculate DUID len */ + slen = strlen(str); + if (slen < 2) + goto bad; + duidlen = 1; + slen -= 2; + if ((slen % 3) != 0) + goto bad; + duidlen += (slen / 3); + if (duidlen > 128) { + dprintf(LOG_ERR, FNAME, "too long DUID (%d)", duidlen); + return (-1); + } + + if ((idbuf = malloc(duidlen)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + return (-1); + } + + for (cp = str, bp = idbuf; *cp;) { + if (*cp == ':') { + cp++; + continue; + } + + if (sscanf(cp, "%02x", &x) != 1) + goto bad; + *bp = x; + cp += 2; + bp++; + } + + duid->duid_len = duidlen; + duid->duid_id = idbuf; + + return (0); + + bad: + if (idbuf) + free(idbuf); + dprintf(LOG_ERR, FNAME, "assumption failure (bad string)"); + return (-1); +} + +/* we currently only construct EUI-64 based interface ID */ +static int +get_default_ifid(pif) + struct prefix_ifconf *pif; +{ + struct ifaddrs *ifa, *ifap; +#ifdef __KAME__ + struct sockaddr_dl *sdl; +#endif +#ifdef __linux__ + struct sockaddr_ll *sll; +#endif + + if (pif->ifid_len < 64) { + dprintf(LOG_NOTICE, FNAME, "ID length too short"); + return (-1); + } + + if (getifaddrs(&ifap) < 0) { + dprintf(LOG_ERR, FNAME, "getifaddrs failed: %s", + strerror(errno)); + return (-1); + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + char *cp; + + if (strcmp(ifa->ifa_name, pif->ifname) != 0) + continue; + + if (ifa->ifa_addr == NULL) + continue; + +#ifdef __KAME__ + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_alen < 6) { + dprintf(LOG_NOTICE, FNAME, + "link layer address is too short (%s)", + pif->ifname); + goto fail; + } + + memset(pif->ifid, 0, sizeof(pif->ifid)); + cp = (char *)(sdl->sdl_data + sdl->sdl_nlen); +#endif +#ifdef __linux__ + if (ifa->ifa_addr->sa_family != AF_PACKET) + continue; + + sll = (struct sockaddr_ll *)ifa->ifa_addr; + if (sll->sll_halen < 6) { + dprintf(LOG_NOTICE, FNAME, + "link layer address is too short (%s)", + pif->ifname); + goto fail; + } + + memset(pif->ifid, 0, sizeof(pif->ifid)); + cp = (char *)(sll->sll_addr); +#endif + pif->ifid[8] = cp[0]; + pif->ifid[8] ^= 0x02; /* reverse the u/l bit*/ + pif->ifid[9] = cp[1]; + pif->ifid[10] = cp[2]; + pif->ifid[11] = 0xff; + pif->ifid[12] = 0xfe; + pif->ifid[13] = cp[3]; + pif->ifid[14] = cp[4]; + pif->ifid[15] = cp[5]; + + break; + } + + if (ifa == NULL) { + dprintf(LOG_INFO, FNAME, + "cannot find interface information for %s", pif->ifname); + goto fail; + } + + freeifaddrs(ifap); + return (0); + + fail: + freeifaddrs(ifap); + return (-1); +} + +void +configure_cleanup() +{ + clear_iaconf(&ia_conflist0); + clear_ifconf(dhcp6_ifconflist); + dhcp6_ifconflist = NULL; + clear_hostconf(host_conflist0); + host_conflist0 = NULL; + clear_keys(key_list0); + key_list0 = NULL; + clear_authinfo(auth_list0); + auth_list0 = NULL; + + dhcp6_clear_list(&siplist0); + TAILQ_INIT(&siplist0); + dhcp6_clear_list(&sipnamelist0); + TAILQ_INIT(&sipnamelist0); + dhcp6_clear_list(&dnslist0); + TAILQ_INIT(&dnslist0); + dhcp6_clear_list(&dnsnamelist0); + TAILQ_INIT(&dnsnamelist0); + dhcp6_clear_list(&ntplist0); + TAILQ_INIT(&ntplist0); + optrefreshtime0 = -1; + clear_poolconf(pool_conflist0); +} + +void +configure_commit() +{ + struct dhcp6_ifconf *ifc; + struct dhcp6_if *ifp; + struct ia_conf *iac; + + /* commit interface configuration */ + for (ifp = dhcp6_if; ifp; ifp = ifp->next) { + /* re-initialization */ + ifp->send_flags = 0; + ifp->allow_flags = 0; + dhcp6_clear_list(&ifp->reqopt_list); + clear_iaconf(&ifp->iaconf_list); + ifp->server_pref = DH6OPT_PREF_UNDEF; + if (ifp->scriptpath != NULL) + free(ifp->scriptpath); + ifp->scriptpath = NULL; + ifp->authproto = DHCP6_AUTHPROTO_UNDEF; + ifp->authalgorithm = DHCP6_AUTHALG_UNDEF; + ifp->authrdm = DHCP6_AUTHRDM_UNDEF; + + for (ifc = dhcp6_ifconflist; ifc; ifc = ifc->next) { + if (strcmp(ifp->ifname, ifc->ifname) == 0) + break; + } + if (ifc == NULL) + continue; + + /* copy new configuration */ + ifp->send_flags = ifc->send_flags; + ifp->allow_flags = ifc->allow_flags; + dhcp6_copy_list(&ifp->reqopt_list, &ifc->reqopt_list); + while ((iac = TAILQ_FIRST(&ifc->iaconf_list)) != NULL) { + TAILQ_REMOVE(&ifc->iaconf_list, iac, link); + TAILQ_INSERT_TAIL(&ifp->iaconf_list, + iac, link); + } + ifp->server_pref = ifc->server_pref; + ifp->scriptpath = ifc->scriptpath; + ifc->scriptpath = NULL; + + if (ifc->authinfo != NULL) { + ifp->authproto = ifc->authinfo->protocol; + ifp->authalgorithm = ifc->authinfo->algorithm; + ifp->authrdm = ifc->authinfo->rdm; + } + ifp->pool = ifc->pool; + ifc->pool.name = NULL; + } + + clear_ifconf(dhcp6_ifconflist); + dhcp6_ifconflist = NULL; + + /* clear unused IA configuration */ + if (!TAILQ_EMPTY(&ia_conflist0)) { + dprintf(LOG_INFO, FNAME, + "some IA configuration defined but not used"); + } + clear_iaconf(&ia_conflist0); + + /* commit per-host configuration */ + clear_hostconf(host_conflist); + host_conflist = host_conflist0; + host_conflist0 = NULL; + + /* commit secret key information */ + clear_keys(key_list); + key_list = key_list0; + key_list0 = NULL; + + /* commit authentication information */ + clear_authinfo(auth_list); + auth_list = auth_list0; + auth_list0 = NULL; + + /* commit SIP server addresses */ + dhcp6_clear_list(&siplist); + dhcp6_move_list(&siplist, &siplist0); + + /* commit SIP domain names */ + dhcp6_clear_list(&sipnamelist); + dhcp6_move_list(&sipnamelist, &sipnamelist0); + + /* commit DNS addresses */ + dhcp6_clear_list(&dnslist); + dhcp6_move_list(&dnslist, &dnslist0); + + /* commit DNS names */ + dhcp6_clear_list(&dnsnamelist); + dhcp6_move_list(&dnsnamelist, &dnsnamelist0); + + /* commit NTP addresses */ + dhcp6_clear_list(&ntplist); + dhcp6_move_list(&ntplist, &ntplist0); + + /* commit NIS server addresses */ + dhcp6_clear_list(&nislist); + dhcp6_move_list(&nislist, &nislist0); + + /* commit NIS domain names */ + dhcp6_clear_list(&nisnamelist); + dhcp6_move_list(&nisnamelist, &nisnamelist0); + + /* commit NIS+ server addresses */ + dhcp6_clear_list(&nisplist); + dhcp6_move_list(&nisplist, &nisplist0); + + /* commit NIS+ domain names */ + dhcp6_clear_list(&nispnamelist); + dhcp6_move_list(&nispnamelist, &nispnamelist0); + + /* commit BCMCS server addresses */ + dhcp6_clear_list(&bcmcslist); + dhcp6_move_list(&bcmcslist, &bcmcslist0); + + /* commit BCMCS domain names */ + dhcp6_clear_list(&bcmcsnamelist); + dhcp6_move_list(&bcmcsnamelist, &bcmcsnamelist0); + + /* commit information refresh time */ + optrefreshtime = optrefreshtime0; + /* commit pool configuration */ + clear_poolconf(pool_conflist); + pool_conflist = pool_conflist0; + pool_conflist0 = NULL; +} + +static void +clear_ifconf(iflist) + struct dhcp6_ifconf *iflist; +{ + struct dhcp6_ifconf *ifc, *ifc_next; + + for (ifc = iflist; ifc; ifc = ifc_next) { + ifc_next = ifc->next; + + free(ifc->ifname); + dhcp6_clear_list(&ifc->reqopt_list); + + clear_iaconf(&ifc->iaconf_list); + + if (ifc->scriptpath) + free(ifc->scriptpath); + + if (ifc->pool.name) + free(ifc->pool.name); + free(ifc); + } +} + +static void +clear_pd_pif(iapdc) + struct iapd_conf *iapdc; +{ + struct prefix_ifconf *pif, *pif_next; + + for (pif = TAILQ_FIRST(&iapdc->iapd_pif_list); pif; pif = pif_next) { + pif_next = TAILQ_NEXT(pif, link); + + free(pif->ifname); + free(pif); + } + + dhcp6_clear_list(&iapdc->iapd_prefix_list); +} + +static void +clear_iaconf(ialist) + struct ia_conflist *ialist; +{ + struct ia_conf *iac; + + while ((iac = TAILQ_FIRST(ialist)) != NULL) { + TAILQ_REMOVE(ialist, iac, link); + + switch(iac->type) { + case IATYPE_PD: + if (!TAILQ_EMPTY(&iac->iadata)) { + dprintf(LOG_ERR, FNAME, "assumption failure"); + exit(1); + } + clear_pd_pif((struct iapd_conf *)iac); + break; + case IATYPE_NA: + break; + } + free(iac); + } +} + +static void +clear_hostconf(hlist) + struct host_conf *hlist; +{ + struct host_conf *host, *host_next; + + for (host = hlist; host; host = host_next) { + host_next = host->next; + + free(host->name); + dhcp6_clear_list(&host->prefix_list); + dhcp6_clear_list(&host->addr_list); + if (host->duid.duid_id) + free(host->duid.duid_id); + if (host->pool.name) + free(host->pool.name); + free(host); + } +} + +static void +clear_keys(klist) + struct keyinfo *klist; +{ + struct keyinfo *key, *key_next; + + for (key = klist; key; key = key_next) { + key_next = key->next; + + free(key->name); + free(key->realm); + free(key->secret); + free(key); + } +} + +static void +clear_authinfo(alist) + struct authinfo *alist; +{ + struct authinfo *auth, *auth_next; + + for (auth = alist; auth; auth = auth_next) { + auth_next = auth->next; + free(auth); + } +} + +static int +add_options(opcode, ifc, cfl0) + int opcode; + struct dhcp6_ifconf *ifc; + struct cf_list *cfl0; +{ + struct cf_list *cfl; + int opttype; + struct authinfo *ainfo; + struct ia_conf *iac; + + for (cfl = cfl0; cfl; cfl = cfl->next) { + switch(cfl->type) { + case DHCPOPT_RAPID_COMMIT: + switch (opcode) { + case DHCPOPTCODE_SEND: + ifc->send_flags |= DHCIFF_RAPID_COMMIT; + break; + case DHCPOPTCODE_ALLOW: + ifc->allow_flags |= DHCIFF_RAPID_COMMIT; + break; + default: + dprintf(LOG_ERR, FNAME, + "invalid operation (%d) " + "for option type (%d)", + opcode, cfl->type); + return (-1); + } + break; + case DHCPOPT_AUTHINFO: + if (opcode != DHCPOPTCODE_SEND) { + dprintf(LOG_ERR, FNAME, + "invalid operation (%d) " + "for option type (%d)", + opcode, cfl->type); + return (-1); + } + ainfo = find_authinfo(auth_list0, cfl->ptr); + if (ainfo == NULL) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "auth info (%s) is not defined", + configfilename, cfl->line, + (char *)cfl->ptr); + return (-1); + } + if (ifc->authinfo != NULL) { + dprintf(LOG_ERR, FNAME, + "%s:%d authinfo is doubly specified on %s", + configfilename, cfl->line, ifc->ifname); + return (-1); + } + ifc->authinfo = ainfo; + break; + case DHCPOPT_IA_PD: + switch (opcode) { + case DHCPOPTCODE_SEND: + iac = find_iaconf(&ia_conflist0, IATYPE_PD, + (u_int32_t)cfl->num); + if (iac == NULL) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "IA_PD (%lu) is not defined", + configfilename, cfl->line, + (u_long)cfl->num); + return (-1); + } + + TAILQ_REMOVE(&ia_conflist0, iac, link); + TAILQ_INSERT_TAIL(&ifc->iaconf_list, + iac, link); + + break; + default: + dprintf(LOG_ERR, FNAME, + "invalid operation (%d) " + "for option type (%d)", opcode, cfl->type); + break; + } + break; + case DHCPOPT_IA_NA: + switch (opcode) { + case DHCPOPTCODE_SEND: + iac = find_iaconf(&ia_conflist0, IATYPE_NA, + (u_int32_t)cfl->num); + if (iac == NULL) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "IA_NA (%lu) is not defined", + configfilename, cfl->line, + (u_long)cfl->num); + return (-1); + } + + TAILQ_REMOVE(&ia_conflist0, iac, link); + TAILQ_INSERT_TAIL(&ifc->iaconf_list, + iac, link); + + break; + default: + dprintf(LOG_ERR, FNAME, + "invalid operation (%d) " + "for option type (%d)", opcode, cfl->type); + break; + } + break; + case DHCPOPT_SIP: + case DHCPOPT_SIPNAME: + case DHCPOPT_DNS: + case DHCPOPT_DNSNAME: + case DHCPOPT_NTP: + case DHCPOPT_NIS: + case DHCPOPT_NISNAME: + case DHCPOPT_NISP: + case DHCPOPT_NISPNAME: + case DHCPOPT_BCMCS: + case DHCPOPT_BCMCSNAME: + case DHCPOPT_REFRESHTIME: + switch (cfl->type) { + case DHCPOPT_SIP: + opttype = DH6OPT_SIP_SERVER_A; + break; + case DHCPOPT_SIPNAME: + opttype = DH6OPT_SIP_SERVER_D; + break; + case DHCPOPT_DNS: + opttype = DH6OPT_DNS; + break; + case DHCPOPT_DNSNAME: + opttype = DH6OPT_DNSNAME; + break; + case DHCPOPT_NTP: + opttype = DH6OPT_NTP; + break; + case DHCPOPT_NIS: + opttype = DH6OPT_NIS_SERVERS; + break; + case DHCPOPT_NISNAME: + opttype = DH6OPT_NIS_DOMAIN_NAME; + break; + case DHCPOPT_NISP: + opttype = DH6OPT_NISP_SERVERS; + break; + case DHCPOPT_NISPNAME: + opttype = DH6OPT_NISP_DOMAIN_NAME; + break; + case DHCPOPT_BCMCS: + opttype = DH6OPT_BCMCS_SERVER_A; + break; + case DHCPOPT_BCMCSNAME: + opttype = DH6OPT_BCMCS_SERVER_D; + break; + case DHCPOPT_REFRESHTIME: + opttype = DH6OPT_REFRESHTIME; + break; + } + switch(opcode) { + case DHCPOPTCODE_REQUEST: + if (dhcp6_find_listval(&ifc->reqopt_list, + DHCP6_LISTVAL_NUM, &opttype, 0) + != NULL) { + dprintf(LOG_INFO, FNAME, + "duplicated requested option: %s", + dhcp6optstr(opttype)); + goto next; /* ignore it */ + } + if (dhcp6_add_listval(&ifc->reqopt_list, + DHCP6_LISTVAL_NUM, &opttype, NULL) + == NULL) { + dprintf(LOG_ERR, FNAME, "failed to " + "configure an option"); + return (-1); + } + break; + default: + dprintf(LOG_ERR, FNAME, + "invalid operation (%d) " + "for option type (%d)", opcode, cfl->type); + break; + } + break; + default: + dprintf(LOG_ERR, FNAME, + "%s:%d unsupported option type: %d", + configfilename, cfl->line, cfl->type); + return (-1); + } + + next: + ; + } + + return (0); +} + +static int +add_prefix(head, name, type, prefix0) + struct dhcp6_list *head; + char *name; + int type; + struct dhcp6_prefix *prefix0; +{ + struct dhcp6_prefix oprefix; + + oprefix = *prefix0; + + /* additional validation of parameters */ + if (oprefix.plen < 0 || oprefix.plen > 128) { + dprintf(LOG_ERR, FNAME, "invalid prefix: %d", oprefix.plen); + return (-1); + } + /* clear trailing bits */ + prefix6_mask(&oprefix.addr, oprefix.plen); + if (!IN6_ARE_ADDR_EQUAL(&prefix0->addr, &oprefix.addr)) { + dprintf(LOG_WARNING, FNAME, "prefix %s/%d for %s " + "has a trailing garbage. It should be %s/%d", + in6addr2str(&prefix0->addr, 0), prefix0->plen, + name, in6addr2str(&oprefix.addr, 0), oprefix.plen); + /* ignore the error */ + } + + /* avoid invalid prefix addresses */ + if (IN6_IS_ADDR_MULTICAST(&oprefix.addr) || + IN6_IS_ADDR_LINKLOCAL(&oprefix.addr) || + IN6_IS_ADDR_SITELOCAL(&oprefix.addr)) { + dprintf(LOG_ERR, FNAME, "invalid prefix address: %s", + in6addr2str(&oprefix.addr, 0)); + return (-1); + } + + /* prefix duplication check */ + if (dhcp6_find_listval(head, type, &oprefix, 0)) { + if (type == DHCP6_LISTVAL_PREFIX6) { + dprintf(LOG_NOTICE, FNAME, + "duplicated prefix: %s/%d for %s", + in6addr2str(&oprefix.addr, 0), oprefix.plen, name); + } else { + dprintf(LOG_NOTICE, FNAME, + "duplicated address: %s for %s", + in6addr2str(&oprefix.addr, 0), name); + } + return (-1); + } + + /* validation about relationship of pltime and vltime */ + if (oprefix.vltime != DHCP6_DURATION_INFINITE && + (oprefix.pltime == DHCP6_DURATION_INFINITE || + oprefix.pltime > oprefix.vltime)) { + if (type == DHCP6_LISTVAL_PREFIX6) { + dprintf(LOG_NOTICE, FNAME, + "%s/%d has larger preferred lifetime " + "than valid lifetime", + in6addr2str(&oprefix.addr, 0), oprefix.plen); + } else { + dprintf(LOG_NOTICE, FNAME, + "%s has larger preferred lifetime " + "than valid lifetime", + in6addr2str(&oprefix.addr, 0)); + } + return (-1); + } + + /* insert the new prefix to the chain */ + if (dhcp6_add_listval(head, type, &oprefix, NULL) == NULL) { + return (-1); + } + + return (0); +} + +struct ia_conf * +find_iaconf(head, type, iaid) + struct ia_conflist *head; + int type; + u_int32_t iaid; +{ + struct ia_conf *iac; + + for (iac = TAILQ_FIRST(head); iac; iac = TAILQ_NEXT(iac, link)) { + if (iac->type == type && iac->iaid == iaid) + return (iac); + } + + return (NULL); +} + +struct host_conf * +find_hostconf(duid) + struct duid *duid; +{ + struct host_conf *host; + + if ((host = find_dynamic_hostconf(duid)) != NULL) { + return (host); + } + + for (host = host_conflist; host; host = host->next) { + if (host->duid.duid_len == duid->duid_len && + memcmp(host->duid.duid_id, duid->duid_id, + host->duid.duid_len) == 0) { + return (host); + } + } + + return (NULL); +} + +struct authinfo * +find_authinfo(head, name) + struct authinfo *head; + char *name; +{ + struct authinfo *ainfo; + + for (ainfo = head; ainfo; ainfo = ainfo->next) { + if (strcmp(ainfo->name, name) == 0) + return (ainfo); + } + + return (NULL); +} + +struct dhcp6_prefix * +find_prefix6(list, prefix) + struct dhcp6_list *list; + struct dhcp6_prefix *prefix; +{ + struct dhcp6_listval *v; + + for (v = TAILQ_FIRST(list); v; v = TAILQ_NEXT(v, link)) { + if (v->val_prefix6.plen == prefix->plen && + IN6_ARE_ADDR_EQUAL(&v->val_prefix6.addr, &prefix->addr)) { + return (&v->val_prefix6); + } + } + return (NULL); +} + +struct keyinfo * +find_key(realm, realmlen, id) + char *realm; + size_t realmlen; + u_int32_t id; +{ + struct keyinfo *key; + + for (key = key_list; key; key = key->next) { + if (key->realmlen == realmlen && + memcmp(key->realm, realm, realmlen) == 0 && + key->keyid == id) { + return (key); + } + } + + return (NULL); +} + +char * +qstrdup(qstr) + char *qstr; +{ + size_t len; + char *dup; + + len = strlen(qstr); + if (qstr[0] != '"' || len < 2 || qstr[len - 1] != '"') + return (NULL); + + if ((dup = malloc(len)) == NULL) + return (NULL); + + memcpy(dup, qstr + 1, len - 1); + dup[len - 2] = '\0'; + + return (dup); +} + +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"); + goto bad; + } + 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"); + free(pool); + return (NULL); + } + 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; +{ + struct in6_addr cur; + if (!pool || !addr) + return (0); + + dprintf(LOG_DEBUG, FNAME, "called (pool=%s)", pool->name); + + for (cur = pool->min; in6_addr_cmp(&cur, &pool->max) <= 0; + in6_addr_inc(&cur)) { + if (!is_leased(&cur) && + !IN6_IS_ADDR_MULTICAST(&cur) && + !IN6_IS_ADDR_LINKLOCAL(&cur) && + !IN6_IS_ADDR_SITELOCAL(&cur)) { + dprintf(LOG_DEBUG, FNAME, "found %s", + in6addr2str(&cur, 0)); + *addr= cur; + return 1; + } + + dprintf(LOG_DEBUG, FNAME, "next address %s", + in6addr2str(&cur, 0)); + } + + dprintf(LOG_NOTICE, FNAME, "no available address"); + return 0; +} + +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; + } +} |