Logo Search packages:      
Sourcecode: wide-dhcpv6 version File versions  Download package

config.c

/*    $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>

extern int errno;

struct prefix_ifconf *prefix_ifconflist;
struct dhcp6_list siplist, sipnamelist, dnslist, dnsnamelist, ntplist;
long long optrefreshtime = -1;

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 long long optrefreshtime0;

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) */
};

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 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 get_default_ifid __P((struct prefix_ifconf *));
static char *qstrdup __P((char *));

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;
                  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;
                  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()
{
      struct cf_list *cl;

      /* check against configuration restriction */
      if ((cf_dns_list && cf_dns_name_list) &&
          dhcp6_mode != DHCP6_MODE_SERVER) {
            dprintf(LOG_INFO, FNAME, "%s:%d server-only configuration",
                configfilename, cf_dns_list->line);
            goto bad;
      }
      if ((cf_sip_list && cf_sip_name_list) &&
          dhcp6_mode != DHCP6_MODE_SERVER) {
            dprintf(LOG_INFO, FNAME, "%s:%d server-only configuration",
                configfilename, cf_sip_list->line);
            goto bad;
      }

      /* SIP Server address */
      TAILQ_INIT(&siplist0);
      for (cl = cf_sip_list; cl; cl = cl->next) {
            /* duplication check */
            if (dhcp6_find_listval(&siplist0, DHCP6_LISTVAL_ADDR6,
                cl->ptr, 0)) {
                  dprintf(LOG_INFO, FNAME,
                      "%s:%d duplicated SIP server: %s",
                      configfilename, cl->line,
                      in6addr2str((struct in6_addr *)cl->ptr, 0));
                  goto bad;
            }
            if (dhcp6_add_listval(&siplist0, DHCP6_LISTVAL_ADDR6,
                cl->ptr, NULL) == NULL) {
                  dprintf(LOG_ERR, FNAME, "failed to add a SIP server");
                  goto bad;
            }
      }

      /* SIP Server domain name */
      TAILQ_INIT(&sipnamelist0);
      for (cl = cf_sip_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 SIP server domain name");
                  goto bad;
            }
            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(&sipnamelist0, DHCP6_LISTVAL_VBUF,
                &name_vbuf, 0)) {
                  dprintf(LOG_INFO, FNAME,
                      "%s:%d duplicated SIP name: %s",
                      configfilename, cl->line, name_vbuf.dv_buf);
                  dhcp6_vbuf_free(&name_vbuf);
                  goto bad;
            }

            /* add the name */
            if (dhcp6_add_listval(&sipnamelist0, DHCP6_LISTVAL_VBUF,
                &name_vbuf, NULL) == NULL) {
                  dprintf(LOG_ERR, FNAME, "failed to add a SIP name");
                  dhcp6_vbuf_free(&name_vbuf);
                  goto bad;
            }
            dhcp6_vbuf_free(&name_vbuf);
      }

      /* DNS servers */
      TAILQ_INIT(&dnslist0);
      for (cl = cf_dns_list; cl; cl = cl->next) {
            /* duplication check */
            if (dhcp6_find_listval(&dnslist0, DHCP6_LISTVAL_ADDR6,
                cl->ptr, 0)) {
                  dprintf(LOG_INFO, FNAME,
                      "%s:%d duplicated DNS server: %s",
                      configfilename, cl->line,
                      in6addr2str((struct in6_addr *)cl->ptr, 0));
                  goto bad;
            }
            if (dhcp6_add_listval(&dnslist0, DHCP6_LISTVAL_ADDR6,
                cl->ptr, NULL) == NULL) {
                  dprintf(LOG_ERR, FNAME, "failed to add a DNS server");
                  goto bad;
            }
      }

      /* DNS name */
      TAILQ_INIT(&dnsnamelist0);
      for (cl = cf_dns_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 DNS name");
                  goto bad;
            }
            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(&dnsnamelist0, DHCP6_LISTVAL_VBUF,
                &name_vbuf, 0)) {
                  dprintf(LOG_INFO, FNAME,
                      "%s:%d duplicated DNS name: %s",
                      configfilename, cl->line, name_vbuf.dv_buf);
                  dhcp6_vbuf_free(&name_vbuf);
                  goto bad;
            }

            /* add the name */
            if (dhcp6_add_listval(&dnsnamelist0, DHCP6_LISTVAL_VBUF,
                &name_vbuf, NULL) == NULL) {
                  dprintf(LOG_ERR, FNAME, "failed to add a DNS name");
                  dhcp6_vbuf_free(&name_vbuf);
                  goto bad;
            }
            dhcp6_vbuf_free(&name_vbuf);
      }

      /* NTP servers */
      TAILQ_INIT(&ntplist0);
      for (cl = cf_ntp_list; cl; cl = cl->next) {
#ifdef USE_DH6OPT_NTP
            /* duplication check */
            if (dhcp6_find_listval(&ntplist0, DHCP6_LISTVAL_ADDR6,
                cl->ptr, 0)) {
                  dprintf(LOG_INFO, FNAME,
                      "%s:%d duplicated NTP server: %s",
                      configfilename, cl->line,
                      in6addr2str((struct in6_addr *)cl->ptr, 0));
                  goto bad;
            }
            if (dhcp6_add_listval(&ntplist0, DHCP6_LISTVAL_ADDR6,
                cl->ptr, NULL) == NULL) {
                  dprintf(LOG_ERR, FNAME, "failed to add an NTP server");
                  goto bad;
            }
#else
            dprintf(LOG_ERR, FNAME,
                "the support for NTP option is disabled");
            goto bad;
#endif
      }

      /* Lifetime for stateless options */
      if (cf_refreshtime >= 0) {
#ifdef USE_DH6OPT_REFRESHTIME
            optrefreshtime0 = cf_refreshtime;
#else
            dprintf(LOG_ERR, FNAME, "the support for "
                "information refresh time option is disabled");
            goto bad;
#endif
      }

      return (0);

  bad:
      return (-1);            /* no need to free intermediate list */
}

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;
}

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;
            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;
            }
      }

      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 server 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 information refresh time */
      optrefreshtime = optrefreshtime0;
}

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);

            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);
            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 dhcp6_listval *opt;
      struct cf_list *cfl;
      int opttype;
      struct authinfo *ainfo;
      struct ia_conf *iac;

      for (cfl = cfl0; cfl; cfl = cfl->next) {
            if (opcode ==  DHCPOPTCODE_REQUEST) {
                  for (opt = TAILQ_FIRST(&ifc->reqopt_list); opt;
                       opt = TAILQ_NEXT(opt, link)) {
                        if (opt->val_num == cfl->type) {
                              dprintf(LOG_INFO, FNAME,
                                  "duplicated requested"
                                  " option: %s",
                                  dhcp6optstr(cfl->type));
                              goto next; /* ignore it */
                        }
                  }
            }

            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_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:
#ifdef USE_DH6OPT_NTP
                        opttype = DH6OPT_NTP;
#else
                        dprintf(LOG_ERR, FNAME, "the support "
                            "for NTP option is disabled");
#endif
                        break;
                  case DHCPOPT_REFRESHTIME:
#ifdef USE_DH6OPT_REFRESHTIME
                        opttype = DH6OPT_REFRESHTIME;
#else
                        dprintf(LOG_ERR, FNAME, "the support "
                            "for information refresh time option "
                            "is disabled");
                        return (-1);
#endif
                        break;
                  }
                  switch(opcode) {
                  case DHCPOPTCODE_REQUEST:
                        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_DURATITION_INFINITE &&
          (oprefix.pltime == DHCP6_DURATITION_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;

      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);
}

static 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);
}

Generated by  Doxygen 1.6.0   Back to index